From 4f8979ae9bbb8a91ae4f37ff8eb8da3e2ef02659 Mon Sep 17 00:00:00 2001 From: Daniel Fichtinger Date: Thu, 17 Jul 2025 21:58:21 -0400 Subject: [PATCH 1/2] fixed incorrect codeberg links in readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c3facfb..ccf5dd1 100644 --- a/README.md +++ b/README.md @@ -231,11 +231,11 @@ modified if they point to a real file that's not included in the ignore list. Zona uses [Pygments] to provide syntax highlighting for fenced code blocks. The following Pygments plugins are included: -- [pygments-kakoune](https://codeberg.com/ficd/pygments-kakoune) +- [pygments-kakoune](https://codeberg.org/ficd/pygments-kakoune) - A lexer providing for highlighting Kakoune code. Available under the `kak` and `kakrc` aliases. -- [pygments-ashen](https://codeberg.com/ficd/ashen/tree/main/item/pygments/README.md) - - An implementation of the [Ashen](https://codeberg.com/ficd/ashen) theme for +- [pygments-ashen](https://codeberg.org/ficd/ashen/tree/main/item/pygments/README.md) + - An implementation of the [Ashen](https://codeberg.org/ficd/ashen) theme for Pygments. If you want to use any external Pygments styles or lexers, they must be @@ -442,7 +442,7 @@ output. If you set `draft: true` in a page's frontmatter, it will be marked as a draft. Drafts are completely excluded from `zona build` and `zona serve` unless the `--draft` flag is specified. -[Ashen]: https://codeberg.com/ficd/ashen +[Ashen]: https://codeberg.org/ficd/ashen [Pygments]: https://pygments.org/ ## Known Problems From 320f74b84eb85bcd7bda9c094c386b8cb5a73afd Mon Sep 17 00:00:00 2001 From: Daniel Fichtinger Date: Sat, 12 Jul 2025 01:06:31 -0400 Subject: [PATCH 2/2] wip: generate RSS feed --- pyproject.toml | 3 +- src/zona/builder.py | 52 +++++++++++++++++++++++++- src/zona/config.py | 51 ++++++++++++++++++++++++- src/zona/data/templates/page.html | 6 +-- src/zona/data/templates/post_list.html | 4 +- src/zona/metadata.py | 37 +++++++++++++----- uv.lock | 52 ++++++++++++++++++++++++++ 7 files changed, 185 insertions(+), 20 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d852304..b6d7de5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ authors = [ requires-python = ">=3.12" dependencies = [ "dacite>=1.9.2", + "feedgen>=1.0.0", "jinja2>=3.1.6", "l2m4m>=1.0.4", "markdown>=3.8.2", @@ -57,7 +58,7 @@ reportUnusedCallResult = false reportCallInDefaultInitializer = false enableTypeIgnoreComments = true reportIgnoreCommentWithoutRule = false -allowedUntypedLibraries = ["frontmatter", "pygments", "pymdownx", "l2m4m"] +allowedUntypedLibraries = ["frontmatter", "pygments", "pymdownx", "l2m4m", "feedgen", "feedgen.feed"] [tool.ruff] line-length = 70 diff --git a/src/zona/builder.py b/src/zona/builder.py index 416edc8..549abbd 100644 --- a/src/zona/builder.py +++ b/src/zona/builder.py @@ -2,6 +2,8 @@ import shutil from datetime import date from pathlib import Path +from feedgen.feed import FeedGenerator + from zona import markdown as zmd from zona import util from zona.config import ZonaConfig @@ -55,7 +57,9 @@ class ZonaBuilder: layout.root / "content" / "static" ): logger.debug(f"Parsing {path.name}.") - item.metadata, item.content = parse_metadata(path) + item.metadata, item.content = parse_metadata( + path, config=self.config + ) if item.metadata.ignore or ( item.metadata.draft and not self.config.build.include_drafts @@ -97,7 +101,45 @@ class ZonaBuilder: items.append(item) self.items = items - def _build(self): + def generate_feed(self) -> bytes: + post_list = self._get_post_list() + config = self.config.feed + if config.link.endswith("/"): + config.link = config.link[:-2] + fg = FeedGenerator() + fg.id(config.link) + fg.title(config.title) + author = { + "name": config.author.name, + "email": config.author.email, + } + fg.author(author) + fg.link( + href=f"{config.link}/{config.path}", + rel="self", + type="application/rss+xml", + ) + fg.language(config.language) + fg.description(config.description) + + for post in post_list: + assert post.metadata + fe = fg.add_entry() # pyright: ignore[reportUnknownVariableType] + fe.id(f"{config.link}{util.normalize_url(post.url)}") # pyright: ignore[reportUnknownMemberType] + fe.link( # pyright: ignore[reportUnknownMemberType] + href=f"{config.link}{util.normalize_url(post.url)}" + ) + fe.title(post.metadata.title) # pyright: ignore[reportUnknownMemberType] + fe.author(author) # pyright: ignore[reportUnknownMemberType] + desc = post.metadata.description + fe.description(desc) # pyright: ignore[reportUnknownMemberType] + date = post.metadata.date + fe.pubDate(date) # pyright: ignore[reportUnknownMemberType] + out: bytes = fg.rss_str(pretty=True) # pyright: ignore[reportUnknownVariableType] + assert isinstance(out, bytes) + return out + + def _get_post_list(self) -> list[Item]: assert self.items # sort according to date # descending order @@ -108,7 +150,12 @@ class ZonaBuilder: else date.min, reverse=True, ) + return post_list + + def _build(self): + post_list = self._get_post_list() # number of posts + # generate RSS here posts = len(post_list) # link post chronology for i, item in enumerate(post_list): @@ -181,3 +228,4 @@ class ZonaBuilder: logger.debug("Building...") self._build() self.fresh = False + print(self.generate_feed().decode()) diff --git a/src/zona/config.py b/src/zona/config.py index 2a50fd4..84844c7 100644 --- a/src/zona/config.py +++ b/src/zona/config.py @@ -1,7 +1,13 @@ +from __future__ import annotations + from dataclasses import dataclass, field +from datetime import datetime, tzinfo from pathlib import Path +from typing import Any +from zoneinfo import ZoneInfo import yaml +from dacite import Config as DaciteConfig from dacite import from_dict from zona.log import get_logger @@ -25,9 +31,17 @@ def find_config(start: Path | None = None) -> Path | None: SitemapConfig = dict[str, str] +@dataclass +class PostDefaultsConfig: + description: str = "A blog post" + + @dataclass class BlogConfig: dir: str = "blog" + defaults: PostDefaultsConfig = field( + default_factory=PostDefaultsConfig + ) @dataclass @@ -69,12 +83,40 @@ class ServerConfig: reload: ReloadConfig = field(default_factory=ReloadConfig) +@dataclass +class AuthorConfig: + name: str = "John Doe" + email: str = "john@doe.net" + + +@dataclass +class FeedConfig: + enabled: bool = True + timezone: tzinfo = field(default_factory=lambda: ZoneInfo("UTC")) + path: str = "rss.xml" + link: str = "https://example.com" + title: str = "Zona Website" + description: str = "My zona website." + language: str = "en" + author: AuthorConfig = field(default_factory=AuthorConfig) + + IGNORELIST = [".marksman.toml"] +def parse_timezone(s: Any) -> tzinfo: + if isinstance(s, str): + return ZoneInfo(s) + else: + raise TypeError( + f"Expected {str}, got {type(s)} for config key timezone" + ) + + @dataclass class ZonaConfig: base_url: str = "/" + feed: FeedConfig = field(default_factory=FeedConfig) # dictionary where key is name, value is url sitemap: SitemapConfig = field( default_factory=lambda: {"Home": "/"} @@ -87,7 +129,12 @@ class ZonaConfig: server: ServerConfig = field(default_factory=ServerConfig) @classmethod - def from_file(cls, path: Path) -> "ZonaConfig": + def from_file(cls, path: Path) -> ZonaConfig: with open(path, "r") as f: raw = yaml.safe_load(f) - return from_dict(data_class=cls, data=raw) + config: ZonaConfig = from_dict( + data_class=cls, + data=raw, + config=DaciteConfig(type_hooks={tzinfo: parse_timezone}), + ) + return config diff --git a/src/zona/data/templates/page.html b/src/zona/data/templates/page.html index 0c22f8b..0ff499b 100644 --- a/src/zona/data/templates/page.html +++ b/src/zona/data/templates/page.html @@ -1,8 +1,8 @@ {% extends "base.html" %} {% block content %} {% if metadata.show_title %} {% -include "title.html" %} {% if metadata.date %} +include "title.html" %} {% if metadata.date.date() %}
- +
{% endif %} {% endif %} {% if is_post %} {% include "post_nav.html" %} {% endif %} diff --git a/src/zona/data/templates/post_list.html b/src/zona/data/templates/post_list.html index 6b0bdca..30ea74f 100644 --- a/src/zona/data/templates/post_list.html +++ b/src/zona/data/templates/post_list.html @@ -8,8 +8,8 @@ include "title.html" %} {% endif %}