|
|
|
@ -109,6 +109,26 @@ class Issue < ActiveRecord::Base |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
# find all issues |
|
|
|
|
# * having set a parent_id where the root_id |
|
|
|
|
# 1) points to self |
|
|
|
|
# 2) points to an issue with a parent |
|
|
|
|
# 3) points to an issue having a different root_id |
|
|
|
|
# * having not set a parent_id but a root_id |
|
|
|
|
# This unfortunately does not find the issue with the id 3 in the following example |
|
|
|
|
# | id | parent_id | root_id | |
|
|
|
|
# | 1 | | 1 | |
|
|
|
|
# | 2 | 1 | 2 | |
|
|
|
|
# | 3 | 2 | 2 | |
|
|
|
|
# This would only be possible using recursive statements |
|
|
|
|
named_scope :invalid_root_ids, { :conditions => "(issues.parent_id IS NOT NULL AND " + |
|
|
|
|
"(issues.root_id = issues.id OR " + |
|
|
|
|
"(issues.root_id = parent_issues.id AND parent_issues.parent_id IS NOT NULL) OR " + |
|
|
|
|
"(issues.root_id != parent_issues.root_id))" + |
|
|
|
|
") OR " + |
|
|
|
|
"(issues.parent_id IS NULL AND issues.root_id != issues.id)", |
|
|
|
|
:joins => "LEFT OUTER JOIN issues parent_issues ON parent_issues.id = issues.parent_id" } |
|
|
|
|
|
|
|
|
|
before_create :default_assign |
|
|
|
|
before_save :close_duplicates, :update_done_ratio_from_issue_status |
|
|
|
|
after_save :reschedule_following_issues, :update_nested_set_attributes, :update_parent_attributes |
|
|
|
@ -772,6 +792,59 @@ class Issue < ActiveRecord::Base |
|
|
|
|
projects |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
# method from acts_as_nested_set |
|
|
|
|
def self.valid? |
|
|
|
|
super && invalid_root_ids.empty? |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def self.all_invalid |
|
|
|
|
(super + invalid_root_ids).uniq |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def self.rebuild_silently!(roots = nil) |
|
|
|
|
|
|
|
|
|
invalid_root_ids_to_fix = if roots.is_a? Array |
|
|
|
|
roots |
|
|
|
|
elsif roots.present? |
|
|
|
|
[roots] |
|
|
|
|
else |
|
|
|
|
[] |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
known_issue_parents = Hash.new do |hash, ancestor_id| |
|
|
|
|
hash[ancestor_id] = Issue.find_by_id(ancestor_id) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
fix_known_invalid_root_ids = lambda do |
|
|
|
|
issues = invalid_root_ids |
|
|
|
|
|
|
|
|
|
issues_roots = [] |
|
|
|
|
|
|
|
|
|
issues.each do |issue| |
|
|
|
|
# At this point we can not trust nested set methods as the root_id is invalid. |
|
|
|
|
# Therefore we trust the parent_issue_id to fetch all ancestors until we find the root |
|
|
|
|
ancestor = issue |
|
|
|
|
|
|
|
|
|
while ancestor.parent_issue_id do |
|
|
|
|
ancestor = known_issue_parents[ancestor.parent_issue_id] |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
issues_roots << ancestor |
|
|
|
|
|
|
|
|
|
if invalid_root_ids_to_fix.empty? || invalid_root_ids_to_fix.map(&:id).include?(ancestor.id) |
|
|
|
|
Issue.update_all({ :root_id => ancestor.id }, |
|
|
|
|
{ :id => issue.id }) |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
fix_known_invalid_root_ids.call unless (issues_roots.map(&:id) & invalid_root_ids_to_fix.map(&:id)).empty? |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
fix_known_invalid_root_ids.call |
|
|
|
|
|
|
|
|
|
super |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
private |
|
|
|
|
|
|
|
|
|
def update_nested_set_attributes |
|
|
|
|