Merge pull request #1138 from middleman/template_jail
Put template rendering in a jail
This commit is contained in:
commit
09c7bda6b1
|
@ -10,6 +10,7 @@ matrix:
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- rvm: ruby-head
|
- rvm: ruby-head
|
||||||
|
- rvm: 2.1.0
|
||||||
- rvm: jruby-19mode
|
- rvm: jruby-19mode
|
||||||
env: TEST=true
|
env: TEST=true
|
||||||
before_install: git submodule update --init --recursive
|
before_install: git submodule update --init --recursive
|
||||||
|
|
|
@ -44,15 +44,6 @@ module Middleman
|
||||||
# Runs after the build is finished
|
# Runs after the build is finished
|
||||||
define_hook :after_build
|
define_hook :after_build
|
||||||
|
|
||||||
# Mix-in helper methods. Accepts either a list of Modules
|
|
||||||
# and/or a block to be evaluated
|
|
||||||
# @return [void]
|
|
||||||
def self.helpers(*extensions, &block)
|
|
||||||
class_eval(&block) if block_given?
|
|
||||||
include(*extensions) if extensions.any?
|
|
||||||
end
|
|
||||||
delegate :helpers, :to => :"self.class"
|
|
||||||
|
|
||||||
# Root project directory (overwritten in middleman build/server)
|
# Root project directory (overwritten in middleman build/server)
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def self.root
|
def self.root
|
||||||
|
@ -170,11 +161,16 @@ module Middleman
|
||||||
# Template cache
|
# Template cache
|
||||||
attr_reader :cache
|
attr_reader :cache
|
||||||
|
|
||||||
|
attr_reader :generic_template_context
|
||||||
|
delegate :link_to, :image_tag, :to => :generic_template_context
|
||||||
|
|
||||||
# Initialize the Middleman project
|
# Initialize the Middleman project
|
||||||
def initialize(&block)
|
def initialize(&block)
|
||||||
@cache = ::Tilt::Cache.new
|
@cache = ::Tilt::Cache.new
|
||||||
@logger = ::Middleman::Logger.singleton
|
@logger = ::Middleman::Logger.singleton
|
||||||
@config_context = ConfigContext.new(self)
|
@template_context_class = Class.new(Middleman::TemplateContext)
|
||||||
|
@generic_template_context = @template_context_class.new(self)
|
||||||
|
@config_context = ConfigContext.new(self, @template_context_class)
|
||||||
|
|
||||||
# Setup the default values from calls to set before initialization
|
# Setup the default values from calls to set before initialization
|
||||||
self.class.config.load_settings(self.class.superclass.config.all_settings)
|
self.class.config.load_settings(self.class.superclass.config.all_settings)
|
||||||
|
|
|
@ -6,16 +6,32 @@ module Middleman
|
||||||
attr_reader :app
|
attr_reader :app
|
||||||
|
|
||||||
# Whitelist methods that can reach out.
|
# Whitelist methods that can reach out.
|
||||||
delegate :config, :logger, :activate, :use, :map, :mime_type, :data, :helpers, :template_extensions, :root, :to => :app
|
delegate :config, :logger, :activate, :use, :map, :mime_type, :data, :template_extensions, :root, :to => :app
|
||||||
|
|
||||||
def initialize(app)
|
def initialize(app, template_context_class)
|
||||||
@app = app
|
@app = app
|
||||||
|
@template_context_class = template_context_class
|
||||||
|
|
||||||
@ready_callbacks = []
|
@ready_callbacks = []
|
||||||
@after_build_callbacks = []
|
@after_build_callbacks = []
|
||||||
@after_configuration_callbacks = []
|
@after_configuration_callbacks = []
|
||||||
@configure_callbacks = {}
|
@configure_callbacks = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def helpers(*helper_modules, &block)
|
||||||
|
helper_modules ||= []
|
||||||
|
|
||||||
|
if block_given?
|
||||||
|
block_module = Module.new
|
||||||
|
block_module.module_eval(&block)
|
||||||
|
helper_modules << block_module
|
||||||
|
end
|
||||||
|
|
||||||
|
helper_modules.each do |mod|
|
||||||
|
@template_context_class.send :include, mod
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def ready(&block)
|
def ready(&block)
|
||||||
@ready_callbacks << block
|
@ready_callbacks << block
|
||||||
end
|
end
|
||||||
|
|
|
@ -182,6 +182,11 @@ module Middleman
|
||||||
end
|
end
|
||||||
|
|
||||||
if klass.is_a?(::Middleman::Extension)
|
if klass.is_a?(::Middleman::Extension)
|
||||||
|
# Forward Extension helpers to TemplateContext
|
||||||
|
(klass.class.defined_helpers || []).each do |m|
|
||||||
|
@template_context_class.send(:include, m)
|
||||||
|
end
|
||||||
|
|
||||||
::Middleman::Extension.activated_extension(klass)
|
::Middleman::Extension.activated_extension(klass)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,7 +24,7 @@ module Middleman
|
||||||
require filename
|
require filename
|
||||||
next unless Object.const_defined?(module_name.to_sym)
|
next unless Object.const_defined?(module_name.to_sym)
|
||||||
|
|
||||||
helpers Object.const_get(module_name.to_sym)
|
@template_context_class.send :include, Object.const_get(module_name.to_sym)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -81,13 +81,11 @@ module Middleman::CoreExtensions
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
helpers do
|
|
||||||
# Get the template data from a path
|
# Get the template data from a path
|
||||||
# @param [String] path
|
# @param [String] path
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def template_data_for_file(path)
|
def template_data_for_file(path)
|
||||||
extensions[:frontmatter].data(path).last
|
data(path).last
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def data(path)
|
def data(path)
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
# Shutup Tilt Warnings
|
require 'middleman-core/template_context'
|
||||||
# @private
|
|
||||||
class Tilt::Template
|
|
||||||
def warn(*args)
|
|
||||||
# Kernel.warn(*args)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Rendering extension
|
# Rendering extension
|
||||||
module Middleman
|
module Middleman
|
||||||
|
|
||||||
module CoreExtensions
|
module CoreExtensions
|
||||||
module Rendering
|
module Rendering
|
||||||
|
|
||||||
|
@ -136,16 +131,17 @@ module Middleman
|
||||||
::I18n.locale = opts[:lang] if opts[:lang]
|
::I18n.locale = opts[:lang] if opts[:lang]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Use a dup of self as a context so that instance variables set within
|
# Sandboxed class for template eval
|
||||||
# the template don't persist for other templates.
|
context = @template_context_class.new(self, locs, opts)
|
||||||
context = self.dup
|
|
||||||
|
if context.respond_to?(:init_haml_helpers)
|
||||||
|
context.init_haml_helpers
|
||||||
|
end
|
||||||
|
|
||||||
blocks.each do |block|
|
blocks.each do |block|
|
||||||
context.instance_eval(&block)
|
context.instance_eval(&block)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Store current locs/opts for later
|
|
||||||
@current_locs = locs, @current_opts = opts
|
|
||||||
|
|
||||||
# Keep rendering template until we've used up all extensions. This
|
# Keep rendering template until we've used up all extensions. This
|
||||||
# handles cases like `style.css.sass.erb`
|
# handles cases like `style.css.sass.erb`
|
||||||
content = nil
|
content = nil
|
||||||
|
@ -170,60 +166,6 @@ module Middleman
|
||||||
# Pop all the saved variables from earlier as we may be returning to a
|
# Pop all the saved variables from earlier as we may be returning to a
|
||||||
# previous render (layouts, partials, nested layouts).
|
# previous render (layouts, partials, nested layouts).
|
||||||
::I18n.locale = old_locale if defined?(::I18n)
|
::I18n.locale = old_locale if defined?(::I18n)
|
||||||
@content_blocks = nil
|
|
||||||
@current_locs = nil
|
|
||||||
@current_opts = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# Sinatra/Padrino compatible render method signature referenced by some view
|
|
||||||
# helpers. Especially partials.
|
|
||||||
#
|
|
||||||
# @param [String, Symbol] engine
|
|
||||||
# @param [String, Symbol] data
|
|
||||||
# @param [Hash] options
|
|
||||||
# @return [String]
|
|
||||||
def render(engine, data, options={}, &block)
|
|
||||||
data = data.to_s
|
|
||||||
|
|
||||||
locals = options[:locals]
|
|
||||||
|
|
||||||
found_partial = false
|
|
||||||
engine = nil
|
|
||||||
|
|
||||||
# If the path is known to the sitemap
|
|
||||||
if resource = sitemap.find_resource_by_path(current_path)
|
|
||||||
current_dir = File.dirname(resource.source_file)
|
|
||||||
engine = File.extname(resource.source_file)[1..-1].to_sym
|
|
||||||
|
|
||||||
# Look for partials relative to the current path
|
|
||||||
relative_dir = File.join(current_dir.sub(%r{^#{Regexp.escape(self.source_dir)}/?}, ''), data)
|
|
||||||
|
|
||||||
# Try to use the current engine first
|
|
||||||
found_partial, found_engine = resolve_template(relative_dir, :preferred_engine => engine, :try_without_underscore => true)
|
|
||||||
|
|
||||||
# Fall back to any engine available
|
|
||||||
if !found_partial
|
|
||||||
found_partial, found_engine = resolve_template(relative_dir, :try_without_underscore => true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Look in the partials_dir for the partial with the current engine
|
|
||||||
partials_path = File.join(config[:partials_dir], data)
|
|
||||||
if !found_partial && !engine.nil?
|
|
||||||
found_partial, found_engine = resolve_template(partials_path, :preferred_engine => engine, :try_without_underscore => true)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Look in the root with any engine
|
|
||||||
if !found_partial
|
|
||||||
found_partial, found_engine = resolve_template(partials_path, :try_without_underscore => true)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Render the partial if found, otherwide throw exception
|
|
||||||
if found_partial
|
|
||||||
render_individual_file(found_partial, locals, options, self, &block)
|
|
||||||
else
|
|
||||||
raise ::Middleman::CoreExtensions::Rendering::TemplateNotFound, "Could not locate partial: #{data}"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Render an on-disk file. Used for everything, including layouts.
|
# Render an on-disk file. Used for everything, including layouts.
|
||||||
|
@ -233,7 +175,7 @@ module Middleman
|
||||||
# @param [Hash] opts
|
# @param [Hash] opts
|
||||||
# @param [Class] context
|
# @param [Class] context
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def render_individual_file(path, locs = {}, opts = {}, context = self, &block)
|
def render_individual_file(path, locs = {}, opts = {}, context, &block)
|
||||||
path = path.to_s
|
path = path.to_s
|
||||||
|
|
||||||
# Detect the remdering engine from the extension
|
# Detect the remdering engine from the extension
|
||||||
|
@ -244,7 +186,7 @@ module Middleman
|
||||||
context.current_engine, engine_was = engine, context.current_engine
|
context.current_engine, engine_was = engine, context.current_engine
|
||||||
|
|
||||||
# Save current buffer for later
|
# Save current buffer for later
|
||||||
@_out_buf, _buf_was = '', @_out_buf
|
_buf_was = context.save_buffer
|
||||||
|
|
||||||
# Read from disk or cache the contents of the file
|
# Read from disk or cache the contents of the file
|
||||||
body = if opts[:template_body]
|
body = if opts[:template_body]
|
||||||
|
@ -287,7 +229,7 @@ module Middleman
|
||||||
output
|
output
|
||||||
ensure
|
ensure
|
||||||
# Reset stored buffer
|
# Reset stored buffer
|
||||||
@_out_buf = _buf_was
|
context.restore_buffer(_buf_was)
|
||||||
context.current_engine = engine_was
|
context.current_engine = engine_was
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -295,8 +237,12 @@ module Middleman
|
||||||
# @param [String] path
|
# @param [String] path
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def template_data_for_file(path)
|
def template_data_for_file(path)
|
||||||
|
if extensions[:frontmatter]
|
||||||
|
extensions[:frontmatter].template_data_for_file(path)
|
||||||
|
else
|
||||||
File.read(File.expand_path(path, source_dir))
|
File.read(File.expand_path(path, source_dir))
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Get a hash of configuration options for a given file extension, from
|
# Get a hash of configuration options for a given file extension, from
|
||||||
# config.rb
|
# config.rb
|
||||||
|
@ -393,49 +339,6 @@ module Middleman
|
||||||
layout_path
|
layout_path
|
||||||
end
|
end
|
||||||
|
|
||||||
# Allow layouts to be wrapped in the contents of other layouts
|
|
||||||
# @param [String, Symbol] layout_name
|
|
||||||
# @return [void]
|
|
||||||
def wrap_layout(layout_name, &block)
|
|
||||||
# Save current buffer for later
|
|
||||||
@_out_buf, _buf_was = '', @_out_buf
|
|
||||||
|
|
||||||
layout_path = locate_layout(layout_name, self.current_engine)
|
|
||||||
|
|
||||||
extension = File.extname(layout_path)
|
|
||||||
engine = extension[1..-1].to_sym
|
|
||||||
|
|
||||||
# Store last engine for later (could be inside nested renders)
|
|
||||||
self.current_engine, engine_was = engine, self.current_engine
|
|
||||||
|
|
||||||
begin
|
|
||||||
content = if block_given?
|
|
||||||
capture_html(&block)
|
|
||||||
else
|
|
||||||
''
|
|
||||||
end
|
|
||||||
ensure
|
|
||||||
# Reset stored buffer
|
|
||||||
@_out_buf = _buf_was
|
|
||||||
end
|
|
||||||
|
|
||||||
concat_safe_content render_individual_file(layout_path, @current_locs || {}, @current_opts || {}, self) { content }
|
|
||||||
ensure
|
|
||||||
self.current_engine = engine_was
|
|
||||||
end
|
|
||||||
|
|
||||||
# The currently rendering engine
|
|
||||||
# @return [Symbol, nil]
|
|
||||||
def current_engine
|
|
||||||
@_current_engine ||= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# The currently rendering engine
|
|
||||||
# @return [Symbol, nil]
|
|
||||||
def current_engine=(v)
|
|
||||||
@_current_engine = v
|
|
||||||
end
|
|
||||||
|
|
||||||
# Find a template on disk given a output path
|
# Find a template on disk given a output path
|
||||||
# @param [String] request_path
|
# @param [String] request_path
|
||||||
# @param [Hash] options
|
# @param [Hash] options
|
||||||
|
|
|
@ -20,7 +20,7 @@ module Middleman
|
||||||
def helpers(*m, &block)
|
def helpers(*m, &block)
|
||||||
self.defined_helpers ||= []
|
self.defined_helpers ||= []
|
||||||
|
|
||||||
if block
|
if block_given?
|
||||||
mod = Module.new
|
mod = Module.new
|
||||||
mod.module_eval(&block)
|
mod.module_eval(&block)
|
||||||
m = [mod]
|
m = [mod]
|
||||||
|
@ -81,8 +81,11 @@ module Middleman
|
||||||
def app=(app)
|
def app=(app)
|
||||||
@app = app
|
@app = app
|
||||||
|
|
||||||
(self.class.defined_helpers || []).each do |m|
|
ext = self
|
||||||
app.class.send(:include, m)
|
if ext.respond_to?(:instance_available)
|
||||||
|
@klass.instance_available do
|
||||||
|
ext.instance_available
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -4,24 +4,42 @@ require 'haml'
|
||||||
module Middleman
|
module Middleman
|
||||||
module Renderers
|
module Renderers
|
||||||
|
|
||||||
|
# Haml precompiles filters before the scope is even available,
|
||||||
|
# thus making it impossible to pass our Middleman instance
|
||||||
|
# in. So we have to resort to heavy hackery :(
|
||||||
|
class HamlTemplate < ::Tilt::HamlTemplate
|
||||||
|
def prepare
|
||||||
|
end
|
||||||
|
|
||||||
|
def evaluate(scope, locals, &block)
|
||||||
|
::Middleman::Renderers::Haml.last_haml_scope = scope
|
||||||
|
|
||||||
|
options = @options.merge(:filename => eval_file, :line => line)
|
||||||
|
@engine = ::Haml::Engine.new(data, options)
|
||||||
|
output = @engine.render(scope, locals, &block)
|
||||||
|
|
||||||
|
::Middleman::Renderers::Haml.last_haml_scope = nil
|
||||||
|
|
||||||
|
output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Haml Renderer
|
# Haml Renderer
|
||||||
module Haml
|
module Haml
|
||||||
|
mattr_accessor :last_haml_scope
|
||||||
|
|
||||||
# Setup extension
|
# Setup extension
|
||||||
class << self
|
class << self
|
||||||
# Once registered
|
# Once registered
|
||||||
def registered(app)
|
def registered(app)
|
||||||
|
::Tilt.prefer(::Middleman::Renderers::HamlTemplate, 'haml')
|
||||||
|
|
||||||
app.before_configuration do
|
app.before_configuration do
|
||||||
template_extensions :haml => :html
|
template_extensions :haml => :html
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add haml helpers to context
|
# Add haml helpers to context
|
||||||
app.send :include, ::Haml::Helpers
|
::Middleman::TemplateContext.send :include, ::Haml::Helpers
|
||||||
|
|
||||||
# Setup haml helper paths
|
|
||||||
app.ready do
|
|
||||||
init_haml_helpers
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
alias :included :registered
|
alias :included :registered
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,8 @@ module Middleman
|
||||||
class KramdownTemplate < ::Tilt::KramdownTemplate
|
class KramdownTemplate < ::Tilt::KramdownTemplate
|
||||||
def evaluate(scope, locals, &block)
|
def evaluate(scope, locals, &block)
|
||||||
@output ||= begin
|
@output ||= begin
|
||||||
|
MiddlemanKramdownHTML.scope = ::Middleman::Renderers::Haml.last_haml_scope || scope
|
||||||
|
|
||||||
output, warnings = MiddlemanKramdownHTML.convert(@engine.root, @engine.options)
|
output, warnings = MiddlemanKramdownHTML.convert(@engine.root, @engine.options)
|
||||||
@engine.warnings.concat(warnings)
|
@engine.warnings.concat(warnings)
|
||||||
output
|
output
|
||||||
|
@ -16,13 +18,13 @@ module Middleman
|
||||||
|
|
||||||
# Custom Kramdown renderer that uses our helpers for images and links
|
# Custom Kramdown renderer that uses our helpers for images and links
|
||||||
class MiddlemanKramdownHTML < ::Kramdown::Converter::Html
|
class MiddlemanKramdownHTML < ::Kramdown::Converter::Html
|
||||||
cattr_accessor :middleman_app
|
cattr_accessor :scope
|
||||||
|
|
||||||
def convert_img(el, indent)
|
def convert_img(el, indent)
|
||||||
attrs = el.attr.dup
|
attrs = el.attr.dup
|
||||||
|
|
||||||
link = attrs.delete('src')
|
link = attrs.delete('src')
|
||||||
middleman_app.image_tag(link, attrs)
|
scope.image_tag(link, attrs)
|
||||||
end
|
end
|
||||||
|
|
||||||
def convert_a(el, indent)
|
def convert_a(el, indent)
|
||||||
|
@ -37,7 +39,7 @@ module Middleman
|
||||||
|
|
||||||
attr = el.attr.dup
|
attr = el.attr.dup
|
||||||
link = attr.delete('href')
|
link = attr.delete('href')
|
||||||
middleman_app.link_to(content, link, attr)
|
scope.link_to(content, link, attr)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,11 +30,9 @@ module Middleman
|
||||||
if config[:markdown_engine] == :redcarpet
|
if config[:markdown_engine] == :redcarpet
|
||||||
require 'middleman-core/renderers/redcarpet'
|
require 'middleman-core/renderers/redcarpet'
|
||||||
::Tilt.prefer(::Middleman::Renderers::RedcarpetTemplate, *markdown_exts)
|
::Tilt.prefer(::Middleman::Renderers::RedcarpetTemplate, *markdown_exts)
|
||||||
MiddlemanRedcarpetHTML.middleman_app = self
|
|
||||||
elsif config[:markdown_engine] == :kramdown
|
elsif config[:markdown_engine] == :kramdown
|
||||||
require 'middleman-core/renderers/kramdown'
|
require 'middleman-core/renderers/kramdown'
|
||||||
::Tilt.prefer(::Middleman::Renderers::KramdownTemplate, *markdown_exts)
|
::Tilt.prefer(::Middleman::Renderers::KramdownTemplate, *markdown_exts)
|
||||||
MiddlemanKramdownHTML.middleman_app = self
|
|
||||||
elsif !config[:markdown_engine].nil?
|
elsif !config[:markdown_engine].nil?
|
||||||
# Map symbols to classes
|
# Map symbols to classes
|
||||||
markdown_engine_klass = if config[:markdown_engine].is_a? Symbol
|
markdown_engine_klass = if config[:markdown_engine].is_a? Symbol
|
||||||
|
|
|
@ -40,6 +40,14 @@ module Middleman
|
||||||
renderer.new(render_options)
|
renderer.new(render_options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def evaluate(scope, locals, &block)
|
||||||
|
@output ||= begin
|
||||||
|
MiddlemanRedcarpetHTML.scope = ::Middleman::Renderers::Haml.last_haml_scope || scope
|
||||||
|
|
||||||
|
@engine.render(data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def covert_options_to_aliases!
|
def covert_options_to_aliases!
|
||||||
|
@ -51,7 +59,7 @@ module Middleman
|
||||||
|
|
||||||
# Custom Redcarpet renderer that uses our helpers for images and links
|
# Custom Redcarpet renderer that uses our helpers for images and links
|
||||||
class MiddlemanRedcarpetHTML < ::Redcarpet::Render::HTML
|
class MiddlemanRedcarpetHTML < ::Redcarpet::Render::HTML
|
||||||
cattr_accessor :middleman_app
|
cattr_accessor :scope
|
||||||
|
|
||||||
def initialize(options={})
|
def initialize(options={})
|
||||||
@local_options = options.dup
|
@local_options = options.dup
|
||||||
|
@ -61,7 +69,7 @@ module Middleman
|
||||||
|
|
||||||
def image(link, title, alt_text)
|
def image(link, title, alt_text)
|
||||||
if !@local_options[:no_images]
|
if !@local_options[:no_images]
|
||||||
middleman_app.image_tag(link, :title => title, :alt => alt_text)
|
scope.image_tag(link, :title => title, :alt => alt_text)
|
||||||
else
|
else
|
||||||
link_string = link.dup
|
link_string = link.dup
|
||||||
link_string << %Q{"#{title}"} if title && title.length > 0 && title != alt_text
|
link_string << %Q{"#{title}"} if title && title.length > 0 && title != alt_text
|
||||||
|
@ -74,7 +82,7 @@ module Middleman
|
||||||
attributes = { :title => title }
|
attributes = { :title => title }
|
||||||
attributes.merge!( @local_options[:link_attributes] ) if @local_options[:link_attributes]
|
attributes.merge!( @local_options[:link_attributes] ) if @local_options[:link_attributes]
|
||||||
|
|
||||||
middleman_app.link_to(content, link, attributes )
|
scope.link_to(content, link, attributes )
|
||||||
else
|
else
|
||||||
link_string = link.dup
|
link_string = link.dup
|
||||||
link_string << %Q{"#{title}"} if title && title.length > 0 && title != alt_text
|
link_string << %Q{"#{title}"} if title && title.length > 0 && title != alt_text
|
||||||
|
|
|
@ -74,7 +74,7 @@ module Middleman
|
||||||
def sass_options
|
def sass_options
|
||||||
more_opts = { :filename => eval_file, :line => line, :syntax => syntax }
|
more_opts = { :filename => eval_file, :line => line, :syntax => syntax }
|
||||||
|
|
||||||
if @context.is_a?(::Middleman::Application) && file
|
if @context.is_a?(::Middleman::TemplateContext) && file
|
||||||
location_of_sass_file = @context.source_dir
|
location_of_sass_file = @context.source_dir
|
||||||
|
|
||||||
parts = basename.split('.')
|
parts = basename.split('.')
|
||||||
|
|
|
@ -31,7 +31,7 @@ module Middleman
|
||||||
}, 'Callbacks that can exclude paths from the sitemap'
|
}, 'Callbacks that can exclude paths from the sitemap'
|
||||||
|
|
||||||
# Include instance methods
|
# Include instance methods
|
||||||
app.send :include, InstanceMethods
|
::Middleman::TemplateContext.send :include, InstanceMethods
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
105
middleman-core/lib/middleman-core/template_context.rb
Normal file
105
middleman-core/lib/middleman-core/template_context.rb
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
module Middleman
|
||||||
|
class TemplateContext
|
||||||
|
attr_reader :app
|
||||||
|
attr_accessor :current_engine, :current_path
|
||||||
|
|
||||||
|
delegate :config, :logger, :sitemap, :build?, :development?, :data, :extensions, :source_dir, :root, :to => :app
|
||||||
|
|
||||||
|
def initialize(app, locs={}, opts={})
|
||||||
|
@app = app
|
||||||
|
@current_locs = locs
|
||||||
|
@current_opts = opts
|
||||||
|
end
|
||||||
|
|
||||||
|
def save_buffer
|
||||||
|
@_out_buf, _buf_was = '', @_out_buf
|
||||||
|
_buf_was
|
||||||
|
end
|
||||||
|
|
||||||
|
def restore_buffer(_buf_was)
|
||||||
|
@_out_buf = _buf_was
|
||||||
|
end
|
||||||
|
|
||||||
|
# Allow layouts to be wrapped in the contents of other layouts
|
||||||
|
# @param [String, Symbol] layout_name
|
||||||
|
# @return [void]
|
||||||
|
def wrap_layout(layout_name, &block)
|
||||||
|
# Save current buffer for later
|
||||||
|
_buf_was = save_buffer
|
||||||
|
|
||||||
|
layout_path = @app.locate_layout(layout_name, self.current_engine)
|
||||||
|
|
||||||
|
extension = File.extname(layout_path)
|
||||||
|
engine = extension[1..-1].to_sym
|
||||||
|
|
||||||
|
# Store last engine for later (could be inside nested renders)
|
||||||
|
self.current_engine, engine_was = engine, self.current_engine
|
||||||
|
|
||||||
|
begin
|
||||||
|
content = if block_given?
|
||||||
|
capture_html(&block)
|
||||||
|
else
|
||||||
|
''
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
# Reset stored buffer
|
||||||
|
restore_buffer(_buf_was)
|
||||||
|
end
|
||||||
|
|
||||||
|
concat_safe_content @app.render_individual_file(layout_path, @current_locs || {}, @current_opts || {}, self) { content }
|
||||||
|
ensure
|
||||||
|
self.current_engine = engine_was
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sinatra/Padrino compatible render method signature referenced by some view
|
||||||
|
# helpers. Especially partials.
|
||||||
|
#
|
||||||
|
# @param [String, Symbol] engine
|
||||||
|
# @param [String, Symbol] data
|
||||||
|
# @param [Hash] options
|
||||||
|
# @return [String]
|
||||||
|
def render(engine, data, options={}, &block)
|
||||||
|
data = data.to_s
|
||||||
|
|
||||||
|
locals = options[:locals]
|
||||||
|
|
||||||
|
found_partial = false
|
||||||
|
engine = nil
|
||||||
|
|
||||||
|
# If the path is known to the sitemap
|
||||||
|
if resource = sitemap.find_resource_by_path(current_path)
|
||||||
|
current_dir = File.dirname(resource.source_file)
|
||||||
|
engine = File.extname(resource.source_file)[1..-1].to_sym
|
||||||
|
|
||||||
|
# Look for partials relative to the current path
|
||||||
|
relative_dir = File.join(current_dir.sub(%r{^#{Regexp.escape(self.source_dir)}/?}, ''), data)
|
||||||
|
|
||||||
|
# Try to use the current engine first
|
||||||
|
found_partial, found_engine = @app.resolve_template(relative_dir, :preferred_engine => engine, :try_without_underscore => true)
|
||||||
|
|
||||||
|
# Fall back to any engine available
|
||||||
|
if !found_partial
|
||||||
|
found_partial, found_engine = @app.resolve_template(relative_dir, :try_without_underscore => true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Look in the partials_dir for the partial with the current engine
|
||||||
|
partials_path = File.join(config[:partials_dir], data)
|
||||||
|
if !found_partial && !engine.nil?
|
||||||
|
found_partial, found_engine = @app.resolve_template(partials_path, :preferred_engine => engine, :try_without_underscore => true)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Look in the root with any engine
|
||||||
|
if !found_partial
|
||||||
|
found_partial, found_engine = @app.resolve_template(partials_path, :try_without_underscore => true)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Render the partial if found, otherwide throw exception
|
||||||
|
if found_partial
|
||||||
|
@app.render_individual_file(found_partial, locals, options, self, &block)
|
||||||
|
else
|
||||||
|
raise ::Middleman::CoreExtensions::Rendering::TemplateNotFound, "Could not locate partial: #{data}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -21,15 +21,15 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
|
||||||
|
|
||||||
require 'active_support/core_ext/object/to_query'
|
require 'active_support/core_ext/object/to_query'
|
||||||
|
|
||||||
app.helpers ::Padrino::Helpers::OutputHelpers
|
::Middleman::TemplateContext.send :include, ::Padrino::Helpers::OutputHelpers
|
||||||
app.helpers ::Padrino::Helpers::TagHelpers
|
::Middleman::TemplateContext.send :include, ::Padrino::Helpers::TagHelpers
|
||||||
app.helpers ::Padrino::Helpers::AssetTagHelpers
|
::Middleman::TemplateContext.send :include, ::Padrino::Helpers::AssetTagHelpers
|
||||||
app.helpers ::Padrino::Helpers::FormHelpers
|
::Middleman::TemplateContext.send :include, ::Padrino::Helpers::FormHelpers
|
||||||
app.helpers ::Padrino::Helpers::FormatHelpers
|
::Middleman::TemplateContext.send :include, ::Padrino::Helpers::FormatHelpers
|
||||||
app.helpers ::Padrino::Helpers::RenderHelpers
|
::Middleman::TemplateContext.send :include, ::Padrino::Helpers::RenderHelpers
|
||||||
app.helpers ::Padrino::Helpers::NumberHelpers
|
::Middleman::TemplateContext.send :include, ::Padrino::Helpers::NumberHelpers
|
||||||
# app.helpers ::Padrino::Helpers::TranslationHelpers
|
# ::Middleman::TemplateContext.send :include, ::Padrino::Helpers::TranslationHelpers
|
||||||
app.helpers ::Padrino::Helpers::Breadcrumbs
|
::Middleman::TemplateContext.send :include, ::Padrino::Helpers::Breadcrumbs
|
||||||
|
|
||||||
app.config.define_setting :relative_links, false, 'Whether to generate relative links instead of absolute ones'
|
app.config.define_setting :relative_links, false, 'Whether to generate relative links instead of absolute ones'
|
||||||
end
|
end
|
||||||
|
@ -39,6 +39,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
|
||||||
|
|
||||||
# Make all block content html_safe
|
# Make all block content html_safe
|
||||||
def content_tag(name, content = nil, options = nil, &block)
|
def content_tag(name, content = nil, options = nil, &block)
|
||||||
|
# safe_content_tag(name, content, options, &block)
|
||||||
if block_given?
|
if block_given?
|
||||||
options = content if content.is_a?(Hash)
|
options = content if content.is_a?(Hash)
|
||||||
content = capture_html(&block)
|
content = capture_html(&block)
|
||||||
|
@ -61,17 +62,23 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
|
||||||
def capture_html(*args, &block)
|
def capture_html(*args, &block)
|
||||||
handler = auto_find_proper_handler(&block)
|
handler = auto_find_proper_handler(&block)
|
||||||
captured_block, captured_html = nil, ''
|
captured_block, captured_html = nil, ''
|
||||||
if handler && handler.is_type? && handler.block_is_type?(block)
|
|
||||||
|
if handler && handler.block_is_type?(block)
|
||||||
captured_html, captured_block = handler.capture_from_template(*args, &block)
|
captured_html, captured_block = handler.capture_from_template(*args, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
# invoking the block directly if there was no template
|
# invoking the block directly if there was no template
|
||||||
captured_html = block_given? && ( captured_block || block.call(*args) ) if captured_html.blank?
|
captured_html = block_given? && ( captured_block || block.call(*args) ) if captured_html.blank?
|
||||||
captured_html
|
captured_html
|
||||||
end
|
end
|
||||||
|
|
||||||
def auto_find_proper_handler(&block)
|
def auto_find_proper_handler(&block)
|
||||||
engine = block_given? ? File.extname(block.source_location[0])[1..-1].to_sym : current_engine
|
if block_given?
|
||||||
::Padrino::Helpers::OutputHelpers.handlers.map { |h| h.new(self) }.find { |h| h.engines.include?(engine) && h.is_type? }
|
engine = File.extname(block.source_location[0])[1..-1].to_sym
|
||||||
|
::Padrino::Helpers::OutputHelpers.handlers.map { |h| h.new(self) }.find { |h| h.engines.include?(engine) && h.block_is_type?(block) }
|
||||||
|
else
|
||||||
|
find_proper_handler
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Disable Padrino cache buster
|
# Disable Padrino cache buster
|
||||||
|
@ -197,7 +204,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
|
||||||
options_with_resource = options.dup
|
options_with_resource = options.dup
|
||||||
options_with_resource[:current_resource] ||= current_resource
|
options_with_resource[:current_resource] ||= current_resource
|
||||||
|
|
||||||
::Middleman::Util.url_for(self, path_or_resource, options_with_resource)
|
::Middleman::Util.url_for(app, path_or_resource, options_with_resource)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Overload the regular link_to to be sitemap-aware - if you
|
# Overload the regular link_to to be sitemap-aware - if you
|
||||||
|
|
|
@ -12,6 +12,10 @@
|
||||||
class Middleman::Extensions::Gzip < ::Middleman::Extension
|
class Middleman::Extensions::Gzip < ::Middleman::Extension
|
||||||
option :exts, %w(.js .css .html .htm), 'File extensions to Gzip when building.'
|
option :exts, %w(.js .css .html .htm), 'File extensions to Gzip when building.'
|
||||||
|
|
||||||
|
class NumberHelpers
|
||||||
|
include ::Padrino::Helpers::NumberHelpers
|
||||||
|
end
|
||||||
|
|
||||||
def initialize(app, options_hash={})
|
def initialize(app, options_hash={})
|
||||||
super
|
super
|
||||||
|
|
||||||
|
@ -57,11 +61,11 @@ class Middleman::Extensions::Gzip < ::Middleman::Extension
|
||||||
if output_filename
|
if output_filename
|
||||||
total_savings += (old_size - new_size)
|
total_savings += (old_size - new_size)
|
||||||
size_change_word = (old_size - new_size) > 0 ? 'smaller' : 'larger'
|
size_change_word = (old_size - new_size) > 0 ? 'smaller' : 'larger'
|
||||||
builder.say_status :gzip, "#{output_filename} (#{app.number_to_human_size((old_size - new_size).abs)} #{size_change_word})"
|
builder.say_status :gzip, "#{output_filename} (#{NumberHelpers.new.number_to_human_size((old_size - new_size).abs)} #{size_change_word})"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
builder.say_status :gzip, "Total gzip savings: #{app.number_to_human_size(total_savings)}", :blue
|
builder.say_status :gzip, "Total gzip savings: #{NumberHelpers.new.number_to_human_size(total_savings)}", :blue
|
||||||
I18n.locale = old_locale
|
I18n.locale = old_locale
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue