* 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:
parent
af0dd5ef32
commit
6e16dfdc29
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue