You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

140 lines
5.3KB

  1. use std::collections::HashMap;
  2. use tera::{Context, Tera};
  3. use errors::{bail, Result};
  4. static DEFAULT_TPL: &str = include_str!("default_tpl.html");
  5. macro_rules! render_default_tpl {
  6. ($filename: expr, $url: expr) => {{
  7. let mut context = Context::new();
  8. context.insert("filename", $filename);
  9. context.insert("url", $url);
  10. Tera::one_off(DEFAULT_TPL, &context, true).map_err(std::convert::Into::into)
  11. }};
  12. }
  13. /// Renders the given template with the given context, but also ensures that, if the default file
  14. /// is not found, it will look up for the equivalent template for the current theme if there is one.
  15. /// Lastly, if it's a default template (index, section or page), it will just return an empty string
  16. /// to avoid an error if there isn't a template with that name
  17. pub fn render_template(
  18. name: &str,
  19. tera: &Tera,
  20. context: Context,
  21. theme: &Option<String>,
  22. ) -> Result<String> {
  23. // check if it is in the templates
  24. if tera.templates.contains_key(name) {
  25. return tera.render(name, &context).map_err(std::convert::Into::into);
  26. }
  27. // check if it is part of a theme
  28. if let Some(ref t) = *theme {
  29. let theme_template_name = format!("{}/templates/{}", t, name);
  30. if tera.templates.contains_key(&theme_template_name) {
  31. return tera.render(&theme_template_name, &context).map_err(std::convert::Into::into);
  32. }
  33. }
  34. // check if it is part of ZOLA_TERA defaults
  35. let default_name = format!("__zola_builtins/{}", name);
  36. if tera.templates.contains_key(&default_name) {
  37. return tera.render(&default_name, &context).map_err(std::convert::Into::into);
  38. }
  39. // maybe it's a default one?
  40. match name {
  41. "index.html" | "section.html" => render_default_tpl!(
  42. name,
  43. "https://www.getzola.org/documentation/templates/pages-sections/#section-variables"
  44. ),
  45. "page.html" => render_default_tpl!(
  46. name,
  47. "https://www.getzola.org/documentation/templates/pages-sections/#page-variables"
  48. ),
  49. "single.html" | "list.html" => {
  50. render_default_tpl!(name, "https://www.getzola.org/documentation/templates/taxonomies/")
  51. }
  52. _ => bail!("Tried to render `{}` but the template wasn't found", name),
  53. }
  54. }
  55. /// Rewrites the path from extend/macros of the theme used to ensure
  56. /// that they will point to the right place (theme/templates/...)
  57. /// Include is NOT supported as it would be a pain to add and using blocks
  58. /// or macros is always better anyway for themes
  59. /// This will also rename the shortcodes to NOT have the themes in the path
  60. /// so themes shortcodes can be used.
  61. pub fn rewrite_theme_paths(tera_theme: &mut Tera, site_templates: Vec<&str>, theme: &str) {
  62. let mut shortcodes_to_move = vec![];
  63. let mut templates = HashMap::new();
  64. let old_templates = ::std::mem::replace(&mut tera_theme.templates, HashMap::new());
  65. // We want to match the paths in the templates to the new names
  66. for (key, mut tpl) in old_templates {
  67. tpl.name = format!("{}/templates/{}", theme, tpl.name);
  68. // First the parent if there is one
  69. // If a template with the same name is also in site, assumes it overrides the theme one
  70. // and do not change anything
  71. if let Some(ref p) = tpl.parent.clone() {
  72. if !site_templates.contains(&p.as_ref()) {
  73. tpl.parent = Some(format!("{}/templates/{}", theme, p));
  74. }
  75. }
  76. // Next the macros import
  77. let mut updated = vec![];
  78. for &(ref filename, ref namespace) in &tpl.imported_macro_files {
  79. updated.push((format!("{}/templates/{}", theme, filename), namespace.to_string()));
  80. }
  81. tpl.imported_macro_files = updated;
  82. if tpl.name.starts_with(&format!("{}/templates/shortcodes", theme)) {
  83. let new_name = tpl.name.replace(&format!("{}/templates/", theme), "");
  84. shortcodes_to_move.push((key, new_name.clone()));
  85. tpl.name = new_name;
  86. }
  87. templates.insert(tpl.name.clone(), tpl);
  88. }
  89. tera_theme.templates = templates;
  90. // and then replace shortcodes in the Tera instance using the new names
  91. for (old_name, new_name) in shortcodes_to_move {
  92. let tpl = tera_theme.templates.remove(&old_name).unwrap();
  93. tera_theme.templates.insert(new_name, tpl);
  94. }
  95. }
  96. #[cfg(test)]
  97. mod tests {
  98. use super::rewrite_theme_paths;
  99. use tera::Tera;
  100. #[test]
  101. fn can_rewrite_all_paths_of_theme() {
  102. let mut tera = Tera::parse("test-templates/*.html").unwrap();
  103. rewrite_theme_paths(&mut tera, vec!["base.html"], "hyde");
  104. // special case to make the test work: we also rename the files to
  105. // match the imports
  106. for (key, val) in &tera.templates.clone() {
  107. tera.templates.insert(format!("hyde/templates/{}", key), val.clone());
  108. }
  109. // Adding our fake base
  110. tera.add_raw_template("base.html", "Hello").unwrap();
  111. tera.build_inheritance_chains().unwrap();
  112. assert_eq!(
  113. tera.templates["hyde/templates/index.html"].parent,
  114. Some("base.html".to_string())
  115. );
  116. assert_eq!(
  117. tera.templates["hyde/templates/child.html"].parent,
  118. Some("hyde/templates/index.html".to_string())
  119. );
  120. }
  121. }