Skip to content

Commit

Permalink
Make trailing slashes optional
Browse files Browse the repository at this point in the history
Now if you have a web server set up to handle this, you can set
trailing_slashes = false in the config and use URLs without them.
  • Loading branch information
valpackett committed Jun 25, 2021
1 parent 3e1a934 commit af586b5
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 27 deletions.
36 changes: 29 additions & 7 deletions components/config/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ pub struct Config {
pub feed_filename: String,
/// If set, files from static/ will be hardlinked instead of copied to the output dir.
pub hard_link_static: bool,
/// If set, paths/permalinks will have a trailing slash appended.
pub trailing_slashes: bool,

pub taxonomies: Vec<taxonomies::Taxonomy>,

Expand Down Expand Up @@ -167,12 +169,15 @@ impl Config {

/// Makes a url, taking into account that the base url might have a trailing slash
pub fn make_permalink(&self, path: &str) -> String {
let trailing_bit =
if path.ends_with('/') || path.ends_with(&self.feed_filename) || path.is_empty() {
""
} else {
"/"
};
let trailing_bit = if !self.trailing_slashes
|| path.ends_with('/')
|| path.ends_with(&self.feed_filename)
|| path.is_empty()
{
""
} else {
"/"
};

// Index section with a base url that has a trailing slash
if self.base_url.ends_with('/') && path == "/" {
Expand Down Expand Up @@ -336,6 +341,7 @@ impl Default for Config {
feed_limit: None,
feed_filename: "atom.xml".to_string(),
hard_link_static: false,
trailing_slashes: true,
taxonomies: Vec::new(),
compile_sass: false,
minify_html: false,
Expand Down Expand Up @@ -415,7 +421,7 @@ hello = "world"
}

#[test]
fn can_make_url_index_page_with_railing_slash_url() {
fn can_make_url_index_page_with_trailing_slash_url() {
let mut config = Config::default();
config.base_url = "http://vincent.is/".to_string();
assert_eq!(config.make_permalink(""), "http://vincent.is/");
Expand All @@ -428,13 +434,29 @@ hello = "world"
assert_eq!(config.make_permalink("hello"), "http://vincent.is/hello/");
}

#[test]
fn can_make_url_with_non_trailing_slash_base_url_without_trailing_slash() {
let mut config = Config::default();
config.trailing_slashes = false;
config.base_url = "http://vincent.is".to_string();
assert_eq!(config.make_permalink("hello"), "http://vincent.is/hello");
}

#[test]
fn can_make_url_with_trailing_slash_path() {
let mut config = Config::default();
config.base_url = "http://vincent.is/".to_string();
assert_eq!(config.make_permalink("/hello"), "http://vincent.is/hello/");
}

#[test]
fn can_make_url_without_trailing_slash_path() {
let mut config = Config::default();
config.trailing_slashes = false;
config.base_url = "http://vincent.is/".to_string();
assert_eq!(config.make_permalink("/hello"), "http://vincent.is/hello");
}

#[test]
fn can_make_url_with_localhost() {
let mut config = Config::default();
Expand Down
2 changes: 1 addition & 1 deletion components/library/src/content/page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ impl Page {
format!("/{}", path)
};

if !page.path.ends_with('/') {
if config.trailing_slashes && !page.path.ends_with('/') {
page.path = format!("{}/", page.path);
}

Expand Down
27 changes: 25 additions & 2 deletions components/library/src/content/section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,11 @@ impl Section {
} else {
"".into()
};
let trailing_bit = if config.trailing_slashes { "/" } else { "" };
section.path = if path.is_empty() {
format!("{}/", lang_path)
format!("{}{}", lang_path, trailing_bit)
} else {
format!("{}/{}/", lang_path, path)
format!("{}/{}{}", lang_path, path, trailing_bit)
};

section.components = section
Expand Down Expand Up @@ -349,4 +350,26 @@ Bonjour le monde"#
assert_eq!(section.lang, "fr".to_string());
assert_eq!(section.permalink, "http://a-website.com/fr/subcontent/");
}

#[test]
fn can_make_links_to_translated_subsections_without_trailing_slash() {
let mut config = Config::default();
config.trailing_slashes = false;
config.languages.insert("fr".to_owned(), LanguageOptions::default());
let content = r#"
+++
+++
Bonjour le monde"#
.to_string();
let res = Section::parse(
Path::new("content/subcontent/_index.fr.md"),
&content,
&config,
&PathBuf::new(),
);
assert!(res.is_ok());
let section = res.unwrap();
assert_eq!(section.lang, "fr".to_string());
assert_eq!(section.permalink, "http://a-website.com/fr/subcontent");
}
}
59 changes: 44 additions & 15 deletions components/library/src/pagination/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ pub struct Paginator<'a> {
impl<'a> Paginator<'a> {
/// Create a new paginator from a section
/// It will always at least create one pager (the first) even if there are not enough pages to paginate
pub fn from_section(section: &'a Section, library: &'a Library) -> Paginator<'a> {
pub fn from_section(
config: &Config,
section: &'a Section,
library: &'a Library,
) -> Paginator<'a> {
let paginate_by = section.meta.paginate_by.unwrap();
let paginate_reversed = section.meta.paginate_reversed;
let mut paginator = Paginator {
Expand All @@ -84,13 +88,14 @@ impl<'a> Paginator<'a> {
template: section.get_template_name().to_string(),
};

paginator.fill_pagers(library);
paginator.fill_pagers(config, library);
paginator
}

/// Create a new paginator from a taxonomy
/// It will always at least create one pager (the first) even if there are not enough pages to paginate
pub fn from_taxonomy(
config: &Config,
taxonomy: &'a Taxonomy,
item: &'a TaxonomyItem,
library: &'a Library,
Expand All @@ -114,11 +119,11 @@ impl<'a> Paginator<'a> {
};

// taxonomy paginators have no sorting so we won't have to reverse
paginator.fill_pagers(library);
paginator.fill_pagers(config, library);
paginator
}

fn fill_pagers(&mut self, library: &'a Library) {
fn fill_pagers(&mut self, config: &Config, library: &'a Library) {
// the list of pagers
let mut pages = vec![];
// the pages in the current pagers
Expand Down Expand Up @@ -150,12 +155,17 @@ impl<'a> Paginator<'a> {
continue;
}

let trailing_bit = if config.trailing_slashes { "/" } else { "" };
let page_path = if self.paginate_path.is_empty() {
format!("{}/", index + 1)
format!("{}{}", index + 1, trailing_bit)
} else {
format!("{}/{}{}", self.paginate_path, index + 1, trailing_bit)
};
let permalink = if self.permalink.ends_with('/') {
format!("{}{}", self.permalink, page_path)
} else {
format!("{}/{}/", self.paginate_path, index + 1)
format!("{}/{}", self.permalink, page_path)
};
let permalink = format!("{}{}", self.permalink, page_path);

let pager_path = if self.is_index {
format!("/{}", page_path)
Expand Down Expand Up @@ -254,7 +264,7 @@ mod tests {
use crate::content::{Page, Section};
use crate::library::Library;
use crate::taxonomies::{Taxonomy, TaxonomyItem};
use config::Taxonomy as TaxonomyConfig;
use config::{Config, Taxonomy as TaxonomyConfig};
use front_matter::SectionFrontMatter;

use super::Paginator;
Expand Down Expand Up @@ -300,8 +310,9 @@ mod tests {

#[test]
fn test_can_create_paginator() {
let config = Config::default();
let (section, library) = create_library(false, 3, false);
let paginator = Paginator::from_section(&section, &library);
let paginator = Paginator::from_section(&config, &section, &library);
assert_eq!(paginator.pagers.len(), 2);

assert_eq!(paginator.pagers[0].index, 1);
Expand All @@ -315,11 +326,24 @@ mod tests {
assert_eq!(paginator.pagers[1].path, "/posts/page/2/");
}

#[test]
fn test_can_create_paginator_without_trailing_slash() {
let mut config = Config::default();
config.trailing_slashes = false;
let (section, library) = create_library(false, 3, false);
let paginator = Paginator::from_section(&config, &section, &library);
assert_eq!(paginator.pagers.len(), 2);

assert_eq!(paginator.pagers[1].permalink, "https://vincent.is/posts/page/2");
assert_eq!(paginator.pagers[1].path, "/posts/page/2");
}

#[test]
fn test_can_create_reversed_paginator() {
// 6 pages, 5 normal and 1 draft
let config = Config::default();
let (section, library) = create_library(false, 5, true);
let paginator = Paginator::from_section(&section, &library);
let paginator = Paginator::from_section(&config, &section, &library);
assert_eq!(paginator.pagers.len(), 3);

assert_eq!(paginator.pagers[0].index, 1);
Expand Down Expand Up @@ -364,8 +388,9 @@ mod tests {

#[test]
fn test_can_create_paginator_for_index() {
let config = Config::default();
let (section, library) = create_library(true, 3, false);
let paginator = Paginator::from_section(&section, &library);
let paginator = Paginator::from_section(&config, &section, &library);
assert_eq!(paginator.pagers.len(), 2);

assert_eq!(paginator.pagers[0].index, 1);
Expand All @@ -381,8 +406,9 @@ mod tests {

#[test]
fn test_can_build_paginator_context() {
let config = Config::default();
let (section, library) = create_library(false, 3, false);
let paginator = Paginator::from_section(&section, &library);
let paginator = Paginator::from_section(&config, &section, &library);
assert_eq!(paginator.pagers.len(), 2);

let context = paginator.build_paginator_context(&paginator.pagers[0]);
Expand All @@ -405,6 +431,7 @@ mod tests {

#[test]
fn test_can_create_paginator_for_taxonomy() {
let config = Config::default();
let (_, library) = create_library(false, 3, false);
let taxonomy_def = TaxonomyConfig {
name: "tags".to_string(),
Expand All @@ -424,7 +451,7 @@ mod tests {
slug: "tags".to_string(),
items: vec![taxonomy_item.clone()],
};
let paginator = Paginator::from_taxonomy(&taxonomy, &taxonomy_item, &library);
let paginator = Paginator::from_taxonomy(&config, &taxonomy, &taxonomy_item, &library);
assert_eq!(paginator.pagers.len(), 2);

assert_eq!(paginator.pagers[0].index, 1);
Expand All @@ -440,6 +467,7 @@ mod tests {

#[test]
fn test_can_create_paginator_for_slugified_taxonomy() {
let config = Config::default();
let (_, library) = create_library(false, 3, false);
let taxonomy_def = TaxonomyConfig {
name: "some tags".to_string(),
Expand All @@ -459,7 +487,7 @@ mod tests {
slug: "some-tags".to_string(),
items: vec![taxonomy_item.clone()],
};
let paginator = Paginator::from_taxonomy(&taxonomy, &taxonomy_item, &library);
let paginator = Paginator::from_taxonomy(&config, &taxonomy, &taxonomy_item, &library);
assert_eq!(paginator.pagers.len(), 2);

assert_eq!(paginator.pagers[0].index, 1);
Expand All @@ -476,9 +504,10 @@ mod tests {
// https://github.com/getzola/zola/issues/866
#[test]
fn works_with_empty_paginate_path() {
let config = Config::default();
let (mut section, library) = create_library(false, 3, false);
section.meta.paginate_path = String::new();
let paginator = Paginator::from_section(&section, &library);
let paginator = Paginator::from_section(&config, &section, &library);
assert_eq!(paginator.pagers.len(), 2);

assert_eq!(paginator.pagers[0].index, 1);
Expand Down
4 changes: 2 additions & 2 deletions components/site/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -816,7 +816,7 @@ impl Site {
if taxonomy.kind.is_paginated() {
self.render_paginated(
comp.clone(),
&Paginator::from_taxonomy(&taxonomy, item, &library),
&Paginator::from_taxonomy(&self.config, &taxonomy, item, &library),
)?;
} else {
let single_output =
Expand Down Expand Up @@ -1005,7 +1005,7 @@ impl Site {
if section.meta.is_paginated() {
self.render_paginated(
components,
&Paginator::from_section(&section, &self.library.read().unwrap()),
&Paginator::from_section(&self.config, &section, &self.library.read().unwrap()),
)?;
} else {
let output =
Expand Down
4 changes: 4 additions & 0 deletions docs/content/documentation/getting-started/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ generate_feed = false
# files are always copied, regardless of this setting.
# hard_link_static = false

# When set to "true", all links are generated with trailing slashes.
# This is the default due to how most default web server configurations work.
trailing_slashes = true

# The taxonomies to be rendered for the site and their configuration.
# Example:
# taxonomies = [
Expand Down

0 comments on commit af586b5

Please sign in to comment.