fido-node.github.io/posts/add_rss_to_blog.html

133 lines
8.8 KiB
HTML
Raw Permalink Normal View History

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"/><meta author="Alex Mikhailov"/><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/><meta name="color-scheme" content="light dark"/><meta http-equiv="content-language" content="en-us"/><meta name="description" content="Let&apos;s add RSS feed to blog"/><meta property="og:description" content="Let&apos;s add RSS feed to blog"/><meta property="og:image" content="https://fidonode.me/resources/images/preview/posts/add_rss_to_blog.org.png"/><meta property="og:title" content="Org blog with RSS"/><meta name="twitter:description" content="Let&apos;s add RSS feed to blog"/><meta name="twitter:title" content="Org blog with RSS"/><meta name="twitter:image" content="https://fidonode.me/resources/images/preview/posts/add_rss_to_blog.org.png"/><meta name="twitter:card" content="summary_large_image"/><link rel="icon" type="image/x-icon" href="/resources/favicon.ico"/><link rel="stylesheet" type="text/css" href="/resources/css/pico.sand.min.css"/><script defer="true" src="https://umami.dokutsu.xyz/script.js" data-website-id="d52d9af1-0c7d-4531-84c6-0b9c2850011f"></script><title>Org blog with RSS</title><link id="highlight-theme" rel="stylesheet" type="text/css"/><script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script><script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/bash.min.js"></script><script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/lisp.min.js"></script><script src="/resources/js/theme-selector.js"></script></head><body><header class="header"><div class="container"><nav><ul><li><strong>Alex Mikhailov</strong></li></ul><ul><li><a href="/index.html">About</a></li><li><a href="/posts.html">Blog</a></li><li><a href="/rss.xml">RSS</a></li></ul></nav></div></header><main class="container blog-post"><hgroup><h1>Org blog with RSS</h1><p>Let&apos;s add RSS feed to blog</p><nav><ul><li>Tags:</li><li><mark><a href="/tags/@org-mode.html" class="secondary">@org-mode</a></mark></li><li><mark><a href="/tags/@elisp.html" class="secondary">@elisp</a></mark></li><li><mark><a href="/tags/@rss.html" class="secondary">@rss</a></mark></li></ul></nav></hgroup><div id="table-of-contents" role="doc-toc">
<h2>Table of Contents</h2>
<div id="text-table-of-contents" role="doc-toc">
<ul>
<li><a href="#org1f81ef0">Why do you even need RSS?</a></li>
<li><a href="#orgd8cf0e3">Add RSS feed</a>
<ul>
<li><a href="#orgf1fa9c5">Use sitemap backend in the build</a></li>
<li><a href="#org8913545">Publishing and formatting functions</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div id="outline-container-org1f81ef0" class="outline-2">
<h2 id="org1f81ef0">Why do you even need RSS?</h2>
<div class="outline-text-2" id="text-org1f81ef0">
<p>
RSS might seem like an outdated, marginal thing. But it still has at least one benefit—you can use an RSS feed as a sitemap for search engines. Plus, it's pretty geeky.
</p>
</div>
</div>
<div id="outline-container-orgd8cf0e3" class="outline-2">
<h2 id="orgd8cf0e3">Add RSS feed</h2>
<div class="outline-text-2" id="text-orgd8cf0e3">
<p>
So, what's happening here? Let's start by integrating our templating functions into the build.
</p>
</div>
<div id="outline-container-orgf1fa9c5" class="outline-3">
<h3 id="orgf1fa9c5">Use sitemap backend in the build</h3>
<div class="outline-text-3" id="text-orgf1fa9c5">
<pre><code class="language-lisp">(setq org-publish-project-alist
(list
(list &quot;blog-rss&quot;
:author &quot;Alex M&quot;
:email &quot;iam@fidonode.me&quot;
:base-directory my/blog-src-path
:base-extension &quot;org&quot;
:recursive t
:exclude (regexp-opt &apos;(&quot;rss.org&quot; &quot;index.org&quot; &quot;404.org&quot; &quot;posts.org&quot;))
:publishing-function &apos;my/publish-to-rss
:publishing-directory my/web-export-path
:rss-extension &quot;xml&quot;
:html-link-home my/url
:html-link-use-abs-url t
:html-link-org-files-as-html t
:auto-sitemap t
:sitemap-filename &quot;rss.org&quot;
:sitemap-title &quot;rss&quot;
:sitemap-style &apos;list
:sitemap-sort-files &apos;anti-chronologically
:sitemap-function &apos;my/format-rss-feed
:sitemap-format-entry &apos;my/format-rss-feed-entry)
))
</code></pre>
<p>
How does it work? As you can see, we use the default sitemap generator from Org Export with some additional steps. By default, the sitemap generator collects all org files from <code>:base-directory</code>. It then places links to these files as separate entries into an intermediate org file and publishes this org file. We use custom functions for collecting entries, formatting entries, and publishing the org file.
</p>
</div>
</div>
<div id="outline-container-org8913545" class="outline-3">
<h3 id="org8913545">Publishing and formatting functions</h3>
<div class="outline-text-3" id="text-org8913545">
<p>
We need a mandatory dependency because we don't want to mess with forming correct XML by ourselves.
</p>
<pre><code class="language-lisp">(require &apos;ox-rss)
</code></pre>
<p>
Here's the core of the process. We need to prepare entries before feeding them to the <code>org-rss-publish-to-rss</code> function. Since we're doing it our own way, we need to start by creating a bullet with a link to the post, and then add <code>RSS_PERMALINK</code>, <code>RSS_TITLE</code>, and <code>PUBDATE</code>. I also add the first two lines of the blog post instead of a description. This is the native way to do a preview in the RSS world.
</p>
<pre><code class="language-lisp">(defun my/format-rss-feed-entry (entry style project)
&quot;Format ENTRY for the RSS feed.
ENTRY is a file name. STYLE is either &apos;list&apos; or &apos;tree&apos;.
PROJECT is the current project.&quot;
(cond ((not (directory-name-p entry))
(let* ((file (org-publish--expand-file-name entry project))
(title (org-publish-find-title entry project))
(date (format-time-string &quot;%Y-%m-%d&quot; (org-publish-find-date entry project)))
(link (concat (file-name-sans-extension entry) &quot;.html&quot;)))
(with-temp-buffer
(org-mode)
(insert (format &quot;* [[file:%s][%s]]\n&quot; file title))
(org-set-property &quot;RSS_PERMALINK&quot; link)
(org-set-property &quot;RSS_TITLE&quot; title)
(org-set-property &quot;PUBDATE&quot; date)
(let ((first-two-lines (with-temp-buffer
(insert-file-contents file)
(buffer-substring-no-properties
(point-min)
(progn (forward-line 2) (point))))))
(if (string-suffix-p &quot;\n&quot; first-two-lines)
(setq first-two-lines (substring first-two-lines 0 -1)))
(insert first-two-lines))
(goto-char (point-max))
(insert &quot;...&quot;)
(buffer-string))))
((eq style &apos;tree)
;; Return only last subdir.
(file-name-nondirectory (directory-file-name entry)))
(t entry)))
</code></pre>
<p>
This function creates the content of the intermediate org file. Mostly a rudimentary title, and then we ask Org to unwind the list of collected files with the <code>my/format-rss-feed-entry</code> function.
</p>
<pre><code class="language-lisp">(defun my/format-rss-feed (title list)
&quot;Generate RSS feed, as a string.
TITLE is the title of the RSS feed. LIST is an internal
representation for the files to include, as returned by
`org-list-to-lisp&apos;. PROJECT is the current project.&quot;
(concat &quot;#+TITLE: &quot; title &quot;\n&quot;
&quot;#+STARTUP: showall \n\n&quot;
(org-list-to-subtree list 1 &apos;(:icount &quot;&quot; :istart &quot;&quot;))))
</code></pre>
<p>
This function replaces the default publishing function to filter everything except the intermediate file before publishing.
</p>
<pre><code class="language-lisp">(defun my/publish-to-rss (plist filename pub-dir)
&quot;Publish RSS with PLIST, only when FILENAME is &apos;rss.org&apos;.
PUB-DIR is when the output will be placed.&quot;
(if (equal &quot;rss.org&quot; (file-name-nondirectory filename))
(org-rss-publish-to-rss plist filename pub-dir)))
</code></pre>
<p>
Voila! Now you have an <code>rss.xml</code> in your export path.
</p>
</div>
</div>
</div>
</main><footer class="footer"><div class="container"><hr/><small><p>Alex Mikhailov</p><p>Built with: <a href="https://www.gnu.org/software/emacs/">GNU Emacs</a> <a href="https://orgmode.org/">Org Mode</a> <a href="https://picocss.com/">picocss</a></p></small></div></footer></body></html>