97 lines
3.0 KiB
Ruby
97 lines
3.0 KiB
Ruby
module Models
|
|
module Extensions
|
|
module Page
|
|
module Tree
|
|
|
|
extend ActiveSupport::Concern
|
|
|
|
included do
|
|
include Mongoid::Acts::Tree
|
|
|
|
## fields ##
|
|
field :position, :type => Integer
|
|
|
|
## behaviours ##
|
|
acts_as_tree :order => ['position', 'asc']
|
|
|
|
## callbacks ##
|
|
before_validate :reset_parent
|
|
before_save { |p| p.send(:write_attribute, :parent_id, nil) if p.parent_id.blank? }
|
|
before_save :change_parent
|
|
before_create { |p| p.send(:fix_position, false) }
|
|
before_create :add_to_list_bottom
|
|
before_destroy :remove_from_list
|
|
|
|
# Fixme (Didier L.): Instances methods are defined before the include itself
|
|
alias :ancestors :hacked_ancestors
|
|
alias :fix_position :hacked_fix_position
|
|
end
|
|
|
|
module InstanceMethods
|
|
|
|
def sort_children!(ids)
|
|
ids.each_with_index do |id, position|
|
|
child = self.children.detect { |p| p._id == id }
|
|
child.position = position
|
|
child.save
|
|
end
|
|
end
|
|
|
|
def parent=(owner) # missing in acts_as_tree
|
|
@_parent = owner
|
|
self.fix_position(false)
|
|
self.instance_variable_set :@_will_move, true
|
|
end
|
|
|
|
def hacked_ancestors
|
|
return [] if root?
|
|
self.class.find(self.path.clone << nil) # bug in mongoid (it does not handle array with one element)
|
|
end
|
|
|
|
protected
|
|
|
|
def change_parent
|
|
if self.parent_id_changed?
|
|
self.fix_position(false)
|
|
self.add_to_list_bottom
|
|
self.instance_variable_set :@_will_move, true
|
|
end
|
|
end
|
|
|
|
def hacked_fix_position(perform_save = true)
|
|
if parent.nil?
|
|
self.write_attribute parent_id_field, nil
|
|
self[path_field] = []
|
|
self[depth_field] = 0
|
|
else
|
|
self.write_attribute parent_id_field, parent._id
|
|
self[path_field] = parent[path_field] + [parent._id]
|
|
self[depth_field] = parent[depth_field] + 1
|
|
self.save if perform_save
|
|
end
|
|
end
|
|
|
|
def reset_parent
|
|
if self.parent_id_changed?
|
|
@_parent = nil
|
|
end
|
|
end
|
|
|
|
def add_to_list_bottom
|
|
self.position = (::Page.where(:_id.ne => self._id).and(:parent_id => self.parent_id).max(:position) || 0) + 1
|
|
end
|
|
|
|
def remove_from_list
|
|
return if (self.site rescue nil).nil?
|
|
|
|
::Page.where(:parent_id => self.parent_id).and(:position.gt => self.position).each do |p|
|
|
p.position -= 1
|
|
p.save
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end |