@@ -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, : |
|
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, :n |
|
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 |
|
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_ |
|
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