Configuring Custom RSS Feeds when using a Custom Page

I am trying to setup a linkroll sorta similar to what Daring Fireball and many others do.

At a high level it’s a reverse chronological page sorted by date with a list of links compromised of individual posts where the title links to the 3rd party URL and the body has my brief commentary. This is not very imaginative but if helpful to see it I have a working version here:

https://danielraffel.me/links/

How it works: I wrote a GCP Cloud Function that listens to a webhook from the bookmarking service omnivore.app and when it’s triggered it programmatically posts links which feature a custom omnivore tag (and annotation) to my blog with the tag ‘links’ right after I save them on Omnivore. It makes sharing interesting stuff I find a little easier than always having to write a traditional blog post. But, obviously there will be a lot more of these generated. While I do want a custom feed for them I also want them to avoid them appearing on my main blog.

Here’s where I am at: I have the content from Omnivore auto-posting to Ghost and the links getting reformated how I want on a custom links.hbs. But, I can’t figure out how to create a custom RSS feed for the linkroll hosted on links.hbs. And, I can’t figure out what I am doing wrong.

I have tried a few things to make this work and could use some help configuring my routes.yaml and / or revisiting how I organize the site. I am quite flexible to adjust things if there’s a way to get this working.

At a high level my intentions are for my blog posts to appear on the top level domain without showing any of the posts that are tagged as links. And, I want to host a custom page that renders posts that are just the links. And I want each of those to have custom RSS feeds.

I’m trying to figure out how to configure my Routes.yaml and RSS.hbs so that Ghost can do the following:

  1. The main domain, https://danielraffel.me/, should display all blog posts, excluding those tagged as links. Additionally, I want danielraffel.me/rss to provide an RSS feed which omits posts tagged with links.

  2. The URL danielraffel.me/links is currently dedicated to a custom links.hbs that renders links I am posting programmatically. This page already exists and aggregates all posts tagged links, sorted by date in reverse chronological order. This page should host a custom RSS feed available at danielraffel.me/links.rss featuring only the posts tagged with links. I cannot figure out how to set up an RSS feed to do this.

I’ve tried a variety of configurations but just can’t figure out how to support #2 while also supporting #1. For example I seem to see all posts in the RSS feed when I add danielraffel.me/links/rss to my feed reader when I would expect to only see the ones tagged links. I’ve played around customizing the links/rss file to remove limit all and to include filter=“tag:links” in #get but nothing I’ve tried has worked and I’m sorta outta ideas.

Any counsel for how I might explore getting this working would be greatly appreciated. Thanks!

route.yaml

routes:
  /rss/:
    template: rss
    content_type: text/xml

  /links/rss/:
    template: links/rss
    content_type: text/xml

collections:
  /:
    permalink: /{year}/{month}/{day}/{slug}/
    template: index
    filter: tag:-links

  /links/:
    permalink: /links/{year}/{month}/{day}/{slug}/
    template: links
    filter: primary_tag:links
    data: tag.links

taxonomies:
  tag: /tag/{slug}/
  author: /author/{slug}/

rss.hbs

<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
<channel>
<title><![CDATA[ {{@site.title}} ]]></title>
<description><![CDATA[ {{@site.description}} ]]></description>
<link>{{@site.url}}</link>
<image>
    <url>{{@site.url}}/favicon.png</url>
    <title>{{@site.title}}</title>
    <link>{{@site.url}}</link>
