130 lines
4.5 KiB
Rust
130 lines
4.5 KiB
Rust
use wo_htmlx::{self, Value};
|
|
use wo_http::response::Response;
|
|
use wo_route::RouteParams;
|
|
use wo_store::Store;
|
|
use wo_htmlx::TemplateRegistry;
|
|
|
|
/// Render the homepage: list of published articles.
|
|
pub fn handle_home(store: &Store, templates: &TemplateRegistry) -> Response {
|
|
let articles = match store.list_published(0, 20) {
|
|
Ok(a) => a,
|
|
Err(e) => return Response::internal_error(&e.to_string()),
|
|
};
|
|
|
|
let articles_val: Vec<Value> = articles
|
|
.iter()
|
|
.map(|a| {
|
|
let json = serde_json::to_value(a).unwrap_or_default();
|
|
Value::from_json(&json)
|
|
})
|
|
.collect();
|
|
|
|
let mut ctx = std::collections::BTreeMap::new();
|
|
ctx.insert("articles".into(), Value::List(articles_val));
|
|
ctx.insert("page_title".into(), Value::String("writeonce".into()));
|
|
let context = Value::Object(ctx);
|
|
|
|
render_page("home", &context, templates)
|
|
}
|
|
|
|
/// Render a single article page.
|
|
pub fn handle_article(params: &RouteParams, store: &Store, templates: &TemplateRegistry) -> Response {
|
|
let sys_title = match params.get("sys_title") {
|
|
Some(t) => t,
|
|
None => return Response::not_found(),
|
|
};
|
|
|
|
let article = match store.get_by_title(sys_title) {
|
|
Ok(Some(a)) => a,
|
|
Ok(None) => return Response::not_found(),
|
|
Err(e) => return Response::internal_error(&e.to_string()),
|
|
};
|
|
|
|
let json = serde_json::to_value(&article).unwrap_or_default();
|
|
let mut ctx = std::collections::BTreeMap::new();
|
|
ctx.insert("article".into(), Value::from_json(&json));
|
|
ctx.insert("page_title".into(), Value::String(article.title.clone()));
|
|
let context = Value::Object(ctx);
|
|
|
|
render_page("article", &context, templates)
|
|
}
|
|
|
|
/// Render a tag listing page.
|
|
pub fn handle_tag(params: &RouteParams, store: &Store, templates: &TemplateRegistry) -> Response {
|
|
let tag = match params.get("tag") {
|
|
Some(t) => t,
|
|
None => return Response::not_found(),
|
|
};
|
|
|
|
let articles = match store.list_by_tag(tag) {
|
|
Ok(a) => a,
|
|
Err(e) => return Response::internal_error(&e.to_string()),
|
|
};
|
|
|
|
let articles_val: Vec<Value> = articles
|
|
.iter()
|
|
.map(|a| {
|
|
let json = serde_json::to_value(a).unwrap_or_default();
|
|
Value::from_json(&json)
|
|
})
|
|
.collect();
|
|
|
|
let mut ctx = std::collections::BTreeMap::new();
|
|
ctx.insert("articles".into(), Value::List(articles_val));
|
|
ctx.insert("page_title".into(), Value::String(format!("tag: {}", tag)));
|
|
ctx.insert("tag".into(), Value::String(tag.to_string()));
|
|
let context = Value::Object(ctx);
|
|
|
|
render_page("home", &context, templates)
|
|
}
|
|
|
|
/// Render a static page (about, contact).
|
|
pub fn handle_static_page(name: &str, templates: &TemplateRegistry) -> Response {
|
|
let mut ctx = std::collections::BTreeMap::new();
|
|
ctx.insert("page_title".into(), Value::String(name.to_string()));
|
|
let context = Value::Object(ctx);
|
|
|
|
render_page(name, &context, templates)
|
|
}
|
|
|
|
/// Render a page template composed with layout, header, and footer.
|
|
fn render_page(template_name: &str, context: &Value, templates: &TemplateRegistry) -> Response {
|
|
let page_nodes = match templates.get(template_name) {
|
|
Some(nodes) => nodes,
|
|
None => return Response::not_found(),
|
|
};
|
|
|
|
let partials = templates.partials();
|
|
|
|
// Render the page content.
|
|
let page_html = wo_htmlx::render(page_nodes, context, partials);
|
|
|
|
// Compose with layout if it exists.
|
|
let html = if let Some(layout_nodes) = templates.get("layout") {
|
|
// Inject page content and header/footer.
|
|
let header_html = templates
|
|
.get("header")
|
|
.map(|n| wo_htmlx::render(n, context, partials))
|
|
.unwrap_or_default();
|
|
let footer_html = templates
|
|
.get("footer")
|
|
.map(|n| wo_htmlx::render(n, context, partials))
|
|
.unwrap_or_default();
|
|
|
|
let mut layout_ctx = std::collections::BTreeMap::new();
|
|
layout_ctx.insert("content".into(), Value::String(page_html));
|
|
layout_ctx.insert("header".into(), Value::String(header_html));
|
|
layout_ctx.insert("footer".into(), Value::String(footer_html));
|
|
if let Value::Object(map) = context {
|
|
for (k, v) in map {
|
|
layout_ctx.insert(k.clone(), v.clone());
|
|
}
|
|
}
|
|
let layout_context = Value::Object(layout_ctx);
|
|
wo_htmlx::render(layout_nodes, &layout_context, partials)
|
|
} else {
|
|
page_html
|
|
};
|
|
|
|
Response::html(html)
|
|
}
|