diff options
-rw-r--r-- | Gemfile | 3 | ||||
-rw-r--r-- | Gemfile.lock | 14 | ||||
-rw-r--r-- | app/assets/stylesheets/application.scss | 7 | ||||
-rw-r--r-- | app/assets/stylesheets/typography.scss | 44 | ||||
-rw-r--r-- | app/controllers/application_controller.rb | 24 | ||||
-rw-r--r-- | app/controllers/pages_controller.rb | 1 | ||||
-rw-r--r-- | app/helpers/application_helper.rb | 30 | ||||
-rw-r--r-- | app/helpers/navigation_helper.rb | 22 | ||||
-rw-r--r-- | app/views/layouts/application.html.haml | 3 | ||||
-rw-r--r-- | config/initializers/pandoc.rb | 2 | ||||
-rw-r--r-- | lib/menu.rb | 4 | ||||
-rw-r--r-- | lib/property_set.rb | 23 | ||||
-rw-r--r-- | lib/static_page.rb | 274 | ||||
-rw-r--r-- | lib/template.html | 9 |
14 files changed, 380 insertions, 80 deletions
@@ -4,7 +4,8 @@ gem 'rails', '3.2.12' gem 'sqlite3' gem 'json' -gem 'markdown-rails' # allows static pages with .md +gem 'pandoc_rails', :path => 'vendor/gems/pandoc_rails' + gem 'haml' # allow pages with .haml gem 'RedCloth' # allow :textile in HAML gem 'sass-rails' # not sure why can't be in :assets group diff --git a/Gemfile.lock b/Gemfile.lock index cfda894..f0825f6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,10 @@ +PATH + remote: vendor/gems/pandoc_rails + specs: + pandoc_rails (0.0.1) + pandoc-ruby + rails + GEM remote: https://rubygems.org/ specs: @@ -55,9 +62,6 @@ GEM i18n (>= 0.4.0) mime-types (~> 1.16) treetop (~> 1.4.8) - markdown-rails (0.2.1) - rails - rdiscount (>= 1.6.8, < 2.0) mime-types (1.21) multi_json (1.5.1) net-scp (1.1.0) @@ -67,6 +71,7 @@ GEM net-ssh (2.6.5) net-ssh-gateway (1.2.0) net-ssh (>= 2.6.5) + pandoc-ruby (0.6.0) polyglot (0.3.3) rack (1.4.5) rack-cache (1.2) @@ -91,7 +96,6 @@ GEM rdoc (~> 3.4) thor (>= 0.14.6, < 2.0) rake (10.0.3) - rdiscount (1.6.8) rdoc (3.12.1) json (~> 1.4) sass (3.2.5) @@ -132,7 +136,7 @@ DEPENDENCIES haml jquery-rails json - markdown-rails + pandoc_rails! rails (= 3.2.12) sass-rails sqlite3 diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 50331b4..82f7e24 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -19,6 +19,13 @@ $fluidGridGutterWidth: 2.127659574% !default; // side navigation $side-columns: 3; +//$linkColor: #2072FF; +//$linkColor: #04c; +//$linkVisitedColor: #40c; + +$linkColor: #00F; +$linkVisitedColor: #339; + // // IMPORT LIBRARIES // diff --git a/app/assets/stylesheets/typography.scss b/app/assets/stylesheets/typography.scss index e96b02f..11519c4 100644 --- a/app/assets/stylesheets/typography.scss +++ b/app/assets/stylesheets/typography.scss @@ -28,7 +28,9 @@ h2 { h1, h2, h3 { &.first { line-height: 0.8em; - margin-bottom: 0.4em + margin-bottom: 0.4em; + //font-size: 3em; + //color: #333; } } p.first { @@ -48,3 +50,43 @@ p.first { line-height: 140%; margin: 20px 0; } + +a:visited { + color: $linkVisitedColor; +} + +// +// Pandoc specific HTML +// + +#TOC { + //ul { + // list-style-type: decimal; + // } + ul { + list-style-type: none; + counter-reset: level1; + } + ul li:before { + content: counter(level1) ". "; + counter-increment: level1; + } + ul li ul { + list-style-type: none; + counter-reset: level2; + } + ul li ul li:before { + content: counter(level1) "." counter(level2) " "; + counter-increment: level2; + } +} + +a[href="#TOC"] { + color: black; + pointer-events: none; + cursor: default; + &:hover { + text-decoration: none; + } +} + diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b73a463..a9a25f0 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -59,30 +59,15 @@ class ApplicationController < ActionController::Base # renders the content of a static page # def render_page(page) - begin - render :template => page.template_path - rescue ActionView::MissingTemplate => exc - begin - render :template => page.template_path(DEFAULT_LOCALE) - rescue - raise exc - end - end + render :text => page.render_to_string(self), :layout => true end + helper_method :render_page # - # same as render page, but returns the string + # same as render page, but returns the string without the layout # def page_body(page) - begin - render_to_string :template => page.template_path - rescue ActionView::MissingTemplate => exc - begin - render_to_string :template => page.template_path(DEFAULT_LOCALE) - rescue - raise exc - end - end + page.render_to_string(self) end helper_method :page_body @@ -114,4 +99,5 @@ class ApplicationController < ActionController::Base # Thread.current[key_name] = true #end + end diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index acf525c..3e76092 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -14,6 +14,7 @@ class PagesController < ApplicationController format.atom { render_atom_feed(@page) } end else + logger.error("ERROR: could not find page %s" % params[:page]) raise PageNotFound.new end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 2933bd1..45e8228 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -69,13 +69,9 @@ module ApplicationHelper def page_title if @page - if @page.props - @page.props.title || @page.title - else - @page.title - end + @page.title else - "" + nil end end @@ -85,16 +81,16 @@ module ApplicationHelper # they call two different 'render' methods (controller and view renders behave differently). # TODO: figure out how to combine into one helper_method. # - def render_page(page) - begin - render :template => page.template_path - rescue ActionView::MissingTemplate => exc - begin - render :template => page.template_path(DEFAULT_LOCALE) - rescue - raise exc - end - end - end + # def render_page(page) + # begin + # render :template => page.template_path + # rescue ActionView::MissingTemplate => exc + # begin + # render :template => page.template_path(DEFAULT_LOCALE) + # rescue + # raise exc + # end + # end + # end end diff --git a/app/helpers/navigation_helper.rb b/app/helpers/navigation_helper.rb index fde6b45..b4ca514 100644 --- a/app/helpers/navigation_helper.rb +++ b/app/helpers/navigation_helper.rb @@ -19,7 +19,7 @@ module NavigationHelper site.menu.each do |item| active = current_page_path.first == item.name ? 'active' : '' haml 'li.tab', :class => first do - haml 'a.tab', I18n.t('pages.' + item.name), :href => menu_item_path(item), :class => active + haml 'a.tab', menu_item_title(item), :href => menu_item_path(item), :class => active end first = '' end @@ -39,7 +39,7 @@ module NavigationHelper def act_as(page) page = site.find_page(page) @current_page_path = page.path - render_page(page) + page_body(page) end private @@ -48,12 +48,24 @@ module NavigationHelper "/#{I18n.locale}/#{item.path.join('/')}" end + def menu_item_title(item) + page = site.pages[item.path_str] || site.pages[item.name] + if page + page.nav_title(I18n.locale) + else + nil + end + end + def display_menu(menu, level=0) menu.each do |item| - haml 'li', :class => path_active(current_page_path, item) do - haml 'a', I18n.t('pages.'+item.name), :href => menu_item_path(item), :class => "level#{level}" + title = menu_item_title(item) + if title + haml 'li', :class => path_active(current_page_path, item) do + haml 'a', menu_item_title(item), :href => menu_item_path(item), :class => "level#{level}" + end + display_menu(item.submenu, level+1) end - display_menu(item.submenu, level+1) end end diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 3d28f38..b85c6c4 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,7 +1,8 @@ !!! 5 %html{:dir=>'ltr'} %head - %title #{site.title} - #{page_title} + %title + = [page_title, site.title].compact.join(' - ') = stylesheet_link_tag "application", :media => "all" = javascript_include_tag "application" = csrf_meta_tags diff --git a/config/initializers/pandoc.rb b/config/initializers/pandoc.rb new file mode 100644 index 0000000..a1f948a --- /dev/null +++ b/config/initializers/pandoc.rb @@ -0,0 +1,2 @@ + +PandocRails.register
\ No newline at end of file diff --git a/lib/menu.rb b/lib/menu.rb index 4da8441..b586151 100644 --- a/lib/menu.rb +++ b/lib/menu.rb @@ -50,6 +50,10 @@ class Menu end end + def path_str + @path_str ||= path.join('/') + end + def each(&block) children.each(&block) end diff --git a/lib/property_set.rb b/lib/property_set.rb index 64982c9..05aa46a 100644 --- a/lib/property_set.rb +++ b/lib/property_set.rb @@ -11,6 +11,7 @@ # getting the property # # page.props.title +# page.props.locale('en').title # require 'i18n' @@ -39,15 +40,17 @@ class PropertySet def textile(str) RedCloth.new(str).to_html end - def get(var_name) + def get(var_name, inheritance=true) value = instance_variable_get("@#{var_name}") if value.nil? if @_locale != DEFAULT_LOCALE # try value from default locale @_ps.get_var(var_name) - else + elsif inheritance # try inherited value @_ps.get_inherited_var(var_name, @_locale) + else + nil end else value @@ -66,7 +69,9 @@ class PropertySet # # evaluate the template_string, and load the variables defined into an AttrObject. # - def eval(locale, template_string) + def eval(template_string, locale) + locale ||= DEFAULT_LOCALE + # render to the template to get the instance variables attrs = AttrObject.new(self, locale) begin @@ -136,6 +141,18 @@ class PropertySet end # + # like get_var, but forbits inheritance + # + def get_var_without_inheritance(var_name, locale=I18n.locale) + attrs = locale(locale) + if attrs + attrs.get(var_name, false) + else + nil + end + end + + # # tries to get the value of an inherited variable # def get_inherited_var(var_name, locale=I18n.locale) diff --git a/lib/static_page.rb b/lib/static_page.rb index eb32319..b4bc40d 100644 --- a/lib/static_page.rb +++ b/lib/static_page.rb @@ -1,4 +1,4 @@ -# + # # class StaticPage # # represents a static website page. @@ -8,9 +8,11 @@ require 'property_set' require 'i18n' require 'pathname' +require 'RedCloth' +require 'pandoc-ruby' class StaticPage - attr_accessor :path, :children, :name, :file_path, :props, :parent, :mount_point + attr_accessor :path, :children, :name, :file_path, :props, :parent, :mount_point, :locales ## ## CLASS METHODS @@ -31,7 +33,8 @@ class StaticPage end # - # loads a directory, creating StaticPages from the directory structure + # loads a directory, creating StaticPages from the directory structure, + # yielding each StaticPage as it is created. # def scan(&block) Dir.chdir(file_path) do @@ -41,7 +44,7 @@ class StaticPage yield child child.scan(&block) elsif is_simple_page?(child_name) - child = StaticPage.new(self, file_without_suffix(child_name)) + child = StaticPage.new(self, child_name) yield child end end @@ -54,7 +57,17 @@ class StaticPage def initialize(parent, name, file_path=nil) @children = [] - @name = name + + # 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 @@ -63,6 +76,8 @@ class StaticPage else @path = [] end + + # set the @file_path if file_path @file_path = file_path elsif @parent && @parent.file_path @@ -70,8 +85,13 @@ class StaticPage else raise 'file path must be specified or in parent' end + + # discover supported locales @simple_page = !File.directory?(@file_path) - @props = load_properties + #@locales = find_locales() + + # eval the property headers, if any + @props = load_properties() end def add_child(page) @@ -100,15 +120,69 @@ class StaticPage 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 - def title + # + # 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 - I18n.t!('pages.' + @name, :raise => true) - rescue I18n::MissingTranslationData - props.title + 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 @@ -124,30 +198,49 @@ class StaticPage private - #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 + ## + ## 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) - Dir.glob(file_path + '/*.haml') do |content_file_path| - locale = File.basename(content_file_path).sub(File.extname(content_file_path),'') - #variable_header = "" - #File.open(content_file_path) do |f| - # while (line = f.gets) =~ /^- @/ - # variable_header << line - # end - #end - props.eval(locale, File.read(content_file_path)) + 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 - SUFFIXES = '(haml|md)' + ## + ## 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 @@ -158,12 +251,137 @@ class StaticPage # * we exclude file names that are locales. # def is_simple_page?(name) - name =~ /\.#{SUFFIXES}$/ && name !~ /^(#{AVAILABLE_LANGUAGES.join('|')})\.#{SUFFIXES}$/ + 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,nil]] + elsif File.directory?(@file_path) + Dir.foreach(@file_path).collect { |file| + if file && file =~ LOCALE_FILE_MATCH + [File.join(@file_path, file), $1] + 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) + elsif REDCLOTH_FORMATS[suffix] + render_redcloth(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) + args = [string, {:from => PANDOC_FORMATS[suffix], :to => :html5}, "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 + + # + # pandoc can do textile, but it does it HORRIBLY + # + REDCLOTH_FORMATS = { + '.txt' => :textile, + '.textile' => :textile, + } + + def render_redcloth(string, suffix, locale) + unless (title = explicit_title(locale)).nil? + string = "h1. #{title}\n\n" + string + end + RedCloth.new(string).to_html + end + + # + # returns title iff explicitly set. + # + def explicit_title(locale) + props.get_var_without_inheritance(:title, locale) + end end diff --git a/lib/template.html b/lib/template.html new file mode 100644 index 0000000..379a602 --- /dev/null +++ b/lib/template.html @@ -0,0 +1,9 @@ +$if(title)$ +<h1 class="first">$title$</h1> +$endif$ +$if(toc)$ +<div id="$idprefix$TOC"> +$toc$ +</div> +$endif$ +$body$
\ No newline at end of file |