Project

General

Profile

Patch #42081 » issues_controller.rb

redmine-5.1.3\app\controllers - Alfredo Renzetti, 2025-01-07 16:42

 
1
# frozen_string_literal: true
2

    
3
# Redmine - project management software
4
# Copyright (C) 2006-  Jean-Philippe Lang
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19

    
20
class IssuesController < ApplicationController
21
  default_search_scope :issues
22

    
23
  before_action :find_issue, :only => [:show, :edit, :update, :issue_tab]
24
  before_action :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]
25
  before_action :authorize, :except => [:index, :new, :create]
26
  before_action :find_optional_project, :only => [:index, :new, :create]
27
  before_action :build_new_issue_from_params, :only => [:new, :create]
28
  accept_atom_auth :index, :show
29
  accept_api_auth :index, :show, :create, :update, :destroy
30

    
31
  rescue_from Query::StatementInvalid, :with => :query_statement_invalid
32
  rescue_from Query::QueryError, :with => :query_error
33

    
34
  helper :journals
35
  helper :projects
36
  helper :custom_fields
37
  helper :issue_relations
38
  helper :watchers
39
  helper :attachments
40
  helper :queries
41
  include QueriesHelper
42
  helper :repositories
43
  helper :timelog
44

    
45
  def index
46
    use_session = !request.format.csv?
47
    retrieve_default_query(use_session)
48
    retrieve_query(IssueQuery, use_session)
49

    
50
    if @query.valid?
51
      respond_to do |format|
52
        format.html do
53
          @issue_count = @query.issue_count
54
          @issue_pages = Paginator.new @issue_count, per_page_option, params['page']
55
          @issues = @query.issues(:offset => @issue_pages.offset, :limit => @issue_pages.per_page)
56
          render :layout => !request.xhr?
57
        end
58
        format.api do
59
          @offset, @limit = api_offset_and_limit
60
          @query.column_names = %w(author)
61
          @issue_count = @query.issue_count
62
          @issues = @query.issues(:offset => @offset, :limit => @limit)
63
          Issue.load_visible_relations(@issues) if include_in_api_response?('relations')
64
          if User.current.allowed_to?(:view_time_entries, nil, :global => true)
65
            Issue.load_visible_spent_hours(@issues)
66
            Issue.load_visible_total_spent_hours(@issues)
67
          end
68
        end
69
        format.atom do
70
          @issues = @query.issues(:limit => Setting.feeds_limit.to_i)
71
          render_feed(@issues,
72
                      :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}")
73
        end
74
        format.csv do
75
          @issues = @query.issues(:limit => Setting.issues_export_limit.to_i)
76
          send_data(query_to_csv(@issues, @query, params[:csv]),
77
                    :type => 'text/csv; header=present', :filename => "#{filename_for_export(@query, 'issues')}.csv")
78
        end
79
        format.pdf do
80
          @issues = @query.issues(:limit => Setting.issues_export_limit.to_i)
81
          send_file_headers! :type => 'application/pdf', :filename => "#{filename_for_export(@query, 'issues')}.pdf"
82
        end
83
      end
84
    else
85
      respond_to do |format|
86
        format.html {render :layout => !request.xhr?}
87
        format.any(:atom, :csv, :pdf) {head 422}
88
        format.api {render_validation_errors(@query)}
89
      end
90
    end
91
  rescue ActiveRecord::RecordNotFound
92
    render_404
93
  end
94

    
95
  def show
96
    @journals = @issue.visible_journals_with_index
97
    @has_changesets = @issue.changesets.visible.preload(:repository, :user).exists? unless api_request?
98
    @relations =
99
      @issue.relations.
100
        select do |r|
101
          r.other_issue(@issue) && r.other_issue(@issue).visible?
102
        end
103
    @journals.reverse! if User.current.wants_comments_in_reverse_order?
104

    
105
    if User.current.allowed_to?(:view_time_entries, @project)
106
      Issue.load_visible_spent_hours([@issue])
107
      Issue.load_visible_total_spent_hours([@issue])
108
    end
109

    
110
    respond_to do |format|
111
      format.html do
112
        @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
113
        @priorities = IssuePriority.active
114
        @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
