diff --git a/content/posts/prettify-hugo-rss-feed-xslt/getting-there-feed.png b/content/posts/prettify-hugo-rss-feed-xslt/getting-there-feed.png new file mode 100644 index 0000000..d634594 Binary files /dev/null and b/content/posts/prettify-hugo-rss-feed-xslt/getting-there-feed.png differ diff --git a/content/posts/prettify-hugo-rss-feed-xslt/index.md b/content/posts/prettify-hugo-rss-feed-xslt/index.md index 726c770..3f54c58 100644 --- a/content/posts/prettify-hugo-rss-feed-xslt/index.md +++ b/content/posts/prettify-hugo-rss-feed-xslt/index.md @@ -1,9 +1,8 @@ --- -title: "Prettify Hugo Rss Feed Xslt" +title: "Prettify Hugo RSS Feeds with XSLT" date: 2024-04-29 -# lastmod: 2024-04-29 draft: true -description: "This is a new post about..." +description: "" featured: false toc: true comments: true @@ -29,3 +28,203 @@ This post will quickly cover how I used XSLT to style my blog's RSS feed and mad ### Starting Point The [RSS Templates](https://gohugo.io/templates/rss/) page from the Hugo documentation site provides some basic information about how to generate (and customize) an RSS feed for a Hugo-powered site. The basic steps are to [enable the RSS output in `hugo.toml`](https://github.com/jbowdre/runtimeterror/blob/871be9794234177c1bfa0b1c470873bde8f046be/config/_default/hugo.toml#L19-L30), include a link to the generated feed inside the `` element of the site template (I added it to [`layouts/partials/head.html`](https://github.com/jbowdre/runtimeterror/blob/871be9794234177c1bfa0b1c470873bde8f046be/layouts/partials/head.html#L8-L11)), and (optionally) include a customized RSS template to influence how the output gets rendered. +Here's the content of my `layouts/_default/rss.xml`, before adding the XSLT styling: + +```xml +# torchlight! {"lineNumbers": true} +{{- $pctx := . -}} +{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}} +{{- $pages := slice -}} +{{- if or $.IsHome $.IsSection -}} +{{- $pages = (where $pctx.RegularPages "Type" "in" site.Params.mainSections) -}} +{{- else -}} +{{- $pages = (where $pctx.Pages "Type" "in" site.Params.mainSections) -}} +{{- end -}} +{{- $limit := .Site.Config.Services.RSS.Limit -}} +{{- if ge $limit 1 -}} +{{- $pages = $pages | first $limit -}} +{{- end -}} +{{- printf "" | safeHTML }} + + + {{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }} + {{ .Permalink }} + Recent content {{ if ne .Title .Site.Title }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }} + Hugo -- gohugo.io{{ with .Site.LanguageCode }} + {{.}}{{end}}{{ with .Site.Copyright }} + {{.}}{{end}}{{ if not .Date.IsZero }} + {{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}{{ end }} + {{- with .OutputFormats.Get "RSS" -}} + {{ printf "" .Permalink .MediaType | safeHTML }} + {{- end -}} + + {{ .Site.Params.fallBackOgImage | absURL }} + {{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }} + {{ .Permalink }} + + {{ range $pages }} + + {{ .Title | plainify }} + {{ .Permalink }} + {{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }} + {{ with .Site.Params.Author.name }}{{.}}{{ end }} + {{ with .Params.series }}{{ . | lower }}{{ end }} + {{ range (.GetTerms "tags") }} + {{ .LinkTitle }}{{ end }} + {{ .Permalink }} + {{- $content := replaceRE "a href=\"(#.*?)\"" (printf "%s%s%s" "a href=\"" .Permalink "$1\"") .Content -}} + {{- $content = replaceRE "img src=\"(.*?)\"" (printf "%s%s%s" "img src=\"" .Permalink "$1\"") $content -}} + {{- $content = replaceRE "" "" $content -}} + {{- $content = replaceRE `-moz-tab-size:\d;-o-tab-size:\d;tab-size:\d;?` "" $content -}} + {{ $content | html }} + + {{ end }} + + +``` + +There's a lot going on here, but much of it is conditional logic so that Hugo can use the same template to render feeds for individual tags, categories, or the entire site. It then loops through the `range` of pages of that type to generate the data for each post. It also uses the [Hugo `strings.ReplaceRE` function](https://gohugo.io/functions/strings/replacere/) to replace relative image and anchor links with the full paths so those references will work correctly in readers, and to clean up some potentially-problematic HTML markup that was causing validation failures. + +All I really need to do to get this XML ready to be styled is just link in a style sheet, and I'll do that by inserting a `` element directly below the top-level `` one: + +```xml +# torchlight! {"lineNumbers": true, "lineNumbersStart": 10} +{{- if ge $limit 1 -}} +{{- $pages = $pages | first $limit -}} +{{- end -}} +{{- printf "" | safeHTML }} +{{ printf "" | safeHTML }} + +``` + +I'll put the stylesheet in `static/xml/feed.xsl` so Hugo will make it available at the appropriate path. + +### Creating the Style +While trying to figure out how I could dress up my RSS XML, I came across the [default XSL file](https://github.com/getnikola/nikola/blob/master/nikola/data/themes/base/assets/xml/rss.xsl) provided with the [Nikola SSG](https://getnikola.com/), and I thought it looked like a pretty good starting point. + +Here's Nikola's default XSL: + +```xml +# torchlight! {"lineNumbers": true} + + + + + + + + +<xsl:value-of select="rss/channel/title"/> (RSS) + + + +

