diff --git a/components/content/src/file_info.rs b/components/content/src/file_info.rs index a94cb4310..fd58f21d7 100644 --- a/components/content/src/file_info.rs +++ b/components/content/src/file_info.rs @@ -109,6 +109,18 @@ impl FileInfo { format!("{}.md", name) }; let grand_parent = parent.parent().map(|p| p.to_path_buf()); + let mut colocated_path = None; + + // If we have a folder with an asset, don't consider it as a component + // Splitting on `.` as we might have a language so it isn't *only* index but also index.fr + // etc + if !components.is_empty() && name.split('.').collect::>()[0] == "_index" { + colocated_path = Some({ + let mut val = components.join("/"); + val.push('/'); + val + }); + } FileInfo { filename: file_path.file_name().unwrap().to_string_lossy().to_string(), @@ -119,7 +131,7 @@ impl FileInfo { name, components, relative, - colocated_path: None, + colocated_path: colocated_path, } } @@ -289,4 +301,41 @@ mod tests { Path::new("/home/vincent/code/site/content/posts/tutorials/python/index") ); } + + #[test] + fn correct_colocated_path() { + struct Test<'a> { + file_info: FileInfo, + expected_colocated_path: &'a str, + } + + // A colocated path: + // - MUST NOT start with a '/' + // - MUST end with a '/' + // Breaking those assumptions may have uncontrolled side effects in some other code, including but not limited to assets permalinks generation. + let tests = vec![ + Test { + file_info: FileInfo::new_page( + Path::new("/home/vincent/code/site/content/posts/tutorials/python/index.md"), + &PathBuf::new(), + ), + expected_colocated_path: "posts/tutorials/python/", + }, + Test { + file_info: FileInfo::new_section( + Path::new("/home/vincent/code/site/content/posts/tutorials/_index.fr.md"), + &PathBuf::new(), + ), + expected_colocated_path: "posts/tutorials/", + }, + ]; + + for test in tests { + assert!(test.file_info.colocated_path.is_some()); + assert_eq!( + test.file_info.colocated_path.as_ref().unwrap(), + test.expected_colocated_path + ) + } + } } diff --git a/components/content/src/page.rs b/components/content/src/page.rs index 041ab981d..71fed7153 100644 --- a/components/content/src/page.rs +++ b/components/content/src/page.rs @@ -19,7 +19,7 @@ use crate::front_matter::{split_page_content, PageFrontMatter}; use crate::library::Library; use crate::ser::SerializingPage; use crate::utils::get_reading_analytics; -use crate::utils::{find_related_assets, has_anchor}; +use crate::utils::{find_related_assets, get_assets_permalinks, has_anchor, serialize_assets}; use utils::anchors::has_anchor_id; use utils::fs::read_file; @@ -45,6 +45,8 @@ pub struct Page { pub assets: Vec, /// All the non-md files we found next to the .md file pub serialized_assets: Vec, + /// The permalinks of all the non-md files we found next to the .md file + pub assets_permalinks: HashMap, /// The HTML rendered of the page pub content: String, /// The slug of that page. @@ -195,7 +197,20 @@ impl Page { if page.file.name == "index" { let parent_dir = path.parent().unwrap(); page.assets = find_related_assets(parent_dir, config, true); - page.serialized_assets = page.serialize_assets(base_path); + if !page.assets.is_empty() { + let colocated_path = page + .file + .colocated_path + .as_ref() + .expect("Should have colocated path for assets"); + page.serialized_assets = serialize_assets( + &page.assets, + page.file.path.parent().unwrap(), + colocated_path, + ); + page.assets_permalinks = + get_assets_permalinks(&page.serialized_assets, &page.permalink, colocated_path); + } } else { page.assets = vec![]; } @@ -255,28 +270,6 @@ impl Page { .with_context(|| format!("Failed to render page '{}'", self.file.path.display())) } - /// Creates a vectors of asset URLs. - fn serialize_assets(&self, base_path: &Path) -> Vec { - self.assets - .iter() - .filter_map(|asset| asset.strip_prefix(self.file.path.parent().unwrap()).ok()) - .filter_map(|filename| filename.to_str()) - .map(|filename| { - let mut path = self.file.path.clone(); - // Popping the index.md from the path since file.parent would be one level too high - // for our need here - path.pop(); - path.push(filename); - path = path - .strip_prefix(&base_path.join("content")) - .expect("Should be able to stripe prefix") - .to_path_buf(); - path - }) - .map(|path| format!("/{}", path.display())) - .collect() - } - pub fn has_anchor(&self, anchor: &str) -> bool { has_anchor(&self.toc, anchor) } @@ -589,8 +582,19 @@ And here's another. [^3] assert_eq!(page.file.parent, path.join("content").join("posts")); assert_eq!(page.slug, "with-assets"); assert_eq!(page.assets.len(), 3); + assert_eq!(page.serialized_assets.len(), 3); assert!(page.serialized_assets[0].starts_with('/')); assert_eq!(page.permalink, "http://a-website.com/posts/with-assets/"); + assert_eq!(page.assets_permalinks.len(), 3); + let random_assets_permalinks_key = + page.assets_permalinks.keys().next().expect("assets permalinks key should be present"); + assert!(!random_assets_permalinks_key.starts_with('/')); + let random_assets_permalinks_value = page + .assets_permalinks + .values() + .next() + .expect("assets permalinks value should be present"); + assert!(random_assets_permalinks_value.starts_with(&page.permalink)); } #[test] @@ -614,6 +618,13 @@ And here's another. [^3] assert_eq!(page.slug, "hey"); assert_eq!(page.assets.len(), 3); assert_eq!(page.permalink, "http://a-website.com/posts/hey/"); + assert_eq!(page.assets_permalinks.len(), 3); + let random_assets_permalinks_value = page + .assets_permalinks + .values() + .next() + .expect("assets permalinks value should be present"); + assert!(random_assets_permalinks_value.starts_with(&page.permalink)); } // https://github.com/getzola/zola/issues/674 @@ -640,6 +651,17 @@ And here's another. [^3] // We should not get with-assets since that's the slugified version assert!(page.serialized_assets[0].contains("with_assets")); assert_eq!(page.permalink, "http://a-website.com/posts/with-assets/"); + assert_eq!(page.assets_permalinks.len(), 3); + let random_assets_permalinks_key = + page.assets_permalinks.keys().next().expect("assets permalinks key should be present"); + // We should not get with-assets since that's the slugified version + assert!(random_assets_permalinks_key.contains("with_assets")); + let random_assets_permalinks_value = page + .assets_permalinks + .values() + .next() + .expect("assets permalinks value should be present"); + assert!(random_assets_permalinks_value.starts_with(&page.permalink)); } // https://github.com/getzola/zola/issues/607 @@ -664,7 +686,21 @@ And here's another. [^3] assert_eq!(page.slug, "with-assets"); assert_eq!(page.meta.date, Some("2013-06-02".to_string())); assert_eq!(page.assets.len(), 3); + assert_eq!(page.serialized_assets.len(), 3); + // We should not get with-assets since that's the slugified version + assert!(page.serialized_assets[0].contains("2013-06-02")); assert_eq!(page.permalink, "http://a-website.com/posts/with-assets/"); + assert_eq!(page.assets_permalinks.len(), 3); + let random_assets_permalinks_key = + page.assets_permalinks.keys().next().expect("assets permalinks key should be present"); + // We should not get with-assets since that's the slugified version + assert!(random_assets_permalinks_key.contains("2013-06-02")); + let random_assets_permalinks_value = page + .assets_permalinks + .values() + .next() + .expect("assets permalinks value should be present"); + assert!(random_assets_permalinks_value.starts_with(&page.permalink)); } #[test] @@ -692,6 +728,8 @@ And here's another. [^3] let page = res.unwrap(); assert_eq!(page.assets.len(), 1); assert_eq!(page.assets[0].file_name().unwrap().to_str(), Some("graph.jpg")); + assert_eq!(page.serialized_assets.len(), 1); + assert_eq!(page.assets_permalinks.len(), 1); } // https://github.com/getzola/zola/issues/1566 diff --git a/components/content/src/section.rs b/components/content/src/section.rs index d4c84a9bd..b4ac035e0 100644 --- a/components/content/src/section.rs +++ b/components/content/src/section.rs @@ -15,7 +15,9 @@ use crate::file_info::FileInfo; use crate::front_matter::{split_section_content, SectionFrontMatter}; use crate::library::Library; use crate::ser::{SectionSerMode, SerializingSection}; -use crate::utils::{find_related_assets, get_reading_analytics, has_anchor}; +use crate::utils::{ + find_related_assets, get_assets_permalinks, get_reading_analytics, has_anchor, serialize_assets, +}; // Default is used to create a default index section if there is no _index.md in the root content directory #[derive(Clone, Debug, Default, PartialEq, Eq)] @@ -38,6 +40,8 @@ pub struct Section { pub assets: Vec, /// All the non-md files we found next to the .md file as string pub serialized_assets: Vec, + /// The permalinks of all the non-md files we found next to the .md file + pub assets_permalinks: HashMap, /// All direct pages of that section pub pages: Vec, /// All pages that cannot be sorted in this section @@ -125,7 +129,23 @@ impl Section { let parent_dir = path.parent().unwrap(); section.assets = find_related_assets(parent_dir, config, false); - section.serialized_assets = section.serialize_assets(); + if !section.assets.is_empty() { + let colocated_path = section + .file + .colocated_path + .as_ref() + .expect("Should have colocated path for assets"); + section.serialized_assets = serialize_assets( + §ion.assets, + section.file.path.parent().unwrap(), + colocated_path, + ); + section.assets_permalinks = get_assets_permalinks( + §ion.serialized_assets, + §ion.permalink, + colocated_path, + ); + } Ok(section) } @@ -202,16 +222,6 @@ impl Section { self.file.components.is_empty() } - /// Creates a vectors of asset URLs. - fn serialize_assets(&self) -> Vec { - self.assets - .iter() - .filter_map(|asset| asset.strip_prefix(self.file.path.parent().unwrap()).ok()) - .filter_map(|filename| filename.to_str()) - .map(|filename| format!("{}{}", self.path, filename)) - .collect() - } - pub fn has_anchor(&self, anchor: &str) -> bool { has_anchor(&self.toc, anchor) } @@ -269,8 +279,22 @@ mod tests { assert!(res.is_ok()); let section = res.unwrap(); assert_eq!(section.assets.len(), 3); + assert_eq!(section.serialized_assets.len(), 3); assert!(section.serialized_assets[0].starts_with('/')); assert_eq!(section.permalink, "http://a-website.com/posts/with-assets/"); + assert_eq!(section.assets_permalinks.len(), 3); + let random_assets_permalinks_key = section + .assets_permalinks + .keys() + .next() + .expect("assets permalinks key should be present"); + assert!(!random_assets_permalinks_key.starts_with('/')); + let random_assets_permalinks_value = section + .assets_permalinks + .values() + .next() + .expect("assets permalinks value should be present"); + assert!(random_assets_permalinks_value.starts_with(§ion.permalink)); } #[test] @@ -300,9 +324,11 @@ mod tests { Section::from_file(article_path.join("_index.md").as_path(), &config, &PathBuf::new()); assert!(res.is_ok()); - let page = res.unwrap(); - assert_eq!(page.assets.len(), 1); - assert_eq!(page.assets[0].file_name().unwrap().to_str(), Some("graph.jpg")); + let section = res.unwrap(); + assert_eq!(section.assets.len(), 1); + assert_eq!(section.assets[0].file_name().unwrap().to_str(), Some("graph.jpg")); + assert_eq!(section.serialized_assets.len(), 1); + assert_eq!(section.assets_permalinks.len(), 1); } #[test] diff --git a/components/content/src/utils.rs b/components/content/src/utils.rs index e26998862..9952f259b 100644 --- a/components/content/src/utils.rs +++ b/components/content/src/utils.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::path::{Path, PathBuf}; use libs::unicode_segmentation::UnicodeSegmentation; @@ -58,6 +59,47 @@ pub fn find_related_assets(path: &Path, config: &Config, recursive: bool) -> Vec assets } +/// Serializes assets source path for assets colocated with a section or a page +pub fn serialize_assets( + assets: &Vec, + parent_path: &Path, + colocated_path: &str, +) -> Vec { + assets + .iter() + .filter_map(|asset| asset.strip_prefix(parent_path).ok()) + .map(|asset_relative_path| { + asset_relative_path + .components() + .map(|component| component.as_os_str().to_string_lossy().to_string()) + .collect::>() + .join("/") + }) + .map(|asset_relative_path_as_string| { + format!("/{}{}", colocated_path, asset_relative_path_as_string) + }) + .collect() +} + +/// Create assets permalinks based on the permalin of the section or the page they are colocated with +pub fn get_assets_permalinks( + serialized_assets: &Vec, + parent_permalink: &str, + colocated_path: &str, +) -> HashMap { + serialized_assets + .iter() + .map(|asset| asset.strip_prefix("/").unwrap_or(asset)) + .map(|asset_file_path| { + let page_relative_asset_path = asset_file_path + .strip_prefix(colocated_path) + .expect("Should be able to stripe colocated path from asset path"); + let asset_permalink = format!("{}{}", parent_permalink, page_relative_asset_path); + (asset_file_path.to_string(), asset_permalink.to_string()) + }) + .collect() +} + /// Get word count and estimated reading time pub fn get_reading_analytics(content: &str) -> (usize, usize) { // code fences "toggle" the state from non-code to code and back, so anything inbetween the @@ -149,6 +191,96 @@ mod tests { ); } } + + #[test] + fn can_serialize_assets() { + let parent_path = Path::new("/tmp/test"); + let page_folder_path = parent_path.join("content").join("posts").join("my-article"); + let assets = vec![ + page_folder_path.join("example.js"), + page_folder_path.join("graph.jpg"), + page_folder_path.join("fail.png"), + page_folder_path.join("extensionless"), + page_folder_path.join("subdir").join("example.js"), + page_folder_path.join("FFF.txt"), + page_folder_path.join("GRAPH.txt"), + page_folder_path.join("subdir").join("GGG.txt"), + ]; + let colocated_path = "posts/my-article/".to_string(); + let expected_serialized_assets = vec![ + "/posts/my-article/example.js", + "/posts/my-article/graph.jpg", + "/posts/my-article/fail.png", + "/posts/my-article/extensionless", + "/posts/my-article/subdir/example.js", + "/posts/my-article/FFF.txt", + "/posts/my-article/GRAPH.txt", + "/posts/my-article/subdir/GGG.txt", + ]; + + let serialized_assets = serialize_assets(&assets, &page_folder_path, &colocated_path); + + assert_eq!( + serialized_assets, expected_serialized_assets, + "Serialized assets (left) are different from expected (right)", + ); + } + + #[test] + fn can_get_assets_permalinks() { + let serialized_assets = vec![ + "/posts/my-article/example.js".to_string(), + "/posts/my-article/graph.jpg".to_string(), + "/posts/my-article/fail.png".to_string(), + "/posts/my-article/extensionless".to_string(), + "/posts/my-article/subdir/example.js".to_string(), + "/posts/my-article/FFF.txt".to_string(), + "/posts/my-article/GRAPH.txt".to_string(), + "/posts/my-article/subdir/GGG.txt".to_string(), + ]; + let parent_permalink = "https://remplace-par-ton-url.fr/posts/my-super-article/"; + let colocated_path = "posts/my-article/".to_string(); + let expected_assets_permalinks: HashMap<_, _> = HashMap::from_iter([ + ( + "posts/my-article/example.js".to_string(), + format!("{}{}", parent_permalink, "example.js"), + ), + ( + "posts/my-article/graph.jpg".to_string(), + format!("{}{}", parent_permalink, "graph.jpg"), + ), + ( + "posts/my-article/fail.png".to_string(), + format!("{}{}", parent_permalink, "fail.png"), + ), + ( + "posts/my-article/extensionless".to_string(), + format!("{}{}", parent_permalink, "extensionless"), + ), + ( + "posts/my-article/subdir/example.js".to_string(), + format!("{}{}", parent_permalink, "subdir/example.js"), + ), + ("posts/my-article/FFF.txt".to_string(), format!("{}{}", parent_permalink, "FFF.txt")), + ( + "posts/my-article/GRAPH.txt".to_string(), + format!("{}{}", parent_permalink, "GRAPH.txt"), + ), + ( + "posts/my-article/subdir/GGG.txt".to_string(), + format!("{}{}", parent_permalink, "subdir/GGG.txt"), + ), + ]); + + let assets_permalinks = + get_assets_permalinks(&serialized_assets, &parent_permalink, &colocated_path); + + assert_eq!( + assets_permalinks, expected_assets_permalinks, + "Assets permalinks (left) are different from expected (right)", + ); + } + #[test] fn can_find_anchor_at_root() { let input = vec![ diff --git a/components/site/src/lib.rs b/components/site/src/lib.rs index cd48c219e..bab47a0d2 100644 --- a/components/site/src/lib.rs +++ b/components/site/src/lib.rs @@ -56,9 +56,12 @@ pub struct Site { pub static_path: PathBuf, pub templates_path: PathBuf, pub taxonomies: Vec, - /// A map of all .md files (section and pages) and their permalink + /// A map of all .md files (sections and pages) and their permalink /// We need that if there are relative links in the content that need to be resolved pub permalinks: HashMap, + /// A map of all assets (non .md files colocated to sections and pages) and their permalink, distributed per lang + /// We need that if there are relative links in the content that need to be resolved + pub assets_permalinks: HashMap>, /// Contains all pages and sections of the site pub library: Arc>, /// Whether to load draft pages @@ -103,6 +106,7 @@ impl Site { templates_path, taxonomies: Vec::new(), permalinks: HashMap::new(), + assets_permalinks: HashMap::new(), include_drafts: false, // We will allocate it properly later on library: Arc::new(RwLock::new(Library::default())), @@ -477,6 +481,10 @@ impl Site { } self.permalinks.insert(page.file.relative.clone(), page.permalink.clone()); + let assets_permalinks = + self.assets_permalinks.entry(page.lang.to_string()).or_insert(HashMap::new()); + assets_permalinks.extend(page.assets_permalinks.clone().into_iter()); + if render_md { let insert_anchor = self.find_parent_section_insert_anchor(&page.file.parent, &page.lang); @@ -512,6 +520,10 @@ impl Site { /// The `render` parameter is used in the serve command with --fast, when rebuilding a page. pub fn add_section(&mut self, mut section: Section, render_md: bool) -> Result<()> { self.permalinks.insert(section.file.relative.clone(), section.permalink.clone()); + let assets_permalinks = + self.assets_permalinks.entry(section.lang.to_string()).or_insert(HashMap::new()); + assets_permalinks.extend(section.assets_permalinks.clone().into_iter()); + if render_md { section.render_markdown( &self.permalinks, diff --git a/components/site/src/tpls.rs b/components/site/src/tpls.rs index c51d0de17..09595056c 100644 --- a/components/site/src/tpls.rs +++ b/components/site/src/tpls.rs @@ -16,6 +16,7 @@ pub fn register_early_global_fns(site: &mut Site) -> TeraResult<()> { site.base_path.clone(), site.config.clone(), site.permalinks.clone(), + site.assets_permalinks.clone(), site.output_path.clone(), ), ); diff --git a/components/templates/src/global_fns/files.rs b/components/templates/src/global_fns/files.rs index eaad1231d..ab5d595d9 100644 --- a/components/templates/src/global_fns/files.rs +++ b/components/templates/src/global_fns/files.rs @@ -30,6 +30,7 @@ pub struct GetUrl { base_path: PathBuf, config: Config, permalinks: HashMap, + assets_permalinks: HashMap>, output_path: PathBuf, } @@ -38,15 +39,21 @@ impl GetUrl { base_path: PathBuf, config: Config, permalinks: HashMap, + assets_permalinks: HashMap>, output_path: PathBuf, ) -> Self { - Self { base_path, config, permalinks, output_path } + Self { base_path, config, permalinks, assets_permalinks, output_path } } } -fn make_path_with_lang(path: String, lang: &str, config: &Config) -> Result { +fn make_path_with_lang(path: String, lang: &str, config: &Config) -> Result<(String, bool)> { + let mut split_path: Vec = path.split('.').map(String::from).collect(); + let ilast = split_path.len() - 1; + + let is_markdown = split_path[ilast].starts_with("md"); + if lang == config.default_language { - return Ok(path); + return Ok((path, is_markdown)); } if !config.other_languages().contains_key(lang) { @@ -55,10 +62,12 @@ fn make_path_with_lang(path: String, lang: &str, config: &Config) -> Result = path.split('.').map(String::from).collect(); - let ilast = split_path.len() - 1; - split_path[ilast] = format!("{}.{}", lang, split_path[ilast]); - Ok(split_path.join(".")) + if is_markdown { + split_path[ilast] = format!("{}.{}", lang, split_path[ilast]); + Ok((split_path.join("."), is_markdown)) + } else { + Ok((path, is_markdown)) + } } impl TeraFn for GetUrl { @@ -85,12 +94,22 @@ impl TeraFn for GetUrl { // if it starts with @/, resolve it as an internal link if path.starts_with("@/") { - let path_with_lang = match make_path_with_lang(path, &lang, &self.config) { + let (path_with_lang, is_markdown) = match make_path_with_lang(path, &lang, &self.config) + { Ok(x) => x, Err(e) => return Err(e), }; - match resolve_internal_link(&path_with_lang, &self.permalinks) { + let permalinks = if is_markdown { + &self.permalinks + } else { + match self.assets_permalinks.get(&lang) { + Some(permalink) => permalink, + None => &HashMap::new(), + } + }; + + match resolve_internal_link(&path_with_lang, permalinks) { Ok(resolved) => Ok(to_value(resolved.permalink).unwrap()), Err(_) => Err(format!( "`get_url`: could not resolve URL for link `{}` not found.", @@ -282,6 +301,7 @@ title = "A title" dir.path().to_path_buf(), Config::default(), HashMap::new(), + HashMap::new(), PathBuf::new(), ); let mut args = HashMap::new(); @@ -310,6 +330,7 @@ title = "A title" dir.path().to_path_buf(), Config::default(), HashMap::new(), + HashMap::new(), PathBuf::new(), ); let mut args = HashMap::new(); @@ -325,6 +346,7 @@ title = "A title" dir.path().to_path_buf(), Config::default(), HashMap::new(), + HashMap::new(), PathBuf::new(), ); let mut args = HashMap::new(); @@ -344,6 +366,7 @@ title = "A title" dir.path().to_path_buf(), Config::default(), HashMap::new(), + HashMap::new(), PathBuf::new(), ); let mut args = HashMap::new(); @@ -363,8 +386,13 @@ title = "A title" create_file(&public.join("style.css"), "// Hello world") .expect("Failed to create file in output directory"); - let static_fn = - GetUrl::new(dir.path().to_path_buf(), Config::default(), HashMap::new(), public); + let static_fn = GetUrl::new( + dir.path().to_path_buf(), + Config::default(), + HashMap::new(), + HashMap::new(), + public, + ); let mut args = HashMap::new(); args.insert("path".to_string(), to_value("style.css").unwrap()); assert_eq!(static_fn.call(&args).unwrap(), "http://a-website.com/style.css"); @@ -374,8 +402,13 @@ title = "A title" fn error_when_language_not_available() { let config = Config::parse(CONFIG_DATA).unwrap(); let dir = create_temp_dir(); - let static_fn = - GetUrl::new(dir.path().to_path_buf(), config, HashMap::new(), PathBuf::new()); + let static_fn = GetUrl::new( + dir.path().to_path_buf(), + config, + HashMap::new(), + HashMap::new(), + PathBuf::new(), + ); let mut args = HashMap::new(); args.insert("path".to_string(), to_value("@/a_section/a_page.md").unwrap()); args.insert("lang".to_string(), to_value("it").unwrap()); @@ -403,6 +436,7 @@ title = "A title" dir.path().to_path_buf(), config.clone(), permalinks.clone(), + HashMap::new(), PathBuf::new(), ); let mut args = HashMap::new(); @@ -427,7 +461,13 @@ title = "A title" "https://remplace-par-ton-url.fr/en/a_section/a_page/".to_string(), ); let dir = create_temp_dir(); - let static_fn = GetUrl::new(dir.path().to_path_buf(), config, permalinks, PathBuf::new()); + let static_fn = GetUrl::new( + dir.path().to_path_buf(), + config, + permalinks, + HashMap::new(), + PathBuf::new(), + ); let mut args = HashMap::new(); args.insert("path".to_string(), to_value("@/a_section/a_page.md").unwrap()); args.insert("lang".to_string(), to_value("en").unwrap()); @@ -437,6 +477,135 @@ title = "A title" ); } + #[test] + fn can_get_asset_url_with_default_language() { + let config = Config::parse(CONFIG_DATA).unwrap(); + let asset_path = "a_section/an_asset.jpg"; + let mut assets_permalinks = HashMap::new(); + let mut fr_assets_permalinks = HashMap::new(); + fr_assets_permalinks.insert( + asset_path.to_string(), + "https://remplace-par-ton-url.fr/a_section/an_asset.jpg".to_string(), + ); + assets_permalinks.insert("fr".to_string(), fr_assets_permalinks); + let mut en_assets_permalinks = HashMap::new(); + en_assets_permalinks.insert( + asset_path.to_string(), + "https://remplace-par-ton-url.fr/en/a_section/an_asset.jpg".to_string(), + ); + assets_permalinks.insert("en".to_string(), en_assets_permalinks); + let dir = create_temp_dir(); + let static_fn = GetUrl::new( + dir.path().to_path_buf(), + config.clone(), + HashMap::new(), + assets_permalinks.clone(), + PathBuf::new(), + ); + let mut args = HashMap::new(); + args.insert("path".to_string(), to_value("@/a_section/an_asset.jpg").unwrap()); + args.insert("lang".to_string(), to_value("fr").unwrap()); + assert_eq!( + static_fn.call(&args).unwrap(), + "https://remplace-par-ton-url.fr/a_section/an_asset.jpg" + ); + } + + #[test] + fn can_get_asset_url_with_other_language() { + let config = Config::parse(CONFIG_DATA).unwrap(); + let asset_path = "a_section/an_asset.jpg"; + let mut assets_permalinks = HashMap::new(); + let mut fr_assets_permalinks = HashMap::new(); + fr_assets_permalinks.insert( + asset_path.to_string(), + "https://remplace-par-ton-url.fr/a_section/an_asset.jpg".to_string(), + ); + assets_permalinks.insert("fr".to_string(), fr_assets_permalinks); + let mut en_assets_permalinks = HashMap::new(); + en_assets_permalinks.insert( + asset_path.to_string(), + "https://remplace-par-ton-url.fr/en/a_section/an_asset.jpg".to_string(), + ); + assets_permalinks.insert("en".to_string(), en_assets_permalinks); + let dir = create_temp_dir(); + let static_fn = GetUrl::new( + dir.path().to_path_buf(), + config.clone(), + HashMap::new(), + assets_permalinks.clone(), + PathBuf::new(), + ); + let mut args = HashMap::new(); + args.insert("path".to_string(), to_value("@/a_section/an_asset.jpg").unwrap()); + args.insert("lang".to_string(), to_value("en").unwrap()); + assert_eq!( + static_fn.call(&args).unwrap(), + "https://remplace-par-ton-url.fr/en/a_section/an_asset.jpg" + ); + } + + #[test] + fn look_for_markdown_in_permalinks() { + let config = Config::parse(CONFIG_DATA).unwrap(); + let non_asset_path = "a_section/a_page.md"; + let mut permalinks = HashMap::new(); + permalinks.insert( + non_asset_path.to_string(), + "https://remplace-par-ton-url.fr/a_section/a_page".to_string(), + ); + let mut assets_permalinks = HashMap::new(); + let mut fr_assets_permalinks = HashMap::new(); + fr_assets_permalinks + .insert(non_asset_path.to_string(), "only assets should be there".to_string()); + assets_permalinks.insert("fr".to_string(), fr_assets_permalinks); + let dir = create_temp_dir(); + let static_fn = GetUrl::new( + dir.path().to_path_buf(), + config.clone(), + permalinks.clone(), + assets_permalinks.clone(), + PathBuf::new(), + ); + let mut args = HashMap::new(); + args.insert("path".to_string(), to_value("@/a_section/a_page.md").unwrap()); + args.insert("lang".to_string(), to_value("fr").unwrap()); + assert_eq!( + static_fn.call(&args).unwrap(), + "https://remplace-par-ton-url.fr/a_section/a_page" + ); + } + + #[test] + fn look_for_asset_in_assets_permalinks() { + let config = Config::parse(CONFIG_DATA).unwrap(); + let asset_path = "a_section/an_asset.jpg"; + let mut permalinks = HashMap::new(); + permalinks.insert(asset_path.to_string(), "only markdown should be there".to_string()); + let mut assets_permalinks = HashMap::new(); + let mut fr_assets_permalinks = HashMap::new(); + fr_assets_permalinks.insert( + asset_path.to_string(), + "https://remplace-par-ton-url.fr/a_section/an_asset.jpg".to_string(), + ); + assets_permalinks.insert("fr".to_string(), fr_assets_permalinks); + let dir = create_temp_dir(); + let static_fn = GetUrl::new( + dir.path().to_path_buf(), + config.clone(), + permalinks.clone(), + assets_permalinks.clone(), + PathBuf::new(), + ); + let mut args = HashMap::new(); + args.insert("path".to_string(), to_value("@/a_section/an_asset.jpg").unwrap()); + args.insert("lang".to_string(), to_value("fr").unwrap()); + assert_eq!( + static_fn.call(&args).unwrap(), + "https://remplace-par-ton-url.fr/a_section/an_asset.jpg" + ); + } + #[test] fn does_not_duplicate_lang() { let config = Config::parse(CONFIG_DATA).unwrap(); @@ -450,7 +619,13 @@ title = "A title" "https://remplace-par-ton-url.fr/en/a_section/a_page/".to_string(), ); let dir = create_temp_dir(); - let static_fn = GetUrl::new(dir.path().to_path_buf(), config, permalinks, PathBuf::new()); + let static_fn = GetUrl::new( + dir.path().to_path_buf(), + config, + permalinks, + HashMap::new(), + PathBuf::new(), + ); let mut args = HashMap::new(); args.insert("path".to_string(), to_value("/en/a_section/a_page/").unwrap()); args.insert("lang".to_string(), to_value("en").unwrap()); @@ -464,8 +639,13 @@ title = "A title" fn can_get_feed_urls_with_default_language() { let config = Config::parse(CONFIG_DATA).unwrap(); let dir = create_temp_dir(); - let static_fn = - GetUrl::new(dir.path().to_path_buf(), config.clone(), HashMap::new(), PathBuf::new()); + let static_fn = GetUrl::new( + dir.path().to_path_buf(), + config.clone(), + HashMap::new(), + HashMap::new(), + PathBuf::new(), + ); for feed_filename in &config.feed_filenames { let mut args = HashMap::new(); args.insert("path".to_string(), to_value(feed_filename).unwrap()); @@ -478,8 +658,13 @@ title = "A title" fn can_get_feed_urls_with_other_language() { let config = Config::parse(CONFIG_DATA).unwrap(); let dir = create_temp_dir(); - let static_fn = - GetUrl::new(dir.path().to_path_buf(), config.clone(), HashMap::new(), PathBuf::new()); + let static_fn = GetUrl::new( + dir.path().to_path_buf(), + config.clone(), + HashMap::new(), + HashMap::new(), + PathBuf::new(), + ); for feed_filename in &config.feed_filenames { let mut args = HashMap::new(); args.insert("path".to_string(), to_value(feed_filename).unwrap()); diff --git a/docs/content/documentation/content/image-processing/index.md b/docs/content/documentation/content/image-processing/index.md index c04e46d98..6534eb2d0 100644 --- a/docs/content/documentation/content/image-processing/index.md +++ b/docs/content/documentation/content/image-processing/index.md @@ -153,7 +153,7 @@ picture gallery with the following shortcode named `gallery.html`: {% for asset in page.assets -%} {%- if asset is matching("[.](jpg|png)$") -%} {% set image = resize_image(path=asset, width=240, height=180) %} - + {%- endif %} diff --git a/docs/templates/shortcodes/gallery.html b/docs/templates/shortcodes/gallery.html index d171d8497..af325d14a 100644 --- a/docs/templates/shortcodes/gallery.html +++ b/docs/templates/shortcodes/gallery.html @@ -2,7 +2,7 @@ {% for asset in page.assets -%} {%- if asset is matching("[.](jpg|png)$") -%} {% set image = resize_image(path=asset, width=240, height=180) %} - + {%- endif %} diff --git a/q b/q new file mode 100644 index 000000000..1d3c7540e --- /dev/null +++ b/q @@ -0,0 +1,74 @@ +diff --git a/components/content/src/page.rs b/components/content/src/page.rs +index 9fff5ed4..71fed715 100644 +--- a/components/content/src/page.rs ++++ b/components/content/src/page.rs +@@ -198,24 +198,18 @@ impl Page { + let parent_dir = path.parent().unwrap(); + page.assets = find_related_assets(parent_dir, config, true); + if !page.assets.is_empty() { ++ let colocated_path = page ++ .file ++ .colocated_path ++ .as_ref() ++ .expect("Should have colocated path for assets"); + page.serialized_assets = serialize_assets( + &page.assets, + page.file.path.parent().unwrap(), +- page.file +- .colocated_path +- .as_ref() +- .expect("Should have colocated path for assets"), +- ); +- } +- if !page.serialized_assets.is_empty() { +- page.assets_permalinks = get_assets_permalinks( +- &page.serialized_assets, +- &page.permalink, +- page.file +- .colocated_path +- .as_ref() +- .expect("Should have colocated path for assets"), ++ colocated_path, + ); ++ page.assets_permalinks = ++ get_assets_permalinks(&page.serialized_assets, &page.permalink, colocated_path); + } + } else { + page.assets = vec![]; +diff --git a/components/content/src/section.rs b/components/content/src/section.rs +index 2cf36644..b4ac035e 100644 +--- a/components/content/src/section.rs ++++ b/components/content/src/section.rs +@@ -130,25 +130,20 @@ impl Section { + let parent_dir = path.parent().unwrap(); + section.assets = find_related_assets(parent_dir, config, false); + if !section.assets.is_empty() { ++ let colocated_path = section ++ .file ++ .colocated_path ++ .as_ref() ++ .expect("Should have colocated path for assets"); + section.serialized_assets = serialize_assets( + §ion.assets, + section.file.path.parent().unwrap(), +- section +- .file +- .colocated_path +- .as_ref() +- .expect("Should have colocated path for assets"), ++ colocated_path, + ); +- } +- if !section.serialized_assets.is_empty() { + section.assets_permalinks = get_assets_permalinks( + §ion.serialized_assets, + §ion.permalink, +- section +- .file +- .colocated_path +- .as_ref() +- .expect("Should have colocated path for assets"), ++ colocated_path, + ); + } +