##// END OF EJS Templates
Adds a setting to allow subtasks to belong to other projects (#5487)....
Jean-Philippe Lang -
r10376:83bcc1f04351
parent child
Show More
@@ -65,7 +65,7 module IssuesHelper
65 s = ''
65 s = ''
66 ancestors = issue.root? ? [] : issue.ancestors.visible.all
66 ancestors = issue.root? ? [] : issue.ancestors.visible.all
67 ancestors.each do |ancestor|
67 ancestors.each do |ancestor|
68 s << '<div>' + content_tag('p', link_to_issue(ancestor))
68 s << '<div>' + content_tag('p', link_to_issue(ancestor, :project => (issue.project_id != ancestor.project_id)))
69 end
69 end
70 s << '<div>'
70 s << '<div>'
71 subject = h(issue.subject)
71 subject = h(issue.subject)
@@ -82,7 +82,7 module IssuesHelper
82 issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level|
82 issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level|
83 s << content_tag('tr',
83 s << content_tag('tr',
84 content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
84 content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
85 content_tag('td', link_to_issue(child, :truncate => 60), :class => 'subject') +
85 content_tag('td', link_to_issue(child, :truncate => 60, :project => (issue.project_id != child.project_id)), :class => 'subject') +
86 content_tag('td', h(child.status)) +
86 content_tag('td', h(child.status)) +
87 content_tag('td', link_to_user(child.assigned_to)) +
87 content_tag('td', link_to_user(child.assigned_to)) +
88 content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
88 content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
@@ -91,4 +91,16 module SettingsHelper
91 l_or_humanize(notifiable.name, :prefix => 'label_').html_safe,
91 l_or_humanize(notifiable.name, :prefix => 'label_').html_safe,
92 :class => notifiable.parent.present? ? "parent" : '').html_safe
92 :class => notifiable.parent.present? ? "parent" : '').html_safe
93 end
93 end
94
95 def cross_project_subtasks_options
96 options = [
97 [:label_disabled, ''],
98 [:label_cross_project_system, 'system'],
99 [:label_cross_project_tree, 'tree'],
100 [:label_cross_project_hierarchy, 'hierarchy'],
101 [:label_cross_project_descendants, 'descendants']
102 ]
103
104 options.map {|label, value| [l(label), value.to_s]}
105 end
94 end
106 end
@@ -285,7 +285,8 class Issue < ActiveRecord::Base
285 if fixed_version && fixed_version.project != project && !project.shared_versions.include?(fixed_version)
285 if fixed_version && fixed_version.project != project && !project.shared_versions.include?(fixed_version)
286 self.fixed_version = nil
286 self.fixed_version = nil
287 end
287 end
288 if parent && parent.project_id != project_id
288 # Clear the parent task if it's no longer valid
289 unless valid_parent_project?
289 self.parent_issue_id = nil
290 self.parent_issue_id = nil
290 end
291 end
291 @custom_field_values = nil
292 @custom_field_values = nil
@@ -550,8 +551,8 class Issue < ActiveRecord::Base
550
551
551 # Checks parent issue assignment
552 # Checks parent issue assignment
552 if @parent_issue
553 if @parent_issue
553 if @parent_issue.project_id != project_id
554 if !valid_parent_project?(@parent_issue)
554 errors.add :parent_issue_id, :not_same_project
555 errors.add :parent_issue_id, :invalid
555 elsif !new_record?
556 elsif !new_record?
556 # moving an existing issue
557 # moving an existing issue
557 if @parent_issue.root_id != root_id
558 if @parent_issue.root_id != root_id
@@ -559,7 +560,7 class Issue < ActiveRecord::Base
559 elsif move_possible?(@parent_issue)
560 elsif move_possible?(@parent_issue)
560 # move accepted inside tree
561 # move accepted inside tree
561 else
562 else
562 errors.add :parent_issue_id, :not_a_valid_parent
563 errors.add :parent_issue_id, :invalid
563 end
564 end
564 end
565 end
565 end
566 end
@@ -963,6 +964,25 class Issue < ActiveRecord::Base
963 end
964 end
964 end
965 end
965
966
967 # Returns true if issue's project is a valid
968 # parent issue project
969 def valid_parent_project?(issue=parent)
970 return true if issue.nil? || issue.project_id == project_id
971
972 case Setting.cross_project_subtasks
973 when 'system'
974 true
975 when 'tree'
976 issue.project.root == project.root
977 when 'hierarchy'
978 issue.project.is_or_is_ancestor_of?(project) || issue.project.is_descendant_of?(project)
979 when 'descendants'
980 issue.project.is_or_is_ancestor_of?(project)
981 else
982 false
983 end
984 end
985
966 # Extracted from the ReportsController.
986 # Extracted from the ReportsController.
967 def self.by_tracker(project)
987 def self.by_tracker(project)
968 count_and_group_by(:project => project,
988 count_and_group_by(:project => project,
@@ -1042,8 +1062,9 class Issue < ActiveRecord::Base
1042 relations_to.clear
1062 relations_to.clear
1043 end
1063 end
1044
1064
1045 # Move subtasks
1065 # Move subtasks that were in the same project
1046 children.each do |child|
1066 children.each do |child|
1067 next unless child.project_id == project_id_was
1047 # Change project and keep project
1068 # Change project and keep project
1048 child.send :project=, project, true
1069 child.send :project=, project, true
1049 unless child.save
1070 unless child.save
@@ -43,7 +43,7
43 <div class="splitcontentright">
43 <div class="splitcontentright">
44 <% if @issue.safe_attribute? 'parent_issue_id' %>
44 <% if @issue.safe_attribute? 'parent_issue_id' %>
45 <p id="parent_issue"><%= f.text_field :parent_issue_id, :size => 10, :required => @issue.required_attribute?('parent_issue_id') %></p>
45 <p id="parent_issue"><%= f.text_field :parent_issue_id, :size => 10, :required => @issue.required_attribute?('parent_issue_id') %></p>
46 <%= javascript_tag "observeAutocompleteField('issue_parent_issue_id', '#{escape_javascript auto_complete_issues_path(:project_id => @issue.project)}')" %>
46 <%= javascript_tag "observeAutocompleteField('issue_parent_issue_id', '#{escape_javascript auto_complete_issues_path}')" %>
47 <% end %>
47 <% end %>
48
48
49 <% if @issue.safe_attribute? 'start_date' %>
49 <% if @issue.safe_attribute? 'start_date' %>
@@ -3,6 +3,8
3 <div class="box tabular settings">
3 <div class="box tabular settings">
4 <p><%= setting_check_box :cross_project_issue_relations %></p>
4 <p><%= setting_check_box :cross_project_issue_relations %></p>
5
5
6 <p><%= setting_select :cross_project_subtasks, cross_project_subtasks_options %></p>
7
6 <p><%= setting_check_box :issue_group_assignment %></p>
8 <p><%= setting_check_box :issue_group_assignment %></p>
7
9
8 <p><%= setting_check_box :default_issue_start_date_to_creation_date %></p>
10 <p><%= setting_check_box :default_issue_start_date_to_creation_date %></p>
@@ -357,6 +357,7 en:
357 setting_date_format: Date format
357 setting_date_format: Date format
358 setting_time_format: Time format
358 setting_time_format: Time format
359 setting_cross_project_issue_relations: Allow cross-project issue relations
359 setting_cross_project_issue_relations: Allow cross-project issue relations
360 setting_cross_project_subtasks: Allow cross-project subtasks
360 setting_issue_list_default_columns: Default columns displayed on the issue list
361 setting_issue_list_default_columns: Default columns displayed on the issue list
361 setting_repositories_encodings: Attachments and repositories encodings
362 setting_repositories_encodings: Attachments and repositories encodings
362 setting_emails_header: Emails header
363 setting_emails_header: Emails header
@@ -356,6 +356,7 fr:
356 setting_date_format: Format de date
356 setting_date_format: Format de date
357 setting_time_format: Format d'heure
357 setting_time_format: Format d'heure
358 setting_cross_project_issue_relations: Autoriser les relations entre demandes de diffΓ©rents projets
358 setting_cross_project_issue_relations: Autoriser les relations entre demandes de diffΓ©rents projets
359 setting_cross_project_subtasks: Autoriser les sous-tΓ’ches dans des projets diffΓ©rents
359 setting_issue_list_default_columns: Colonnes affichΓ©es par dΓ©faut sur la liste des demandes
360 setting_issue_list_default_columns: Colonnes affichΓ©es par dΓ©faut sur la liste des demandes
360 setting_emails_footer: Pied-de-page des emails
361 setting_emails_footer: Pied-de-page des emails
361 setting_protocol: Protocole
362 setting_protocol: Protocole
@@ -133,6 +133,9 user_format:
133 format: symbol
133 format: symbol
134 cross_project_issue_relations:
134 cross_project_issue_relations:
135 default: 0
135 default: 0
136 # Enables subtasks to be in other projects
137 cross_project_subtasks:
138 default: 'tree'
136 issue_group_assignment:
139 issue_group_assignment:
137 default: 0
140 default: 0
138 default_issue_start_date_to_creation_date:
141 default_issue_start_date_to_creation_date:
@@ -41,4 +41,7 projects_trackers_013:
41 projects_trackers_014:
41 projects_trackers_014:
42 project_id: 1
42 project_id: 1
43 tracker_id: 3
43 tracker_id: 3
44 projects_trackers_015:
45 project_id: 6
46 tracker_id: 1
44 No newline at end of file
47
@@ -156,6 +156,14 class ActiveSupport::TestCase
156 hs
156 hs
157 end
157 end
158
158
159 def assert_save(object)
160 saved = object.save
161 message = "#{object.class} could not be saved"
162 errors = object.errors.full_messages.map {|m| "- #{m}"}
163 message << ":\n#{errors.join("\n")}" if errors.any?
164 assert_equal true, saved, message
165 end
166
159 def assert_error_tag(options={})
167 def assert_error_tag(options={})
160 assert_tag({:attributes => { :id => 'errorExplanation' }}.merge(options))
168 assert_tag({:attributes => { :id => 'errorExplanation' }}.merge(options))
161 end
169 end
@@ -49,7 +49,15 class IssueNestedSetTest < ActiveSupport::TestCase
49 assert_equal [parent.id, parent.id, 2, 3], [child.root_id, child.parent_id, child.lft, child.rgt]
49 assert_equal [parent.id, parent.id, 2, 3], [child.root_id, child.parent_id, child.lft, child.rgt]
50 end
50 end
51
51
52 def test_creating_a_child_in_different_project_should_not_validate
52 def test_creating_a_child_in_a_subproject_should_validate
53 issue = create_issue!
54 child = Issue.new(:project_id => 3, :tracker_id => 2, :author_id => 1,
55 :subject => 'child', :parent_issue_id => issue.id)
56 assert_save child
57 assert_equal issue, child.reload.parent
58 end
59
60 def test_creating_a_child_in_an_invalid_project_should_not_validate
53 issue = create_issue!
61 issue = create_issue!
54 child = Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
62 child = Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
55 :subject => 'child', :parent_issue_id => issue.id)
63 :subject => 'child', :parent_issue_id => issue.id)
@@ -906,6 +906,24 class IssueTest < ActiveSupport::TestCase
906 assert_equal 7, issue.fixed_version_id
906 assert_equal 7, issue.fixed_version_id
907 end
907 end
908
908
909 def test_move_to_another_project_should_keep_parent_if_valid
910 issue = Issue.find(1)
911 issue.update_attribute(:parent_issue_id, 2)
912 issue.project = Project.find(3)
913 assert issue.save
914 issue.reload
915 assert_equal 2, issue.parent_id
916 end
917
918 def test_move_to_another_project_should_clear_parent_if_not_valid
919 issue = Issue.find(1)
920 issue.update_attribute(:parent_issue_id, 2)
921 issue.project = Project.find(2)
922 assert issue.save
923 issue.reload
924 assert_nil issue.parent_id
925 end
926
909 def test_move_to_another_project_with_disabled_tracker
927 def test_move_to_another_project_with_disabled_tracker
910 issue = Issue.find(1)
928 issue = Issue.find(1)
911 target = Project.find(2)
929 target = Project.find(2)
@@ -996,6 +1014,48 class IssueTest < ActiveSupport::TestCase
996 end
1014 end
997 end
1015 end
998
1016
1017 def test_valid_parent_project
1018 issue = Issue.find(1)
1019 issue_in_same_project = Issue.find(2)
1020 issue_in_child_project = Issue.find(5)
1021 issue_in_grandchild_project = Issue.generate!(:project_id => 6, :tracker_id => 1)
1022 issue_in_other_child_project = Issue.find(6)
1023 issue_in_different_tree = Issue.find(4)
1024
1025 with_settings :cross_project_subtasks => '' do
1026 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1027 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1028 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1029 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1030 end
1031
1032 with_settings :cross_project_subtasks => 'system' do
1033 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1034 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1035 assert_equal true, issue.valid_parent_project?(issue_in_different_tree)
1036 end
1037
1038 with_settings :cross_project_subtasks => 'tree' do
1039 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1040 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1041 assert_equal true, issue.valid_parent_project?(issue_in_grandchild_project)
1042 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1043
1044 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_same_project)
1045 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1046 end
1047
1048 with_settings :cross_project_subtasks => 'descendants' do
1049 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1050 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1051 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1052 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1053
1054 assert_equal true, issue_in_child_project.valid_parent_project?(issue)
1055 assert_equal false, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1056 end
1057 end
1058
999 def test_recipients_should_include_previous_assignee
1059 def test_recipients_should_include_previous_assignee
1000 user = User.find(3)
1060 user = User.find(3)
1001 user.members.update_all ["mail_notification = ?", false]
1061 user.members.update_all ["mail_notification = ?", false]
General Comments 0
You need to be logged in to leave comments. Login now