Use the regular issue form to copy a single issue.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@8552 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
f134eeef45
commit
121bc44cc5
|
@ -158,7 +158,7 @@ class IssuesController < ApplicationController
|
|||
format.html {
|
||||
render_attachment_warning_if_needed(@issue)
|
||||
flash[:notice] = l(:notice_issue_successful_create, :id => "<a href='#{issue_path(@issue)}'>##{@issue.id}</a>")
|
||||
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?} } :
|
||||
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?} } :
|
||||
{ :action => 'show', :id => @issue })
|
||||
}
|
||||
format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
|
||||
|
|
|
@ -127,12 +127,14 @@ class Issue < ActiveRecord::Base
|
|||
(project && tracker) ? (project.all_issue_custom_fields & tracker.custom_fields.all) : []
|
||||
end
|
||||
|
||||
# Copies attributes from another issue, arg can be an id or an Issue
|
||||
def copy_from(arg)
|
||||
issue = arg.is_a?(Issue) ? arg : Issue.visible.find(arg)
|
||||
self.attributes = issue.attributes.dup.except("id", "root_id", "parent_id", "lft", "rgt", "created_on", "updated_on")
|
||||
self.custom_field_values = issue.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h}
|
||||
self.status = issue.status
|
||||
self.author = User.current
|
||||
@copied_from = issue
|
||||
self
|
||||
end
|
||||
|
||||
|
@ -143,6 +145,11 @@ class Issue < ActiveRecord::Base
|
|||
copy
|
||||
end
|
||||
|
||||
# Returns true if the issue is a copy
|
||||
def copy?
|
||||
@copied_from.present?
|
||||
end
|
||||
|
||||
# Moves/copies an issue to a new project and tracker
|
||||
# Returns the moved/copied issue on success, false on failure
|
||||
def move_to_project(new_project, new_tracker=nil, options={})
|
||||
|
@ -255,7 +262,9 @@ class Issue < ActiveRecord::Base
|
|||
|
||||
safe_attributes 'project_id',
|
||||
:if => lambda {|issue, user|
|
||||
if user.allowed_to?(:move_issues, issue.project)
|
||||
if issue.new_record?
|
||||
issue.copy?
|
||||
elsif user.allowed_to?(:move_issues, issue.project)
|
||||
projects = Issue.allowed_target_projects_on_move(user)
|
||||
projects.include?(issue.project) && projects.size > 1
|
||||
end
|
||||
|
|
|
@ -107,11 +107,12 @@
|
|||
<% end %>
|
||||
|
||||
<% if @issue.present? %>
|
||||
<li><%= context_menu_link l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue},
|
||||
:class => 'icon-duplicate', :disabled => !@can[:copy] %></li>
|
||||
<% end %>
|
||||
<li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue},
|
||||
:class => 'icon-copy', :disabled => !@can[:copy] %></li>
|
||||
<% else %>
|
||||
<li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :copy => '1'},
|
||||
:class => 'icon-copy', :disabled => !@can[:move] %></li>
|
||||
:class => 'icon-copy', :disabled => !@can[:move] %></li>
|
||||
<% end %>
|
||||
<li><%= context_menu_link l(:button_delete), issues_path(:ids => @issues.collect(&:id), :back_url => @back),
|
||||
:method => :delete, :confirm => issues_destroy_confirmation_message(@issues), :class => 'icon-del', :disabled => !@can[:delete] %></li>
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
<%= link_to_if_authorized(l(:button_update), {:controller => 'issues', :action => 'edit', :id => @issue }, :onclick => 'showAndScrollTo("update", "notes"); return false;', :class => 'icon icon-edit', :accesskey => accesskey(:edit)) %>
|
||||
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue}, :class => 'icon icon-time-add' %>
|
||||
<%= watcher_tag(@issue, User.current) %>
|
||||
<%= link_to_if_authorized l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue }, :class => 'icon icon-duplicate' %>
|
||||
<%= link_to_if_authorized l(:button_copy), {:controller => 'issues', :action => 'bulk_edit', :id => @issue, :copy => '1'}, :class => 'icon icon-copy' %>
|
||||
<%= link_to_if_authorized l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue}, :class => 'icon icon-copy' %>
|
||||
<%= link_to l(:button_delete), issue_path(@issue), :confirm => issues_destroy_confirmation_message(@issue), :method => :delete, :class => 'icon icon-del' if User.current.allowed_to?(:delete_issues, @project) %>
|
||||
</div>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</p>
|
||||
<% end %>
|
||||
|
||||
<% if !@issue.new_record? && @issue.safe_attribute?('project_id') %>
|
||||
<% if @issue.safe_attribute? 'project_id' %>
|
||||
<p><%= f.select :project_id, project_tree_options_for_select(Issue.allowed_target_projects_on_move, :selected => @issue.project), :required => true %></p>
|
||||
<%= observe_field :issue_project_id, :url => project_issue_form_path(@project, :id => @issue, :project_change => '1'),
|
||||
:with => "Form.serialize('issue-form')" %>
|
||||
|
|
|
@ -5,8 +5,11 @@
|
|||
<% labelled_form_for @issue, :url => project_issues_path(@project),
|
||||
:html => {:id => 'issue-form', :multipart => true} do |f| %>
|
||||
<%= error_messages_for 'issue' %>
|
||||
<%= hidden_field_tag 'copy_from', params[:copy_from] if params[:copy_from] %>
|
||||
<div class="box tabular">
|
||||
<div id="all_attributes">
|
||||
<%= render :partial => 'issues/form', :locals => {:f => f} %>
|
||||
</div>
|
||||
|
||||
<p id="attachments_form"><%= label_tag('attachments[1][file]', l(:label_attachment_plural))%><%= render :partial => 'attachments/form' %></p>
|
||||
|
||||
|
|
|
@ -43,11 +43,8 @@ class ContextMenusControllerTest < ActionController::TestCase
|
|||
assert_tag :tag => 'a', :content => 'Dave Lopper',
|
||||
:attributes => { :href => '/issues/bulk_update?ids%5B%5D=1&issue%5Bassigned_to_id%5D=3',
|
||||
:class => '' }
|
||||
assert_tag :tag => 'a', :content => 'Duplicate',
|
||||
:attributes => { :href => '/projects/ecookbook/issues/1/copy',
|
||||
:class => 'icon-duplicate' }
|
||||
assert_tag :tag => 'a', :content => 'Copy',
|
||||
:attributes => { :href => '/issues/bulk_edit?copy=1&ids%5B%5D=1',
|
||||
:attributes => { :href => '/projects/ecookbook/issues/1/copy',
|
||||
:class => 'icon-copy' }
|
||||
assert_no_tag :tag => 'a', :content => 'Move'
|
||||
assert_tag :tag => 'a', :content => 'Delete',
|
||||
|
|
|
@ -1571,13 +1571,63 @@ class IssuesControllerTest < ActionController::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_copy_issue
|
||||
def test_new_as_copy
|
||||
@request.session[:user_id] = 2
|
||||
get :new, :project_id => 1, :copy_from => 1
|
||||
|
||||
assert_response :success
|
||||
assert_template 'new'
|
||||
|
||||
assert_not_nil assigns(:issue)
|
||||
orig = Issue.find(1)
|
||||
assert_equal 1, assigns(:issue).project_id
|
||||
assert_equal orig.subject, assigns(:issue).subject
|
||||
assert assigns(:issue).copy?
|
||||
|
||||
assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
|
||||
assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
|
||||
assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
|
||||
:child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}, :content => 'eCookbook'}
|
||||
assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
|
||||
:child => {:tag => 'option', :attributes => {:value => '2', :selected => nil}, :content => 'OnlineStore'}
|
||||
assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
|
||||
end
|
||||
|
||||
def test_create_as_copy_on_different_project
|
||||
@request.session[:user_id] = 2
|
||||
assert_difference 'Issue.count' do
|
||||
post :create, :project_id => 1, :copy_from => 1,
|
||||
:issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
|
||||
|
||||
assert_not_nil assigns(:issue)
|
||||
assert assigns(:issue).copy?
|
||||
end
|
||||
issue = Issue.first(:order => 'id DESC')
|
||||
assert_redirected_to "/issues/#{issue.id}"
|
||||
|
||||
assert_equal 2, issue.project_id
|
||||
assert_equal 3, issue.tracker_id
|
||||
assert_equal 'Copy', issue.subject
|
||||
end
|
||||
|
||||
def test_create_as_copy_with_failure
|
||||
@request.session[:user_id] = 2
|
||||
post :create, :project_id => 1, :copy_from => 1,
|
||||
:issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => ''}
|
||||
|
||||
assert_response :success
|
||||
assert_template 'new'
|
||||
|
||||
assert_not_nil assigns(:issue)
|
||||
assert assigns(:issue).copy?
|
||||
|
||||
assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
|
||||
assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
|
||||
assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
|
||||
:child => {:tag => 'option', :attributes => {:value => '1', :selected => nil}, :content => 'eCookbook'}
|
||||
assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
|
||||
:child => {:tag => 'option', :attributes => {:value => '2', :selected => 'selected'}, :content => 'OnlineStore'}
|
||||
assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
|
||||
end
|
||||
|
||||
def test_get_edit
|
||||
|
|
|
@ -388,6 +388,7 @@ class IssueTest < ActiveSupport::TestCase
|
|||
|
||||
def test_copy
|
||||
issue = Issue.new.copy_from(1)
|
||||
assert issue.copy?
|
||||
assert issue.save
|
||||
issue.reload
|
||||
orig = Issue.find(1)
|
||||
|
|
Loading…
Reference in New Issue