rendered correctly a simple and default LocomotiveCMS site

did committed Dec 30, 2012
commit c95649ad22594342e28d009472c7c04a6da96d98
Showing 48 changed files with 1870 additions and 62 deletions
.gitignore +3 -0
@@ @@ -16,3 +16,6 @@ spec/reports
test/tmp
test/version_tmp
tmp
+
+ /.rbenv-gemsets
+ /.sass-cache/
Gemfile +2 -1
@@ @@ -2,4 +2,5 @@ source 'https://rubygems.org'
# Specify your gem's dependencies in steam.gemspec
gemspec
- gem 'locomotivecms_mounter', path: '../mounter'
\ No newline at end of file
+
+ gem 'locomotivecms_mounter', path: '../gems/mounter'
\ No newline at end of file
bin/builder +4 -0
@@ @@ -1,4 +1,8 @@
#!/usr/bin/env ruby
+
+ # FIXME: needed if you don't launch it with bundler
+ # $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
+
require "locomotive/builder"
require "locomotive/builder/cli"
locomotive/builder/cli.rb b/lib/locomotive/builder/cli.rb +14 -3
@@ @@ -3,20 +3,31 @@ require "thor"
module Locomotive
module Builder
class CLI < Thor
-
+
desc "import NAME SITE_URL EMAIL PASSWORD", "Import an existing locomotive site"
def import(name, site_url, email, password)
say("ERROR: \"#{name}\" directory already exists", :red) and return if File.exists?(name)
Locomotive::Builder.import(name, site_url, email, password)
end
-
+
+ desc "push PATH SITE_URL EMAIL PASSWORD", "Push a site created by the builder to a remote LocomotiveCMS engine"
+ def push(path, site_url, email, password)
+ Locomotive::Builder.push(path, site_url, email, password)
+ end
+
desc "server PATH [PORT]", "Serve an existing site from the file system"
def server(path, port = 3333)
require "thin"
require "locomotive/builder/server"
reader = Locomotive::Mounter::Reader::FileSystem.instance
reader.run!(path: path)
- Thin::Server.start('0.0.0.0', port, Locomotive::Builder::Server.new(reader))
+
+ server = Thin::Server.new('0.0.0.0', port, Locomotive::Builder::Server.new(reader))
+ server.threaded = true
+ server.start
+
+ # TODO: To be removed
+ # Thin::Server.start('0.0.0.0', port, Locomotive::Builder::Server.new(reader), threaded: true)
end
end
end
locomotive/builder/liquid.rb b/lib/locomotive/builder/liquid.rb +21 -0
@@ @@ -0,0 +1,21 @@
+ require 'liquid'
+ require 'locomotive/builder/liquid/drops/base'
+
+ %w{. drops tags filters}.each do |dir|
+ Dir[File.join(File.dirname(__FILE__), 'liquid', dir, '*.rb')].each { |lib| require lib }
+ end
+
+ # add to_liquid methods to main models from the mounter
+ %w{site page content_entry}.each do |name|
+ # require "locomotive/mounter/models/#{name}"
+
+ klass = "Locomotive::Mounter::Models::#{name.classify}".constantize
+
+ klass.class_eval <<-EOV
+ def to_liquid
+ ::Locomotive::Builder::Liquid::Drops::#{name.classify}.new(self)
+ end
+ EOV
+ end
+
+ # ::Liquid::Template.file_system = Locomotive::Builder::Liquid::TemplateFileSystem.new(LocomotiveEditor.site_templates_root)
locomotive/builder/liquid/drops/base.rb b/lib/locomotive/builder/liquid/drops/base.rb +44 -0
@@ @@ -0,0 +1,44 @@
+ # Code taken from Mephisto sources (http://mephistoblog.com/)
+ module Locomotive
+ module Builder
+ module Liquid
+ module Drops
+ class Base < ::Liquid::Drop
+
+ @@forbidden_attributes = %w{_id _version _index}
+
+ attr_reader :_source
+
+ def initialize(source)
+ @_source = source
+ end
+
+ def id
+ (@_source.respond_to?(:id) ? @_source.id : nil) || 'new'
+ end
+
+ # converts an array of records to an array of liquid drops
+ def self.liquify(*records, &block)
+ i = -1
+ records =
+ records.inject [] do |all, r|
+ i+=1
+ attrs = (block && block.arity == 1) ? [r] : [r, i]
+ all << (block ? block.call(*attrs) : r.to_liquid)
+ all
+ end
+ records.compact!
+ records
+ end
+
+ protected
+
+ def liquify(*records, &block)
+ self.class.liquify(*records, &block)
+ end
+
+ end
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/drops/content_entry.rb b/lib/locomotive/builder/liquid/drops/content_entry.rb +29 -0
@@ @@ -0,0 +1,29 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Drops
+ class ContentEntry < Base
+
+ delegate :_permalink, :seo_title, :meta_keywords, :meta_description, :to => '_source'
+
+ def before_method(meth)
+ return '' if @_source.nil?
+
+ if not @@forbidden_attributes.include?(meth.to_s)
+ value = @_source.send(meth)
+ end
+ end
+
+ def _permalink
+ @_source._permalink.parameterize
+ end
+
+ def highlighted_field_value
+ @_source.highlighted_field_value
+ end
+
+ end
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/drops/content_types.rb b/lib/locomotive/builder/liquid/drops/content_types.rb +103 -0
@@ @@ -0,0 +1,103 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Drops
+ class ContentTypes < ::Liquid::Drop
+
+ def before_method(meth)
+ type = self.mounting_point.content_types[meth.to_s]
+ ProxyCollection.new(type)
+ end
+
+ end
+
+ class ProxyCollection < ::Liquid::Drop
+
+ def initialize(content_type)
+ @content_type = content_type
+ @collection = nil
+ end
+
+ def first
+ self.collection.first
+ end
+
+ def last
+ self.collection.last
+ end
+
+ def size
+ self.collection.size
+ end
+
+ alias :length :size
+
+ def each(&block)
+ self.collection.each(&block)
+ end
+
+ def public_submission_url
+ "/entry_submissions/#{@content_type.slug}"
+ end
+
+ def api
+ { 'create' => "/entry_submissions/#{@content_type.slug}" }
+ end
+
+ def before_method(meth)
+ if (meth.to_s =~ /^group_by_(.+)$/) == 0
+ # TODO
+ @content_type.group_contents_by($1)
+ elsif (meth.to_s =~ /^(.+)_options$/) == 0
+ # TODO
+ @content_type.select_names($1)
+ else
+ @content_type.send(meth)
+ end
+ end
+
+ protected
+
+ def paginate(options = {})
+ @collection ||= self.collection.paginate(options)
+ {
+ collection: @collection,
+ current_page: @collection.current_page,
+ previous_page: @collection.previous_page,
+ next_page: @collection.next_page,
+ total_entries: @collection.total_entries,
+ total_pages: @collection.total_pages,
+ per_page: @collection.per_page
+ }
+ end
+
+ def collection
+ return unless @collection.blank?
+
+ if @context['with_scope'].blank?
+ @collection = @content_type.entries
+ else
+ @collection = []
+
+ conditions = @context['with_scope'].clone.delete_if { |k, _| %w(order_by per_page page).include?(k) }
+
+ @content_type.contents.each do |content|
+ accepted = (conditions.map do |key, value|
+ case value
+ when TrueClass, FalseClass, String then content.send(key) == value
+ else
+ true
+ end
+ end).all? # all conditions works ?
+
+ @collection << content if accepted
+ end
+ end
+
+ @collection
+ end
+ end
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/drops/page.rb b/lib/locomotive/builder/liquid/drops/page.rb +34 -0
@@ @@ -0,0 +1,34 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Drops
+ class Page < Base
+
+ delegate :title, :slug, :fullpath, :parent, :depth, :seo_title, :redirect_url, :meta_description, :meta_keywords, :to => '_source'
+
+ def children
+ @children ||= liquify(*@_source.children)
+ end
+
+ def published?
+ @_source.published?
+ end
+
+ def redirect?
+ self._source.redirect?
+ end
+
+ def breadcrumbs
+ # TODO
+ ''
+ end
+
+ def listed?
+ @_source.listed?
+ end
+
+ end
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/drops/site.rb b/lib/locomotive/builder/liquid/drops/site.rb +21 -0
@@ @@ -0,0 +1,21 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Drops
+ class Site < Base
+
+ delegate :name, :seo_title, :meta_description, :meta_keywords, :to => '_source'
+
+ def index
+ @index ||= @_source.lookup_page('index')
+ end
+
+ def pages
+ @pages ||= liquify(*self._source.pages)
+ end
+
+ end
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/errors.rb b/lib/locomotive/builder/liquid/errors.rb +7 -0
@@ @@ -0,0 +1,7 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ class PageNotFound < ::Liquid::Error; end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/filters/date.rb b/lib/locomotive/builder/liquid/filters/date.rb +98 -0
@@ @@ -0,0 +1,98 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Filters
+ module Date
+
+ def localized_date(input, *args)
+ return '' if input.blank?
+
+ format, locale = args
+
+ locale ||= I18n.locale
+ format ||= I18n.t('date.formats.default', :locale => locale)
+
+ if input.is_a?(String)
+ begin
+ fragments = ::Date._strptime(input, format)
+ input = ::Date.new(fragments[:year], fragments[:mon], fragments[:mday])
+ rescue
+ input = Time.parse(input)
+ end
+ end
+
+ return input.to_s unless input.respond_to?(:strftime)
+
+ I18n.l input, :format => format, :locale => locale
+ end
+
+ alias :format_date :localized_date
+
+ def distance_of_time_in_words(input, *args)
+ return '' if input.blank?
+
+ from_time = input
+ to_time = args[0] || Time.now
+
+ from_time = from_time.to_time if from_time.respond_to?(:to_time)
+ to_time = to_time.to_time if to_time.respond_to?(:to_time)
+ distance_in_minutes = (((to_time - from_time).abs)/60).round
+ distance_in_seconds = ((to_time - from_time).abs).round
+
+ ::I18n.with_options({ :scope => :'datetime.distance_in_words' }) do |locale|
+
+ case distance_in_minutes
+ when 0..1
+ return distance_in_minutes == 0 ?
+ locale.t(:less_than_x_minutes, :count => 1) :
+ locale.t(:x_minutes, :count => distance_in_minutes) unless include_seconds
+
+ case distance_in_seconds
+ when 0..4 then locale.t :less_than_x_seconds, :count => 5
+ when 5..9 then locale.t :less_than_x_seconds, :count => 10
+ when 10..19 then locale.t :less_than_x_seconds, :count => 20
+ when 20..39 then locale.t :half_a_minute
+ when 40..59 then locale.t :less_than_x_minutes, :count => 1
+ else locale.t :x_minutes, :count => 1
+ end
+
+ when 2..44 then locale.t :x_minutes, :count => distance_in_minutes
+ when 45..89 then locale.t :about_x_hours, :count => 1
+ when 90..1439 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round
+ when 1440..2519 then locale.t :x_days, :count => 1
+ when 2520..43199 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round
+ when 43200..86399 then locale.t :about_x_months, :count => 1
+ when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round
+ else
+ fyear = from_time.year
+ fyear += 1 if from_time.month >= 3
+ tyear = to_time.year
+ tyear -= 1 if to_time.month < 3
+ leap_years = (fyear > tyear) ? 0 : (fyear..tyear).count{|x| ::Date.leap?(x)}
+ minute_offset_for_leap_year = leap_years * 1440
+ # Discount the leap year days when calculating year distance.
+ # e.g. if there are 20 leap year days between 2 dates having the same day
+ # and month then the based on 365 days calculation
+ # the distance in years will come out to over 80 years when in written
+ # english it would read better as about 80 years.
+ minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year
+ remainder = (minutes_with_offset % 525600)
+ distance_in_years = (minutes_with_offset / 525600)
+ if remainder < 131400
+ locale.t(:about_x_years, :count => distance_in_years)
+ elsif remainder < 394200
+ locale.t(:over_x_years, :count => distance_in_years)
+ else
+ locale.t(:almost_x_years, :count => distance_in_years + 1)
+ end
+ end
+ end
+ end
+
+ end
+
+ ::Liquid::Template.register_filter(Date)
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/filters/html.rb b/lib/locomotive/builder/liquid/filters/html.rb +154 -0
@@ @@ -0,0 +1,154 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Filters
+ module Html
+
+ # Write the link to a stylesheet resource
+ # input: url of the css file
+ def stylesheet_tag(input, media = 'screen')
+ return '' if input.nil?
+
+ input = "/stylesheets/#{input}" unless input =~ /^(\/|http:)/
+
+ input = "#{input}.css" unless input.ends_with?('.css')
+
+ %{<link href="#{input}" media="#{media}" rel="stylesheet" type="text/css" />}
+ end
+
+ # Write the link to javascript resource
+ # input: url of the javascript file
+ def javascript_tag(input)
+ return '' if input.nil?
+
+ input = "/javascripts/#{input}" unless input =~ /^(\/|http:)/
+
+ input = "#{input}.js" unless input.ends_with?('.js')
+
+ %{<script src="#{input}" type="text/javascript"></script>}
+ end
+
+ # Write an image tag
+ # input: url of the image OR asset drop
+ def image_tag(input, *args)
+ image_options = inline_options(args_to_options(args))
+
+ input = "/images/#{input}" unless input =~ /^(\/|http:)/
+
+ "<img src=\"#{File.join('/', get_url_from_asset(input))}\" #{image_options}/>"
+ end
+
+ # Write a theme image tag
+ # input: name of file including folder
+ # example: 'about/myphoto.jpg' | theme_image # <img src="images/about/myphoto.jpg" />
+ def theme_image_tag(input, *args)
+ image_options = inline_options(args_to_options(args))
+ "<img src=\"#{theme_image_url(input)}\" #{image_options}/>"
+ end
+
+ def theme_image_url(input)
+ return '' if input.nil?
+
+ input = "images/#{input}" unless input.starts_with?('/')
+
+ File.join('/', input)
+ end
+
+ def image_format(input, *args)
+ format = args_to_options(args).first
+ "#{input}.#{format}"
+ end
+
+ # Embed a flash movie into a page
+ # input: url of the flash movie OR asset drop
+ # width: width (in pixel or in %) of the embedded movie
+ # height: height (in pixel or in %) of the embedded movie
+ def flash_tag(input, *args)
+ path = get_url_from_asset(input)
+ embed_options = inline_options(args_to_options(args))
+ %{
+ <object #{embed_options}>
+ <param name="movie" value="#{path}" />
+ <embed src="#{path}" #{embed_options}/>
+ </embed>
+ </object>
+ }.gsub(/ >/, '>').strip
+ end
+
+ # Render the navigation for a paginated collection
+ def default_pagination(paginate, *args)
+ return '' if paginate['parts'].empty?
+
+ options = args_to_options(args)
+
+ previous_label = options[:previous_label] || I18n.t('pagination.previous')
+ next_label = options[:next_label] || I18n.t('pagination.next')
+
+ previous_link = (if paginate['previous'].blank?
+ "<span class=\"disabled prev_page\">#{previous_label}</span>"
+ else
+ "<a href=\"#{absolute_url(paginate['previous']['url'])}\" class=\"prev_page\">#{previous_label}</a>"
+ end)
+
+ links = ""
+ paginate['parts'].each do |part|
+ links << (if part['is_link']
+ "<a href=\"#{absolute_url(part['url'])}\">#{part['title']}</a>"
+ elsif part['hellip_break']
+ "<span class=\"gap\">#{part['title']}</span>"
+ else
+ "<span class=\"current\">#{part['title']}</span>"
+ end)
+ end
+
+ next_link = (if paginate['next'].blank?
+ "<span class=\"disabled next_page\">#{next_label}</span>"
+ else
+ "<a href=\"#{absolute_url(paginate['next']['url'])}\" class=\"next_page\">#{next_label}</a>"
+ end)
+
+ %{<div class="pagination #{options[:css]}">
+ #{previous_link}
+ #{links}
+ #{next_link}
+ </div>}
+ end
+
+ protected
+
+ # Convert an array of properties ('key:value') into a hash
+ # Ex: ['width:50', 'height:100'] => { :width => '50', :height => '100' }
+ def args_to_options(*args)
+ options = {}
+ args.flatten.each do |a|
+ if (a =~ /^(.*):(.*)$/)
+ options[$1.to_sym] = $2
+ end
+ end
+ options
+ end
+
+ # Write options (Hash) into a string according to the following pattern:
+ # <key1>="<value1>", <key2>="<value2", ...etc
+ def inline_options(options = {})
+ return '' if options.empty?
+ (options.stringify_keys.to_a.collect { |a, b| "#{a}=\"#{b}\"" }).join(' ') << ' '
+ end
+
+ # Get the path to be used in html tags such as image_tag, flash_tag, ...etc
+ # input: url (String) OR asset drop
+ def get_url_from_asset(input)
+ input.respond_to?(:url) ? input.url : input
+ end
+
+ def absolute_url(url)
+ url =~ /^\// ? url : "/#{url}"
+ end
+ end
+
+ ::Liquid::Template.register_filter(Html)
+
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/filters/misc.rb b/lib/locomotive/builder/liquid/filters/misc.rb +28 -0
@@ @@ -0,0 +1,28 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Filters
+ module Misc
+
+ # was called modulo at first
+ def str_modulo(word, index, modulo)
+ (index.to_i + 1) % modulo == 0 ? word : ''
+ end
+
+ # Get the nth element of the passed in array
+ def index(array, position)
+ array.at(position) if array.respond_to?(:at)
+ end
+
+ def default(input, value)
+ input.blank? ? value : input
+ end
+
+ end
+
+ ::Liquid::Template.register_filter(Misc)
+
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/filters/resize.rb b/lib/locomotive/builder/liquid/filters/resize.rb +18 -0
@@ @@ -0,0 +1,18 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Filters
+ module Resize
+
+ def resize(input, resize_string)
+ Locomotive::Builder::DragonflyExt.resize_url(input, resize_string)
+ end
+
+ end
+
+ ::Liquid::Template.register_filter(Resize)
+
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/filters/text.rb b/lib/locomotive/builder/liquid/filters/text.rb +50 -0
@@ @@ -0,0 +1,50 @@
+ require 'RedCloth'
+
+ module Locomotive
+ module Builder
+ module Liquid
+ module Filters
+ module Text
+
+ # right justify and padd a string
+ def rjust(input, integer, padstr = '')
+ input.to_s.rjust(integer, padstr)
+ end
+
+ # left justify and padd a string
+ def ljust(input, integer, padstr = '')
+ input.to_s.ljust(integer, padstr)
+ end
+
+ def underscore(input)
+ input.to_s.gsub(' ', '_').gsub('/', '_').underscore
+ end
+
+ def dasherize(input)
+ input.to_s.gsub(' ', '-').gsub('/', '-').dasherize
+ end
+
+ # alias newline_to_br
+ def multi_line(input)
+ input.to_s.gsub("\n", '<br/>')
+ end
+
+ def concat(input, *args)
+ result = input.to_s
+ args.flatten.each { |a| result << a.to_s }
+ result
+ end
+
+
+ def textile(input)
+ ::RedCloth.new(input).to_html
+ end
+
+ end
+
+ ::Liquid::Template.register_filter(Text)
+
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/patches.rb b/lib/locomotive/builder/liquid/patches.rb +47 -0
@@ @@ -0,0 +1,47 @@
+ module Liquid
+
+ class Drop
+
+ def mounting_point
+ @context.registers[:mounting_point]
+ end
+
+ def site
+ @context.registers[:site]
+ end
+
+ end
+
+ class Template
+
+ # creates a new <tt>Template</tt> object from liquid source code
+ def parse_with_utf8(source, context = {})
+ if RUBY_VERSION =~ /1\.9/
+ source = source.force_encoding('UTF-8') if source.present?
+ end
+ self.parse_without_utf8(source, context)
+ end
+
+ alias_method_chain :parse, :utf8
+
+ end
+
+ module StandardFilters
+
+ private
+
+ def to_number(obj)
+ case obj
+ when Numeric
+ obj
+ when String
+ (obj.strip =~ /^\d+\.\d+$/) ? obj.to_f : obj.to_i
+ when DateTime, Date, Time
+ obj.to_time.to_i
+ else
+ 0
+ end
+ end
+ end
+
+ end
\ No newline at end of file
locomotive/builder/liquid/tags/consume.rb b/lib/locomotive/builder/liquid/tags/consume.rb +58 -0
@@ @@ -0,0 +1,58 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Tags
+
+ # Consume web services as easy as pie directly in liquid !
+ #
+ # Usage:
+ #
+ # {% consume blog from 'http://nocoffee.tumblr.com/api/read.json?num=3' username: 'john', password: 'easy', format: 'json', expires_in: 3000 %}
+ # {% for post in blog.posts %}
+ # {{ post.title }}
+ # {% endfor %}
+ # {% endconsume %}
+ #
+ class Consume < ::Liquid::Block
+
+ Syntax = /(#{::Liquid::VariableSignature}+)\s*from\s*(#{::Liquid::QuotedString}+)/
+
+ def initialize(tag_name, markup, tokens, context)
+ if markup =~ Syntax
+ @target = $1
+ @url = $2.gsub(/['"]/, '')
+ @options = {}
+ markup.scan(::Liquid::TagAttributes) do |key, value|
+ @options[key] = value if key != 'http'
+ end
+ @options.delete('expires_in')
+ else
+ raise ::Liquid::SyntaxError.new("Syntax Error in 'consume' - Valid syntax: consume <var> from \"<url>\" [username: value, password: value]")
+ end
+
+ super
+ end
+
+ def render(context)
+ context.stack do
+ _response = nil
+
+ begin
+ _response = Locomotive::Builder::Httparty::Webservice.consume(@url, @options.symbolize_keys)
+ rescue Exception => e
+ _response = { 'error' => e.message.to_s }.to_liquid
+ end
+
+ context.scopes.last[@target.to_s] = _response
+
+ render_all(@nodelist, context)
+ end
+ end
+
+ end
+
+ ::Liquid::Template.register_tag('consume', Consume)
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/tags/csrf.rb b/lib/locomotive/builder/liquid/tags/csrf.rb +34 -0
@@ @@ -0,0 +1,34 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Tags
+ module Csrf
+
+ class Param < ::Liquid::Tag
+
+ def render(context)
+ %{<input type="hidden" name="authenticity_token" value="helloworld" />}
+ end
+
+ end
+
+ class Meta < ::Liquid::Tag
+
+ def render(context)
+ %{
+ <meta name="csrf-param" content="authenticity_token" />
+ <meta name="csrf-token" content="helloworld" />
+ }
+ end
+
+ end
+
+ end
+
+ ::Liquid::Template.register_tag('csrf_param', Csrf::Param)
+ ::Liquid::Template.register_tag('csrf_meta', Csrf::Meta)
+
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/tags/editable.rb b/lib/locomotive/builder/liquid/tags/editable.rb +5 -0
@@ @@ -0,0 +1,5 @@
+ require 'locomotive/builder/liquid/tags/editable/base'
+ require 'locomotive/builder/liquid/tags/editable/short_text'
+ require 'locomotive/builder/liquid/tags/editable/long_text'
+ require 'locomotive/builder/liquid/tags/editable/file'
+ require 'locomotive/builder/liquid/tags/editable/control'
\ No newline at end of file
locomotive/builder/liquid/tags/editable/base.rb b/lib/locomotive/builder/liquid/tags/editable/base.rb +32 -0
@@ @@ -0,0 +1,32 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Tags
+ module Editable
+ class Base < ::Liquid::Block
+
+ Syntax = /(#{::Liquid::QuotedFragment})(\s*,\s*#{::Liquid::Expression}+)?/
+
+ def initialize(tag_name, markup, tokens, context)
+ if markup =~ Syntax
+ @slug = $1.gsub('\'', '')
+ @options = {}
+ markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/^'/, '').gsub(/'$/, '') }
+ else
+ raise ::Liquid::SyntaxError.new("Syntax Error in 'editable_xxx' - Valid syntax: editable_xxx <slug>(, <options>)")
+ end
+
+ super
+ end
+
+ def render(context)
+ super
+ end
+
+ end
+
+ end
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/tags/editable/control.rb b/lib/locomotive/builder/liquid/tags/editable/control.rb +15 -0
@@ @@ -0,0 +1,15 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Tags
+ module Editable
+ class Control < Base
+
+ end
+
+ ::Liquid::Template.register_tag('editable_control', Control)
+ end
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/tags/editable/file.rb b/lib/locomotive/builder/liquid/tags/editable/file.rb +15 -0
@@ @@ -0,0 +1,15 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Tags
+ module Editable
+ class File < Base
+
+ end
+
+ ::Liquid::Template.register_tag('editable_file', File)
+ end
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/tags/editable/long_text.rb b/lib/locomotive/builder/liquid/tags/editable/long_text.rb +15 -0
@@ @@ -0,0 +1,15 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Tags
+ module Editable
+ class LongText < ShortText
+
+ end
+
+ ::Liquid::Template.register_tag('editable_long_text', LongText)
+ end
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/tags/editable/short_text.rb b/lib/locomotive/builder/liquid/tags/editable/short_text.rb +15 -0
@@ @@ -0,0 +1,15 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Tags
+ module Editable
+ class ShortText < Base
+
+ end
+
+ ::Liquid::Template.register_tag('editable_short_text', ShortText)
+ end
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/tags/extends.rb b/lib/locomotive/builder/liquid/tags/extends.rb +25 -0
@@ @@ -0,0 +1,25 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Tags
+ class Extends < ::Liquid::Extends
+
+ def parse_parent_template
+ mounting_point = @context[:mounting_point]
+
+ page = if @template_name == 'parent'
+ @context[:page].parent
+ else
+ mounting_point.pages[@template_name]
+ end
+
+ ::Liquid::Template.parse(page.source, { mounting_point: mounting_point, page: page })
+ end
+
+ end
+
+ ::Liquid::Template.register_tag('extends', Extends)
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/tags/google_analytics.rb b/lib/locomotive/builder/liquid/tags/google_analytics.rb +28 -0
@@ @@ -0,0 +1,28 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Tags
+ class GoogleAnalytics < ::Liquid::Tag
+
+ Syntax = /(#{::Liquid::Expression}+)?/
+
+ def initialize(tag_name, markup, tokens, context)
+ if markup =~ Syntax
+ @account_id = $1.gsub('\'', '')
+ else
+ raise ::Liquid::SyntaxError.new("Syntax Error in 'google_analytics' - Valid syntax: google_analytics <account_id>")
+ end
+
+ super
+ end
+
+ def render(context)
+ "<!-- google analytics for #{@account_id} -->"
+ end
+ end
+
+ ::Liquid::Template.register_tag('google_analytics', GoogleAnalytics)
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/tags/inline_editor.rb b/lib/locomotive/builder/liquid/tags/inline_editor.rb +16 -0
@@ @@ -0,0 +1,16 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Tags
+ class InlineEditor < ::Liquid::Tag
+
+ def render(context)
+ ''
+ end
+ end
+
+ ::Liquid::Template.register_tag('inline_editor', InlineEditor)
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/tags/locale_switcher.rb b/lib/locomotive/builder/liquid/tags/locale_switcher.rb +72 -0
@@ @@ -0,0 +1,72 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Tags
+
+ # Display the links to change the locale of the current page
+ #
+ # Usage:
+ #
+ # {% locale_switcher %} => <div id="locale-switcher"><a href="/features" class="current en">Features</a><a href="/fr/fonctionnalites" class="fr">Fonctionnalités</a></div>
+ #
+ # {% locale_switcher label: locale, sep: ' - ' }
+ #
+ # options:
+ # - label: iso (de, fr, en, ...etc), locale (Deutsch, Français, English, ...etc), title (page title)
+ # - sep: piece of html code separating 2 locales
+ #
+ # notes:
+ # - "iso" is the default choice for label
+ # - " | " is the default separating code
+ #
+ class LocaleSwitcher < ::Liquid::Tag
+
+ Syntax = /(#{::Liquid::Expression}+)?/
+
+ def initialize(tag_name, markup, tokens, context)
+ @options = { :label => 'iso', :sep => ' | ' }
+
+ if markup =~ Syntax
+ markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/"|'/, '') }
+
+ @options[:exclude] = Regexp.new(@options[:exclude]) if @options[:exclude]
+ else
+ raise ::Liquid::SyntaxError.new("Syntax Error in 'locale_switcher' - Valid syntax: locale_switcher <options>")
+ end
+
+ super
+ end
+
+ def render(context)
+ @site, @page = context.registers[:site], context.registers[:page]
+
+ output = %(<div id="locale-switcher">)
+
+ output += @site.locales.collect do |locale|
+ fullpath = locale == context['current_locale'] ? '/' : locale
+
+ %(<a href="/#{fullpath}" class="#{locale} #{'current' if locale == context['current_locale']}">#{link_label(locale)}</a>)
+ end.join(@options[:sep])
+
+ output += %(</div>)
+ end
+
+ private
+
+ def link_label(locale)
+ case @options[:label]
+ when :iso then locale
+ when :locale then I18n.t("locomotive.locales.#{locale}", :locale => locale)
+ when :title then @page.title # FIXME: this returns nil if the page has not been translated in the locale
+ else
+ locale
+ end
+ end
+
+ end
+
+ ::Liquid::Template.register_tag('locale_switcher', LocaleSwitcher)
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/tags/nav.rb b/lib/locomotive/builder/liquid/tags/nav.rb +167 -0
@@ @@ -0,0 +1,167 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Tags
+ # Display the children pages of the site, current page or the parent page. If not precised, nav is applied on the current page.
+ # The html output is based on the ul/li tags.
+ #
+ # Usage:
+ #
+ # {% nav site %} => <ul class="nav"><li class="on"><a href="/features">Features</a></li></ul>
+ #
+ # {% nav site, no_wrapper: true, exclude: 'contact|about', id: 'main-nav', class: 'nav', active_class: 'on' }
+ #
+ class Nav < ::Liquid::Tag
+
+ Syntax = /(#{::Liquid::Expression}+)?/
+
+ attr_accessor :current_page, :mounting_point
+
+ def initialize(tag_name, markup, tokens, context)
+ if markup =~ Syntax
+ @source = ($1 || 'page').gsub(/"|'/, '')
+ @options = { :id => 'nav', :class => '', :active_class => 'on', :bootstrap => false }
+ markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/"|'/, '') }
+
+ @options[:exclude] = Regexp.new(@options[:exclude]) if @options[:exclude]
+
+ if @options[:snippet]
+ if template = self.parse_snippet_template(context, @options[:snippet])
+ @options[:liquid_render] = template
+ end
+ end
+ else
+ raise ::Liquid::SyntaxError.new("Syntax Error in 'nav' - Valid syntax: nav <site|parent|page|<path to a page>> <options>")
+ end
+
+ super
+ end
+
+ def render(context)
+ self.set_accessors_from_context(context)
+
+ children_output = []
+
+ entries = self.fetch_entries
+
+ entries.each_with_index do |p, index|
+ css = []
+ css << 'first' if index == 0
+ css << 'last' if index == entries.size - 1
+
+ children_output << render_entry_link(p, css.join(' '), 1)
+ end
+
+ output = children_output.join("\n")
+
+ if @options[:no_wrapper] != 'true'
+ output = %{<ul id="#{@options[:id]}" class="#{@options[:class]}">\n#{output}</ul>}
+ end
+
+ output
+ end
+
+ protected
+
+ def set_accessors_from_context(context)
+ self.current_page = context.registers[:page]
+ self.mounting_point = context.registers[:mounting_point]
+ end
+
+ def parse_snippet_template(context, template_name)
+ source = if template_name.include?('{')
+ template_name
+ else
+ context[:mounting_point].snippets[template_name].try(:source)
+ end
+
+ source ? ::Liquid::Template.parse(source) : nil
+ end
+
+ def fetch_entries
+ children = (case @source
+ when 'site' then self.mounting_point.pages['index']
+ when 'parent' then self.current_page.parent || self.current_page
+ when 'page' then self.current_page
+ else
+ self.mounting_point.pages[@source]
+ end).children.clone
+
+ children.delete_if { |p| !include_page?(p) }
+ end
+
+ # Determines whether or not a page should be a part of the menu
+ def include_page?(page)
+ if !page.listed? || page.templatized? || !page.published?
+ false
+ elsif @options[:exclude]
+ (page.fullpath =~ @options[:exclude]).nil?
+ else
+ true
+ end
+ end
+
+ # Returns a list element, a link to the page and its children
+ def render_entry_link(page, css, depth)
+ selected = self.current_page.fullpath =~ /^#{page.fullpath}/ ? " #{@options[:active_class]}" : ''
+
+ icon = @options[:icon] ? '<span></span>' : ''
+
+ title = @options[:liquid_render] ? @options[:liquid_render].render('page' => page) : page.title
+
+ label = %{#{icon if @options[:icon] != 'after' }#{title}#{icon if @options[:icon] == 'after' }}
+
+ dropdow = ""
+ link_options = ""
+ href = ::I18n.locale.to_s == self.mounting_point.default_locale.to_s ? "/#{page.fullpath}" : "/#{::I18n.locale}/#{page.fullpath}"
+ caret = ""
+
+ if render_children_for_page?(page, depth) && bootstrap?
+ dropdow = "dropdown"
+ link_options = %{class="dropdown-toggle" data-toggle="dropdown"}
+ href = "#"
+ caret = %{<b class="caret"></b>}
+ end
+
+ output = %{<li id="#{page.slug.to_s.dasherize}-link" class="link#{selected} #{css} #{dropdow}">}
+ output << %{<a href="#{href}" #{link_options}>#{label} #{caret}</a>}
+ output << render_entry_children(page, depth.succ) if (depth.succ <= @options[:depth].to_i)
+ output << %{</li>}
+
+ output.strip
+ end
+
+ def render_children_for_page?(page, depth)
+ depth.succ <= @options[:depth].to_i && page.children.reject { |c| !include_page?(c) }.any?
+ end
+
+ # Recursively creates a nested unordered list for the depth specified
+ def render_entry_children(page, depth)
+ output = %{}
+
+ children = page.children.reject { |c| !include_page?(c) }
+ if children.present?
+ output = %{<ul id="#{@options[:id]}-#{page.slug.to_s.dasherize}" class="#{bootstrap? ? "dropdown-menu" : ""}">}
+ children.each do |c, page|
+ css = []
+ css << 'first' if children.first == c
+ css << 'last' if children.last == c
+
+ output << render_entry_link(c, css.join(' '),depth)
+ end
+ output << %{</ul>}
+ end
+
+ output
+ end
+
+ def bootstrap?
+ @options[:bootstrap] == 'true' || @options[:bootstrap] == true
+ end
+
+ ::Liquid::Template.register_tag('nav', Nav)
+ end
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/tags/paginate.rb b/lib/locomotive/builder/liquid/tags/paginate.rb +105 -0
@@ @@ -0,0 +1,105 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Tags
+
+ # Paginate a collection
+ #
+ # Usage:
+ #
+ # {% paginate contents.projects by 5 %}
+ # {% for project in paginate.collection %}
+ # {{ project.name }}
+ # {% endfor %}
+ # {% endpaginate %}
+ #
+
+ class Paginate < ::Liquid::Block
+
+ Syntax = /(#{::Liquid::Expression}+)\s+by\s+([0-9]+)/
+
+ def initialize(tag_name, markup, tokens, context)
+ if markup =~ Syntax
+ @collection_name = $1
+ @per_page = $2.to_i
+ else
+ raise ::Liquid::SyntaxError.new("Syntax Error in 'paginate' - Valid syntax: paginate <collection> by <number>")
+ end
+
+ super
+ end
+
+ def render(context)
+ context.stack do
+ collection = context[@collection_name]
+
+ raise ::Liquid::ArgumentError.new("Cannot paginate array '#{@collection_name}'. Not found.") if collection.nil?
+
+ pagination = collection.send(:paginate, {
+ :page => context['current_page'],
+ :per_page => @per_page }).stringify_keys!
+
+ page_count, current_page = pagination['total_pages'], pagination['current_page']
+
+ path = sanitize_path(context['fullpath'])
+
+ pagination['previous'] = link(I18n.t('pagination.previous'), current_page - 1, path) if pagination['previous_page']
+ pagination['next'] = link(I18n.t('pagination.next'), current_page + 1, path) if pagination['next_page']
+ pagination['parts'] = []
+
+ hellip_break = false
+
+ if page_count > 1
+ 1.upto(page_count) do |page|
+ if current_page == page
+ pagination['parts'] << no_link(page)
+ elsif page == 1
+ pagination['parts'] << link(page, page, path)
+ elsif page == page_count - 1
+ pagination['parts'] << link(page, page, path)
+ elsif page <= current_page - window_size or page >= current_page + window_size
+ next if hellip_break
+ pagination['parts'] << no_link('&hellip;')
+ hellip_break = true
+ next
+ else
+ pagination['parts'] << link(page, page, path)
+ end
+
+ hellip_break = false
+ end
+ end
+
+ context['paginate'] = pagination
+
+ render_all(@nodelist, context)
+ end
+ end
+
+ private
+
+ def sanitize_path(path)
+ _path = path.gsub(/page=[0-9]+&?/, '').gsub(/_pjax=true&?/, '')
+ _path = _path.slice(0..-2) if _path.last == '?' || _path.last == '&'
+ _path
+ end
+
+ def window_size
+ 3
+ end
+
+ def no_link(title)
+ { 'title' => title, 'is_link' => false, 'hellip_break' => title == '&hellip;' }
+ end
+
+ def link(title, page, path)
+ _path = %(#{path}#{path.include?('?') ? '&' : '?'}page=#{page})
+ { 'title' => title, 'url' => _path, 'is_link' => true }
+ end
+ end
+
+ ::Liquid::Template.register_tag('paginate', Paginate)
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/tags/seo.rb b/lib/locomotive/builder/liquid/tags/seo.rb +74 -0
@@ @@ -0,0 +1,74 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Tags
+ module SEO
+
+ class Base < ::Liquid::Tag
+
+ def render(context)
+ %{
+ #{self.render_title(context)}
+ #{self.render_metadata(context)}
+ }
+ end
+
+ protected
+
+ def render_title(context)
+ title = self.value_for(:seo_title, context)
+ title = context.registers[:site].name if title.blank?
+
+ %{
+ <title>#{title}</title>
+ }
+ end
+
+ def render_metadata(context)
+ %{
+ <meta name="description" content="#{self.value_for(:meta_description, context)}" />
+ <meta name="keywords" content="#{self.value_for(:meta_keywords, context)}" />
+ }
+ end
+
+ # Removes whitespace and quote characters from the input
+ def sanitized_string(string)
+ string ? string.strip.gsub(/"/, '') : ''
+ end
+
+ def value_for(attribute, context)
+ object = self.metadata_object(context)
+ value = object.try(attribute.to_sym).blank? ? context.registers[:site].send(attribute.to_sym) : object.send(attribute.to_sym)
+ self.sanitized_string(value)
+ end
+
+ def metadata_object(context)
+ context['content_instance'] || context['page']
+ end
+ end
+
+ class Title < Base
+
+ def render(context)
+ self.render_title(context)
+ end
+
+ end
+
+ class Metadata < Base
+
+ def render(context)
+ self.render_metadata(context)
+ end
+
+ end
+
+ end
+
+ ::Liquid::Template.register_tag('seo', SEO::Base)
+ ::Liquid::Template.register_tag('seo_title', SEO::Title)
+ ::Liquid::Template.register_tag('seo_metadata', SEO::Metadata)
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/tags/snippet.rb b/lib/locomotive/builder/liquid/tags/snippet.rb +40 -0
@@ @@ -0,0 +1,40 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Tags
+
+ class Snippet < ::Liquid::Include
+
+ def render(context)
+ source = context.registers[:mounting_point].snippets[@template_name].try(:source)
+
+ partial = ::Liquid::Template.parse(source)
+
+ variable = context[@variable_name || @template_name[1..-2]]
+
+ context.stack do
+ @attributes.each do |key, value|
+ context[key] = context[value]
+ end
+
+ output = (if variable.is_a?(Array)
+ variable.collect do |variable|
+ context[@template_name[1..-2]] = variable
+ partial.render(context)
+ end
+ else
+ context[@template_name[1..-2]] = variable
+ partial.render(context)
+ end)
+
+ output
+ end
+ end
+
+ end
+
+ ::Liquid::Template.register_tag('include', Snippet)
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/liquid/tags/with_scope.rb b/lib/locomotive/builder/liquid/tags/with_scope.rb +43 -0
@@ @@ -0,0 +1,43 @@
+ module Locomotive
+ module Builder
+ module Liquid
+ module Tags
+ class WithScope < ::Liquid::Block
+
+ def initialize(tag_name, markup, tokens, context)
+ @options = {}
+
+ markup.scan(::Liquid::TagAttributes) do |key, value|
+ @options[key] = value
+ end
+
+ super
+ end
+
+ def render(context)
+ context.stack do
+ context['with_scope'] = decode(@options, context)
+ render_all(@nodelist, context)
+ end
+ end
+
+ private
+
+ def decode(attributes, context)
+ attributes.each_pair do |key, value|
+ attributes[key] = (case value
+ when /^true|false$/i then value == 'true'
+ when /^[0-9]+$/ then value.to_i
+ when /^["|'](.+)["|']$/ then $1.gsub(/^["|']/, '').gsub(/["|']$/, '')
+ else
+ context[value] || value
+ end)
+ end
+ end
+ end
+
+ ::Liquid::Template.register_tag('with_scope', WithScope)
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/misc.rb b/lib/locomotive/builder/misc.rb +1 -0
@@ @@ -0,0 +1 @@
+ require 'locomotive/builder/misc/httparty.rb'
\ No newline at end of file
locomotive/builder/misc/httparty.rb b/lib/locomotive/builder/misc/httparty.rb +46 -0
@@ @@ -0,0 +1,46 @@
+ require 'uri'
+
+ module Locomotive
+ module Builder
+ module Httparty
+ class Webservice
+
+ include ::HTTParty
+
+ def self.consume(url, options = {})
+ url = ::HTTParty.normalize_base_uri(url)
+
+ uri = URI.parse(url)
+ options[:base_uri] = "#{uri.scheme}://#{uri.host}"
+ options[:base_uri] += ":#{uri.port}" if uri.port != 80
+ path = uri.request_uri
+
+ options.delete(:format) if options[:format] == 'default'
+
+ username, password = options.delete(:username), options.delete(:password)
+ options[:basic_auth] = { username: username, password: password } if username
+
+ path ||= '/'
+
+ # puts "[WebService] consuming #{path}, #{options.inspect}"
+
+ response = self.get(path, options)
+
+ if response.code == 200
+ if response.respond_to?(:underscore_keys)
+ response.underscore_keys
+ else
+ response.collect(&:underscore_keys)
+ end
+ else
+ # TODO: handle errors
+ puts "[Locomotive][Builder][Error] consuming #{path}, #{options.inspect}, response = #{response.inspect}"
+ nil
+ end
+
+ end
+
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/server.rb b/lib/locomotive/builder/server.rb +33 -11
@@ @@ -1,23 +1,45 @@
- require "locomotive/builder/server/middleware"
- require "locomotive/builder/server/index"
- require "locomotive/builder/server/pages"
- require "locomotive/builder/server/not_found"
+ require 'rack/showexceptions'
+ require 'locomotive/builder/server/middleware'
+ require 'locomotive/builder/server/favicon'
+ require 'locomotive/builder/server/dynamic_assets'
+ require 'locomotive/builder/server/path'
+ require 'locomotive/builder/server/locale'
+ require 'locomotive/builder/server/page'
+ require 'locomotive/builder/server/not_found'
+ require 'locomotive/builder/server/renderer'
+
+ require 'locomotive/builder/liquid'
+ require 'locomotive/builder/misc'
module Locomotive::Builder
class Server
+
def initialize(reader)
@reader = reader
@app = Rack::Builder.new do
- use Rack::Lint
- use Index
- use Pages
- run NotFound.new
- end
+ use Rack::ShowExceptions
+ use Rack::Lint
+
+ use Rack::Static, {
+ urls: ['/images', '/fonts', '/samples'],
+ root: File.join(reader.mounting_point.path, 'public')
+ }
+ use Favicon
+ use DynamicAssets
+ use Path
+ use Locale
+ use Page
+ use NotFound
+ use Renderer
+
+ run Renderer.new
+ end
end
-
+
def call(env)
- env["steam.mounting_point"] = @reader.mounting_point
+ env['builder.mounting_point'] = @reader.mounting_point
@app.call(env)
end
+
end
end
\ No newline at end of file
locomotive/builder/server/dynamic_assets.rb b/lib/locomotive/builder/server/dynamic_assets.rb +31 -0
@@ @@ -0,0 +1,31 @@
+ module Locomotive::Builder
+ class Server
+
+ class DynamicAssets < Middleware
+
+ def call(env)
+ self.set_accessors(env)
+
+ path = env['PATH_INFO']
+
+ if path =~ /^\/(stylesheets|javascripts)\//
+
+ mime_type = MIME::Types.type_for(path).first.try(:to_s) || 'text/plain'
+ asset = self.mounting_point.theme_assets.detect do |_asset|
+ _asset.path == path
+ end
+
+ if asset
+ [200, { 'Content-Type' => mime_type }, [asset.content]]
+ else
+ [404, { 'Content-Type' => mime_type }, ['Asset not found']]
+ end
+ else
+ app.call(env)
+ end
+ end
+
+ end
+
+ end
+ end
\ No newline at end of file
locomotive/builder/server/favicon.rb b/lib/locomotive/builder/server/favicon.rb +17 -0
@@ @@ -0,0 +1,17 @@
+ module Locomotive::Builder
+ class Server
+
+ class Favicon < Middleware
+
+ def call(env)
+ if env['PATH_INFO'] == '/favicon.ico'
+ [200, { 'Content-Type' => 'image/vnd.microsoft.icon' }, ['']]
+ else
+ app.call(env)
+ end
+ end
+
+ end
+
+ end
+ end
\ No newline at end of file
locomotive/builder/server/index.rb b/lib/locomotive/builder/server/index.rb +0 -13
@@ @@ -1,13 +0,0 @@
- module Locomotive::Builder
- class Server
- class Index < Middleware
- def call(env)
- if env['PATH_INFO'] == '/'
- [404, {'Content-Type' => 'text/html'}, [env["steam.mounting_point"].pages["index"].source]]
- else
- super
- end
- end
- end
- end
- end
\ No newline at end of file
locomotive/builder/server/locale.rb b/lib/locomotive/builder/server/locale.rb +43 -0
@@ @@ -0,0 +1,43 @@
+ module Locomotive::Builder
+ class Server
+
+ # Set the locale from the path if possible or use the default one
+ # Examples:
+ # /fr/index => locale = :fr
+ # /fr/ => locale = :fr
+ # /index => locale = :en (default one)
+ #
+ class Locale < Middleware
+
+ def call(env)
+ self.set_accessors(env)
+
+ self.set_locale!(env)
+
+ app.call(env)
+ end
+
+ protected
+
+ def set_locale!(env)
+ path = env['builder.path']
+ locale = self.mounting_point.default_locale
+
+ if path =~ /^(#{self.mounting_point.locales.join('|')})+(\/|$)/
+ locale = $1
+ path = path.gsub($1 + $2, '')
+
+ # TODO: I18n.locale ???
+
+ Locomotive::Mounter.locale = locale
+ end
+
+ puts "[Builder|Locale] path = #{path.inspect}, locale = #{locale.inspect}"
+
+ env['builder.locale'] = locale
+ env['builder.path'] = path
+ end
+
+ end
+ end
+ end
\ No newline at end of file
locomotive/builder/server/middleware.rb b/lib/locomotive/builder/server/middleware.rb +26 -4
@@ @@ -1,15 +1,37 @@
module Locomotive::Builder
class Server
+
class Middleware
- attr_accessor :app
-
- def initialize(app)
+
+ attr_accessor :app, :request
+
+ attr_accessor :mounting_point, :page
+
+ def initialize(app = nil)
@app = app
end
-
+
def call(env)
app.call(env)
end
+
+ protected
+
+ def set_accessors(env)
+ self.request = Rack::Request.new(env)
+ self.mounting_point = env['builder.mounting_point']
+ self.page = env['builder.page']
+ end
+
+ def site
+ self.mounting_point.site
+ end
+
+ def params
+ self.request.params
+ end
+
end
+
end
end
\ No newline at end of file
locomotive/builder/server/not_found.rb b/lib/locomotive/builder/server/not_found.rb +11 -2
@@ @@ -1,9 +1,18 @@
module Locomotive::Builder
class Server
- class NotFound
+
+ class NotFound < Middleware
+
def call(env)
- [404, {'Content-Type' => 'text/html'}, [env["steam.mounting_point"].pages["404"].source]]
+ self.set_accessors(env)
+
+ if self.page.nil?
+ env['builder.page'] = self.mounting_point.pages['404']
+ end
+
+ app.call(env)
end
+
end
end
end
\ No newline at end of file
locomotive/builder/server/page.rb b/lib/locomotive/builder/server/page.rb +61 -0
@@ @@ -0,0 +1,61 @@
+ module Locomotive::Builder
+ class Server
+
+ # Sanitize the path from the previous middleware in order
+ # to make it work for the renderer.
+ #
+ class Page < Middleware
+
+ def call(env)
+ self.set_accessors(env)
+
+ self.set_page!(env)
+
+ app.call(env)
+ end
+
+ protected
+
+ def set_page!(env)
+ path = env['builder.path']
+
+ page = self.fetch_page(path)
+
+ puts "[Builder|Page] #{page.inspect}"
+
+ env['builder.page'] = page
+ end
+
+ def fetch_page(path)
+ matchers = self.path_combinations(path)
+
+ self.mounting_point.pages.values.detect do |_page|
+ matchers.include?(_page.safe_fullpath) ||
+ matchers.include?(_page.safe_fullpath.underscore)
+ end
+ end
+
+ def path_combinations(path)
+ self._path_combinations(path.split('/'))
+ end
+
+ def _path_combinations(segments, can_include_template = true)
+ return nil if segments.empty?
+
+ segment = segments.shift
+
+ (can_include_template ? [segment, '*'] : [segment]).map do |_segment|
+ if (_combinations = _path_combinations(segments.clone, can_include_template && _segment != '*'))
+ [*_combinations].map do |_combination|
+ File.join(_segment, _combination)
+ end
+ else
+ [_segment]
+ end
+ end.flatten
+ end
+
+ end
+
+ end
+ end
\ No newline at end of file
locomotive/builder/server/pages.rb b/lib/locomotive/builder/server/pages.rb +0 -14
@@ @@ -1,14 +0,0 @@
- module Locomotive::Builder
- class Server
- class Pages < Middleware
- def call(env)
- requested = env['PATH_INFO'].gsub(/^\//, '')
- if env["steam.mounting_point"].pages.has_key?(requested)
- [200, {'Content-Type' => 'text/html'}, [env["steam.mounting_point"].pages[requested].source]]
- else
- super
- end
- end
- end
- end
- end
\ No newline at end of file
locomotive/builder/server/path.rb b/lib/locomotive/builder/server/path.rb +34 -0
@@ @@ -0,0 +1,34 @@
+ module Locomotive::Builder
+ class Server
+
+ # Sanitize the path from the previous middleware in order
+ # to make it work for the renderer.
+ #
+ class Path < Middleware
+
+ def call(env)
+ self.set_accessors(env)
+
+ self.set_path!(env)
+
+ app.call(env)
+ end
+
+ protected
+
+ def set_path!(env)
+ path = env['PATH_INFO'].clone
+
+ path.gsub!(/\.[a-zA-Z][a-zA-Z0-9]{2,}$/, '')
+ path.gsub!(/^\//, '')
+ path.gsub!(/^[A-Z]:\//, '')
+
+ path = 'index' if path.blank?
+
+ env['builder.path'] = path
+ end
+
+ end
+
+ end
+ end
\ No newline at end of file
locomotive/builder/server/renderer.rb b/lib/locomotive/builder/server/renderer.rb +108 -0
@@ @@ -0,0 +1,108 @@
+ module Locomotive::Builder
+ class Server
+
+ class Renderer < Middleware
+
+ def call(env)
+ self.set_accessors(env)
+
+ puts "[Builder|Renderer] page = #{page.inspect}"
+
+ if self.page
+ if self.page.redirect?
+ [self.page.redirect_type, { 'Location' => self.page.redirect_url, 'Content-Type' => 'text/html' }, []]
+ else
+ type = self.page.response_type || 'text/html'
+ html = self.render
+
+ [200, { 'Content-Type' => type }, [html]]
+ end
+ else
+ puts "argggg"
+ # no page at all, even not the 404 page
+ [404, { 'Content-Type' => 'text/html' }, ['Page not found']]
+ end
+ end
+
+ protected
+
+ def render
+ context = self.locomotive_context
+
+ template = ::Liquid::Template.parse(self.page.source, {
+ page: self.page,
+ mounting_point: self.mounting_point
+ })
+
+ template.render(context)
+ end
+
+ # Build the Liquid context used to render the Locomotive page. It
+ # stores both assigns and registers.
+ #
+ # @param [ Hash ] other_assigns Assigns coming for instance from the controler (optional)
+ #
+ # @return [ Object ] A new instance of the Liquid::Context class.
+ #
+ def locomotive_context(other_assigns = {})
+ assigns = self.locomotive_default_assigns
+
+ # process data from the session
+ # assigns.merge!(self.locomotive_flash_assigns)
+
+ assigns.merge!(other_assigns)
+
+ # TODO: templatized page
+
+ # if defined?(self.page) && self.page.templatized? # add instance from content type
+ # content_entry = self.page.content_entry.to_liquid
+ # ['content_entry', 'entry', @page.target_entry_name].each do |key|
+ # assigns[key] = content_entry
+ # end
+ # end
+
+ # Tip: switch from false to true to enable the re-thrown exception flag
+ ::Liquid::Context.new({}, assigns, self.locomotive_default_registers, true)
+ end
+
+ # Return the default Liquid assigns used inside the Locomotive Liquid context
+ #
+ # @return [ Hash ] The default liquid assigns object
+ #
+ def locomotive_default_assigns
+ {
+ 'site' => self.site.to_liquid,
+ 'page' => self.page,
+ 'models' => Locomotive::Builder::Liquid::Drops::ContentTypes.new,
+ 'contents' => Locomotive::Builder::Liquid::Drops::ContentTypes.new,
+ 'current_page' => self.params[:page],
+ 'params' => self.params,
+ 'path' => self.request.path,
+ 'fullpath' => self.request.fullpath,
+ 'url' => self.request.url,
+ 'now' => Time.now.utc,
+ 'today' => Date.today,
+ 'locale' => I18n.locale.to_s,
+ 'default_locale' => self.mounting_point.default_locale.to_s,
+ 'locales' => self.mounting_point.locales.map(&:to_s),
+ 'current_user' => {}
+ }
+ end
+
+ # Return the default Liquid registers used inside the Locomotive Liquid context
+ #
+ # @return [ Hash ] The default liquid registers object
+ #
+ def locomotive_default_registers
+ {
+ site: self.site,
+ page: self.page,
+ mounting_point: self.mounting_point,
+ inline_editor: false
+ }
+ end
+
+ end
+
+ end
+ end
\ No newline at end of file
locomotivecms_builder.gemspec +23 -14
@@ @@ -4,25 +4,34 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'locomotive/builder/version'
Gem::Specification.new do |gem|
- gem.name = "locomotivecms_builder"
+ gem.name = 'locomotivecms_builder'
gem.version = Locomotive::Builder::VERSION
- gem.authors = ["Rodrigo Alvarez"]
- gem.email = ["papipo@gmail.com"]
+ gem.authors = ['Didier Lafforgue', 'Rodrigo Alvarez']
+ gem.email = ['papipo@gmail.com']
gem.description = %q{TODO: Write a gem description}
gem.summary = %q{TODO: Write a gem summary}
- gem.homepage = ""
+ gem.homepage = ''
gem.files = `git ls-files`.split($/)
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
- gem.require_paths = ["lib"]
- gem.executables = ["builder"]
-
- gem.add_dependency "thor"
- gem.add_dependency "thin"
- # gem.add_dependency "locomotivecms_mounter" # remove from Gemfile before adding it here
- gem.add_development_dependency "rspec"
- gem.add_development_dependency "vcr"
- gem.add_development_dependency "webmock", "~> 1.8.0"
- gem.add_development_dependency "rack-test"
+ gem.require_paths = ['lib']
+ gem.executables = ['builder']
+
+ gem.add_dependency 'thor'
+ gem.add_dependency 'thin'
+ gem.add_dependency 'locomotive_liquid', '~> 2.4.1'
+ gem.add_dependency 'RedCloth', '~> 4.2.9'
+ gem.add_dependency 'dragonfly', '~> 0.9.12'
+ gem.add_dependency 'rack-cache', '~> 1.1'
+ gem.add_dependency 'rack-rescue', '~> 0.1.2'
+ gem.add_dependency 'rmagick', '2.12.2'
+ gem.add_dependency 'httmultiparty', '~> 0.3.8'
+ gem.add_dependency 'will_paginate', '~> 3.0.3'
+ # gem.add_dependency 'locomotivecms_mounter' # remove from Gemfile before adding it here
+
+ gem.add_development_dependency 'rspec'
+ gem.add_development_dependency 'vcr'
+ gem.add_development_dependency 'webmock', '~> 1.8.0'
+ gem.add_development_dependency 'rack-test'
end