Compare commits
2 commits
d74495ddef
...
5f45cb4b53
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f45cb4b53 | ||
| b4025aecaf |
4 changed files with 276 additions and 2 deletions
24
src/content.config.ts
Normal file
24
src/content.config.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { defineCollection } from "astro:content";
|
||||||
|
import { glob } from "astro/loaders";
|
||||||
|
import { z } from "astro/zod";
|
||||||
|
|
||||||
|
const archive = defineCollection({
|
||||||
|
loader: glob({
|
||||||
|
base: "./content",
|
||||||
|
pattern: ["blog/**/*.md", "speeches/**/*.md"],
|
||||||
|
generateId: ({ entry }) => entry.replace(/\.md$/, ""),
|
||||||
|
}),
|
||||||
|
schema: z.object({
|
||||||
|
title: z.string().optional(),
|
||||||
|
type: z.enum(["blog", "speech"]).optional(),
|
||||||
|
published: z.string().optional(),
|
||||||
|
updated: z.string().optional(),
|
||||||
|
source: z.string().optional(),
|
||||||
|
speechYear: z.string().optional(),
|
||||||
|
speechCollection: z.string().optional(),
|
||||||
|
categories: z.array(z.string()).optional().default([]),
|
||||||
|
tags: z.array(z.string()).optional().default([]),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const collections = { archive };
|
||||||
42
src/lib/content-routes.ts
Normal file
42
src/lib/content-routes.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { getCollection } from "astro:content";
|
||||||
|
|
||||||
|
const blogSourcePattern = /^\/blog\/(?<year>\d{4})\/(?<month>\d{2})\/(?<day>\d{2})\/(?<slug>[^/]+)\.html$/;
|
||||||
|
|
||||||
|
export type BlogSourceRoute = {
|
||||||
|
year: string;
|
||||||
|
month: string;
|
||||||
|
day: string;
|
||||||
|
slug: string;
|
||||||
|
route: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function parseBlogSource(source?: string): BlogSourceRoute | undefined {
|
||||||
|
if (!source) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const match = blogSourcePattern.exec(source);
|
||||||
|
if (!match?.groups) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { year, month, day, slug } = match.groups;
|
||||||
|
|
||||||
|
return {
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
slug,
|
||||||
|
route: `blog/${year}/${month}/${day}/${slug}.html`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function contentSourceRoutes(): Promise<Set<string>> {
|
||||||
|
const entries = await getCollection("archive");
|
||||||
|
|
||||||
|
return new Set(
|
||||||
|
entries
|
||||||
|
.map((entry) => parseBlogSource(entry.data.source)?.route)
|
||||||
|
.filter((route): route is string => Boolean(route)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
|
import { contentSourceRoutes } from "../lib/content-routes";
|
||||||
import { isMigratedHtmlPage, relativeWebsitePath, routeFromRelativePath, sourcePathFromRoute, walkHtmlFiles } from "../lib/static-pages";
|
import { isMigratedHtmlPage, relativeWebsitePath, routeFromRelativePath, sourcePathFromRoute, walkHtmlFiles } from "../lib/static-pages";
|
||||||
|
|
||||||
export function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
|
const markdownRoutes = await contentSourceRoutes();
|
||||||
|
|
||||||
return walkHtmlFiles()
|
return walkHtmlFiles()
|
||||||
.map(relativeWebsitePath)
|
.map(relativeWebsitePath)
|
||||||
.filter((relativePath) => !isMigratedHtmlPage(relativePath))
|
.filter((relativePath) => !isMigratedHtmlPage(relativePath) && !markdownRoutes.has(relativePath))
|
||||||
.map((relativePath) => ({
|
.map((relativePath) => ({
|
||||||
params: {
|
params: {
|
||||||
route: routeFromRelativePath(relativePath),
|
route: routeFromRelativePath(relativePath),
|
||||||
|
|
|
||||||
205
src/pages/blog/[year]/[month]/[day]/[slug].astro
Normal file
205
src/pages/blog/[year]/[month]/[day]/[slug].astro
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
---
|
||||||
|
import { getCollection, render } from "astro:content";
|
||||||
|
import SiteLayout from "../../../../../components/SiteLayout.astro";
|
||||||
|
import SiteSidebar from "../../../../../components/SiteSidebar.astro";
|
||||||
|
import { parseBlogSource } from "../../../../../lib/content-routes";
|
||||||
|
|
||||||
|
const bodyClass =
|
||||||
|
"post-template-default single single-post single-format-standard custom-background tribe-no-js metaslider-plugin parabola-image-three caption-light meta-light parabola_triagles parabola-menu-left";
|
||||||
|
|
||||||
|
function formatDate(value?: string): string | undefined {
|
||||||
|
if (!value) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const date = new Date(value);
|
||||||
|
if (Number.isNaN(date.valueOf())) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Intl.DateTimeFormat("en-US", {
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
year: "numeric",
|
||||||
|
}).format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
function slugify(value: string): string {
|
||||||
|
return value
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/&/g, "and")
|
||||||
|
.replace(/[^a-z0-9]+/g, "-")
|
||||||
|
.replace(/^-|-$/g, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function categoryHref(entry, category: string): string {
|
||||||
|
if (entry.data.type === "speech") {
|
||||||
|
if (entry.data.speechCollection === "mrs-hak-ja-han-moon" || category.includes("Mrs. Hak Ja Han Moon")) {
|
||||||
|
return "/speeches/categories/mrs-hak-ja-han-moon.html";
|
||||||
|
}
|
||||||
|
|
||||||
|
const year = entry.data.speechYear ?? category.match(/(\d{4})$/)?.[1];
|
||||||
|
if (year === "1956") {
|
||||||
|
return "/speeches/categories/rev-sun-myung-moon.html";
|
||||||
|
}
|
||||||
|
|
||||||
|
return year ? `/speeches/categories/rev-sun-myung-moon-${year}.html` : "/speeches/categories/rev-sun-myung-moon.html";
|
||||||
|
}
|
||||||
|
|
||||||
|
return `/blog/categories/${slugify(category)}.html`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
const entries = await getCollection("archive");
|
||||||
|
const posts = entries
|
||||||
|
.map((entry) => {
|
||||||
|
const sourceRoute = parseBlogSource(entry.data.source);
|
||||||
|
return sourceRoute ? { entry, sourceRoute } : undefined;
|
||||||
|
})
|
||||||
|
.filter((post) => Boolean(post))
|
||||||
|
.sort((a, b) => {
|
||||||
|
const dateA = Date.parse(a.entry.data.published ?? "");
|
||||||
|
const dateB = Date.parse(b.entry.data.published ?? "");
|
||||||
|
|
||||||
|
if (!Number.isNaN(dateA) && !Number.isNaN(dateB) && dateA !== dateB) {
|
||||||
|
return dateA - dateB;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (a.entry.data.title ?? a.entry.id).localeCompare(b.entry.data.title ?? b.entry.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
return posts.map((post, index) => ({
|
||||||
|
params: {
|
||||||
|
year: post.sourceRoute.year,
|
||||||
|
month: post.sourceRoute.month,
|
||||||
|
day: post.sourceRoute.day,
|
||||||
|
slug: post.sourceRoute.slug,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
entry: post.entry,
|
||||||
|
previous: posts[index - 1],
|
||||||
|
next: posts[index + 1],
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const { entry, previous, next } = Astro.props;
|
||||||
|
const { Content } = await render(entry);
|
||||||
|
const title = entry.data.title ?? "Untitled";
|
||||||
|
const publishedDate = formatDate(entry.data.published);
|
||||||
|
const updatedDate = formatDate(entry.data.updated);
|
||||||
|
const categories = entry.data.categories ?? [];
|
||||||
|
const tags = entry.data.tags ?? [];
|
||||||
|
const canonical = entry.data.source;
|
||||||
|
const pdfHref = canonical ? `${canonical}?print=pdf` : undefined;
|
||||||
|
---
|
||||||
|
|
||||||
|
<SiteLayout title={`${title} – FFWPU Ireland`} bodyClass={bodyClass} pathname={canonical} canonical={canonical}>
|
||||||
|
<div id="main">
|
||||||
|
<div id="forbottom">
|
||||||
|
<div style="clear:both;"> </div>
|
||||||
|
|
||||||
|
<section id="container" class="two-columns-right">
|
||||||
|
<div id="content" role="main">
|
||||||
|
<div class:list={["post", "type-post", "status-publish", "format-standard", "hentry", categories.map((category) => `category-${slugify(category)}`)]}>
|
||||||
|
<h1 class="entry-title">{title}</h1>
|
||||||
|
<div class="entry-meta">
|
||||||
|
<span class="author vcard">By <a class="url fn n" rel="author" href="#" title="View all posts by Fami1yfed">Fami1yfed</a></span>
|
||||||
|
{
|
||||||
|
publishedDate && canonical && (
|
||||||
|
<span>
|
||||||
|
<time class="onDate date published" datetime={entry.data.published}>
|
||||||
|
{" "}
|
||||||
|
<a href={canonical} rel="bookmark">{publishedDate}</a>
|
||||||
|
{" "}
|
||||||
|
</time>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{updatedDate && <time class="updated" datetime={entry.data.updated}>{updatedDate}</time>}
|
||||||
|
{
|
||||||
|
categories.map((category) => (
|
||||||
|
<span class="bl_categ">
|
||||||
|
{" "}
|
||||||
|
<a href={categoryHref(entry, category)} rel="tag">{category}</a>
|
||||||
|
{" "}
|
||||||
|
</span>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="entry-content">
|
||||||
|
{
|
||||||
|
pdfHref && (
|
||||||
|
<div class="pdfprnt-buttons pdfprnt-buttons-post pdfprnt-top-bottom-left">
|
||||||
|
<a href={pdfHref} class="pdfprnt-button pdfprnt-button-pdf" target="_blank">
|
||||||
|
<img decoding="async" src="/assets/vendor/pdf/images/pdf.png" alt="image_pdf" title="Download PDF" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<Content />
|
||||||
|
{
|
||||||
|
pdfHref && (
|
||||||
|
<div class="pdfprnt-buttons pdfprnt-buttons-post pdfprnt-top-bottom-left">
|
||||||
|
<a href={pdfHref} class="pdfprnt-button pdfprnt-button-pdf" target="_blank">
|
||||||
|
<img decoding="async" src="/assets/vendor/pdf/images/pdf.png" alt="image_pdf" title="Download PDF" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
canonical && (
|
||||||
|
<div class="entry-utility">
|
||||||
|
{
|
||||||
|
tags.length > 0 && (
|
||||||
|
<div class="footer-tags">
|
||||||
|
<span class="bl_posted">Tagged</span> {" "}
|
||||||
|
{tags.map((tag, index) => (
|
||||||
|
<>
|
||||||
|
<a href={`/blog/tags/${slugify(tag)}.html`} rel="tag">{tag}</a>
|
||||||
|
{index < tags.length - 1 ? ", " : "."}
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<span class="bl_bookmark">
|
||||||
|
Bookmark the <a href={canonical} title={`Permalink to ${title}`} rel="bookmark">permalink</a>.{" "}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
(previous || next) && (
|
||||||
|
<div id="nav-below" class="navigation">
|
||||||
|
{previous?.entry.data.source && (
|
||||||
|
<div class="nav-previous">
|
||||||
|
<a href={previous.entry.data.source} rel="prev">
|
||||||
|
<span class="meta-nav">«</span> {previous.entry.data.title ?? "Previous"}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{next?.entry.data.source && (
|
||||||
|
<div class="nav-next">
|
||||||
|
<a href={next.entry.data.source} rel="next">
|
||||||
|
{next.entry.data.title ?? "Next"} <span class="meta-nav">»</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SiteSidebar />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SiteLayout>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue