Fix Post#url escape

Post#url was escaped using CGI.escape.
When file name contains a space character, its url points to
non-existing URL.

For example, when we have a post named '2014-01-02-foo bar.md',
we expect its url to be '/2014/01/02/foo%20bar.html',
but it was actually '/2014/01/02/foo+bar.html'.

We now define Jekyll::URL.escape_path and Jekyll::URL.unescape_path,
and use them to escape and unescape Post#url
This commit is contained in:
nitoyon 2013-11-06 00:29:27 +09:00
parent 93700f91e7
commit eebb6414bf
5 changed files with 78 additions and 5 deletions

View File

@ -208,10 +208,10 @@ module Jekyll
:year => date.strftime("%Y"),
:month => date.strftime("%m"),
:day => date.strftime("%d"),
:title => CGI.escape(slug),
:title => URL.escape_path(slug),
:i_day => date.strftime("%d").to_i.to_s,
:i_month => date.strftime("%m").to_i.to_s,
:categories => (categories || []).map { |c| URI.escape(c.to_s) }.join('/'),
:categories => (categories || []).map { |c| URL.escape_path(c.to_s) }.join('/'),
:short_month => date.strftime("%b"),
:y_day => date.strftime("%j"),
:output_ext => output_ext
@ -260,7 +260,7 @@ module Jekyll
# Returns destination file path String.
def destination(dest)
# The url needs to be unescaped in order to preserve the correct filename
path = Jekyll.sanitized_path(dest, CGI.unescape(url))
path = Jekyll.sanitized_path(dest, URL.unescape_path(url))
path = File.join(path, "index.html") if path[/\.html$/].nil?
path
end

View File

@ -1,3 +1,5 @@
require 'uri'
# Public: Methods that generate a URL for a resource such as a Post or a Page.
#
# Examples
@ -65,5 +67,43 @@ module Jekyll
url
end
# Escapes a path to be a valid URL path segment
#
# path - The path to be escaped.
#
# Examples:
#
# URL.escape_path("/a b")
# # => "/a%20b"
#
# Returns the escaped path.
def self.escape_path(path)
# Because URI.escape doesn't escape '?', '[' and ']' by defaut,
# specify unsafe string (except unreserved, sub-delims, ":", "@" and "/").
#
# URI path segment is defined in RFC 3986 as follows:
# segment = *pchar
# pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
# unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
# pct-encoded = "%" HEXDIG HEXDIG
# sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
# / "*" / "+" / "," / ";" / "="
URI.escape(path, /[^a-zA-Z\d\-._~!$&\'()*+,;=:@\/]/)
end
# Unescapes a URL path segment
#
# path - The path to be unescaped.
#
# Examples:
#
# URL.unescape_path("/a%20b")
# # => "/a b"
#
# Returns the unescaped path.
def self.unescape_path(path)
URI.unescape(path)
end
end
end

View File

@ -0,0 +1,6 @@
---
layout: default
title: Plus space percent
---
Signs are nice

View File

@ -14,7 +14,7 @@ class TestGeneratedSite < Test::Unit::TestCase
end
should "ensure post count is as expected" do
assert_equal 39, @site.posts.size
assert_equal 40, @site.posts.size
end
should "insert site.posts into the index" do

View File

@ -86,13 +86,20 @@ class TestPost < Test::Unit::TestCase
end
end
should "CGI escape urls" do
should "escape urls" do
@post.categories = []
@post.process("2009-03-12-hash-#1.markdown")
assert_equal "/2009/03/12/hash-%231.html", @post.url
assert_equal "/2009/03/12/hash-#1", @post.id
end
should "escape urls with non-alphabetic characters" do
@post.categories = []
@post.process("2014-03-22-escape-+ %20[].markdown")
assert_equal "/2014/03/22/escape-+%20%2520%5B%5D.html", @post.url
assert_equal "/2014/03/22/escape-+ %20[]", @post.id
end
should "respect permalink in yaml front matter" do
file = "2008-12-03-permalinked-post.textile"
@post.process(file)
@ -521,6 +528,26 @@ class TestPost < Test::Unit::TestCase
assert File.exists?(File.join(dest_dir, '2008', '10', '18', 'foo-bar.html'))
end
should "write properly when url has hash" do
post = setup_post("2009-03-12-hash-#1.markdown")
do_render(post)
post.write(dest_dir)
assert File.directory?(dest_dir)
assert File.exists?(File.join(dest_dir, '2009', '03', '12',
'hash-#1.html'))
end
should "write properly when url has space" do
post = setup_post("2014-03-22-escape-+ %20[].markdown")
do_render(post)
post.write(dest_dir)
assert File.directory?(dest_dir)
assert File.exists?(File.join(dest_dir, '2014', '03', '22',
'escape-+ %20[].html'))
end
should "write properly without html extension" do
post = setup_post("2008-10-18-foo-bar.textile")
post.site.permalink_style = ":title"