115
        @time_entries = @issue.time_entries.visible.preload(:activity, :user)
116
        @relation = IssueRelation.new
117
        retrieve_previous_and_next_issue_ids
118
        render :template => 'issues/show'
119
      end
120
      format.api do
121
        @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
122
        if include_in_api_response?('changesets')
123
          @changesets = @issue.changesets.visible.preload(:repository, :user).to_a
124
          @changesets.reverse! if User.current.wants_comments_in_reverse_order?
125
        end
126
      end
127
      format.atom do
128
        render :template => 'journals/index', :layout => false,
129
        :content_type => 'application/atom+xml'
130
      end
131
      format.pdf do
132
        send_file_headers!(:type => 'application/pdf',
133
                           :filename => "#{@project.identifier}-#{@issue.id}.pdf")
134
      end
135
    end
136
  end
137

    
138
  def new
139
    respond_to do |format|
140
      format.html {render :action => 'new', :layout => !request.xhr?}
141
      format.js
142
    end
143
  end
144

    
145
  def create
146
    unless User.current.allowed_to?(:add_issues, @issue.project, :global => true)
147
      raise ::Unauthorized
148
    end
149

    
150
    call_hook(:controller_issues_new_before_save, {:params => params, :issue => @issue})
151
    @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
152
    if @issue.save
153
	
154
	  # ALF
155
	  if params[:copy_follows] == '1'
156
	    @issue.recursive_copy_followings
157
	  end
158
	  # /ALF
159
	
160
      call_hook(:controller_issues_new_after_save, {:params => params, :issue => @issue})
161
      respond_to do |format|
162
        format.html do
163
          render_attachment_warning_if_needed(@issue)
164
          flash[:notice] =
165
            l(:notice_issue_successful_create,
166
              :id => view_context.link_to("##{@issue.id}", issue_path(@issue),
167
                                          :title => @issue.subject))
168
          redirect_after_create
169
        end
170
        format.api do
171
          render :action => 'show', :status => :created,
172
          :location => issue_url(@issue)
173
        end
174
      end
175
      return
176
    else
177
      respond_to do |format|
178
        format.html do
179
          if @issue.project.nil?
180
            render_error :status => 422
181
          else
182
            render :action => 'new'
183
          end
184
        end
185
        format.api  {render_validation_errors(@issue)}
186
      end
187
    end
188
  end
189

    
190
  def edit
191
    return unless update_issue_from_params
192

    
193
    respond_to do |format|
194
      format.html {}
195
      format.js
196
    end
197
  end
198

    
199
  def update
200
    return unless update_issue_from_params
201

    
202
    attachments = params[:attachments] || params.dig(:issue, :uploads)
203
    if @issue.attachments_addable?
204
      @issue.save_attachments(attachments)
205
    else
206
      attachments = attachments.to_unsafe_hash if attachments.respond_to?(:to_unsafe_hash)
207
      if [Hash, Array].any? { |klass| attachments.is_a?(klass) } && attachments.any?
208
        flash[:warning] = l(:warning_attachments_not_saved, attachments.size)
209
      end
210
    end
211

    
212
    saved = false
213
    begin
214
      saved = save_issue_with_child_records
215
    rescue ActiveRecord::StaleObjectError
216
      @issue.detach_saved_attachments
217
      @conflict = true
218
      if params[:last_journal_id]
219
        @conflict_journals = @issue.journals_after(params[:last_journal_id]).to_a
220
        unless User.current.allowed_to?(:view_private_notes, @issue.project)
221
          @conflict_journals.reject!(&:private_notes?)
222
        end
223
      end
224
    end
225

    
226
    if saved
227
      render_attachment_warning_if_needed(@issue)
228
      unless @issue.current_journal.new_record? || params[:no_flash]
229
        flash[:notice] = l(:notice_successful_update)
230
      end
231
      respond_to do |format|
232
        format.html do
233
          redirect_back_or_default(
234
            issue_path(@issue, previous_and_next_issue_ids_params)
235
          )
236
        end
237
        format.api  {render_api_ok}
238
      end
239
    else
240
      respond_to do |format|
241
        format.html {render :action => 'edit'}
242
        format.api  {render_validation_errors(@issue)}
243
      end
244
    end
