Dear All, I've got a little static website that gets built with the Org publishing system. Recently I've been looking at ways to add a full-text RSS feed with ox-rss.el[0].
Here's a minimum example. Dependencies: - Emacs - Emacs Htmlize - Emacs Org - Emacs ox-rss.el - Make Incidentally, if you're on a Guix system, I've been using this manifest: #+begin_src scheme (specifications->manifest '("coreutils" "emacs" "emacs-htmlize" "emacs-org" "emacs-ox-rss" "make")) #+end_src File structure: #+begin_src shell $ tree . ├── Makefile ├── posts │ ├── another-post │ │ └── index.org │ └── a-post │ └── index.org └── publish.el #+end_src Files: #+begin_src org :tangle posts/a-post/index.org ,#+title: A post ,#+author: Jane Doe ,#+email: j...@example.com ,#+date: <2025-03-23 Sun> ,* Intro Foo and bar. - One - Two - Three ,* A section Some data: | Name | Phone | |-------+-------| | Alice | 0123 | | Bob | 1234 | | Carol | 2345 | #+end_src #+begin_src org :tangle posts/another-post/index.org ,#+title: Another post ,#+author: John Donne ,#+email: j...@example.com ,#+date: <2025-02-23 Sun> ,* Intro Foo and bar. - One - Two - Three ,* Another section Some code: ,#+begin_src emacs-lisp (defun my/rss-feed-generate (title list) "Generate the RSS feed as a string." (concat "#+TITLE: " title "\n\n" (org-list-to-subtree list))) ,#+end_src #+end_src #+begin_src emacs-lisp :tangle publish.el (package-initialize) (require 'ox-publish) (require 'ox-rss) (load "ox-rss.el") (defun my/rss-feed-generate (title list) "Generate the RSS feed as a string." (concat "#+TITLE: " title "\n\n" (org-list-to-subtree list))) (defun my/rss-feed-format-entry (entry style project) "Format ENTRY for the RSS feed." (cond ((not (directory-name-p entry)) (let ((file (org-publish--expand-file-name entry project)) (title (org-publish-find-title entry project))) (with-temp-buffer (insert (format "%s\n\n" title)) (insert-file-contents file) (buffer-string)))) ((eq style 'tree) (file-name-nondirectory (directory-file-name entry))) (t entry))) (defun my/rss-feed-publish (plist filename pub-dir) "Publish the RSS feed." (when (equal "rss.org" (file-name-nondirectory filename)) (org-rss-publish-to-rss plist filename pub-dir))) (setq org-publish-project-alist '(("rss" :base-directory "posts" :publishing-directory "build" :base-extension "org" :recursive t :publishing-function my/rss-feed-publish :rss-extension "xml" :section-numbers nil :table-of-contents nil :html-link-home "https://example.com" :html-link-use-abs-url t :html-link-org-files-as-html t :auto-sitemap t :sitemap-title "Test" :sitemap-filename "rss.org" :sitemap-function my/rss-feed-generate :sitemap-style tree :sitemap-sort-files anti-chronologically :sitemap-format-entry my/rss-feed-format-entry) ("all" :components ("rss")))) #+end_src #+begin_src makefile :tangle Makefile all: clean build build: emacs \ --batch \ --no-init-file \ --load publish.el \ --eval '(org-publish-all t)' \ --kill clean: rm -fr build rm -fr posts/rss.org rm -fr posts/rss.org~ .PHONY: all build clean #+end_src For simplicity, this minimal example only builds the XML feed and none of the actual pages. Here's the result: #+begin_src xml <?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" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"><channel> <title>Test</title> <atom:link href="https://example.com/rss.xml" rel="self" type="application/rss+xml" /> <link>https://example.com</link> <description><![CDATA[]]></description> <language>en</language> <pubDate>Mon, 03 Mar 2025 09:52:16 +0000</pubDate> <lastBuildDate>Mon, 03 Mar 2025 09:52:16 +0000</lastBuildDate> <generator>Emacs 29.4 Org-mode 9.7.20</generator> <webMaster>user@laptop (nil)</webMaster> <image> <url>https://orgmode.org/img/org-mode-unicorn-logo.png</url> <title>Test</title> <link>https://example.com</link> </image> <item> <title>another-post</title> <link>https://example.com/rss.html#org211204a</link> <author>user@laptop (nil)</author> <guid isPermaLink="false">https://example.com/rss.html#org211204a</guid> <pubDate>Mon, 03 Mar 2025 09:52:00 +0000</pubDate> <description><![CDATA[<div id="outline-container-orgfc772e2" class="outline-3"> <h3 id="orgfc772e2">Another post</h3> <div class="outline-text-3" id="text-orgfc772e2"> </div> <div id="outline-container-orgc8b6f19" class="outline-4"> <h4 id="orgc8b6f19">Intro</h4> <div class="outline-text-4" id="text-orgc8b6f19"> <p> Foo and bar. </p> </div> </div> <div id="outline-container-org63baba9" class="outline-4"> <h4 id="org63baba9">One</h4> <div class="outline-text-4" id="text-org63baba9"> </div> </div> <div id="outline-container-orgd30bd42" class="outline-4"> <h4 id="orgd30bd42">Two</h4> <div class="outline-text-4" id="text-orgd30bd42"> </div> </div> <div id="outline-container-org3fb09c5" class="outline-4"> <h4 id="org3fb09c5">Three</h4> <div class="outline-text-4" id="text-org3fb09c5"> </div> </div> <div id="outline-container-org9a50939" class="outline-4"> <h4 id="org9a50939">Another section</h4> <div class="outline-text-4" id="text-org9a50939"> <p> Some code: </p> <div class="org-src-container"> <pre class="src src-emacs-lisp">(<span style="font-weight: bold;">defun</span> <span style="font-weight: bold;">my/rss-feed-generate</span> (title list) <span style="font-style: italic;">"Generate the RSS feed as a string."</span> (concat <span style="font-style: italic;">"#+TITLE: "</span> title <span style="font-style: italic;">"\n\n"</span> (org-list-to-subtree list))) </pre> </div> </div> </div> </div> ]]></description> </item> <item> <title>a-post</title> <link>https://example.com/rss.html#orga85cbea</link> <author>user@laptop (nil)</author> <guid isPermaLink="false">https://example.com/rss.html#orga85cbea</guid> <pubDate>Mon, 03 Mar 2025 09:52:00 +0000</pubDate> <description><![CDATA[<div id="outline-container-org03c3b3b" class="outline-3"> <h3 id="org03c3b3b">A post</h3> <div class="outline-text-3" id="text-org03c3b3b"> </div> <div id="outline-container-org7453f96" class="outline-4"> <h4 id="org7453f96">Intro</h4> <div class="outline-text-4" id="text-org7453f96"> <p> Foo and bar. </p> </div> </div> <div id="outline-container-org12bfd39" class="outline-4"> <h4 id="org12bfd39">One</h4> <div class="outline-text-4" id="text-org12bfd39"> </div> </div> <div id="outline-container-org05d0a1e" class="outline-4"> <h4 id="org05d0a1e">Two</h4> <div class="outline-text-4" id="text-org05d0a1e"> </div> </div> <div id="outline-container-org1692eeb" class="outline-4"> <h4 id="org1692eeb">Three</h4> <div class="outline-text-4" id="text-org1692eeb"> </div> </div> <div id="outline-container-org89963e0" class="outline-4"> <h4 id="org89963e0">A section</h4> <div class="outline-text-4" id="text-org89963e0"> <p> Some data: </p> <table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"> <colgroup> <col class="org-left" /> <col class="org-right" /> </colgroup> <thead> <tr> <th scope="col" class="org-left">Name</th> <th scope="col" class="org-right">Phone</th> </tr> </thead> <tbody> <tr> <td class="org-left">Alice</td> <td class="org-right">0123</td> </tr> <tr> <td class="org-left">Bob</td> <td class="org-right">1234</td> </tr> <tr> <td class="org-left">Carol</td> <td class="org-right">2345</td> </tr> </tbody> </table> </div> </div> </div> ]]></description> </item> </channel> </rss> #+end_src There's a couple of things that I'm not sure about. My main concern is around bullet point lists. Lists like this: #+begin_src org - One - Two - Three #+end_src get transformed to: #+begin_src html <div id="outline-container-org63baba9" class="outline-4"> <h4 id="org63baba9">One</h4> <div class="outline-text-4" id="text-org63baba9"> </div> </div> <div id="outline-container-orgd30bd42" class="outline-4"> <h4 id="orgd30bd42">Two</h4> <div class="outline-text-4" id="text-orgd30bd42"> </div> </div> <div id="outline-container-org3fb09c5" class="outline-4"> <h4 id="org3fb09c5">Three</h4> <div class="outline-text-4" id="text-org3fb09c5"> </div> </div> #+end_src whereas I'd expect a HTML UL block. The problem seems to boil down to ~org-list-to-subtree~ and the way the original Org files are merged into a single Org sitemap. For instance, this: #+begin_src emacs-lisp (org-list-to-subtree '(unordered ("* Intro\n\n- one\n- two\n"))) #+end_src returns: #+begin_src text ,* * Intro ,** one ,** two #+end_src Whereas, I'd expect: #+begin_src text ,** Intro - one - two #+end_src Anybody knows whether I'm not using ~org-list-to-subtree~ correctly or if there's a more clever way to define a ~my/rss-feed-generate~ function? Separately, less important, I wonder why I need to explicitly load ~(load "ox-rss.el")~. Only requiring (without also loading) the library results in a silent failure, i.e. an XML with no items. I suppose this may have to do with the lack of an ~autoload~ declaration in the ox-rss.el library? Any other suggestion around producing RSS feeds out of a Org-based website also appreciated. Have a lovely day! Best wishes, Fabio. PS: Should you reply to this email, kindly CC me in. - 0: https://github.com/BenedictHW/ox-rss -- Fabio Natali https://fabionatali.com