##// END OF EJS Templates
Allows project to be changed from the regular issue update action (#4769, #9803)....
Jean-Philippe Lang -
r8411:81cf6b234397
parent child
Show More
@@ -135,7 +135,17 class IssuesController < ApplicationController
135 def new
135 def new
136 respond_to do |format|
136 respond_to do |format|
137 format.html { render :action => 'new', :layout => !request.xhr? }
137 format.html { render :action => 'new', :layout => !request.xhr? }
138 format.js { render :partial => 'attributes' }
138 format.js {
139 render(:update) { |page|
140 if params[:project_change]
141 page.replace_html 'all_attributes', :partial => 'form'
142 else
143 page.replace_html 'attributes', :partial => 'attributes'
144 end
145 m = User.current.allowed_to?(:log_time, @issue.project) ? 'show' : 'hide'
146 page << "if ($('log_time')) {Element.#{m}('log_time');}"
147 }
148 }
139 end
149 end
140 end
150 end
141
151
@@ -274,7 +284,7 private
274 end
284 end
275
285
276 def find_project
286 def find_project
277 project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
287 project_id = params[:project_id] || (params[:issue] && params[:issue][:project_id])
278 @project = Project.find(project_id)
288 @project = Project.find(project_id)
279 rescue ActiveRecord::RecordNotFound
289 rescue ActiveRecord::RecordNotFound
280 render_404
290 render_404
@@ -147,7 +147,9 class Issue < ActiveRecord::Base
147
147
148 issue.init_journal(User.current, options[:notes])
148 issue.init_journal(User.current, options[:notes])
149
149
150 issue.project = new_project
150 # Preserve previous behaviour
151 # #move_to_project doesn't change tracker automatically
152 issue.send :project=, new_project, true
151 if new_tracker
153 if new_tracker
152 issue.tracker = new_tracker
154 issue.tracker = new_tracker
153 end
155 end
@@ -169,6 +171,16 class Issue < ActiveRecord::Base
169 write_attribute(:priority_id, pid)
171 write_attribute(:priority_id, pid)
170 end
172 end
171
173
174 def category_id=(cid)
175 self.category = nil
176 write_attribute(:category_id, cid)
177 end
178
179 def fixed_version_id=(vid)
180 self.fixed_version = nil
181 write_attribute(:fixed_version_id, vid)
182 end
183
172 def tracker_id=(tid)
184 def tracker_id=(tid)
173 self.tracker = nil
185 self.tracker = nil
174 result = write_attribute(:tracker_id, tid)
186 result = write_attribute(:tracker_id, tid)
@@ -182,11 +194,14 class Issue < ActiveRecord::Base
182 end
194 end
183 end
195 end
184
196
185 def project=(project)
197 def project=(project, keep_tracker=false)
186 project_was = self.project
198 project_was = self.project
187 write_attribute(:project_id, project ? project.id : nil)
199 write_attribute(:project_id, project ? project.id : nil)
188 association_instance_set('project', project)
200 association_instance_set('project', project)
189 if project_was && project && project_was != project
201 if project_was && project && project_was != project
202 unless keep_tracker || project.trackers.include?(tracker)
203 self.tracker = project.trackers.first
204 end
190 # Reassign to the category with same name if any
205 # Reassign to the category with same name if any
191 if category
206 if category
192 self.category = project.issue_categories.find_by_name(category.name)
207 self.category = project.issue_categories.find_by_name(category.name)
@@ -229,6 +244,12 class Issue < ActiveRecord::Base
229 write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h)
244 write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h)
230 end
245 end
231
246
247 safe_attributes 'project_id',
248 :if => lambda {|issue, user|
249 projects = Issue.allowed_target_projects_on_move(user)
250 projects.include?(issue.project) && projects.size > 1
251 }
252
232 safe_attributes 'tracker_id',
253 safe_attributes 'tracker_id',
233 'status_id',
254 'status_id',
234 'category_id',
255 'category_id',
@@ -278,7 +299,11 class Issue < ActiveRecord::Base
278 attrs = delete_unsafe_attributes(attrs, user)
299 attrs = delete_unsafe_attributes(attrs, user)
279 return if attrs.empty?
300 return if attrs.empty?
280
301
281 # Tracker must be set before since new_statuses_allowed_to depends on it.
302 # Project and Tracker must be set before since new_statuses_allowed_to depends on it.
303 if p = attrs.delete('project_id')
304 self.project_id = p
305 end
306
282 if t = attrs.delete('tracker_id')
307 if t = attrs.delete('tracker_id')
283 self.tracker_id = t
308 self.tracker_id = t
284 end
309 end
@@ -725,16 +750,16 class Issue < ActiveRecord::Base
725 # End ReportsController extraction
750 # End ReportsController extraction
726
751
727 # Returns an array of projects that current user can move issues to
752 # Returns an array of projects that current user can move issues to
728 def self.allowed_target_projects_on_move
753 def self.allowed_target_projects_on_move(user=User.current)
729 projects = []
754 projects = []
730 if User.current.admin?
755 if user.admin?
731 # admin is allowed to move issues to any active (visible) project
756 # admin is allowed to move issues to any active (visible) project
732 projects = Project.visible.all
757 projects = Project.visible(user).all
733 elsif User.current.logged?
758 elsif user.logged?
734 if Role.non_member.allowed_to?(:move_issues)
759 if Role.non_member.allowed_to?(:move_issues)
735 projects = Project.visible.all
760 projects = Project.visible(user).all
736 else
761 else
737 User.current.memberships.each {|m| projects << m.project if m.roles.detect {|r| r.allowed_to?(:move_issues)}}
762 user.memberships.each {|m| projects << m.project if m.roles.detect {|r| r.allowed_to?(:move_issues)}}
738 end
763 end
739 end
764 end
740 projects
765 projects
@@ -754,7 +779,8 class Issue < ActiveRecord::Base
754
779
755 # Move subtasks
780 # Move subtasks
756 children.each do |child|
781 children.each do |child|
757 child.project = project
782 # Change project and keep project
783 child.send :project=, project, true
758 unless child.save
784 unless child.save
759 raise ActiveRecord::Rollback
785 raise ActiveRecord::Rollback
760 end
786 end
@@ -15,14 +15,14
15 <p><%= f.select :assigned_to_id, principals_options_for_select(@issue.assignable_users, @issue.assigned_to), :include_blank => true %></p>
15 <p><%= f.select :assigned_to_id, principals_options_for_select(@issue.assignable_users, @issue.assigned_to), :include_blank => true %></p>
16 <% end %>
16 <% end %>
17
17
18 <% if @issue.safe_attribute?('category_id') && @project.issue_categories.any? %>
18 <% if @issue.safe_attribute?('category_id') && @issue.project.issue_categories.any? %>
19 <p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
19 <p><%= f.select :category_id, (@issue.project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
20 <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
20 <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
21 l(:label_issue_category_new),
21 l(:label_issue_category_new),
22 'issue_category[name]',
22 'issue_category[name]',
23 {:controller => 'issue_categories', :action => 'create', :project_id => @project},
23 {:controller => 'issue_categories', :action => 'create', :project_id => @issue.project},
24 :title => l(:label_issue_category_new),
24 :title => l(:label_issue_category_new),
25 :tabindex => 199) if authorize_for('issue_categories', 'new') %></p>
25 :tabindex => 199) if User.current.allowed_to?(:manage_categories, @issue.project) %></p>
26 <% end %>
26 <% end %>
27
27
28 <% if @issue.safe_attribute?('fixed_version_id') && @issue.assignable_versions.any? %>
28 <% if @issue.safe_attribute?('fixed_version_id') && @issue.assignable_versions.any? %>
@@ -30,9 +30,9
30 <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
30 <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
31 l(:label_version_new),
31 l(:label_version_new),
32 'version[name]',
32 'version[name]',
33 {:controller => 'versions', :action => 'create', :project_id => @project},
33 {:controller => 'versions', :action => 'create', :project_id => @issue.project},
34 :title => l(:label_version_new),
34 :title => l(:label_version_new),
35 :tabindex => 200) if authorize_for('versions', 'new') %>
35 :tabindex => 200) if User.current.allowed_to?(:manage_versions, @issue.project) %>
36 </p>
36 </p>
37 <% end %>
37 <% end %>
38 </div>
38 </div>
@@ -41,7 +41,7
41 <% if @issue.safe_attribute? 'parent_issue_id' %>
41 <% if @issue.safe_attribute? 'parent_issue_id' %>
42 <p id="parent_issue"><%= f.text_field :parent_issue_id, :size => 10 %></p>
42 <p id="parent_issue"><%= f.text_field :parent_issue_id, :size => 10 %></p>
43 <div id="parent_issue_candidates" class="autocomplete"></div>
43 <div id="parent_issue_candidates" class="autocomplete"></div>
44 <%= javascript_tag "observeParentIssueField('#{auto_complete_issues_path(:id => @issue, :project_id => @project) }')" %>
44 <%= javascript_tag "observeParentIssueField('#{auto_complete_issues_path(:id => @issue, :project_id => @issue.project) }')" %>
45 <% end %>
45 <% end %>
46
46
47 <% if @issue.safe_attribute? 'start_date' %>
47 <% if @issue.safe_attribute? 'start_date' %>
@@ -3,7 +3,9
3 <div class="box">
3 <div class="box">
4 <% if @edit_allowed || !@allowed_statuses.empty? %>
4 <% if @edit_allowed || !@allowed_statuses.empty? %>
5 <fieldset class="tabular"><legend><%= l(:label_change_properties) %></legend>
5 <fieldset class="tabular"><legend><%= l(:label_change_properties) %></legend>
6 <div id="all_attributes">
6 <%= render :partial => 'form', :locals => {:f => f} %>
7 <%= render :partial => 'form', :locals => {:f => f} %>
8 </div>
7 </fieldset>
9 </fieldset>
8 <% end %>
10 <% end %>
9 <% if User.current.allowed_to?(:log_time, @project) %>
11 <% if User.current.allowed_to?(:log_time, @project) %>
@@ -1,3 +1,4
1 <% labelled_fields_for :issue, @issue do |f| %>
1 <%= call_hook(:view_issues_form_details_top, { :issue => @issue, :form => f }) %>
2 <%= call_hook(:view_issues_form_details_top, { :issue => @issue, :form => f }) %>
2
3
3 <% if @issue.safe_attribute? 'is_private' %>
4 <% if @issue.safe_attribute? 'is_private' %>
@@ -6,10 +7,15
6 </p>
7 </p>
7 <% end %>
8 <% end %>
8
9
10 <% if !@issue.new_record? && @issue.safe_attribute?('project_id') %>
11 <p><%= f.select :project_id, Issue.allowed_target_projects_on_move.collect {|t| [t.name, t.id]}, :required => true %></p>
12 <%= observe_field :issue_project_id, :url => project_issue_form_path(@project, :id => @issue, :project_change => '1'),
13 :with => "Form.serialize('issue-form')" %>
14 <% end %>
15
9 <% if @issue.safe_attribute? 'tracker_id' %>
16 <% if @issue.safe_attribute? 'tracker_id' %>
10 <p><%= f.select :tracker_id, @project.trackers.collect {|t| [t.name, t.id]}, :required => true %></p>
17 <p><%= f.select :tracker_id, @issue.project.trackers.collect {|t| [t.name, t.id]}, :required => true %></p>
11 <%= observe_field :issue_tracker_id, :url => project_issue_form_path(@project, :id => @issue),
18 <%= observe_field :issue_tracker_id, :url => project_issue_form_path(@project, :id => @issue),
12 :update => :attributes,
13 :with => "Form.serialize('issue-form')" %>
19 :with => "Form.serialize('issue-form')" %>
14 <% end %>
20 <% end %>
15
21
@@ -39,3 +45,4
39 </div>
45 </div>
40
46
41 <%= call_hook(:view_issues_form_details_bottom, { :issue => @issue, :form => f }) %>
47 <%= call_hook(:view_issues_form_details_bottom, { :issue => @issue, :form => f }) %>
48 <% end %>
@@ -152,7 +152,6 roles_004:
152 - :edit_issues
152 - :edit_issues
153 - :manage_issue_relations
153 - :manage_issue_relations
154 - :add_issue_notes
154 - :add_issue_notes
155 - :move_issues
156 - :save_queries
155 - :save_queries
157 - :view_gantt
156 - :view_gantt
158 - :view_calendar
157 - :view_calendar
@@ -721,6 +721,7 class IssuesControllerTest < ActionController::TestCase
721
721
722 assert_tag 'form', :attributes => {:id => 'issue-form'}
722 assert_tag 'form', :attributes => {:id => 'issue-form'}
723 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
723 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
724 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
724 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
725 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
725 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
726 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
726 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
727 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
@@ -748,6 +749,7 class IssuesControllerTest < ActionController::TestCase
748
749
749 assert_tag 'form', :attributes => {:id => 'issue-form'}
750 assert_tag 'form', :attributes => {:id => 'issue-form'}
750 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
751 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
752 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
751 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
753 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
752 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
754 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
753 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
755 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
@@ -774,6 +776,7 class IssuesControllerTest < ActionController::TestCase
774
776
775 assert_tag 'form', :attributes => {:id => 'issue-form'}
777 assert_tag 'form', :attributes => {:id => 'issue-form'}
776 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
778 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
779 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
777 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
780 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
778 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
781 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
779 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
782 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
@@ -1014,6 +1017,7 class IssuesControllerTest < ActionController::TestCase
1014 assert_template 'new'
1017 assert_template 'new'
1015
1018
1016 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
1019 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
1020 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1017 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1021 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1018 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1022 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1019 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1023 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
@@ -1045,6 +1049,7 class IssuesControllerTest < ActionController::TestCase
1045 assert_template 'new'
1049 assert_template 'new'
1046
1050
1047 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
1051 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
1052 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1048 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1053 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1049 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1054 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1050 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1055 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
@@ -1636,7 +1641,7 class IssuesControllerTest < ActionController::TestCase
1636
1641
1637 def test_update_edit_form
1642 def test_update_edit_form
1638 @request.session[:user_id] = 2
1643 @request.session[:user_id] = 2
1639 xhr :post, :new, :project_id => 1,
1644 xhr :put, :new, :project_id => 1,
1640 :id => 1,
1645 :id => 1,
1641 :issue => {:tracker_id => 2,
1646 :issue => {:tracker_id => 2,
1642 :subject => 'This is the test_new issue',
1647 :subject => 'This is the test_new issue',
@@ -1653,6 +1658,27 class IssuesControllerTest < ActionController::TestCase
1653 assert_equal 'This is the test_new issue', issue.subject
1658 assert_equal 'This is the test_new issue', issue.subject
1654 end
1659 end
1655
1660
1661 def test_update_edit_form_with_project_change
1662 @request.session[:user_id] = 2
1663 xhr :put, :new, :project_id => 1,
1664 :id => 1,
1665 :project_change => '1',
1666 :issue => {:project_id => 2,
1667 :tracker_id => 2,
1668 :subject => 'This is the test_new issue',
1669 :description => 'This is the description',
1670 :priority_id => 5}
1671 assert_response :success
1672 assert_template 'form'
1673
1674 issue = assigns(:issue)
1675 assert_kind_of Issue, issue
1676 assert_equal 1, issue.id
1677 assert_equal 2, issue.project_id
1678 assert_equal 2, issue.tracker_id
1679 assert_equal 'This is the test_new issue', issue.subject
1680 end
1681
1656 def test_update_using_invalid_http_verbs
1682 def test_update_using_invalid_http_verbs
1657 @request.session[:user_id] = 2
1683 @request.session[:user_id] = 2
1658 subject = 'Updated by an invalid http verb'
1684 subject = 'Updated by an invalid http verb'
@@ -1696,6 +1722,57 class IssuesControllerTest < ActionController::TestCase
1696 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
1722 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
1697 end
1723 end
1698
1724
1725 def test_put_update_with_project_change
1726 @request.session[:user_id] = 2
1727 ActionMailer::Base.deliveries.clear
1728
1729 assert_difference('Journal.count') do
1730 assert_difference('JournalDetail.count', 3) do
1731 put :update, :id => 1, :issue => {:project_id => '2',
1732 :tracker_id => '1', # no change
1733 :priority_id => '6',
1734 :category_id => '3'
1735 }
1736 end
1737 end
1738 assert_redirected_to :action => 'show', :id => '1'
1739 issue = Issue.find(1)
1740 assert_equal 2, issue.project_id
1741 assert_equal 1, issue.tracker_id
1742 assert_equal 6, issue.priority_id
1743 assert_equal 3, issue.category_id
1744
1745 mail = ActionMailer::Base.deliveries.last
1746 assert_not_nil mail
1747 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
1748 assert mail.body.include?("Project changed from eCookbook to OnlineStore")
1749 end
1750
1751 def test_put_update_with_tracker_change
1752 @request.session[:user_id] = 2
1753 ActionMailer::Base.deliveries.clear
1754
1755 assert_difference('Journal.count') do
1756 assert_difference('JournalDetail.count', 2) do
1757 put :update, :id => 1, :issue => {:project_id => '1',
1758 :tracker_id => '2',
1759 :priority_id => '6'
1760 }
1761 end
1762 end
1763 assert_redirected_to :action => 'show', :id => '1'
1764 issue = Issue.find(1)
1765 assert_equal 1, issue.project_id
1766 assert_equal 2, issue.tracker_id
1767 assert_equal 6, issue.priority_id
1768 assert_equal 1, issue.category_id
1769
1770 mail = ActionMailer::Base.deliveries.last
1771 assert_not_nil mail
1772 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
1773 assert mail.body.include?("Tracker changed from Bug to Feature request")
1774 end
1775
1699 def test_put_update_with_custom_field_change
1776 def test_put_update_with_custom_field_change
1700 @request.session[:user_id] = 2
1777 @request.session[:user_id] = 2
1701 issue = Issue.find(1)
1778 issue = Issue.find(1)
@@ -455,6 +455,22 class ApiTest::IssuesTest < ActionController::IntegrationTest
455 end
455 end
456 end
456 end
457
457
458 context "PUT /issues/3.xml with project change" do
459 setup do
460 @parameters = {:issue => {:project_id => 2, :subject => 'Project changed'}}
461 end
462
463 should "update project" do
464 assert_no_difference('Issue.count') do
465 put '/issues/3.xml', @parameters, credentials('jsmith')
466 end
467
468 issue = Issue.find(3)
469 assert_equal 2, issue.project_id
470 assert_equal 'Project changed', issue.subject
471 end
472 end
473
458 context "PUT /issues/6.xml with failed update" do
474 context "PUT /issues/6.xml with failed update" do
459 setup do
475 setup do
460 @parameters = {:issue => {:subject => ''}}
476 @parameters = {:issue => {:subject => ''}}
General Comments 0
You need to be logged in to leave comments. Login now