12 Commits

Author SHA1 Message Date
David Thompson
1bfb388123 Bump version to 0.2.4. 2018-11-29 11:40:46 -05:00
Christopher Lemmer Webber
286edaa5de atom: links should use href attribute, not url attribute.
* haunt/builder/atom.scm (post->atom-entry): Switch url attribute to
  href attribute.
2018-11-29 11:29:59 -05:00
David Thompson
a7dac982c2 git: Ignore tarball signature files. 2018-11-25 16:05:46 -05:00
David Thompson
928f0e2921 Bump version to 0.2.3. 2018-11-25 15:59:07 -05:00
David Thompson
98471930c9 README: Mention RSS. 2018-11-25 15:58:42 -05:00
Christopher Lemmer Webber
8891da0e3a Add RSS support.
* haunt/builder/rss.scm: New file with support for RSS feeds.
* Makefile.am: Add it.
2018-11-25 15:56:21 -05:00
David Thompson
15c4605e1d atom: Add support for enclosures.
The most notable use-case here is allowing Haunt to be used for
podcasting.  Thanks to Christopher Lemmer Webber for wanting to use
Haunt to build their podcast Atom feed!

* haunt/builder/atom.scm (<enclosure>): New record type.
(make-enclosure, enclosure?, enclosure-title, enclosure-url,
enclosure-extra, enclosure-mime-type, parse-enclosure): New
procedures.
(post->atom-entry): Render enclosures.
2018-11-18 20:47:47 -05:00
David Thompson
51e2f10645 post: Add post-ref-all procedure.
* haunt/post.scm (post-ref-all): New procedure.
2018-11-18 20:46:36 -05:00
David Thompson
ea14e56f0c post: Fix export for register-metadata-parser!
* haunt/post.scm: Fix typo preventing other modules from using
  register-metadata-parser!
2018-11-18 20:45:31 -05:00
David Thompson
b37ef63b8a Thank Jorge Maldonado Ventura. 2018-04-02 10:11:06 -04:00
Jorge Maldonado Ventura
f49a5cb3d4 doc: Fix tarball download link.
* doc/haunt.texi (Downloading): Fix outdated download link.
2018-04-02 10:10:18 -04:00
David Thompson
efa145cc4b Thank Alex Kost. 2018-03-19 10:11:25 -04:00
9 changed files with 227 additions and 8 deletions

1
.gitignore vendored
View File

@@ -13,6 +13,7 @@ Makefile.in
/example/site/ /example/site/
/scripts/haunt /scripts/haunt
*.tar.gz *.tar.gz
*.tar.gz.asc
/website/site /website/site
/test-env /test-env
*.log *.log

View File

@@ -54,6 +54,7 @@ SOURCES = \
haunt/builder/assets.scm \ haunt/builder/assets.scm \
haunt/builder/atom.scm \ haunt/builder/atom.scm \
haunt/builder/blog.scm \ haunt/builder/blog.scm \
haunt/builder/rss.scm \
haunt/reader.scm \ haunt/reader.scm \
haunt/reader/texinfo.scm \ haunt/reader/texinfo.scm \
haunt/ui.scm \ haunt/ui.scm \

2
README
View File

@@ -5,7 +5,7 @@ simple, functional, and extensible.
* Features * Features
- Easy blog and Atom feed generation - Easy blog and Atom/RSS feed generation
- Supports any markup language that can be parsed to SXML - Supports any markup language that can be parsed to SXML
- Simple development server - Simple development server
- Purely functional build process - Purely functional build process

2
THANKS
View File

@@ -1,3 +1,5 @@
Ben Sturmfels <ben@sturm.com.au> Ben Sturmfels <ben@sturm.com.au>
Vladimir Zhbanov <vzhbanov@gmail.com> Vladimir Zhbanov <vzhbanov@gmail.com>
Urbain Vaes <urbain@vaes.uk> Urbain Vaes <urbain@vaes.uk>
Alex Kost <alezost@gmail.com>
Jorge Maldonado Ventura <jorgesumle@freakspot.net>

View File

@@ -1,6 +1,6 @@
dnl -*- Autoconf -*- dnl -*- Autoconf -*-
AC_INIT(Haunt, 0.2.2) AC_INIT(Haunt, 0.2.4)
AC_CONFIG_SRCDIR(haunt) AC_CONFIG_SRCDIR(haunt)
AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([color-tests -Wall -Wno-portability foreign]) AM_INIT_AUTOMAKE([color-tests -Wall -Wno-portability foreign])

View File

