@@ -158,7 +158,7 class IssuesController < ApplicationController | |||||
158 | format.html { |
|
158 | format.html { | |
159 | render_attachment_warning_if_needed(@issue) |
|
159 | render_attachment_warning_if_needed(@issue) | |
160 | flash[:notice] = l(:notice_issue_successful_create, :id => "<a href='#{issue_path(@issue)}'>##{@issue.id}</a>") |
|
160 | flash[:notice] = l(:notice_issue_successful_create, :id => "<a href='#{issue_path(@issue)}'>##{@issue.id}</a>") | |
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?} } : |
|
161 | 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 }) |
|
162 | { :action => 'show', :id => @issue }) | |
163 | } |
|
163 | } | |
164 | format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) } |
|
164 | format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) } |
@@ -127,12 +127,14 class Issue < ActiveRecord::Base | |||||
127 | (project && tracker) ? (project.all_issue_custom_fields & tracker.custom_fields.all) : [] |
|
127 | (project && tracker) ? (project.all_issue_custom_fields & tracker.custom_fields.all) : [] | |
128 | end |
|
128 | end | |
129 |
|
129 | |||
|
130 | # Copies attributes from another issue, arg can be an id or an Issue | |||
130 | def copy_from(arg) |
|
131 | def copy_from(arg) | |
131 | issue = arg.is_a?(Issue) ? arg : Issue.visible.find(arg) |
|
132 | issue = arg.is_a?(Issue) ? arg : Issue.visible.find(arg) | |
132 | self.attributes = issue.attributes.dup.except("id", "root_id", "parent_id", "lft", "rgt", "created_on", "updated_on") |
|
133 | self.attributes = issue.attributes.dup.except("id", "root_id", "parent_id", "lft", "rgt", "created_on", "updated_on") | |
133 | self.custom_field_values = issue.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h} |
|
134 | self.custom_field_values = issue.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h} | |
134 | self.status = issue.status |
|
135 | self.status = issue.status | |
135 | self.author = User.current |
|
136 | self.author = User.current | |
|
137 | @copied_from = issue | |||
136 | self |
|
138 | self | |
137 | end |
|
139 | end | |
138 |
|
140 | |||
@@ -143,6 +145,11 class Issue < ActiveRecord::Base | |||||
143 | copy |
|
145 | copy | |
144 | end |
|
146 | end | |
145 |
|
147 | |||
|
148 | # Returns true if the issue is a copy | |||
|
149 | def copy? | |||
|
150 | @copied_from.present? | |||
|
151 | end | |||
|
152 | ||||
146 | # Moves/copies an issue to a new project and tracker |
|
153 | # Moves/copies an issue to a new project and tracker | |
147 | # Returns the moved/copied issue on success, false on failure |
|
154 | # Returns the moved/copied issue on success, false on failure | |
148 | def move_to_project(new_project, new_tracker=nil, options={}) |
|
155 | def move_to_project(new_project, new_tracker=nil, options={}) | |
@@ -255,7 +262,9 class Issue < ActiveRecord::Base | |||||
255 |
|
262 | |||
256 | safe_attributes 'project_id', |
|
263 | safe_attributes 'project_id', | |
257 | :if => lambda {|issue, user| |
|
264 | :if => lambda {|issue, user| | |
258 | if user.allowed_to?(:move_issues, issue.project) |
|
265 | if issue.new_record? | |
|
266 | issue.copy? | |||
|
267 | elsif user.allowed_to?(:move_issues, issue.project) | |||
259 | projects = Issue.allowed_target_projects_on_move(user) |
|
268 | projects = Issue.allowed_target_projects_on_move(user) | |
260 | projects.include?(issue.project) && projects.size > 1 |
|
269 | projects.include?(issue.project) && projects.size > 1 | |
261 | end |
|
270 | end |
@@ -107,11 +107,12 | |||||
107 | <% end %> |
|
107 | <% end %> | |
108 |
|
108 | |||
109 | <% if @issue.present? %> |
|
109 | <% if @issue.present? %> | |
110 |
<li><%= context_menu_link l(:button_ |
|
110 | <li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue}, | |
111 |
:class => 'icon- |
|
111 | :class => 'icon-copy', :disabled => !@can[:copy] %></li> | |
112 |
<% e |
|
112 | <% else %> | |
113 | <li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :copy => '1'}, |
|
113 | <li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :copy => '1'}, | |
114 |
:class => 'icon-copy', :disabled => !@can[:move] |
|
114 | :class => 'icon-copy', :disabled => !@can[:move] %></li> | |
|
115 | <% end %> | |||
115 | <li><%= context_menu_link l(:button_delete), issues_path(:ids => @issues.collect(&:id), :back_url => @back), |
|
116 | <li><%= context_menu_link l(:button_delete), issues_path(:ids => @issues.collect(&:id), :back_url => @back), | |
116 | :method => :delete, :confirm => issues_destroy_confirmation_message(@issues), :class => 'icon-del', :disabled => !@can[:delete] %></li> |
|
117 | :method => :delete, :confirm => issues_destroy_confirmation_message(@issues), :class => 'icon-del', :disabled => !@can[:delete] %></li> | |
117 |
|
118 |
@@ -2,7 +2,6 | |||||
2 | <%= 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)) %> |
|
2 | <%= 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)) %> | |
3 | <%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue}, :class => 'icon icon-time-add' %> |
|
3 | <%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue}, :class => 'icon icon-time-add' %> | |
4 | <%= watcher_tag(@issue, User.current) %> |
|
4 | <%= watcher_tag(@issue, User.current) %> | |
5 |
<%= link_to_if_authorized l(:button_ |
|
5 | <%= link_to_if_authorized l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue}, :class => 'icon icon-copy' %> | |
6 | <%= link_to_if_authorized l(:button_copy), {:controller => 'issues', :action => 'bulk_edit', :id => @issue, :copy => '1'}, :class => 'icon icon-copy' %> |
|
|||
7 | <%= 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) %> |
|
6 | <%= 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) %> | |
8 | </div> |
|
7 | </div> |
@@ -7,7 +7,7 | |||||
7 | </p> |
|
7 | </p> | |
8 | <% end %> |
|
8 | <% end %> | |
9 |
|
9 | |||
10 |
<% if |
|
10 | <% if @issue.safe_attribute? 'project_id' %> | |
11 | <p><%= f.select :project_id, project_tree_options_for_select(Issue.allowed_target_projects_on_move, :selected => @issue.project), :required => true %></p> |
|
11 | <p><%= f.select :project_id, project_tree_options_for_select(Issue.allowed_target_projects_on_move, :selected => @issue.project), :required => true %></p> | |
12 | <%= observe_field :issue_project_id, :url => project_issue_form_path(@project, :id => @issue, :project_change => '1'), |
|
12 | <%= observe_field :issue_project_id, :url => project_issue_form_path(@project, :id => @issue, :project_change => '1'), | |
13 | :with => "Form.serialize('issue-form')" %> |
|
13 | :with => "Form.serialize('issue-form')" %> |
@@ -5,8 +5,11 | |||||
5 | <% labelled_form_for @issue, :url => project_issues_path(@project), |
|
5 | <% labelled_form_for @issue, :url => project_issues_path(@project), | |
6 | :html => {:id => 'issue-form', :multipart => true} do |f| %> |
|
6 | :html => {:id => 'issue-form', :multipart => true} do |f| %> | |
7 | <%= error_messages_for 'issue' %> |
|
7 | <%= error_messages_for 'issue' %> | |
|
8 | <%= hidden_field_tag 'copy_from', params[:copy_from] if params[:copy_from] %> | |||
8 | <div class="box tabular"> |
|
9 | <div class="box tabular"> | |
|
10 | <div id="all_attributes"> | |||
9 | <%= render :partial => 'issues/form', :locals => {:f => f} %> |
|
11 | <%= render :partial => 'issues/form', :locals => {:f => f} %> | |
|
12 | </div> | |||
10 |
|
13 | |||
11 | <p id="attachments_form"><%= label_tag('attachments[1][file]', l(:label_attachment_plural))%><%= render :partial => 'attachments/form' %></p> |
|
14 | <p id="attachments_form"><%= label_tag('attachments[1][file]', l(:label_attachment_plural))%><%= render :partial => 'attachments/form' %></p> | |
12 |
|
15 |
@@ -43,11 +43,8 class ContextMenusControllerTest < ActionController::TestCase | |||||
43 | assert_tag :tag => 'a', :content => 'Dave Lopper', |
|
43 | assert_tag :tag => 'a', :content => 'Dave Lopper', | |
44 | :attributes => { :href => '/issues/bulk_update?ids%5B%5D=1&issue%5Bassigned_to_id%5D=3', |
|
44 | :attributes => { :href => '/issues/bulk_update?ids%5B%5D=1&issue%5Bassigned_to_id%5D=3', | |
45 | :class => '' } |
|
45 | :class => '' } | |
46 | assert_tag :tag => 'a', :content => 'Duplicate', |
|
|||
47 | :attributes => { :href => '/projects/ecookbook/issues/1/copy', |
|
|||
48 | :class => 'icon-duplicate' } |
|
|||
49 | assert_tag :tag => 'a', :content => 'Copy', |
|
46 | assert_tag :tag => 'a', :content => 'Copy', | |
50 |
:attributes => { :href => '/issues/ |
|
47 | :attributes => { :href => '/projects/ecookbook/issues/1/copy', | |
51 | :class => 'icon-copy' } |
|
48 | :class => 'icon-copy' } | |
52 | assert_no_tag :tag => 'a', :content => 'Move' |
|
49 | assert_no_tag :tag => 'a', :content => 'Move' | |
53 | assert_tag :tag => 'a', :content => 'Delete', |
|
50 | assert_tag :tag => 'a', :content => 'Delete', |
@@ -1571,13 +1571,63 class IssuesControllerTest < ActionController::TestCase | |||||
1571 | end |
|
1571 | end | |
1572 | end |
|
1572 | end | |
1573 |
|
1573 | |||
1574 |
def test_copy |
|
1574 | def test_new_as_copy | |
1575 | @request.session[:user_id] = 2 |
|
1575 | @request.session[:user_id] = 2 | |
1576 | get :new, :project_id => 1, :copy_from => 1 |
|
1576 | get :new, :project_id => 1, :copy_from => 1 | |
|
1577 | ||||
|
1578 | assert_response :success | |||
1577 | assert_template 'new' |
|
1579 | assert_template 'new' | |
|
1580 | ||||
1578 | assert_not_nil assigns(:issue) |
|
1581 | assert_not_nil assigns(:issue) | |
1579 | orig = Issue.find(1) |
|
1582 | orig = Issue.find(1) | |
|
1583 | assert_equal 1, assigns(:issue).project_id | |||
1580 | assert_equal orig.subject, assigns(:issue).subject |
|
1584 | assert_equal orig.subject, assigns(:issue).subject | |
|
1585 | assert assigns(:issue).copy? | |||
|
1586 | ||||
|
1587 | assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'} | |||
|
1588 | assert_tag 'select', :attributes => {:name => 'issue[project_id]'} | |||
|
1589 | assert_tag 'select', :attributes => {:name => 'issue[project_id]'}, | |||
|
1590 | :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}, :content => 'eCookbook'} | |||
|
1591 | assert_tag 'select', :attributes => {:name => 'issue[project_id]'}, | |||
|
1592 | :child => {:tag => 'option', :attributes => {:value => '2', :selected => nil}, :content => 'OnlineStore'} | |||
|
1593 | assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'} | |||
|
1594 | end | |||
|
1595 | ||||
|
1596 | def test_create_as_copy_on_different_project | |||
|
1597 | @request.session[:user_id] = 2 | |||
|
1598 | assert_difference 'Issue.count' do | |||
|
1599 | post :create, :project_id => 1, :copy_from => 1, | |||
|
1600 | :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => 'Copy'} | |||
|
1601 | ||||
|
1602 | assert_not_nil assigns(:issue) | |||
|
1603 | assert assigns(:issue).copy? | |||
|
1604 | end | |||
|
1605 | issue = Issue.first(:order => 'id DESC') | |||
|
1606 | assert_redirected_to "/issues/#{issue.id}" | |||
|
1607 | ||||
|
1608 | assert_equal 2, issue.project_id | |||
|
1609 | assert_equal 3, issue.tracker_id | |||
|
1610 | assert_equal 'Copy', issue.subject | |||
|
1611 | end | |||
|
1612 | ||||
|
1613 | def test_create_as_copy_with_failure | |||
|
1614 | @request.session[:user_id] = 2 | |||
|
1615 | post :create, :project_id => 1, :copy_from => 1, | |||
|
1616 | :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => ''} | |||
|
1617 | ||||
|
1618 | assert_response :success | |||
|
1619 | assert_template 'new' | |||
|
1620 | ||||
|
1621 | assert_not_nil assigns(:issue) | |||
|
1622 | assert assigns(:issue).copy? | |||
|
1623 | ||||
|
1624 | assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'} | |||
|
1625 | assert_tag 'select', :attributes => {:name => 'issue[project_id]'} | |||
|
1626 | assert_tag 'select', :attributes => {:name => 'issue[project_id]'}, | |||
|
1627 | :child => {:tag => 'option', :attributes => {:value => '1', :selected => nil}, :content => 'eCookbook'} | |||
|
1628 | assert_tag 'select', :attributes => {:name => 'issue[project_id]'}, | |||
|
1629 | :child => {:tag => 'option', :attributes => {:value => '2', :selected => 'selected'}, :content => 'OnlineStore'} | |||
|
1630 | assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'} | |||
1581 | end |
|
1631 | end | |
1582 |
|
1632 | |||
1583 | def test_get_edit |
|
1633 | def test_get_edit |
@@ -388,6 +388,7 class IssueTest < ActiveSupport::TestCase | |||||
388 |
|
388 | |||
389 | def test_copy |
|
389 | def test_copy | |
390 | issue = Issue.new.copy_from(1) |
|
390 | issue = Issue.new.copy_from(1) | |
|
391 | assert issue.copy? | |||
391 | assert issue.save |
|
392 | assert issue.save | |
392 | issue.reload |
|
393 | issue.reload | |
393 | orig = Issue.find(1) |
|
394 | orig = Issue.find(1) |
General Comments 0
You need to be logged in to leave comments.
Login now