Skip to content

Commit bdd989b

Browse files
Dan blog career article pages (#1338)
1 parent 63963d4 commit bdd989b

File tree

20 files changed

+664
-74
lines changed

20 files changed

+664
-74
lines changed

pgml-dashboard/src/api/cms.rs

Lines changed: 98 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize};
2222
use std::fmt;
2323

2424
lazy_static! {
25-
static ref BLOG: Collection = Collection::new(
25+
pub static ref BLOG: Collection = Collection::new(
2626
"Blog",
2727
true,
2828
HashMap::from([
@@ -93,7 +93,7 @@ impl FromStr for DocType {
9393
}
9494
}
9595

96-
#[derive(Debug, Serialize, Deserialize)]
96+
#[derive(Debug, Serialize, Deserialize, Default)]
9797
pub struct Document {
9898
/// The absolute path on disk
9999
pub path: PathBuf,
@@ -110,10 +110,15 @@ pub struct Document {
110110
pub doc_type: Option<DocType>,
111111
// url to thumbnail for social share
112112
pub thumbnail: Option<String>,
113+
pub url: String,
113114
}
114115

115116
// Gets document markdown
116117
impl Document {
118+
pub fn new() -> Document {
119+
Document { ..Default::default() }
120+
}
121+
117122
pub async fn from_path(path: &PathBuf) -> anyhow::Result<Document, std::io::Error> {
118123
let doc_type = match path.strip_prefix(config::cms_dir()) {
119124
Ok(path) => match path.into_iter().next() {
@@ -151,11 +156,14 @@ impl Document {
151156
(None, contents)
152157
};
153158

154-
let default_image_path = BLOG
155-
.asset_url_root
156-
.join("blog_image_placeholder.png")
157-
.display()
158-
.to_string();
159+
let default_image_path = match doc_type {
160+
Some(DocType::Blog) => BLOG
161+
.asset_url_root
162+
.join("blog_image_placeholder.png")
163+
.display()
164+
.to_string(),
165+
_ => String::from("/dashboard/static/images/careers_article_default.png"),
166+
};
159167

160168
// parse meta section
161169
let (description, image, featured, tags) = match meta {
@@ -166,15 +174,20 @@ impl Document {
166174
Some(meta["description"].as_str().unwrap().to_string())
167175
};
168176

169-
// For now the only images shown are blog images TODO: use doc_type to set asset path when working.
170177
let image = if meta["image"].is_badvalue() {
171178
Some(default_image_path.clone())
172179
} else {
173180
match PathBuf::from_str(meta["image"].as_str().unwrap()) {
174181
Ok(image_path) => match image_path.file_name() {
175182
Some(file_name) => {
176183
let file = PathBuf::from(file_name).display().to_string();
177-
Some(BLOG.asset_url_root.join(file).display().to_string())
184+
match doc_type {
185+
Some(DocType::Docs) => Some(DOCS.asset_url_root.join(file).display().to_string()),
186+
Some(DocType::Careers) => {
187+
Some(CAREERS.asset_url_root.join(file).display().to_string())
188+
}
189+
_ => Some(BLOG.asset_url_root.join(file).display().to_string()),
190+
}
178191
}
179192
_ => Some(default_image_path.clone()),
180193
},
@@ -221,6 +234,34 @@ impl Document {
221234
let toc_links = crate::utils::markdown::get_toc(root).unwrap();
222235
let (author, date, author_image) = crate::utils::markdown::get_author(root);
223236

237+
// convert author image relative url path to absolute url path
238+
let author_image = if author_image.is_some() {
239+
let image = author_image.clone().unwrap();
240+
let image = PathBuf::from(image);
241+
let image = image.file_name().unwrap();
242+
match &doc_type {
243+
Some(DocType::Blog) => Some(BLOG.asset_url_root.join(image.to_str().unwrap()).display().to_string()),
244+
Some(DocType::Docs) => Some(DOCS.asset_url_root.join(image.to_str().unwrap()).display().to_string()),
245+
Some(DocType::Careers) => Some(
246+
CAREERS
247+
.asset_url_root
248+
.join(PathBuf::from(image.to_str().unwrap()))
249+
.display()
250+
.to_string(),
251+
),
252+
_ => None,
253+
}
254+
} else {
255+
None
256+
};
257+
258+
let url = match doc_type {
259+
Some(DocType::Blog) => BLOG.path_to_url(&path),
260+
Some(DocType::Docs) => DOCS.path_to_url(&path),
261+
Some(DocType::Careers) => CAREERS.path_to_url(&path),
262+
_ => String::new(),
263+
};
264+
224265
let document = Document {
225266
path: path.to_owned(),
226267
description,
@@ -235,6 +276,7 @@ impl Document {
235276
contents,
236277
doc_type,
237278
thumbnail,
279+
url,
238280
};
239281
Ok(document)
240282
}
@@ -478,6 +520,25 @@ impl Collection {
478520
self.root_dir.join(path_pb)
479521
}
480522

523+
// Convert a file path to a url
524+
pub fn path_to_url(&self, path: &PathBuf) -> String {
525+
let url = path.strip_prefix(config::cms_dir()).unwrap();
526+
let url = format!("/{}", url.display().to_string());
527+
528+
let url = if url.ends_with("README.md") {
529+
url.replace("README.md", "")
530+
} else {
531+
url
532+
};
533+
534+
let url = if url.ends_with(".md") {
535+
url.replace(".md", "")
536+
} else {
537+
url
538+
};
539+
url
540+
}
541+
481542
// get all urls in the collection and preserve order.
482543
pub fn get_all_urls(&self) -> Vec<String> {
483544
let mut urls: Vec<String> = Vec::new();
@@ -524,35 +585,39 @@ impl Collection {
524585
) -> Result<ResponseOk, crate::responses::NotFound> {
525586
match Document::from_path(&path).await {
526587
Ok(doc) => {
527-
let mut layout = crate::templates::Layout::new(&doc.title, Some(cluster));
528-
if let Some(image) = &doc.thumbnail {
529-
layout.image(&image);
530-
}
531-
if let Some(description) = &doc.description {
532-
layout.description(description);
533-
}
588+
let head = crate::components::layouts::Head::new()
589+
.title(&doc.title)
590+
.description(&doc.description.clone().unwrap_or_else(|| String::new()))
591+
.image(&doc.thumbnail.clone().unwrap_or_else(|| String::new()))
592+
.canonical(&canonical);
534593

535-
let layout = layout.canonical(canonical).toc_links(&doc.toc_links);
594+
let layout = Base::from_head(head, Some(cluster)).theme(Theme::Docs);
536595

537-
Ok(ResponseOk(
538-
layout.render(crate::templates::Article { content: doc.html() }),
539-
))
596+
let mut article = crate::components::pages::article::Index::new(&cluster)
597+
.document(doc)
598+
.await;
599+
600+
article = if self.name == "Blog" {
601+
article.is_blog()
602+
} else {
603+
article.is_careers()
604+
};
605+
606+
Ok(ResponseOk(layout.render(article)))
540607
}
541608
// Return page not found on bad path
542609
_ => {
543-
let mut layout = crate::templates::Layout::new("404", Some(cluster));
544-
545-
let doc = String::from(
546-
r#"
547-
<div style='height: 80vh'>
548-
<h2>Oops, document not found!</h2>
549-
<p>The document you are searching for may have been moved or replaced with better content.</p>
550-
</div>"#,
551-
);
552-
553-
Err(crate::responses::NotFound(
554-
layout.render(crate::templates::Article { content: doc }).into(),
555-
))
610+
let layout = Base::new("404", Some(cluster)).theme(Theme::Docs);
611+
612+
let mut article = crate::components::pages::article::Index::new(&cluster).document_not_found();
613+
614+
article = if self.name == "Blog" {
615+
article.is_blog()
616+
} else {
617+
article.is_careers()
618+
};
619+
620+
Err(crate::responses::NotFound(layout.render(article)))
556621
}
557622
}
558623
}

pgml-dashboard/src/components/cards/blog/article_preview/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
use crate::api::cms::Document;
12
use chrono::NaiveDate;
23
use pgml_components::component;
34
use sailfish::TemplateOnce;
5+
use std::path::PathBuf;
46

57
#[derive(Clone)]
68
pub struct DocMeta {
@@ -54,6 +56,23 @@ impl ArticlePreview {
5456
self.card_type = card_type.to_owned();
5557
self
5658
}
59+
60+
pub async fn from_path(path: &str) -> ArticlePreview {
61+
let doc = Document::from_path(&PathBuf::from(path)).await.unwrap();
62+
63+
let meta = DocMeta {
64+
description: doc.description,
65+
author: doc.author,
66+
author_image: doc.author_image,
67+
featured: false,
68+
date: doc.date,
69+
tags: doc.tags,
70+
image: doc.image,
71+
title: doc.title,
72+
path: doc.url,
73+
};
74+
ArticlePreview::new(&meta)
75+
}
5776
}
5877

5978
component!(ArticlePreview);

pgml-dashboard/src/components/cards/blog/article_preview/template.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"#,
1010
if meta.author_image.is_some() {
1111
format!(r#"
12-
<img src="blog/{}"class="rounded-circle me-1 author-image" style="height: 3rem;" alt="Author">
12+
<img src="{}"class="rounded-circle me-1 author-image" style="height: 3rem;" alt="Author">
1313
"#, meta.author_image.clone().unwrap())} else {String::new() },
1414

1515
if meta.author.is_some() {

pgml-dashboard/src/components/layouts/head/mod.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,25 @@ impl Head {
2828
}
2929

3030
pub fn description(mut self, description: &str) -> Head {
31-
self.description = Some(description.to_owned());
31+
self.description = if description.len() == 0 {
32+
None
33+
} else {
34+
Some(description.to_owned())
35+
};
3236
self
3337
}
3438

3539
pub fn canonical(mut self, canonical: &str) -> Head {
36-
self.canonical = Some(canonical.to_owned());
40+
self.canonical = if canonical.len() == 0 {
41+
None
42+
} else {
43+
Some(canonical.to_owned())
44+
};
3745
self
3846
}
3947

4048
pub fn image(mut self, image: &str) -> Head {
41-
self.image = Some(image.to_owned());
49+
self.image = if image.len() == 0 { None } else { Some(image.to_owned()) };
4250
self
4351
}
4452

0 commit comments

Comments
 (0)