# # class StaticPage # # represents a static website page. # # require 'property_set' require 'i18n' require 'pathname' require 'RedCloth' require 'rbst' require 'rdiscount' #require 'pandoc-ruby' #require 'iconv' class StaticPage attr_accessor :path, :children, :name, :file_path, :parent, :mount_point, :locales attr_accessor :props # type PropertySet ## ## CLASS METHODS ## def self.find(site, filter) if filter =~ /\// path = filter.split('/').map{|segment| segment.gsub(/[^0-9a-z_-]/, '')} page = site.pages[path.join('/')] if page return page else return site.pages[path.last] end else site.pages[filter] end end # # loads a directory, creating StaticPages from the directory structure, # yielding each StaticPage as it is created. # def scan(&block) Dir.chdir(file_path) do Dir.glob("*").each do |child_name| if File.directory?(child_name) child = StaticPage.new(self, child_name) yield child child.scan(&block) elsif is_simple_page?(child_name) child = StaticPage.new(self, child_name) yield child end end end end ## ## INSTANCE METHODS ## def initialize(parent, name, file_path=nil) @children = [] # set the @name and @suffix @suffix = File.extname(name) if @suffix.chars.any? @name = file_without_suffix(name) else @name = name @suffix = nil end # set @parent & @path if parent @parent = parent @mount_point = @parent.mount_point @parent.add_child(self) @path = [@parent.path, @name].flatten.compact else @path = [] end # set the @file_path if file_path @file_path = file_path elsif @parent && @parent.file_path @file_path = File.join(@parent.file_path, @name) else raise 'file path must be specified or in parent' end # discover supported locales @simple_page = !File.directory?(@file_path) #@locales = find_locales() # eval the property headers, if any @props = load_properties() end def add_child(page) @children << page end def all_children StaticPageArray.new(child_tree.flatten.compact) end # # e.g. /home/user/dev/leap-public-site/app/views/pages/about-us/contact # #def file_path # "#{@mount_point.directory}/#{@path.join('/')}" #end # # e.g. pages/about-us/contact/en # def template_path(locale=I18n.locale) if @simple_page "#{@mount_point.relative_directory}/#{@path.join('/')}" else "#{@mount_point.relative_directory}/#{@path.join('/')}/#{locale}" end end # # e.g. pages/about-us/contact/en # def absolute_template_path(locale=I18n.locale) if @simple_page "#{@mount_point.directory}/#{@path.join('/')}" else "#{@mount_point.directory}/#{@path.join('/')}/#{locale}" end end # # e.g. pages/about-us/contact/en.haml # #def source_path(locale=I18n.locale) # if @simple_page # # else # # end #end def inspect "<'#{@path.join('/')}' #{children.inspect}>" end # # title is tricky # # * for nav_title, default to title, then try to inherit. # * for title, default to nav_title, then try to inherit. # def title(locale=I18n.locale) title = props.get_var_without_inheritance(:title, locale) title ||= props.get_var_without_inheritance(:nav_title, locale) title ||= props.get_var(:title, locale) title ||= props.get_var(:nav_title, locale) title ||= I18n.t('pages.'+@name, :raise => false, :default => "Untitled", :locale => locale) return title end def nav_title(locale=I18n.locale) return_value = I18n.t('pages.'+@name, :raise => false, :default => "Untitled", :locale => locale) if return_value == "Untitled" property_title = props.get_var_without_inheritance(:nav_title, locale) property_title ||= props.get_var_without_inheritance(:title, locale) property_title ||= props.get_var(:nav_title, locale) property_title ||= props.get_var(:title, locale) return_value = property_title || return_value end return_value end def render_to_string(renderer) begin render_locale(renderer, I18n.locale) rescue ActionView::MissingTemplate, MissingTemplate => exc begin render_locale(renderer, DEFAULT_LOCALE) rescue Rails.logger.error "ERROR: could not file template path #{self.template_path}" raise exc end end end def id self.name end # # returns a child matching +name+, if any. # def child(name) children.detect {|child| child.name == name} end protected def child_tree [self, children.collect{|child| child.child_tree}] end private ## ## PROPERTIES ## # # scans the source content files for property headers in the form: # # @variable = 'x' # - @variable = 'x' # # (with or without leading hypen works) # # this text is extracted and evaluated as ruby to set properties. # def load_properties props = PropertySet.new(self) content_files.each do |content_file, locale| if File.extname(content_file) == '.haml' props.eval(File.read(content_file), locale) else headers = [] File.open(content_file) do |f| while (line = f.gets) =~ /^(- |)@\w/ if line !~ /^-/ line = '- ' + line end headers << line end end props.eval(headers.join("\n"), locale) end end return props end ## ## CONTENT FILES ## SUFFIXES = '(haml|md|markdown|txt|textile|rst|latex|pandoc|html)' # e.g. en.haml or es.md LOCALE_FILE_MATCH = /^(#{AVAILABLE_LANGUAGES.join('|')})\.#{SUFFIXES}$/ # # returns true if the name of a file could be a 'simple' static page # with only one translation. # # rules: # * we include files that end in appriopriate suffixes # * we exclude file names that are locales. # def is_simple_page?(name) name =~ /\.#{SUFFIXES}$/ && name !~ LOCALE_FILE_MATCH end def file_without_suffix(name) name.sub(/^(.*?)\.#{SUFFIXES}$/, "\\1") end # # returns an array like so: # # [ # ['/path/to/page/en.haml', :en] # ['/path/to/page/es.haml', :es] # ] # # Or this, if page is simple: # # [ # ['/path/to/page.haml', nil] # ] # # def content_files if @simple_page [[[@file_path, @suffix].join,nil]] elsif File.directory?(@file_path) Dir.foreach(@file_path).collect { |file| if file && file =~ LOCALE_FILE_MATCH [File.join(@file_path, file), $1.to_sym] end }.compact end end #def self.relative_to_rails_view_root(absolute_path) # if Rails.root # absolute = Pathname.new(absolute_path) # rails_view_root = Pathname.new(Rails.root + 'app/views') # absolute.relative_path_from(rails_view_root).to_s # end #end ## ## RENDERING ## PROPERTY_HEADER = /^\s*(^(|- )@\w[^\n]*?\n)*/m class MissingTemplate < StandardError end def render_locale(renderer, locale) if is_haml_template?(locale) renderer.render_to_string(:template => self.template_path(locale), :layout => false).html_safe else render_static_locale(locale).html_safe end end def render_static_locale(locale) content_files.each do |content_file, file_locale| if file_locale.nil? || locale == file_locale return render_content_file(content_file, locale) end end raise MissingTemplate.new(template_path(locale)) end # # todo: maybe use RDiscount for markdown instead? # def render_content_file(content_file, locale) content = File.read(content_file).sub(PROPERTY_HEADER, '') suffix = File.extname(content_file) #if PANDOC_FORMATS[suffix] # render_pandoc(content, suffix, locale) if REDCLOTH_FORMATS[suffix] render_redcloth(content, suffix, locale) elsif RBST_FORMATS[suffix] render_rbst(content, suffix, locale) elsif RDISCOUNT_FORMATS[suffix] render_rdiscount(content, suffix, locale) else "sorry, i don't understand how to render #{suffix}" end end def is_haml_template?(locale) @suffix == '.haml' || File.exists?(self.absolute_template_path(locale) + '.haml') end #PANDOC_FORMATS = { # '.md' => :markdown, # '.markdown' => :markdown, # #'.txt' => :textile, # #'.textile' => :textile, # '.rst' => :rst, # '.latex' => :latex, # '.pandoc' => :pandoc, #} #def render_pandoc(string, suffix, locale) # string = Iconv.conv("UTF-8//IGNORE", "UTF-8", string) # remove illegal utf-8 # args = [string, {:from => PANDOC_FORMATS[suffix], :to => :html}, "smart"] # if props.locale(locale).toc != false # args << "table_of_contents" # args << {"template" => "'#{File.dirname(__FILE__) + '/template.html'}'"} # end # unless (title = explicit_title(locale)).nil? # args << {"variable" => "title:'#{title}'"} # end # renderer = PandocRuby.new(*args) # renderer.convert #end RBST_FORMATS = { '.rst' => :rst } def render_rbst(string, suffix, locale) html = RbST.new(string).to_html unless (title = explicit_title(locale)).nil? html = "

#{title}

\n\n" + html end return html end REDCLOTH_FORMATS = { '.txt' => :textile, '.textile' => :textile } def render_redcloth(string, suffix, locale) unless (title = explicit_title(locale)).nil? string = "h1(first). #{title}\n\n" + string end RedCloth.new(string).to_html end RDISCOUNT_FORMATS = { '.md' => :markdown, '.markdown' => :markdown } def render_rdiscount(string, suffix, locale) rd = RDiscount.new(string, :smart, :generate_toc, :autolink) html = rd.to_html if props.locale(locale).toc != false && rd.toc_content #html = "
%s
\n\n%s" % [rd.toc_content.force_encoding('utf-8'), html] html = "
%s
\n\n%s" % [rd.toc_content, html] end unless (title = explicit_title(locale)).nil? html = "

#{title}

\n\n" + html end return html end # # returns title iff explicitly set. # def explicit_title(locale) props.get_var_without_inheritance(:title, locale) end end