locomotivecms/engine

View on GitHub
app/models/locomotive/concerns/page/layout.rb

Summary

Maintainability
A
1 hr
Test Coverage
module Locomotive
  module Concerns
    module Page
      module Layout

        extend ActiveSupport::Concern

        IS_LAYOUT_REGEX         = /^layouts($|\/)/o.freeze
        EXTENDS_REGEX           = /\{%\s+extends\s/o.freeze
        EXTENDS_PARENT_REGEX    = /\{%\s+extends\s"?parent"?\s%}/o.freeze
        EXTENDS_FULLPATH_REGEX  = /\{%\s+extends\s"?(\S+)"?\s%}/o.freeze
        BLOCK_REGEX             = /\{%\s+block\s/o.freeze

        included do

          ## fields ##
          field :is_layout,     type: Boolean, default: false
          field :allow_layout,  type: Boolean, default: ->{ new_record? }

          ## associations ##
          belongs_to  :layout, class_name: 'Locomotive::Page', inverse_of: :layout_children, validate: false
          has_many    :layout_children, class_name: 'Locomotive::Page', inverse_of: :layout

          ## callbacks ##
          before_validation :valid_allow_layout_consistency
          before_validation :set_raw_template_on_create, if: :new_record?
          before_validation :set_raw_template_on_update, unless: :new_record?

          ## validations ##
          validate :index_can_not_extend_parent

          ## scopes ##
          scope :layouts, -> { where(is_layout: true) }

        end

        def is_layout_or_related?
          !(self.fullpath =~ IS_LAYOUT_REGEX).nil?
        end

        def use_layout?
          !(self.raw_template =~ EXTENDS_REGEX).nil?
        end

        # The layout is also defined in the raw_template. The UI
        # needs it in order to select the layout in the layouts dropdown menu
        def find_layout
          return if !self.allow_layout? || self.layout_id

          if self.raw_template =~ EXTENDS_FULLPATH_REGEX
            # first in the current locale
            if (page = self.site.pages.fullpath($1).first).nil? && !self.site.is_default_locale?(::Mongoid::Fields::I18n.locale)
              # give it a try in the default locale too
              page = self.site.with_default_locale { self.site.pages.fullpath($1).first }
            end

            self.layout_id = page.try(:_id)
          end
        end

        private

        # Even if the allow_layout attribute is true in a first time, we need
        # to make sure the the raw_template includes only the "extends" liquid tag
        # without any other code.
        def valid_allow_layout_consistency
          if allow_layout && raw_template.present? && raw_template =~ EXTENDS_REGEX
            self.allow_layout = (raw_template =~ BLOCK_REGEX).nil?
          end
          true
        end

        def set_raw_template_on_create
          set_raw_template
          true
        end

        def set_raw_template_on_update
          set_raw_template if allow_layout? && layout_id_changed?
          true
        end

        def set_raw_template
          return unless site

          site.with_default_locale do
            if layout_id == 'parent'
              self.raw_template = '{% extends parent %}'
            elsif layout
              self.raw_template = %({% extends "#{self.layout.fullpath}" %})
            elsif raw_template.blank? && !index?
              self.raw_template = '{% extends parent %}'
            end
          end
          # needed it by Steam to get the template from the default locale
          site.each_locale(false) { self.raw_template = '' }
        end

        def index_can_not_extend_parent
          if index? && raw_template =~ EXTENDS_PARENT_REGEX
            self.errors.add(:layout_id, :index_can_not_extend_parent)
          end
        end

      end
    end
  end
end