Flexible Lisp Blogware. Fork for personal use. Mirrored from https://github.com/kingcons/coleslaw originally.

indices.lisp 3.9KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. (in-package :coleslaw)
  2. (defun all-months ()
  3. "Retrieve a list of all months with published posts."
  4. (remove-duplicates (mapcar (lambda (x) (get-month (post-date x)))
  5. (hash-table-values *posts*)) :test #'string=))
  6. (defun all-tags ()
  7. "Retrieve a list of all tags used in posts."
  8. (reduce (lambda (x y) (union x y :test #'string=))
  9. (mapcar #'post-tags (hash-table-values *posts*))))
  10. (defun taglinks (&optional tags)
  11. "Generate links to all the tag indices or those in TAGS."
  12. (loop for tag in (or tags (sort (all-tags) #'string<))
  13. collect (list :url (format nil "tag/~a.html" tag) :name tag)))
  14. (defun monthlinks ()
  15. "Generate links to all the month indices."
  16. (loop for month in (sort (all-months) #'string<)
  17. collect (list :url (format nil "date/~a.html" month) :name month)))
  18. (defun get-month (timestamp)
  19. "Extract the YYYY-MM portion of TIMESTAMP."
  20. (subseq timestamp 0 7))
  21. (defun by-date (posts)
  22. "Sort POSTS in reverse chronological order."
  23. (sort posts #'string> :key #'post-date))
  24. (defun write-index (posts filename title &key prev next (relative t))
  25. "Write out the HTML for POSTS to FILENAME.html."
  26. (let ((content (loop for post in posts
  27. collect (list :url (if relative
  28. (format nil "../posts/~a" (post-url post))
  29. (format nil "~a/posts/~a"
  30. (domain *config*) (post-url post)))
  31. :title (post-title post)
  32. :date (post-date post)
  33. :content (render-content (post-content post)
  34. (post-format post))))))
  35. (render-page filename
  36. (funcall (theme-fn "INDEX")
  37. (list :taglinks (taglinks)
  38. :monthlinks (monthlinks)
  39. :siteroot (domain *config*)
  40. :title title
  41. :posts content
  42. :prev (and prev (format nil "~d.html" prev))
  43. :next (and next (format nil "~d.html" next)))))))
  44. (defun render-by-20 ()
  45. "Render the indices to view posts in reverse chronological order by 20."
  46. (flet ((by-20 (posts start)
  47. (let ((index (* 20 (1- start))))
  48. (subseq posts index (min (length posts) (+ index 20))))))
  49. (let ((posts (by-date (hash-table-values *posts*))))
  50. (loop for i = 1 then (1+ i)
  51. do (write-index (by-20 posts i) (format nil "~d.html" i) "Recent Posts"
  52. :prev (and (plusp (1- i)) (1- i))
  53. :next (and (< (* i 20) (length posts)) (1+ i))
  54. :relative nil)
  55. until (> (* i 20) (length posts)))
  56. (update-symlink "index.html" "1.html"))))
  57. (defun render-by-tag ()
  58. "Render the indices to view posts by tag."
  59. (loop for tag in (all-tags)
  60. do (flet ((match-tag (post)
  61. (member tag (post-tags post) :test #'string=)))
  62. (let ((posts (remove-if-not #'match-tag (hash-table-values *posts*))))
  63. (write-index (by-date posts)
  64. (format nil "tag/~a.html" tag)
  65. (format nil "Posts tagged ~a" tag))))))
  66. (defun render-by-month ()
  67. "Render the indices to view posts by month."
  68. (loop for month in (all-months)
  69. do (flet ((match-month (post)
  70. (search month (post-date post))))
  71. (let ((posts (remove-if-not #'match-month (hash-table-values *posts*))))
  72. (write-index (by-date posts)
  73. (format nil "date/~a.html" month)
  74. (format nil "Posts from ~a" month))))))
  75. (defun render-indices ()
  76. "Render the indices to view posts in groups of 20, by month, and by tag."
  77. (render-by-20)
  78. (render-by-tag)
  79. (render-by-month))