Make current_path always start with a slash (#1101)

* Make {section, page}.path always start with a slash

Change tests accordingly

* Fix missing leading/trailing slash in current_path of Taxonomy ("tags") and TaxonomyItem ("some-tag")

* Make {Paginator, Pager}.path always start with a slash

Fix Paginator.path missing trailing slash in from_taxonomy()

Change tests accordingly

* Update documentation regarding current_path now always starting with a slash

* Fix asymptomatic inverted logic in filter() for {section, page}.assets

* Add to 3 integration tests several checks for current_path in different templates

* Add a check for current_path in a paginated index section, "/page/2/"

This requires adding two dummy pages in the content root.

* Fix false passing of test on paginator.last due to URL prefix matching

A string formatting such as {name: value} can help prevent this.
This commit is contained in:
yanghuidong 2020-07-30 02:44:09 +08:00 committed by GitHub
parent 28523ac9ad
commit b9296f7985
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 97 additions and 45 deletions

View file

@ -48,7 +48,7 @@ pub struct Page {
/// The slug of that page. /// The slug of that page.
/// First tries to find the slug in the meta and defaults to filename otherwise /// First tries to find the slug in the meta and defaults to filename otherwise
pub slug: String, pub slug: String,
/// The URL path of the page /// The URL path of the page, always starting with a slash
pub path: String, pub path: String,
/// The components of the path of the page /// The components of the path of the page
pub components: Vec<String>, pub components: Vec<String>,
@ -182,8 +182,14 @@ impl Page {
} }
}; };
if let Some(ref p) = page.meta.path { page.path = if let Some(ref p) = page.meta.path {
page.path = p.trim().trim_start_matches('/').to_string(); let path = p.trim();
if path.starts_with('/') {
path.into()
} else {
format!("/{}", path)
}
} else { } else {
let mut path = if page.file.components.is_empty() { let mut path = if page.file.components.is_empty() {
page.slug.clone() page.slug.clone()
@ -195,8 +201,8 @@ impl Page {
path = format!("{}/{}", page.lang, path); path = format!("{}/{}", page.lang, path);
} }
page.path = path; format!("/{}", path)
} };
if !page.path.ends_with('/') { if !page.path.ends_with('/') {
page.path = format!("{}/", page.path); page.path = format!("{}/", page.path);
@ -238,7 +244,7 @@ impl Page {
page.assets = assets page.assets = assets
.into_iter() .into_iter()
.filter(|path| match path.file_name() { .filter(|path| match path.file_name() {
None => true, None => false,
Some(file) => !globset.is_match(file), Some(file) => !globset.is_match(file),
}) })
.collect(); .collect();
@ -420,7 +426,7 @@ Hello world"#;
Page::parse(Path::new("content/posts/intro/start.md"), content, &conf, &PathBuf::new()); Page::parse(Path::new("content/posts/intro/start.md"), content, &conf, &PathBuf::new());
assert!(res.is_ok()); assert!(res.is_ok());
let page = res.unwrap(); let page = res.unwrap();
assert_eq!(page.path, "posts/intro/hello-world/"); assert_eq!(page.path, "/posts/intro/hello-world/");
assert_eq!(page.components, vec!["posts", "intro", "hello-world"]); assert_eq!(page.components, vec!["posts", "intro", "hello-world"]);
assert_eq!(page.permalink, "http://hello.com/posts/intro/hello-world/"); assert_eq!(page.permalink, "http://hello.com/posts/intro/hello-world/");
} }
@ -436,7 +442,7 @@ Hello world"#;
let res = Page::parse(Path::new("start.md"), content, &config, &PathBuf::new()); let res = Page::parse(Path::new("start.md"), content, &config, &PathBuf::new());
assert!(res.is_ok()); assert!(res.is_ok());
let page = res.unwrap(); let page = res.unwrap();
assert_eq!(page.path, "hello-world/"); assert_eq!(page.path, "/hello-world/");
assert_eq!(page.components, vec!["hello-world"]); assert_eq!(page.components, vec!["hello-world"]);
assert_eq!(page.permalink, config.make_permalink("hello-world")); assert_eq!(page.permalink, config.make_permalink("hello-world"));
} }
@ -453,7 +459,7 @@ Hello world"#;
let res = Page::parse(Path::new("start.md"), content, &config, &PathBuf::new()); let res = Page::parse(Path::new("start.md"), content, &config, &PathBuf::new());
assert!(res.is_ok()); assert!(res.is_ok());
let page = res.unwrap(); let page = res.unwrap();
assert_eq!(page.path, "hello-world/"); assert_eq!(page.path, "/hello-world/");
assert_eq!(page.components, vec!["hello-world"]); assert_eq!(page.components, vec!["hello-world"]);
assert_eq!(page.permalink, config.make_permalink("hello-world")); assert_eq!(page.permalink, config.make_permalink("hello-world"));
} }
@ -470,7 +476,7 @@ Hello world"#;
let res = Page::parse(Path::new("start.md"), content, &config, &PathBuf::new()); let res = Page::parse(Path::new("start.md"), content, &config, &PathBuf::new());
assert!(res.is_ok()); assert!(res.is_ok());
let page = res.unwrap(); let page = res.unwrap();
assert_eq!(page.path, "日本/"); assert_eq!(page.path, "/日本/");
assert_eq!(page.components, vec!["日本"]); assert_eq!(page.components, vec!["日本"]);
assert_eq!(page.permalink, config.make_permalink("日本")); assert_eq!(page.permalink, config.make_permalink("日本"));
} }
@ -491,7 +497,7 @@ Hello world"#;
); );
assert!(res.is_ok()); assert!(res.is_ok());
let page = res.unwrap(); let page = res.unwrap();
assert_eq!(page.path, "hello-world/"); assert_eq!(page.path, "/hello-world/");
assert_eq!(page.components, vec!["hello-world"]); assert_eq!(page.components, vec!["hello-world"]);
assert_eq!(page.permalink, config.make_permalink("hello-world")); assert_eq!(page.permalink, config.make_permalink("hello-world"));
} }
@ -512,7 +518,7 @@ Hello world"#;
); );
assert!(res.is_ok()); assert!(res.is_ok());
let page = res.unwrap(); let page = res.unwrap();
assert_eq!(page.path, "hello-world/"); assert_eq!(page.path, "/hello-world/");
assert_eq!(page.permalink, config.make_permalink("hello-world")); assert_eq!(page.permalink, config.make_permalink("hello-world"));
} }

View file

@ -23,7 +23,7 @@ pub struct Section {
pub file: FileInfo, pub file: FileInfo,
/// The front matter meta-data /// The front matter meta-data
pub meta: SectionFrontMatter, pub meta: SectionFrontMatter,
/// The URL path of the page /// The URL path of the page, always starting with a slash
pub path: String, pub path: String,
/// The components for the path of that page /// The components for the path of that page
pub components: Vec<String>, pub components: Vec<String>,
@ -111,16 +111,18 @@ impl Section {
let (word_count, reading_time) = get_reading_analytics(&section.raw_content); let (word_count, reading_time) = get_reading_analytics(&section.raw_content);
section.word_count = Some(word_count); section.word_count = Some(word_count);
section.reading_time = Some(reading_time); section.reading_time = Some(reading_time);
let path = section.file.components.join("/"); let path = section.file.components.join("/");
if section.lang != config.default_language { let lang_path = if section.lang != config.default_language {
if path.is_empty() { format!("/{}", section.lang)
section.path = format!("{}/", section.lang);
} else {
section.path = format!("{}/{}/", section.lang, path);
}
} else { } else {
section.path = format!("{}/", path); "".into()
} };
section.path = if path.is_empty() {
format!("{}/", lang_path)
} else {
format!("{}/{}/", lang_path, path)
};
section.components = section section.components = section
.path .path
@ -156,7 +158,7 @@ impl Section {
section.assets = assets section.assets = assets
.into_iter() .into_iter()
.filter(|path| match path.file_name() { .filter(|path| match path.file_name() {
None => true, None => false,
Some(file) => !globset.is_match(file), Some(file) => !globset.is_match(file),
}) })
.collect(); .collect();

View file

@ -96,7 +96,7 @@ impl<'a> Paginator<'a> {
paginate_by, paginate_by,
root: PaginationRoot::Taxonomy(taxonomy, item), root: PaginationRoot::Taxonomy(taxonomy, item),
permalink: item.permalink.clone(), permalink: item.permalink.clone(),
path: format!("{}/{}", taxonomy.kind.name, item.slug), path: format!("/{}/{}/", taxonomy.kind.name, item.slug),
paginate_path: taxonomy paginate_path: taxonomy
.kind .kind
.paginate_path .paginate_path
@ -146,7 +146,7 @@ impl<'a> Paginator<'a> {
let permalink = format!("{}{}", self.permalink, page_path); let permalink = format!("{}{}", self.permalink, page_path);
let pager_path = if self.is_index { let pager_path = if self.is_index {
page_path format!("/{}", page_path)
} else if self.path.ends_with('/') { } else if self.path.ends_with('/') {
format!("{}{}", self.path, page_path) format!("{}{}", self.path, page_path)
} else { } else {
@ -252,10 +252,11 @@ mod tests {
f.paginate_path = "page".to_string(); f.paginate_path = "page".to_string();
let mut s = Section::new("content/_index.md", f, &PathBuf::new()); let mut s = Section::new("content/_index.md", f, &PathBuf::new());
if !is_index { if !is_index {
s.path = "posts/".to_string(); s.path = "/posts/".to_string();
s.permalink = "https://vincent.is/posts/".to_string(); s.permalink = "https://vincent.is/posts/".to_string();
s.file.components = vec!["posts".to_string()]; s.file.components = vec!["posts".to_string()];
} else { } else {
s.path = "/".into();
s.permalink = "https://vincent.is/".to_string(); s.permalink = "https://vincent.is/".to_string();
} }
s s
@ -285,12 +286,12 @@ mod tests {
assert_eq!(paginator.pagers[0].index, 1); assert_eq!(paginator.pagers[0].index, 1);
assert_eq!(paginator.pagers[0].pages.len(), 2); assert_eq!(paginator.pagers[0].pages.len(), 2);
assert_eq!(paginator.pagers[0].permalink, "https://vincent.is/posts/"); assert_eq!(paginator.pagers[0].permalink, "https://vincent.is/posts/");
assert_eq!(paginator.pagers[0].path, "posts/"); assert_eq!(paginator.pagers[0].path, "/posts/");
assert_eq!(paginator.pagers[1].index, 2); assert_eq!(paginator.pagers[1].index, 2);
assert_eq!(paginator.pagers[1].pages.len(), 2); assert_eq!(paginator.pagers[1].pages.len(), 2);
assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/posts/page/2/"); assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/posts/page/2/");
assert_eq!(paginator.pagers[1].path, "posts/page/2/"); assert_eq!(paginator.pagers[1].path, "/posts/page/2/");
} }
#[test] #[test]
@ -302,12 +303,12 @@ mod tests {
assert_eq!(paginator.pagers[0].index, 1); assert_eq!(paginator.pagers[0].index, 1);
assert_eq!(paginator.pagers[0].pages.len(), 2); assert_eq!(paginator.pagers[0].pages.len(), 2);
assert_eq!(paginator.pagers[0].permalink, "https://vincent.is/"); assert_eq!(paginator.pagers[0].permalink, "https://vincent.is/");
assert_eq!(paginator.pagers[0].path, ""); assert_eq!(paginator.pagers[0].path, "/");
assert_eq!(paginator.pagers[1].index, 2); assert_eq!(paginator.pagers[1].index, 2);
assert_eq!(paginator.pagers[1].pages.len(), 2); assert_eq!(paginator.pagers[1].pages.len(), 2);
assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/page/2/"); assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/page/2/");
assert_eq!(paginator.pagers[1].path, "page/2/"); assert_eq!(paginator.pagers[1].path, "/page/2/");
} }
#[test] #[test]
@ -355,12 +356,12 @@ mod tests {
assert_eq!(paginator.pagers[0].index, 1); assert_eq!(paginator.pagers[0].index, 1);
assert_eq!(paginator.pagers[0].pages.len(), 2); assert_eq!(paginator.pagers[0].pages.len(), 2);
assert_eq!(paginator.pagers[0].permalink, "https://vincent.is/tags/something/"); assert_eq!(paginator.pagers[0].permalink, "https://vincent.is/tags/something/");
assert_eq!(paginator.pagers[0].path, "tags/something"); assert_eq!(paginator.pagers[0].path, "/tags/something/");
assert_eq!(paginator.pagers[1].index, 2); assert_eq!(paginator.pagers[1].index, 2);
assert_eq!(paginator.pagers[1].pages.len(), 2); assert_eq!(paginator.pagers[1].pages.len(), 2);
assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/tags/something/page/2/"); assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/tags/something/page/2/");
assert_eq!(paginator.pagers[1].path, "tags/something/page/2/"); assert_eq!(paginator.pagers[1].path, "/tags/something/page/2/");
} }
// https://github.com/getzola/zola/issues/866 // https://github.com/getzola/zola/issues/866
@ -374,12 +375,12 @@ mod tests {
assert_eq!(paginator.pagers[0].index, 1); assert_eq!(paginator.pagers[0].index, 1);
assert_eq!(paginator.pagers[0].pages.len(), 2); assert_eq!(paginator.pagers[0].pages.len(), 2);
assert_eq!(paginator.pagers[0].permalink, "https://vincent.is/posts/"); assert_eq!(paginator.pagers[0].permalink, "https://vincent.is/posts/");
assert_eq!(paginator.pagers[0].path, "posts/"); assert_eq!(paginator.pagers[0].path, "/posts/");
assert_eq!(paginator.pagers[1].index, 2); assert_eq!(paginator.pagers[1].index, 2);
assert_eq!(paginator.pagers[1].pages.len(), 2); assert_eq!(paginator.pagers[1].pages.len(), 2);
assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/posts/2/"); assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/posts/2/");
assert_eq!(paginator.pagers[1].path, "posts/2/"); assert_eq!(paginator.pagers[1].path, "/posts/2/");
let context = paginator.build_paginator_context(&paginator.pagers[0]); let context = paginator.build_paginator_context(&paginator.pagers[0]);
assert_eq!(context["base_url"], to_value("https://vincent.is/posts/").unwrap()); assert_eq!(context["base_url"], to_value("https://vincent.is/posts/").unwrap());

View file

@ -150,7 +150,7 @@ impl Taxonomy {
"current_url", "current_url",
&config.make_permalink(&format!("{}/{}", self.kind.name, item.slug)), &config.make_permalink(&format!("{}/{}", self.kind.name, item.slug)),
); );
context.insert("current_path", &format!("/{}/{}", self.kind.name, item.slug)); context.insert("current_path", &format!("/{}/{}/", self.kind.name, item.slug));
render_template(&format!("{}/single.html", self.kind.name), tera, context, &config.theme) render_template(&format!("{}/single.html", self.kind.name), tera, context, &config.theme)
.map_err(|e| { .map_err(|e| {
@ -172,7 +172,7 @@ impl Taxonomy {
context.insert("lang", &self.kind.lang); context.insert("lang", &self.kind.lang);
context.insert("taxonomy", &self.kind); context.insert("taxonomy", &self.kind);
context.insert("current_url", &config.make_permalink(&self.kind.name)); context.insert("current_url", &config.make_permalink(&self.kind.name));
context.insert("current_path", &self.kind.name); context.insert("current_path", &format!("/{}/", self.kind.name));
render_template(&format!("{}/list.html", self.kind.name), tera, context, &config.theme) render_template(&format!("{}/list.html", self.kind.name), tera, context, &config.theme)
.map_err(|e| { .map_err(|e| {

View file

@ -19,12 +19,12 @@ fn can_parse_site() {
let library = site.library.read().unwrap(); let library = site.library.read().unwrap();
// Correct number of pages (sections do not count as pages, draft are ignored) // Correct number of pages (sections do not count as pages, draft are ignored)
assert_eq!(library.pages().len(), 21); assert_eq!(library.pages().len(), 23);
let posts_path = path.join("content").join("posts"); let posts_path = path.join("content").join("posts");
// Make sure the page with a url doesn't have any sections // Make sure the page with a url doesn't have any sections
let url_post = library.get_page(&posts_path.join("fixed-url.md")).unwrap(); let url_post = library.get_page(&posts_path.join("fixed-url.md")).unwrap();
assert_eq!(url_post.path, "a-fixed-url/"); assert_eq!(url_post.path, "/a-fixed-url/");
// Make sure the article in a folder with only asset doesn't get counted as a section // Make sure the article in a folder with only asset doesn't get counted as a section
let asset_folder_post = let asset_folder_post =
@ -37,7 +37,7 @@ fn can_parse_site() {
// And that the sections are correct // And that the sections are correct
let index_section = library.get_section(&path.join("content").join("_index.md")).unwrap(); let index_section = library.get_section(&path.join("content").join("_index.md")).unwrap();
assert_eq!(index_section.subsections.len(), 4); assert_eq!(index_section.subsections.len(), 4);
assert_eq!(index_section.pages.len(), 1); assert_eq!(index_section.pages.len(), 3);
assert!(index_section.ancestors.is_empty()); assert!(index_section.ancestors.is_empty());
let posts_section = library.get_section(&posts_path.join("_index.md")).unwrap(); let posts_section = library.get_section(&posts_path.join("_index.md")).unwrap();
@ -446,6 +446,16 @@ fn can_build_site_with_pagination_for_section() {
"sitemap.xml", "sitemap.xml",
"<loc>https://replace-this-with-your-url.com/posts/page/4/</loc>" "<loc>https://replace-this-with-your-url.com/posts/page/4/</loc>"
)); ));
// current_path
assert!(file_contains!(public, "posts/index.html", &current_path("/posts/")));
assert!(file_contains!(public, "posts/page/2/index.html", &current_path("/posts/page/2/")));
assert!(file_contains!(public, "posts/python/index.html", &current_path("/posts/python/")));
assert!(file_contains!(
public,
"posts/tutorials/index.html",
&current_path("/posts/tutorials/")
));
} }
#[test] #[test]
@ -492,19 +502,28 @@ fn can_build_site_with_pagination_for_index() {
"page/1/index.html", "page/1/index.html",
"<a href=\"https://replace-this-with-your-url.com/\">Click here</a>" "<a href=\"https://replace-this-with-your-url.com/\">Click here</a>"
)); ));
assert!(file_contains!(public, "index.html", "Num pages: 1")); assert!(file_contains!(public, "index.html", "Num pages: 2"));
assert!(file_contains!(public, "index.html", "Current index: 1")); assert!(file_contains!(public, "index.html", "Current index: 1"));
assert!(file_contains!(public, "index.html", "First: https://replace-this-with-your-url.com/")); assert!(file_contains!(public, "index.html", "First: https://replace-this-with-your-url.com/"));
assert!(file_contains!(public, "index.html", "Last: https://replace-this-with-your-url.com/")); assert!(file_contains!(
public,
"index.html",
"Last: https://replace-this-with-your-url.com/page/2/"
));
assert_eq!(file_contains!(public, "index.html", "has_prev"), false); assert_eq!(file_contains!(public, "index.html", "has_prev"), false);
assert_eq!(file_contains!(public, "index.html", "has_next"), false); assert_eq!(file_contains!(public, "index.html", "has_next"), true);
// sitemap contains the pager pages // sitemap contains the pager pages
assert!(file_contains!( assert!(file_contains!(
public, public,
"sitemap.xml", "sitemap.xml",
"<loc>https://replace-this-with-your-url.com/page/1/</loc>" "<loc>https://replace-this-with-your-url.com/page/1/</loc>"
)) ));
// current_path
assert!(file_contains!(public, "index.html", &current_path("/")));
assert!(file_contains!(public, "page/2/index.html", &current_path("/page/2/")));
assert!(file_contains!(public, "paginated/index.html", &current_path("/paginated/")));
} }
#[test] #[test]
@ -585,7 +604,12 @@ fn can_build_site_with_pagination_for_taxonomy() {
public, public,
"sitemap.xml", "sitemap.xml",
"<loc>https://replace-this-with-your-url.com/tags/a/page/6/</loc>" "<loc>https://replace-this-with-your-url.com/tags/a/page/6/</loc>"
)) ));
// current_path
assert!(file_contains!(public, "tags/index.html", &current_path("/tags/")));
assert!(file_contains!(public, "tags/a/index.html", &current_path("/tags/a/")));
assert!(file_contains!(public, "tags/a/page/2/index.html", &current_path("/tags/a/page/2/")));
} }
#[test] #[test]
@ -718,3 +742,8 @@ fn check_site() {
site.config.enable_check_mode(); site.config.enable_check_mode();
site.load().expect("link check test_site"); site.load().expect("link check test_site");
} }
// Follows test_site/themes/sample/templates/current_path.html
fn current_path(path: &str) -> String {
format!("[current_path]({})", path)
}

