require 'awesome_nested_set' # TODO Изменять slug только в том случае, когда он был изменен вручную module Activa class Page < ActiveRecord::Base MENU_TYPES = %w(none neighbours children) attr_accessible :title, :article, :menu_type, :slug, :parent_id # see https://github.com/collectiveidea/awesome_nested_set acts_as_nested_set # see https://github.com/rsl/stringex acts_as_url :title, :url_attribute => :slug, :sync_url => false, # we update this manually :only_when_blank => true validates :title, :presence => true validates :article, :presence => true validates :slug, :format => { :with => /^[0-9a-z_-]+$/i }, :allow_blank => true validates :menu_type, :inclusion => { :in => MENU_TYPES } default_scope order('lft') before_save :set_default_values # Если создаётся страница без указанного родителя, # то автоматически назначаем родителем корневую страницу # Но только если текущая страница не корневая! before_save do if parent_id.blank? && Activa::Page.count > 0 if (new_record? && Activa::Page.has_root?) || (persisted? && self.id != Page.root.id) self.parent_id = Page.root.id end end end # Перед сохранением вычисляем uri и slug. # Для страниц, кроме корневой, slug вычисляется # автоматически при помощи Stringex, но только когда title пуст. before_save do if Activa::Page.has_root? && title.blank? self.uri = generate_uri end unless Activa::Page.has_root? self.slug = 'home' self.uri = '/' end end # Если slug был изменен и при этом заголовок страницы не пуст # и страница не является корневой, перегенерируем uri before_save do if slug_changed? && Activa::Page.has_root? && !title.blank? self.uri = generate_uri end end after_save do # Если страница имеет потомков, то нужно перегенерировать # uri и для них if children.size > 0 && !root? children.each { |child| child.regenerate_uri! } end end set_callback :move, :after do if Activa::Page.has_root? update_attribute(:uri, generate_uri) end end scope :published, where(:state => :published) scope :drafts, where(:state => :draft) def publish(params = nil) self.attributes = params if params self.state = :published save end def save_as_draft self.state = :draft save end def update_as_draft(params) self.attributes = params save_as_draft end # Retrieve list of all potential parents pages for current page def potential_parents new_record? ? Activa::Page.published : Activa::Page.published.where("id != ?", id) end def regenerate_uri! update_attribute(:uri, generate_uri) end def path root? ? slug : Activa::Page.normalize_path(uri) end def inner_pages children.select([:id, :parent_id, :title, :uri, :slug]) end def neighbours self_and_siblings.select([:id, :parent_id, :title, :uri, :slug]) end def draft? state == 'draft' end def published? state == 'published' end def to_s title end # Singleton methods class << self def has_root? unscoped.count > 0 end def retrieve(uri) uri = Activa::Page.normalize_uri(uri) uri = '/' if uri == '/home' Activa::Page.published.find_by_uri(Activa::Page.normalize_uri(uri)) end def normalize_uri(uri) "/#{uri}".sub(/\/+/, '/') end def normalize_path(path) path.sub(/^\/+/, '') end # Move node as a neighbour of child of another node def move(firstNodeId, secondNodeId, nesting) firstNode = find(firstNodeId) secondNode = find(secondNodeId) if (nesting.to_i == 0) firstNode.move_to_right_of(secondNode) else firstNode.move_to_child_of(secondNode) end return true end end private # После переноса узла запускается этот метод и успешно # генерирует URI (на данный момент). def generate_uri slices = (ancestors.reject {|x| x.root? }).map(&:slug) slices << slug if Activa::Page.has_root? "/#{slices.join('/')}".gsub(/^\/+/, '/') end def set_default_values self.menu_type ||= 'none' end end end