Add generic template fallbacks to taxonomies (#1651)
* Split checking for theme template off into separate function Allows to check for fallbacks elsewhere in the code, without attempting to actually render the template. * Add template fallback checking to taxonomy pages. * Add template fallback checking to paginated taxonomies Requires passing additional arguments to Paginator::from_taxonomy, which may not be desirable. * Update documentation to reflect taxonomy template fallbacks. * Update generic taxonomy template names. * Make check_template_fallbacks() return &str. * Add tests for check_template_fallbacks
This commit is contained in:
parent
976ff6f2ef
commit
35359fb312
@ -6,7 +6,7 @@ use tera::{to_value, Context, Tera, Value};
|
||||
|
||||
use config::Config;
|
||||
use errors::{Error, Result};
|
||||
use utils::templates::render_template;
|
||||
use utils::templates::{check_template_fallbacks, render_template};
|
||||
|
||||
use crate::content::{Section, SerializingPage, SerializingSection};
|
||||
use crate::library::Library;
|
||||
@ -94,8 +94,16 @@ impl<'a> Paginator<'a> {
|
||||
taxonomy: &'a Taxonomy,
|
||||
item: &'a TaxonomyItem,
|
||||
library: &'a Library,
|
||||
tera: &Tera,
|
||||
theme: &Option<String>,
|
||||
) -> Paginator<'a> {
|
||||
let paginate_by = taxonomy.kind.paginate_by.unwrap();
|
||||
// Check for taxon-specific template, or use generic as fallback.
|
||||
let specific_template = format!("{}/single.html", taxonomy.kind.name);
|
||||
let template = match check_template_fallbacks(&specific_template, tera, theme) {
|
||||
Some(template) => template,
|
||||
None => "taxonomy_single.html",
|
||||
};
|
||||
let mut paginator = Paginator {
|
||||
all_pages: Cow::Borrowed(&item.pages),
|
||||
pagers: Vec::with_capacity(item.pages.len() / paginate_by),
|
||||
@ -110,7 +118,7 @@ impl<'a> Paginator<'a> {
|
||||
.clone()
|
||||
.unwrap_or_else(|| "page".to_string()),
|
||||
is_index: false,
|
||||
template: format!("{}/single.html", taxonomy.kind.name),
|
||||
template: template.to_string(),
|
||||
};
|
||||
|
||||
// taxonomy paginators have no sorting so we won't have to reverse
|
||||
@ -249,7 +257,7 @@ impl<'a> Paginator<'a> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::PathBuf;
|
||||
use tera::to_value;
|
||||
use tera::{to_value, Tera};
|
||||
|
||||
use crate::content::{Page, Section};
|
||||
use crate::library::Library;
|
||||
@ -408,6 +416,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_can_create_paginator_for_taxonomy() {
|
||||
let (_, library) = create_library(false, 3, false);
|
||||
let tera = Tera::default();
|
||||
let taxonomy_def = TaxonomyConfig {
|
||||
name: "tags".to_string(),
|
||||
paginate_by: Some(2),
|
||||
@ -427,7 +436,7 @@ mod tests {
|
||||
permalink: "/tags/".to_string(),
|
||||
items: vec![taxonomy_item.clone()],
|
||||
};
|
||||
let paginator = Paginator::from_taxonomy(&taxonomy, &taxonomy_item, &library);
|
||||
let paginator = Paginator::from_taxonomy(&taxonomy, &taxonomy_item, &library, &tera, &None);
|
||||
assert_eq!(paginator.pagers.len(), 2);
|
||||
|
||||
assert_eq!(paginator.pagers[0].index, 1);
|
||||
@ -444,6 +453,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_can_create_paginator_for_slugified_taxonomy() {
|
||||
let (_, library) = create_library(false, 3, false);
|
||||
let tera = Tera::default();
|
||||
let taxonomy_def = TaxonomyConfig {
|
||||
name: "some tags".to_string(),
|
||||
paginate_by: Some(2),
|
||||
@ -463,7 +473,7 @@ mod tests {
|
||||
permalink: "/some-tags/".to_string(),
|
||||
items: vec![taxonomy_item.clone()],
|
||||
};
|
||||
let paginator = Paginator::from_taxonomy(&taxonomy, &taxonomy_item, &library);
|
||||
let paginator = Paginator::from_taxonomy(&taxonomy, &taxonomy_item, &library, &tera, &None);
|
||||
assert_eq!(paginator.pagers.len(), 2);
|
||||
|
||||
assert_eq!(paginator.pagers[0].index, 1);
|
||||
|
@ -7,7 +7,7 @@ use tera::{Context, Tera};
|
||||
|
||||
use config::{Config, Taxonomy as TaxonomyConfig};
|
||||
use errors::{bail, Error, Result};
|
||||
use utils::templates::render_template;
|
||||
use utils::templates::{check_template_fallbacks, render_template};
|
||||
|
||||
use crate::content::SerializingPage;
|
||||
use crate::library::Library;
|
||||
@ -202,10 +202,16 @@ impl Taxonomy {
|
||||
);
|
||||
context.insert("current_path", &format!("/{}/{}/", self.kind.name, item.slug));
|
||||
|
||||
render_template(&format!("{}/single.html", self.kind.name), tera, context, &config.theme)
|
||||
.map_err(|e| {
|
||||
Error::chain(format!("Failed to render single term {} page.", self.kind.name), e)
|
||||
})
|
||||
// Check for taxon-specific template, or use generic as fallback.
|
||||
let specific_template = format!("{}/single.html", self.kind.name);
|
||||
let template = match check_template_fallbacks(&specific_template, tera, &config.theme) {
|
||||
Some(template) => template,
|
||||
None => "taxonomy_single.html",
|
||||
};
|
||||
|
||||
render_template(&template, tera, context, &config.theme).map_err(|e| {
|
||||
Error::chain(format!("Failed to render single term {} page.", self.kind.name), e)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn render_all_terms(
|
||||
@ -224,10 +230,16 @@ impl Taxonomy {
|
||||
context.insert("current_url", &config.make_permalink(&self.kind.name));
|
||||
context.insert("current_path", &format!("/{}/", self.kind.name));
|
||||
|
||||
render_template(&format!("{}/list.html", self.kind.name), tera, context, &config.theme)
|
||||
.map_err(|e| {
|
||||
Error::chain(format!("Failed to render a list of {} page.", self.kind.name), e)
|
||||
})
|
||||
// Check for taxon-specific template, or use generic as fallback.
|
||||
let specific_template = format!("{}/list.html", self.kind.name);
|
||||
let template = match check_template_fallbacks(&specific_template, tera, &config.theme) {
|
||||
Some(template) => template,
|
||||
None => "taxonomy_list.html",
|
||||
};
|
||||
|
||||
render_template(&template, tera, context, &config.theme).map_err(|e| {
|
||||
Error::chain(format!("Failed to render a list of {} page.", self.kind.name), e)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_serialized<'a>(&'a self, library: &'a Library) -> SerializedTaxonomy<'a> {
|
||||
|
@ -847,7 +847,7 @@ impl Site {
|
||||
if taxonomy.kind.is_paginated() {
|
||||
self.render_paginated(
|
||||
comp.clone(),
|
||||
&Paginator::from_taxonomy(taxonomy, item, &library),
|
||||
&Paginator::from_taxonomy(taxonomy, item, &library, &self.tera, &self.config.theme),
|
||||
)?;
|
||||
} else {
|
||||
let single_output =
|
||||
|
@ -77,23 +77,8 @@ pub fn render_template(
|
||||
context: Context,
|
||||
theme: &Option<String>,
|
||||
) -> Result<String> {
|
||||
// check if it is in the templates
|
||||
if tera.templates.contains_key(name) {
|
||||
return tera.render(name, &context).map_err(std::convert::Into::into);
|
||||
}
|
||||
|
||||
// check if it is part of a theme
|
||||
if let Some(ref t) = *theme {
|
||||
let theme_template_name = format!("{}/templates/{}", t, name);
|
||||
if tera.templates.contains_key(&theme_template_name) {
|
||||
return tera.render(&theme_template_name, &context).map_err(std::convert::Into::into);
|
||||
}
|
||||
}
|
||||
|
||||
// check if it is part of ZOLA_TERA defaults
|
||||
let default_name = format!("__zola_builtins/{}", name);
|
||||
if tera.templates.contains_key(&default_name) {
|
||||
return tera.render(&default_name, &context).map_err(std::convert::Into::into);
|
||||
if let Some(template) = check_template_fallbacks(name, tera, theme) {
|
||||
return tera.render(&template, &context).map_err(std::convert::Into::into);
|
||||
}
|
||||
|
||||
// maybe it's a default one?
|
||||
@ -130,8 +115,40 @@ pub fn rewrite_theme_paths(tera_theme: &mut Tera, theme: &str) {
|
||||
tera_theme.templates.extend(new_templates);
|
||||
}
|
||||
|
||||
/// Checks for the presence of a given template. If none is found, also looks for a
|
||||
/// fallback in theme and default templates. Returns the path of the most specific
|
||||
/// template found, or none if none are present.
|
||||
pub fn check_template_fallbacks<'a>(
|
||||
name: &'a str,
|
||||
tera: &'a Tera,
|
||||
theme: &Option<String>,
|
||||
) -> Option<&'a str> {
|
||||
// check if it is in the templates
|
||||
if tera.templates.contains_key(name) {
|
||||
return Some(name);
|
||||
}
|
||||
|
||||
// check if it is part of a theme
|
||||
if let Some(ref t) = *theme {
|
||||
let theme_template_name = format!("{}/templates/{}", t, name);
|
||||
if let Some((key, _)) = tera.templates.get_key_value(&theme_template_name) {
|
||||
return Some(key);
|
||||
}
|
||||
}
|
||||
|
||||
// check if it is part of ZOLA_TERA defaults
|
||||
let default_name = format!("__zola_builtins/{}", name);
|
||||
if let Some((key, _)) = tera.templates.get_key_value(&default_name) {
|
||||
return Some(key);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::templates::check_template_fallbacks;
|
||||
|
||||
use super::rewrite_theme_paths;
|
||||
use tera::Tera;
|
||||
|
||||
@ -157,4 +174,23 @@ mod tests {
|
||||
Some("index.html".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn template_fallback_is_successful() {
|
||||
let mut tera = Tera::parse("test-templates/*.html").unwrap();
|
||||
tera.add_raw_template(&"hyde/templates/index.html", "Hello").unwrap();
|
||||
tera.add_raw_template(&"hyde/templates/theme-only.html", "Hello").unwrap();
|
||||
|
||||
// Check finding existing template
|
||||
assert_eq!(check_template_fallbacks("index.html", &tera, &None), Some("index.html"));
|
||||
|
||||
// Check trying to find non-existant template
|
||||
assert_eq!(check_template_fallbacks("not-here.html", &tera, &None), None);
|
||||
|
||||
// Check theme fallback
|
||||
assert_eq!(
|
||||
check_template_fallbacks("theme-only.html", &tera, &Some("hyde".to_string())),
|
||||
Some("hyde/templates/theme-only.html")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -3,11 +3,15 @@ title = "Taxonomies"
|
||||
weight = 40
|
||||
+++
|
||||
|
||||
Zola will look up the following files in the `templates` directory:
|
||||
Zola will look up the following, taxon-specific files in the `templates` directory:
|
||||
|
||||
- `$TAXONOMY_NAME/single.html`
|
||||
- `$TAXONOMY_NAME/list.html`
|
||||
|
||||
if they are not found, it will attempt to fall back on the following generic template files:
|
||||
- `taxonomy_single.html`
|
||||
- `taxonomy_list.html`
|
||||
|
||||
First, `TaxonomyTerm` has the following fields:
|
||||
|
||||
```ts
|
||||
|
Loading…
x
Reference in New Issue
Block a user