View file

@ -16,7 +16,7 @@ you can place `{{ __tera_context }}` in the template to print the whole context.
A few variables are available on all templates except feeds and the sitemap: A few variables are available on all templates except feeds and the sitemap:
- `config`: the [configuration](@/documentation/getting-started/configuration.md) without any modifications - `config`: the [configuration](@/documentation/getting-started/configuration.md) without any modifications
- `current_path`: the path (full URL without `base_url`) of the current page, never starting with a `/` - `current_path`: the path (full URL without `base_url`) of the current page, always starting with a `/`
- `current_url`: the full URL for the current page - `current_url`: the full URL for the current page
- `lang`: the language for the current page - `lang`: the language for the current page

View file

@ -0,0 +1,2 @@
+++
+++

View file

@ -0,0 +1,2 @@
+++
+++

View file

@ -29,5 +29,8 @@
</div> </div>
{% endblock content %} {% endblock content %}
</div> </div>
{% include "current_path.html" %}
</body> </body>
</html> </html>

View file

@ -1,3 +1,5 @@
{% for tag in terms %} {% for tag in terms %}
{{ tag.name }} {{ tag.slug }} {{ tag.pages | length }} {{ tag.name }} {{ tag.slug }} {{ tag.pages | length }}
{% endfor %} {% endfor %}
{% include "current_path.html" %}

View file

@ -19,3 +19,5 @@
{% if paginator.previous %}has_prev{% endif%} {% if paginator.previous %}has_prev{% endif%}
{% if paginator.next %}has_next{% endif%} {% if paginator.next %}has_next{% endif%}
{% endif %} {% endif %}
{% include "current_path.html" %}

View file

@ -0,0 +1 @@
<div>[current_path]({{ current_path | safe }})</div>

View file

@ -16,6 +16,8 @@
{% block content %}Hello{% endblock content %} {% block content %}Hello{% endblock content %}
</div> </div>
{% include "current_path.html" %}
{% block script %} {% block script %}
{% endblock script %} {% endblock script %}
</body> </body>