|
@@ -5,27 +5,6 @@
|
5
|
5
|
(posts :initform nil :initarg :posts :accessor index-posts)
|
6
|
6
|
(title :initform nil :initarg :title :accessor index-title)))
|
7
|
7
|
|
8
|
|
-(defclass tag-index (index) ())
|
9
|
|
-(defclass date-index (index) ())
|
10
|
|
-(defclass numeric-index (index) ())
|
11
|
|
-
|
12
|
|
-(defclass feed (index)
|
13
|
|
- ((format :initform nil :initarg :format :accessor feed-format)))
|
14
|
|
-;; TODO: tag-feed isn't reached by do-subclasses!
|
15
|
|
-(defclass tag-feed (feed) ())
|
16
|
|
-
|
17
|
|
-(defmethod page-url ((object tag-index))
|
18
|
|
- (format nil "tag/~a" (index-slug object)))
|
19
|
|
-(defmethod page-url ((object date-index))
|
20
|
|
- (format nil "date/~a" (index-slug object)))
|
21
|
|
-(defmethod page-url ((object numeric-index))
|
22
|
|
- (format nil "~d" (index-slug object)))
|
23
|
|
-
|
24
|
|
-(defmethod page-url ((object feed))
|
25
|
|
- (format nil "~(~a~).xml" (feed-format object)))
|
26
|
|
-(defmethod page-url ((object tag-feed))
|
27
|
|
- (format nil "tag/~a~(~a~).xml" (index-slug object) (feed-format object)))
|
28
|
|
-
|
29
|
8
|
(defmethod render ((object index) &key prev next)
|
30
|
9
|
(funcall (theme-fn 'index) (list :tags (all-tags)
|
31
|
10
|
:months (all-months)
|
|
@@ -34,17 +13,18 @@
|
34
|
13
|
:prev prev
|
35
|
14
|
:next next)))
|
36
|
15
|
|
37
|
|
-(defun all-months ()
|
38
|
|
- "Retrieve a list of all months with published content."
|
39
|
|
- (let ((months (mapcar (lambda (x) (subseq (content-date x) 0 7))
|
40
|
|
- (hash-table-values *content*))))
|
41
|
|
- (sort (remove-duplicates months :test #'string=) #'string>)))
|
|
16
|
+;;; Index by Tag
|
42
|
17
|
|
43
|
|
-(defun all-tags ()
|
44
|
|
- "Retrieve a list of all tags used in content."
|
45
|
|
- (let* ((dupes (mappend #'content-tags (hash-table-values *content*)))
|
46
|
|
- (tags (remove-duplicates dupes :test #'string= :key #'tag-slug)))
|
47
|
|
- (sort tags #'string< :key #'tag-name)))
|
|
18
|
+(defclass tag-index (index) ())
|
|
19
|
+
|
|
20
|
+(defmethod page-url ((object tag-index))
|
|
21
|
+ (format nil "tag/~a" (index-slug object)))
|
|
22
|
+
|
|
23
|
+(defmethod discover ((doc-type (eql (find-class 'tag-index))))
|
|
24
|
+ (purge-all (class-name doc-type))
|
|
25
|
+ (let ((content (by-date (find-all 'post))))
|
|
26
|
+ (dolist (tag (all-tags))
|
|
27
|
+ (add-document (index-by-tag tag content)))))
|
48
|
28
|
|
49
|
29
|
(defun index-by-tag (tag content)
|
50
|
30
|
"Return an index of all CONTENT matching the given TAG."
|
|
@@ -52,12 +32,46 @@
|
52
|
32
|
:posts (remove-if-not (lambda (x) (tag-p tag x)) content)
|
53
|
33
|
:title (format nil "Posts tagged ~a" (tag-name tag))))
|
54
|
34
|
|
|
35
|
+(defmethod publish ((doc-type (eql (find-class 'tag-index))))
|
|
36
|
+ (dolist (index (find-all 'tag-index))
|
|
37
|
+ (render-index index)))
|
|
38
|
+
|
|
39
|
+;;; Index by Month
|
|
40
|
+
|
|
41
|
+(defclass month-index (index) ())
|
|
42
|
+
|
|
43
|
+(defmethod page-url ((object month-index))
|
|
44
|
+ (format nil "date/~a" (index-slug object)))
|
|
45
|
+
|
|
46
|
+(defmethod discover ((doc-type (eql (find-class 'month-index))))
|
|
47
|
+ (purge-all (class-name doc-type))
|
|
48
|
+ (let ((content (by-date (find-all 'post))))
|
|
49
|
+ (dolist (month (all-months))
|
|
50
|
+ (add-document (index-by-month month content)))))
|
|
51
|
+
|
55
|
52
|
(defun index-by-month (month content)
|
56
|
53
|
"Return an index of all CONTENT matching the given MONTH."
|
57
|
|
- (make-instance 'date-index :slug month
|
|
54
|
+ (make-instance 'month-index :slug month
|
58
|
55
|
:posts (remove-if-not (lambda (x) (month-p month x)) content)
|
59
|
56
|
:title (format nil "Posts from ~a" month)))
|
60
|
57
|
|
|
58
|
+(defmethod publish ((doc-type (eql (find-class 'month-index))))
|
|
59
|
+ (dolist (index (find-all 'month-index))
|
|
60
|
+ (render-index index)))
|
|
61
|
+
|
|
62
|
+;;; Reverse Chronological Index
|
|
63
|
+
|
|
64
|
+(defclass numeric-index (index) ())
|
|
65
|
+
|
|
66
|
+(defmethod page-url ((object numeric-index))
|
|
67
|
+ (format nil "~d" (index-slug object)))
|
|
68
|
+
|
|
69
|
+(defmethod discover ((doc-type (eql (find-class 'numeric-index))))
|
|
70
|
+ (purge-all (class-name doc-type))
|
|
71
|
+ (let ((content (by-date (find-all 'post))))
|
|
72
|
+ (dotimes (i (ceiling (length content) 10))
|
|
73
|
+ (add-document (index-by-n i content)))))
|
|
74
|
+
|
61
|
75
|
(defun index-by-n (i content)
|
62
|
76
|
"Return the index for the Ith page of CONTENT in reverse chronological order."
|
63
|
77
|
(let ((content (subseq content (* 10 i))))
|
|
@@ -65,6 +79,66 @@
|
65
|
79
|
:posts (take-up-to 10 content)
|
66
|
80
|
:title "Recent Posts")))
|
67
|
81
|
|
|
82
|
+(defmethod publish ((doc-type (eql (find-class 'numeric-index))))
|
|
83
|
+ (let ((indexes (sort (find-all 'numeric-index) #'< :key #'index-slug)))
|
|
84
|
+ (dolist (index indexes)
|
|
85
|
+ (let ((prev (1- (index-slug index)))
|
|
86
|
+ (next (1+ (index-slug index))))
|
|
87
|
+ (render-index index :prev (when (plusp prev) prev)
|
|
88
|
+ :next (when (<= next (length indexes)) next))))))
|
|
89
|
+
|
|
90
|
+;;; Atom and RSS Feeds
|
|
91
|
+
|
|
92
|
+(defclass feed (index)
|
|
93
|
+ ((format :initform nil :initarg :format :accessor feed-format)))
|
|
94
|
+
|
|
95
|
+(defmethod page-url ((object feed))
|
|
96
|
+ (format nil "~(~a~).xml" (feed-format object)))
|
|
97
|
+
|
|
98
|
+(defmethod discover ((doc-type (eql (find-class 'feed))))
|
|
99
|
+ (let ((content (take-up-to 10 (by-date (find-all 'post)))))
|
|
100
|
+ (dolist (format '(rss atom))
|
|
101
|
+ (let ((feed (make-instance 'feed :posts content :format format)))
|
|
102
|
+ (add-document feed)))))
|
|
103
|
+
|
|
104
|
+(defmethod publish ((doc-type (eql (find-class 'feed))))
|
|
105
|
+ (dolist (feed (find-all 'feed))
|
|
106
|
+ (render-feed feed)))
|
|
107
|
+
|
|
108
|
+;; TODO: tag-feed isn't reached by do-subclasses!
|
|
109
|
+(defclass tag-feed (feed) ())
|
|
110
|
+
|
|
111
|
+(defmethod page-url ((object tag-feed))
|
|
112
|
+ (format nil "tag/~a~(~a~).xml" (index-slug object) (feed-format object)))
|
|
113
|
+
|
|
114
|
+(defmethod discover ((doc-type (eql (find-class 'tag-feed))))
|
|
115
|
+ (let ((content (by-date (find-all 'post))))
|
|
116
|
+ (dolist (tag (feeds *config*))
|
|
117
|
+ (let ((posts (remove-if-not (lambda (x) (tag-p tag x)) content)))
|
|
118
|
+ (dolist (format '(rss atom))
|
|
119
|
+ (let ((feed (make-instance 'tag-feed :posts (take-up-to 10 posts)
|
|
120
|
+ :format format
|
|
121
|
+ :slug tag)))
|
|
122
|
+ (add-document feed)))))))
|
|
123
|
+
|
|
124
|
+(defmethod publish ((doc-type (eql (find-class 'tag-feed))))
|
|
125
|
+ (dolist (feed (find-all 'tag-feed))
|
|
126
|
+ (render-feed feed)))
|
|
127
|
+
|
|
128
|
+;;; Helper Functions
|
|
129
|
+
|
|
130
|
+(defun all-months ()
|
|
131
|
+ "Retrieve a list of all months with published content."
|
|
132
|
+ (let ((months (mapcar (lambda (x) (subseq (content-date x) 0 7))
|
|
133
|
+ (hash-table-values *content*))))
|
|
134
|
+ (sort (remove-duplicates months :test #'string=) #'string>)))
|
|
135
|
+
|
|
136
|
+(defun all-tags ()
|
|
137
|
+ "Retrieve a list of all tags used in content."
|
|
138
|
+ (let* ((dupes (mappend #'content-tags (hash-table-values *content*)))
|
|
139
|
+ (tags (remove-duplicates dupes :test #'string= :key #'tag-slug)))
|
|
140
|
+ (sort tags #'string< :key #'tag-name)))
|
|
141
|
+
|
68
|
142
|
(defun render-feed (feed)
|
69
|
143
|
"Render the given FEED to both RSS and ATOM."
|
70
|
144
|
(let ((theme-fn (theme-fn (feed-format feed) "feeds")))
|
|
@@ -73,25 +147,3 @@
|
73
|
147
|
(defun render-index (index &rest render-args)
|
74
|
148
|
"Render the given INDEX using RENDER-ARGS if provided."
|
75
|
149
|
(write-page (page-path index) (apply #'render-page index nil render-args)))
|
76
|
|
-
|
77
|
|
-(defun render-indexes (tag-feeds)
|
78
|
|
- "Render the indexes to view content in groups of size N, by month, or by tag,
|
79
|
|
-along with RSS and ATOM feeds and any supplied TAG-FEEDS."
|
80
|
|
- (let ((content (by-date (find-all 'post))))
|
81
|
|
- (dolist (tag (all-tags))
|
82
|
|
- (render-index (index-by-tag tag content)))
|
83
|
|
- (dolist (month (all-months))
|
84
|
|
- (render-index (index-by-month month content)))
|
85
|
|
- (dotimes (i (ceiling (length content) 10))
|
86
|
|
- (render-index (index-by-n i content)
|
87
|
|
- :prev (and (plusp i) i)
|
88
|
|
- :next (and (< (* (1+ i) 10) (length content))
|
89
|
|
- (+ 2 i))))
|
90
|
|
- (dolist (format '(rss atom))
|
91
|
|
- (dolist (tag tag-feeds)
|
92
|
|
- (let ((posts (remove-if-now (lambda (x) (tag-p (make-tag tag) x)) content)))
|
93
|
|
- (render-feed (make-instance 'tag-feed :posts (take-up-to 10 posts)
|
94
|
|
- :format format
|
95
|
|
- :slug tag))))
|
96
|
|
- (render-feed (make-instance 'feed :posts (take-up-to 10 content)
|
97
|
|
- :format format)))))
|