245
  end
246

    
247
  def issue_tab
248
    return render_error :status => 422 unless request.xhr?
249

    
250
    tab = params[:name]
251

    
252
    case tab
253
    when 'time_entries'
254
      @time_entries = @issue.time_entries.visible.preload(:activity, :user).to_a
255
      render :partial => 'issues/tabs/time_entries', :locals => {:time_entries => @time_entries}
256
    when 'changesets'
257
      @changesets = @issue.changesets.visible.preload(:repository, :user).to_a
258
      @changesets.reverse! if User.current.wants_comments_in_reverse_order?
259
      render :partial => 'issues/tabs/changesets', :locals => {:changesets => @changesets, :project => @project}
260
    end
261
  end
262

    
263
  # Bulk edit/copy a set of issues
264
  def bulk_edit
265
    @issues.sort!
266
    @copy = params[:copy].present?
267
    @notes = params[:notes]
268

    
269
    # ALF
270
	if params[:copy_follows] == '1'
271
	  Issue.init_static_copied_issue_ids
272
	  #logger.info("ALF INIT bulk_edit @@static_copied_issue_ids")
273
	end
274
	# /ALF
275

    
276
    if @copy
277
      unless User.current.allowed_to?(:copy_issues, @projects)
278
        raise ::Unauthorized
279
      end
280
    else
281
      unless @issues.all?(&:attributes_editable?)
282
        raise ::Unauthorized
283
      end
284
    end
285

    
286
    edited_issues = Issue.where(:id => @issues.map(&:id)).to_a
287

    
288
    @values_by_custom_field = {}
289
    edited_issues.each do |issue|
290
      issue.custom_field_values.each do |c|
291
        if c.value_present?
292
          @values_by_custom_field[c.custom_field] ||= []
293
          @values_by_custom_field[c.custom_field] << issue.id
294
        end
295
      end
296
    end
297

    
298
    @allowed_projects = Issue.allowed_target_projects
299
    if params[:issue]
300
      @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s}
301
      if @target_project
302
        target_projects = [@target_project]
303
        edited_issues.each {|issue| issue.project = @target_project}
304
      end
305
    end
306
    target_projects ||= @projects
307

    
308
    @trackers = target_projects.map {|p| Issue.allowed_target_trackers(p)}.reduce(:&)
309
    if params[:issue]
310
      @target_tracker = @trackers.detect {|t| t.id.to_s == params[:issue][:tracker_id].to_s}
311
      if @target_tracker
312
        edited_issues.each {|issue| issue.tracker = @target_tracker}
313
      end
314
    end
315

    
316
    if @copy
317
      # Copied issues will get their default statuses
318
      @available_statuses = []
319
    else
320
      @available_statuses = edited_issues.map(&:new_statuses_allowed_to).reduce(:&)
321
    end
322
    if params[:issue]
323
      @target_status = @available_statuses.detect {|t| t.id.to_s == params[:issue][:status_id].to_s}
324
      if @target_status
325
        edited_issues.each {|issue| issue.status = @target_status}
326
      end
327
    end
328

    
329
    edited_issues.each do |issue|
330
      issue.custom_field_values.each do |c|
331
        if c.value_present? && @values_by_custom_field[c.custom_field]
332
          @values_by_custom_field[c.custom_field].delete(issue.id)
333
        end
334
      end
335
    end
336
    @values_by_custom_field.delete_if {|k, v| v.blank?}
337
    @custom_fields =
338
      edited_issues.map{|i| i.editable_custom_fields}.
339
        reduce(:&).select {|field| field.format.bulk_edit_supported}
340
    @assignables = target_projects.map(&:assignable_users).reduce(:&)
341
    @versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
342
    @categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
343
    if @copy
344
      @attachments_present = @issues.detect {|i| i.attachments.any?}.present?
345
	  # ALF
346
	  @following_tasks_present = @issues.detect {|i| i.relations_from.where(relation_type: 'precedes').any?}.present?
347
	  # /ALF
348
      @subtasks_present = @issues.detect {|i| !i.leaf?}.present?
349
      @watchers_present = User.current.allowed_to?(:add_issue_watchers, @projects) &&