</image>
<lastBuildDate>{{date format="ddd, DD MMM YYYY HH:mm:ss ZZ"}}</lastBuildDate>
<atom:link href="{{@site.url}}" rel="self" type="application/rss+xml"/>
<ttl>60</ttl>
{{#get "posts" limit="all" include="authors,tags"}}
    {{#foreach posts}}
    <item>
        <title><![CDATA[ {{title}} ]]></title>
        <description><![CDATA[ {{excerpt}} ]]></description>
        <link>{{url absolute="true"}}</link>
        <guid isPermaLink="false">{{id}}</guid>
        <category><![CDATA[ {{primary_tag.name}} ]]></category>
        <dc:creator><![CDATA[ {{primary_author.name}} ]]></dc:creator>
        <pubDate>{{date format="ddd, DD MMM YYYY HH:mm:ss ZZ"}}</pubDate>
        <media:content url="{{feature_image}}" medium="image"/>
        <content:encoded><![CDATA[ {{content}} ]]></content:encoded>
    </item>
    {{/foreach}}
{{/get}}

</channel>
</rss>
1 Like

Looks like you’re really close.

You have two separate copies of the rss.hbs file, right? One in the theme root and one in the links folder?

So I’d edit each rss.hbs file.
You need to add a filter to this bit, right here:

{{#get "posts" limit="all" include="authors,tags"}}

to

{{#get "posts" limit="all" filter="primary_tag:links" include="authors,tags"}}

or

{{#get "posts" limit="all" filter="primary_tag:-links" include="authors,tags"}}

Also looks like you need to tweak your routes file. For the links and not-links routes, the filter needs to match except the -, so either do both by primary_tag, or both by tags, but not a mix.

TL;DR version: It doesn’t look like there’s a way to apply a filter to an rss file in routes.yaml (or maybe I don’t know what it is), but for a small number of routes like this, just make a separate file for each and add a filter.

And: always match your filters so that posts have somewhere to be. When you have two collections, all posts should be in one.

First, the good news: thanks to the filter changes you suggested, I’ve successfully got both RSS feeds working. The feeds at Daniel's Journal and Daniel's Journal are correctly displaying the tags defined in routes.yaml.

Now, the bad news: the {{ghost_head}} element is generating metadata that links to a generic RSS feed on every page of the website (for example, in routes.yaml, this corresponds to the home/rss template). I’m struggling to figure out how to modify ghost_head or the default.hbs file to ensure that the page at links - Daniel's Journal has metadata that directs to its specific RSS feed, which is at Daniel's Journal (as defined under the links/rss template in routes.yaml). It seems like ghost_head wants to use the home/rss everywhere and I can’t figure out how to override that on the links page.

This is functioning as intended:
Visiting the main URL, http://danielraffel.me, and then adding this URL to a feed reader correctly redirects to Daniel's Journal. This is due to the {{ghost_head}} tag in conjunction with the home/rss template defined in routes.yaml, which results in the page including <link rel="alternate" type="application/rss+xml" title="Daniel's Journal" href="https://danielraffel.me/rss/">.

This is not functioning as intended:
When accessing http://danielraffel.me/**links** and using this URL in a feed reader, it incorrectly redirects to Daniel's Journal as well. This happens because the {{ghost_head}} tag and the home/rss template in routes.yaml generate the same link tag <link rel="alternate" type="application/rss+xml" title="Daniel's Journal" href="https://danielraffel.me/rss/">. Ideally, it should use the links/rss template specified in routes.yaml for this URL.

I guess the question is whether you have any idea how to link to the right feed template on the links page? I’m hoping to avoid using JS but open to it if need be.

routes:
  /links/rss/:
    template: links/rss
    content_type: text/xml

  /rss/:
    template: home/rss
    content_type: text/xml

collections:
  /:
    permalink: /{year}/{month}/{day}/{slug}/
    template: index
    filter: primary_tag:-links

  /links/:
    permalink: /links/{year}/{month}/{day}/{slug}/
    template: links
    filter: primary_tag:links
    data: tag.links

taxonomies:
  tag: /tag/{slug}/
  author: /author/{slug}/

So… I don’t see how to get {{ghost_head}} to realize there’s a custom rss feed on the route you’re on.

So… at the top of the links.hbs template:

<script>
  document.querySelector('link[type="application/rss+xml"]').href = window.location + "rss/"
</script>

===

Side note for anyone interested: the values below work for getting the non-customized rss feeds to correctly pull the posts at /links at /rss/links (without having to make a new rss file for each route), but they don’t get ghost_head to behave.

routes:

collections:
  /:
    permalink: /{year}/{month}/{day}/{slug}/
    template: index
    filter: primary_tag:-links

  /links/:
    permalink: /links/{year}/{month}/{day}/{slug}/
    template: links
    filter: primary_tag:links
    data: tag.links
    

taxonomies:
  tag: /tag/{slug}/
  author: /author/{slug}/

This uses the default RSS. rss.hbs is not loaded at all.

I filed a bug: {{ghost_head}} doesn't recognize custom rss feed for the route I am on · Issue #19615 · TryGhost/Ghost · GitHub

Thanks for the suggested script!

Unfortunately, using JS is not working. My hunch is that either traditional feed readers do not execute JavaScript and are designed to simply parse and display content based on existing XML files, and/or links to RSS or Atom feeds.

Alternatively, perhaps the route URLs are colliding and the template linking to home/rss is winning 100% of the time.

FWIW I added some console logging and can confirm that the HTML is being rendered:

Found RSS link: <link rel=​"alternate" type=​"application/​rss+xml" title=​"Daniel's Journal" href=​"http:​/​/​localhost:​2368/​links/​rss/​">​
links/:275 Setting new RSS href to: http://localhost:2368/links/rss/

If you or anyone has ideas I’d love to figure out a solution.

Thanks for your help @Cathy_Sarisky

I’ve detailed my experience of getting all this to function on my blog and included a thank you to you in the footer. You can find the post here:

Additionally, I shared OmnivoreToGhostSync which is a Cloud Function designed to seamlessly integrate the bookmarking service Omnivorewith Ghost. This project simplifies the process of publishing curated links and annotations from Omnivore directly to a Ghost blog, making it ideal for bloggers who want a lightweight mechanism to easily post links and brief commentary (when saving a bookmark.)

1 Like

If you’re running into issues with JS, you can try inserting the proper feed tag before and after the Ghost head - hopefully feed readers will pick the first or last RSS link :stuck_out_tongue:

You should be able to use the contentFor and block helpers to experiment with it:

2 Likes

Good point. I don’t personally know enough about how feed readers deal with multiple feed links, but some googling got me an article saying the first link is treated as default.

1 Like