Setup rss feeds in Kirby and test them with Thunderbird

The underlying base for making this really easy in Kirby: it's openness. Being a contender on the cms market, they mention on various occasions throughout their website, that a lock-in is not wanted. Every part of the website is approachable, extendable and whatnot from various angles. That means, you can hook in wherever you want in order to build upon built-in functionality and expand the website to your needs.

However, this warrants a word of precaution. Great power comes with great … knowing things. Kirby is definitely a developer cms, i.e. you need to be a web developer or programmer to bring the website to the finishing line.

The intuitive editor experience comes close to Neos CMS, even though the backend handling is more in line with Typo3 or Drupal. I would say, that's mainly based on the clean design the Kirby backend provides. And it requires the developer to stay within that design frame and continuously keep the editor hat on while preparing the site. But i digress.

The rss feeds

Enough of the preface. Let's move on the rss feed setup workflow i actually want to talk about. The website has two main collections, book reviews and event announcements. So i added two feeds.

For setting up the feeds, i've followed kevquirk's How to Add an RSS Feed to Your Kirby Blog. The first check if all is working is to call the feed URL http://localhost:8000/feed/events in the browser. It should download a .rss file.

The code for the rss feeds

In site/config/config.php:

<?php
return [
    // …other settings in the config file
    'routes' => [
        // …other routes, e.g. sitemap.xml
        // rss feed
        [
            'pattern'   => ['/feed/reviews'],
            'action'    => function () {

                $title          = "Name of the website";
                $description    = "Our reviews";
                $books          = kirby()->collection('books')->limit(20);

                return new Response(snippet('rssbooks', compact('title', 'description', 'books') , true), 'application/rss+xml');
            }
        ],
        [
            'pattern'   => ['/feed/events'],
            'action'    => function () {

                $title          = "Name of the website";
                $description    = "Our events";
                $events          = kirby()->collection('events')->limit(5);

                return new Response(snippet('rssevents', compact('title', 'description', 'events') , true), 'application/rss+xml');
            }
        ],
    ],
];

This is already an incredibly simple approach that Kirby offers here, see routes documentation.

Declare the 'routes'. The pattern is the URL for the feed and the action is what happens when the URL is called in the browser.

The action is a function that declares a few variables ($title, $description and the recent entries from the $events collection and $books collection respectively) and returns …. Well, i admit the new Response is a bit of magic for me and where i am really grateful for others to share their knowledge.

But the shiny part is: we use a snippet, just like any other template snippet for the frontend rendering, and pass on the variables. Now the rss snippet is where we write xml markup for the rss feed, based on the code provided by Kev. I adjusted the variable names of the collections, the language, the favicon path, and the content fields' rendering. Since we're in the realm of template snippets, this is the usual Kirby template scripting.

Again, thanks to Kev for checking out all of the rss feed markup requirements!

Here's the final template snippet for the events site/snippets/rssevents.php:

<?php
echo '<?xml version="1.0" encoding="utf-8"?>';
?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title><?= $title ?></title>
    <link><?= site()->url() ?></link>
    <atom:link href="<?= site()->url() ?>/feed" rel="self" type="application/rss+xml" />
    <language>de-DE</language>
    <lastBuildDate><?= $events->first()->date()->toDate('r') ?></lastBuildDate>
    <description><?= $description ?></description>
    <image>
      <url><?= site()->url() ?>/favicon.svg</url>
      <title><?= $title ?></title>
      <link><?= site()->url() ?></link>
    </image>

    <?php foreach ($events as $item): ?>
      <item>
        <title><?= Xml::encode($item->title()) ?></title>
        <link><?= Xml::encode($item->url()) ?></link>
        <guid isPermaLink="false"><?= Xml::encode($item->id()) ?></guid>
        <pubDate><?= $item->date()->toDate('r') ?></pubDate>
        <description>

          <![CDATA[<?= $item->text()->toBlocks() ?>]]>

          <![CDATA[
            <hr>
            <h2>Informationen zur Veranstaltung</h2>
            <p>Veranstaltungsdatum: <?= $item->published() ?></p>
            <p>Eintritt: <?= $item->price()->kt() ?></p>
            <p>Anmeldung: <?= $item->rsvpText()->kt() ?></p>
            <p><a href="<?= Xml::encode($item->url()) ?>">Veranstaltung auf unserer Website ansehen</a></p>

          ]]>

        </description>
      </item>
    <?php endforeach; ?>
  </channel>
</rss>

Test the rss feed locally

I use Thunderbird for some of my emails. Since it comes with a feed reader, i tend to use it for testing rss feeds.

Pre-requisite: The localhost server must be running. (You know, every sign tells a story…)

I'm testing locally, so the feed URL for the events is http://localhost:8000/feed/events.

Now i go:

  1. Add the feed URL to Thunderbird.
  2. Check the output of the entries.
  3. If i find an issue: Update the php template.
  4. Rinse…: delete the feed in Thunderbird.
  5. …and repeat: starting with step 1.

And that's it. Feeds crossed off from the todo list.