Add RSS support.
authorChristopher Lemmer Webber <cwebber@dustycloud.org>
Mon, 19 Nov 2018 21:11:56 +0000 (16:11 -0500)
committerDavid Thompson <dthompson2@worcester.edu>
Sun, 25 Nov 2018 20:56:21 +0000 (15:56 -0500)
* haunt/builder/rss.scm: New file with support for RSS feeds.
* Makefile.am: Add it.

Makefile.am
haunt/builder/rss.scm [new file with mode: 0644]

index 1389cfb..b30753a 100644 (file)
@@ -54,6 +54,7 @@ SOURCES =                                     \
   haunt/builder/assets.scm                     \
   haunt/builder/atom.scm                       \
   haunt/builder/blog.scm                       \
+  haunt/builder/rss.scm                                \
   haunt/reader.scm                             \
   haunt/reader/texinfo.scm                     \
   haunt/ui.scm                                 \
diff --git a/haunt/builder/rss.scm b/haunt/builder/rss.scm
new file mode 100644 (file)
index 0000000..1d988a7
--- /dev/null
@@ -0,0 +1,107 @@
+;;; Haunt --- Static site generator for GNU Guile
+;;; Copyright © 2018 Christopher Lemmer Webber <cwebber@dustycloud.org>
+;;;
+;;; This file is part of Haunt.
+;;;
+;;; Haunt is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; Haunt is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;;; General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with Haunt.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; RSS feed builder.
+;;
+;;; Code:
+
+(define-module (haunt builder rss)
+  #:use-module (srfi srfi-19)
+  #:use-module (srfi srfi-26)
+  #:use-module (ice-9 match)
+  #:use-module (sxml simple)
+  #:use-module (haunt site)
+  #:use-module (haunt post)
+  #:use-module (haunt page)
+  #:use-module (haunt utils)
+  #:use-module (haunt html)
+  #:use-module (haunt serve mime-types)
+  #:use-module (haunt builder atom)
+  #:export (rss-feed))
+
+;; Reader beware: this isn't as nice as atom.scm, because rss isn't
+;; as nice as atom.  Worse beats better on the play field again...
+
+;; RFC 822 dates are inferior to ISO 8601, but it's
+;; what RSS wants, so...
+(define (date->rfc822-str date)
+  (date->string date "~a, ~d ~b ~Y ~T ~z"))
+
+(define (sxml->xml* sxml port)
+  "Write SXML to PORT, preceded by an <?xml> tag."
+  (display "<?xml version=\"1.0\" encoding=\"utf-8\"?>" port)
+  (sxml->xml sxml port))
+
+(define* (post->rss-item site post #:key (blog-prefix ""))
+  "Convert POST into an RSS <item> node."
+  `(item
+    (title ,(post-ref post 'title))
+    ;; Looks like: <author>lawyer@boyer.net (Lawyer Boyer)</author>
+    (author
+     ,(let ((email (post-ref post 'email))
+            (author (post-ref post 'author)))
+        (string-append (if email
+                           (string-append email " ")
+                           "")
+                       (if author
+                           (string-append "(" author ")")
+                           ""))))
+    (pubDate ,(date->rfc822-str (post-date post)))
+    (link (@ (href ,(string-append blog-prefix "/"
+                                   (site-post-slug site post) ".html"))
+             (rel "alternate")))
+    (description ,(sxml->html-string (post-sxml post)))
+    ,@(map (lambda (enclosure)
+             `(enclosure (@ (title ,(enclosure-title enclosure))
+                            (url ,(enclosure-url enclosure))
+                            (type ,(enclosure-mime-type enclosure))
+                            ,@(map (match-lambda
+                                     ((key . value)
+                                      (list key value)))
+                                   (enclosure-extra enclosure)))))
+           (post-ref-all post 'enclosure))))
+
+(define* (rss-feed #:key
+                   (file-name "rss-feed.xml")
+                   (subtitle "Recent Posts")
+                   (filter posts/reverse-chronological)
+                   (max-entries 20)
+                   (blog-prefix ""))
+  "Return a builder procedure that renders a list of posts as an RSS
+feed.  All arguments are optional:
+
+FILE-NAME: The page file name
+SUBTITLE: The feed subtitle
+FILTER: The procedure called to manipulate the posts list before rendering
+MAX-ENTRIES: The maximum number of posts to render in the feed"
+  (lambda (site posts)
+    (make-page file-name
+               `(rss (@ (version "2.0"))
+                     (channel
+                      (title ,(site-title site))
+                      ;; It looks like RSS's description and atom's subtitle
+                      ;; are equivalent?
+                      (description ,subtitle)
+                      (pubDate ,(date->rfc822-str (current-date)))
+                      (link (@ (href ,(site-domain site))))
+                      ,@(map (cut post->rss-item site <>
+                                  #:blog-prefix blog-prefix)
+                             (take-up-to max-entries (filter posts)))))
+               sxml->xml*)))