Skip to main content

max / makenotwork

4.1 KB · 110 lines History Blame Raw
1 //! Seed blog posts on each project (Phase 4).
2 //!
3 //! Drives the project blog page (`/p/{slug}/blog`) and per-project RSS
4 //! (`/p/{slug}/blog/feed.xml`). Posts are published (`published_at = NOW()`); the
5 //! owning project is already public and the creator non-sandbox, so they render to
6 //! anonymous fans. `body_html` is pre-rendered the same way the live blog handler
7 //! does (`render_creator_markdown`).
8
9 use super::SeedError;
10 use super::projects::SeededProject;
11 use crate::db;
12 use crate::formatting::slugify;
13 use crate::markdown::render_creator_markdown;
14
15 /// Publish every roster blog post under its project.
16 pub async fn seed_blog(pool: &sqlx::PgPool, projects: &[SeededProject]) -> Result<(), SeedError> {
17 for project in projects {
18 for post in project.spec.blog {
19 let slug = slugify(post.title);
20 // Bodies are plain prose with no embedded media, so the CDN base used
21 // for media-path rewriting is irrelevant here.
22 let body_html = render_creator_markdown(post.body, project.user_id, "");
23 db::blog_posts::create_blog_post(
24 pool,
25 project.project.id,
26 project.user_id,
27 post.title,
28 &slug,
29 post.body,
30 &body_html,
31 true, // publish -> published_at = NOW()
32 false, // web_only
33 false, // show_on_landing (inert off the changelog project)
34 )
35 .await?;
36 tracing::info!(title = post.title, slug = %slug, "example seed: published blog post");
37 }
38 }
39 Ok(())
40 }
41
42 /// Project the media-credits post is published under. Commonshare is the benefit
43 /// account whose whole premise is the commons, so the attribution for the box's
44 /// public-domain media belongs there rather than on a creator's storefront.
45 const CREDITS_PROJECT_SLUG: &str = "commons-sampler";
46
47 /// Title (and, slugified, the URL) of the media-credits post.
48 const CREDITS_TITLE: &str = "Media credits and sources";
49
50 /// Publish a credits post listing every curated asset on the box, with its
51 /// licence and a link to the page that substantiates the claim.
52 ///
53 /// A licence field in `media-manifest.toml` is a note to ourselves; a visitor
54 /// looking at a CC0 photograph on a public site needs somewhere to go. This is
55 /// that somewhere. The public `/docs/credits` page is deliberately not it: that
56 /// page enumerates what the *software* is built on and ships identically to
57 /// production, where none of this media exists.
58 ///
59 /// No-ops when nothing is curated, so an uncurated box does not publish an empty
60 /// credits page.
61 pub async fn seed_media_credits(
62 pool: &sqlx::PgPool,
63 projects: &[SeededProject],
64 assets: &super::manifest::ResolvedAssets,
65 ) -> Result<(), SeedError> {
66 let list = assets.attribution_markdown();
67 if list.is_empty() {
68 tracing::info!("example seed: no curated media, skipping the credits post");
69 return Ok(());
70 }
71 let Some(project) = projects
72 .iter()
73 .find(|p| p.spec.slug == CREDITS_PROJECT_SLUG)
74 else {
75 tracing::warn!(
76 slug = CREDITS_PROJECT_SLUG,
77 "example seed: credits project missing from the roster; credits not published"
78 );
79 return Ok(());
80 };
81
82 let body = format!(
83 "Every file on this demo is public domain or CC0. This is where each one \
84 came from.\n\n{list}\n\nNothing here is a real listing. The catalog is \
85 fabricated for demonstration; only the media is real, and only because \
86 its licence allows it."
87 );
88 let slug = slugify(CREDITS_TITLE);
89 let body_html = render_creator_markdown(&body, project.user_id, "");
90 db::blog_posts::create_blog_post(
91 pool,
92 project.project.id,
93 project.user_id,
94 CREDITS_TITLE,
95 &slug,
96 &body,
97 &body_html,
98 true, // publish
99 false, // web_only
100 false, // show_on_landing
101 )
102 .await?;
103 tracing::info!(
104 assets = assets.len(),
105 slug = %slug,
106 "example seed: published media credits"
107 );
108 Ok(())
109 }
110