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