@@ -144,9 +144,9 @@ Happy haunting!
@node Downloading @node Downloading
@section Downloading @section Downloading
Official Haunt source code release tarballs can be found on the Official Haunt source code release tarballs can be found under Releases
@url{http://haunt.dthompson.us/downloads.html, downloads page} of in @url{https://dthompson.us/projects/haunt.html, Haunt's website},
Haunt's website, along with their associated checksums. along with their associated checksums.
@node Requirements @node Requirements
@section Requirements @section Requirements

View File

@@ -24,6 +24,7 @@
;;; Code: ;;; Code:
(define-module (haunt builder atom) (define-module (haunt builder atom)
#:use-module (srfi srfi-9)
#:use-module (srfi srfi-19) #:use-module (srfi srfi-19)
#:use-module (srfi srfi-26) #:use-module (srfi srfi-26)
#:use-module (ice-9 match) #:use-module (ice-9 match)
@@ -33,9 +34,98 @@
#:use-module (haunt page) #:use-module (haunt page)
#:use-module (haunt utils) #:use-module (haunt utils)
#:use-module (haunt html) #:use-module (haunt html)
#:export (atom-feed #:use-module (haunt serve mime-types)
#:export (make-enclosure
enclosure?
enclosure-title
enclosure-url
enclosure-extra
enclosure-mime-type
atom-feed
atom-feeds-by-tag)) atom-feeds-by-tag))
(define-record-type <enclosure>
(make-enclosure title url extra)
enclosure?
(title enclosure-title)
(url enclosure-url)
(extra enclosure-extra))
(define (enclosure-mime-type enclosure)
(mime-type (enclosure-url enclosure)))
(define char-set:enclosure-key
(char-set-union char-set:letter+digit
(char-set-delete char-set:punctuation #\: #\")
(char-set-delete char-set:symbol #\=)))
(define (parse-enclosure s)
(call-with-input-string s
(lambda (port)
(define (assert-char char)
(let ((c (read-char port)))
(unless (eqv? c char)
(error "enclosure: parse: expected" char "got" c))))
(define (whitespace? char)
(char-set-contains? char-set:whitespace char))
(define (consume-whitespace)
(match (peek-char port)
((? eof-object?) *unspecified*)
((? whitespace?)
(read-char port)
(consume-whitespace))
(_ *unspecified*)))
(define (read-escape-character)
(match (read-char port)
(#\" #\")
(#\\ #\\)
(char (error "enclosure: parse: invalid escape character:" char))))
(define (read-unquoted-string)
(list->string
(let loop ()
(let ((c (peek-char port)))
(cond
((eof-object? c)
'())
((char-set-contains? char-set:enclosure-key c)
(read-char port)
(cons c (loop)))
(else
'()))))))
(define (read-string)
(if (eqv? (peek-char port) #\")
(begin
(assert-char #\")
(list->string
(let loop ()
(match (read-char port)
((? eof-object?)
(error "enclosure: parse: EOF while reading string"))
(#\" '())
(#\\ (cons (read-escape-character) (loop)))
(char (cons char (loop)))))))
(read-unquoted-string)))
(define (read-key)
(string->symbol (read-unquoted-string)))
(let loop ((attrs '()))
(consume-whitespace)
(if (eof-object? (peek-char port))
(make-enclosure (assq-ref attrs 'title)
(assq-ref attrs 'url)
(let loop ((attrs attrs))
(match attrs
(() '())
((((or 'title 'url) . _) . rest)
(loop rest))
((attr . rest)
(cons attr (loop rest))))))
(let ((key (read-key)))
(assert-char #\:)
(loop (cons (cons key (read-string)) attrs))))))))
(register-metadata-parser! 'enclosure parse-enclosure)
(define (sxml->xml* sxml port) (define (sxml->xml* sxml port)
"Write SXML to PORT, preceded by an <?xml> tag." "Write SXML to PORT, preceded by an <?xml> tag."
(display "<?xml version=\"1.0\" encoding=\"utf-8\"?>" port) (display "<?xml version=\"1.0\" encoding=\"utf-8\"?>" port)
@@ -58,7 +148,17 @@
(site-post-slug site post) ".html")) (site-post-slug site post) ".html"))
(rel "alternate"))) (rel "alternate")))
(summary (@ (type "html")) (summary (@ (type "html"))
,(sxml->html-string (post-sxml post))))) ,(sxml->html-string (post-sxml post)))
,@(map (lambda (enclosure)
`(link (@ (rel "enclosure")
(title ,(enclosure-title enclosure))
(href ,(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* (atom-feed #:key (define* (atom-feed #:key
(file-name "feed.xml") (file-name "feed.xml")

107
haunt/builder/rss.scm Normal file
View File

@@ -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*)))

View File

@@ -36,13 +36,14 @@
post-sxml post-sxml
post-metadata post-metadata
post-ref post-ref
post-ref-all
post-slug post-slug
%default-date %default-date
post-date post-date
posts/reverse-chronological posts/reverse-chronological
posts/group-by-tag posts/group-by-tag
register-metdata-parser! register-metadata-parser!
parse-metadata parse-metadata
read-metadata-headers)) read-metadata-headers))
@@ -57,6 +58,13 @@
"Return the metadata corresponding to KEY within POST." "Return the metadata corresponding to KEY within POST."
(assq-ref (post-metadata post) key)) (assq-ref (post-metadata post) key))
(define (post-ref-all post key)
"Return a list of all metadata values for KEY within POST."
(filter-map (match-lambda
((k . v)
(and (eq? key k) v)))
(post-metadata post)))
(define char-set:slug (define char-set:slug
(char-set-union char-set:letter+digit (char-set #\-))) (char-set-union char-set:letter+digit (char-set #\-)))