--- redmine-2.3.0-ori/app/models/issue.rb 2014-09-18 17:11:51.388167720 +0200 +++ redmine-2.3.0/app/models/issue.rb 2014-09-18 17:28:21.651718421 +0200 @@ -18,6 +18,8 @@ class Issue < ActiveRecord::Base include Redmine::SafeAttributes include Redmine::Utils::DateCalculation + + cattr_accessor :skip_callbacks belongs_to :project belongs_to :tracker @@ -91,7 +93,7 @@ } before_create :default_assign - before_save :close_duplicates, :update_done_ratio_from_issue_status, :force_updated_on_change, :update_closed_on + before_save :close_duplicates, :update_mirrors, :update_mirrored, :update_done_ratio_from_issue_status, :force_updated_on_change, :update_closed_on, :unless => :skip_callbacks after_save {|issue| issue.send :after_project_change if !issue.id_changed? && issue.project_id_changed?} after_save :reschedule_following_issues, :update_nested_set_attributes, :update_parent_attributes, :create_journal # Should be after_create but would be called before previous after_save callbacks @@ -870,6 +872,16 @@ relations_to.select {|r| r.relation_type == IssueRelation::TYPE_DUPLICATES}.collect {|r| r.issue_from} end + # Returns an array of issues that mirror this one + def mirrors + relations_to.select {|r| r.relation_type == IssueRelation::TYPE_MIRRORED}.collect {|r| r.issue_from} + end + + # Returns an array of issues that mirror this one + def mirrored + relations_from.select {|r| r.relation_type == IssueRelation::TYPE_MIRRORED}.collect {|r| r.issue_to} + end + # Returns the due date or the target due date if any # Used on gantt chart def due_before @@ -1330,6 +1342,50 @@ end end end + + # Update mirrors + def update_mirrors + if status_id_changed? + logger.error "## #{id} UPDATE MIRROR ## #{relations_to}" + logger.error "# #{id} - Relation mirror : #{mirrors}" + mirrors.each do |mirror| + # Reload is need in case the duplicate was updated by a previous duplicate + mirror.reload + # Don't re-close it if it's already closed + logger.error "## status : #{self.status} mirror : #{mirror.status}" + next if mirror.closed? || mirror.status == self.status + # Same user and notes + if @current_journal + mirror.init_journal(@current_journal.user, @current_journal.notes) + end + Issue.skip_callbacks = true + mirror.update_attribute :status, self.status + Issue.skip_callbacks = false + end + end + end + + # Update mirrored + def update_mirrored + if status_id_changed? + logger.error "## #{id} UPDATE MIRRORED ## #{relations_from}" + logger.error "# #{id} - Relations mirrored : #{mirrored}" + mirrored.each do |mirrored| + # Reload is need in case the duplicate was updated by a previous duplicate + mirrored.reload + # Don't re-close it if it's already closed + logger.error "## status : #{self.status} mirrored : #{mirrored.status}" + next if mirrored.closed? || mirrored.status == self.status + # Same user and notes + if @current_journal + mirrored.init_journal(@current_journal.user, @current_journal.notes) + end + Issue.skip_callbacks = true + mirrored.update_attribute :status, self.status + Issue.skip_callbacks = false + end + end + end # Make sure updated_on is updated when adding a note and set updated_on now # so we can set closed_on with the same value on closing