annotate app/controllers/issues_controller.rb @ 8:0c83d98252d9 yuya

* Add custom repo prefix and proper auth realm, remove auth cache (seems like an unwise feature), pass DB handle around, various other bits of tidying
author Chris Cannam
date Thu, 12 Aug 2010 15:31:37 +0100
parents 513646585e45
children 1d32c0a0efbf
rev   line source
Chris@0 1 # Redmine - project management software
Chris@0 2 # Copyright (C) 2006-2008 Jean-Philippe Lang
Chris@0 3 #
Chris@0 4 # This program is free software; you can redistribute it and/or
Chris@0 5 # modify it under the terms of the GNU General Public License
Chris@0 6 # as published by the Free Software Foundation; either version 2
Chris@0 7 # of the License, or (at your option) any later version.
Chris@0 8 #
Chris@0 9 # This program is distributed in the hope that it will be useful,
Chris@0 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
Chris@0 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Chris@0 12 # GNU General Public License for more details.
Chris@0 13 #
Chris@0 14 # You should have received a copy of the GNU General Public License
Chris@0 15 # along with this program; if not, write to the Free Software
Chris@0 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Chris@0 17
Chris@0 18 class IssuesController < ApplicationController
Chris@0 19 menu_item :new_issue, :only => [:new, :create]
Chris@0 20 default_search_scope :issues
Chris@0 21
Chris@0 22 before_filter :find_issue, :only => [:show, :edit, :update, :reply]
Chris@0 23 before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
Chris@0 24 before_filter :find_project, :only => [:new, :create, :update_form, :preview, :auto_complete]
Chris@0 25 before_filter :authorize, :except => [:index, :changes, :preview, :context_menu]
Chris@0 26 before_filter :find_optional_project, :only => [:index, :changes]
Chris@0 27 before_filter :check_for_default_issue_status, :only => [:new, :create]
Chris@0 28 before_filter :build_new_issue_from_params, :only => [:new, :create]
Chris@0 29 accept_key_auth :index, :show, :changes
Chris@0 30
Chris@0 31 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
Chris@0 32
Chris@0 33 helper :journals
Chris@0 34 helper :projects
Chris@0 35 include ProjectsHelper
Chris@0 36 helper :custom_fields
Chris@0 37 include CustomFieldsHelper
Chris@0 38 helper :issue_relations
Chris@0 39 include IssueRelationsHelper
Chris@0 40 helper :watchers
Chris@0 41 include WatchersHelper
Chris@0 42 helper :attachments
Chris@0 43 include AttachmentsHelper
Chris@0 44 helper :queries
Chris@0 45 include QueriesHelper
Chris@0 46 helper :sort
Chris@0 47 include SortHelper
Chris@0 48 include IssuesHelper
Chris@0 49 helper :timelog
Chris@0 50 include Redmine::Export::PDF
Chris@0 51
Chris@0 52 verify :method => [:post, :delete],
Chris@0 53 :only => :destroy,
Chris@0 54 :render => { :nothing => true, :status => :method_not_allowed }
Chris@0 55
Chris@0 56 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
Chris@0 57 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
Chris@0 58
Chris@0 59 def index
Chris@0 60 retrieve_query
Chris@0 61 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
Chris@0 62 sort_update(@query.sortable_columns)
Chris@0 63
Chris@0 64 if @query.valid?
Chris@0 65 limit = case params[:format]
Chris@0 66 when 'csv', 'pdf'
Chris@0 67 Setting.issues_export_limit.to_i
Chris@0 68 when 'atom'
Chris@0 69 Setting.feeds_limit.to_i
Chris@0 70 else
Chris@0 71 per_page_option
Chris@0 72 end
Chris@0 73
Chris@0 74 @issue_count = @query.issue_count
Chris@0 75 @issue_pages = Paginator.new self, @issue_count, limit, params['page']
Chris@0 76 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
Chris@0 77 :order => sort_clause,
Chris@0 78 :offset => @issue_pages.current.offset,
Chris@0 79 :limit => limit)
Chris@0 80 @issue_count_by_group = @query.issue_count_by_group
Chris@0 81
Chris@0 82 respond_to do |format|
Chris@0 83 format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
Chris@0 84 format.xml { render :layout => false }
Chris@0 85 format.json { render :text => @issues.to_json, :layout => false }
Chris@0 86 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
Chris@0 87 format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
Chris@0 88 format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
Chris@0 89 end
Chris@0 90 else
Chris@0 91 # Send html if the query is not valid
Chris@0 92 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
Chris@0 93 end
Chris@0 94 rescue ActiveRecord::RecordNotFound
Chris@0 95 render_404
Chris@0 96 end
Chris@0 97
Chris@0 98 def changes
Chris@0 99 retrieve_query
Chris@0 100 sort_init 'id', 'desc'
Chris@0 101 sort_update(@query.sortable_columns)
Chris@0 102
Chris@0 103 if @query.valid?
Chris@0 104 @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
Chris@0 105 :limit => 25)
Chris@0 106 end
Chris@0 107 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
Chris@0 108 render :layout => false, :content_type => 'application/atom+xml'
Chris@0 109 rescue ActiveRecord::RecordNotFound
Chris@0 110 render_404
Chris@0 111 end
Chris@0 112
Chris@0 113 def show
Chris@0 114 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
Chris@0 115 @journals.each_with_index {|j,i| j.indice = i+1}
Chris@0 116 @journals.reverse! if User.current.wants_comments_in_reverse_order?
Chris@0 117 @changesets = @issue.changesets.visible.all
Chris@0 118 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
Chris@0 119 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
Chris@0 120 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
Chris@0 121 @priorities = IssuePriority.all
Chris@0 122 @time_entry = TimeEntry.new
Chris@0 123 respond_to do |format|
Chris@0 124 format.html { render :template => 'issues/show.rhtml' }
Chris@0 125 format.xml { render :layout => false }
Chris@0 126 format.json { render :text => @issue.to_json, :layout => false }
Chris@0 127 format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
Chris@0 128 format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
Chris@0 129 end
Chris@0 130 end
Chris@0 131
Chris@0 132 # Add a new issue
Chris@0 133 # The new issue will be created from an existing one if copy_from parameter is given
Chris@0 134 def new
Chris@0 135 render :action => 'new', :layout => !request.xhr?
Chris@0 136 end
Chris@0 137
Chris@0 138 def create
Chris@0 139 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
Chris@0 140 if @issue.save
Chris@0 141 attachments = Attachment.attach_files(@issue, params[:attachments])
Chris@0 142 render_attachment_warning_if_needed(@issue)
Chris@0 143 flash[:notice] = l(:notice_successful_create)
Chris@0 144 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
Chris@0 145 respond_to do |format|
Chris@0 146 format.html {
Chris@0 147 redirect_to(params[:continue] ? { :action => 'new', :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
Chris@0 148 { :action => 'show', :id => @issue })
Chris@0 149 }
Chris@0 150 format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'issues', :action => 'show', :id => @issue) }
Chris@0 151 format.json { render :text => @issue.to_json, :status => :created, :location => url_for(:controller => 'issues', :action => 'show'), :layout => false }
Chris@0 152 end
Chris@0 153 return
Chris@0 154 else
Chris@0 155 respond_to do |format|
Chris@0 156 format.html { render :action => 'new' }
Chris@0 157 format.xml { render(:xml => @issue.errors, :status => :unprocessable_entity); return }
Chris@0 158 format.json { render :text => object_errors_to_json(@issue), :status => :unprocessable_entity, :layout => false }
Chris@0 159 end
Chris@0 160 end
Chris@0 161 end
Chris@0 162
Chris@0 163 # Attributes that can be updated on workflow transition (without :edit permission)
Chris@0 164 # TODO: make it configurable (at least per role)
Chris@0 165 UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
Chris@0 166
Chris@0 167 def edit
Chris@0 168 update_issue_from_params
Chris@0 169
Chris@0 170 @journal = @issue.current_journal
Chris@0 171
Chris@0 172 respond_to do |format|
Chris@0 173 format.html { }
Chris@0 174 format.xml { }
Chris@0 175 end
Chris@0 176 end
Chris@0 177
Chris@0 178 def update
Chris@0 179 update_issue_from_params
Chris@0 180
Chris@0 181 if @issue.save_issue_with_child_records(params, @time_entry)
Chris@0 182 render_attachment_warning_if_needed(@issue)
Chris@0 183 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
Chris@0 184
Chris@0 185 respond_to do |format|
Chris@0 186 format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
Chris@0 187 format.xml { head :ok }
Chris@0 188 format.json { head :ok }
Chris@0 189 end
Chris@0 190 else
Chris@0 191 render_attachment_warning_if_needed(@issue)
Chris@0 192 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
Chris@0 193 @journal = @issue.current_journal
Chris@0 194
Chris@0 195 respond_to do |format|
Chris@0 196 format.html { render :action => 'edit' }
Chris@0 197 format.xml { render :xml => @issue.errors, :status => :unprocessable_entity }
Chris@0 198 format.json { render :text => object_errors_to_json(@issue), :status => :unprocessable_entity, :layout => false }
Chris@0 199 end
Chris@0 200 end
Chris@0 201 end
Chris@0 202
Chris@0 203 def reply
Chris@0 204 journal = Journal.find(params[:journal_id]) if params[:journal_id]
Chris@0 205 if journal
Chris@0 206 user = journal.user
Chris@0 207 text = journal.notes
Chris@0 208 else
Chris@0 209 user = @issue.author
Chris@0 210 text = @issue.description
Chris@0 211 end
Chris@0 212 # Replaces pre blocks with [...]
Chris@0 213 text = text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]')
Chris@0 214 content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
Chris@0 215 content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
Chris@0 216
Chris@0 217 render(:update) { |page|
Chris@0 218 page.<< "$('notes').value = \"#{escape_javascript content}\";"
Chris@0 219 page.show 'update'
Chris@0 220 page << "Form.Element.focus('notes');"
Chris@0 221 page << "Element.scrollTo('update');"
Chris@0 222 page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
Chris@0 223 }
Chris@0 224 end
Chris@0 225
Chris@0 226 # Bulk edit a set of issues
Chris@0 227 def bulk_edit
Chris@0 228 @issues.sort!
Chris@0 229 if request.post?
Chris@0 230 attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
Chris@0 231 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
Chris@0 232 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
Chris@0 233
Chris@0 234 unsaved_issue_ids = []
Chris@0 235 @issues.each do |issue|
Chris@0 236 issue.reload
Chris@0 237 journal = issue.init_journal(User.current, params[:notes])
Chris@0 238 issue.safe_attributes = attributes
Chris@0 239 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
Chris@0 240 unless issue.save
Chris@0 241 # Keep unsaved issue ids to display them in flash error
Chris@0 242 unsaved_issue_ids << issue.id
Chris@0 243 end
Chris@0 244 end
Chris@0 245 set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
Chris@0 246 redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
Chris@0 247 return
Chris@0 248 end
Chris@0 249 @available_statuses = Workflow.available_statuses(@project)
Chris@0 250 @custom_fields = @project.all_issue_custom_fields
Chris@0 251 end
Chris@0 252
Chris@0 253 def move
Chris@0 254 @issues.sort!
Chris@0 255 @copy = params[:copy_options] && params[:copy_options][:copy]
Chris@0 256 @allowed_projects = Issue.allowed_target_projects_on_move
Chris@0 257 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
Chris@0 258 @target_project ||= @project
Chris@0 259 @trackers = @target_project.trackers
Chris@0 260 @available_statuses = Workflow.available_statuses(@project)
Chris@0 261 if request.post?
Chris@0 262 new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
Chris@0 263 unsaved_issue_ids = []
Chris@0 264 moved_issues = []
Chris@0 265 @issues.each do |issue|
Chris@0 266 issue.reload
Chris@0 267 changed_attributes = {}
Chris@0 268 [:assigned_to_id, :status_id, :start_date, :due_date].each do |valid_attribute|
Chris@0 269 unless params[valid_attribute].blank?
Chris@0 270 changed_attributes[valid_attribute] = (params[valid_attribute] == 'none' ? nil : params[valid_attribute])
Chris@0 271 end
Chris@0 272 end
Chris@0 273 issue.init_journal(User.current)
Chris@0 274 call_hook(:controller_issues_move_before_save, { :params => params, :issue => issue, :target_project => @target_project, :copy => !!@copy })
Chris@0 275 if r = issue.move_to_project(@target_project, new_tracker, {:copy => @copy, :attributes => changed_attributes})
Chris@0 276 moved_issues << r
Chris@0 277 else
Chris@0 278 unsaved_issue_ids << issue.id
Chris@0 279 end
Chris@0 280 end
Chris@0 281 set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
Chris@0 282
Chris@0 283 if params[:follow]
Chris@0 284 if @issues.size == 1 && moved_issues.size == 1
Chris@0 285 redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first
Chris@0 286 else
Chris@0 287 redirect_to :controller => 'issues', :action => 'index', :project_id => (@target_project || @project)
Chris@0 288 end
Chris@0 289 else
Chris@0 290 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
Chris@0 291 end
Chris@0 292 return
Chris@0 293 end
Chris@0 294 render :layout => false if request.xhr?
Chris@0 295 end
Chris@0 296
Chris@0 297 def destroy
Chris@0 298 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
Chris@0 299 if @hours > 0
Chris@0 300 case params[:todo]
Chris@0 301 when 'destroy'
Chris@0 302 # nothing to do
Chris@0 303 when 'nullify'
Chris@0 304 TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
Chris@0 305 when 'reassign'
Chris@0 306 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
Chris@0 307 if reassign_to.nil?
Chris@0 308 flash.now[:error] = l(:error_issue_not_found_in_project)
Chris@0 309 return
Chris@0 310 else
Chris@0 311 TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
Chris@0 312 end
Chris@0 313 else
Chris@0 314 unless params[:format] == 'xml' || params[:format] == 'json'
Chris@0 315 # display the destroy form if it's a user request
Chris@0 316 return
Chris@0 317 end
Chris@0 318 end
Chris@0 319 end
Chris@0 320 @issues.each(&:destroy)
Chris@0 321 respond_to do |format|
Chris@0 322 format.html { redirect_to :action => 'index', :project_id => @project }
Chris@0 323 format.xml { head :ok }
Chris@0 324 format.json { head :ok }
Chris@0 325 end
Chris@0 326 end
Chris@0 327
Chris@0 328 def context_menu
Chris@0 329 @issues = Issue.find_all_by_id(params[:ids], :include => :project)
Chris@0 330 if (@issues.size == 1)
Chris@0 331 @issue = @issues.first
Chris@0 332 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
Chris@0 333 end
Chris@0 334 projects = @issues.collect(&:project).compact.uniq
Chris@0 335 @project = projects.first if projects.size == 1
Chris@0 336
Chris@0 337 @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
Chris@0 338 :log_time => (@project && User.current.allowed_to?(:log_time, @project)),
Chris@0 339 :update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))),
Chris@0 340 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
Chris@0 341 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
Chris@0 342 :delete => (@project && User.current.allowed_to?(:delete_issues, @project))
Chris@0 343 }
Chris@0 344 if @project
Chris@0 345 @assignables = @project.assignable_users
Chris@0 346 @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
Chris@0 347 @trackers = @project.trackers
Chris@0 348 end
Chris@0 349
Chris@0 350 @priorities = IssuePriority.all.reverse
Chris@0 351 @statuses = IssueStatus.find(:all, :order => 'position')
Chris@0 352 @back = params[:back_url] || request.env['HTTP_REFERER']
Chris@0 353
Chris@0 354 render :layout => false
Chris@0 355 end
Chris@0 356
Chris@0 357 def update_form
Chris@0 358 if params[:id].blank?
Chris@0 359 @issue = Issue.new
Chris@0 360 @issue.project = @project
Chris@0 361 else
Chris@0 362 @issue = @project.issues.visible.find(params[:id])
Chris@0 363 end
Chris@0 364 @issue.attributes = params[:issue]
Chris@0 365 @allowed_statuses = ([@issue.status] + @issue.status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq
Chris@0 366 @priorities = IssuePriority.all
Chris@0 367
Chris@0 368 render :partial => 'attributes'
Chris@0 369 end
Chris@0 370
Chris@0 371 def preview
Chris@0 372 @issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank?
Chris@0 373 if @issue
Chris@0 374 @attachements = @issue.attachments
Chris@0 375 @description = params[:issue] && params[:issue][:description]
Chris@0 376 if @description && @description.gsub(/(\r?\n|\n\r?)/, "\n") == @issue.description.to_s.gsub(/(\r?\n|\n\r?)/, "\n")
Chris@0 377 @description = nil
Chris@0 378 end
Chris@0 379 @notes = params[:notes]
Chris@0 380 else
Chris@0 381 @description = (params[:issue] ? params[:issue][:description] : nil)
Chris@0 382 end
Chris@0 383 render :layout => false
Chris@0 384 end
Chris@0 385
Chris@0 386 def auto_complete
Chris@0 387 @issues = []
Chris@0 388 q = params[:q].to_s
Chris@0 389 if q.match(/^\d+$/)
Chris@0 390 @issues << @project.issues.visible.find_by_id(q.to_i)
Chris@0 391 end
Chris@0 392 unless q.blank?
Chris@0 393 @issues += @project.issues.visible.find(:all, :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%"], :limit => 10)
Chris@0 394 end
Chris@0 395 render :layout => false
Chris@0 396 end
Chris@0 397
Chris@0 398 private
Chris@0 399 def find_issue
Chris@0 400 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
Chris@0 401 @project = @issue.project
Chris@0 402 rescue ActiveRecord::RecordNotFound
Chris@0 403 render_404
Chris@0 404 end
Chris@0 405
Chris@0 406 # Filter for bulk operations
Chris@0 407 def find_issues
Chris@0 408 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
Chris@0 409 raise ActiveRecord::RecordNotFound if @issues.empty?
Chris@0 410 projects = @issues.collect(&:project).compact.uniq
Chris@0 411 if projects.size == 1
Chris@0 412 @project = projects.first
Chris@0 413 else
Chris@0 414 # TODO: let users bulk edit/move/destroy issues from different projects
Chris@0 415 render_error 'Can not bulk edit/move/destroy issues from different projects'
Chris@0 416 return false
Chris@0 417 end
Chris@0 418 rescue ActiveRecord::RecordNotFound
Chris@0 419 render_404
Chris@0 420 end
Chris@0 421
Chris@0 422 def find_project
Chris@0 423 project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
Chris@0 424 @project = Project.find(project_id)
Chris@0 425 rescue ActiveRecord::RecordNotFound
Chris@0 426 render_404
Chris@0 427 end
Chris@0 428
Chris@0 429 # Used by #edit and #update to set some common instance variables
Chris@0 430 # from the params
Chris@0 431 # TODO: Refactor, not everything in here is needed by #edit
Chris@0 432 def update_issue_from_params
Chris@0 433 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
Chris@0 434 @priorities = IssuePriority.all
Chris@0 435 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
Chris@0 436 @time_entry = TimeEntry.new
Chris@0 437
Chris@0 438 @notes = params[:notes]
Chris@0 439 @issue.init_journal(User.current, @notes)
Chris@0 440 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
Chris@0 441 if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
Chris@0 442 attrs = params[:issue].dup
Chris@0 443 attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
Chris@0 444 attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
Chris@0 445 @issue.safe_attributes = attrs
Chris@0 446 end
Chris@0 447
Chris@0 448 end
Chris@0 449
Chris@0 450 # TODO: Refactor, lots of extra code in here
Chris@0 451 def build_new_issue_from_params
Chris@0 452 @issue = Issue.new
Chris@0 453 @issue.copy_from(params[:copy_from]) if params[:copy_from]
Chris@0 454 @issue.project = @project
Chris@0 455 # Tracker must be set before custom field values
Chris@0 456 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
Chris@0 457 if @issue.tracker.nil?
Chris@0 458 render_error l(:error_no_tracker_in_project)
Chris@0 459 return false
Chris@0 460 end
Chris@0 461 if params[:issue].is_a?(Hash)
Chris@0 462 @issue.safe_attributes = params[:issue]
Chris@0 463 @issue.watcher_user_ids = params[:issue]['watcher_user_ids'] if User.current.allowed_to?(:add_issue_watchers, @project)
Chris@0 464 end
Chris@0 465 @issue.author = User.current
Chris@0 466 @issue.start_date ||= Date.today
Chris@0 467 @priorities = IssuePriority.all
Chris@0 468 @allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
Chris@0 469 end
Chris@0 470
Chris@0 471 def set_flash_from_bulk_issue_save(issues, unsaved_issue_ids)
Chris@0 472 if unsaved_issue_ids.empty?
Chris@0 473 flash[:notice] = l(:notice_successful_update) unless issues.empty?
Chris@0 474 else
Chris@0 475 flash[:error] = l(:notice_failed_to_save_issues,
Chris@0 476 :count => unsaved_issue_ids.size,
Chris@0 477 :total => issues.size,
Chris@0 478 :ids => '#' + unsaved_issue_ids.join(', #'))
Chris@0 479 end
Chris@0 480 end
Chris@0 481
Chris@0 482 def check_for_default_issue_status
Chris@0 483 if IssueStatus.default.nil?
Chris@0 484 render_error l(:error_no_default_issue_status)
Chris@0 485 return false
Chris@0 486 end
Chris@0 487 end
Chris@0 488 end