AlchemyCMS/alchemy_cms

View on GitHub
app/serializers/alchemy/page_tree_serializer.rb

Summary

Maintainability
B
5 hrs
Test Coverage
A
97%
# frozen_string_literal: true

module Alchemy
  class PageTreeSerializer < BaseSerializer
    def attributes
      {"pages" => nil}
    end

    def pages
      tree = []
      path = [{id: object.parent_id, children: tree}]
      page_list = object.self_and_descendants.includes(:public_version, {language: :site})
      base_level = object.level - 1
      # Load folded pages in advance
      folded_user_pages = FoldedPage.folded_for_user(opts[:user]).pluck(:page_id)
      folded_depth = Float::INFINITY

      page_list.each_with_index do |page, i|
        has_children = page_list[i + 1] && page_list[i + 1].parent_id == page.id
        folded = has_children && folded_user_pages.include?(page.id)

        if page.depth > folded_depth
          next
        else
          folded_depth = Float::INFINITY
        end

        # If this page is folded, skip all pages that are on a higher level (further down the tree).
        if folded && !opts[:full]
          folded_depth = page.depth
        end

        if page.parent_id != path.last[:id]
          if path.map { |o| o[:id] }.include?(page.parent_id) # Lower level
            path.pop while path.last[:id] != page.parent_id
          else # One level up
            path << path.last[:children].last
          end
        end

        level = path.count + base_level

        path.last[:children] << page_hash(page, level, folded)
      end

      tree
    end

    protected

    def page_hash(page, level, folded)
      p_hash = {
        id: page.id,
        name: page.name,
        public: page.public?,
        restricted: page.restricted?,
        page_layout: page.page_layout,
        slug: page.slug,
        urlname: page.urlname,
        url_path: page.url_path,
        level: level,
        root: page.root?,
        root_or_leaf: page.root? || page.leaf?,
        children: []
      }

      if opts[:elements]
        p_hash.update(elements: ActiveModel::Serializer::CollectionSerializer.new(page_elements(page)))
      end

      if opts[:ability].can?(:index, :alchemy_admin_pages)
        p_hash.merge({
          definition_missing: page.definition.blank?,
          folded: folded,
          locked: page.locked?,
          locked_notice: page.locked? ? Alchemy.t("This page is locked", name: page.locker_name) : nil,
          permissions: page_permissions(page, opts[:ability]),
          status_titles: page_status_titles(page)
        })
      else
        p_hash
      end
    end

    def page_elements(page)
      elements = page.public_version&.elements || Alchemy::Element.none
      if opts[:elements] == "true"
        elements
      else
        elements.named(opts[:elements].split(",") || [])
      end
    end

    def page_permissions(page, ability)
      {
        info: ability.can?(:info, page),
        configure: ability.can?(:configure, page),
        copy: ability.can?(:copy, page),
        destroy: ability.can?(:destroy, page),
        create: ability.can?(:create, Alchemy::Page),
        edit_content: ability.can?(:edit_content, page)
      }
    end

    def page_status_titles(page)
      {
        public: page.status_title(:public),
        restricted: page.status_title(:restricted),
        locked: page.status_title(:locked)
      }
    end
  end
end