diff --git a/docs/hacking.md b/docs/hacking.md index b0ab44b..055d3d1 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -41,10 +41,11 @@ template, and their inclusion in an INDEX is presently ad-hoc. ### Current Content Types & Indexes -There are 3 INDEX subclasses at present: TAG-INDEX, DATE-INDEX, and -NUMERIC-INDEX, for grouping content by tags, publishing date, and -reverse chronological order, respectively. Currently, there is only 1 -content type: POST, for blog entries. +There are 5 INDEX subclasses at present: TAG-INDEX, DATE-INDEX, +NUMERIC-INDEX, FEED, and TAG-FEED. Respectively, they support +grouping content by tags, publishing date, and reverse chronological +order. Feeds exist to special case RSS and ATOM generation. +Currently, there is only 1 content type: POST, for blog entries. I'm planning to add a content type PAGE, for static pages. It should be a pretty straightforward subclass of CONTENT with the necessary @@ -89,9 +90,8 @@ the objects of that content type by iterating over the objects in an appropriate fashion, rendering them, and passing the result to `write-page` (which should probably just be renamed to `write-file`). -After this, `render-indexes` and `render-feeds` are called, and an -'index.html' symlink is created to point to the first reverse -chronological index. +After this, `render-indexes` is called, and an 'index.html' symlink +is created to point to the first reverse chronological index. - `(deploy dir)` @@ -116,7 +116,7 @@ Unfortunately, this does not solve: was installed in the theme package. The plugin would need to do this itself or the template would need to be included in 'core'. 2. More seriously, there is no formal relationship between content - types and indexes. Indices include *ALL* objects in the `*content*` + types and indexes. Indexes include *ALL* objects in the `*content*` hash table. This may be undesirable and doesn't permit indexes dedicated to particular content types. diff --git a/src/coleslaw.lisp b/src/coleslaw.lisp index c21a003..e5b27bf 100644 --- a/src/coleslaw.lisp +++ b/src/coleslaw.lisp @@ -59,9 +59,8 @@ Additional args to render CONTENT can be passed via RENDER-ARGS." (when (probe-file dir) (run-program "rsync --delete -raz ~a ." dir))) (do-ctypes (publish (make-keyword ctype))) - (render-indexes) - (update-symlink "index.html" "1.html") - (render-feeds (feeds *config*)))) + (render-indexes (feeds *config*)) + (update-symlink "index.html" "1.html"))) (defgeneric deploy (staging) (:documentation "Deploy the STAGING dir, updating the .prev and .curr symlinks.") diff --git a/src/feeds.lisp b/src/feeds.lisp deleted file mode 100644 index 879f733..0000000 --- a/src/feeds.lisp +++ /dev/null @@ -1,27 +0,0 @@ -(in-package :coleslaw) - -(defun make-pubdate () - "Make a RFC1123 pubdate representing the current time." - (local-time:format-rfc1123-timestring nil (local-time:now))) - -(defun render-feed (posts &key path template tag) - (flet ((first-10 (list) (subseq list 0 (min (length list) 10))) - (tag-posts (list) (remove-if-not (lambda (x) (tag-p tag x)) list))) - (let ((template (theme-fn template "feeds")) - (index (if tag - (make-instance 'tag-index :slug path - :posts (first-10 (tag-posts posts))) - (make-instance 'index :slug path - :posts (first-10 posts))))) - (write-page (page-path index) (render-page index template))))) - -(defun render-feeds (tag-feeds) - "Render the default RSS and ATOM feeds along with any TAG-FEEDS." - (let ((posts (by-date (find-all 'post)))) - (dolist (feed '((:path "rss.xml" :template :rss-feed) - (:path "atom.xml" :template :atom-feed))) - (apply #'render-feed posts feed)) - (dolist (feed tag-feeds) - (apply #'render-feed posts (list :path (format nil "~A-rss.xml" feed) - :tag (make-tag feed) - :template :rss-feed))))) diff --git a/src/indexes.lisp b/src/indexes.lisp index e217a0f..72eb179 100644 --- a/src/indexes.lisp +++ b/src/indexes.lisp @@ -9,14 +9,21 @@ (defclass date-index (index) ()) (defclass numeric-index (index) ()) -(defmethod page-url ((object index)) - (index-id object)) +(defclass feed (index) + (format :initform nil :initarg :format :accessor feed-format)) +(defclass tag-feed (feed) ()) + (defmethod page-url ((object tag-index)) - (format nil "tag/~a" (index-id object))) + (format nil "tag/~a" (index-slug object))) (defmethod page-url ((object date-index)) - (format nil "date/~a" (index-id object))) + (format nil "date/~a" (index-slug object))) (defmethod page-url ((object numeric-index)) - (format nil "~d" (index-id object))) + (format nil "~d" (index-slug object))) + +(defmethod page-url ((object feed)) + (format nil "~(~a~).xml" (feed-format object))) +(defmethod page-url ((object tag-feed)) + (format nil "tag/~a~(~a~).xml" (index-slug object) (feed-format object))) (defmethod render ((object index) &key prev next) (funcall (theme-fn 'index) (list :tags (all-tags) @@ -50,27 +57,40 @@ :posts (remove-if-not (lambda (x) (month-p month x)) content) :title (format nil "Posts from ~a" month))) -(defun index-by-n (i content &optional (step 10)) +(defun index-by-n (i content) "Return the index for the Ith page of CONTENT in reverse chronological order." - (let* ((start (* step i)) - (end (min (length content) (+ start step)))) + (let ((content (subseq content (* 10 i)))) (make-instance 'numeric-index :slug (1+ i) - :posts (subseq content start end) - :title "Recent Posts"))) + :posts (take-up-to 10 content) + :title "Recent Posts"))) + +(defun render-feed (feed) + "Render the given FEED to both RSS and ATOM." + (let ((theme-fn (theme-fn (feed-format feed) "feeds"))) + (write-page (page-path feed) (render-page feed theme-fn)))) (defun render-index (index &rest render-args) "Render the given INDEX using RENDER-ARGS if provided." (write-page (page-path index) (apply #'render-page index nil render-args))) -(defun render-indexes () - "Render the indexes to view content in groups of size N, by month, and by tag." - (let ((results (by-date (find-all 'post)))) +(defun render-indexes (tag-feeds) + "Render the indexes to view content in groups of size N, by month, or by tag, +along with RSS and ATOM feeds and any supplied TAG-FEEDS." + (let ((content (by-date (find-all 'post)))) (dolist (tag (all-tags)) - (render-index (index-by-tag tag results))) + (render-index (index-by-tag tag content))) (dolist (month (all-months)) - (render-index (index-by-month month results))) - (dotimes (i (ceiling (length results) 10)) - (render-index (index-by-n i results) + (render-index (index-by-month month content))) + (dotimes (i (ceiling (length content) 10)) + (render-index (index-by-n i content) :prev (and (plusp i) i) - :next (and (< (* (1+ i) 10) (length results)) - (+ 2 i)))))) + :next (and (< (* (1+ i) 10) (length content)) + (+ 2 i)))) + (dolist (format '(rss atom)) + (dolist (tag tag-feeds) + (let ((posts (remove-if-now (lambda (x) (tag-p (make-tag tag) x)) content))) + (render-feed (make-instance 'tag-feed :posts (take-up-to 10 posts) + :format format + :slug tag)))) + (render-feed (make-instance 'feed :posts (take-up-to 10 content) + :format format))))) diff --git a/src/util.lisp b/src/util.lisp index 35ed841..a587c3a 100644 --- a/src/util.lisp +++ b/src/util.lisp @@ -69,3 +69,11 @@ an UNWIND-PROTECT, then change back to the current directory." (setf (current-directory) ,path) ,@body) (setf (current-directory) ,old))))) + +(defun take-up-to (n seq) + "Take elements from SEQ until all elements or N have been taken." + (subseq seq 0 (min (length seq) n))) + +(defun make-pubdate () + "Make a RFC1123 pubdate representing the current time." + (local-time:format-rfc1123-timestring nil (local-time:now))) diff --git a/themes/atom.tmpl b/themes/atom.tmpl index 4108c19..f1604d1 100644 --- a/themes/atom.tmpl +++ b/themes/atom.tmpl @@ -1,6 +1,6 @@ {namespace coleslaw.theme.feeds} -{template atom-feed} +{template atom} {\n} diff --git a/themes/rss.tmpl b/themes/rss.tmpl index 29f8cf7..126f7b7 100644 --- a/themes/rss.tmpl +++ b/themes/rss.tmpl @@ -1,6 +1,6 @@ {namespace coleslaw.theme.feeds} -{template rss-feed} +{template rss} {\n}