Skip to content
Snippets Groups Projects
page.rb 7.73 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
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             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
  # kind was replaced by type in January 2023
  self.ignored_columns = %w(path kind)
Arnaud Levy's avatar
Arnaud Levy committed
  include AsDirectObject
  include Contentful # TODO L10N : To remove
  include Filterable
pabois's avatar
pabois committed
  include Sanitizable
  include Shareable # TODO L10N : To remove
  include Localizable
Arnaud Levy's avatar
Arnaud Levy committed
  include WithAutomaticMenus
  include WithBlobs # TODO L10N : To remove
  include WithDuplication # TODO L10N : To adjust
  include WithFeaturedImage # TODO L10N : To remove
pabois's avatar
pabois committed
  include WithMenuItemTarget
  # TODO L10N : To adjust (WithType)
  include WithSpecialPage # WithSpecialPage can set default publication status, so must be included before WithPublication
Arnaud Levy's avatar
Arnaud Levy committed
  include WithPosition # Scope :ordered must override WithPublication
  include WithTree
Arnaud Levy's avatar
Arnaud Levy committed
  include WithUniversity
pabois's avatar
pabois committed

  has_summernote :text # TODO: Remove text attribute

  # TODO L10N : remove after migrations
  has_many  :permalinks,
            class_name: "Communication::Website::Permalink",
            as: :about,
            dependent: :destroy

Arnaud Levy's avatar
Arnaud Levy committed
  belongs_to :parent,
             class_name: 'Communication::Website::Page',
             optional: true
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
pabois's avatar
pabois committed

  after_save :touch_elements_if_special_page_in_hierarchy

  scope :latest_in, -> (language) { published_now_in(language).order("communication_website_page_localizations.updated_at DESC").limit(5) }

  scope :ordered_by_title, -> (language) {
    localization_title_select = <<-SQL
      COALESCE(
        MAX(CASE WHEN localizations.language_id = '#{language.id}' THEN TRIM(LOWER(UNACCENT(localizations.title))) END),
        MAX(TRIM(LOWER(UNACCENT(localizations.title)))) FILTER (WHERE localizations.rank = 1)
      ) AS localization_title
    SQL

    joins(sanitize_sql_array([<<-SQL
      LEFT JOIN (
        SELECT
          localizations.*,
          ROW_NUMBER() OVER(PARTITION BY localizations.about_id ORDER BY localizations.created_at ASC) as rank
        FROM
          communication_website_page_localizations as localizations
      ) localizations ON communication_website_pages.id = localizations.about_id
    SQL
    ]))
    .select("communication_website_pages.*", localization_title_select)
    .group("communication_website_pages.id")
    .order("localization_title ASC")
Arnaud Levy's avatar
Arnaud Levy committed
  }
  scope :for_search_term, -> (term, language) {
     joins(:localizations)
      .where(communication_website_page_localizations: { language_id: language.id })
      .where("
        unaccent(communication_website_page_localizations.meta_description) ILIKE unaccent(:term) OR
        unaccent(communication_website_page_localizations.summary) ILIKE unaccent(:term) OR
        unaccent(communication_website_page_localizations.title) ILIKE unaccent(:term)
      ", term: "%#{sanitize_sql_like(term)}%")
  }
  scope :for_published, -> (published, language) { 
    joins(:localizations)
      .where(communication_website_page_localizations: { language_id: language.id , published: published == 'true'})
  }
  scope :for_full_width, -> (full_width, language = nil) { where(full_width: full_width == 'true') }
Arnaud Levy's avatar
Arnaud Levy committed

Arnaud Levy's avatar
Arnaud Levy committed
  def dependencies
    localizations.in_languages(website.active_language_ids)
Arnaud Levy's avatar
Arnaud Levy committed
  end

  def references
Sébastien Gaya's avatar
Sébastien Gaya committed
    [parent] +
    website.menus.in_languages(website.active_language_ids) +
Arnaud Levy's avatar
Arnaud Levy committed
    abouts_with_page_block
  # 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_class
  # TODO L10N : to remove
  def translate_other_attachments(translation)
    translate_attachment(translation, :shared_image) if shared_image.attached?
  end

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
Sébastien Gaya's avatar
Sébastien Gaya committed
  def last_ordered_element
    website.pages.where(parent_id: parent_id).ordered.last
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