sterrym/smithycms

View on GitHub
app/models/smithy/page.rb

Summary

Maintainability
A
25 mins
Test Coverage
module Smithy
  class Page < ActiveRecord::Base
    validates_presence_of :template, :title
    validate :validate_one_root
    validate :validate_exclusion_of_reserved_words
    validate :validate_page_to_copy_exists

    belongs_to :template, inverse_of: :pages
    has_many :containers, :through => :template
    has_many :contents, :class_name => "PageContent"

    acts_as_nested_set :dependent => :destroy
    extend FriendlyId
    friendly_id :title, :use => [:slugged, :scoped], :slug_column => 'path', :scope => :parent_id

    before_save :build_permalink
    before_save :set_published_at

    accepts_nested_attributes_for :contents, :reject_if => lambda {|a| a['label'].blank? || a['container'].blank? || a['content_block_type'].blank? }, :allow_destroy => true

    scope :included_in_navigation, -> { where("show_in_navigation=? AND published_at <= ?", true, Time.now) }
    scope :published, -> { where('published_at <= ?', Time.now) }

    attr_accessor :publish, :copy_content_from
    attr_reader :liquid_context

    def self.page_selector_options
      items = Array(self.roots)
      result = []
      items.each do |root|
        result += Page.associate_parents(root.self_and_descendants).map do |i|
          ["#{'-' * i.level} #{i.title}", i.url]
        end.compact
      end
      result
    end

    def container?(container_name)
      containers.where(:name => container_name).count > 0
    end

    def contents_for_container_name(container_name)
      self.contents.publishable.for_container(container_name)
    end

    def duplicate_content_from(page_id)
      page_id = page_id.id if page_id.is_a?(Smithy::Page)
      page = Page.find(page_id)
      self.contents = page.contents.map(&:amoeba_dup)
      self.contents.each do |content|
        content.content_block = content.content_block.amoeba_dup
      end
      self
    end

    def normalize_friendly_id(value) # normalize_friendly_id overrides the default creator for friendly_id
      return "/" if self.parent.blank?
      value = self.permalink? ? self.permalink.parameterize : value.to_s.parameterize
      [(self.parent.present? && !self.parent.root? ? self.parent.path : nil), value].join('/').downcase
    end

    def should_generate_new_friendly_id?
      title_changed? || permalink_changed?
    end

    def published?
      self.published_at?
    end

    def render(liquid_context)
      @liquid_context = liquid_context
      self.template.liquid_template.render(liquid_context)
    end

    def render_container(container_name)
      return '' unless container?(container_name)
      Rails.cache.fetch(self.container_cache_key(container_name)) do
        self.contents_for_container_name(container_name).map{|c| c.render(liquid_context) }.join("\n\n")
      end
    end

    def shallow_copy
      self.dup.tap do |p|
        p.title << " (Copy)"
        p.permalink.clear
        p.browser_title.clear
        p.keywords.clear
        p.description.clear
      end
    end

    def site
      @site ||= Smithy::Site.instance
    end

    def to_liquid
      Smithy::Liquid::Drops::Page.new(self)
    end

    def url
      self.external_link.present? ? self.external_link : self.path
    end

    protected
      def container_cache_key(container_name)
        # fetch the most recently adjusted content and add the updated_at timestamp to the cache_key
        content_last_updated = if self.contents_for_container_name(container_name).count > 0
          self.contents_for_container_name(container_name).order(nil).order('updated_at DESC').first.updated_at
        else
          self.updated_at
        end
        "#{self.cache_key}/#{container_name}-container/#{content_last_updated.utc.to_s(cache_timestamp_format)}"
      end

    private
      def build_permalink
        self.permalink = self.root? ? title.parameterize : path.split('/').last unless self.permalink?
      end

      def set_published_at
        self.published_at = Time.now if self.publish.present?
        self.published_at = nil if self.publish == false
      end

      def validate_page_to_copy_exists
        errors.add(:copy_content_from, "this page could not be found. Please choose another and try again.") if self.copy_content_from.present? && Page.find_by(id: self.copy_content_from).blank?
      end

      def validate_exclusion_of_reserved_words
        reserved = %w(index new edit session login logout users smithy)
        errors.add(:title, "cannot contain reserved words (#{reserved.join(', ')})") if reserved.include?(self.title.to_s.parameterize)
      end

      def validate_one_root
        errors.add(:parent_id, 'must have a parent') if self.class.root && self.class.root != self && self.parent_id.blank?
      end

  end
end