diff --git a/src/blog.rs b/src/blog.rs index 7e67d7c413c6316307a4aedee52059c0b2ef4c86..a937856e7e8c31272d5b9edf2bd471fe75945875 100644 --- a/src/blog.rs +++ b/src/blog.rs @@ -58,7 +58,7 @@ impl Blog { content.push_str("</table>"); - Ok(self.dress_page(None, &content)) + Ok(self.dress_page(None, &content, None)) } fn rss(&self) -> anyhow::Result<String> { @@ -85,7 +85,7 @@ impl Blog { Ok(out) } - fn dress_page(&self, title: Option<&str>, content: &str) -> String { + fn dress_page(&self, title: Option<&str>, content: &str, head: Option<&str>) -> String { let title = match title { Some(title) => format!("{} | {}", title.trim(), self.config.name.trim()), None => self.config.name.trim().to_string(), @@ -109,8 +109,10 @@ impl Blog { page_links.push_str(&p.link_short()); } + let head = head.unwrap_or(""); + format!( - r#"<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/style.css" />{favicons}<title>{title}</title></head><body><div><a href="/">Home</a>{page_links}{logo}</div><hr>{content}</body></html>"# + r#"<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/style.css" />{favicons}<title>{title}</title>{head}</head><body><div><a href="/">Home</a>{page_links}{logo}</div><hr>{content}</body></html>"# ) } } @@ -145,13 +147,21 @@ impl TryFrom<Blog> for RenderedBlog { let mut pages = FnvHashMap::default(); for p in &b.posts { - let body = b.dress_page(Some(&p.title), &p.html()?); + let body = b.dress_page( + Some(&p.title), + &p.html()?, + Some(p.open_graph(&b.config.root, &b.config.logo)?.as_ref()), + ); insert_path(&mut pages, &p.url, Response::html(body))?; } for p in &b.pages { - let body = b.dress_page(Some(&p.title), &p.html()?); + let body = b.dress_page( + Some(&p.title), + &p.html()?, + Some(p.open_graph(&b.config.root, &b.config.logo)?.as_ref()), + ); insert_path(&mut pages, &p.url, Response::html(body))?; } @@ -184,8 +194,11 @@ impl TryFrom<Blog> for RenderedBlog { Response::svg(include_bytes!("assets/rss.svg").to_vec()), )?; - let not_found = - Response::html(b.dress_page(Some("Page not found"), include_str!("assets/404.html"))); + let not_found = Response::html(b.dress_page( + Some("Page not found"), + include_str!("assets/404.html"), + None, + )); if let Some(fav) = b.favicon { for (p, r) in fav.responses { diff --git a/src/path.rs b/src/path.rs index 497d8fa7c8925c8485fd74def7c5ba1bc6680a28..dac7df48e15243763b72c26db34f4a56b402daf7 100644 --- a/src/path.rs +++ b/src/path.rs @@ -1,6 +1,6 @@ use std::fmt::{Debug, Display}; -#[derive(Eq, PartialEq, Hash, Debug)] +#[derive(Eq, PartialEq, Hash, Debug, Clone)] pub struct UrlPath { parts: Vec<String>, } diff --git a/src/post.rs b/src/post.rs index 1b3faece8f2dc3c8d210fa88244eb69623ec4c50..577f58dd2424821ea982ff74155044c90055cc5f 100644 --- a/src/post.rs +++ b/src/post.rs @@ -5,6 +5,7 @@ use chrono::{DateTime, NaiveDate, Utc}; use orgize::{elements::Keyword, export::HtmlHandler, Element}; use crate::{ + config::Logo, date::parse_org_date, load::Loadable, parser::{parse, CustomHtmlHandler, Parser}, @@ -20,6 +21,7 @@ pub struct Post { pub index: u32, pub content: String, pub url: String, + pub primary_photo: Option<String>, } impl Post { @@ -65,6 +67,45 @@ impl Post { )) } + pub fn open_graph(&self, root_url: &str, logo: &Option<Logo>) -> anyhow::Result<String> { + let root = UrlPath::new_from_raw(root_url.to_string()); + + let og_image = match self + .primary_photo + .as_ref() + .or_else(|| logo.as_ref().map(|l| &l.path)) + { + Some(l) => format!( + r#"<meta property="og:image" content="{}" />"#, + root.clone().join(l) + ), + None => String::new(), + }; + + let date = match self.date { + Some(d) => { + format!( + r#"<meta property="og:article:published_time" content="{}" />"#, + DateTime::<Utc>::from_utc( + d.and_hms_opt(0, 0, 0) + .context("Could not convert date to datetime")?, + Utc + ) + .to_rfc3339() + ) + } + None => "".to_string(), + }; + + Ok(format!( + r#"<meta property="og:title" content="{}" /><meta property="og:type" content="article" />{}<meta property="og:url" content="{}" />{}"#, + self.title, + og_image, + root.join(&self.url), + date, + )) + } + pub fn link(&self) -> anyhow::Result<String> { let link = match self.date_f()? { Some(date_f) => format!( @@ -147,6 +188,7 @@ struct PostParser { url: String, content: Vec<u8>, handler: CustomHtmlHandler, + primary_photo: Option<String>, } impl PostParser { @@ -159,6 +201,7 @@ impl PostParser { url, content: vec![], handler: CustomHtmlHandler::new(base_path)?, + primary_photo: None, }) } } @@ -178,6 +221,14 @@ impl Parser<Post> for PostParser { self.title = Some(value.to_string()); } + Element::Keyword(Keyword { key, value, .. }) if key == "PREVIEW" => { + if self.primary_photo.is_some() { + bail!("Post has more than one preview"); + } + + self.primary_photo = Some(format!("/thumb/{value}")); + } + Element::Keyword(Keyword { key, value, .. }) if key == "DATE" => { if self.date.is_some() { bail!("Post has more than one date"); @@ -215,6 +266,7 @@ impl Parser<Post> for PostParser { let index = self.index.unwrap_or(u32::MAX); let content = String::from_utf8(self.content)?; let url = self.url; + let primary_photo = self.primary_photo; Ok(Post { hidden, @@ -223,6 +275,7 @@ impl Parser<Post> for PostParser { index, content, url, + primary_photo, }) } } diff --git a/src/wrapper.rs b/src/wrapper.rs deleted file mode 100644 index 1d40a3b2390745bd1cde06767f4588d991e4ebde..0000000000000000000000000000000000000000 --- a/src/wrapper.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub struct Wrapper { - // -} - -impl Wrapper { - fn new() {} - fn before() {} - fn after() {} -}