350
                            Watcher.where(:watchable_type => 'Issue',
351
                                          :watchable_id => @issues.map(&:id)).exists?
352
    end
353

    
354
    @safe_attributes = edited_issues.map(&:safe_attribute_names).reduce(:&)
355

    
356
    @issue_params = params[:issue] || {}
357
    @issue_params[:custom_field_values] ||= {}
358
  end
359

    
360
  def bulk_update
361
    @issues.sort!
362
    @copy = params[:copy].present?
363

    
364
    # ALF
365
	if params[:copy_follows] == '1'
366
	  Issue.init_static_copied_issue_ids
367
	  Issue.set_from_bulk_update(true)
368
	  #logger.info("ALF INIT bulk_update @@static_copied_issue_ids")
369
	end
370
	# /ALF
371

    
372
    attributes = parse_params_for_bulk_update(params[:issue])
373
    copy_subtasks = (params[:copy_subtasks] == '1')
374
	# ALF
375
    copy_follows = (params[:copy_follows] == '1')
376
	# /ALF
377
    copy_attachments = (params[:copy_attachments] == '1')
378
    copy_watchers = (params[:copy_watchers] == '1')
379

    
380
    if @copy
381
      unless User.current.allowed_to?(:copy_issues, @projects)
382
        raise ::Unauthorized
383
      end
384

    
385
      target_projects = @projects
386
      if attributes['project_id'].present?
387
        target_projects = Project.where(:id => attributes['project_id']).to_a
388
      end
389
      unless User.current.allowed_to?(:add_issues, target_projects)
390
        raise ::Unauthorized
391
      end
392

    
393
      unless User.current.allowed_to?(:add_issue_watchers, @projects)
394
        copy_watchers = false
395
      end
396
    else
397
      unless @issues.all?(&:attributes_editable?)
398
        raise ::Unauthorized
399
      end
400
    end
401

    
402
    unsaved_issues = []
403
    saved_issues = []
404

    
405
    if @copy && copy_subtasks
406
      # Descendant issues will be copied with the parent task
407
      # Don't copy them twice
408
      @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}}
409
    end
410
	
411
	# ALF
412
    if @copy && copy_follows
413
	  if !copy_subtasks
414
        # Following issues will be copied with the preceding task
415
        # Don't copy them twice
416
        @issues.reject! {|issue| @issues.detect {|other| issue.follows?(other)}}
417
	  end
418
    end	
419
    # /ALF
420

    
421
    @issues.each do |orig_issue|
422
      orig_issue.reload
423
      if @copy
424
	    # ALF: Added ", :follows => copy_follows" to "orig_issue.copy()"
425
        issue = orig_issue.copy(
426
          {},
427
          :attachments => copy_attachments,
428
          :subtasks => copy_subtasks,
429
		  :follows => copy_follows,
430
          :watchers => copy_watchers,
431
          :link => link_copy?(params[:link_copy])
432
        ) 
433
      else
434
        issue = orig_issue
435
      end
436
      journal = issue.init_journal(User.current, params[:notes])
437
      issue.safe_attributes = attributes
438
      call_hook(:controller_issues_bulk_edit_before_save, {:params => params, :issue => issue})
439
	  
440
      if issue.save
441
        saved_issues << issue
442
      # ALF
443
      if @copy && copy_follows
444
	    if !copy_subtasks
445
          issue.recursive_copy_followings
446
		else
447
		  hash = {orig_issue.id => issue.id}
448
	      Issue.merge_static_copied_issue_ids(hash)
449
	    end
450
      end	
451
      # /ALF
452
      else
453
        unsaved_issues << orig_issue
454
      end
455
    end
456

    
457
    # ALF
458
    if @copy && copy_subtasks && copy_follows
459
	  non_static_copied_issue_ids = Issue.get_static_copied_issue_ids
460
	  non_static_copied_issue_ids.each do |original, copied|
461
	    origIssue = Issue.find(original)
462
		copiedIssue = Issue.find(copied)
463
		origIssue.recursive_copy_followings_2(
464
		  copiedIssue,
465
		  :attachments => copy_attachments,
466
          :subtasks => copy_subtasks,
467
		  :follows => copy_follows,
468
          :watchers => copy_watchers,
469
          :link => link_copy?(params[:link_copy])
470
		  )
