Skip to content
Snippets Groups Projects
page.rb 5.39 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
#  design_options           :jsonb
Arnaud Levy's avatar
Arnaud Levy committed
#  full_width               :boolean          default(FALSE)
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
#  created_at               :datetime         not null
#  updated_at               :datetime         not null
pabois's avatar
pabois committed
#  communication_website_id :uuid             not null, indexed
#  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)
#  index_communication_website_pages_on_parent_id                 (parent_id)
#  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_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
Sébastien Gaya's avatar
Sébastien Gaya committed
  include Duplicable
  include Filterable
Arnaud Levy's avatar
Arnaud Levy committed
  include Categorizable # Must be loaded after Filterable to be filtered by categories
  include Localizable
  include Orderable
  include Sanitizable
  include Searchable
Arnaud Levy's avatar
Arnaud Levy committed
  include WithAutomaticMenus
pabois's avatar
pabois committed
  include WithMenuItemTarget
Sébastien Gaya's avatar
Sébastien Gaya committed
  include WithOpenApi
  include WithSpecialPage # WithSpecialPage can set default publication status, so must be included before WithPublication
Arnaud Levy's avatar
Arnaud Levy committed
  include WithTree
Arnaud Levy's avatar
Arnaud Levy committed
  include WithUniversity
pabois's avatar
pabois committed

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) +
    categories
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
  # Pages do have a category, but we do not list all the existing pages categories
  def special_page_categories
    false
  end

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
Arnaud Levy's avatar
Arnaud Levy committed
  protected

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
Arnaud Levy's avatar
Arnaud Levy committed
    website.blocks.template_pages.collect(&:about)
Arnaud Levy's avatar
Arnaud Levy committed
  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