Renamed to Wagon ;D

Rodrigo Alvarez committed Feb 27, 2013
commit ae585cf0eae3e4178ecda0034d6904015fb91db0
Showing 145 changed files with 3524 additions and 3489 deletions
Gemfile +1 -1
@@ @@ -1,6 +1,6 @@
source 'https://rubygems.org'
- # Specify your gem's dependencies in builder.gemspec
+ # Specify your gem's dependencies in wagon.gemspec
gemspec
gem 'locomotivecms_mounter', path: '../gems/mounter', require: false
README.md +10 -10
@@ @@ -1,6 +1,6 @@
- # LocomotiveCMS::Builder
+ # LocomotiveCMS::Wagon
- The LocomotiveCMS builder is a site generator for the LocomotiveCMS engine powered by all the efficient and modern HTML development tools (Haml, SASS, Compass, Less).
+ The LocomotiveCMS wagon is a site generator for the LocomotiveCMS engine powered by all the efficient and modern HTML development tools (Haml, SASS, Compass, Less).
(TO BE COMPLETED)
@@ @@ -15,10 +15,10 @@ Also, please, keep in mind, that is nearly an alpha version so it is not stable
Note: If you want to contribute, you may consider to fork it instead
- ### Get the source of the builder
+ ### Get the source of the wagon
- $ git clone git://github.com/locomotivecms/builder.git
- $ cd builder
+ $ git clone git://github.com/locomotivecms/wagon.git
+ $ cd wagon
$ git checkout wip
Note: Again, if you want to contribute, you may consider to fork it instead
@@ @@ -31,15 +31,15 @@ Also, please, keep in mind, that is nearly an alpha version so it is not stable
#### Run the server with a default site
- $ bundle exec bin/builder server <path to the mounter gem>/spec/fixtures/default
+ $ bundle exec bin/wagon server <path to the mounter gem>/spec/fixtures/default
#### Push a site (WIP)
- $ bundle exec bin/builder push <path to your LocomotiveCMS local site> <url of your remote LocomotiveCMS site> <email of your admin account> <password>
+ $ bundle exec bin/wagon push <path to your LocomotiveCMS local site> <url of your remote LocomotiveCMS site> <email of your admin account> <password>
#### Pull a site (Not tested yet)
- $ bundle exec bin/builder pull <url of your remote LocomotiveCMS site> <email of your admin account> <password>
+ $ bundle exec bin/wagon pull <url of your remote LocomotiveCMS site> <email of your admin account> <password>
## Contributing
@@ @@ -53,7 +53,7 @@ Also, please, keep in mind, that is nearly an alpha version so it is not stable
Add this line to your application's Gemfile:
- gem 'locomotive_builder'
+ gem 'locomotive_wagon'
And then execute:
@@ @@ -61,7 +61,7 @@ And then execute:
Or install it yourself as:
- $ gem install locomotive_builder
+ $ gem install locomotive_wagon
## Usage
Rakefile +6 -6
@@ @@ -12,17 +12,17 @@ require 'rubygems/package_task'
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
- require 'locomotive/builder'
- require 'locomotive/builder/version'
+ require 'locomotive/wagon'
+ require 'locomotive/wagon/version'
- gemspec = eval(File.read('locomotivecms_builder.gemspec'))
+ gemspec = eval(File.read('locomotivecms_wagon.gemspec'))
Gem::PackageTask.new(gemspec) do |pkg|
pkg.gem_spec = gemspec
end
desc 'build the gem and release it to rubygems.org'
task :release => :gem do
- sh "gem push pkg/locomotivecms_builder-#{gemspec.version}.gem"
+ sh "gem push pkg/locomotivecms_wagon-#{gemspec.version}.gem"
end
namespace :development do
@@ @@ -38,9 +38,9 @@ namespace :development do
FileUtils.rm_rf(File.join(File.dirname(__FILE__), 'site'))
VCR.use_cassette('pull') do
- exit unless Locomotive::Builder.clone("site", {"host" => "http://locomotive.engine.dev:3000"}, "email" => "admin@locomotivecms.com", "password" => "locomotive")
+ exit unless Locomotive::Wagon.clone("site", {"host" => "http://locomotive.engine.dev:3000"}, "email" => "admin@locomotivecms.com", "password" => "locomotive")
end
- Locomotive::Builder.push("site", {"host" => "http://locomotive.engine.dev:3000"}, "email" => "admin@locomotivecms.com", "password" => "locomotive", "force" => true, "data" => true)
+ Locomotive::Wagon.push("site", {"host" => "http://locomotive.engine.dev:3000"}, "email" => "admin@locomotivecms.com", "password" => "locomotive", "force" => true, "data" => true)
end
end
TODO +2 -2
@@ @@ -36,7 +36,7 @@ x params to launch the thin server (port, address ?)
- pull
- translations (Rod)
- version checkings:
- - builder when running it
+ - wagon when running it
- engine when pushing a site
- nice error page (replace the default exception middleware) to display:
- liquid errors
@@ @@ -48,7 +48,7 @@ x params to launch the thin server (port, address ?)
- refactoring:
x generators/sites -> generators/site
x list is only for site generators
- x move the call to the CC generator directly in the builder.rb file
+ x move the call to the CC generator directly in the wagon.rb file
*** FEATURES ***
bin/builder +0 -9
@@ @@ -1,9 +0,0 @@
- #!/usr/bin/env ruby
-
- # needed if you launch it without bundler
- $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
-
- require 'locomotive/builder'
- require 'locomotive/builder/cli'
-
- Locomotive::Builder::CLI::Main.start
\ No newline at end of file
bin/wagon +9 -0
@@ @@ -0,0 +1,9 @@
+ #!/usr/bin/env ruby
+
+ # needed if you launch it without bundler
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
+
+ require 'locomotive/wagon'
+ require 'locomotive/wagon/cli'
+
+ Locomotive::Wagon::CLI::Main.start
\ No newline at end of file
generators/blank/Gemfile.tt +1 -1
@@ @@ -2,7 +2,7 @@ source 'https://rubygems.org'
# ruby '1.9.3'
- gem 'locomotivecms_builder', '<%= config[:version] -%>'
+ gem 'locomotivecms_wagon', '<%= config[:version] -%>'
group :development do
# Mac OS X
generators/blank/config.ru +2 -2
@@ @@ -1,3 +1,3 @@
- require 'locomotive/builder/standalone_server'
+ require 'locomotive/wagon/standalone_server'
- run Locomotive::Builder::StandaloneServer.new(File.expand_path('.'))
\ No newline at end of file
+ run Locomotive::Wagon::StandaloneServer.new(File.expand_path('.'))
\ No newline at end of file
generators/bootstrap/Gemfile.tt +1 -1
@@ @@ -2,7 +2,7 @@ source 'https://rubygems.org'
# ruby '1.9.3'
- gem 'locomotivecms_builder', '<%= config[:version] -%>'
+ gem 'locomotivecms_wagon', '<%= config[:version] -%>'
group :development do
# Mac OS X
generators/bootstrap/config.ru +2 -2
@@ @@ -1,3 +1,3 @@
- require 'locomotive/builder/standalone_server'
+ require 'locomotive/wagon/standalone_server'
- run Locomotive::Builder::StandaloneServer.new(File.expand_path('.'))
\ No newline at end of file
+ run Locomotive::Wagon::StandaloneServer.new(File.expand_path('.'))
\ No newline at end of file
locomotive/builder.rb b/lib/locomotive/builder.rb +0 -163
@@ @@ -1,163 +0,0 @@
- require 'locomotive/builder/version'
- require 'locomotive/builder/logger'
- require 'locomotive/builder/exceptions'
-
- module Locomotive
- module Builder
-
- # Create a site from a site generator.
- #
- # @param [ String ] name The name of the site (underscored)
- # @param [ String ] path The destination path of the site
- # @param [ Object ] generator The wrapping class of the generator itself
- #
- def self.init(name, path, generator)
- generator.klass.start [name, path]
- end
-
- # Start the thin server which serves the LocomotiveCMS site from the system.
- #
- # @param [ String ] path The path of the site
- # @param [ Hash ] options The options for the thin server (host, port)
- #
- def self.serve(path, options)
- if reader = self.require_mounter(path, true)
- Bundler.require 'misc'
-
- require 'thin'
- require 'locomotive/builder/server'
-
- server = Thin::Server.new(options[:host], options[:port], Locomotive::Builder::Server.new(reader))
- server.threaded = true # TODO: make it an option ?
- server.start
- end
- end
-
- # Generate components for the LocomotiveCMS site such as content types, snippets, pages.
- #
- # @param [ Symbol ] name The name of the generator
- # @param [ Array ] *args The arguments for the generator
- #
- def self.generate(name, *args)
- Bundler.require 'misc'
-
- lib = "locomotive/builder/generators/#{name}"
- require lib
-
- generator = lib.camelize.constantize.new(args, {}, {})
- generator.invoke_all
- end
-
- # Push a site to a remote LocomotiveCMS engine described
- # by the config/deploy.yml file of the site and for a specific environment.
- #
- # @param [ String ] path The path of the site
- # @param [ Hash ] connection_info The information to get connected to the remote site
- # @param [ Hash ] options The options passed to the push process
- #
- def self.push(path, connection_info, options = {})
- if reader = self.require_mounter(path, true)
- Bundler.require 'misc'
-
- writer = Locomotive::Mounter::Writer::Api.instance
-
- connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api"
-
- _options = { mounting_point: reader.mounting_point, console: true }.merge(options).symbolize_keys
- _options[:only] = _options.delete(:resources)
-
- writer.run!(_options.merge(connection_info))
- end
- end
-
- # Pull a site from a remote LocomotiveCMS engine described
- # by the config/deploy.yml file of the site and for a specific environment.
- #
- # @param [ String ] path The path of the site
- # @param [ Hash ] connection_info The information to get connected to the remote site
- # @param [ Hash ] options The options passed to the pull process
- #
- def self.pull(path, connection_info, options = {})
- self.require_mounter(path)
-
- Bundler.require 'misc'
-
- connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api"
-
- _options = { console: true }.merge(options)
- _options[:only] = _options.delete(:resources)
-
- reader = Locomotive::Mounter::Reader::Api.instance
- reader.run!(_options.merge(connection_info))
-
- writer = Locomotive::Mounter::Writer::FileSystem.instance
- writer.run!(mounting_point: reader.mounting_point, target_path: path)
- rescue Exception => e
- puts e.backtrace
- end
-
- def self.clone(path, connection_info, options = {})
- if File.exists?(path)
- puts "Path already exists. If it's an existing site, you might want to pull instead of clone."
- return false
- end
- require 'locomotive/mounter'
-
- connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api"
-
- _options = options.dup
- _options[:only] = _options.delete(:resources)
-
- reader = Locomotive::Mounter::Reader::Api.instance
- reader.run!(_options.merge(connection_info))
-
- writer = Locomotive::Mounter::Writer::FileSystem.instance
- writer.run!(mounting_point: reader.mounting_point, target_path: path)
- # rescue Exception => e
- # puts e.backtrace
- end
-
- # Destroy a remote site
- #
- # @param [ String ] path The path of the site
- # @param [ Hash ] connection_info The information to get connected to the remote site
- # @param [ Hash ] options The options passed to the push process
- #
- def self.destroy(path, connection_info, options = {})
- self.require_mounter(path)
-
- connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api"
-
- Locomotive::Mounter::EngineApi.set_token connection_info.symbolize_keys
- Locomotive::Mounter::EngineApi.delete('/current_site.json')
- end
-
- # Load the Locomotive::Mounter lib and set it up (logger, ...etc).
- # If the second parameter is set to true, then the method builds
- # an instance of the reader from the path passed in first parameter.
- #
- # @param [ String ] path The path to the local site
- # @param [ Boolean ] get_reader Tell if it builds an instance of the reader.
- #
- # @param [ Object ] An instance of the reader is the get_reader parameter has been set.
- #
- def self.require_mounter(path, get_reader = false)
- Locomotive::Builder::Logger.setup(path, false)
-
- require 'locomotive/mounter'
-
- Locomotive::Mounter.logger = Locomotive::Builder::Logger.instance.logger
-
- if get_reader
- begin
- reader = Locomotive::Mounter::Reader::FileSystem.instance
- reader.run!(path: path)
- reader
- rescue Exception => e
- raise Locomotive::Builder::MounterException.new "Unable to read the local LocomotiveCMS site. Please check the logs.", e
- end
- end
- end
-
- end
- end
\ No newline at end of file
locomotive/builder/cli.rb b/lib/locomotive/builder/cli.rb +0 -229
@@ @@ -1,229 +0,0 @@
- require 'thor'
- require 'thor/runner'
-
- module Locomotive
- module Builder
- module CLI
-
- module CheckPath
-
- protected
-
- # Check if the path given in option ('.' by default) points to a LocomotiveCMS
- # site. It is also possible to pass a path other than the one from the options.
- #
- # @param [ String ] path The optional path instead of options['path']
- #
- # @return [ String ] The fullpath to the LocomotiveCMS site or nil if it is not a valid site.
- #
- def check_path!(path = nil)
- path ||= options['path']
-
- path = path == '.' ? Dir.pwd : File.expand_path(path)
-
- (File.exists?(File.join(path, 'config', 'site.yml')) ? path : nil).tap do |_path|
- if _path.nil?
- say 'The path does not point to a LocomotiveCMS site', :red
- end
- end
- end
-
- end
-
- class Generate < Thor
-
- include Locomotive::Builder::CLI::CheckPath
-
- class_option :path, aliases: '-p', type: :string, default: '.', optional: true, desc: 'if your LocomotiveCMS site is not in the current path'
-
- desc 'content_type NAME FIELDS', 'Create a content type with NAME as the slug and FIELDS as the list of fields.'
- long_desc <<-LONGDESC
- Create a content type with NAME as the slug and FIELDS as the list of fields.
- The fields follows that schema:
-
- field_1[:type][:required] field_2[:type][:required]
-
- Examples:
-
- * builder generate content_type songs name:string duration:string
-
- * builder generate content_type posts title body:text:true published_at:date
- LONGDESC
- def content_type(name, *fields)
- say('The fields are missing', :red) and return false if fields.empty?
-
- if check_path!
- Locomotive::Builder.generate :content_type, name, self.options['path'], fields
- end
- end
-
- desc 'page FULLPATH', 'Create a page. No need to pass an extension to the FULLPATH arg'
- long_desc <<-LONGDESC
- Create a page. The generator will ask for the extension (liquid or haml) and also
- if the page is localized or not.
-
- Examples:
-
- * builder generate page contact
-
- * builder generate page about_us/me
- LONGDESC
- def page(fullpath)
- if check_path!
- Locomotive::Builder.generate :page, fullpath, self.options['path']
- end
- end
-
- desc 'snippet SLUG', 'Create a snippet'
- long_desc <<-LONGDESC
- Create a snippet. The generator will ask for the extension (liquid or haml) and also
- if the snippet is localized or not.
-
- Example:
-
- * builder generate snippet footer
- LONGDESC
- def snippet(slug)
- if check_path!
- Locomotive::Builder.generate :snippet, slug, self.options['path']
- end
- end
-
- end
-
- class Main < Thor
-
- include Locomotive::Builder::CLI::CheckPath
-
- desc 'version', 'Version of the LocomotiveCMS builder'
- def version
- require 'locomotive/builder/version'
- say Locomotive::Builder::VERSION
- end
-
- desc 'init NAME [PATH]', 'Create a brand new LocomotiveCMS site'
- method_option :template, aliases: '-t', type: 'string', default: 'blank', desc: 'instead of building from a blank site, you can have a pre-fetched site with form a template (see the templates command)'
- def init(name, path = '.')
- require 'locomotive/builder/generators/site'
- generator = Locomotive::Builder::Generators::Site.get(options[:template])
- if generator.nil?
- say "Unknown site template '#{options[:template]}'", :red
- else
- begin
- Locomotive::Builder.init(name, path, generator)
- rescue GeneratorException => e
- say e.message, :red
- end
- end
- end
-
- desc 'generate TYPE ...ARGS', 'Generate resources (content_types, page, snippets) for a LocomotiveCMS site'
- subcommand 'generate', Generate
-
- desc 'list_templates', 'List all the templates to create either a site or a content type'
- def list_templates
- require 'locomotive/builder/generators/site'
- if Locomotive::Builder::Generators::Site.empty?
- say 'No templates', :red
- else
- Locomotive::Builder::Generators::Site.list.each do |info|
- say info.name, :bold, false
- say " - #{info.description}" unless info.description.blank?
- end
- end
- end
-
- desc 'serve [PATH]', 'Serve a LocomotiveCMS site from the file system'
- method_option :host, aliases: '-h', type: 'string', default: '0.0.0.0', desc: 'The host (address) of the Thin server'
- method_option :port, aliases: '-p', type: 'string', default: '3333', desc: 'The port of the Thin server'
- def serve(path = '.')
- if check_path!(path)
- begin
- Locomotive::Builder.serve(path, options)
- rescue Exception => e
- say e.message, :red
- end
- end
- end
-
- desc 'push ENV [PATH]', 'Push a site to a remote LocomotiveCMS engine'
- method_option :resources, aliases: '-r', type: 'array', default: nil, desc: 'Only push the resource(s) passed in argument'
- method_option :force, aliases: '-f', type: 'boolean', default: false, desc: 'Force the push of a resource'
- method_option :data, aliases: '-d', type: 'boolean', default: false, desc: 'Push the content entries and the editable elements (by default, they are not)'
- def push(env, path = '.')
- if check_path!(path)
- if connection_info = self.retrieve_connection_info(env, path)
- begin
- Locomotive::Builder.push(path, connection_info, options)
- rescue Exception => e
- say e.message, :red
- end
- end
- end
- end
-
- desc 'pull ENV [PATH]', 'Pull a site from a remote LocomotiveCMS engine'
- method_option :resources, aliases: '-r', type: 'array', default: nil, desc: 'Only pull the resource(s) passed in argument'
- # method_option :force, aliases: '-f', type: 'boolean', default: false, desc: 'Force the push of a resource'
- # method_option :data, aliases: '-d', type: 'boolean', default: false, desc: 'Push the content entries and the editable elements (by default, they are not)'
- def pull(env, path = '.')
- if check_path!(path)
- if connection_info = self.retrieve_connection_info(env, path)
- begin
- Locomotive::Builder.pull(path, connection_info, options)
- rescue Exception => e
- say e.message, :red
- end
- end
- end
- end
-
- desc 'destroy ENV [PATH]', 'Destroy a remote LocomotiveCMS engine'
- def destroy(env, path = '.')
- if check_path!(path)
- if connection_info = self.retrieve_connection_info(env, path)
- if ask('Are you sure ?', limited_to: %w(yes no)) == 'yes'
- Locomotive::Builder.destroy(path, connection_info)
- else
- say 'The destroy operation has been cancelled', :red
- end
- end
- end
- end
-
- # desc "pull NAME SITE_URL EMAIL PASSWORD", "Pull an existing LocomotiveCMS site powered by the engine"
- # def pull(name, site_url, email, password)
- # say("ERROR: \"#{name}\" directory already exists", :red) and return if File.exists?(name)
- # Locomotive::Builder.pull(name, site_url, email, password)
- # end
-
- protected
-
- # From a site specified by a path, retrieve the information of the connection
- # for a environment located in the config/deploy.yml file of the site.
- #
- # @param [ String ] env The environment (development, staging, production, ...etc)
- # @param [ String ] path The path of the local site
- #
- # @return [ Hash ] The information of the connection or nil if errors
- #
- def retrieve_connection_info(env, path)
- connection_info = nil
- begin
- path_to_deploy_file = File.join(path, 'config', 'deploy.yml')
- connection_info = YAML::load(File.open(path_to_deploy_file).read)[env.to_s]
-
- if connection_info.nil?
- raise "No #{env.to_s} environment found in the config/deploy.yml file"
- end
- rescue Exception => e
- say "Unable to read the information about the remote LocomotiveCMS site (#{e.message})", :red
- end
- connection_info
- end
-
- end
-
- end
- end
- end
\ No newline at end of file
locomotive/builder/exceptions.rb b/lib/locomotive/builder/exceptions.rb +0 -35
@@ @@ -1,35 +0,0 @@
- module Locomotive
- module Builder
-
- class DefaultException < ::Exception
-
- def initialize(message = nil, parent_exception = nil)
- self.log_backtrace(parent_exception) if parent_exception
-
- super(message)
- end
-
- protected
-
- def log_backtrace(parent_exception)
- full_error_message = "#{parent_exception.message}\n\t"
- full_error_message += parent_exception.backtrace.join("\n\t")
- full_error_message += "\n\n"
- Locomotive::Builder::Logger.fatal full_error_message
- end
-
- end
-
- class MounterException < DefaultException
- end
-
- class GeneratorException < DefaultException
-
- def log_backtrace(parent_exception)
- # Logger not initialized at this step
- end
-
- end
-
- end
- end
locomotive/builder/generators/content_type.rb b/lib/locomotive/builder/generators/content_type.rb +0 -47
@@ @@ -1,47 +0,0 @@
- require 'thor/group'
- require 'ostruct'
- require 'active_support'
- require 'active_support/core_ext'
- require 'faker'
-
- module Locomotive
- module Builder
- module Generators
- class ContentType < Thor::Group
-
- include Thor::Actions
-
- argument :name
- argument :target_path
- argument :fields
-
- def copy_sources
- directory('.', target_path, { recursive: true }, {
- name: self.name,
- fields: extract_fields(fields)
- })
- end
-
- def self.source_root
- File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'generators', 'content_type')
- end
-
- protected
-
- def extract_fields(fields)
- fields.map do |raw_attributes|
- name, type, required = raw_attributes.split(':')
-
- OpenStruct.new({
- name: name,
- type: type || 'string',
- required: %w(true required).include?(required)
- })
- end
- end
-
- end
-
- end
- end
- end
\ No newline at end of file
locomotive/builder/generators/page.rb b/lib/locomotive/builder/generators/page.rb +0 -57
@@ @@ -1,57 +0,0 @@
- require 'thor/group'
- require 'active_support'
- require 'active_support/core_ext'
-
- module Locomotive
- module Builder
- module Generators
- class Page < Thor::Group
-
- include Thor::Actions
-
- argument :slug
- argument :target_path # path to the site
-
- attr_accessor :haml, :locales
-
- def ask_for_haml_and_locales
- self.locales = []
- self.haml = yes?('Do you prefer a HAML template ?')
-
- if yes?('Is your page localized ?')
- self.locales = ask('What are the locales other than the default one (comma separated) ?').split(',').map(&:strip)
- end
- end
-
- def create_page
- extension = self.haml ? 'liquid.haml' : 'liquid'
-
- segments = self.slug.split('/')
- while segment = segments.pop do
- options = { slug: segment, translated: false }
- file_path = File.join(pages_path, segments, segment)
-
- template "template.#{extension}.tt", "#{file_path}.#{extension}", options
-
- self.locales.each do |locale|
- options[:translated] = true
- template "template.#{extension}.tt", "#{file_path}.#{locale}.#{extension}", options
- end
- end
- end
-
- def self.source_root
- File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'generators', 'page')
- end
-
- protected
-
- def pages_path
- File.join(target_path, 'app', 'views', 'pages')
- end
-
- end
-
- end
- end
- end
\ No newline at end of file
locomotive/builder/generators/site.rb b/lib/locomotive/builder/generators/site.rb +0 -98
@@ @@ -1,98 +0,0 @@
- require 'ostruct'
- require 'singleton'
-
- module Locomotive
- module Builder
- module Generators
-
- module Site
-
- # Register a generator by adding it to the list of existing generators.
- #
- # @param [ String ] name The name of the generator
- # @param [ Class ] klass The class of the generator
- # @param [ String ] description The description of the generator (can be nil)
- #
- # @return [ Boolean ] True if the registration has been successful, false otherwise.
- #
- def self.register(name, klass, description = nil)
- Locomotive::Builder::Generators::Site::List.instance.register(name, klass, description)
- end
-
- # Return the information about a generator from its name.
- #
- # @param [ String ] name The name of the generator
- #
- # @return [ Object ] The information of the found generator or nil
- #
- def self.get(name)
- Locomotive::Builder::Generators::Site::List.instance.get(name)
- end
-
- # List all the generators
- #
- # @return [ Array ] The filtered (or not) list of generators
- #
- def self.list
- Locomotive::Builder::Generators::Site::List.instance._list
- end
-
- # Tell if the list of generators is empty or not .
- #
- # @return [ Boolean ] True if empty
- #
- def self.empty?
- Locomotive::Builder::Generators::Site::List.instance._list.empty?
- end
-
- class List
-
- include ::Singleton
-
- attr_accessor :_list
-
- def initialize
- self._list = []
- end
-
- # Return the information about a generator from its name.
- #
- # @param [ String ] name The name of the generator
- #
- # @return [ Object ] The information of the found generator or nil
- #
- def get(name)
- self._list.detect { |entry| entry.name == name.to_sym }
- end
-
- # Register a generator by adding it to the list of existing generators.
- #
- # @param [ String ] name The name of the generator
- # @param [ Class ] klass The class of the generator
- # @param [ String ] description The description of the generator (can be nil)
- #
- # @return [ Boolean ] True if the registration has been successful, false otherwise.
- #
- def register(name, klass, description = nil)
- return false unless self.get(name).nil?
-
- self._list << OpenStruct.new({
- name: name.to_sym,
- klass: klass,
- description: description ? description.strip.gsub("\n", '') : nil
- })
-
- self._list.last
- end
- end
-
- end
-
- end
- end
- end
-
- require 'locomotive/builder/generators/site/base'
- require 'locomotive/builder/generators/site/blank'
- require 'locomotive/builder/generators/site/bootstrap'
- require 'locomotive/builder/generators/site/unzip'
locomotive/builder/generators/site/base.rb b/lib/locomotive/builder/generators/site/base.rb +0 -30
@@ @@ -1,30 +0,0 @@
- require 'thor/group'
- require 'active_support'
- require 'active_support/core_ext'
-
- module Locomotive
- module Builder
- module Generators
- module Site
-
- class Base < Thor::Group
-
- include Thor::Actions
-
- argument :name
- argument :target_path
-
- def self.source_root
- File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'generators', self.name.demodulize.underscore)
- end
-
- def destination
- File.join(target_path, name)
- end
-
- end
-
- end
- end
- end
- end
\ No newline at end of file
locomotive/builder/generators/site/blank.rb b/lib/locomotive/builder/generators/site/blank.rb +0 -23
@@ @@ -1,23 +0,0 @@
- module Locomotive
- module Builder
- module Generators
- module Site
-
- class Blank < Base
-
- def copy_sources
- directory('.', self.destination, { recursive: true }, {
- name: self.name,
- version: Locomotive::Builder::VERSION
- })
- end
-
- end
-
- Locomotive::Builder::Generators::Site.register(:blank, Blank, %{
- A blank LocomotiveCMS site with the minimal files.
- })
- end
- end
- end
- end
\ No newline at end of file
locomotive/builder/generators/site/bootstrap.rb b/lib/locomotive/builder/generators/site/bootstrap.rb +0 -35
@@ @@ -1,35 +0,0 @@
- module Locomotive
- module Builder
- module Generators
- module Site
-
- class Bootstrap < Base
-
- def copy_sources
- directory('.', self.destination, { recursive: true }, {
- name: self.name,
- version: Locomotive::Builder::VERSION
- })
- end
-
- def choose_haml_over_html
- if yes?('Do you prefer HAML templates ?')
- remove_file File.join(self.destination, 'app/views/pages/index.liquid')
- remove_file File.join(self.destination, 'app/views/pages/404.liquid')
- remove_file File.join(self.destination, 'app/views/snippets/footer.liquid')
- else
- remove_file File.join(self.destination, 'app/views/pages/index.liquid.haml')
- remove_file File.join(self.destination, 'app/views/pages/404.liquid.haml')
- remove_file File.join(self.destination, 'app/views/snippets/footer.liquid.haml')
- end
- end
-
- end
-
- Locomotive::Builder::Generators::Site.register(:bootstrap, Bootstrap, %{
- A LocomotiveCMS site powered by Twitter bootstrap (v2.2.2).
- })
- end
- end
- end
- end
\ No newline at end of file
locomotive/builder/generators/site/unzip.rb b/lib/locomotive/builder/generators/site/unzip.rb +0 -81
@@ @@ -1,81 +0,0 @@
- require 'open-uri'
- require 'zip/zipfilesystem'
-
- module Locomotive
- module Builder
- module Generators
- module Site
-
- class Unzip < Base
-
- def prepare
- remove_file join('tmp')
- empty_directory join('tmp')
- end
-
- def ask_for_location
- @location = ask('What is the location (on the filesystem or url) of the zip file ?')
- raise GeneratorException.new('Please enter a location') if @location.blank?
- end
-
- def download_or_copy
- @template_path = join('tmp', File.basename(@location))
-
- if @location =~ /^https?:\/\//
- say "downloading...#{@location}"
- create_file @template_path, open(@location).read
- else
- say "copying...#{@location}"
- create_file @template_path, open(@location, 'rb') { |io| io.read }
- end
- end
-
- def unzip
- say "unzipping...#{@template_path}"
-
- begin
- Zip::ZipFile.open(@template_path) do |zipfile|
- zipfile.each do |file|
- next if file.name =~ /^__MACOSX/
- zipfile.extract(file, join('tmp', file.name))
-
- @path = $1 if file.name =~ /(.*)\/config\/site.yml$/
- end
- end
- rescue Exception => e
- raise GeneratorException.new("Unable to unzip the archive")
- end
-
- raise GeneratorException.new('Not a valid LocomotiveCMS site') if @path.blank?
- end
-
- def copy_sources
- self.class.source_root = File.expand_path(join('tmp', @path, '/'))
- say "copying files from #{self.class.source_root} / #{self.destination}"
- directory('.', self.destination, { recursive: true })
- end
-
- def self.source_root
- # only way to change the source root from the instance
- @@source_root
- end
-
- def self.source_root=(value)
- @@source_root = value
- end
-
- protected
-
- def join(*args)
- File.join(self.destination, *args)
- end
-
- end
-
- Locomotive::Builder::Generators::Site.register(:unzip, Unzip, %{
- Unzip a local or remote (http, https, ftp) zipped LocomotiveCMS site.
- })
- end
- end
- end
- end
\ No newline at end of file
locomotive/builder/generators/snippet.rb b/lib/locomotive/builder/generators/snippet.rb +0 -54
@@ @@ -1,54 +0,0 @@
- require 'thor/group'
- require 'active_support'
- require 'active_support/core_ext'
-
- module Locomotive
- module Builder
- module Generators
- class Snippet < Thor::Group
-
- include Thor::Actions
-
- argument :slug
- argument :target_path # path to the site
-
- attr_accessor :haml, :locales
-
- def ask_for_haml_and_locales
- self.locales = []
- self.haml = yes?('Do you prefer a HAML template ?')
-
- if yes?('Is your snippet localized ?')
- self.locales = ask('What are the locales other than the default one (comma separated) ?').split(',').map(&:strip)
- end
- end
-
- def create_snippet
- extension = self.haml ? 'liquid.haml' : 'liquid'
-
- options = { slug: slug, translated: false }
- file_path = File.join(pages_path, slug)
-
- template "template.#{extension}.tt", "#{file_path}.#{extension}", options
-
- self.locales.each do |locale|
- options[:translated] = true
- template "template.#{extension}.tt", "#{file_path}.#{locale}.#{extension}", options
- end
- end
-
- def self.source_root
- File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'generators', 'snippet')
- end
-
- protected
-
- def pages_path
- File.join(target_path, 'app', 'views', 'snippets')
- end
-
- end
-
- end
- end
- end
\ No newline at end of file
locomotive/builder/liquid.rb b/lib/locomotive/builder/liquid.rb +0 -19
@@ @@ -1,19 +0,0 @@
- require "locomotive/mounter"
- 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|
- 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
locomotive/builder/liquid/drops/base.rb b/lib/locomotive/builder/liquid/drops/base.rb +0 -44
@@ @@ -1,44 +0,0 @@
- # 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 +0 -48
@@ @@ -1,48 +0,0 @@
- module Locomotive
- module Builder
- module Liquid
- module Drops
- class ContentEntry < Base
-
- delegate :seo_title, :meta_keywords, :meta_description, :to => '_source'
-
- def _label
- @_label ||= self._source._label
- end
-
- def _permalink
- @_source._permalink.try(:parameterize)
- end
-
- alias :_slug :_permalink
-
- def next
- self
- end
-
- def previous
- self
- end
-
- def errors
- (@_source.errors || []).inject({}) do |memo, name|
- memo[name] = ::I18n.t('errors.messages.blank')
- memo
- end
- end
-
- def before_method(meth)
- return '' if self._source.nil?
-
- if not @@forbidden_attributes.include?(meth.to_s)
- self._source.send(meth)
- else
- nil
- end
- 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 +0 -121
@@ @@ -1,121 +0,0 @@
- 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
- self.group_entries_by(@content_type, $1)
- elsif (meth.to_s =~ /^(.+)_options$/) == 0
- self.select_options_for(@content_type, $1)
- else
- @content_type.send(meth)
- end
- end
-
- protected
-
- def group_entries_by(content_type, name)
- field = @content_type.find_field(name)
-
- return {} if field.nil? || !%w(belongs_to select).include?(field.type.to_s)
-
- (@content_type.entries || []).group_by do |entry|
- entry.send(name.to_sym)
- end.to_a.collect do |group|
- { name: group.first, entries: group.last }.with_indifferent_access
- end
- end
-
- def select_options_for(content_type, name)
- field = @content_type.find_field(name)
-
- return {} if field.nil? || field.type.to_s != 'select'
-
- field.select_options.map(&:name)
- end
-
- 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.entries.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 +0 -36
@@ @@ -1,36 +0,0 @@
- 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 = @_source.children || []
- _children = _children.sort { |a, b| a.position.to_i <=> b.position.to_i }
- @children ||= liquify(*_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 +0 -21
@@ @@ -1,21 +0,0 @@
- module Locomotive
- module Builder
- module Liquid
- module Drops
- class Site < Base
-
- delegate :name, :seo_title, :meta_description, :meta_keywords, :to => '_source'
-
- def index
- @index ||= self.mounting_point.pages['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 +0 -7
@@ @@ -1,7 +0,0 @@
- 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 +0 -98
@@ @@ -1,98 +0,0 @@
- 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 +0 -154
@@ @@ -1,154 +0,0 @@
- 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 +0 -28
@@ @@ -1,28 +0,0 @@
- 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 +0 -18
@@ @@ -1,18 +0,0 @@
- module Locomotive
- module Builder
- module Liquid
- module Filters
- module Resize
-
- def resize(input, resize_string)
- Locomotive::Builder::Dragonfly.instance.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 +0 -50
@@ @@ -1,50 +0,0 @@
- 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/filters/translate.rb b/lib/locomotive/builder/liquid/filters/translate.rb +0 -24
@@ @@ -1,24 +0,0 @@
- module Locomotive
- module Builder
- module Liquid
- module Filters
- module Translate
-
- def translate(key, locale = nil)
- translation = @context.registers[:mounting_point].translations[key.to_s]
-
- if translation
- translation.get(locale) || translation.get(Locomotive::Mounter.locale)
- else
- "[unknown translation key: #{key}]"
- end
- end
-
- end
-
- ::Liquid::Template.register_filter(Translate)
-
- end
- end
- end
- end
\ No newline at end of file
locomotive/builder/liquid/patches.rb b/lib/locomotive/builder/liquid/patches.rb +0 -47
@@ @@ -1,47 +0,0 @@
- 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 +0 -58
@@ @@ -1,58 +0,0 @@
- 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 +0 -34
@@ @@ -1,34 +0,0 @@
- 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 +0 -5
@@ @@ -1,5 +0,0 @@
- 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 +0 -46
@@ @@ -1,46 +0,0 @@
- 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)
- current_page = context.registers[:page]
-
- element = current_page.find_editable_element(context['block'].try(:name), @slug)
-
- if element.present?
- render_element(context, element)
- else
- super
- end
- end
-
- protected
-
- def render_element(context, element)
- element.content
- 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 +0 -19
@@ @@ -1,19 +0,0 @@
- module Locomotive
- module Builder
- module Liquid
- module Tags
- module Editable
- class Control < Base
-
- def render(context)
- super
- end
-
- 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 +0 -15
@@ @@ -1,15 +0,0 @@
- 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 +0 -15
@@ @@ -1,15 +0,0 @@
- 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 +0 -15
@@ @@ -1,15 +0,0 @@
- 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 +0 -25
@@ @@ -1,25 +0,0 @@
- 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 +0 -28
@@ @@ -1,28 +0,0 @@
- 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 +0 -16
@@ @@ -1,16 +0,0 @@
- 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 +0 -180
@@ @@ -1,180 +0,0 @@
- 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]
- @default_locale = context.registers[:mounting_point].default_locale
-
- output = %(<div id="locale-switcher">)
-
- output += @site.locales.collect do |locale|
- Locomotive::Mounter.with_locale(locale) do
- fullpath = localized_fullpath(locale)
-
- if @page.templatized?
- permalink = context['entry']._permalink
-
- if permalink
- fullpath.gsub!('*', permalink)
- else
- fullpath = '404'
- end
- end
-
- css = link_class(locale, context['locale'])
-
- %(<a href="/#{fullpath}" class="#{css}">#{link_label(locale)}</a>)
- end
- end.join(@options[:sep])
-
- output += %(</div>)
- end
-
- private
-
- def link_class(locale, current_locale)
- css = [locale]
- css << 'current' if locale.to_s == current_locale.to_s
- css.join(' ')
- end
-
- 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
-
- def localized_fullpath(locale)
- # @site.localized_page_fullpath(@page, locale)
-
- return nil if @page.fullpath_translations.blank?
-
- fullpath = @page.safe_fullpath || @page.fullpath_or_default
-
- if locale.to_s == @default_locale.to_s # no need to specify the locale
- @page.index? ? '' : fullpath
- elsif @page.index? # avoid /en/index or /fr/index, prefer /en or /fr instead
- locale
- else
- File.join(locale, fullpath)
- end
- end
-
- end
-
- ::Liquid::Template.register_tag('locale_switcher', LocaleSwitcher)
- end
- end
- end
- end
- # 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.to_s == context['default_locale'].to_s ? '/' : locale
-
- # %(<a href="/#{fullpath}" class="#{locale} #{'current' if locale.to_s == context['default_locale'].to_s}">#{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 +0 -167
@@ @@ -1,167 +0,0 @@
- 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 +0 -105
@@ @@ -1,105 +0,0 @@
- 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 +0 -74
@@ @@ -1,74 +0,0 @@
- 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 +0 -44
@@ @@ -1,44 +0,0 @@
- module Locomotive
- module Builder
- module Liquid
- module Tags
-
- class Snippet < ::Liquid::Include
-
- def render(context)
- name = @template_name.gsub(/[\"\']/, '')
-
- source = context.registers[:mounting_point].snippets[name].try(:source)
-
- Locomotive::Builder::Logger.info " Rendered snippet #{name}"
-
- 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 +0 -43
@@ @@ -1,43 +0,0 @@
- 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/listen.rb b/lib/locomotive/builder/listen.rb +0 -57
@@ @@ -1,57 +0,0 @@
- require 'listen'
-
- module Locomotive::Builder
- class Listen
-
- attr_accessor :reader
-
- def self.instance
- @@instance = new
- end
-
- def start(reader)
- self.reader = reader
-
- self.definitions.each do |definition|
- self.apply(definition)
- end
- end
-
- def definitions
- [
- ['config', /\.yml/, [:site, :content_types, :pages, :snippets, :content_entries, :translations]],
- ['app/views', /\.liquid/, [:pages, :snippets]],
- ['app/content_types', /\.yml/, [:content_types, :content_entries]],
- ['data', /\.yml/, :content_entries]
- ]
- end
-
- protected
-
- def apply(definition)
- reloader = Proc.new do |modified, added, removed|
- resources = [*definition.last]
- names = resources.map { |n| "\"#{n}\"" }.join(', ')
-
- Locomotive::Builder::Logger.info "* Reloaded #{names} at #{Time.now}"
-
- begin
- reader.reload(resources)
- rescue Exception => e
- Locomotive::Builder::MounterException.new('Unable to reload', e)
- end
- end
-
- filter = definition[1]
- path = File.join(self.reader.mounting_point.path, definition.first)
- path = File.expand_path(path)
-
- listener = ::Listen.to(path).filter(filter).change(&reloader)
-
- # non blocking listener
- listener.start(false)
- end
-
- end
-
- end
\ No newline at end of file
locomotive/builder/logger.rb b/lib/locomotive/builder/logger.rb +0 -54
@@ @@ -1,54 +0,0 @@
- module Locomotive
- module Builder
-
- class Logger
-
- attr_accessor :logger, :logfile_path, :stdout
-
- def initialize
- self.logger = nil
- end
-
- # Setup the single instance of the ruby logger.
- #
- # @param [ String ] path The path to the log file (default: log/builder.log)
- # @param [ Boolean ] stdout Instead of having a file, log to the standard output
- #
- def setup(path, stdout = false)
- require 'logger'
-
- self.stdout = stdout
-
- self.logfile_path = File.expand_path(File.join(path, 'log', 'builder.log'))
- FileUtils.mkdir_p(File.dirname(logfile_path))
-
- out = self.stdout ? STDOUT : self.logfile_path
-
- self.logger = ::Logger.new(out).tap do |log|
- log.level = ::Logger::DEBUG
- log.formatter = proc do |severity, datetime, progname, msg|
- "#{msg}\n"
- end
- end
- end
-
- def self.instance
- @@instance ||= self.new
- end
-
- def self.setup(path, stdout = false)
- self.instance.setup(path, stdout)
- end
-
- class << self
- %w(debug info warn error fatal unknown).each do |name|
- define_method(name) do |message|
- self.instance.logger.send(name.to_sym, message)
- end
- end
- end
-
- end
-
- end
- end
\ No newline at end of file
locomotive/builder/misc.rb b/lib/locomotive/builder/misc.rb +0 -5
@@ @@ -1,5 +0,0 @@
- require 'locomotive/builder/misc/core_ext.rb'
- require 'locomotive/builder/misc/will_paginate.rb'
- require 'locomotive/builder/misc/httparty.rb'
- require 'locomotive/builder/misc/dragonfly.rb'
- require 'locomotive/builder/misc/i18n.rb'
\ No newline at end of file
locomotive/builder/misc/core_ext.rb b/lib/locomotive/builder/misc/core_ext.rb +0 -29
@@ @@ -1,29 +0,0 @@
- unless Hash.instance_methods.include?(:underscore_keys)
- class Hash
-
- def underscore_keys
- new_hash = {}
-
- self.each_pair do |key, value|
- if value.respond_to?(:collect!) # Array
- value.collect do |item|
- if item.respond_to?(:each_pair) # Hash item within
- item.underscore_keys
- else
- item
- end
- end
- elsif value.respond_to?(:each_pair) # Hash
- value = value.underscore_keys
- end
-
- new_key = key.is_a?(String) ? key.underscore : key # only String keys
-
- new_hash[new_key] = value
- end
-
- self.replace(new_hash)
- end
-
- end
- end
\ No newline at end of file
locomotive/builder/misc/dragonfly.rb b/lib/locomotive/builder/misc/dragonfly.rb +0 -79
@@ @@ -1,79 +0,0 @@
- module Locomotive
- module Builder
- class Dragonfly
-
- attr_accessor :path, :enabled
-
- def enabled?
- !!self.enabled
- end
-
- def resize_url(source, resize_string)
- _source = (case source
- when String then source
- when Hash then source['url'] || source[:url]
- else
- source.try(:url)
- end)
-
- if _source.blank?
- LocomotiveEditor::Logger.error "Unable to resize on the fly: #{source.inspect}"
- return
- end
-
- return _source unless self.enabled?
-
- if _source =~ /^http/
- file = self.class.app.fetch_url(_source)
- else
- file = self.class.app.fetch_file(File.join(self.path, 'public', _source))
- end
-
- file.process(:thumb, resize_string).url
- end
-
- def self.app
- ::Dragonfly[:images]
- end
-
-
- def self.instance
- @@instance ||= new
- end
-
- def self.setup!(path)
- self.instance.path = path
- self.instance.enabled = false
-
- begin
- require 'rack/cache'
- require 'RMagick'
- require 'dragonfly'
-
- ## initialize Dragonfly ##
- app = ::Dragonfly[:images].configure_with(:imagemagick)
-
- ## configure it ##
- ::Dragonfly[:images].configure do |c|
- convert = `which convert`.strip.presence || '/usr/local/bin/convert'
- c.convert_command = convert
- c.identify_command = convert
-
- c.allow_fetch_url = true
- c.allow_fetch_file = true
-
- c.url_format = '/images/dynamic/:job/:basename.:format'
- end
-
- self.instance.enabled = true
- rescue Exception => e
- Locomotive::Builder::Logger.warn %{
- [Dragonfly] !disabled!
- [Dragonfly] If you want to take full benefits of all the features in the LocomotiveBuilder, we recommend you to install ImageMagick and RMagick. Check out the documentation here: http://doc.locomotivecms.com/editor/installation.
- }
- end
- end
-
- end
- end
- end
\ No newline at end of file
locomotive/builder/misc/httparty.rb b/lib/locomotive/builder/misc/httparty.rb +0 -46
@@ @@ -1,46 +0,0 @@
- 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 ||= '/'
-
- # Locomotive::Builder::Logger.debug "[WebService] consuming #{path}, #{options.inspect}"
-
- response = self.get(path, options)
-
- if response.code == 200
- _response = response.parsed_response
- if _response.respond_to?(:underscore_keys)
- _response.underscore_keys
- else
- _response.collect(&:underscore_keys)
- end
- else
- Locomotive::Builder::Logger.error "[WebService] consumed #{path}, #{options.inspect}, response = #{response.inspect}"
- nil
- end
-
- end
-
- end
- end
- end
- end
\ No newline at end of file
locomotive/builder/misc/i18n.rb b/lib/locomotive/builder/misc/i18n.rb +0 -2
@@ @@ -1,2 +0,0 @@
- I18n.load_path = Dir[File.join(File.dirname(__FILE__), "/../../../../locales/*.yml")]
- I18n.backend.reload!
\ No newline at end of file
locomotive/builder/misc/will_paginate.rb b/lib/locomotive/builder/misc/will_paginate.rb +0 -16
@@ @@ -1,16 +0,0 @@
- require 'will_paginate'
- require 'will_paginate/collection'
-
- Array.class_eval do
- def paginate(options = {})
- raise ArgumentError, "parameter hash expected (got #{options.inspect})" unless Hash === options
-
- WillPaginate::Collection.create(
- options[:page] || 1,
- options[:per_page] || 30,
- options[:total_entries] || self.length
- ) { |pager|
- pager.replace self[pager.offset, pager.per_page].to_a
- }
- end
- end
\ No newline at end of file
locomotive/builder/server.rb b/lib/locomotive/builder/server.rb +0 -81
@@ @@ -1,81 +0,0 @@
- require 'rack/showexceptions'
- require 'coffee_script'
-
- require 'locomotive/builder/listen'
- require 'locomotive/builder/server/middleware'
- require 'locomotive/builder/server/favicon'
- require 'locomotive/builder/server/dynamic_assets'
- require 'locomotive/builder/server/logging'
- require 'locomotive/builder/server/entry_submission'
- require 'locomotive/builder/server/path'
- require 'locomotive/builder/server/locale'
- require 'locomotive/builder/server/page'
- require 'locomotive/builder/server/templatized_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, options = {})
- Locomotive::Builder::Dragonfly.setup!(reader.mounting_point.path)
-
- @reader = reader
- @app = self.create_rack_app(@reader)
-
- unless options[:disable_listen]
- Locomotive::Builder::Listen.instance.start(@reader)
- end
- end
-
- def call(env)
- env['builder.mounting_point'] = @reader.mounting_point
- @app.call(env)
- end
-
- protected
-
- def create_rack_app(reader)
- Rack::Builder.new do
- use Rack::ShowExceptions
- use Rack::Lint
-
- use Rack::Session::Cookie, {
- key: 'rack.session',
- domain: '0.0.0.0',
- path: '/',
- expire_after: 2592000,
- secret: 'uselessinlocal'
- }
-
- use ::Dragonfly::Middleware, :images
-
- use Rack::Static, {
- urls: ['/images', '/fonts', '/samples'],
- root: File.join(reader.mounting_point.path, 'public')
- }
-
- use Favicon
- use DynamicAssets
-
- use Logging
-
- use EntrySubmission
-
- use Path
- use Locale
-
- use Page
- use TemplatizedPage
- use NotFound
- use Renderer
-
- run Renderer.new
- end
- end
-
- end
- end
\ No newline at end of file
locomotive/builder/server/dynamic_assets.rb b/lib/locomotive/builder/server/dynamic_assets.rb +0 -31
@@ @@ -1,31 +0,0 @@
- 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/entry_submission.rb b/lib/locomotive/builder/server/entry_submission.rb +0 -116
@@ @@ -1,116 +0,0 @@
- module Locomotive::Builder
- class Server
-
- # Mimic the submission of a content entry
- #
- class EntrySubmission < Middleware
-
- def call(env)
- self.set_accessors(env)
-
- if self.request.post? && env['PATH_INFO'] =~ /^\/entry_submissions\/(.*)/
- self.process_form($1)
-
- if @entry.valid?
- if self.html?
- self.record_submitted_entry
- self.redirect_to self.callback_url
- elsif self.json?
- self.json_response
- end
- else
- if self.html?
- if self.callback_url =~ /^http:\/\//
- self.redirect_to self.callback_url
- else
- env['PATH_INFO'] = self.callback_url
- self.liquid_assigns[@content_type.slug.singularize] = @entry
- app.call(env)
- end
- elsif self.json?
- self.json_response(422)
- end
- end
- else
- self.fetch_submitted_entry
-
- app.call(env)
- end
- end
-
- protected
-
- def record_submitted_entry
- self.request.session[:now] ||= {}
- self.request.session[:now][:submitted_entry] = [@content_type.slug, @entry._slug]
- end
-
- def fetch_submitted_entry
- if data = self.request.session[:now].try(:delete, :submitted_entry)
- content_type = self.mounting_point.content_types[data.first.to_s]
-
- entry = (content_type.entries || []).detect { |e| e._slug == data.last }
-
- # do not keep track of the entry
- content_type.entries.delete(entry) if entry
-
- # add it to the additional liquid assigns for the next liquid rendering
- if entry
- self.liquid_assigns[content_type.slug.singularize] = entry
- end
- end
- end
-
- # Mimic the creation of a content entry with a minimal validation.
- #
- # @param [ String ] permalink The permalink (or slug) of the content type
- #
- #
- def process_form(permalink)
- permalink = permalink.split('.').first
-
- @content_type = self.mounting_point.content_types[permalink]
-
- raise "Unknown content type '#{@content_type.inspect}'" if @content_type.nil?
-
- @entry = @content_type.build_entry(self.params[:entry] || self.params[:content])
-
- # if not valid, we do not need to keep track of the entry
- @content_type.entries.delete(@entry) if !@entry.valid?
- end
-
- def callback_url
- (@entry.valid? ? params[:success_callback] : params[:error_callback]) || '/'
- end
-
- # Build the JSON response
- #
- # @param [ Integer ] status The HTTP return code
- #
- # @return [ Array ] The rack response depending on the validation status and the requested format
- #
- def json_response(status = 200)
- locale = self.mounting_point.default_locale
-
- if self.request.path =~ /^\/(#{self.mounting_point.locales.join('|')})+(\/|$)/
- locale = $1
- end
-
- hash = @entry.to_hash(false).tap do |_hash|
- if !@entry.valid?
- _hash['errors'] = @entry.errors.inject({}) do |memo, name|
- memo[name] = ::I18n.t('errors.messages.blank', locale: locale)
- memo
- end
- end
- end
-
- [status, { 'Content-Type' => 'application/json' }, [
- { @content_type.slug.singularize => hash }.to_json
- ]]
- end
-
- end
-
- end
- end
\ No newline at end of file
locomotive/builder/server/favicon.rb b/lib/locomotive/builder/server/favicon.rb +0 -17
@@ @@ -1,17 +0,0 @@
- 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/locale.rb b/lib/locomotive/builder/server/locale.rb +0 -42
@@ @@ -1,42 +0,0 @@
- 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)
- locale = self.mounting_point.default_locale
-
- if self.path =~ /^(#{self.mounting_point.locales.join('|')})+(\/|$)/
- locale = $1
- self.path = self.path.gsub($1 + $2, '')
- self.path = 'index' if self.path.blank?
- end
-
- Locomotive::Mounter.locale = locale
- ::I18n.locale = locale
-
- self.log "Detecting locale #{locale.upcase}"
-
- env['builder.locale'] = locale
- env['builder.path'] = self.path
- end
-
- end
- end
- end
\ No newline at end of file
locomotive/builder/server/logging.rb b/lib/locomotive/builder/server/logging.rb +0 -32
@@ @@ -1,32 +0,0 @@
- module Locomotive::Builder
- class Server
-
- # Track the request into the current logger
- #
- class Logging < Middleware
-
- def call(env)
- now = Time.now
-
- log "Started #{env['REQUEST_METHOD'].upcase} \"#{env['PATH_INFO']}\" at #{now}"
-
- app.call(env).tap do |response|
- done_in_ms = (Time.now - now) * 1000
- log "Completed #{code_to_human(response.first)} in #{done_in_ms}ms\n\n"
- end
- end
-
- protected
-
- def code_to_human(code)
- case code.to_i
- when 200 then '200 OK'
- when 301 then '301 Found'
- when 302 then '302 Found'
- when 404 then '404 Not Found'
- end
- end
-
- end
- end
- end
\ No newline at end of file
locomotive/builder/server/middleware.rb b/lib/locomotive/builder/server/middleware.rb +0 -59
@@ @@ -1,59 +0,0 @@
- module Locomotive::Builder
- class Server
-
- class Middleware
-
- attr_accessor :app, :request, :path, :liquid_assigns
-
- attr_accessor :mounting_point, :page, :content_entry
-
- def initialize(app = nil)
- @app = app
- end
-
- def call(env)
- app.call(env)
- end
-
- protected
-
- def set_accessors(env)
- self.path = env['builder.path']
- self.request = Rack::Request.new(env)
- self.mounting_point = env['builder.mounting_point']
- self.page = env['builder.page']
- self.content_entry = env['builder.content_entry']
-
- env['builder.liquid_assigns'] ||= {}
- self.liquid_assigns = env['builder.liquid_assigns']
- end
-
- def site
- self.mounting_point.site
- end
-
- def params
- self.request.params.deep_symbolize_keys
- end
-
- def html?
- self.request.media_type == 'text/html' || !self.request.xhr?
- end
-
- def json?
- self.request.content_type == 'application/json' || File.extname(self.request.path) == '.json'
- end
-
- def redirect_to(location, type = 301)
- self.log "Redirected to #{location}"
- [type, { 'Content-Type' => 'text/html', 'Location' => location }, []]
- end
-
- def log(msg)
- Locomotive::Builder::Logger.info msg
- end
-
- end
-
- end
- end
\ No newline at end of file
locomotive/builder/server/not_found.rb b/lib/locomotive/builder/server/not_found.rb +0 -19
@@ @@ -1,19 +0,0 @@
- module Locomotive::Builder
- class Server
-
- class NotFound < Middleware
-
- def call(env)
- self.set_accessors(env)
-
- if self.page.nil?
- self.log "Page not found"
- 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 +0 -61
@@ @@ -1,61 +0,0 @@
- 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)
- page = self.fetch_page
-
- if page
- self.log "Found page \"#{page.title}\" [/#{page.inspect}]"
- end
-
- env['builder.page'] = page
- end
-
- def fetch_page
- matchers = self.path_combinations(self.path)
-
- self.mounting_point.pages.values.detect do |_page|
- matchers.include?(_page.safe_fullpath) ||
- matchers.include?(_page.safe_fullpath.try(: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/path.rb b/lib/locomotive/builder/server/path.rb +0 -34
@@ @@ -1,34 +0,0 @@
- 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 +0 -105
@@ @@ -1,105 +0,0 @@
- module Locomotive::Builder
- class Server
-
- class Renderer < Middleware
-
- def call(env)
- self.set_accessors(env)
-
- if self.page
- if self.page.redirect?
- self.redirect_to(self.page.redirect_url, self.page.redirect_type)
- else
- type = self.page.response_type || 'text/html'
- html = self.render
-
- self.log " Rendered liquid page template"
-
- [200, { 'Content-Type' => type }, [html]]
- end
- else
- # 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
-
- # assigns from other middlewares
- assigns.merge!(self.liquid_assigns)
-
- assigns.merge!(other_assigns)
-
- # templatized page
- if self.page && self.content_entry
- ['content_entry', 'entry', self.page.content_type.slug.singularize].each do |key|
- assigns[key] = self.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
locomotive/builder/server/templatized_page.rb b/lib/locomotive/builder/server/templatized_page.rb +0 -32
@@ @@ -1,32 +0,0 @@
- module Locomotive::Builder
- class Server
-
- class TemplatizedPage < Middleware
-
- def call(env)
- self.set_accessors(env)
-
- if self.page && self.page.templatized?
- self.set_content_entry!(env)
- end
-
- app.call(env)
- end
-
- protected
-
- def set_content_entry!(env)
- %r(^#{self.page.safe_fullpath.gsub('*', '([^\/]+)')}$) =~ self.path
-
- permalink = $1
-
- if content_entry = self.page.content_type.find_entry(permalink)
- env['builder.content_entry'] = content_entry
- else
- env['builder.page'] = nil
- end
- end
-
- end
- end
- end
\ No newline at end of file
locomotive/builder/standalone_server.rb b/lib/locomotive/builder/standalone_server.rb +0 -28
@@ @@ -1,28 +0,0 @@
- $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../..'))
-
- require 'locomotive/builder/logger'
- require 'locomotive/builder/version'
- require 'locomotive/builder/exceptions'
- require 'locomotive/builder/server'
- require 'locomotive/mounter'
-
- module Locomotive
- module Builder
- class StandaloneServer < Server
-
- def initialize(path)
- Locomotive::Builder::Logger.setup(path, false)
-
- # get the reader
- reader = Locomotive::Mounter::Reader::FileSystem.instance
- reader.run!(path: path)
- reader
-
- Bundler.require 'misc'
-
- # run the rack app
- super(reader, disable_listen: true)
- end
- end
- end
- end
\ No newline at end of file
locomotive/builder/version.rb b/lib/locomotive/builder/version.rb +0 -5
@@ @@ -1,5 +0,0 @@
- module Locomotive
- module Builder
- VERSION = '1.0.0.alpha8'
- end
- end
locomotive/wagon.rb b/lib/locomotive/wagon.rb +163 -0
@@ @@ -0,0 +1,163 @@
+ require 'locomotive/wagon/version'
+ require 'locomotive/wagon/logger'
+ require 'locomotive/wagon/exceptions'
+
+ module Locomotive
+ module Wagon
+
+ # Create a site from a site generator.
+ #
+ # @param [ String ] name The name of the site (underscored)
+ # @param [ String ] path The destination path of the site
+ # @param [ Object ] generator The wrapping class of the generator itself
+ #
+ def self.init(name, path, generator)
+ generator.klass.start [name, path]
+ end
+
+ # Start the thin server which serves the LocomotiveCMS site from the system.
+ #
+ # @param [ String ] path The path of the site
+ # @param [ Hash ] options The options for the thin server (host, port)
+ #
+ def self.serve(path, options)
+ if reader = self.require_mounter(path, true)
+ Bundler.require 'misc'
+
+ require 'thin'
+ require 'locomotive/wagon/server'
+
+ server = Thin::Server.new(options[:host], options[:port], Locomotive::Wagon::Server.new(reader))
+ server.threaded = true # TODO: make it an option ?
+ server.start
+ end
+ end
+
+ # Generate components for the LocomotiveCMS site such as content types, snippets, pages.
+ #
+ # @param [ Symbol ] name The name of the generator
+ # @param [ Array ] *args The arguments for the generator
+ #
+ def self.generate(name, *args)
+ Bundler.require 'misc'
+
+ lib = "locomotive/wagon/generators/#{name}"
+ require lib
+
+ generator = lib.camelize.constantize.new(args, {}, {})
+ generator.invoke_all
+ end
+
+ # Push a site to a remote LocomotiveCMS engine described
+ # by the config/deploy.yml file of the site and for a specific environment.
+ #
+ # @param [ String ] path The path of the site
+ # @param [ Hash ] connection_info The information to get connected to the remote site
+ # @param [ Hash ] options The options passed to the push process
+ #
+ def self.push(path, connection_info, options = {})
+ if reader = self.require_mounter(path, true)
+ Bundler.require 'misc'
+
+ writer = Locomotive::Mounter::Writer::Api.instance
+
+ connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api"
+
+ _options = { mounting_point: reader.mounting_point, console: true }.merge(options).symbolize_keys
+ _options[:only] = _options.delete(:resources)
+
+ writer.run!(_options.merge(connection_info))
+ end
+ end
+
+ # Pull a site from a remote LocomotiveCMS engine described
+ # by the config/deploy.yml file of the site and for a specific environment.
+ #
+ # @param [ String ] path The path of the site
+ # @param [ Hash ] connection_info The information to get connected to the remote site
+ # @param [ Hash ] options The options passed to the pull process
+ #
+ def self.pull(path, connection_info, options = {})
+ self.require_mounter(path)
+
+ Bundler.require 'misc'
+
+ connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api"
+
+ _options = { console: true }.merge(options)
+ _options[:only] = _options.delete(:resources)
+
+ reader = Locomotive::Mounter::Reader::Api.instance
+ reader.run!(_options.merge(connection_info))
+
+ writer = Locomotive::Mounter::Writer::FileSystem.instance
+ writer.run!(mounting_point: reader.mounting_point, target_path: path)
+ rescue Exception => e
+ puts e.backtrace
+ end
+
+ def self.clone(path, connection_info, options = {})
+ if File.exists?(path)
+ puts "Path already exists. If it's an existing site, you might want to pull instead of clone."
+ return false
+ end
+ require 'locomotive/mounter'
+
+ connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api"
+
+ _options = options.dup
+ _options[:only] = _options.delete(:resources)
+
+ reader = Locomotive::Mounter::Reader::Api.instance
+ reader.run!(_options.merge(connection_info))
+
+ writer = Locomotive::Mounter::Writer::FileSystem.instance
+ writer.run!(mounting_point: reader.mounting_point, target_path: path)
+ # rescue Exception => e
+ # puts e.backtrace
+ end
+
+ # Destroy a remote site
+ #
+ # @param [ String ] path The path of the site
+ # @param [ Hash ] connection_info The information to get connected to the remote site
+ # @param [ Hash ] options The options passed to the push process
+ #
+ def self.destroy(path, connection_info, options = {})
+ self.require_mounter(path)
+
+ connection_info['uri'] = "#{connection_info.delete('host')}/locomotive/api"
+
+ Locomotive::Mounter::EngineApi.set_token connection_info.symbolize_keys
+ Locomotive::Mounter::EngineApi.delete('/current_site.json')
+ end
+
+ # Load the Locomotive::Mounter lib and set it up (logger, ...etc).
+ # If the second parameter is set to true, then the method builds
+ # an instance of the reader from the path passed in first parameter.
+ #
+ # @param [ String ] path The path to the local site
+ # @param [ Boolean ] get_reader Tell if it builds an instance of the reader.
+ #
+ # @param [ Object ] An instance of the reader is the get_reader parameter has been set.
+ #
+ def self.require_mounter(path, get_reader = false)
+ Locomotive::Wagon::Logger.setup(path, false)
+
+ require 'locomotive/mounter'
+
+ Locomotive::Mounter.logger = Locomotive::Wagon::Logger.instance.logger
+
+ if get_reader
+ begin
+ reader = Locomotive::Mounter::Reader::FileSystem.instance
+ reader.run!(path: path)
+ reader
+ rescue Exception => e
+ raise Locomotive::Wagon::MounterException.new "Unable to read the local LocomotiveCMS site. Please check the logs.", e
+ end
+ end
+ end
+
+ end
+ end
\ No newline at end of file
locomotive/wagon/cli.rb b/lib/locomotive/wagon/cli.rb +229 -0
@@ @@ -0,0 +1,229 @@
+ require 'thor'
+ require 'thor/runner'
+
+ module Locomotive
+ module Wagon
+ module CLI
+
+ module CheckPath
+
+ protected
+
+ # Check if the path given in option ('.' by default) points to a LocomotiveCMS
+ # site. It is also possible to pass a path other than the one from the options.
+ #
+ # @param [ String ] path The optional path instead of options['path']
+ #
+ # @return [ String ] The fullpath to the LocomotiveCMS site or nil if it is not a valid site.
+ #
+ def check_path!(path = nil)
+ path ||= options['path']
+
+ path = path == '.' ? Dir.pwd : File.expand_path(path)
+
+ (File.exists?(File.join(path, 'config', 'site.yml')) ? path : nil).tap do |_path|
+ if _path.nil?
+ say 'The path does not point to a LocomotiveCMS site', :red
+ end
+ end
+ end
+
+ end
+
+ class Generate < Thor
+
+ include Locomotive::Wagon::CLI::CheckPath
+
+ class_option :path, aliases: '-p', type: :string, default: '.', optional: true, desc: 'if your LocomotiveCMS site is not in the current path'
+
+ desc 'content_type NAME FIELDS', 'Create a content type with NAME as the slug and FIELDS as the list of fields.'
+ long_desc <<-LONGDESC
+ Create a content type with NAME as the slug and FIELDS as the list of fields.
+ The fields follows that schema:
+
+ field_1[:type][:required] field_2[:type][:required]
+
+ Examples:
+
+ * wagon generate content_type songs name:string duration:string
+
+ * wagon generate content_type posts title body:text:true published_at:date
+ LONGDESC
+ def content_type(name, *fields)
+ say('The fields are missing', :red) and return false if fields.empty?
+
+ if check_path!
+ Locomotive::Wagon.generate :content_type, name, self.options['path'], fields
+ end
+ end
+
+ desc 'page FULLPATH', 'Create a page. No need to pass an extension to the FULLPATH arg'
+ long_desc <<-LONGDESC
+ Create a page. The generator will ask for the extension (liquid or haml) and also
+ if the page is localized or not.
+
+ Examples:
+
+ * wagon generate page contact
+
+ * wagon generate page about_us/me
+ LONGDESC
+ def page(fullpath)
+ if check_path!
+ Locomotive::Wagon.generate :page, fullpath, self.options['path']
+ end
+ end
+
+ desc 'snippet SLUG', 'Create a snippet'
+ long_desc <<-LONGDESC
+ Create a snippet. The generator will ask for the extension (liquid or haml) and also
+ if the snippet is localized or not.
+
+ Example:
+
+ * wagon generate snippet footer
+ LONGDESC
+ def snippet(slug)
+ if check_path!
+ Locomotive::Wagon.generate :snippet, slug, self.options['path']
+ end
+ end
+
+ end
+
+ class Main < Thor
+
+ include Locomotive::Wagon::CLI::CheckPath
+
+ desc 'version', 'Version of the LocomotiveCMS wagon'
+ def version
+ require 'locomotive/wagon/version'
+ say Locomotive::Wagon::VERSION
+ end
+
+ desc 'init NAME [PATH]', 'Create a brand new LocomotiveCMS site'
+ method_option :template, aliases: '-t', type: 'string', default: 'blank', desc: 'instead of building from a blank site, you can have a pre-fetched site with form a template (see the templates command)'
+ def init(name, path = '.')
+ require 'locomotive/wagon/generators/site'
+ generator = Locomotive::Wagon::Generators::Site.get(options[:template])
+ if generator.nil?
+ say "Unknown site template '#{options[:template]}'", :red
+ else
+ begin
+ Locomotive::Wagon.init(name, path, generator)
+ rescue GeneratorException => e
+ say e.message, :red
+ end
+ end
+ end
+
+ desc 'generate TYPE ...ARGS', 'Generate resources (content_types, page, snippets) for a LocomotiveCMS site'
+ subcommand 'generate', Generate
+
+ desc 'list_templates', 'List all the templates to create either a site or a content type'
+ def list_templates
+ require 'locomotive/wagon/generators/site'
+ if Locomotive::Wagon::Generators::Site.empty?
+ say 'No templates', :red
+ else
+ Locomotive::Wagon::Generators::Site.list.each do |info|
+ say info.name, :bold, false
+ say " - #{info.description}" unless info.description.blank?
+ end
+ end
+ end
+
+ desc 'serve [PATH]', 'Serve a LocomotiveCMS site from the file system'
+ method_option :host, aliases: '-h', type: 'string', default: '0.0.0.0', desc: 'The host (address) of the Thin server'
+ method_option :port, aliases: '-p', type: 'string', default: '3333', desc: 'The port of the Thin server'
+ def serve(path = '.')
+ if check_path!(path)
+ begin
+ Locomotive::Wagon.serve(path, options)
+ rescue Exception => e
+ say e.message, :red
+ end
+ end
+ end
+
+ desc 'push ENV [PATH]', 'Push a site to a remote LocomotiveCMS engine'
+ method_option :resources, aliases: '-r', type: 'array', default: nil, desc: 'Only push the resource(s) passed in argument'
+ method_option :force, aliases: '-f', type: 'boolean', default: false, desc: 'Force the push of a resource'
+ method_option :data, aliases: '-d', type: 'boolean', default: false, desc: 'Push the content entries and the editable elements (by default, they are not)'
+ def push(env, path = '.')
+ if check_path!(path)
+ if connection_info = self.retrieve_connection_info(env, path)
+ begin
+ Locomotive::Wagon.push(path, connection_info, options)
+ rescue Exception => e
+ say e.message, :red
+ end
+ end
+ end
+ end
+
+ desc 'pull ENV [PATH]', 'Pull a site from a remote LocomotiveCMS engine'
+ method_option :resources, aliases: '-r', type: 'array', default: nil, desc: 'Only pull the resource(s) passed in argument'
+ # method_option :force, aliases: '-f', type: 'boolean', default: false, desc: 'Force the push of a resource'
+ # method_option :data, aliases: '-d', type: 'boolean', default: false, desc: 'Push the content entries and the editable elements (by default, they are not)'
+ def pull(env, path = '.')
+ if check_path!(path)
+ if connection_info = self.retrieve_connection_info(env, path)
+ begin
+ Locomotive::Wagon.pull(path, connection_info, options)
+ rescue Exception => e
+ say e.message, :red
+ end
+ end
+ end
+ end
+
+ desc 'destroy ENV [PATH]', 'Destroy a remote LocomotiveCMS engine'
+ def destroy(env, path = '.')
+ if check_path!(path)
+ if connection_info = self.retrieve_connection_info(env, path)
+ if ask('Are you sure ?', limited_to: %w(yes no)) == 'yes'
+ Locomotive::Wagon.destroy(path, connection_info)
+ else
+ say 'The destroy operation has been cancelled', :red
+ end
+ end
+ end
+ end
+
+ # desc "pull NAME SITE_URL EMAIL PASSWORD", "Pull an existing LocomotiveCMS site powered by the engine"
+ # def pull(name, site_url, email, password)
+ # say("ERROR: \"#{name}\" directory already exists", :red) and return if File.exists?(name)
+ # Locomotive::Wagon.pull(name, site_url, email, password)
+ # end
+
+ protected
+
+ # From a site specified by a path, retrieve the information of the connection
+ # for a environment located in the config/deploy.yml file of the site.
+ #
+ # @param [ String ] env The environment (development, staging, production, ...etc)
+ # @param [ String ] path The path of the local site
+ #
+ # @return [ Hash ] The information of the connection or nil if errors
+ #
+ def retrieve_connection_info(env, path)
+ connection_info = nil
+ begin
+ path_to_deploy_file = File.join(path, 'config', 'deploy.yml')
+ connection_info = YAML::load(File.open(path_to_deploy_file).read)[env.to_s]
+
+ if connection_info.nil?
+ raise "No #{env.to_s} environment found in the config/deploy.yml file"
+ end
+ rescue Exception => e
+ say "Unable to read the information about the remote LocomotiveCMS site (#{e.message})", :red
+ end
+ connection_info
+ end
+
+ end
+
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/exceptions.rb b/lib/locomotive/wagon/exceptions.rb +35 -0
@@ @@ -0,0 +1,35 @@
+ module Locomotive
+ module Wagon
+
+ class DefaultException < ::Exception
+
+ def initialize(message = nil, parent_exception = nil)
+ self.log_backtrace(parent_exception) if parent_exception
+
+ super(message)
+ end
+
+ protected
+
+ def log_backtrace(parent_exception)
+ full_error_message = "#{parent_exception.message}\n\t"
+ full_error_message += parent_exception.backtrace.join("\n\t")
+ full_error_message += "\n\n"
+ Locomotive::Wagon::Logger.fatal full_error_message
+ end
+
+ end
+
+ class MounterException < DefaultException
+ end
+
+ class GeneratorException < DefaultException
+
+ def log_backtrace(parent_exception)
+ # Logger not initialized at this step
+ end
+
+ end
+
+ end
+ end
locomotive/wagon/generators/content_type.rb b/lib/locomotive/wagon/generators/content_type.rb +47 -0
@@ @@ -0,0 +1,47 @@
+ require 'thor/group'
+ require 'ostruct'
+ require 'active_support'
+ require 'active_support/core_ext'
+ require 'faker'
+
+ module Locomotive
+ module Wagon
+ module Generators
+ class ContentType < Thor::Group
+
+ include Thor::Actions
+
+ argument :name
+ argument :target_path
+ argument :fields
+
+ def copy_sources
+ directory('.', target_path, { recursive: true }, {
+ name: self.name,
+ fields: extract_fields(fields)
+ })
+ end
+
+ def self.source_root
+ File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'generators', 'content_type')
+ end
+
+ protected
+
+ def extract_fields(fields)
+ fields.map do |raw_attributes|
+ name, type, required = raw_attributes.split(':')
+
+ OpenStruct.new({
+ name: name,
+ type: type || 'string',
+ required: %w(true required).include?(required)
+ })
+ end
+ end
+
+ end
+
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/generators/page.rb b/lib/locomotive/wagon/generators/page.rb +57 -0
@@ @@ -0,0 +1,57 @@
+ require 'thor/group'
+ require 'active_support'
+ require 'active_support/core_ext'
+
+ module Locomotive
+ module Wagon
+ module Generators
+ class Page < Thor::Group
+
+ include Thor::Actions
+
+ argument :slug
+ argument :target_path # path to the site
+
+ attr_accessor :haml, :locales
+
+ def ask_for_haml_and_locales
+ self.locales = []
+ self.haml = yes?('Do you prefer a HAML template ?')
+
+ if yes?('Is your page localized ?')
+ self.locales = ask('What are the locales other than the default one (comma separated) ?').split(',').map(&:strip)
+ end
+ end
+
+ def create_page
+ extension = self.haml ? 'liquid.haml' : 'liquid'
+
+ segments = self.slug.split('/')
+ while segment = segments.pop do
+ options = { slug: segment, translated: false }
+ file_path = File.join(pages_path, segments, segment)
+
+ template "template.#{extension}.tt", "#{file_path}.#{extension}", options
+
+ self.locales.each do |locale|
+ options[:translated] = true
+ template "template.#{extension}.tt", "#{file_path}.#{locale}.#{extension}", options
+ end
+ end
+ end
+
+ def self.source_root
+ File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'generators', 'page')
+ end
+
+ protected
+
+ def pages_path
+ File.join(target_path, 'app', 'views', 'pages')
+ end
+
+ end
+
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/generators/site.rb b/lib/locomotive/wagon/generators/site.rb +98 -0
@@ @@ -0,0 +1,98 @@
+ require 'ostruct'
+ require 'singleton'
+
+ module Locomotive
+ module Wagon
+ module Generators
+
+ module Site
+
+ # Register a generator by adding it to the list of existing generators.
+ #
+ # @param [ String ] name The name of the generator
+ # @param [ Class ] klass The class of the generator
+ # @param [ String ] description The description of the generator (can be nil)
+ #
+ # @return [ Boolean ] True if the registration has been successful, false otherwise.
+ #
+ def self.register(name, klass, description = nil)
+ Locomotive::Wagon::Generators::Site::List.instance.register(name, klass, description)
+ end
+
+ # Return the information about a generator from its name.
+ #
+ # @param [ String ] name The name of the generator
+ #
+ # @return [ Object ] The information of the found generator or nil
+ #
+ def self.get(name)
+ Locomotive::Wagon::Generators::Site::List.instance.get(name)
+ end
+
+ # List all the generators
+ #
+ # @return [ Array ] The filtered (or not) list of generators
+ #
+ def self.list
+ Locomotive::Wagon::Generators::Site::List.instance._list
+ end
+
+ # Tell if the list of generators is empty or not .
+ #
+ # @return [ Boolean ] True if empty
+ #
+ def self.empty?
+ Locomotive::Wagon::Generators::Site::List.instance._list.empty?
+ end
+
+ class List
+
+ include ::Singleton
+
+ attr_accessor :_list
+
+ def initialize
+ self._list = []
+ end
+
+ # Return the information about a generator from its name.
+ #
+ # @param [ String ] name The name of the generator
+ #
+ # @return [ Object ] The information of the found generator or nil
+ #
+ def get(name)
+ self._list.detect { |entry| entry.name == name.to_sym }
+ end
+
+ # Register a generator by adding it to the list of existing generators.
+ #
+ # @param [ String ] name The name of the generator
+ # @param [ Class ] klass The class of the generator
+ # @param [ String ] description The description of the generator (can be nil)
+ #
+ # @return [ Boolean ] True if the registration has been successful, false otherwise.
+ #
+ def register(name, klass, description = nil)
+ return false unless self.get(name).nil?
+
+ self._list << OpenStruct.new({
+ name: name.to_sym,
+ klass: klass,
+ description: description ? description.strip.gsub("\n", '') : nil
+ })
+
+ self._list.last
+ end
+ end
+
+ end
+
+ end
+ end
+ end
+
+ require 'locomotive/wagon/generators/site/base'
+ require 'locomotive/wagon/generators/site/blank'
+ require 'locomotive/wagon/generators/site/bootstrap'
+ require 'locomotive/wagon/generators/site/unzip'
locomotive/wagon/generators/site/base.rb b/lib/locomotive/wagon/generators/site/base.rb +30 -0
@@ @@ -0,0 +1,30 @@
+ require 'thor/group'
+ require 'active_support'
+ require 'active_support/core_ext'
+
+ module Locomotive
+ module Wagon
+ module Generators
+ module Site
+
+ class Base < Thor::Group
+
+ include Thor::Actions
+
+ argument :name
+ argument :target_path
+
+ def self.source_root
+ File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'generators', self.name.demodulize.underscore)
+ end
+
+ def destination
+ File.join(target_path, name)
+ end
+
+ end
+
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/generators/site/blank.rb b/lib/locomotive/wagon/generators/site/blank.rb +23 -0
@@ @@ -0,0 +1,23 @@
+ module Locomotive
+ module Wagon
+ module Generators
+ module Site
+
+ class Blank < Base
+
+ def copy_sources
+ directory('.', self.destination, { recursive: true }, {
+ name: self.name,
+ version: Locomotive::Wagon::VERSION
+ })
+ end
+
+ end
+
+ Locomotive::Wagon::Generators::Site.register(:blank, Blank, %{
+ A blank LocomotiveCMS site with the minimal files.
+ })
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/generators/site/bootstrap.rb b/lib/locomotive/wagon/generators/site/bootstrap.rb +35 -0
@@ @@ -0,0 +1,35 @@
+ module Locomotive
+ module Wagon
+ module Generators
+ module Site
+
+ class Bootstrap < Base
+
+ def copy_sources
+ directory('.', self.destination, { recursive: true }, {
+ name: self.name,
+ version: Locomotive::Wagon::VERSION
+ })
+ end
+
+ def choose_haml_over_html
+ if yes?('Do you prefer HAML templates ?')
+ remove_file File.join(self.destination, 'app/views/pages/index.liquid')
+ remove_file File.join(self.destination, 'app/views/pages/404.liquid')
+ remove_file File.join(self.destination, 'app/views/snippets/footer.liquid')
+ else
+ remove_file File.join(self.destination, 'app/views/pages/index.liquid.haml')
+ remove_file File.join(self.destination, 'app/views/pages/404.liquid.haml')
+ remove_file File.join(self.destination, 'app/views/snippets/footer.liquid.haml')
+ end
+ end
+
+ end
+
+ Locomotive::Wagon::Generators::Site.register(:bootstrap, Bootstrap, %{
+ A LocomotiveCMS site powered by Twitter bootstrap (v2.2.2).
+ })
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/generators/site/unzip.rb b/lib/locomotive/wagon/generators/site/unzip.rb +81 -0
@@ @@ -0,0 +1,81 @@
+ require 'open-uri'
+ require 'zip/zipfilesystem'
+
+ module Locomotive
+ module Wagon
+ module Generators
+ module Site
+
+ class Unzip < Base
+
+ def prepare
+ remove_file join('tmp')
+ empty_directory join('tmp')
+ end
+
+ def ask_for_location
+ @location = ask('What is the location (on the filesystem or url) of the zip file ?')
+ raise GeneratorException.new('Please enter a location') if @location.blank?
+ end
+
+ def download_or_copy
+ @template_path = join('tmp', File.basename(@location))
+
+ if @location =~ /^https?:\/\//
+ say "downloading...#{@location}"
+ create_file @template_path, open(@location).read
+ else
+ say "copying...#{@location}"
+ create_file @template_path, open(@location, 'rb') { |io| io.read }
+ end
+ end
+
+ def unzip
+ say "unzipping...#{@template_path}"
+
+ begin
+ Zip::ZipFile.open(@template_path) do |zipfile|
+ zipfile.each do |file|
+ next if file.name =~ /^__MACOSX/
+ zipfile.extract(file, join('tmp', file.name))
+
+ @path = $1 if file.name =~ /(.*)\/config\/site.yml$/
+ end
+ end
+ rescue Exception => e
+ raise GeneratorException.new("Unable to unzip the archive")
+ end
+
+ raise GeneratorException.new('Not a valid LocomotiveCMS site') if @path.blank?
+ end
+
+ def copy_sources
+ self.class.source_root = File.expand_path(join('tmp', @path, '/'))
+ say "copying files from #{self.class.source_root} / #{self.destination}"
+ directory('.', self.destination, { recursive: true })
+ end
+
+ def self.source_root
+ # only way to change the source root from the instance
+ @@source_root
+ end
+
+ def self.source_root=(value)
+ @@source_root = value
+ end
+
+ protected
+
+ def join(*args)
+ File.join(self.destination, *args)
+ end
+
+ end
+
+ Locomotive::Wagon::Generators::Site.register(:unzip, Unzip, %{
+ Unzip a local or remote (http, https, ftp) zipped LocomotiveCMS site.
+ })
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/generators/snippet.rb b/lib/locomotive/wagon/generators/snippet.rb +54 -0
@@ @@ -0,0 +1,54 @@
+ require 'thor/group'
+ require 'active_support'
+ require 'active_support/core_ext'
+
+ module Locomotive
+ module Wagon
+ module Generators
+ class Snippet < Thor::Group
+
+ include Thor::Actions
+
+ argument :slug
+ argument :target_path # path to the site
+
+ attr_accessor :haml, :locales
+
+ def ask_for_haml_and_locales
+ self.locales = []
+ self.haml = yes?('Do you prefer a HAML template ?')
+
+ if yes?('Is your snippet localized ?')
+ self.locales = ask('What are the locales other than the default one (comma separated) ?').split(',').map(&:strip)
+ end
+ end
+
+ def create_snippet
+ extension = self.haml ? 'liquid.haml' : 'liquid'
+
+ options = { slug: slug, translated: false }
+ file_path = File.join(pages_path, slug)
+
+ template "template.#{extension}.tt", "#{file_path}.#{extension}", options
+
+ self.locales.each do |locale|
+ options[:translated] = true
+ template "template.#{extension}.tt", "#{file_path}.#{locale}.#{extension}", options
+ end
+ end
+
+ def self.source_root
+ File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'generators', 'snippet')
+ end
+
+ protected
+
+ def pages_path
+ File.join(target_path, 'app', 'views', 'snippets')
+ end
+
+ end
+
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/liquid.rb b/lib/locomotive/wagon/liquid.rb +19 -0
@@ @@ -0,0 +1,19 @@
+ require "locomotive/mounter"
+ require 'liquid'
+ require 'locomotive/wagon/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|
+ klass = "Locomotive::Mounter::Models::#{name.classify}".constantize
+
+ klass.class_eval <<-EOV
+ def to_liquid
+ ::Locomotive::Wagon::Liquid::Drops::#{name.classify}.new(self)
+ end
+ EOV
+ end
locomotive/wagon/liquid/drops/base.rb b/lib/locomotive/wagon/liquid/drops/base.rb +44 -0
@@ @@ -0,0 +1,44 @@
+ # Code taken from Mephisto sources (http://mephistoblog.com/)
+ module Locomotive
+ module Wagon
+ 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/wagon/liquid/drops/content_entry.rb b/lib/locomotive/wagon/liquid/drops/content_entry.rb +48 -0
@@ @@ -0,0 +1,48 @@
+ module Locomotive
+ module Wagon
+ module Liquid
+ module Drops
+ class ContentEntry < Base
+
+ delegate :seo_title, :meta_keywords, :meta_description, :to => '_source'
+
+ def _label
+ @_label ||= self._source._label
+ end
+
+ def _permalink
+ @_source._permalink.try(:parameterize)
+ end
+
+ alias :_slug :_permalink
+
+ def next
+ self
+ end
+
+ def previous
+ self
+ end
+
+ def errors
+ (@_source.errors || []).inject({}) do |memo, name|
+ memo[name] = ::I18n.t('errors.messages.blank')
+ memo
+ end
+ end
+
+ def before_method(meth)
+ return '' if self._source.nil?
+
+ if not @@forbidden_attributes.include?(meth.to_s)
+ self._source.send(meth)
+ else
+ nil
+ end
+ end
+
+ end
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/liquid/drops/content_types.rb b/lib/locomotive/wagon/liquid/drops/content_types.rb +121 -0
@@ @@ -0,0 +1,121 @@
+ module Locomotive
+ module Wagon
+ 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
+ self.group_entries_by(@content_type, $1)
+ elsif (meth.to_s =~ /^(.+)_options$/) == 0
+ self.select_options_for(@content_type, $1)
+ else
+ @content_type.send(meth)
+ end
+ end
+
+ protected
+
+ def group_entries_by(content_type, name)
+ field = @content_type.find_field(name)
+
+ return {} if field.nil? || !%w(belongs_to select).include?(field.type.to_s)
+
+ (@content_type.entries || []).group_by do |entry|
+ entry.send(name.to_sym)
+ end.to_a.collect do |group|
+ { name: group.first, entries: group.last }.with_indifferent_access
+ end
+ end
+
+ def select_options_for(content_type, name)
+ field = @content_type.find_field(name)
+
+ return {} if field.nil? || field.type.to_s != 'select'
+
+ field.select_options.map(&:name)
+ end
+
+ 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.entries.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/wagon/liquid/drops/page.rb b/lib/locomotive/wagon/liquid/drops/page.rb +36 -0
@@ @@ -0,0 +1,36 @@
+ module Locomotive
+ module Wagon
+ 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 = @_source.children || []
+ _children = _children.sort { |a, b| a.position.to_i <=> b.position.to_i }
+ @children ||= liquify(*_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/wagon/liquid/drops/site.rb b/lib/locomotive/wagon/liquid/drops/site.rb +21 -0
@@ @@ -0,0 +1,21 @@
+ module Locomotive
+ module Wagon
+ module Liquid
+ module Drops
+ class Site < Base
+
+ delegate :name, :seo_title, :meta_description, :meta_keywords, :to => '_source'
+
+ def index
+ @index ||= self.mounting_point.pages['index']
+ end
+
+ def pages
+ @pages ||= liquify(*self._source.pages)
+ end
+
+ end
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/liquid/errors.rb b/lib/locomotive/wagon/liquid/errors.rb +7 -0
@@ @@ -0,0 +1,7 @@
+ module Locomotive
+ module Wagon
+ module Liquid
+ class PageNotFound < ::Liquid::Error; end
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/liquid/filters/date.rb b/lib/locomotive/wagon/liquid/filters/date.rb +98 -0
@@ @@ -0,0 +1,98 @@
+ module Locomotive
+ module Wagon
+ 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/wagon/liquid/filters/html.rb b/lib/locomotive/wagon/liquid/filters/html.rb +154 -0
@@ @@ -0,0 +1,154 @@
+ module Locomotive
+ module Wagon
+ 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/wagon/liquid/filters/misc.rb b/lib/locomotive/wagon/liquid/filters/misc.rb +28 -0
@@ @@ -0,0 +1,28 @@
+ module Locomotive
+ module Wagon
+ 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/wagon/liquid/filters/resize.rb b/lib/locomotive/wagon/liquid/filters/resize.rb +18 -0
@@ @@ -0,0 +1,18 @@
+ module Locomotive
+ module Wagon
+ module Liquid
+ module Filters
+ module Resize
+
+ def resize(input, resize_string)
+ Locomotive::Wagon::Dragonfly.instance.resize_url(input, resize_string)
+ end
+
+ end
+
+ ::Liquid::Template.register_filter(Resize)
+
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/liquid/filters/text.rb b/lib/locomotive/wagon/liquid/filters/text.rb +50 -0
@@ @@ -0,0 +1,50 @@
+ require 'RedCloth'
+
+ module Locomotive
+ module Wagon
+ 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/wagon/liquid/filters/translate.rb b/lib/locomotive/wagon/liquid/filters/translate.rb +24 -0
@@ @@ -0,0 +1,24 @@
+ module Locomotive
+ module Wagon
+ module Liquid
+ module Filters
+ module Translate
+
+ def translate(key, locale = nil)
+ translation = @context.registers[:mounting_point].translations[key.to_s]
+
+ if translation
+ translation.get(locale) || translation.get(Locomotive::Mounter.locale)
+ else
+ "[unknown translation key: #{key}]"
+ end
+ end
+
+ end
+
+ ::Liquid::Template.register_filter(Translate)
+
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/liquid/patches.rb b/lib/locomotive/wagon/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/wagon/liquid/tags/consume.rb b/lib/locomotive/wagon/liquid/tags/consume.rb +58 -0
@@ @@ -0,0 +1,58 @@
+ module Locomotive
+ module Wagon
+ 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::Wagon::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/wagon/liquid/tags/csrf.rb b/lib/locomotive/wagon/liquid/tags/csrf.rb +34 -0
@@ @@ -0,0 +1,34 @@
+ module Locomotive
+ module Wagon
+ 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/wagon/liquid/tags/editable.rb b/lib/locomotive/wagon/liquid/tags/editable.rb +5 -0
@@ @@ -0,0 +1,5 @@
+ require 'locomotive/wagon/liquid/tags/editable/base'
+ require 'locomotive/wagon/liquid/tags/editable/short_text'
+ require 'locomotive/wagon/liquid/tags/editable/long_text'
+ require 'locomotive/wagon/liquid/tags/editable/file'
+ require 'locomotive/wagon/liquid/tags/editable/control'
\ No newline at end of file
locomotive/wagon/liquid/tags/editable/base.rb b/lib/locomotive/wagon/liquid/tags/editable/base.rb +46 -0
@@ @@ -0,0 +1,46 @@
+ module Locomotive
+ module Wagon
+ 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)
+ current_page = context.registers[:page]
+
+ element = current_page.find_editable_element(context['block'].try(:name), @slug)
+
+ if element.present?
+ render_element(context, element)
+ else
+ super
+ end
+ end
+
+ protected
+
+ def render_element(context, element)
+ element.content
+ end
+
+ end
+
+ end
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/liquid/tags/editable/control.rb b/lib/locomotive/wagon/liquid/tags/editable/control.rb +19 -0
@@ @@ -0,0 +1,19 @@
+ module Locomotive
+ module Wagon
+ module Liquid
+ module Tags
+ module Editable
+ class Control < Base
+
+ def render(context)
+ super
+ end
+
+ end
+
+ ::Liquid::Template.register_tag('editable_control', Control)
+ end
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/liquid/tags/editable/file.rb b/lib/locomotive/wagon/liquid/tags/editable/file.rb +15 -0
@@ @@ -0,0 +1,15 @@
+ module Locomotive
+ module Wagon
+ 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/wagon/liquid/tags/editable/long_text.rb b/lib/locomotive/wagon/liquid/tags/editable/long_text.rb +15 -0
@@ @@ -0,0 +1,15 @@
+ module Locomotive
+ module Wagon
+ 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/wagon/liquid/tags/editable/short_text.rb b/lib/locomotive/wagon/liquid/tags/editable/short_text.rb +15 -0
@@ @@ -0,0 +1,15 @@
+ module Locomotive
+ module Wagon
+ 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/wagon/liquid/tags/extends.rb b/lib/locomotive/wagon/liquid/tags/extends.rb +25 -0
@@ @@ -0,0 +1,25 @@
+ module Locomotive
+ module Wagon
+ 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/wagon/liquid/tags/google_analytics.rb b/lib/locomotive/wagon/liquid/tags/google_analytics.rb +28 -0
@@ @@ -0,0 +1,28 @@
+ module Locomotive
+ module Wagon
+ 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/wagon/liquid/tags/inline_editor.rb b/lib/locomotive/wagon/liquid/tags/inline_editor.rb +16 -0
@@ @@ -0,0 +1,16 @@
+ module Locomotive
+ module Wagon
+ 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/wagon/liquid/tags/locale_switcher.rb b/lib/locomotive/wagon/liquid/tags/locale_switcher.rb +180 -0
@@ @@ -0,0 +1,180 @@
+ module Locomotive
+ module Wagon
+ 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]
+ @default_locale = context.registers[:mounting_point].default_locale
+
+ output = %(<div id="locale-switcher">)
+
+ output += @site.locales.collect do |locale|
+ Locomotive::Mounter.with_locale(locale) do
+ fullpath = localized_fullpath(locale)
+
+ if @page.templatized?
+ permalink = context['entry']._permalink
+
+ if permalink
+ fullpath.gsub!('*', permalink)
+ else
+ fullpath = '404'
+ end
+ end
+
+ css = link_class(locale, context['locale'])
+
+ %(<a href="/#{fullpath}" class="#{css}">#{link_label(locale)}</a>)
+ end
+ end.join(@options[:sep])
+
+ output += %(</div>)
+ end
+
+ private
+
+ def link_class(locale, current_locale)
+ css = [locale]
+ css << 'current' if locale.to_s == current_locale.to_s
+ css.join(' ')
+ end
+
+ 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
+
+ def localized_fullpath(locale)
+ # @site.localized_page_fullpath(@page, locale)
+
+ return nil if @page.fullpath_translations.blank?
+
+ fullpath = @page.safe_fullpath || @page.fullpath_or_default
+
+ if locale.to_s == @default_locale.to_s # no need to specify the locale
+ @page.index? ? '' : fullpath
+ elsif @page.index? # avoid /en/index or /fr/index, prefer /en or /fr instead
+ locale
+ else
+ File.join(locale, fullpath)
+ end
+ end
+
+ end
+
+ ::Liquid::Template.register_tag('locale_switcher', LocaleSwitcher)
+ end
+ end
+ end
+ end
+ # module Locomotive
+ # module Wagon
+ # 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.to_s == context['default_locale'].to_s ? '/' : locale
+
+ # %(<a href="/#{fullpath}" class="#{locale} #{'current' if locale.to_s == context['default_locale'].to_s}">#{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/wagon/liquid/tags/nav.rb b/lib/locomotive/wagon/liquid/tags/nav.rb +167 -0
@@ @@ -0,0 +1,167 @@
+ module Locomotive
+ module Wagon
+ 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/wagon/liquid/tags/paginate.rb b/lib/locomotive/wagon/liquid/tags/paginate.rb +105 -0
@@ @@ -0,0 +1,105 @@
+ module Locomotive
+ module Wagon
+ 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/wagon/liquid/tags/seo.rb b/lib/locomotive/wagon/liquid/tags/seo.rb +74 -0
@@ @@ -0,0 +1,74 @@
+ module Locomotive
+ module Wagon
+ 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/wagon/liquid/tags/snippet.rb b/lib/locomotive/wagon/liquid/tags/snippet.rb +44 -0
@@ @@ -0,0 +1,44 @@
+ module Locomotive
+ module Wagon
+ module Liquid
+ module Tags
+
+ class Snippet < ::Liquid::Include
+
+ def render(context)
+ name = @template_name.gsub(/[\"\']/, '')
+
+ source = context.registers[:mounting_point].snippets[name].try(:source)
+
+ Locomotive::Wagon::Logger.info " Rendered snippet #{name}"
+
+ 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/wagon/liquid/tags/with_scope.rb b/lib/locomotive/wagon/liquid/tags/with_scope.rb +43 -0
@@ @@ -0,0 +1,43 @@
+ module Locomotive
+ module Wagon
+ 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/wagon/listen.rb b/lib/locomotive/wagon/listen.rb +57 -0
@@ @@ -0,0 +1,57 @@
+ require 'listen'
+
+ module Locomotive::Wagon
+ class Listen
+
+ attr_accessor :reader
+
+ def self.instance
+ @@instance = new
+ end
+
+ def start(reader)
+ self.reader = reader
+
+ self.definitions.each do |definition|
+ self.apply(definition)
+ end
+ end
+
+ def definitions
+ [
+ ['config', /\.yml/, [:site, :content_types, :pages, :snippets, :content_entries, :translations]],
+ ['app/views', /\.liquid/, [:pages, :snippets]],
+ ['app/content_types', /\.yml/, [:content_types, :content_entries]],
+ ['data', /\.yml/, :content_entries]
+ ]
+ end
+
+ protected
+
+ def apply(definition)
+ reloader = Proc.new do |modified, added, removed|
+ resources = [*definition.last]
+ names = resources.map { |n| "\"#{n}\"" }.join(', ')
+
+ Locomotive::Wagon::Logger.info "* Reloaded #{names} at #{Time.now}"
+
+ begin
+ reader.reload(resources)
+ rescue Exception => e
+ Locomotive::Wagon::MounterException.new('Unable to reload', e)
+ end
+ end
+
+ filter = definition[1]
+ path = File.join(self.reader.mounting_point.path, definition.first)
+ path = File.expand_path(path)
+
+ listener = ::Listen.to(path).filter(filter).change(&reloader)
+
+ # non blocking listener
+ listener.start(false)
+ end
+
+ end
+
+ end
\ No newline at end of file
locomotive/wagon/logger.rb b/lib/locomotive/wagon/logger.rb +54 -0
@@ @@ -0,0 +1,54 @@
+ module Locomotive
+ module Wagon
+
+ class Logger
+
+ attr_accessor :logger, :logfile_path, :stdout
+
+ def initialize
+ self.logger = nil
+ end
+
+ # Setup the single instance of the ruby logger.
+ #
+ # @param [ String ] path The path to the log file (default: log/wagon.log)
+ # @param [ Boolean ] stdout Instead of having a file, log to the standard output
+ #
+ def setup(path, stdout = false)
+ require 'logger'
+
+ self.stdout = stdout
+
+ self.logfile_path = File.expand_path(File.join(path, 'log', 'wagon.log'))
+ FileUtils.mkdir_p(File.dirname(logfile_path))
+
+ out = self.stdout ? STDOUT : self.logfile_path
+
+ self.logger = ::Logger.new(out).tap do |log|
+ log.level = ::Logger::DEBUG
+ log.formatter = proc do |severity, datetime, progname, msg|
+ "#{msg}\n"
+ end
+ end
+ end
+
+ def self.instance
+ @@instance ||= self.new
+ end
+
+ def self.setup(path, stdout = false)
+ self.instance.setup(path, stdout)
+ end
+
+ class << self
+ %w(debug info warn error fatal unknown).each do |name|
+ define_method(name) do |message|
+ self.instance.logger.send(name.to_sym, message)
+ end
+ end
+ end
+
+ end
+
+ end
+ end
\ No newline at end of file
locomotive/wagon/misc.rb b/lib/locomotive/wagon/misc.rb +5 -0
@@ @@ -0,0 +1,5 @@
+ require 'locomotive/wagon/misc/core_ext.rb'
+ require 'locomotive/wagon/misc/will_paginate.rb'
+ require 'locomotive/wagon/misc/httparty.rb'
+ require 'locomotive/wagon/misc/dragonfly.rb'
+ require 'locomotive/wagon/misc/i18n.rb'
\ No newline at end of file
locomotive/wagon/misc/core_ext.rb b/lib/locomotive/wagon/misc/core_ext.rb +29 -0
@@ @@ -0,0 +1,29 @@
+ unless Hash.instance_methods.include?(:underscore_keys)
+ class Hash
+
+ def underscore_keys
+ new_hash = {}
+
+ self.each_pair do |key, value|
+ if value.respond_to?(:collect!) # Array
+ value.collect do |item|
+ if item.respond_to?(:each_pair) # Hash item within
+ item.underscore_keys
+ else
+ item
+ end
+ end
+ elsif value.respond_to?(:each_pair) # Hash
+ value = value.underscore_keys
+ end
+
+ new_key = key.is_a?(String) ? key.underscore : key # only String keys
+
+ new_hash[new_key] = value
+ end
+
+ self.replace(new_hash)
+ end
+
+ end
+ end
\ No newline at end of file
locomotive/wagon/misc/dragonfly.rb b/lib/locomotive/wagon/misc/dragonfly.rb +79 -0
@@ @@ -0,0 +1,79 @@
+ module Locomotive
+ module Wagon
+ class Dragonfly
+
+ attr_accessor :path, :enabled
+
+ def enabled?
+ !!self.enabled
+ end
+
+ def resize_url(source, resize_string)
+ _source = (case source
+ when String then source
+ when Hash then source['url'] || source[:url]
+ else
+ source.try(:url)
+ end)
+
+ if _source.blank?
+ LocomotiveEditor::Logger.error "Unable to resize on the fly: #{source.inspect}"
+ return
+ end
+
+ return _source unless self.enabled?
+
+ if _source =~ /^http/
+ file = self.class.app.fetch_url(_source)
+ else
+ file = self.class.app.fetch_file(File.join(self.path, 'public', _source))
+ end
+
+ file.process(:thumb, resize_string).url
+ end
+
+ def self.app
+ ::Dragonfly[:images]
+ end
+
+
+ def self.instance
+ @@instance ||= new
+ end
+
+ def self.setup!(path)
+ self.instance.path = path
+ self.instance.enabled = false
+
+ begin
+ require 'rack/cache'
+ require 'RMagick'
+ require 'dragonfly'
+
+ ## initialize Dragonfly ##
+ app = ::Dragonfly[:images].configure_with(:imagemagick)
+
+ ## configure it ##
+ ::Dragonfly[:images].configure do |c|
+ convert = `which convert`.strip.presence || '/usr/local/bin/convert'
+ c.convert_command = convert
+ c.identify_command = convert
+
+ c.allow_fetch_url = true
+ c.allow_fetch_file = true
+
+ c.url_format = '/images/dynamic/:job/:basename.:format'
+ end
+
+ self.instance.enabled = true
+ rescue Exception => e
+ Locomotive::Wagon::Logger.warn %{
+ [Dragonfly] !disabled!
+ [Dragonfly] If you want to take full benefits of all the features in the LocomotiveWagon, we recommend you to install ImageMagick and RMagick. Check out the documentation here: http://doc.locomotivecms.com/editor/installation.
+ }
+ end
+ end
+
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/misc/httparty.rb b/lib/locomotive/wagon/misc/httparty.rb +46 -0
@@ @@ -0,0 +1,46 @@
+ require 'uri'
+
+ module Locomotive
+ module Wagon
+ 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 ||= '/'
+
+ # Locomotive::Wagon::Logger.debug "[WebService] consuming #{path}, #{options.inspect}"
+
+ response = self.get(path, options)
+
+ if response.code == 200
+ _response = response.parsed_response
+ if _response.respond_to?(:underscore_keys)
+ _response.underscore_keys
+ else
+ _response.collect(&:underscore_keys)
+ end
+ else
+ Locomotive::Wagon::Logger.error "[WebService] consumed #{path}, #{options.inspect}, response = #{response.inspect}"
+ nil
+ end
+
+ end
+
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/misc/i18n.rb b/lib/locomotive/wagon/misc/i18n.rb +2 -0
@@ @@ -0,0 +1,2 @@
+ I18n.load_path = Dir[File.join(File.dirname(__FILE__), "/../../../../locales/*.yml")]
+ I18n.backend.reload!
\ No newline at end of file
locomotive/wagon/misc/will_paginate.rb b/lib/locomotive/wagon/misc/will_paginate.rb +16 -0
@@ @@ -0,0 +1,16 @@
+ require 'will_paginate'
+ require 'will_paginate/collection'
+
+ Array.class_eval do
+ def paginate(options = {})
+ raise ArgumentError, "parameter hash expected (got #{options.inspect})" unless Hash === options
+
+ WillPaginate::Collection.create(
+ options[:page] || 1,
+ options[:per_page] || 30,
+ options[:total_entries] || self.length
+ ) { |pager|
+ pager.replace self[pager.offset, pager.per_page].to_a
+ }
+ end
+ end
\ No newline at end of file
locomotive/wagon/server.rb b/lib/locomotive/wagon/server.rb +81 -0
@@ @@ -0,0 +1,81 @@
+ require 'rack/showexceptions'
+ require 'coffee_script'
+
+ require 'locomotive/wagon/listen'
+ require 'locomotive/wagon/server/middleware'
+ require 'locomotive/wagon/server/favicon'
+ require 'locomotive/wagon/server/dynamic_assets'
+ require 'locomotive/wagon/server/logging'
+ require 'locomotive/wagon/server/entry_submission'
+ require 'locomotive/wagon/server/path'
+ require 'locomotive/wagon/server/locale'
+ require 'locomotive/wagon/server/page'
+ require 'locomotive/wagon/server/templatized_page'
+ require 'locomotive/wagon/server/not_found'
+ require 'locomotive/wagon/server/renderer'
+
+ require 'locomotive/wagon/liquid'
+ require 'locomotive/wagon/misc'
+
+ module Locomotive::Wagon
+ class Server
+
+ def initialize(reader, options = {})
+ Locomotive::Wagon::Dragonfly.setup!(reader.mounting_point.path)
+
+ @reader = reader
+ @app = self.create_rack_app(@reader)
+
+ unless options[:disable_listen]
+ Locomotive::Wagon::Listen.instance.start(@reader)
+ end
+ end
+
+ def call(env)
+ env['wagon.mounting_point'] = @reader.mounting_point
+ @app.call(env)
+ end
+
+ protected
+
+ def create_rack_app(reader)
+ Rack::Builder.new do
+ use Rack::ShowExceptions
+ use Rack::Lint
+
+ use Rack::Session::Cookie, {
+ key: 'rack.session',
+ domain: '0.0.0.0',
+ path: '/',
+ expire_after: 2592000,
+ secret: 'uselessinlocal'
+ }
+
+ use ::Dragonfly::Middleware, :images
+
+ use Rack::Static, {
+ urls: ['/images', '/fonts', '/samples'],
+ root: File.join(reader.mounting_point.path, 'public')
+ }
+
+ use Favicon
+ use DynamicAssets
+
+ use Logging
+
+ use EntrySubmission
+
+ use Path
+ use Locale
+
+ use Page
+ use TemplatizedPage
+ use NotFound
+ use Renderer
+
+ run Renderer.new
+ end
+ end
+
+ end
+ end
\ No newline at end of file
locomotive/wagon/server/dynamic_assets.rb b/lib/locomotive/wagon/server/dynamic_assets.rb +31 -0
@@ @@ -0,0 +1,31 @@
+ module Locomotive::Wagon
+ 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/wagon/server/entry_submission.rb b/lib/locomotive/wagon/server/entry_submission.rb +116 -0
@@ @@ -0,0 +1,116 @@
+ module Locomotive::Wagon
+ class Server
+
+ # Mimic the submission of a content entry
+ #
+ class EntrySubmission < Middleware
+
+ def call(env)
+ self.set_accessors(env)
+
+ if self.request.post? && env['PATH_INFO'] =~ /^\/entry_submissions\/(.*)/
+ self.process_form($1)
+
+ if @entry.valid?
+ if self.html?
+ self.record_submitted_entry
+ self.redirect_to self.callback_url
+ elsif self.json?
+ self.json_response
+ end
+ else
+ if self.html?
+ if self.callback_url =~ /^http:\/\//
+ self.redirect_to self.callback_url
+ else
+ env['PATH_INFO'] = self.callback_url
+ self.liquid_assigns[@content_type.slug.singularize] = @entry
+ app.call(env)
+ end
+ elsif self.json?
+ self.json_response(422)
+ end
+ end
+ else
+ self.fetch_submitted_entry
+
+ app.call(env)
+ end
+ end
+
+ protected
+
+ def record_submitted_entry
+ self.request.session[:now] ||= {}
+ self.request.session[:now][:submitted_entry] = [@content_type.slug, @entry._slug]
+ end
+
+ def fetch_submitted_entry
+ if data = self.request.session[:now].try(:delete, :submitted_entry)
+ content_type = self.mounting_point.content_types[data.first.to_s]
+
+ entry = (content_type.entries || []).detect { |e| e._slug == data.last }
+
+ # do not keep track of the entry
+ content_type.entries.delete(entry) if entry
+
+ # add it to the additional liquid assigns for the next liquid rendering
+ if entry
+ self.liquid_assigns[content_type.slug.singularize] = entry
+ end
+ end
+ end
+
+ # Mimic the creation of a content entry with a minimal validation.
+ #
+ # @param [ String ] permalink The permalink (or slug) of the content type
+ #
+ #
+ def process_form(permalink)
+ permalink = permalink.split('.').first
+
+ @content_type = self.mounting_point.content_types[permalink]
+
+ raise "Unknown content type '#{@content_type.inspect}'" if @content_type.nil?
+
+ @entry = @content_type.build_entry(self.params[:entry] || self.params[:content])
+
+ # if not valid, we do not need to keep track of the entry
+ @content_type.entries.delete(@entry) if !@entry.valid?
+ end
+
+ def callback_url
+ (@entry.valid? ? params[:success_callback] : params[:error_callback]) || '/'
+ end
+
+ # Build the JSON response
+ #
+ # @param [ Integer ] status The HTTP return code
+ #
+ # @return [ Array ] The rack response depending on the validation status and the requested format
+ #
+ def json_response(status = 200)
+ locale = self.mounting_point.default_locale
+
+ if self.request.path =~ /^\/(#{self.mounting_point.locales.join('|')})+(\/|$)/
+ locale = $1
+ end
+
+ hash = @entry.to_hash(false).tap do |_hash|
+ if !@entry.valid?
+ _hash['errors'] = @entry.errors.inject({}) do |memo, name|
+ memo[name] = ::I18n.t('errors.messages.blank', locale: locale)
+ memo
+ end
+ end
+ end
+
+ [status, { 'Content-Type' => 'application/json' }, [
+ { @content_type.slug.singularize => hash }.to_json
+ ]]
+ end
+
+ end
+
+ end
+ end
\ No newline at end of file
locomotive/wagon/server/favicon.rb b/lib/locomotive/wagon/server/favicon.rb +17 -0
@@ @@ -0,0 +1,17 @@
+ module Locomotive::Wagon
+ 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/wagon/server/locale.rb b/lib/locomotive/wagon/server/locale.rb +42 -0
@@ @@ -0,0 +1,42 @@
+ module Locomotive::Wagon
+ 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)
+ locale = self.mounting_point.default_locale
+
+ if self.path =~ /^(#{self.mounting_point.locales.join('|')})+(\/|$)/
+ locale = $1
+ self.path = self.path.gsub($1 + $2, '')
+ self.path = 'index' if self.path.blank?
+ end
+
+ Locomotive::Mounter.locale = locale
+ ::I18n.locale = locale
+
+ self.log "Detecting locale #{locale.upcase}"
+
+ env['wagon.locale'] = locale
+ env['wagon.path'] = self.path
+ end
+
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/server/logging.rb b/lib/locomotive/wagon/server/logging.rb +32 -0
@@ @@ -0,0 +1,32 @@
+ module Locomotive::Wagon
+ class Server
+
+ # Track the request into the current logger
+ #
+ class Logging < Middleware
+
+ def call(env)
+ now = Time.now
+
+ log "Started #{env['REQUEST_METHOD'].upcase} \"#{env['PATH_INFO']}\" at #{now}"
+
+ app.call(env).tap do |response|
+ done_in_ms = (Time.now - now) * 1000
+ log "Completed #{code_to_human(response.first)} in #{done_in_ms}ms\n\n"
+ end
+ end
+
+ protected
+
+ def code_to_human(code)
+ case code.to_i
+ when 200 then '200 OK'
+ when 301 then '301 Found'
+ when 302 then '302 Found'
+ when 404 then '404 Not Found'
+ end
+ end
+
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/server/middleware.rb b/lib/locomotive/wagon/server/middleware.rb +59 -0
@@ @@ -0,0 +1,59 @@
+ module Locomotive::Wagon
+ class Server
+
+ class Middleware
+
+ attr_accessor :app, :request, :path, :liquid_assigns
+
+ attr_accessor :mounting_point, :page, :content_entry
+
+ def initialize(app = nil)
+ @app = app
+ end
+
+ def call(env)
+ app.call(env)
+ end
+
+ protected
+
+ def set_accessors(env)
+ self.path = env['wagon.path']
+ self.request = Rack::Request.new(env)
+ self.mounting_point = env['wagon.mounting_point']
+ self.page = env['wagon.page']
+ self.content_entry = env['wagon.content_entry']
+
+ env['wagon.liquid_assigns'] ||= {}
+ self.liquid_assigns = env['wagon.liquid_assigns']
+ end
+
+ def site
+ self.mounting_point.site
+ end
+
+ def params
+ self.request.params.deep_symbolize_keys
+ end
+
+ def html?
+ self.request.media_type == 'text/html' || !self.request.xhr?
+ end
+
+ def json?
+ self.request.content_type == 'application/json' || File.extname(self.request.path) == '.json'
+ end
+
+ def redirect_to(location, type = 301)
+ self.log "Redirected to #{location}"
+ [type, { 'Content-Type' => 'text/html', 'Location' => location }, []]
+ end
+
+ def log(msg)
+ Locomotive::Wagon::Logger.info msg
+ end
+
+ end
+
+ end
+ end
\ No newline at end of file
locomotive/wagon/server/not_found.rb b/lib/locomotive/wagon/server/not_found.rb +19 -0
@@ @@ -0,0 +1,19 @@
+ module Locomotive::Wagon
+ class Server
+
+ class NotFound < Middleware
+
+ def call(env)
+ self.set_accessors(env)
+
+ if self.page.nil?
+ self.log "Page not found"
+ env['wagon.page'] = self.mounting_point.pages['404']
+ end
+
+ app.call(env)
+ end
+
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/server/page.rb b/lib/locomotive/wagon/server/page.rb +61 -0
@@ @@ -0,0 +1,61 @@
+ module Locomotive::Wagon
+ 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)
+ page = self.fetch_page
+
+ if page
+ self.log "Found page \"#{page.title}\" [/#{page.inspect}]"
+ end
+
+ env['wagon.page'] = page
+ end
+
+ def fetch_page
+ matchers = self.path_combinations(self.path)
+
+ self.mounting_point.pages.values.detect do |_page|
+ matchers.include?(_page.safe_fullpath) ||
+ matchers.include?(_page.safe_fullpath.try(: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/wagon/server/path.rb b/lib/locomotive/wagon/server/path.rb +34 -0
@@ @@ -0,0 +1,34 @@
+ module Locomotive::Wagon
+ 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['wagon.path'] = path
+ end
+
+ end
+
+ end
+ end
\ No newline at end of file
locomotive/wagon/server/renderer.rb b/lib/locomotive/wagon/server/renderer.rb +105 -0
@@ @@ -0,0 +1,105 @@
+ module Locomotive::Wagon
+ class Server
+
+ class Renderer < Middleware
+
+ def call(env)
+ self.set_accessors(env)
+
+ if self.page
+ if self.page.redirect?
+ self.redirect_to(self.page.redirect_url, self.page.redirect_type)
+ else
+ type = self.page.response_type || 'text/html'
+ html = self.render
+
+ self.log " Rendered liquid page template"
+
+ [200, { 'Content-Type' => type }, [html]]
+ end
+ else
+ # 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
+
+ # assigns from other middlewares
+ assigns.merge!(self.liquid_assigns)
+
+ assigns.merge!(other_assigns)
+
+ # templatized page
+ if self.page && self.content_entry
+ ['content_entry', 'entry', self.page.content_type.slug.singularize].each do |key|
+ assigns[key] = self.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::Wagon::Liquid::Drops::ContentTypes.new,
+ 'contents' => Locomotive::Wagon::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
locomotive/wagon/server/templatized_page.rb b/lib/locomotive/wagon/server/templatized_page.rb +32 -0
@@ @@ -0,0 +1,32 @@
+ module Locomotive::Wagon
+ class Server
+
+ class TemplatizedPage < Middleware
+
+ def call(env)
+ self.set_accessors(env)
+
+ if self.page && self.page.templatized?
+ self.set_content_entry!(env)
+ end
+
+ app.call(env)
+ end
+
+ protected
+
+ def set_content_entry!(env)
+ %r(^#{self.page.safe_fullpath.gsub('*', '([^\/]+)')}$) =~ self.path
+
+ permalink = $1
+
+ if content_entry = self.page.content_type.find_entry(permalink)
+ env['wagon.content_entry'] = content_entry
+ else
+ env['wagon.page'] = nil
+ end
+ end
+
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/standalone_server.rb b/lib/locomotive/wagon/standalone_server.rb +28 -0
@@ @@ -0,0 +1,28 @@
+ $:.unshift(File.expand_path(File.dirname(__FILE__) + '/../..'))
+
+ require 'locomotive/wagon/logger'
+ require 'locomotive/wagon/version'
+ require 'locomotive/wagon/exceptions'
+ require 'locomotive/wagon/server'
+ require 'locomotive/mounter'
+
+ module Locomotive
+ module Wagon
+ class StandaloneServer < Server
+
+ def initialize(path)
+ Locomotive::Wagon::Logger.setup(path, false)
+
+ # get the reader
+ reader = Locomotive::Mounter::Reader::FileSystem.instance
+ reader.run!(path: path)
+ reader
+
+ Bundler.require 'misc'
+
+ # run the rack app
+ super(reader, disable_listen: true)
+ end
+ end
+ end
+ end
\ No newline at end of file
locomotive/wagon/version.rb b/lib/locomotive/wagon/version.rb +5 -0
@@ @@ -0,0 +1,5 @@
+ module Locomotive
+ module Wagon
+ VERSION = '1.0.0.alpha8'
+ end
+ end
locomotivecms_builder.gemspec +0 -42
@@ @@ -1,42 +0,0 @@
- # -*- encoding: utf-8 -*-
- lib = File.expand_path('../lib', __FILE__)
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
- require 'locomotive/builder/version'
-
- Gem::Specification.new do |gem|
- gem.name = 'locomotivecms_builder'
- gem.version = Locomotive::Builder::VERSION
- gem.authors = ['Didier Lafforgue', 'Rodrigo Alvarez']
- gem.email = ['did@locomotivecms.com', 'papipo@gmail.com']
- gem.description = %q{The LocomotiveCMS builder is a site generator for the LocomotiveCMS engine}
- gem.summary = %q{The LocomotiveCMS builder is a site generator for the LocomotiveCMS engine powered by all the efficient and modern HTML development tools (Haml, SASS, Compass, Less).}
- gem.homepage = 'http://www.locomotivecms.com'
-
- 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 '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 'listen', '~> 0.7.0'
-
- 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', '1.0.0.alpha4'
-
- gem.add_dependency 'faker', '~> 0.9.5'
-
- 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
locomotivecms_wagon.gemspec +42 -0
@@ @@ -0,0 +1,42 @@
+ # -*- encoding: utf-8 -*-
+ lib = File.expand_path('../lib', __FILE__)
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+ require 'locomotive/wagon/version'
+
+ Gem::Specification.new do |gem|
+ gem.name = 'locomotivecms_wagon'
+ gem.version = Locomotive::Wagon::VERSION
+ gem.authors = ['Didier Lafforgue', 'Rodrigo Alvarez']
+ gem.email = ['did@locomotivecms.com', 'papipo@gmail.com']
+ gem.description = %q{The LocomotiveCMS wagon is a site generator for the LocomotiveCMS engine}
+ gem.summary = %q{The LocomotiveCMS wagon is a site generator for the LocomotiveCMS engine powered by all the efficient and modern HTML development tools (Haml, SASS, Compass, Less).}
+ gem.homepage = 'http://www.locomotivecms.com'
+
+ 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 = ['wagon']
+
+ 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 'listen', '~> 0.7.0'
+
+ 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', '1.0.0.alpha4'
+
+ gem.add_dependency 'faker', '~> 0.9.5'
+
+ 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
spec/integration/cassettes/push.yml +35 -0
@@ @@ -772,4 +772,39 @@ http_interactions:
world!","es":"\u00a1Hola, Mundo!"}}'
http_version:
recorded_at: Thu, 24 Jan 2013 11:09:54 GMT
+ - request:
+ method: get
+ uri: http://locomotive.engine.dev:3000/locomotive/api/translations.json?auth_token=jKkxPorzWo8uNYUxiiQC
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers: {}
+ response:
+ status:
+ code: 401
+ message: ! 'Unauthorized '
+ headers:
+ Content-Type:
+ - application/json; charset=utf-8
+ X-Ua-Compatible:
+ - IE=Edge
+ Cache-Control:
+ - no-cache
+ X-Request-Id:
+ - 8b99d01f9a7fc58002c7d92f8cbcec2e
+ X-Runtime:
+ - '1.137608'
+ Server:
+ - WEBrick/1.3.1 (Ruby/1.9.3/2012-11-10)
+ Date:
+ - Wed, 27 Feb 2013 11:39:27 GMT
+ Content-Length:
+ - '41'
+ Connection:
+ - Keep-Alive
+ body:
+ encoding: US-ASCII
+ string: ! '{"error":"Invalid authentication token."}'
+ http_version:
+ recorded_at: Wed, 27 Feb 2013 11:39:27 GMT
recorded_with: VCR 2.3.0
spec/integration/server_spec.rb +3 -3
@@ @@ -1,16 +1,16 @@
# encoding: utf-8
require File.dirname(__FILE__) + "/integration_helper"
- require "locomotive/builder/server"
+ require "locomotive/wagon/server"
require "rack/test"
- describe Locomotive::Builder::Server do
+ describe Locomotive::Wagon::Server do
include Rack::Test::Methods
def app
clone_site
reader = Locomotive::Mounter::Reader::FileSystem.instance
reader.run!(path: "site")
- Locomotive::Builder::Server.new(reader)
+ Locomotive::Wagon::Server.new(reader)
end
it "shows the index page" do
spec/integration/sites_spec.rb +2 -2
@@ @@ -1,6 +1,6 @@
require File.dirname(__FILE__) + "/integration_helper"
- describe Locomotive::Builder do
+ describe Locomotive::Wagon do
it "imports" do
File.exists?("site/config/site.yml").should be_false
clone_site
@@ @@ -19,7 +19,7 @@ describe Locomotive::Builder do
text.gsub!(/Content of the home page/, "New content of the home page")
File.open(file_name, "w") { |file| file.puts text}
VCR.use_cassette('push') do
- Locomotive::Builder.push("site", {"host" => "locomotive.engine.dev:3000"}, "email" => "admin@locomotivecms.com", "password" => "locomotive")
+ Locomotive::Wagon.push("site", {"host" => "locomotive.engine.dev:3000"}, "email" => "admin@locomotivecms.com", "password" => "locomotive")
end
WebMock.should have_requested(:put, /pages\/.+.json\?auth_token=.+/).with(:body => /page\[raw_template\]=New%20content%20of%20the%20home%20page/).once
spec/spec_helper.rb +1 -1
@@ @@ -1,4 +1,4 @@
- require "locomotive/builder"
+ require "locomotive/wagon"
require "rspec"
Dir["#{File.expand_path('../support', __FILE__)}/*.rb"].each do |file|
spec/support/helpers.rb +1 -1
@@ @@ -6,7 +6,7 @@ module Spec
def clone_site
VCR.use_cassette('pull') do
- Locomotive::Builder.clone("site", {"host" => "locomotive.engine.dev:3000"}, "email" => "admin@locomotivecms.com", "password" => "locomotive")
+ Locomotive::Wagon.clone("site", {"host" => "locomotive.engine.dev:3000"}, "email" => "admin@locomotivecms.com", "password" => "locomotive")
end
end
end