diff --git a/components/site/src/lib.rs b/components/site/src/lib.rs index a18b847b..a6cf93d3 100644 --- a/components/site/src/lib.rs +++ b/components/site/src/lib.rs @@ -91,11 +91,7 @@ impl Site { ); let mut tera_theme = Tera::parse(&theme_tpl_glob) .map_err(|e| Error::chain("Error parsing templates from themes", e))?; - rewrite_theme_paths( - &mut tera_theme, - tera.templates.values().map(|v| v.name.as_ref()).collect(), - &theme, - ); + rewrite_theme_paths(&mut tera_theme, &theme); // TODO: we do that twice, make it dry? if theme_path.join("templates").join("robots.txt").exists() { tera_theme diff --git a/components/utils/src/templates.rs b/components/utils/src/templates.rs index 5c25d255..aa2ed463 100644 --- a/components/utils/src/templates.rs +++ b/components/utils/src/templates.rs @@ -61,52 +61,21 @@ pub fn render_template( } } -/// Rewrites the path from extend/macros of the theme used to ensure -/// that they will point to the right place (theme/templates/...) -/// Include is NOT supported as it would be a pain to add and using blocks -/// or macros is always better anyway for themes -/// This will also rename the shortcodes to NOT have the themes in the path -/// so themes shortcodes can be used. -pub fn rewrite_theme_paths(tera_theme: &mut Tera, site_templates: Vec<&str>, theme: &str) { - let mut shortcodes_to_move = vec![]; - let mut templates = HashMap::new(); - let old_templates = ::std::mem::replace(&mut tera_theme.templates, HashMap::new()); - - // We want to match the paths in the templates to the new names - for (key, mut tpl) in old_templates { - tpl.name = format!("{}/templates/{}", theme, tpl.name); - // First the parent if there is one - // If a template with the same name is also in site, assumes it overrides the theme one - // and do not change anything - if let Some(ref p) = tpl.parent.clone() { - if !site_templates.contains(&p.as_ref()) { - tpl.parent = Some(format!("{}/templates/{}", theme, p)); - } - } - - // Next the macros import - let mut updated = vec![]; - for &(ref filename, ref namespace) in &tpl.imported_macro_files { - updated.push((format!("{}/templates/{}", theme, filename), namespace.to_string())); - } - tpl.imported_macro_files = updated; - - if tpl.name.starts_with(&format!("{}/templates/shortcodes", theme)) { - let new_name = tpl.name.replace(&format!("{}/templates/", theme), ""); - shortcodes_to_move.push((key, new_name.clone())); - tpl.name = new_name; - } - - templates.insert(tpl.name.clone(), tpl); - } - - tera_theme.templates = templates; - - // and then replace shortcodes in the Tera instance using the new names - for (old_name, new_name) in shortcodes_to_move { - let tpl = tera_theme.templates.remove(&old_name).unwrap(); - tera_theme.templates.insert(new_name, tpl); +/// Rewrites the path of duplicate templates to include the complete theme path +/// Theme templates will be injected into site templates, with higher priority for site +/// templates. To keep a copy of the template in case it's being extended from a site template +/// of the same name, we reinsert it with the theme path prepended +pub fn rewrite_theme_paths(tera_theme: &mut Tera, theme: &str) { + let theme_basepath = format!("{}/templates/", theme); + let mut new_templates = HashMap::new(); + for (key, template) in &tera_theme.templates { + let mut tpl = template.clone(); + tpl.name = format!("{}{}", theme_basepath, key); + new_templates.insert(tpl.name.clone(), tpl); } + // Contrary to tera.extend, hashmap.extend does replace existing keys + // We can safely extend because there's no conflicting paths anymore + tera_theme.templates.extend(new_templates); } #[cfg(test)] @@ -117,7 +86,7 @@ mod tests { #[test] fn can_rewrite_all_paths_of_theme() { let mut tera = Tera::parse("test-templates/*.html").unwrap(); - rewrite_theme_paths(&mut tera, vec!["base.html"], "hyde"); + rewrite_theme_paths(&mut tera, "hyde"); // special case to make the test work: we also rename the files to // match the imports for (key, val) in &tera.templates.clone() { @@ -133,7 +102,7 @@ mod tests { ); assert_eq!( tera.templates["hyde/templates/child.html"].parent, - Some("hyde/templates/index.html".to_string()) + Some("index.html".to_string()) ); } } diff --git a/docs/content/documentation/themes/creating-a-theme.md b/docs/content/documentation/themes/creating-a-theme.md index 05b93689..8f4f9c3e 100644 --- a/docs/content/documentation/themes/creating-a-theme.md +++ b/docs/content/documentation/themes/creating-a-theme.md @@ -51,11 +51,6 @@ theme, with live reload working as expected. Make sure to commit every directory (including `content`) in order for other people to be able to build the theme from your repository. -### Caveat - -Please note that [include paths](https://tera.netlify.com/docs#include) can only be used in normal templates. -Theme templates should use [macros](https://tera.netlify.com/docs#macros) instead. - ## Submitting a theme to the gallery If you want your theme to be featured in the [themes](@/themes/_index.md) section diff --git a/docs/content/documentation/themes/extending-a-theme.md b/docs/content/documentation/themes/extending-a-theme.md new file mode 100644 index 00000000..d403427c --- /dev/null +++ b/docs/content/documentation/themes/extending-a-theme.md @@ -0,0 +1,21 @@ ++++ +title = "Extending a theme" +weight = 30 ++++ + +When your site uses a theme, you can replace parts of it in your site's templates folder. For any given theme template, you can either override a single block in it, or replace the whole template. If a site template and a theme template collide, the site template will be given priority. Whether a theme template collides or not, theme templates remain accessible from any template within `theme_name/templates/`. + +## Replacing a template + +When a site template and a theme template have the same path, for example `templates/page.html` and `themes/theme_name/templates/page.html`, the site template is the one that will be used. This is how you can replace a whole template for a theme. + +## Overriding a block + +If you don't want to replace a whole template, but override parts of it, you can [extend the template](https://tera.netlify.app/docs/#inheritance) and redefine some specific blocks. For example, if you want to override the `title` block in your theme's page.html, you can create a page.html file in your site templates with the following content: + +``` +{% extends "theme_name/templates/page.html" %} +{% block title %}{{ page.title }}{% endblock %} +``` + +If you extend `page.html` and not `theme_name/templates/page.html` specifically, it will extend the site's page template if it exists, and the theme's page template otherwise. This makes it possible to override your theme's base template(s) from your site templates, as long as the theme templates do not hardcode the theme name in template paths. For instance, children templates in the theme should use `{% extends 'index.html' %}`, not `{% extends 'theme_name/templates/index.html' %}`. diff --git a/test_site/templates/index.html b/test_site/templates/index.html index 14bc3260..b991c0e5 100644 --- a/test_site/templates/index.html +++ b/test_site/templates/index.html @@ -1,29 +1,11 @@ - - - - - - - - - - - {{ config.title }} - - -
- {% block content %} -
- {% for page in section.pages %} - - {% endfor %} -
- {% endblock content %} +{% extends 'sample/templates/index.html' %} +{% block content %} +
+ {% for page in section.pages %} + + {% endfor %}
- - - +{% endblock content %} diff --git a/test_site/templates/section.html b/test_site/templates/section.html index 724795a6..3a262d3b 100644 --- a/test_site/templates/section.html +++ b/test_site/templates/section.html @@ -1,13 +1,7 @@ -{% extends "index.html" %} - +{% extends "sample/templates/section-specific-extends.html" %} {% block content %} - {% for page in section.pages %} + {% for page in section.pages %} {{page.title}} {% endfor %} - {{ section.relative_path | safe }} - {% for sub in section.subsections %} - {% set subsection = get_section(path=sub) %} - {{subsection.title}} - Sub-pages: {{subsection.pages | length}} - {% endfor %} + {{ super() }} {% endblock content %} diff --git a/test_site/templates/shortcodes/pirate.html b/test_site/templates/shortcodes/pirate_included.html similarity index 100% rename from test_site/templates/shortcodes/pirate.html rename to test_site/templates/shortcodes/pirate_included.html diff --git a/test_site/themes/sample/templates/index.html b/test_site/themes/sample/templates/index.html index e965047a..73158127 100644 --- a/test_site/themes/sample/templates/index.html +++ b/test_site/themes/sample/templates/index.html @@ -1 +1,19 @@ -Hello + + + + + + + + + + + {{ config.title }} + + + +
+ {% block content %}Hello{% endblock content %} +
+ + diff --git a/test_site/themes/sample/templates/section-specific-extends.html b/test_site/themes/sample/templates/section-specific-extends.html new file mode 100644 index 00000000..7124bf1a --- /dev/null +++ b/test_site/themes/sample/templates/section-specific-extends.html @@ -0,0 +1,9 @@ +{% extends "index.html" %} +{% block content %} + {{ section.relative_path | safe }} + {% for sub in section.subsections %} + {% set subsection = get_section(path=sub) %} + {{subsection.title}} + Sub-pages: {{subsection.pages | length}} + {% endfor %} +{% endblock content %} diff --git a/test_site/themes/sample/templates/section.html b/test_site/themes/sample/templates/section.html new file mode 100644 index 00000000..c206205c --- /dev/null +++ b/test_site/themes/sample/templates/section.html @@ -0,0 +1,4 @@ +{% extends 'index.html' %} +{% block content %} +I'm overriden in all cases so seeing me is a bug +{% endblock %} diff --git a/test_site/themes/sample/templates/shortcodes/pirate.html b/test_site/themes/sample/templates/shortcodes/pirate.html new file mode 100644 index 00000000..7c5326b2 --- /dev/null +++ b/test_site/themes/sample/templates/shortcodes/pirate.html @@ -0,0 +1 @@ +{% include 'shortcodes/pirate_included.html' %} diff --git a/test_site/themes/sample/templates/shortcodes/pirate_included.html b/test_site/themes/sample/templates/shortcodes/pirate_included.html new file mode 100644 index 00000000..2e5b9387 --- /dev/null +++ b/test_site/themes/sample/templates/shortcodes/pirate_included.html @@ -0,0 +1 @@ +SHOULDNOTAPPEAR: overriden by site shortcode