From 8d16d1701ce7a8ec38508326bd7889f2cd8dc202 Mon Sep 17 00:00:00 2001 From: Vincent Prouillet Date: Sun, 19 Mar 2023 20:37:13 +0100 Subject: [PATCH 1/8] Next version --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdb28810d3..9891777789 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 0.18.0 (unreleased) + ## 0.17.2 (2023-03-19) - Fix one more invalid error with colocated directories From b00ae4261bfb2d613583d51a1ce877b157c8740b Mon Sep 17 00:00:00 2001 From: Ever Date: Fri, 7 Apr 2023 03:28:20 +0800 Subject: [PATCH 2/8] print error message when no config file found (#2168) fixed #2195 --- src/main.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6f3fcdfeb7..983f7b2aa7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use std::path::{Path, PathBuf}; use std::time::Instant; use cli::{Cli, Command}; +use errors::anyhow; use utils::net::{get_available_port, port_is_available}; use clap::{CommandFactory, Parser}; @@ -13,10 +14,17 @@ mod messages; mod prompt; fn get_config_file_path(dir: &Path, config_path: &Path) -> (PathBuf, PathBuf) { - let root_dir = dir - .ancestors() - .find(|a| a.join(config_path).exists()) - .unwrap_or_else(|| panic!("could not find directory containing config file")); + let root_dir = dir.ancestors().find(|a| a.join(config_path).exists()).unwrap_or_else(|| { + messages::unravel_errors( + "", + &anyhow!( + "{} not found in current directory or ancestors, current_dir is {}", + config_path.display(), + dir.display() + ), + ); + std::process::exit(1); + }); // if we got here we found root_dir so config file should exist so we can unwrap safely let config_file = root_dir From 1ed722c076e801c7ec3d62209d40315b4353b811 Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 6 Apr 2023 23:21:01 +0200 Subject: [PATCH 3/8] Speedup "zola check" command by reusing the Client (#2171) * Reuse Client when checking urls and add timeout for requests --- components/link_checker/src/lib.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/components/link_checker/src/lib.rs b/components/link_checker/src/lib.rs index 3b3551afd6..ab44259217 100644 --- a/components/link_checker/src/lib.rs +++ b/components/link_checker/src/lib.rs @@ -30,6 +30,13 @@ pub fn message(res: &Result) -> String { // Keep history of link checks so a rebuild doesn't have to check again static LINKS: Lazy>>> = Lazy::new(|| Arc::new(RwLock::new(HashMap::new()))); +// Make sure to create only a single Client so that we can reuse the connections +static CLIENT: Lazy = Lazy::new(|| { + Client::builder() + .user_agent(concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"))) + .build() + .expect("reqwest client build") +}); pub fn check_url(url: &str, config: &LinkChecker) -> Result { { @@ -44,15 +51,11 @@ pub fn check_url(url: &str, config: &LinkChecker) -> Result { headers.append(ACCEPT, "*/*".parse().unwrap()); // TODO: pass the client to the check_url, do not pass the config - let client = Client::builder() - .user_agent(concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"))) - .build() - .expect("reqwest client build"); let check_anchor = !config.skip_anchor_prefixes.iter().any(|prefix| url.starts_with(prefix)); // Need to actually do the link checking - let res = match client.get(url).headers(headers).send() { + let res = match CLIENT.get(url).headers(headers).send() { Ok(ref mut response) if check_anchor && has_anchor(url) => { let body = { let mut buf: Vec = vec![]; From 6a5c241152fead78bc6f08db18739d4aa0070ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=8F=E2=80=8FDave?= <47663767+cydave@users.noreply.github.com> Date: Thu, 20 Apr 2023 17:44:19 +0000 Subject: [PATCH 4/8] Implement replace_re filter (#2163) * Implement replace_re filter * Cargo fmt * add regex caching * cargo fmt * update docs, update unit test * rename replace_re -> regex_replace --- components/templates/src/filters.rs | 70 ++++++++++++++++++- components/templates/src/lib.rs | 1 + .../documentation/templates/overview.md | 8 +++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/components/templates/src/filters.rs b/components/templates/src/filters.rs index b9b53d3767..432f2b3d22 100644 --- a/components/templates/src/filters.rs +++ b/components/templates/src/filters.rs @@ -1,9 +1,12 @@ use std::borrow::Cow; use std::collections::HashMap; use std::hash::BuildHasher; +use std::sync::{Arc, Mutex}; use config::Config; + use libs::base64::engine::{general_purpose::STANDARD as standard_b64, Engine}; +use libs::regex::Regex; use libs::tera::{ to_value, try_get_value, Error as TeraError, Filter as TeraFilter, Result as TeraResult, Tera, Value, @@ -78,6 +81,53 @@ pub fn base64_decode( Ok(to_value(as_str).unwrap()) } +#[derive(Debug)] +pub struct RegexReplaceFilter { + re_cache: Arc>>, +} + +impl RegexReplaceFilter { + pub fn new() -> Self { + return Self { re_cache: Arc::new(Mutex::new(HashMap::new())) }; + } +} + +impl TeraFilter for RegexReplaceFilter { + fn filter(&self, value: &Value, args: &HashMap) -> TeraResult { + let text = try_get_value!("regex_replace", "value", String, value); + let pattern = match args.get("pattern") { + Some(val) => try_get_value!("regex_replace", "pattern", String, val), + None => { + return Err(TeraError::msg( + "Filter `regex_replace` expected an arg called `pattern`", + )) + } + }; + let rep = match args.get("rep") { + Some(val) => try_get_value!("regex_replace", "rep", String, val), + None => { + return Err(TeraError::msg("Filter `regex_replace` expected an arg called `rep`")) + } + }; + + let mut cache = self.re_cache.lock().expect("re_cache lock"); + let replaced = { + match cache.get(&pattern) { + Some(pat) => pat.replace_all(&text, &rep), + None => { + let pat = Regex::new(&pattern) + .map_err(|e| format!("`regex_replace`: failed to compile regex: {}", e))?; + let replaced = pat.replace_all(&text, &rep); + cache.insert(pattern, pat); + replaced + } + } + }; + + Ok(to_value(replaced).unwrap()) + } +} + #[derive(Debug)] pub struct NumFormatFilter { default_language: String, @@ -114,7 +164,9 @@ mod tests { use libs::tera::{to_value, Filter, Tera}; - use super::{base64_decode, base64_encode, MarkdownFilter, NumFormatFilter}; + use super::{ + base64_decode, base64_encode, MarkdownFilter, NumFormatFilter, RegexReplaceFilter, + }; use config::Config; #[test] @@ -251,6 +303,22 @@ mod tests { } } + #[test] + fn regex_replace_filter() { + let value = "Springsteen, Bruce"; + let expected = "Bruce Springsteen"; + let pattern = r"(?P[^,\s]+),\s+(?P\S+)"; + let rep = "$first $last"; + let mut args = HashMap::new(); + args.insert("pattern".to_string(), to_value(pattern).unwrap()); + args.insert("rep".to_string(), to_value(rep).unwrap()); + let regex_replace = RegexReplaceFilter::new(); + let result = regex_replace.filter(&to_value(value).unwrap(), &args); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), to_value(expected).unwrap()); + assert!(regex_replace.re_cache.lock().unwrap().contains_key(pattern)); + } + #[test] fn num_format_filter() { let tests = vec![ diff --git a/components/templates/src/lib.rs b/components/templates/src/lib.rs index 6441ff4999..59ab2abaf9 100644 --- a/components/templates/src/lib.rs +++ b/components/templates/src/lib.rs @@ -28,6 +28,7 @@ pub static ZOLA_TERA: Lazy = Lazy::new(|| { .unwrap(); tera.register_filter("base64_encode", filters::base64_encode); tera.register_filter("base64_decode", filters::base64_decode); + tera.register_filter("regex_replace", filters::RegexReplaceFilter::new()); tera }); diff --git a/docs/content/documentation/templates/overview.md b/docs/content/documentation/templates/overview.md index 20c823070d..7eca41ecb1 100644 --- a/docs/content/documentation/templates/overview.md +++ b/docs/content/documentation/templates/overview.md @@ -86,6 +86,14 @@ Encode the variable to base64. ### base64_decode Decode the variable from base64. +### regex_replace +Replace text via regular expressions. + +```jinja2 +{{ "World Hello" | regex_replace(pattern=`(?P\w+), (?P\w+)`, rep=`$greeting $subject`) }} + +``` + ### num_format Format a number into its string representation. From c48b61ad72b73bd94e6c3a361a66a36900f40898 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Mon, 24 Apr 2023 05:56:30 +0800 Subject: [PATCH 5/8] templates: add base URL for feed content (#2190) Relative links in the entry content do not currently have a base URI, so will be resolved relative to the feed URI: Given an entry with the content: And URIS of: * entry: https://example.org/blog/some-entry/ * feed: https://example.org/atom.xml The link URI will end up as: https://example.org/some-resource.bin rather than the URI that ends up resolved in the rendered page: https://example.org/blog/some-entry/some-resource.bin The atom and RSS formats allow for an xml:base attribute (itself specified in [1]) to provide a base URI of a subset of a document. This change adds xml:base attributes to each entry, using the page permalink. This gives us something equivalent to: ]]> [1]: https://www.w3.org/TR/xmlbase/ Signed-off-by: Jeremy Kerr --- components/templates/src/builtins/atom.xml | 2 +- components/templates/src/builtins/rss.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/templates/src/builtins/atom.xml b/components/templates/src/builtins/atom.xml index 8b2d331e5c..2f16095563 100644 --- a/components/templates/src/builtins/atom.xml +++ b/components/templates/src/builtins/atom.xml @@ -40,7 +40,7 @@ {% if page.summary %} {{ page.summary }} {% else %} - {{ page.content }} + {{ page.content }} {% endif %} {%- endfor %} diff --git a/components/templates/src/builtins/rss.xml b/components/templates/src/builtins/rss.xml index 1c09e4cf4d..a684a48af9 100644 --- a/components/templates/src/builtins/rss.xml +++ b/components/templates/src/builtins/rss.xml @@ -33,7 +33,7 @@ {{ page.permalink | escape_xml | safe }} {{ page.permalink | escape_xml | safe }} - {% if page.summary %}{{ page.summary }}{% else %}{{ page.content }}{% endif %} + {% if page.summary %}{{ page.summary }}{% else %}{{ page.content }}{% endif %} {%- endfor %} From 332cf15804532671cb5649724669e11135748e5f Mon Sep 17 00:00:00 2001 From: Andrew Langmeier Date: Mon, 1 May 2023 16:10:35 -0400 Subject: [PATCH 6/8] Fixes #2135: Warn users about default template usage --- components/site/src/lib.rs | 36 ++++++++++++++++++++++++++++++- components/utils/src/templates.rs | 10 +++++++++ src/cmd/build.rs | 1 + src/cmd/check.rs | 1 + src/cmd/serve.rs | 1 + src/messages.rs | 11 +++++++++- 6 files changed, 58 insertions(+), 2 deletions(-) diff --git a/components/site/src/lib.rs b/components/site/src/lib.rs index b40e0b5c76..ddbb1c8ea6 100644 --- a/components/site/src/lib.rs +++ b/components/site/src/lib.rs @@ -26,7 +26,7 @@ use utils::fs::{ ensure_directory_exists, }; use utils::net::{get_available_port, is_external_link}; -use utils::templates::{render_template, ShortcodeDefinition}; +use utils::templates::{is_default_template, render_template, ShortcodeDefinition}; use utils::types::InsertAnchor; pub static SITE_CONTENT: Lazy>>> = @@ -362,6 +362,40 @@ impl Site { Ok(()) } + /// Gather all sites and pages which were rendered with a default template. + /// We can later warn the user about possible unexpected defaults. + pub fn get_default_templates(&self) -> Result> { + let lib = self.library.read().unwrap(); + let pages = &lib.pages; + let sections = &lib.sections; + let mut result: Vec = Vec::with_capacity(pages.len() + sections.len()); + for (path, page) in pages.iter() { + let name; + let default_name = "page.html".to_owned(); + if let Some(template) = &page.meta.template { + name = template; + } else { + name = &default_name; + } + if is_default_template(&name, &self.tera, &self.config.theme)? { + result.push(path.display().to_string()); + } + } + for (path, section) in sections.iter() { + let name; + let default_name = "section.html".to_owned(); + if let Some(template) = §ion.meta.template { + name = template; + } else { + name = &default_name; + } + if is_default_template(&name, &self.tera, &self.config.theme)? { + result.push(path.display().to_string()); + } + } + Ok(result) + } + /// Insert a default index section for each language if necessary so we don't need to create /// a _index.md to render the index page at the root of the site pub fn create_default_index_sections(&mut self) -> Result<()> { diff --git a/components/utils/src/templates.rs b/components/utils/src/templates.rs index b51206bac5..1ee41de236 100644 --- a/components/utils/src/templates.rs +++ b/components/utils/src/templates.rs @@ -100,6 +100,16 @@ pub fn render_template( } } +pub fn is_default_template(name: &str, tera: &Tera, theme: &Option) -> Result { + if let Some(_) = check_template_fallbacks(name, tera, theme) { + return Ok(false); + } + match name { + "index.html" | "section.html" | "page.html" | "single.html" | "list.html" => Ok(true), + _ => bail!("Template not found for {}", name), + } +} + /// 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 diff --git a/src/cmd/build.rs b/src/cmd/build.rs index d088743687..f4bcb298fa 100644 --- a/src/cmd/build.rs +++ b/src/cmd/build.rs @@ -33,5 +33,6 @@ pub fn build( site.load()?; messages::notify_site_size(&site); messages::warn_about_ignored_pages(&site); + messages::warn_about_default_templates(&site)?; site.build() } diff --git a/src/cmd/check.rs b/src/cmd/check.rs index 46522deab9..443f6e9d7d 100644 --- a/src/cmd/check.rs +++ b/src/cmd/check.rs @@ -25,5 +25,6 @@ pub fn check( site.load()?; messages::check_site_summary(&site); messages::warn_about_ignored_pages(&site); + messages::warn_about_default_templates(&site)?; Ok(()) } diff --git a/src/cmd/serve.rs b/src/cmd/serve.rs index 52b1d585b9..4872a23844 100644 --- a/src/cmd/serve.rs +++ b/src/cmd/serve.rs @@ -281,6 +281,7 @@ fn create_new_site( } messages::notify_site_size(&site); messages::warn_about_ignored_pages(&site); + messages::warn_about_default_templates(&site)?; site.build()?; Ok((site, address)) } diff --git a/src/messages.rs b/src/messages.rs index 1ad4d7d410..2c1e0fdebd 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -2,7 +2,7 @@ use libs::time::Duration; use std::convert::TryInto; use std::time::Instant; -use errors::Error; +use errors::{Error, Result}; use site::Site; /// Display in the console the number of pages/sections in the site @@ -52,6 +52,15 @@ pub fn warn_about_ignored_pages(site: &Site) { } } +/// Display a warning in the console if there are default templates rendered +pub fn warn_about_default_templates(site: &Site) -> Result<()> { + let default_templates = site.get_default_templates()?; + for path_string in default_templates.iter() { + console::warn(&format!("- {} is using the default template", path_string)); + } + Ok(()) +} + /// Print the time elapsed rounded to 1 decimal pub fn report_elapsed_time(instant: Instant) { let duration: Duration = instant.elapsed().try_into().unwrap(); From eb7cfdb1741fa0ac9f1ecec1371fda08f995c962 Mon Sep 17 00:00:00 2001 From: Andrew Langmeier Date: Mon, 1 May 2023 16:26:52 -0400 Subject: [PATCH 7/8] clippy --- components/site/src/lib.rs | 4 ++-- components/utils/src/templates.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/site/src/lib.rs b/components/site/src/lib.rs index ddbb1c8ea6..c100798fb1 100644 --- a/components/site/src/lib.rs +++ b/components/site/src/lib.rs @@ -377,7 +377,7 @@ impl Site { } else { name = &default_name; } - if is_default_template(&name, &self.tera, &self.config.theme)? { + if is_default_template(name, &self.tera, &self.config.theme)? { result.push(path.display().to_string()); } } @@ -389,7 +389,7 @@ impl Site { } else { name = &default_name; } - if is_default_template(&name, &self.tera, &self.config.theme)? { + if is_default_template(name, &self.tera, &self.config.theme)? { result.push(path.display().to_string()); } } diff --git a/components/utils/src/templates.rs b/components/utils/src/templates.rs index 1ee41de236..79ec89601b 100644 --- a/components/utils/src/templates.rs +++ b/components/utils/src/templates.rs @@ -101,7 +101,7 @@ pub fn render_template( } pub fn is_default_template(name: &str, tera: &Tera, theme: &Option) -> Result { - if let Some(_) = check_template_fallbacks(name, tera, theme) { + if check_template_fallbacks(name, tera, theme).is_some() { return Ok(false); } match name { From 10123404affa5dadd0e724fe88ddb4253ceef7a4 Mon Sep 17 00:00:00 2001 From: Andrew Langmeier Date: Mon, 1 May 2023 17:18:56 -0400 Subject: [PATCH 8/8] cargo fmt --- components/utils/src/templates.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/utils/src/templates.rs b/components/utils/src/templates.rs index 79ec89601b..90ef6921a0 100644 --- a/components/utils/src/templates.rs +++ b/components/utils/src/templates.rs @@ -101,7 +101,7 @@ pub fn render_template( } pub fn is_default_template(name: &str, tera: &Tera, theme: &Option) -> Result { - if check_template_fallbacks(name, tera, theme).is_some() { + if check_template_fallbacks(name, tera, theme).is_some() { return Ok(false); } match name {