#!/usr/bin/env ruby require 'loggability' require 'pathname' require 'redcloth' require 'mail' require 'strelka/app' require 'strelka/mixins' require 'strelka/cms' class Strelka::CMS::ContentManager < Strelka::App extend Loggability, Configurability, Strelka::MethodUtilities # Loggability API -- log to Strelka::CMS's logger log_to :strelka_cms # Configurability API -- use values from the 'cms' section of the config config_key :content_manager # Strelka App ID ID = 'content-manager' # The default directory to scan for pages DEFAULT_CONFIG = { pageroot: Pathname( __FILE__ ).dirname.parent.parent.parent + 'public' } # Pattern for untainting the page path PAGE_PATH_PATTERN = %r{ \A (?(?: [\w\-/] # Allow word characters, slashes, and hyphens | \.(?!\.) # or periods not followed by another period )*) (?:\.(?html|page))? # optional suffix \z }x ## # The configured catalog root singleton_attr_accessor :pageroot ## # The default content-type default_type 'text/html' # Strelka App setup plugin :templating layout 'layout.tmpl' templates \ page: 'page.tmpl' # Handle errors using a templated view instead of the default text plugin :fancyerrors ### Configurability API -- configure the CMS class once the config is loaded. def self::configure( config=nil ) if config && config.member?( :pageroot ) self.log.debug "Config is: %p" % [ config ] self.pageroot = Pathname( config.pageroot ) else self.pageroot = DEFAULT_CONFIG[:pageroot] end end ### Create a new instance of the CMS handler application. def initialize( * ) super @catalog = Strelka::CMS::PageCatalog.new( self.class.pageroot ) end # # Handler Routes # plugin :routing # / # -> /.page # -> /index.page # /foo/ # -> /foo.page # -> /foo/index.page # /foo/project # -> /foo/project.page # -> /foo/project/index.page # -> /foo/project.html (literal) # -> /foo/project/index.html (literal) get do |request| unless path = request.app_path[ PAGE_PATH_PATTERN, :path ] self.log.error "Invalid app_path: %p" % [ request.app_path ] finish_with HTTP::NOT_FOUND end # Force the path to be relative and clean it up path = Pathname( path.gsub(%r{\A\.?/+|/(?=/)|/+\z}, '') ) page = nil # Try to find a matching .page file self.log.debug "Page path is: %p" % [ path ] if path.to_s.empty? subcat = @catalog.matching_pattern( 'index.page' ) page = subcat.first else subcat = @catalog.relative_to( path.dirname ). matching_pattern( "#{path.basename(path.extname)}{.page,/index.page}" ) page = subcat.first end # If there was a matching .page file, use that if page self.log.debug " found page: %p" % [ page ] return self.page_response( page ) end # Try a literal HTML page htmlpage = self.class.pageroot + path.sub_ext( '.html' ) if htmlpage.exist? return self.raw_response( request, htmlpage ) end # Give up self.log.error "No pages matching %p" % [ path ] finish_with HTTP::NOT_FOUND end ### Package up the specified +page+ in the page template and return it. def page_response( page ) tmpl = self.template( :page ) tmpl.page = page return tmpl end ### Package up the page at the specified +path+ in the response and return it. def raw_response( request, pagepath ) response = request.response response.body = pagepath.open( 'r', encoding: 'utf-8' ) return response end end # class Strelka::CMS::ContentManager Encoding.default_internal = Encoding::UTF_8 Strelka::CMS::ContentManager.run if __FILE__ == $0