diff --git a/NEWS.md b/NEWS.md
index 3330d7f..fd65e1f 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -7,6 +7,14 @@
the old behavior.
* Coleslaw no longer expects a particular repo layout. Use whatever
directory hierarchy you like.
+* New Content Type Plugin: Static Pages, accepting a title, url, and
+ optionally tags and a date. All files with a `.page` extension are
+ compiled as static pages and reuse the POST template.
+ To enable Static Pages, add `(static-pages)` to the `:plugins`
+ section of your config.
+* Coleslaw now allows content without a date or tags. Note that POSTs
+ without a date will still show up in the reverse chronological
+ indexes at the very end.
## Changes for 0.9.3 (2013-04-16):
diff --git a/docs/hacking.md b/docs/hacking.md
index 327bbd7..f8adae3 100644
--- a/docs/hacking.md
+++ b/docs/hacking.md
@@ -168,25 +168,6 @@ freshly built site.
## Areas for Improvement
-### Allow Tagless or Dateless Content
-
-Several users have expected to be able to not supply tags or a date
-for their content. This is a reasonable expectation and requires
-changes to at least the post templates and the `read-content`
-function. There may be other areas where it was assumed tags/dates
-will always be present.
-
-### New Content Type: Pages!
-
-Many users have requested a content type PAGE, for static pages. It
-should be a pretty straightforward subclass of CONTENT with the
-necessary methods: `render`, `page-url` and `publish`. It could have a
-`url` slot with `page-url` as a reader to allow arbitrary layout on
-the site. For now, we can be sloppy and reuse the post template and
-limit static-pages to being written in markdown. If we want to support
-other formats, consider moving the format slot from POST to CONTENT.
-This has been implemented on the branch `static-pages`.
-
### New Content Type: Shouts!
I've also toyed with the idea of a content type called a SHOUT, which
diff --git a/plugins/static-pages.lisp b/plugins/static-pages.lisp
new file mode 100644
index 0000000..66d276a
--- /dev/null
+++ b/plugins/static-pages.lisp
@@ -0,0 +1,32 @@
+(defpackage :coleslaw-static-pages
+ (:use :cl)
+ (:export #:enable)
+ (:import-from :coleslaw #:*config*
+ #:content
+ #:page-url
+ #:find-all
+ #:render
+ #:publish
+ #:write-document))
+
+(in-package :coleslaw-static-pages)
+
+(defclass page (content)
+ ((title :initarg :title :reader page-title)
+ (url :initarg :url :reader page-url)))
+
+(defmethod initialize-instance :after ((object page) &key)
+ ;; Expect all static-pages to be written in Markdown for now.
+ (with-accessors ((text content-text)) object
+ (setf text (render-text text :md))))
+
+(defmethod render ((object page) &key next prev)
+ ;; For the time being, we'll re-use the normal post theme.
+ (funcall (theme-fn 'post) (list :config *config*
+ :post object)))
+
+(defmethod publish ((doc-type (eql (find-class 'page))))
+ (dolist (page (find-all 'page))
+ (write-document page)))
+
+(defun enable ())
diff --git a/src/content.lisp b/src/content.lisp
index dde3541..a80fc1f 100644
--- a/src/content.lisp
+++ b/src/content.lisp
@@ -36,6 +36,11 @@
(date :initform nil :initarg :date :accessor content-date)
(text :initform nil :initarg :text :accessor content-text)))
+(defmethod initialize-instance :after ((object content) &key)
+ (with-accessors ((tags content-tags)) object
+ (when (stringp tags)
+ (setf tags (mapcar #'make-tag (cl-ppcre:split "," tags))))))
+
(defun read-content (file)
"Returns a plist of metadata from FILE with :text holding the content as a string."
(flet ((slurp-remainder (stream)
@@ -46,9 +51,7 @@
(parse-field (str)
(nth-value 1 (cl-ppcre:scan-to-strings "[a-zA-Z]+: (.*)" str)))
(field-name (line)
- (make-keyword (string-upcase (subseq line 0 (position #\: line)))))
- (read-tags (str)
- (mapcar #'make-tag (cl-ppcre:split "," str))))
+ (make-keyword (string-upcase (subseq line 0 (position #\: line))))))
(with-open-file (in file :external-format '(:utf-8))
(unless (string= (read-line in) (separator *config*))
(error "The provided file lacks the expected header."))
@@ -57,9 +60,10 @@
appending (list (field-name line)
(aref (parse-field line) 0))))
(content (slurp-remainder in)))
- (setf (getf meta :tags) (read-tags (getf meta :tags)))
(append meta (list :text content))))))
+;; Helper Functions
+
(defun tag-p (tag obj)
"Test if OBJ is tagged with TAG."
(let ((tag (if (typep tag 'tag) tag (make-tag tag))))
diff --git a/src/packages.lisp b/src/packages.lisp
index 6c7b774..7b59711 100644
--- a/src/packages.lisp
+++ b/src/packages.lisp
@@ -15,6 +15,7 @@
#:index
#:render-text
#:add-injection
+ #:theme-fn
;; The Document Protocol
#:add-document
#:find-all
diff --git a/themes/hyde/post.tmpl b/themes/hyde/post.tmpl
index 6406035..cebe759 100644
--- a/themes/hyde/post.tmpl
+++ b/themes/hyde/post.tmpl
@@ -4,13 +4,17 @@
{\n}
{$post.title}
{\n}
{\n}
{\n}
- Written on {$post.date}
+ {if $post.date}
+ Written on {$post.date}
+ {/if}
{\n}
{\n}
{\n}
diff --git a/themes/readable/post.tmpl b/themes/readable/post.tmpl
index a5be92f..b655ce7 100644
--- a/themes/readable/post.tmpl
+++ b/themes/readable/post.tmpl
@@ -3,13 +3,19 @@
{template post}
{\n}
{\n}
-
Tagged as
- {foreach $tag in $post.tags}
- {$tag.name}{nil}
- {if not isLast($tag)},{sp}{/if}
- {/foreach}
+
+ {if $post.tags}
+ Tagged as {foreach $tag in $post.tags}
+ {$tag.name}{nil}
+ {if not isLast($tag)},{sp}{/if}
+ {/foreach}
+ {/if}
+
+
+ {if $post.date}
+ Written on {$post.date}
+ {/if}
-
Written on {$post.date}
{$post.text |noAutoescape}