A Fix for the permalinks in #1136 (#1142)

* add fix for (#1135) Taxonomies with identical slugs now get merged (#1136)

* update templates so they propperly render taxonomy names

* squash! add fix for (#1135) Taxonomies with identical slugs now get merged (#1136)

reimplement taxonomy deduping

* revert unwanted changes to templates

* add tests for unic in permalinks

* add tests for unic in permalinks
This commit is contained in:
Sam Vente 2020-08-26 19:36:02 +02:00 committed by GitHub
parent af0dd5ef32
commit 6e16dfdc29
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 228 additions and 32 deletions

View file

@ -2,7 +2,7 @@ mod config;
pub mod highlighting; pub mod highlighting;
mod theme; mod theme;
pub use crate::config::{ pub use crate::config::{
languages::Language, link_checker::LinkChecker, taxonomies::Taxonomy, Config, languages::Language, link_checker::LinkChecker, slugify::Slugify, taxonomies::Taxonomy, Config,
}; };
use std::path::Path; use std::path::Path;

View file

@ -1,3 +1,4 @@
use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
use serde_derive::Serialize; use serde_derive::Serialize;
@ -40,7 +41,7 @@ impl<'a> SerializedTaxonomyItem<'a> {
} }
/// A taxonomy with all its pages /// A taxonomy with all its pages
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone)]
pub struct TaxonomyItem { pub struct TaxonomyItem {
pub name: String, pub name: String,
pub slug: String, pub slug: String,
@ -70,22 +71,33 @@ impl TaxonomyItem {
}) })
.collect(); .collect();
let (mut pages, ignored_pages) = sort_pages_by_date(data); let (mut pages, ignored_pages) = sort_pages_by_date(data);
let slug = slugify_paths(name, config.slugify.taxonomies); let item_slug = slugify_paths(name, config.slugify.taxonomies);
let taxo_slug = slugify_paths(&taxonomy.name, config.slugify.taxonomies);
let permalink = if taxonomy.lang != config.default_language { let permalink = if taxonomy.lang != config.default_language {
config.make_permalink(&format!("/{}/{}/{}", taxonomy.lang, taxonomy.name, slug)) config.make_permalink(&format!("/{}/{}/{}", taxonomy.lang, taxo_slug, item_slug))
} else { } else {
config.make_permalink(&format!("/{}/{}", taxonomy.name, slug)) config.make_permalink(&format!("/{}/{}", taxo_slug, item_slug))
}; };
// We still append pages without dates at the end // We still append pages without dates at the end
pages.extend(ignored_pages); pages.extend(ignored_pages);
TaxonomyItem { name: name.to_string(), permalink, slug, pages } TaxonomyItem { name: name.to_string(), permalink, slug: item_slug, pages }
} }
pub fn serialize<'a>(&'a self, library: &'a Library) -> SerializedTaxonomyItem<'a> { pub fn serialize<'a>(&'a self, library: &'a Library) -> SerializedTaxonomyItem<'a> {
SerializedTaxonomyItem::from_item(self, library) SerializedTaxonomyItem::from_item(self, library)
} }
pub fn merge(&mut self, other: Self) {
self.pages.extend(other.pages);
}
}
impl PartialEq for TaxonomyItem {
fn eq(&self, other: &Self) -> bool {
self.permalink == other.permalink
}
} }
#[derive(Debug, Clone, PartialEq, Serialize)] #[derive(Debug, Clone, PartialEq, Serialize)]
@ -121,8 +133,23 @@ impl Taxonomy {
for (name, pages) in items { for (name, pages) in items {
sorted_items.push(TaxonomyItem::new(&name, &kind, config, pages, library)); sorted_items.push(TaxonomyItem::new(&name, &kind, config, pages, library));
} }
sorted_items.sort_by(|a, b| a.name.cmp(&b.name)); //sorted_items.sort_by(|a, b| a.name.cmp(&b.name));
sorted_items.sort_by(|a, b| match a.slug.cmp(&b.slug) {
Ordering::Less => Ordering::Less,
Ordering::Greater => Ordering::Greater,
Ordering::Equal => a.name.cmp(&b.name),
});
sorted_items.dedup_by(|a, b| {
// custom Eq impl checks for equal permalinks
// here we make sure all pages from a get coppied to b
// before dedup gets rid of it
if a == b {
b.merge(a.to_owned());
true
} else {
false
}
});
Taxonomy { kind, items: sorted_items } Taxonomy { kind, items: sorted_items }
} }
@ -189,27 +216,25 @@ pub fn find_taxonomies(config: &Config, library: &Library) -> Result<Vec<Taxonom
let taxonomies_def = { let taxonomies_def = {
let mut m = HashMap::new(); let mut m = HashMap::new();
for t in &config.taxonomies { for t in &config.taxonomies {
m.insert( let slug = slugify_paths(&t.name, config.slugify.taxonomies);
format!("{}-{}", slugify_paths(&t.name, config.slugify.taxonomies), t.lang), m.insert(format!("{}-{}", slug, t.lang), t);
t,
);
} }
m m
}; };
let mut all_taxonomies = HashMap::new(); let mut all_taxonomies = HashMap::new();
for (key, page) in library.pages() { for (key, page) in library.pages() {
for (name, val) in &page.meta.taxonomies { for (name, taxo_term) in &page.meta.taxonomies {
let taxo_key = let taxo_slug = slugify_paths(&name, config.slugify.taxonomies);
format!("{}-{}", slugify_paths(name, config.slugify.taxonomies), page.lang); let taxo_key = format!("{}-{}", &taxo_slug, page.lang);
if taxonomies_def.contains_key(&taxo_key) { if taxonomies_def.contains_key(&taxo_key) {
all_taxonomies.entry(taxo_key.clone()).or_insert_with(HashMap::new); all_taxonomies.entry(taxo_key.clone()).or_insert_with(HashMap::new);
for v in val { for term in taxo_term {
all_taxonomies all_taxonomies
.get_mut(&taxo_key) .get_mut(&taxo_key)
.unwrap() .unwrap()
.entry(v.to_string()) .entry(term.to_string())
.or_insert_with(|| vec![]) .or_insert_with(|| vec![])
.push(key); .push(key);
} }
@ -239,7 +264,7 @@ mod tests {
use crate::content::Page; use crate::content::Page;
use crate::library::Library; use crate::library::Library;
use config::{Config, Language, Taxonomy as TaxonomyConfig}; use config::{Config, Language, Slugify, Taxonomy as TaxonomyConfig};
use utils::slugs::SlugifyStrategy; use utils::slugs::SlugifyStrategy;
#[test] #[test]
@ -714,8 +739,9 @@ mod tests {
); );
assert_eq!(categories.items[1].pages.len(), 1); assert_eq!(categories.items[1].pages.len(), 1);
} }
#[test] #[test]
fn taxonomies_are_groupted_by_slug() { fn taxonomies_are_groupted_by_permalink() {
let mut config = Config::default(); let mut config = Config::default();
let mut library = Library::new(2, 0, false); let mut library = Library::new(2, 0, false);
@ -744,40 +770,208 @@ mod tests {
let mut page1 = Page::default(); let mut page1 = Page::default();
let mut taxo_page1 = HashMap::new(); let mut taxo_page1 = HashMap::new();
taxo_page1 taxo_page1.insert(
.insert("test-taxonomy".to_string(), vec!["term1".to_string(), "term2".to_string()]); "test-taxonomy".to_string(),
vec!["term one".to_string(), "term two".to_string()],
);
page1.meta.taxonomies = taxo_page1; page1.meta.taxonomies = taxo_page1;
page1.lang = config.default_language.clone(); page1.lang = config.default_language.clone();
library.insert_page(page1); library.insert_page(page1);
let mut page2 = Page::default(); let mut page2 = Page::default();
let mut taxo_page2 = HashMap::new(); let mut taxo_page2 = HashMap::new();
taxo_page2 taxo_page2.insert(
.insert("test taxonomy".to_string(), vec!["term2".to_string(), "term3".to_string()]); "test taxonomy".to_string(),
vec!["Term Two".to_string(), "term-one".to_string()],
);
page2.meta.taxonomies = taxo_page2; page2.meta.taxonomies = taxo_page2;
page2.lang = config.default_language.clone(); page2.lang = config.default_language.clone();
library.insert_page(page2); library.insert_page(page2);
let mut page3 = Page::default(); let mut page3 = Page::default();
let mut taxo_page3 = HashMap::new(); let mut taxo_page3 = HashMap::new();
taxo_page3.insert("test-taxonomy ".to_string(), vec!["term4".to_string()]); taxo_page3.insert("test-taxonomy ".to_string(), vec!["term one ".to_string()]);
page3.meta.taxonomies = taxo_page3; page3.meta.taxonomies = taxo_page3;
page3.lang = config.default_language.clone(); page3.lang = config.default_language.clone();
library.insert_page(page3); library.insert_page(page3);
let mut page4 = Page::default(); let mut page4 = Page::default();
let mut taxo_page4 = HashMap::new(); let mut taxo_page4 = HashMap::new();
taxo_page4.insert("Test-Taxonomy ".to_string(), vec!["term8".to_string()]); taxo_page4.insert("Test-Taxonomy ".to_string(), vec!["Term-Two ".to_string()]);
page4.meta.taxonomies = taxo_page4; page4.meta.taxonomies = taxo_page4;
page4.lang = config.default_language.clone(); page4.lang = config.default_language.clone();
library.insert_page(page4); library.insert_page(page4);
// taxonomies get merged correctly // taxonomies should all be the same
let taxonomies = find_taxonomies(&config, &library).unwrap(); let taxonomies = find_taxonomies(&config, &library).unwrap();
assert_eq!(taxonomies.len(), 1); assert_eq!(taxonomies.len(), 1);
// merged taxonomies contains all of the terms let tax = &taxonomies[0];
let term = taxonomies.iter().next().unwrap();
assert_eq!(term.items.len(), 5); // terms should be "term one", "term two"
assert_eq!(tax.items.len(), 2);
let term1 = &tax.items[0];
let term2 = &tax.items[1];
assert_eq!(term1.name, "term one");
assert_eq!(term1.slug, "term-one");
assert_eq!(term1.permalink, "http://a-website.com/test-taxonomy/term-one/");
assert_eq!(term1.pages.len(), 3);
assert_eq!(term2.name, "Term Two");
assert_eq!(term2.slug, "term-two");
assert_eq!(term2.permalink, "http://a-website.com/test-taxonomy/term-two/");
assert_eq!(term2.pages.len(), 3);
}
#[test]
fn taxonomies_with_unic_are_grouped_with_default_slugify_strategy() {
let mut config = Config::default();
let mut library = Library::new(2, 0, false);
config.taxonomies = vec![
TaxonomyConfig {
name: "test-taxonomy".to_string(),
lang: config.default_language.clone(),
..TaxonomyConfig::default()
},
TaxonomyConfig {
name: "test taxonomy".to_string(),
lang: config.default_language.clone(),
..TaxonomyConfig::default()
},
TaxonomyConfig {
name: "test-taxonomy ".to_string(),
lang: config.default_language.clone(),
..TaxonomyConfig::default()
},
TaxonomyConfig {
name: "Test-Taxonomy ".to_string(),
lang: config.default_language.clone(),
..TaxonomyConfig::default()
},
];
let mut page1 = Page::default();
let mut taxo_page1 = HashMap::new();
taxo_page1.insert("test-taxonomy".to_string(), vec!["Ecole".to_string()]);
page1.meta.taxonomies = taxo_page1;
page1.lang = config.default_language.clone();
library.insert_page(page1);
let mut page2 = Page::default();
let mut taxo_page2 = HashMap::new();
taxo_page2.insert("test taxonomy".to_string(), vec!["École".to_string()]);
page2.meta.taxonomies = taxo_page2;
page2.lang = config.default_language.clone();
library.insert_page(page2);
let mut page3 = Page::default();
let mut taxo_page3 = HashMap::new();
taxo_page3.insert("test-taxonomy ".to_string(), vec!["ecole".to_string()]);
page3.meta.taxonomies = taxo_page3;
page3.lang = config.default_language.clone();
library.insert_page(page3);
let mut page4 = Page::default();
let mut taxo_page4 = HashMap::new();
taxo_page4.insert("Test-Taxonomy ".to_string(), vec!["école".to_string()]);
page4.meta.taxonomies = taxo_page4;
page4.lang = config.default_language.clone();
library.insert_page(page4);
// taxonomies should all be the same
let taxonomies = find_taxonomies(&config, &library).unwrap();
assert_eq!(taxonomies.len(), 1);
let tax = &taxonomies[0];
// under the default slugify stratagy all of the provided terms should be the same
assert_eq!(tax.items.len(), 1);
let term1 = &tax.items[0];
assert_eq!(term1.name, "Ecole");
assert_eq!(term1.slug, "ecole");
assert_eq!(term1.permalink, "http://a-website.com/test-taxonomy/ecole/");
assert_eq!(term1.pages.len(), 4);
}
#[test]
fn taxonomies_with_unic_are_not_grouped_with_safe_slugify_strategy() {
let mut config = Config::default();
config.slugify = Slugify {
paths: SlugifyStrategy::Safe,
taxonomies: SlugifyStrategy::Safe,
anchors: SlugifyStrategy::Safe,
};
let mut library = Library::new(2, 0, false);
config.taxonomies = vec![
TaxonomyConfig {
name: "test-taxonomy".to_string(),
lang: config.default_language.clone(),
..TaxonomyConfig::default()
},
TaxonomyConfig {
name: "test taxonomy".to_string(),
lang: config.default_language.clone(),
..TaxonomyConfig::default()
},
TaxonomyConfig {
name: "test-taxonomy ".to_string(),
lang: config.default_language.clone(),
..TaxonomyConfig::default()
},
TaxonomyConfig {
name: "Test-Taxonomy ".to_string(),
lang: config.default_language.clone(),
..TaxonomyConfig::default()
},
];
let mut page1 = Page::default();
let mut taxo_page1 = HashMap::new();
taxo_page1.insert("test-taxonomy".to_string(), vec!["Ecole".to_string()]);
page1.meta.taxonomies = taxo_page1;
page1.lang = config.default_language.clone();
library.insert_page(page1);
let mut page2 = Page::default();
let mut taxo_page2 = HashMap::new();
taxo_page2.insert("test-taxonomy".to_string(), vec!["École".to_string()]);
page2.meta.taxonomies = taxo_page2;
page2.lang = config.default_language.clone();
library.insert_page(page2);
let mut page3 = Page::default();
let mut taxo_page3 = HashMap::new();
taxo_page3.insert("test-taxonomy".to_string(), vec!["ecole".to_string()]);
page3.meta.taxonomies = taxo_page3;
page3.lang = config.default_language.clone();
library.insert_page(page3);
let mut page4 = Page::default();
let mut taxo_page4 = HashMap::new();
taxo_page4.insert("test-taxonomy".to_string(), vec!["école".to_string()]);
page4.meta.taxonomies = taxo_page4;
page4.lang = config.default_language.clone();
library.insert_page(page4);
// taxonomies should all be the same
let taxonomies = find_taxonomies(&config, &library).unwrap();
let tax = &taxonomies[0];
// if names are different permalinks should also be different so
// the tems are still accessable
for term1 in tax.items.iter() {
for term2 in tax.items.iter() {
assert!(term1.name == term2.name || term1.permalink != term2.permalink);
}
}
// under the safe slugify strategy all terms should be distinct
assert_eq!(tax.items.len(), 4);
} }
} }

View file

@ -177,12 +177,14 @@ Currently, the only supported keys are `width` and `height`.
Gets the permalink for the taxonomy item found. Gets the permalink for the taxonomy item found.
```jinja2 ```jinja2
{% set url = get_taxonomy_url(kind="categories", name=page.taxonomies.category) %} {% set url = get_taxonomy_url(kind="categories", name=page.taxonomies.category, lang=page.lang) %}
``` ```
`name` will almost always come from a variable but in case you want to do it manually, `name` will almost always come from a variable but in case you want to do it manually,
the value should be the same as the one in the front matter, not the slugified version. the value should be the same as the one in the front matter, not the slugified version.
`lang` (optional) default to `config.default_language` in config.toml
### `get_taxonomy` ### `get_taxonomy`
Gets the whole taxonomy of a specific kind. Gets the whole taxonomy of a specific kind.