Feature #6033 » 6033_subtasks_show_in_parent_history.patch
| app/helpers/issues_helper.rb | ||
|---|---|---|
| 547 | 547 |
value = "##{detail.value}" unless detail.value.blank?
|
| 548 | 548 |
old_value = "##{detail.old_value}" unless detail.old_value.blank?
|
| 549 | 549 | |
| 550 |
when 'child_id' |
|
| 551 |
label = l(:label_subtask) |
|
| 552 |
value = "##{detail.value}" unless detail.value.blank?
|
|
| 553 |
old_value = "##{detail.old_value}" unless detail.old_value.blank?
|
|
| 554 |
multiple = true |
|
| 555 | ||
| 550 | 556 |
when 'is_private' |
| 551 | 557 |
value = l(detail.value == "0" ? :general_text_No : :general_text_Yes) unless detail.value.blank? |
| 552 | 558 |
old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank? |
| app/models/issue.rb | ||
|---|---|---|
| 118 | 118 |
after_save :reschedule_following_issues, :update_nested_set_attributes, |
| 119 | 119 |
:update_parent_attributes, :delete_selected_attachments, :create_journal |
| 120 | 120 |
# Should be after_create but would be called before previous after_save callbacks |
| 121 |
after_save :after_create_from_copy |
|
| 122 |
after_destroy :update_parent_attributes |
|
| 121 |
after_save :after_create_from_copy, :create_parent_issue_journal
|
|
| 122 |
after_destroy :update_parent_attributes, :create_parent_issue_journal
|
|
| 123 | 123 |
after_create_commit :send_notification |
| 124 | 124 | |
| 125 | 125 |
# Returns a SQL conditions string used to find all issues visible by the specified user |
| ... | ... | |
| 1988 | 1988 |
end |
| 1989 | 1989 |
end |
| 1990 | 1990 | |
| 1991 |
def create_parent_issue_journal |
|
| 1992 |
return if persisted? && !saved_change_to_parent_id? |
|
| 1993 |
return if destroyed? && @without_nested_set_update |
|
| 1994 | ||
| 1995 |
child_id = self.id |
|
| 1996 |
old_parent_id, new_parent_id = |
|
| 1997 |
if persisted? |
|
| 1998 |
[parent_id_before_last_save, parent_id] |
|
| 1999 |
elsif destroyed? |
|
| 2000 |
[parent_id, nil] |
|
| 2001 |
else |
|
| 2002 |
[nil, parent_id] |
|
| 2003 |
end |
|
| 2004 | ||
| 2005 |
if old_parent_id.present? && old_parent_issue = Issue.visible.find_by_id(old_parent_id) |
|
| 2006 |
old_parent_issue.init_journal(User.current) |
|
| 2007 |
old_parent_issue.current_journal.__send__(:add_attribute_detail, 'child_id', child_id, nil) |
|
| 2008 |
old_parent_issue.save |
|
| 2009 |
end |
|
| 2010 |
if new_parent_id.present? && new_parent_issue = Issue.visible.find_by_id(new_parent_id) |
|
| 2011 |
new_parent_issue.init_journal(User.current) |
|
| 2012 |
new_parent_issue.current_journal.__send__(:add_attribute_detail, 'child_id', nil, child_id) |
|
| 2013 |
new_parent_issue.save |
|
| 2014 |
end |
|
| 2015 |
end |
|
| 2016 | ||
| 1991 | 2017 |
def send_notification |
| 1992 | 2018 |
if notify? && Setting.notified_events.include?('issue_added')
|
| 1993 | 2019 |
Mailer.deliver_issue_add(self) |
| config/locales/en.yml | ||
|---|---|---|
| 995 | 995 |
label_missing_api_access_key: Missing an API access key |
| 996 | 996 |
label_api_access_key_created_on: "API access key created %{value} ago"
|
| 997 | 997 |
label_profile: Profile |
| 998 |
label_subtask: Subtask |
|
| 998 | 999 |
label_subtask_plural: Subtasks |
| 999 | 1000 |
label_project_copy_notifications: Send email notifications during the project copy |
| 1000 | 1001 |
label_import_notifications: Send email notifications during the import |
| test/functional/issues_controller_test.rb | ||
|---|---|---|
| 2344 | 2344 |
end |
| 2345 | 2345 | |
| 2346 | 2346 |
def test_show_should_list_subtasks |
| 2347 |
Issue. |
|
| 2347 |
issue = Issue.
|
|
| 2348 | 2348 |
create!( |
| 2349 | 2349 |
:project_id => 1, :author_id => 1, :tracker_id => 1, |
| 2350 | 2350 |
:parent_issue_id => 1, :subject => 'Child Issue' |
| ... | ... | |
| 2354 | 2354 |
assert_select 'div#issue_tree' do |
| 2355 | 2355 |
assert_select 'td.subject', :text => /Child Issue/ |
| 2356 | 2356 |
end |
| 2357 |
assert_select 'div#tab-content-history' do |
|
| 2358 |
assert_select 'div[id=?]', "change-#{Issue.find(1).journals.last.id}" do
|
|
| 2359 |
assert_select 'ul.details', :text => "Subtask ##{issue.id} added"
|
|
| 2360 |
end |
|
| 2361 |
end |
|
| 2357 | 2362 |
end |
| 2358 | 2363 | |
| 2359 | 2364 |
def test_show_should_show_subtasks_stats |
| ... | ... | |
| 8199 | 8204 |
assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6)) |
| 8200 | 8205 |
end |
| 8201 | 8206 | |
| 8207 |
def test_destroy_child_issue |
|
| 8208 |
parent = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Parent Issue') |
|
| 8209 |
child = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Child Issue', :parent_issue_id => parent.id) |
|
| 8210 |
assert child.is_descendant_of?(parent.reload) |
|
| 8211 | ||
| 8212 |
@request.session[:user_id] = 2 |
|
| 8213 |
assert_difference 'Issue.count', -1 do |
|
| 8214 |
delete :destroy, :params => {:id => child.id}
|
|
| 8215 |
end |
|
| 8216 |
assert_response :found |
|
| 8217 |
assert_redirected_to :action => 'index', :project_id => 'ecookbook' |
|
| 8218 | ||
| 8219 |
parent.reload |
|
| 8220 |
assert_equal 2, parent.journals.count |
|
| 8221 | ||
| 8222 |
get :show, :params => {:id => parent.id}
|
|
| 8223 |
assert_response :success |
|
| 8224 | ||
| 8225 |
assert_select 'div#tab-content-history' do |
|
| 8226 |
assert_select 'div[id=?]', "change-#{parent.journals.last.id}" do
|
|
| 8227 |
assert_select 'ul.details', :text => "Subtask deleted (##{child.id})"
|
|
| 8228 |
end |
|
| 8229 |
end |
|
| 8230 |
end |
|
| 8231 | ||
| 8202 | 8232 |
def test_destroy_parent_and_child_issues |
| 8203 | 8233 |
parent = Issue.create!(:project_id => 1, :author_id => 1, |
| 8204 | 8234 |
:tracker_id => 1, :subject => 'Parent Issue') |
| test/unit/issue_nested_set_test.rb | ||
|---|---|---|
| 49 | 49 |
def test_create_child_issue |
| 50 | 50 |
lft = new_issue_lft |
| 51 | 51 |
parent = Issue.generate! |
| 52 |
child = parent.generate_child! |
|
| 52 |
child = nil |
|
| 53 |
assert_difference 'Journal.count', 1 do |
|
| 54 |
child = parent.generate_child! |
|
| 55 |
end |
|
| 53 | 56 |
parent.reload |
| 54 | 57 |
child.reload |
| 55 | 58 |
assert_equal [parent.id, nil, lft, lft + 3], [parent.root_id, parent.parent_id, parent.lft, parent.rgt] |
| ... | ... | |
| 58 | 61 | |
| 59 | 62 |
def test_creating_a_child_in_a_subproject_should_validate |
| 60 | 63 |
issue = Issue.generate! |
| 61 |
child = Issue.new(:project_id => 3, :tracker_id => 2, :author_id => 1, |
|
| 62 |
:subject => 'child', :parent_issue_id => issue.id) |
|
| 63 |
assert_save child |
|
| 64 |
child = nil |
|
| 65 |
assert_difference 'Journal.count', 1 do |
|
| 66 |
child = Issue.new(:project_id => 3, :tracker_id => 2, :author_id => 1, |
|
| 67 |
:subject => 'child', :parent_issue_id => issue.id) |
|
| 68 |
assert_save child |
|
| 69 |
end |
|
| 64 | 70 |
assert_equal issue, child.reload.parent |
| 65 | 71 |
end |
| 66 | 72 | |
| 67 | 73 |
def test_creating_a_child_in_an_invalid_project_should_not_validate |
| 68 | 74 |
issue = Issue.generate! |
| 69 |
child = Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1, |
|
| 70 |
:subject => 'child', :parent_issue_id => issue.id) |
|
| 71 |
assert !child.save |
|
| 75 |
child = nil |
|
| 76 |
assert_no_difference 'Journal.count' do |
|
| 77 |
child = Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1, |
|
| 78 |
:subject => 'child', :parent_issue_id => issue.id) |
|
| 79 |
assert !child.save |
|
| 80 |
end |
|
| 72 | 81 |
assert_not_equal [], child.errors[:parent_issue_id] |
| 73 | 82 |
end |
| 74 | 83 | |
| ... | ... | |
| 77 | 86 |
parent1 = Issue.generate! |
| 78 | 87 |
parent2 = Issue.generate! |
| 79 | 88 |
child = parent1.generate_child! |
| 80 |
parent2.parent_issue_id = parent1.id |
|
| 81 |
parent2.save! |
|
| 89 |
assert_difference 'Journal.count', 2 do |
|
| 90 |
parent2.init_journal(User.find(2)) |
|
| 91 |
parent2.parent_issue_id = parent1.id |
|
| 92 |
parent2.save! |
|
| 93 |
end |
|
| 82 | 94 |
child.reload |
| 83 | 95 |
parent1.reload |
| 84 | 96 |
parent2.reload |
| ... | ... | |
| 94 | 106 |
parent2 = Issue.generate! |
| 95 | 107 |
lft3 = new_issue_lft |
| 96 | 108 |
child = parent1.generate_child! |
| 97 |
child.parent_issue_id = nil |
|
| 98 |
child.save! |
|
| 109 |
assert_difference 'Journal.count', 2 do |
|
| 110 |
child.init_journal(User.find(2)) |
|
| 111 |
child.parent_issue_id = nil |
|
| 112 |
child.save! |
|
| 113 |
end |
|
| 99 | 114 |
child.reload |
| 100 | 115 |
parent1.reload |
| 101 | 116 |
parent2.reload |
| ... | ... | |
| 110 | 125 |
lft2 = new_issue_lft |
| 111 | 126 |
parent2 = Issue.generate! |
| 112 | 127 |
child = parent1.generate_child! |
| 113 |
child.parent_issue_id = parent2.id |
|
| 114 |
child.save! |
|
| 128 |
assert_difference 'Journal.count', 3 do |
|
| 129 |
child.init_journal(User.find(2)) |
|
| 130 |
child.parent_issue_id = parent2.id |
|
| 131 |
child.save! |
|
| 132 |
end |
|
| 115 | 133 |
child.reload |
| 116 | 134 |
parent1.reload |
| 117 | 135 |
parent2.reload |
| ... | ... | |
| 154 | 172 |
grandchild = child.generate_child! |
| 155 | 173 |
lft4 = new_issue_lft |
| 156 | 174 |
child.reload |
| 157 |
child.project = Project.find(2) |
|
| 158 |
assert child.save |
|
| 175 |
assert_difference 'Journal.count', 2 do |
|
| 176 |
assert_difference 'JournalDetail.count', 3 do |
|
| 177 |
child.init_journal(User.find(2)) |
|
| 178 |
child.project = Project.find(2) |
|
| 179 |
assert child.save |
|
| 180 |
end |
|
| 181 |
end |
|
| 159 | 182 |
child.reload |
| 160 | 183 |
grandchild.reload |
| 161 | 184 |
parent1.reload |
| ... | ... | |
| 173 | 196 |
grandchild = child.generate_child! |
| 174 | 197 | |
| 175 | 198 |
child.reload |
| 176 |
child.parent_issue_id = grandchild.id |
|
| 177 |
assert !child.save |
|
| 199 |
assert_no_difference 'Journal.count' do |
|
| 200 |
child.init_journal(User.find(2)) |
|
| 201 |
child.parent_issue_id = grandchild.id |
|
| 202 |
assert !child.save |
|
| 203 |
end |
|
| 178 | 204 |
assert_not_equal [], child.errors[:parent_issue_id] |
| 179 | 205 |
end |
| 180 | 206 | |
| ... | ... | |
| 223 | 249 |
issue3.subject = 'child with journal' |
| 224 | 250 |
issue3.save! |
| 225 | 251 |
assert_difference 'Issue.count', -2 do |
| 226 |
assert_difference 'Journal.count', -1 do
|
|
| 227 |
assert_difference 'JournalDetail.count', -1 do
|
|
| 252 |
assert_difference 'Journal.count', -2 do
|
|
| 253 |
assert_difference 'JournalDetail.count', -2 do
|
|
| 228 | 254 |
Issue.find(issue2.id).destroy |
| 229 | 255 |
end |
| 230 | 256 |
end |
| ... | ... | |
| 244 | 270 |
child2 = issue.generate_child! |
| 245 | 271 |
issue.reload |
| 246 | 272 |
assert_equal [issue.id, lft1, lft1 + 5], [issue.root_id, issue.lft, issue.rgt] |
| 247 |
child2.reload.destroy |
|
| 273 |
assert_difference 'Journal.count', 1 do |
|
| 274 |
child2.reload.destroy |
|
| 275 |
end |
|
| 248 | 276 |
issue.reload |
| 249 | 277 |
assert_equal [issue.id, lft1, lft1 + 3], [issue.root_id, issue.lft, issue.rgt] |
| 250 | 278 |
end |
| ... | ... | |
| 255 | 283 |
parent.generate_child!(:start_date => 2.days.from_now) |
| 256 | 284 | |
| 257 | 285 |
assert_difference 'Issue.count', -3 do |
| 258 |
Issue.find(parent.id).destroy |
|
| 286 |
assert_difference 'Journal.count', -2 do |
|
| 287 |
Issue.find(parent.id).destroy |
|
| 288 |
end |
|
| 259 | 289 |
end |
| 260 | 290 |
end |
| 261 | 291 | |
| ... | ... | |
| 287 | 317 |
grandchild1 = child.generate_child! |
| 288 | 318 |
grandchild2 = child.generate_child! |
| 289 | 319 |
assert_difference 'Issue.count', -4 do |
| 290 |
Issue.find(issue.id).destroy |
|
| 320 |
assert_difference 'Journal.count', -2 do |
|
| 321 |
Issue.find(issue.id).destroy |
|
| 322 |
end |
|
| 291 | 323 |
parent.reload |
| 292 | 324 |
assert_equal [lft1, lft1 + 1], [parent.lft, parent.rgt] |
| 293 | 325 |
end |
| test/unit/issue_subtasking_test.rb | ||
|---|---|---|
| 277 | 277 |
with_settings :issue_done_ratio => 'issue_status' do |
| 278 | 278 |
status = IssueStatus.find(4) |
| 279 | 279 |
status.update_attribute :default_done_ratio, 50 |
| 280 |
child1.reload |
|
| 280 | 281 |
child1.update_attribute :status, status |
| 281 | 282 | |
| 282 | 283 |
assert_equal 50, child1.done_ratio |