(RSS)

+

This is an RSS feed. To subscribe to it, copy its address and paste it when your feed reader asks for it. It will be updated periodically in your reader. New to feeds? Learn more.

+

+ +urladdressfalse +

+

Preview of the feed’s current headlines:

+
    + +
  1. +
    +
+ + +
+
+``` + +If I just plug that in at `static/xml/feed.xml`, I do successfully get a styled (though *very* white) RSS page: + +![A very bright white (but styled) RSS page](very-white-feed.png) + +I'd like this to inherit the same styling as the rest of the site so that it looks like it belongs. I can go a long way toward that by bringing in the CSS stylesheets that are used on every page, and I'll also tweak the existing `<xsl:value-of select="rss/channel/title"/> (RSS) + + + + + +``` + +While I'm at it, I'll also go on and add in some favicons: + +```xml +# torchlight! {"lineNumbers": true, "lineNumbersStart": 10} +<xsl:value-of select="rss/channel/title"/> (RSS) + + + + + + + + + + +``` + +That's getting there: + +![A darker styled RSS page](getting-there-feed.png) + +Including those CSS styles means that the rendered page now uses my color palette and the [font I worked so hard to integrate](/using-custom-font-hugo/). I'm just going to make a few more tweaks to change some of the formatting, put the `New to feeds?` bit on its own line, and point to my [self-hosted instance of the SearXNG metasearch engine](https://scribbles.jbowdre.lol/post/self-hosting-a-search-engine-iyjdlk6y) instead of DDG. + +Here's my final (for now) `static/xml/feed.xsl` file: + +```xml +# torchlight! {"lineNumbers": true} + + + + + + + + + +<xsl:value-of select="rss/channel/title"/> (RSS) + + + + + + + + + + + + +

(RSS)

+

This is an RSS feed. To subscribe to it, copy its address and paste it when your feed reader asks for it. It will be updated periodically in your reader.

+

New to feeds? Learn more.

+

+ +urladdressfalse +

+

Recent posts:

+
    + +
  • +
    +
+ + +
+
+``` + +I'm pretty pleased with [that result](/feed.xml)! \ No newline at end of file diff --git a/content/posts/prettify-hugo-rss-feed-xslt/very-white-feed.png b/content/posts/prettify-hugo-rss-feed-xslt/very-white-feed.png new file mode 100644 index 0000000..c366e31 Binary files /dev/null and b/content/posts/prettify-hugo-rss-feed-xslt/very-white-feed.png differ