471
	  end
472
    end
473
	# /ALF
474

    
475
    if unsaved_issues.empty?
476
      flash[:notice] = l(:notice_successful_update) unless saved_issues.empty?
477
      if params[:follow]
478
        if @issues.size == 1 && saved_issues.size == 1
479
          redirect_to issue_path(saved_issues.first)
480
        elsif saved_issues.map(&:project).uniq.size == 1
481
          redirect_to project_issues_path(saved_issues.map(&:project).first)
482
        end
483
      else
484
        redirect_back_or_default _project_issues_path(@project)
485
      end
486
    else
487
      @saved_issues = @issues
488
      @unsaved_issues = unsaved_issues
489
      @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a
490
      bulk_edit
491
      render :action => 'bulk_edit'
492
    end
493
  end
494

    
495
  def destroy
496
    raise Unauthorized unless @issues.all?(&:deletable?)
497

    
498
    # all issues and their descendants are about to be deleted
499
    issues_and_descendants_ids = Issue.self_and_descendants(@issues).pluck(:id)
500
    time_entries = TimeEntry.where(:issue_id => issues_and_descendants_ids)
501
    @hours = time_entries.sum(:hours).to_f
502

    
503
    if @hours > 0
504
      case params[:todo]
505
      when 'destroy'
506
        # nothing to do
507
      when 'nullify'
508
        if Setting.timelog_required_fields.include?('issue_id')
509
          flash.now[:error] = l(:field_issue) + " " + ::I18n.t('activerecord.errors.messages.blank')
510
          return
511
        else
512
          time_entries.update_all(:issue_id => nil)
513
        end
514
      when 'reassign'
515
        reassign_to = @project && @project.issues.find_by_id(params[:reassign_to_id])
516
        if reassign_to.nil?
517
          flash.now[:error] = l(:error_issue_not_found_in_project)
518
          return
519
        elsif issues_and_descendants_ids.include?(reassign_to.id)
520
          flash.now[:error] = l(:error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted)
521
          return
522
        else
523
          time_entries.update_all(:issue_id => reassign_to.id, :project_id => reassign_to.project_id)
524
        end
525
      else
526
        # display the destroy form if it's a user request
527
        return unless api_request?
528
      end
529
    end
530
    @issues.each do |issue|
531
      begin
532
        issue.reload.destroy
533
      rescue ::ActiveRecord::RecordNotFound # raised by #reload if issue no longer exists
534
        # nothing to do, issue was already deleted (eg. by a parent)
535
      end
536
    end
537
    respond_to do |format|
538
      format.html do
539
        flash[:notice] = l(:notice_successful_delete)
540
        redirect_back_or_default _project_issues_path(@project)
541
      end
542
      format.api  {render_api_ok}
543
    end
544
  end
545

    
546
  # Overrides Redmine::MenuManager::MenuController::ClassMethods for
547
  # when the "New issue" tab is enabled
548
  def current_menu_item
549
    if Setting.new_item_menu_tab == '1' && [:new, :create].include?(action_name.to_sym)
550
      :new_issue
551
    else
552
      super
553
    end
554
  end
555

    
556
  private
557

    
558
  def query_error(exception)
559
    session.delete(:issue_query)
560
    super
561
  end
562

    
563
  def retrieve_default_query(use_session)
564
    return if params[:query_id].present?
565
    return if api_request?
566
    return if params[:set_filter]
567

    
568
    if params[:without_default].present?
569
      params[:set_filter] = 1
570
      return
571
    end
572
    if !params[:set_filter] && use_session && session[:issue_query]
573
      query_id, project_id = session[:issue_query].values_at(:id, :project_id)
574
      return if IssueQuery.where(id: query_id).exists? && project_id == @project&.id
575
    end
576
    if default_query = IssueQuery.default(project: @project)
577
      params[:query_id] = default_query.id
578
    end
579
  end
580

    
581
  def retrieve_previous_and_next_issue_ids
582
    if params[:prev_issue_id].present? || params[:next_issue_id].present?
583
      @prev_issue_id = params[:prev_issue_id].presence.try(:to_i)
584
      @next_issue_id = params[:next_issue_id].presence.try(:to_i)
585
      @issue_position = params[:issue_position].presence.try(:to_i)
586
      @issue_count = params[:issue_count].presence.try(:to_i)
587
    else
588
      retrieve_query_from_session
589
      if @query
590
        @per_page = per_page_option
591
        limit = 500
592
        issue_ids = @query.issue_ids(:limit => (limit + 1))
593
        if (idx = issue_ids.index(@issue.id)) && idx < limit
594
          if issue_ids.size < 500
595
            @issue_position = idx + 1
596
            @issue_count = issue_ids.size
597
          end
598
          @prev_issue_id = issue_ids[idx - 1] if idx > 0
599
          @next_issue_id = issue_ids[idx + 1] if idx < (issue_ids.size - 1)
600
        end
601
        query_params = @query.as_params
602
        if @issue_position
603
          query_params = query_params.merge(:page => (@issue_position / per_page_option) + 1, :per_page => per_page_option)
604
        end
605
        @query_path = _project_issues_path(@query.project, query_params)
606
      end
607
    end
608
  end
609

    
610
  def previous_and_next_issue_ids_params
611
    {
612
      :prev_issue_id => params[:prev_issue_id],
613
      :next_issue_id => params[:next_issue_id],
614
      :issue_position => params[:issue_position],
615
      :issue_count => params[:issue_count]
616
    }.reject {|k, v| k.blank?}
617
  end
618

    
619
  # Used by #edit and #update to set some common instance variables
620
  # from the params
621
  def update_issue_from_params
622
    @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
623
    if params[:time_entry]
624
      @time_entry.safe_attributes = params[:time_entry]
625
    end
626
    @issue.init_journal(User.current)
627
    issue_attributes = params[:issue]
628
    if issue_attributes && issue_attributes[:assigned_to_id] == 'me'
629
      issue_attributes[:assigned_to_id] = User.current.id
630
    end
631
    if issue_attributes && params[:conflict_resolution]
632
      case params[:conflict_resolution]
633
      when 'overwrite'
634
        issue_attributes = issue_attributes.dup
635
        issue_attributes.delete(:lock_version)
636
      when 'add_notes'
637
        issue_attributes = issue_attributes.slice(:notes, :private_notes)
638
      when 'cancel'
639
        redirect_to issue_path(@issue)
640
        return false
641
      end
642
    end
643
    issue_attributes = replace_none_values_with_blank(issue_attributes)
644
    @issue.safe_attributes = issue_attributes
645
    @priorities = IssuePriority.active
646
    @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
647
    true
648
  end
649

    
650
  # Used by #new and #create to build a new issue from the params
651
  # The new issue will be copied from an existing one if copy_from parameter is given
652
  def build_new_issue_from_params
653
    @issue = Issue.new
654
	
655
    # ALF
656
	if params[:copy_follows] == '1'
657
	  Issue.init_static_copied_issue_ids
658
	  Issue.set_from_bulk_update(false)
659
	  #logger.info("ALF INIT build_new_issue_from_params @@static_copied_issue_ids")
660
	end
661
	# /ALF	
662
	
663
    if params[:copy_from]
664
      begin
665
        @issue.init_journal(User.current)
666
        @copy_from = Issue.visible.find(params[:copy_from])
667
        unless User.current.allowed_to?(:copy_issues, @copy_from.project)
668
          raise ::Unauthorized
669
        end
670

    
671
        @link_copy = link_copy?(params[:link_copy]) || request.get?
672
        @copy_attachments = params[:copy_attachments].present? || request.get?
673
        @copy_subtasks = params[:copy_subtasks].present? || request.get?
674
		# ALF
675
		@copy_follows = params[:copy_follows].present? || request.get?
676
		# \ALF
677
        @copy_watchers = User.current.allowed_to?(:add_issue_watchers, @project)
678
		# ALF: Added ", :follows => @copy_follows" to "@issue.copy_from()"
679
        @issue.copy_from(@copy_from, :attachments => @copy_attachments,
680
                         :subtasks => @copy_subtasks, :watchers => @copy_watchers,
681
                         :link => @link_copy, :follows => @copy_follows)
682
        @issue.parent_issue_id = @copy_from.parent_id
