Skip to content
Snippets Groups Projects
page.rb 7.63 KiB
Newer Older
Arnaud Levy's avatar
Arnaud Levy committed
# == Schema Information
#
# Table name: communication_website_pages
#
#  id                       :uuid             not null, primary key
pabois's avatar
pabois committed
#  bodyclass                :string
pabois's avatar
pabois committed
#  breadcrumb_title         :string
Sébastien Gaya's avatar
Sébastien Gaya committed
#  featured_image_alt       :string
Arnaud Levy's avatar
Arnaud Levy committed
#  featured_image_credit    :text
Arnaud Levy's avatar
Arnaud Levy committed
#  full_width               :boolean          default(FALSE)
#  header_cta               :boolean          default(FALSE)
#  header_cta_label         :string
#  header_cta_url           :string
pabois's avatar
pabois committed
#  header_text              :text
#  kind                     :integer
pabois's avatar
pabois committed
#  meta_description         :text
Arnaud Levy's avatar
Arnaud Levy committed
#  migration_identifier     :string
Arnaud Levy's avatar
Arnaud Levy committed
#  position                 :integer          default(0), not null
Arnaud Levy's avatar
Arnaud Levy committed
#  published                :boolean          default(FALSE)
Arnaud Levy's avatar
Arnaud Levy committed
#  published_at             :datetime
pabois's avatar
pabois committed
#  slug                     :string           indexed
#  summary                  :text
Sébastien Gaya's avatar
Sébastien Gaya committed
#  text                     :text
Arnaud Levy's avatar
Arnaud Levy committed
#  title                    :string
Arnaud Levy's avatar
Arnaud Levy committed
#  created_at               :datetime         not null
#  updated_at               :datetime         not null
pabois's avatar
pabois committed
#  communication_website_id :uuid             not null, indexed
#  language_id              :uuid             not null, indexed
#  original_id              :uuid             indexed
pabois's avatar
pabois committed
#  parent_id                :uuid             indexed
#  university_id            :uuid             not null, indexed
Arnaud Levy's avatar
Arnaud Levy committed
#
# Indexes
#
#  index_communication_website_pages_on_communication_website_id  (communication_website_id)
pabois's avatar
pabois committed
#  index_communication_website_pages_on_language_id               (language_id)
#  index_communication_website_pages_on_original_id               (original_id)
Arnaud Levy's avatar
Arnaud Levy committed
#  index_communication_website_pages_on_parent_id                 (parent_id)
pabois's avatar
pabois committed
#  index_communication_website_pages_on_slug                      (slug)
Arnaud Levy's avatar
Arnaud Levy committed
#  index_communication_website_pages_on_university_id             (university_id)
#
# Foreign Keys
#
pabois's avatar
pabois committed
#  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
#  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
pabois's avatar
pabois committed
#  fk_rails_d208d15a73  (university_id => universities.id)
Arnaud Levy's avatar
Arnaud Levy committed
class Communication::Website::Page < ApplicationRecord
  # FIXME: Remove legacy column from db
pabois's avatar
pabois committed
  self.ignored_columns = %w(path)

Arnaud Levy's avatar
Arnaud Levy committed
  include AsDirectObject
Arnaud Levy's avatar
Arnaud Levy committed
  include Contentful
  include Permalinkable
pabois's avatar
pabois committed
  include Sanitizable
Arnaud Levy's avatar
Arnaud Levy committed
  include Shareable
  include Sluggable # We override slug_unavailable? method (and set_slug and skip_slug_validation? in Page::Home)
  include Translatable
Arnaud Levy's avatar
Arnaud Levy committed
  include WithAccessibility
Arnaud Levy's avatar
Arnaud Levy committed
  include WithAutomaticMenus
pabois's avatar
pabois committed
  include WithBlobs
Arnaud Levy's avatar
Arnaud Levy committed
  include WithDuplication
  include WithFeaturedImage
pabois's avatar
pabois committed
  include WithMenuItemTarget
  include WithType # WithType can set default publication status, so must be included before WithPublication
Arnaud Levy's avatar
Arnaud Levy committed
  include WithPublication
  include WithPosition # Scope :ordered must override WithPublication
  include WithTree
  include WithPath # Must be included after Sluggable. WithPath overwrites the git_path method defined in WithWebsites
Arnaud Levy's avatar
Arnaud Levy committed
  include WithUniversity
pabois's avatar
pabois committed

  has_summernote :text # TODO: Remove text attribute

Arnaud Levy's avatar
Arnaud Levy committed
  belongs_to :parent,
             class_name: 'Communication::Website::Page',
             optional: true
  belongs_to :original,
             class_name: 'Communication::Website::Page',
             optional: true
  belongs_to :language
Arnaud Levy's avatar
Arnaud Levy committed
  has_many   :children,
             class_name: 'Communication::Website::Page',
pabois's avatar
pabois committed
             foreign_key: :parent_id,
pabois's avatar
pabois committed
             dependent: :destroy
Sébastien Gaya's avatar
Sébastien Gaya committed
  has_many   :translations,
             class_name: 'Communication::Website::Page',
             foreign_key: :original_id
pabois's avatar
pabois committed

  after_save :touch_elements_if_special_page_in_hierarchy

Arnaud Levy's avatar
Arnaud Levy committed
  validates :title, presence: true
  validates :header_cta_label, :header_cta_url, presence: true, if: :header_cta
