denny/ShinyCMS-ruby

View on GitHub
plugins/ShinyPages/app/controllers/shiny_pages/pages_controller.rb

Summary

Maintainability
A
0 mins
Test Coverage
D
60%
# frozen_string_literal: true

# ShinyPages plugin for ShinyCMS ~ https://shinycms.org
#
# Copyright 2009-2024 Denny de la Haye ~ https://denny.me
#
# ShinyCMS is free software; you can redistribute it and/or modify it under the terms of the GPL (version 2 or later)

module ShinyPages
  # Main site controller - ShinyPages plugin for ShinyCMS
  class PagesController < ApplicationController
    include ShinyCMS::MainSiteControllerBase

    include ShinyPages::MainSiteHelper

    before_action :enforce_html_format

    # Handle requests for the root page
    # /  (or /pages)
    def index
      @page = default_page

      return show_page if @page

      render inline: <<~HTML
        <p>This site does not have any content yet. Please try again later.
        </p>
        <p><i>
          Powered by <a href="https://shinycms.org/">ShinyCMS<a/>
          (<a href="https://shinycms.org/ruby">Ruby version</a>)
        </i></p>
      HTML
    end

    # Figure out whether we're at top level or going deeper
    def show
      path_parts = params[ :path ].split '/'

      if path_parts.size == 1
        show_top_level( path_parts.first )
      else
        show_in_section( path_parts )
      end
    end

    private

    # Handle requests with a single-part path - e.g. /foo
    def show_top_level( slug )
      return if show_top_level_page( slug )
      return if show_top_level_section( slug )

      raise ActiveRecord::RecordNotFound, 'Page not found'
    end

    def show_top_level_page( slug )
      @page = find_top_level_page( slug )
      return unless @page

      show_page
      true
    end

    def show_top_level_section( slug )
      @page = find_top_level_section( slug )&.default_page
      return unless @page

      show_page
      true
    end

    # Handle requests with a multi-part path - e.g. /foo/bar, /foo/bar/baz, etc
    def show_in_section( path_parts )
      slug = path_parts.pop
      section = traverse_path( path_parts, top_level_sections )

      @page = page_for_last_slug( section, slug )
      show_page && return if @page

      raise ActiveRecord::RecordNotFound, 'Page not found'
    end

    # Render the page with the appropriate template
    def show_page
      if @page.template.file_exists?
        render template: "shiny_pages/pages/#{@page.template.filename}", locals: @page.elements_hash
      else
        render status: :failed_dependency, inline: I18n.t( 'shiny_pages.page.template_file_missing' )
      end
    end

    # Find the correct section to look for the specified (or default) page in
    def traverse_path( path_parts, sections )
      slug = path_parts.shift
      section = sections&.find_by( slug: slug )

      return section if path_parts.empty? || section.nil?

      traverse_path( path_parts, section.sections )
    end

    def page_for_last_slug( section, slug )
      return if section.blank?

      last_slug_matches_page( section, slug ) || last_slug_matches_section( section, slug )
    end

    def last_slug_matches_page( section, slug )
      return if section.pages.blank?

      section.pages.with_elements.find_by( slug: slug )
    end

    def last_slug_matches_section( section, slug )
      return if section.sections.blank?

      section.sections.find_by( slug: slug )&.default_page
    end

    def enforce_html_format
      request.format = :html
    end
  end
end