Merge pull request #2870 from gjtorikian/support-relative-include
This commit is contained in:
commit
6644c77d23
|
@ -18,8 +18,6 @@ module Jekyll
|
||||||
VALID_SYNTAX = /([\w-]+)\s*=\s*(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+))/
|
VALID_SYNTAX = /([\w-]+)\s*=\s*(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+))/
|
||||||
VARIABLE_SYNTAX = /(?<variable>[^{]*\{\{\s*(?<name>[\w\-\.]+)\s*(\|.*)?\}\}[^\s}]*)(?<params>.*)/
|
VARIABLE_SYNTAX = /(?<variable>[^{]*\{\{\s*(?<name>[\w\-\.]+)\s*(\|.*)?\}\}[^\s}]*)(?<params>.*)/
|
||||||
|
|
||||||
INCLUDES_DIR = '_includes'
|
|
||||||
|
|
||||||
def initialize(tag_name, markup, tokens)
|
def initialize(tag_name, markup, tokens)
|
||||||
super
|
super
|
||||||
matched = markup.strip.match(VARIABLE_SYNTAX)
|
matched = markup.strip.match(VARIABLE_SYNTAX)
|
||||||
|
@ -96,8 +94,12 @@ eos
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def includes_dir
|
||||||
|
'_includes'
|
||||||
|
end
|
||||||
|
|
||||||
def render(context)
|
def render(context)
|
||||||
dir = File.join(File.realpath(context.registers[:site].source), INCLUDES_DIR)
|
dir = resolved_includes_dir(context)
|
||||||
|
|
||||||
file = render_variable(context) || @file
|
file = render_variable(context) || @file
|
||||||
validate_file_name(file)
|
validate_file_name(file)
|
||||||
|
@ -113,10 +115,14 @@ eos
|
||||||
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, File.join(includes_dir, @file)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def resolved_includes_dir(context)
|
||||||
|
File.join(File.realpath(context.registers[:site].source), includes_dir)
|
||||||
|
end
|
||||||
|
|
||||||
def validate_path(path, dir, safe)
|
def validate_path(path, dir, safe)
|
||||||
if safe && !realpath_prefixed_with?(path, dir)
|
if safe && !realpath_prefixed_with?(path, dir)
|
||||||
raise IOError.new "The included file '#{path}' should exist and should not be a symlink"
|
raise IOError.new "The included file '#{path}' should exist and should not be a symlink"
|
||||||
|
@ -126,7 +132,7 @@ eos
|
||||||
end
|
end
|
||||||
|
|
||||||
def path_relative_to_source(dir, path)
|
def path_relative_to_source(dir, path)
|
||||||
File.join(INCLUDES_DIR, path.sub(Regexp.new("^#{dir}"), ""))
|
File.join(includes_dir, path.sub(Regexp.new("^#{dir}"), ""))
|
||||||
end
|
end
|
||||||
|
|
||||||
def realpath_prefixed_with?(path, dir)
|
def realpath_prefixed_with?(path, dir)
|
||||||
|
@ -138,7 +144,19 @@ eos
|
||||||
File.read(file, file_read_opts(context))
|
File.read(file, file_read_opts(context))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class IncludeRelativeTag < IncludeTag
|
||||||
|
def includes_dir
|
||||||
|
'.'
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolved_includes_dir(context)
|
||||||
|
page_path = context.registers[:page].nil? ? includes_dir : File.dirname(context.registers[:page]["path"])
|
||||||
|
Jekyll.sanitized_path(context.registers[:site].source, page_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Liquid::Template.register_tag('include', Jekyll::Tags::IncludeTag)
|
Liquid::Template.register_tag('include', Jekyll::Tags::IncludeTag)
|
||||||
|
Liquid::Template.register_tag('include_relative', Jekyll::Tags::IncludeRelativeTag)
|
||||||
|
|
|
@ -292,6 +292,17 @@ These parameters are available via Liquid in the include:
|
||||||
{% raw %}{{ include.param }}{% endraw %}
|
{% raw %}{{ include.param }}{% endraw %}
|
||||||
{% endhighlight %}
|
{% endhighlight %}
|
||||||
|
|
||||||
|
### Including files relative to another file
|
||||||
|
|
||||||
|
You can also choose to include files relative to the current file:
|
||||||
|
|
||||||
|
{% highlight ruby %}
|
||||||
|
{% raw %}{% include_relative somedir/footer.html %}{% endraw %}
|
||||||
|
{% endhighlight %}
|
||||||
|
|
||||||
|
You won't need to place your included content within the `_includes` directory.
|
||||||
|
All the other capaibilities of the `include` tag are available to the `include_relative` tag.
|
||||||
|
|
||||||
### Code snippet highlighting
|
### Code snippet highlighting
|
||||||
|
|
||||||
Jekyll has built in support for syntax highlighting of [over 100
|
Jekyll has built in support for syntax highlighting of [over 100
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
title: Post
|
||||||
|
layout: post
|
||||||
|
include1: rel_include.html
|
||||||
|
include2: include_relative/rel_include
|
||||||
|
include3: rel_INCLUDE
|
||||||
|
include4: params
|
||||||
|
include5: clude
|
||||||
|
---
|
||||||
|
|
||||||
|
Liquid tests
|
||||||
|
- 1 {% include_relative include_relative/{{ page.include1 }} %}
|
||||||
|
- 2 {% include_relative {{ page.include2 | append: '.html' }} %}
|
||||||
|
- 3 {% include_relative include_relative/{{ page.include3 | downcase | append: '.html' }} %}
|
||||||
|
|
||||||
|
Whitespace tests
|
||||||
|
- 4 {% include_relative include_relative/{{page.include1}} %}
|
||||||
|
- 5 {% include_relative include_relative/{{ page.include1}} %}
|
||||||
|
- 6 {% include_relative include_relative/{{ page.include3 | downcase | append: '.html'}} %}
|
||||||
|
|
||||||
|
Parameters test
|
||||||
|
- 7 {% include_relative include_relative/{{ page.include4 | append: '.html' }} var1='foo' var2='bar' %}
|
||||||
|
|
||||||
|
Partial variable test
|
||||||
|
- 8 {% include_relative include_relative/rel_in{{ page.include5 }}.html %}
|
||||||
|
|
||||||
|
Relative to self test:
|
||||||
|
|
||||||
|
- 9 {% include_relative 2014-03-03-yaml-with-dots.md %}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<span id='include-param'>{{include.param}}</span>
|
||||||
|
|
||||||
|
<ul id='param-list'>
|
||||||
|
{% for param in include %}
|
||||||
|
<li>{{param[0]}} = {{param[1]}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
|
@ -0,0 +1 @@
|
||||||
|
relative_included
|
|
@ -14,7 +14,7 @@ class TestGeneratedSite < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
should "ensure post count is as expected" do
|
should "ensure post count is as expected" do
|
||||||
assert_equal 42, @site.posts.size
|
assert_equal 43, @site.posts.size
|
||||||
end
|
end
|
||||||
|
|
||||||
should "insert site.posts into the index" do
|
should "insert site.posts into the index" do
|
||||||
|
|
|
@ -48,7 +48,7 @@ class TestSite < Test::Unit::TestCase
|
||||||
Jekyll::Configuration::DEFAULTS.merge({'source' => source_dir, 'destination' => dest_dir})
|
Jekyll::Configuration::DEFAULTS.merge({'source' => source_dir, 'destination' => dest_dir})
|
||||||
end
|
end
|
||||||
@site = Site.new(Jekyll.configuration)
|
@site = Site.new(Jekyll.configuration)
|
||||||
@num_invalid_posts = 2
|
@num_invalid_posts = 4
|
||||||
end
|
end
|
||||||
|
|
||||||
should "have an empty tag hash by default" do
|
should "have an empty tag hash by default" do
|
||||||
|
|
|
@ -510,4 +510,98 @@ CONTENT
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "relative include tag with variable and liquid filters" do
|
||||||
|
setup do
|
||||||
|
stub(Jekyll).configuration do
|
||||||
|
site_configuration({'pygments' => true})
|
||||||
|
end
|
||||||
|
|
||||||
|
site = Site.new(Jekyll.configuration)
|
||||||
|
post = Post.new(site, source_dir, '', "2014-09-02-relative-includes.markdown")
|
||||||
|
layouts = { "default" => Layout.new(site, source_dir('_layouts'), "simple.html")}
|
||||||
|
post.render(layouts, {"site" => {"posts" => []}})
|
||||||
|
@content = post.content
|
||||||
|
end
|
||||||
|
|
||||||
|
should "include file as variable with liquid filters" do
|
||||||
|
assert_match %r{1 relative_include}, @content
|
||||||
|
assert_match %r{2 relative_include}, @content
|
||||||
|
assert_match %r{3 relative_include}, @content
|
||||||
|
end
|
||||||
|
|
||||||
|
should "include file as variable and liquid filters with arbitrary whitespace" do
|
||||||
|
assert_match %r{4 relative_include}, @content
|
||||||
|
assert_match %r{5 relative_include}, @content
|
||||||
|
assert_match %r{6 relative_include}, @content
|
||||||
|
end
|
||||||
|
|
||||||
|
should "include file as variable and filters with additional parameters" do
|
||||||
|
assert_match '<li>var1 = foo</li>', @content
|
||||||
|
assert_match '<li>var2 = bar</li>', @content
|
||||||
|
end
|
||||||
|
|
||||||
|
should "include file as partial variable" do
|
||||||
|
assert_match %r{8 relative_include}, @content
|
||||||
|
end
|
||||||
|
|
||||||
|
should "include files relative to self" do
|
||||||
|
assert_match %r{9 —\ntitle: Test Post Where YAML}, @content
|
||||||
|
end
|
||||||
|
|
||||||
|
context "trying to do bad stuff" do
|
||||||
|
context "include missing file" do
|
||||||
|
setup do
|
||||||
|
@content = <<CONTENT
|
||||||
|
---
|
||||||
|
title: missing file
|
||||||
|
---
|
||||||
|
|
||||||
|
{% include_relative missing.html %}
|
||||||
|
CONTENT
|
||||||
|
end
|
||||||
|
|
||||||
|
should "raise error relative to source directory" do
|
||||||
|
exception = assert_raise IOError do
|
||||||
|
create_post(@content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
|
||||||
|
end
|
||||||
|
assert_equal 'Included file \'./missing.html\' not found', exception.message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with symlink'd include" do
|
||||||
|
|
||||||
|
should "not allow symlink includes" do
|
||||||
|
File.open("/tmp/pages-test", 'w') { |file| file.write("SYMLINK TEST") }
|
||||||
|
assert_raise IOError do
|
||||||
|
content = <<CONTENT
|
||||||
|
---
|
||||||
|
title: Include symlink
|
||||||
|
---
|
||||||
|
|
||||||
|
{% include_relative tmp/pages-test %}
|
||||||
|
|
||||||
|
CONTENT
|
||||||
|
create_post(content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true, 'safe' => true })
|
||||||
|
end
|
||||||
|
assert_no_match /SYMLINK TEST/, @result
|
||||||
|
end
|
||||||
|
|
||||||
|
should "not expose the existence of symlinked files" do
|
||||||
|
ex = assert_raise IOError do
|
||||||
|
content = <<CONTENT
|
||||||
|
---
|
||||||
|
title: Include symlink
|
||||||
|
---
|
||||||
|
|
||||||
|
{% include_relative tmp/pages-test-does-not-exist %}
|
||||||
|
|
||||||
|
CONTENT
|
||||||
|
create_post(content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true, 'safe' => true })
|
||||||
|
end
|
||||||
|
assert_match /should exist and should not be a symlink/, ex.message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue