diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb
index d3949cb..2285979 100644
--- a/app/controllers/issues_controller.rb
+++ b/app/controllers/issues_controller.rb
@@ -174,6 +174,7 @@ class IssuesController < ApplicationController
@assignables << @issue.assigned_to if @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
@can = {:edit => User.current.allowed_to?(:edit_issues, @project),
:change_status => User.current.allowed_to?(:change_issue_status, @project),
+ :add => User.current.allowed_to?(:add_issues, @project),
:move => User.current.allowed_to?(:move_issues, @project),
:delete => User.current.allowed_to?(:delete_issues, @project)}
render :layout => false
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 377073f..394e545 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -192,43 +192,45 @@ class ProjectsController < ApplicationController
end
# Add a new issue to @project
+ # The new issue will be created from an existing one if copy_from parameter is given
def add_issue
- @tracker = Tracker.find(params[:tracker_id])
- @priorities = Enumeration::get_values('IPRI')
+ @issue = params[:copy_from] ? Issue.new.copy_from(params[:copy_from]) : Issue.new(params[:issue])
+ @issue.project = @project
+ @issue.author = User.current
+ @issue.tracker ||= Tracker.find(params[:tracker_id])
default_status = IssueStatus.default
unless default_status
- flash.now[:error] = 'No default issue status defined. Please check your configuration.'
+ flash.now[:error] = 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
render :nothing => true, :layout => true
return
- end
- @issue = Issue.new(:project => @project, :tracker => @tracker)
+ end
@issue.status = default_status
@allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker))if logged_in_user
+
if request.get?
- @issue.start_date = Date.today
- @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) }
+ @issue.start_date ||= Date.today
+ @custom_values = @issue.custom_values.empty? ?
+ @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue) } :
+ @issue.custom_values
else
- @issue.attributes = params[:issue]
-
requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
+ # Check that the user is allowed to apply the requested status
@issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
-
- @issue.author_id = self.logged_in_user.id if self.logged_in_user
- # Multiple file upload
- @attachments = []
- params[:attachments].each { |a|
- @attachments << Attachment.new(:container => @issue, :file => a, :author => logged_in_user) unless a.size == 0
- } if params[:attachments] and params[:attachments].is_a? Array
- @custom_values = @project.custom_fields_for_issues(@tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
+ @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) }
@issue.custom_values = @custom_values
if @issue.save
- @attachments.each(&:save)
+ if params[:attachments] && params[:attachments].is_a?(Array)
+ # Save attachments
+ params[:attachments].each {|a| Attachment.create(:container => @issue, :file => a, :author => User.current) unless a.size == 0}
+ end
flash[:notice] = l(:notice_successful_create)
Mailer.deliver_issue_add(@issue) if Setting.notified_events.include?('issue_added')
redirect_to :action => 'list_issues', :id => @project
+ return
end
end
+ @priorities = Enumeration::get_values('IPRI')
end
# Show filtered/sorted issues list of @project
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 972bf01..0d2d6fc 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -54,6 +54,13 @@ class Issue < ActiveRecord::Base
end
end
+ def copy_from(arg)
+ issue = arg.is_a?(Issue) ? arg : Issue.find(arg)
+ self.attributes = issue.attributes.dup
+ self.custom_values = issue.custom_values.collect {|v| v.clone}
+ self
+ end
+
def priority_id=(pid)
self.priority = nil
write_attribute(:priority_id, pid)
diff --git a/app/views/issues/context_menu.rhtml b/app/views/issues/context_menu.rhtml
index caf6a76..798fd42 100644
--- a/app/views/issues/context_menu.rhtml
+++ b/app/views/issues/context_menu.rhtml
@@ -31,6 +31,8 @@
:selected => @issue.assigned_to.nil?, :disabled => !(@can[:edit] || @can[:change_status]) %>
+
<%= context_menu_link l(:button_copy), {:controller => 'projects', :action => 'add_issue', :id => @project, :copy_from => @issue},
+ :class => 'icon-copy', :disabled => !@can[:add] %>
<%= context_menu_link l(:button_move), {:controller => 'projects', :action => 'move_issues', :id => @project, "issue_ids[]" => @issue.id },
:class => 'icon-move', :disabled => !@can[:move] %>
<%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue},
diff --git a/app/views/issues/show.rhtml b/app/views/issues/show.rhtml
index ed615c6..dfeccc6 100644
--- a/app/views/issues/show.rhtml
+++ b/app/views/issues/show.rhtml
@@ -3,6 +3,7 @@
<%= link_to_if_authorized l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue}, :class => 'icon icon-edit', :accesskey => accesskey(:edit) %>
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, :class => 'icon icon-time' %>
<%= watcher_tag(@issue, User.current) %>
+<%= link_to_if_authorized l(:button_copy), {:controller => 'projects', :action => 'add_issue', :id => @project, :copy_from => @issue }, :class => 'icon icon-copy' %>
<%= link_to_if_authorized l(:button_move), {:controller => 'projects', :action => 'move_issues', :id => @project, "issue_ids[]" => @issue.id }, :class => 'icon icon-move' %>
<%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
diff --git a/app/views/projects/add_issue.rhtml b/app/views/projects/add_issue.rhtml
index 8382d6c..a689229 100644
--- a/app/views/projects/add_issue.rhtml
+++ b/app/views/projects/add_issue.rhtml
@@ -1,10 +1,10 @@
-<%=l(:label_issue_new)%>: <%= @tracker.name %>
+<%=l(:label_issue_new)%>: <%= @issue.tracker %>
<% labelled_tabular_form_for :issue, @issue,
:url => {:action => 'add_issue'},
:html => {:multipart => true, :id => 'issue-form'} do |f| %>
- <%= hidden_field_tag 'tracker_id', @tracker.id %>
- <%= render :partial => 'issues/form', :locals => {:f => f} %>
+ <%= f.hidden_field :tracker_id %>
+ <%= render :partial => 'issues/form', :locals => {:f => f} %>
<%= submit_tag l(:button_create) %>
<%= link_to_remote l(:label_preview),
{ :url => { :controller => 'issues', :action => 'preview', :id => @issue },
diff --git a/lang/en.yml b/lang/en.yml
index ec0ee30..4a52d28 100644
--- a/lang/en.yml
+++ b/lang/en.yml
@@ -476,6 +476,7 @@ button_unarchive: Unarchive
button_reset: Reset
button_rename: Rename
button_change_password: Change password
+button_copy: Copy
status_active: active
status_registered: registered
diff --git a/lang/fr.yml b/lang/fr.yml
index 0aeed30..e5a2b84 100644
--- a/lang/fr.yml
+++ b/lang/fr.yml
@@ -476,6 +476,7 @@ button_unarchive: Désarchiver
button_reset: Réinitialiser
button_rename: Renommer
button_change_password: Changer de mot de passe
+button_copy: Copier
status_active: actif
status_registered: enregistré
diff --git a/public/images/copy.png b/public/images/copy.png
new file mode 100644
index 0000000000000000000000000000000000000000..dccaa0614c0018226b20f390d96f269764f18aff
GIT binary patch
literal 291
zc%17D@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPHFD{+k|aV|Q
z%+*scvQWrRE&sB(+6IOO2J@L-%mV6UFY)wsWxviP!z(3W!P%Y<
z6yhxKh%9Dc;1&X5#!GkW{s0A8(j9#r85lP9bN@+X1@eUgd_r9RGcX)Gc5La=t^fc3
zfByXWy?giCCR@t_Wf@C?{DK)Ap4~_Ta(q2q978H@B_}j6x~VD%Fc{6~X>?#bV!+GG
z%Vylu+ECP`Aeg#fj^GSIDJiZb3FXcmI~H^+T;bqqToX`MC}_xZlTAdBP4X;*(M(20
Z2I~aAy*qO@-31!V;OXk;vd$@?2>_lQRbv1E
literal 0
Hc$@ 1, :tracker_id => 1
+ assert_response :success
+ assert_template 'add_issue'
+ post :add_issue, :id => 1, :issue => {:tracker_id => 1, :subject => 'This is the test_add_issue issue', :description => 'This is the description', :priority_id => 5}
+ assert_redirected_to 'projects/list_issues'
+ assert Issue.find_by_subject('This is the test_add_issue issue')
+ end
+
+ def test_copy_issue
+ @request.session[:user_id] = 2
+ get :add_issue, :id => 1, :copy_from => 1
+ assert_template 'add_issue'
+ assert_not_nil assigns(:issue)
+ orig = Issue.find(1)
+ assert_equal orig.subject, assigns(:issue).subject
+ end
end
diff --git a/test/unit/issue_test.rb b/test/unit/issue_test.rb
index 254bffa..5ddd4bd 100644
--- a/test/unit/issue_test.rb
+++ b/test/unit/issue_test.rb
@@ -18,13 +18,23 @@
require File.dirname(__FILE__) + '/../test_helper'
class IssueTest < Test::Unit::TestCase
- fixtures :projects, :users, :members, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues
+ fixtures :projects, :users, :members, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :custom_fields, :custom_values
def test_category_based_assignment
issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => Enumeration.get_values('IPRI').first, :subject => 'Assignment test', :description => 'Assignment test', :category_id => 1)
assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
end
+ def test_copy
+ issue = Issue.new.copy_from(1)
+ assert issue.save
+ issue.reload
+ orig = Issue.find(1)
+ assert_equal orig.subject, issue.subject
+ assert_equal orig.tracker, issue.tracker
+ assert_equal orig.custom_values.first.value, issue.custom_values.first.value
+ end
+
def test_close_duplicates
# Create 3 issues
issue1 = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :priority => Enumeration.get_values('IPRI').first, :subject => 'Duplicates test', :description => 'Duplicates test')