summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Gemfile3
-rw-r--r--Gemfile.lock14
-rw-r--r--app/assets/stylesheets/application.scss7
-rw-r--r--app/assets/stylesheets/typography.scss44
-rw-r--r--app/controllers/application_controller.rb24
-rw-r--r--app/controllers/pages_controller.rb1
-rw-r--r--app/helpers/application_helper.rb30
-rw-r--r--app/helpers/navigation_helper.rb22
-rw-r--r--app/views/layouts/application.html.haml3
-rw-r--r--config/initializers/pandoc.rb2
-rw-r--r--lib/menu.rb4
-rw-r--r--lib/property_set.rb23
-rw-r--r--lib/static_page.rb274
-rw-r--r--lib/template.html9
14 files changed, 380 insertions, 80 deletions
diff --git a/Gemfile b/Gemfile
index 7748246..2a0e0c0 100644
--- a/Gemfile
+++ b/Gemfile
@@ -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