IncludeTag: implement multiple load paths
This commit is contained in:
parent
0920d2b48a
commit
33255e3ac3
|
@ -11,7 +11,7 @@ module Jekyll
|
||||||
:gems, :plugin_manager, :theme
|
:gems, :plugin_manager, :theme
|
||||||
|
|
||||||
attr_accessor :converters, :generators, :reader
|
attr_accessor :converters, :generators, :reader
|
||||||
attr_reader :regenerator, :liquid_renderer
|
attr_reader :regenerator, :liquid_renderer, :includes_load_paths
|
||||||
|
|
||||||
# Public: Initialize a new Site.
|
# Public: Initialize a new Site.
|
||||||
#
|
#
|
||||||
|
@ -52,8 +52,12 @@ module Jekyll
|
||||||
self.plugin_manager = Jekyll::PluginManager.new(self)
|
self.plugin_manager = Jekyll::PluginManager.new(self)
|
||||||
self.plugins = plugin_manager.plugins_path
|
self.plugins = plugin_manager.plugins_path
|
||||||
|
|
||||||
|
self.theme = nil
|
||||||
self.theme = Jekyll::Theme.new(config["theme"]) if config["theme"]
|
self.theme = Jekyll::Theme.new(config["theme"]) if config["theme"]
|
||||||
|
|
||||||
|
@includes_load_paths = Array(in_source_dir(config["includes_dir"].to_s))
|
||||||
|
@includes_load_paths << theme.includes_path if self.theme
|
||||||
|
|
||||||
self.file_read_opts = {}
|
self.file_read_opts = {}
|
||||||
self.file_read_opts[:encoding] = config['encoding'] if config['encoding']
|
self.file_read_opts[:encoding] = config['encoding'] if config['encoding']
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,6 @@ module Jekyll
|
||||||
end
|
end
|
||||||
|
|
||||||
class IncludeTag < Liquid::Tag
|
class IncludeTag < Liquid::Tag
|
||||||
attr_reader :includes_dir
|
|
||||||
|
|
||||||
VALID_SYNTAX = /([\w-]+)\s*=\s*(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+))/
|
VALID_SYNTAX = /([\w-]+)\s*=\s*(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+))/
|
||||||
VARIABLE_SYNTAX = /(?<variable>[^{]*(\{\{\s*[\w\-\.]+\s*(\|.*)?\}\}[^\s{}]*)+)(?<params>.*)/
|
VARIABLE_SYNTAX = /(?<variable>[^{]*(\{\{\s*[\w\-\.]+\s*(\|.*)?\}\}[^\s{}]*)+)(?<params>.*)/
|
||||||
|
|
||||||
|
@ -98,20 +96,29 @@ eos
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def tag_includes_dir(context)
|
def tag_includes_dirs(context)
|
||||||
context.registers[:site].config['includes_dir'].freeze
|
context.registers[:site].includes_load_paths.freeze
|
||||||
|
end
|
||||||
|
|
||||||
|
def locate_include_file(context, file, safe)
|
||||||
|
includes_dirs = tag_includes_dirs(context)
|
||||||
|
includes_dirs.each do |dir|
|
||||||
|
path = File.join(dir, file)
|
||||||
|
return path if valid_include_file?(path, dir, safe)
|
||||||
|
end
|
||||||
|
raise IOError, "Could not locate the included file '#{file}' in any of #{includes_dirs}." \
|
||||||
|
" Ensure it exists in one of those directories and, if it is a symlink, " \
|
||||||
|
"does not point outside your site source."
|
||||||
end
|
end
|
||||||
|
|
||||||
def render(context)
|
def render(context)
|
||||||
site = context.registers[:site]
|
site = context.registers[:site]
|
||||||
@includes_dir = tag_includes_dir(context)
|
|
||||||
dir = resolved_includes_dir(context)
|
|
||||||
|
|
||||||
file = render_variable(context) || @file
|
file = render_variable(context) || @file
|
||||||
validate_file_name(file)
|
validate_file_name(file)
|
||||||
|
|
||||||
path = File.join(dir, file)
|
path = locate_include_file(context, file, site.safe)
|
||||||
validate_path(path, dir, site.safe)
|
return unless path
|
||||||
|
|
||||||
# Add include to dependency tree
|
# Add include to dependency tree
|
||||||
if context.registers[:page] && context.registers[:page].key?("path")
|
if context.registers[:page] && context.registers[:page].key?("path")
|
||||||
|
@ -121,16 +128,16 @@ eos
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
#begin
|
||||||
partial = load_cached_partial(path, context)
|
partial = load_cached_partial(path, context)
|
||||||
|
|
||||||
context.stack do
|
context.stack do
|
||||||
context['include'] = parse_params(context) if @params
|
context['include'] = parse_params(context) if @params
|
||||||
partial.render!(context)
|
partial.render!(context)
|
||||||
end
|
end
|
||||||
rescue => e
|
#rescue => e
|
||||||
raise IncludeTagError.new e.message, File.join(@includes_dir, @file)
|
#raise IncludeTagError.new e.message, path
|
||||||
end
|
#end
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_cached_partial(path, context)
|
def load_cached_partial(path, context)
|
||||||
|
@ -144,24 +151,18 @@ eos
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def resolved_includes_dir(context)
|
def valid_include_file?(path, dir, safe)
|
||||||
context.registers[:site].in_source_dir(@includes_dir)
|
!(outside_site_source?(path, dir, safe) || !File.exist?(path))
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_path(path, dir, safe)
|
def outside_site_source?(path, dir, safe)
|
||||||
if safe && !realpath_prefixed_with?(path, dir)
|
safe && !realpath_prefixed_with?(path, dir)
|
||||||
raise IOError.new "The included file '#{path}' should exist and should not be a symlink"
|
|
||||||
elsif !File.exist?(path)
|
|
||||||
raise IOError.new "Included file '#{path_relative_to_source(dir, path)}' not found"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def path_relative_to_source(dir, path)
|
|
||||||
File.join(@includes_dir, path.sub(Regexp.new("^#{dir}"), ""))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def realpath_prefixed_with?(path, dir)
|
def realpath_prefixed_with?(path, dir)
|
||||||
File.exist?(path) && File.realpath(path).start_with?(dir)
|
File.exist?(path) && File.realpath(path).start_with?(dir)
|
||||||
|
rescue
|
||||||
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
# This method allows to modify the file content by inheriting from the class.
|
# This method allows to modify the file content by inheriting from the class.
|
||||||
|
@ -171,16 +172,17 @@ eos
|
||||||
end
|
end
|
||||||
|
|
||||||
class IncludeRelativeTag < IncludeTag
|
class IncludeRelativeTag < IncludeTag
|
||||||
def tag_includes_dir(context)
|
def tag_includes_dirs(context)
|
||||||
'.'.freeze
|
Array(page_path(context)).freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
def page_path(context)
|
def page_path(context)
|
||||||
context.registers[:page].nil? ? includes_dir : File.dirname(context.registers[:page]["path"])
|
if context.registers[:page].nil?
|
||||||
end
|
context.registers[:site].source
|
||||||
|
else
|
||||||
def resolved_includes_dir(context)
|
current_doc_dir = File.dirname(context.registers[:page]["path"])
|
||||||
context.registers[:site].in_source_dir(page_path(context))
|
context.registers[:site].in_source_dir current_doc_dir
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -615,7 +615,7 @@ title: Include symlink
|
||||||
CONTENT
|
CONTENT
|
||||||
create_post(content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true, 'safe' => true })
|
create_post(content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true, 'safe' => true })
|
||||||
end
|
end
|
||||||
assert_match /should exist and should not be a symlink/, ex.message
|
assert_match "Could not locate the included file 'tmp/pages-test-does-not-exist' in any of [\"/Users/parkr/jekyll/jekyll/test/source/_includes\"].", ex.message
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -756,7 +756,7 @@ CONTENT
|
||||||
exception = assert_raises IOError do
|
exception = assert_raises IOError do
|
||||||
create_post(@content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
|
create_post(@content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
|
||||||
end
|
end
|
||||||
assert_equal 'Included file \'_includes/missing.html\' not found', exception.message
|
assert_match "Could not locate the included file 'missing.html' in any of [\"#{source_dir}/_includes\"].", exception.message
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -838,7 +838,7 @@ CONTENT
|
||||||
exception = assert_raises IOError do
|
exception = assert_raises IOError do
|
||||||
create_post(@content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
|
create_post(@content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
|
||||||
end
|
end
|
||||||
assert_equal 'Included file \'./missing.html\' not found', exception.message
|
assert_match "Could not locate the included file 'missing.html' in any of [\"#{source_dir}\"].", exception.message
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -892,7 +892,7 @@ title: Include symlink
|
||||||
CONTENT
|
CONTENT
|
||||||
create_post(content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true, 'safe' => true })
|
create_post(content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true, 'safe' => true })
|
||||||
end
|
end
|
||||||
assert_match /should exist and should not be a symlink/, ex.message
|
assert_match /Ensure it exists in one of those directories and, if it is a symlink, does not point outside your site source./, ex.message
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue