diff --git a/TODO b/TODO
index 31be6c9..7507152 100644
--- a/TODO
+++ b/TODO
@@ -12,16 +12,23 @@ Ideas:
;;;; DYNAMIC
;;;; STATIC
-;;;; implement render-site, head-inject/body-inject/navigation.
+;;;; implement head-inject/body-inject/navigation!
+;;;; implement start-coleslaw, stop-coleslaw!
;;;; non-disqus comment support?
+;;; indexes
+;; what should it really look like, keeping in mind the API should be mostly
+;; identical between the static and dynamic backend?
;;; posts
;; Make find-by-date, post-url not suck.
+;; -- re: find-by-date, aside from the subseq+string= grossness, the posts it
+;; -- returns are not in reverse chronological order. Fix it!
;; -- re: post-url, escaping is insufficient and there are collisions by title.
;; -- What are alternatives?
;;;; PLUGINS
;;;; implement disqus support
;;; import
+;; add output to HTML files when static-p is true
;; add comment handling ... (when comments ...)
;; support old URLs via use of post-aliases?
diff --git a/src/indices.lisp b/src/indices.lisp
index f7b75d8..2d61911 100644
--- a/src/indices.lisp
+++ b/src/indices.lisp
@@ -6,8 +6,11 @@
(defgeneric remove-index (id)
(:documentation "Remove the index matching ID from *storage*."))
-(defgeneric render-index (index)
- (:documentation "Generate the final HTML for INDEX."))
+(defgeneric render-index (id page)
+ (:documentation "Generate the final HTML for the index with given ID."))
(defgeneric find-index (id)
(:documentation "Retrieve the index matching ID from *storage*."))
+
+(defgeneric index-url (id page)
+ (:documentation "Return the URL for the PAGE of index with given ID."))
diff --git a/src/posts.lisp b/src/posts.lisp
index a380ba7..5e34629 100644
--- a/src/posts.lisp
+++ b/src/posts.lisp
@@ -14,7 +14,8 @@
(aliases :initform nil :initarg :aliases
:accessor post-aliases)))
-(defgeneric make-post (title tags date content &key id &allow-other-keys)
+(defgeneric make-post (title tags date content
+ &key id aliases &allow-other-keys)
(:documentation "Create a POST with the given data."))
(defgeneric add-post (post id)
@@ -24,7 +25,7 @@
(:documentation "Remove a post from *storage* matching ID."))
(defgeneric render-post (id)
- (:documentation "Generate the final HTML for post."))
+ (:documentation "Generate the final HTML for the post with given ID."))
(defgeneric find-post (id)
(:documentation "Retrieve a post from *storage* matching ID."))
diff --git a/static/coleslaw.lisp b/static/coleslaw.lisp
index 47f8638..f3a6e69 100644
--- a/static/coleslaw.lisp
+++ b/static/coleslaw.lisp
@@ -2,7 +2,7 @@
(defvar *site-root* nil
"A string representing the base URL of the site,
-e.g. \"http://blog.redlinernotes.com/\".")
+e.g. \"http://blog.redlinernotes.com\".")
(defvar *site-title* nil
"A string containing the title of the site,
@@ -39,3 +39,15 @@ e.g. \"CC-BY-SA\". Otherwise, standard copyright is assumed.")
(defmethod set-credentials (name credentials)
(setf (gethash name (gethash :credentials *storage*)) credentials))
+
+(defmethod render-page (content)
+ (let ((result (funcall (find-symbol "BASE" (theme-package))
+ (list :title *site-title*
+ :siteroot *site-root*
+ :head-inject nil
+ :navigation nil
+ :content content
+ :body-inject nil
+ :license *site-license*
+ :credits *site-credits*))))
+ result))
diff --git a/static/indices.lisp b/static/indices.lisp
index 9987621..43fdc63 100644
--- a/static/indices.lisp
+++ b/static/indices.lisp
@@ -1,13 +1,61 @@
(in-package :coleslaw)
-(defmethod find-index (id)
- (gethash id (gethash :indices *storage*)))
+(defun monthlinks ()
+ (loop for month in (gethash :months-index *storage*)
+ collect (cons (index-url :date month) month) into months
+ finally (return
+ (pretty-list (mapcar (lambda (consp)
+ (format nil "~a"
+ (car consp) (cdr consp)))
+ months)))))
-(defmethod add-index (index id)
- (setf (find-index id) index))
+(defun taglinks ()
+ (loop for tag in (gethash :tags-index *storage*)
+ collect (cons (index-url :tag tag) tag) into tags
+ finally (return
+ (pretty-list (mapcar (lambda (consp)
+ (format nil "~a"
+ (car consp) (cdr consp)))
+ tags)))))
-(defmethod remove-index (id)
- (setf (find-index id) nil))
+(defun index-title (id &optional page)
+ (case id
+ (:range "Recent Posts")
+ (:date (format nil "Posts from ~A" page))
+ (:tag (format nil "Posts tagged ~A" page))))
-(defmethod render-index (index)
- )
+(defun index-posts (id page)
+ (case id
+ (:range (let* ((count (hash-table-count (gethash :posts *storage*)))
+ (start (- count (* 10 (1- page))))
+ (end (- start 9)))
+ (remove nil (find-by-range start end))))
+ (:date (find-by-date page))
+ (:tag (find-by-tag page))))
+
+(defmethod render-index (id page)
+ (let* ((posts (index-posts id page))
+ (content (funcall (find-symbol "INDEX" (theme-package))
+ (list :taglinks (taglinks)
+ :monthlinks (monthlinks)
+ :title (index-title id page)
+ :posts (loop for post in posts collect
+ (list :url (post-url (post-id post))
+ :title (post-title post)
+ :date (pretty-date (post-date post))
+ :contents (post-content post)))
+ :prev (when (and (numberp id)
+ (index-posts id (1- page)))
+ (index-url id (1- page)))
+ :next (when (and (numberp id)
+ (index-posts id (1+ page)))
+ (index-url id (1+ page)))))))
+ content))
+
+(defmethod index-url (id page)
+ (flet ((keyword-name (keyword)
+ (format nil "~A" keyword)))
+ (if (member id '(:date :tag))
+ (concatenate 'string *site-root* "/"
+ (string-downcase (keyword-name id)) "/" page)
+ (concatenate 'string *site-root* "/page/" (write-to-string page)))))
diff --git a/static/posts.lisp b/static/posts.lisp
index 7f11252..2793d93 100644
--- a/static/posts.lisp
+++ b/static/posts.lisp
@@ -11,29 +11,27 @@
(defmethod add-post ((post post) id)
(setf (gethash id (gethash :posts *storage*)) post)
(loop for tag in (post-tags post)
- do (pushnew tag (gethash :tags-index *storage*) :test #'string=)))
+ do (pushnew tag (gethash :tags-index *storage*) :test #'string=))
+ (let ((date (post-date post))
+ (month (format nil "~4d-~2,'0d" (local-time:timestamp-year date)
+ (local-time:timestamp-month date))))
+ (pushnew month (gethash :months-index *storage*) :test #'string=)))
(defmethod remove-post (id)
(setf (gethash id (gethash :posts *storage*)) nil))
(defmethod render-post (id)
- (flet ((fn (name)
- (find-symbol name (theme-package)))
- (pretty-date (date)
- (subseq (local-time:format-rfc1123-timestring nil date) 0 16))
- (pretty-tags (tags)
- (format nil "~{~A~^, ~}" tags)))
- (let* ((post (find-post id))
- (result (funcall (fn "POST")
- (list :title (post-title post)
- :tags (pretty-tags (post-tags post))
- :date (pretty-date (post-date post))
- :content (post-content post)
- :prev (when (find-post (1- id))
- (post-url (1- id)))
- :next (when (find-post (1+ id))
- (post-url (1+ id)))))))
- result)))
+ (let* ((post (find-post id))
+ (result (funcall (theme-fn "POST")
+ (list :title (post-title post)
+ :tags (pretty-tags (post-tags post))
+ :date (pretty-date (post-date post))
+ :content (post-content post)
+ :prev (when (find-post (1- id))
+ (post-url (1- id)))
+ :next (when (find-post (1+ id))
+ (post-url (1+ id)))))))
+ result))
(defmethod find-post (id)
(gethash id (gethash :posts *storage*)))
@@ -41,7 +39,7 @@
(defmethod find-by-tag (tag)
(let ((results nil))
(loop for post being the hash-values in (gethash :posts *storage*)
- do (when (search tag (post-tags post))
+ do (when (member tag (post-tags post) :test #'string=)
(push post results)))
results))
@@ -64,4 +62,4 @@
(defmethod post-url (id)
(flet ((escape (str)
(substitute #\- #\Space str)))
- (concatenate 'string *site-root* (escape (post-title (find-post id))))))
+ (concatenate 'string *site-root* "/" (escape (post-title (find-post id))))))
diff --git a/static/util.lisp b/static/util.lisp
new file mode 100644
index 0000000..41e2759
--- /dev/null
+++ b/static/util.lisp
@@ -0,0 +1,10 @@
+(in-package :coleslaw)
+
+(defun pretty-date (date)
+ (subseq (local-time:format-rfc1123-timestring nil date) 0 16))
+
+(defun pretty-list (list)
+ (format nil "~{~A~^, ~}" list))
+
+(defun theme-fn (name)
+ (find-symbol name (theme-package)))