Arnaud Levy's avatar
Arnaud Levy committed

  scope :latest, -> { published.order(updated_at: :desc).limit(5) }
pabois's avatar
pabois committed
  scope :published, -> { where(published: true) }
Arnaud Levy's avatar
Arnaud Levy committed
  scope :ordered_by_title, -> { order(:title) }
pabois's avatar
pabois committed

Arnaud Levy's avatar
Arnaud Levy committed
  scope :for_search_term, -> (term) {
    where("
      unaccent(communication_website_pages.meta_description) ILIKE unaccent(:term) OR
      unaccent(communication_website_pages.summary) ILIKE unaccent(:term) OR
      unaccent(communication_website_pages.title) ILIKE unaccent(:term)
    ", term: "%#{sanitize_sql_like(term)}%")
  }
  scope :for_published, -> (published) { where(published: published == 'true') }
  scope :for_full_width, -> (full_width) { where(full_width: full_width == 'true') }
Arnaud Levy's avatar
Arnaud Levy committed
  def template_static
    "admin/communication/websites/pages/static"
  end

Arnaud Levy's avatar
Arnaud Levy committed
  def dependencies
Arnaud Levy's avatar
Arnaud Levy committed
    calculated_dependencies = active_storage_blobs + contents_dependencies
    calculated_dependencies += [website.config_default_content_security_policy]
pabois's avatar
pabois committed
    # children are used only if there is no block to display
pabois's avatar
pabois committed
    calculated_dependencies += children unless blocks.published.any?
pabois's avatar
pabois committed
    calculated_dependencies
Arnaud Levy's avatar
Arnaud Levy committed
  end

  def references
Sébastien Gaya's avatar
Sébastien Gaya committed
    [parent] +
Sébastien Gaya's avatar
Sébastien Gaya committed
    website.menus +
Arnaud Levy's avatar
Arnaud Levy committed
    abouts_with_page_block
Arnaud Levy's avatar
Arnaud Levy committed
  def best_title
    breadcrumb_title.blank? ? title : breadcrumb_title
  end

Arnaud Levy's avatar
Arnaud Levy committed
  def to_s
Sébastien Gaya's avatar
Sébastien Gaya committed
    "#{title}"
Arnaud Levy's avatar
Arnaud Levy committed
  end

  # La page actuelle a les bodyclass classe1 et classe2 ("classe1 classe2")
  # Les différents ancêtres ont les classes home, bodyclass et secondclass
  # -> "page-classe1 page-classe2 ancestor-home ancestor-bodyclass ancestor-secondclass"
pabois's avatar
pabois committed
  def best_bodyclass
    classes = []
    classes += add_prefix_to_classes(bodyclass.split(' '), 'page') unless bodyclass.blank?
    classes += add_prefix_to_classes(ancestor_classes, 'ancestor') unless ancestor_classes.blank?
    classes.join(' ')
Sébastien Gaya's avatar
Sébastien Gaya committed
  def siblings
Arnaud Levy's avatar
Arnaud Levy committed
    self.class.unscoped
              .where(parent: parent, university: university, website: website)
              .where.not(id: id)
Sébastien Gaya's avatar
Sébastien Gaya committed
  # Some special pages can override this method to allow explicit direct connections
  # Example: The Communication::Website::Page::Person special page allows to connect University::Person records directly.
  def self.direct_connection_permitted_about_type
    nil
Arnaud Levy's avatar
Arnaud Levy committed
  protected

  # ["class1", "class2"], "page" -> ["page-class1", "page-class2"]
  def add_prefix_to_classes(classes, prefix)
    classes.map { |single_class| "#{prefix}-#{single_class}" }
  end

  # ["class1", "class2", "class3 class4"] -> ["class1", "class2", "class3", "class4"]
  def ancestor_classes
    @ancestor_classes ||= ancestors.pluck(:bodyclass)
                                   .compact_blank
                                   .join(' ')
                                   .split(' ')
                                   .compact_blank
  def slug_unavailable?(slug)
    self.class.unscoped
              .where(communication_website_id: self.communication_website_id, language_id: language_id, slug: slug)
              .where.not(id: self.id)
              .exists?
  end

Arnaud Levy's avatar
Arnaud Levy committed
  def check_accessibility
Arnaud Levy's avatar
Arnaud Levy committed
    accessibility_merge_array blocks
Sébastien Gaya's avatar
Sébastien Gaya committed
  def last_ordered_element
Sébastien Gaya's avatar
Sébastien Gaya committed
    website.pages.where(parent_id: parent_id, language_id: language_id).ordered.last
Sébastien Gaya's avatar
Sébastien Gaya committed
  end

  def explicit_blob_ids
Arnaud Levy's avatar
Arnaud Levy committed
    super.concat [
      featured_image&.blob_id,
      shared_image&.blob_id
    ]
  end

  def inherited_blob_ids
    [best_featured_image&.blob_id]
  end
Arnaud Levy's avatar
Arnaud Levy committed

  def abouts_with_page_block
    website.blocks.pages.collect(&:about)
  end

  def touch_elements_if_special_page_in_hierarchy
    # We do not call touch as we don't want to trigger the sync on the connected objects
    descendants_and_self.each do |page|
      if page.type == 'Communication::Website::Page::Person'
        website.connected_people.update_all(updated_at: Time.zone.now)
      elsif page.type == 'Communication::Website::Page::Organization'
        website.connected_organizations.update_all(updated_at: Time.zone.now)
Arnaud Levy's avatar
Arnaud Levy committed
end