683
      rescue ActiveRecord::RecordNotFound
684
        render_404
685
        return
686
      end
687
    end
688
    @issue.project = @project
689
    if request.get?
690
      @issue.project ||= @issue.allowed_target_projects.first
691
    end
692
    @issue.author ||= User.current
693
    @issue.start_date ||= User.current.today if Setting.default_issue_start_date_to_creation_date?
694

    
695
    attrs = (params[:issue] || {}).deep_dup
696
    if action_name == 'new' && params[:was_default_status] == attrs[:status_id]
697
      attrs.delete(:status_id)
698
    end
699
    if action_name == 'new' && params[:form_update_triggered_by] == 'issue_project_id'
700
      # Discard submitted version when changing the project on the issue form
701
      # so we can use the default version for the new project
702
      attrs.delete(:fixed_version_id)
703
    end
704
    attrs[:assigned_to_id] = User.current.id if attrs[:assigned_to_id] == 'me'
705
    @issue.safe_attributes = attrs
706

    
707
    if @issue.project
708
      @issue.tracker ||= @issue.allowed_target_trackers.first
709
      if @issue.tracker.nil?
710
        if @issue.project.trackers.any?
711
          # None of the project trackers is allowed to the user
712
          render_error :message => l(:error_no_tracker_allowed_for_new_issue_in_project), :status => 403
713
        else
714
          # Project has no trackers
715
          render_error l(:error_no_tracker_in_project)
716
        end
717
        return false
718
      end
719
      if @issue.status.nil?
720
        render_error l(:error_no_default_issue_status)
721
        return false
722
      end
723
    elsif request.get?
724
      render_error :message => l(:error_no_projects_with_tracker_allowed_for_new_issue), :status => 403
725
      return false
726
    end
727

    
728
    @priorities = IssuePriority.active
729
    @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
730
  end
731

    
732
  # Saves @issue and a time_entry from the parameters
733
  def save_issue_with_child_records
734
    Issue.transaction do
735
      if params[:time_entry] &&
736
           (params[:time_entry][:hours].present? || params[:time_entry][:comments].present?) &&
737
           User.current.allowed_to?(:log_time, @issue.project)
738
        time_entry = @time_entry || TimeEntry.new
739
        time_entry.project = @issue.project
740
        time_entry.issue = @issue
741
        time_entry.author = User.current
742
        time_entry.user = User.current
743
        time_entry.spent_on = User.current.today
744
        time_entry.safe_attributes = params[:time_entry]
745
        @issue.time_entries << time_entry
746
      end
747
      call_hook(
748
        :controller_issues_edit_before_save,
749
        {:params => params, :issue => @issue,
750
         :time_entry => time_entry,
751
         :journal => @issue.current_journal}
752
      )
753
      if @issue.save
754
        call_hook(
755
          :controller_issues_edit_after_save,
756
          {:params => params, :issue => @issue,
757
           :time_entry => time_entry,
758
           :journal => @issue.current_journal}
759
        )
760
      else
761
        raise ActiveRecord::Rollback
762
      end
763
    end
764
  end
765

    
766
  # Returns true if the issue copy should be linked
767
  # to the original issue
768
  def link_copy?(param)
769
    case Setting.link_copied_issue
770
    when 'yes'
771
      true
772
    when 'no'
773
      false
774
    when 'ask'
775
      param == '1'
776
    end
777
  end
778

    
779
  # Redirects user after a successful issue creation
780
  def redirect_after_create
781
    if params[:continue]
782
      url_params = {}
783
      url_params[:issue] =
784
        {
785
          :tracker_id => @issue.tracker,
786
          :parent_issue_id => @issue.parent_issue_id
787
        }.reject {|k, v| v.nil?}
788
      url_params[:back_url] = params[:back_url].presence
789

    
790
      if params[:project_id]
791
        redirect_to new_project_issue_path(@issue.project, url_params)
792
      else
793
        url_params[:issue][:project_id] = @issue.project_id
794
        redirect_to new_issue_path(url_params)
795
      end
796
    elsif params[:follow]
797
      redirect_to issue_path(@issue)
798
    else
799
      redirect_back_or_default issue_path(@issue)
800
    end
801
  end
802
end
(4-4/5)