commit 4e91fd2dc72ee6180843012ee2b9778391529ced
parent 3701397bce23d30653cfc5f883c9cf4023b504d6
Author: Decay <decay@todayiwilllaunchmyinfantsonintoorbit.com>
Date: Tue, 13 Oct 2020 12:52:34 -0700
Switch from cl-emb to Djula
cl-emb is an ugly JSP-like that's pretty outdated at this point. Djula
doesn't have all of Django's features yet but it's in active
development. The new templates are cleaner and we now enforce a
stricter separtion between - aheh - "business logic" and our views.
Diffstat:
15 files changed, 339 insertions(+), 314 deletions(-)
diff --git a/miniblog.asd b/miniblog.asd
@@ -14,7 +14,7 @@ into date-structured directories as a normal HTML."
"alexandria"
"cl-fad"
"cl-markdown"
- "cl-emb"
+ "djula"
"dbd-sqlite3"
"sxql"
"mito"
@@ -30,12 +30,10 @@ into date-structured directories as a normal HTML."
(:file "content")
(:file "miniblog")))
(:module "templates"
- :components ((:static-file "html-head.lhtml")
- (:static-file "header.lhtml")
- (:static-file "left-column.lhtml")
- (:static-file "pagetemplate.lhtml")
- (:static-file "template.lhtml")
- (:static-file "rss.lxml")))
+ :components ((:static-file "template.dtl")
+ (:static-file "posts.dtl")
+ (:static-file "page.dtl")
+ (:static-file "rss.dtl")))
(:static-file "COPYING")
(:static-file "README"))
:in-order-to ((test-op (test-op "miniblog/tests"))))
diff --git a/src/content.lisp b/src/content.lisp
@@ -3,88 +3,99 @@
(let* ((this-file #.(or *compile-file-truename* *load-truename*))
(src-dir (pathname-directory this-file))
(templates-dir (append (butlast src-dir) '("templates"))))
- (defparameter *templates-dir* (make-pathname :directory templates-dir))
- (register-emb "default-template"
- (make-pathname :name "template" :type "lhtml"
- :directory templates-dir
- :defaults this-file))
- (register-emb "default-page-template"
- (make-pathname :name "pagetemplate" :type "lhtml"
- :directory templates-dir
- :defaults this-file))
- (register-emb "rss"
- (make-pathname :name "rss" :type "lxml"
- :directory templates-dir
- :defaults this-file)))
+ (add-template-directory (make-pathname :directory templates-dir))
+ (defvar *posts-template* (compile-template* "posts.dtl"))
+ (defvar *page-template* (compile-template* "page.dtl"))
+ (defvar *rss-template* (compile-template* "rss.dtl")))
+
+(defun render-posts (posts pages &key stream title root-uri header links
+ stylesheet year month archive-date-list
+ enable-rss)
+ (render-template* *posts-template* stream
+ :posts posts
+ :pages pages
+ :title title
+ :root-uri root-uri
+ :header header
+ :links links
+ :stylesheet stylesheet
+ :year year
+ :month month
+ :archive-date-list archive-date-list
+ :enable-rss enable-rss))
+
+(defun render-page (page path pages &key stream title root-uri header links
+ stylesheet year month
+ archive-date-list enable-rss)
+ (render-template* *page-template* stream
+ :page page
+ :path path
+ :pages pages
+ :title title
+ :root-uri root-uri
+ :header header
+ :links links
+ :stylesheet stylesheet
+ :year year
+ :month month
+ :archive-date-list archive-date-list
+ :enable-rss enable-rss))
+
+(defun render-rss (posts &key stream title link description image-url language
+ copyright managing-editor webmaster category)
+ (render-template* *rss-template* stream
+ :posts posts
+ :title title
+ :link link
+ :description description
+ :image-url image-url
+ :language language
+ :copyright copyright
+ :managing-editor managing-editor
+ :webmaster webmaster
+ :category category
+ :build-date (miniblog.format:rfc-822-format (now)) ))
(defun make-generator (&key title root-uri header links stylesheet)
- (lambda (entries pages &key year month archive-date-list enable-rss)
- (let ((*default-pathname-defaults* *templates-dir*))
- (execute-emb
- "default-template"
- :env (list
- :title title
- :root-uri root-uri
- :header header
- :links links
- :stylesheet stylesheet
- :enable-rss enable-rss
- :posts entries
- :year year
- :month month
- :pages pages
- :archive-date-list archive-date-list
- :content-formatter #'miniblog.format:markdown)))))
+ (lambda (posts pages &key year month archive-date-list enable-rss)
+ (render-posts posts pages
+ :title title
+ :root-uri root-uri
+ :header header
+ :links links
+ :stylesheet stylesheet
+ :year year
+ :month month
+ :archive-date-list archive-date-list
+ :enable-rss enable-rss)))
(defun make-page-generator (&key title root-uri header links stylesheet)
- (lambda (entry path pages &key archive-date-list enable-rss)
- (let ((*default-pathname-defaults* *templates-dir*))
- (execute-emb
- "default-page-template"
- :env (list
- :title title
- :root-uri root-uri
- :header header
- :links links
- :stylesheet stylesheet
- :enable-rss enable-rss
- :pages pages
- :post entry
- :path path
- :archive-date-list archive-date-list
- :content-formatter #'miniblog.format:markdown)))))
-
-(defun strip-html-tags (content)
- (format nil "~{~A~}"
- (mapcar
- (lambda (s)
- (let ((chunks (split ">" s)))
- (or (second chunks) (first chunks))))
- (split "<" content))))
+ (lambda (page path pages &key archive-date-list enable-rss)
+ (render-page page path pages
+ :title title
+ :root-uri root-uri
+ :header header
+ :links links
+ :stylesheet stylesheet
+ :enable-rss enable-rss
+ :archive-date-list archive-date-list)))
(defun make-rss-generator (&key title link description image-url language
copyright managing-editor webmaster category)
(if (and title link description)
- (lambda (entries)
- (let ((*default-pathname-defaults* *templates-dir*))
- (execute-emb
- "rss"
- :env (list
- :title title
- :link link
- :description description
- :image-url image-url
- :language language
- :copyright copyright
- :managing-editor managing-editor
- :webmaster webmaster
- :category category
- :posts entries
- :build-date (miniblog.format:rfc-822-format (now))
- :content-formatter #'miniblog.format:markdown
- :content-stripper #'strip-html-tags))))
- (lambda (entries)
- (declare (ignore entries)))))
+ (lambda (posts)
+ (render-rss posts
+ :title title
+ :link link
+ :description description
+ :image-url image-url
+ :language language
+ :copyright copyright
+ :managing-editor managing-editor
+ :webmaster webmaster
+ :category category))
+ (lambda (posts)
+ (declare (ignore posts)))))
(defun get-page-by-path (path pages)
(labels ((get-page-name (name page-list)
diff --git a/src/format.lisp b/src/format.lisp
@@ -11,7 +11,7 @@
Monday, February 3rd 2020 at 2:46 PM PST")
(defun markdown (content)
- "Translate CONTENT with CL-MARKDOWN"
+ "Translate CONTENT with cl-markdown"
(nth 1 (multiple-value-list (cl-markdown:markdown content :stream nil))))
(defun rfc-822-format (datetime &optional (tz *default-timezone*))
@@ -35,3 +35,69 @@
(defun make-content-formatter ()
"Return the default content formatter (parses Markdown)"
#'markdown)
+
+;;; Djula formatting elements (tags and filters)
+
+(def-tag-compiler :page-tree (path pages &optional (root-uri "/"))
+ (lambda (stream)
+ (labels ((% (x)
+ (etypecase x
+ (string x)
+ (number x)
+ (symbol (djula::resolve-variable-phrase (list x)))))
+ (descend (parent-path child-path pages)
+ (format stream "<ul class=\"page-list\">~%")
+ (loop for page in (getf pages :children)
+ do (let* ((next-name (car child-path))
+ (descendents (cdr child-path))
+ (page-name (getf page :name))
+ (page-path (append parent-path (list page-name)))
+ (page-path-str (str:join "/" page-path)))
+ (format stream "<li><a href=\"~apage/~a\">~a</a></li>~%" (% root-uri) page-path-str (getf page :title))
+ (if (string= page-name next-name)
+ (descend page-path descendents page))))
+ (format stream "</ul>~%")))
+ (descend nil (% path) (% pages)))))
+
+(def-tag-compiler :nav-calendar (archive-date-list &optional (root-uri "/"))
+ (flet ((% (x)
+ (etypecase x
+ (string x)
+ (number x)
+ (symbol (djula::resolve-variable-phrase (list x))))))
+ (lambda (stream)
+ (let ((arc (copy-list (% archive-date-list))))
+ (loop while arc do
+ (format stream "<table class=\"calendar\"><tr><th colspan=\"4\">~a</th></tr>~%" (caar arc))
+ (let ((month-entries '())
+ (month-names '("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")))
+ (loop for cal-month downfrom 12 to 1 do
+ (if (and arc (= cal-month (cdar arc)))
+ (progn
+ (push (format nil "~d/~2,'0d/index.html" (caar arc) (cdar arc)) month-entries)
+ (pop arc))
+ (push nil month-entries)))
+ (loop for row from 0 to 2 do
+ (format stream "<tr>~%")
+ (loop for cal-month from (* row 4) to (+ (* row 4) 3) do
+ (format stream "<td>")
+ (if (nth cal-month month-entries)
+ (format stream "<a href=\"~a~a\">~a</a>"
+ (% root-uri)
+ (nth cal-month month-entries)
+ (nth cal-month month-names))
+ (format stream "~A" (nth cal-month month-names)))
+ (format stream "</td>~%"))
+ (format stream "</tr>~%")))
+ (format stream "</table>~%"))))))
+
+(def-filter :markdown (content)
+ (markdown content))
+
+(def-filter :strip-html (content)
+ (format nil "~{~A~}"
+ (mapcar
+ (lambda (s)
+ (let ((chunks (split ">" s)))
+ (or (second chunks) (first chunks))))
+ (split "<" content))))
diff --git a/src/packages.lisp b/src/packages.lisp
@@ -2,6 +2,8 @@
(defpackage :miniblog.format
(:use :cl :local-time)
+ (:import-from :str :split)
+ (:import-from :djula :def-tag-compiler :def-filter)
(:export #:markdown #:rfc-822-format #:short-date-format #:long-date-format
#:make-content-formatter
#:make-rfc-822-date-formatter
@@ -38,8 +40,12 @@
#:move-page))
(defpackage :miniblog.content
- (:use :cl :local-time :cl-emb :str)
- (:export #:make-generator #:make-page-generator
+ (:use :cl :local-time :str)
+ (:import-from :djula :add-template-directory :compile-template*
+ :render-template*)
+ (:export *posts-template* *page-template* *rss-template*
+ #:render-posts #:render-page #:render-rss
+ #:make-generator #:make-page-generator
#:make-rss-generator #:get-archive-date-list
#:get-page-id-by-path #:get-page-by-path #:get-path-to-page
#:year-month-of-entry #:year-month-of-latest-entry
diff --git a/templates/header.lhtml b/templates/header.lhtml
@@ -1,5 +0,0 @@
-<header id="miniblog-header">
- <% @if header %>
- <% @includevar header %>
- <% @endif %>
-</header>
diff --git a/templates/html-head.lhtml b/templates/html-head.lhtml
@@ -1,32 +0,0 @@
-<head>
- <title>
- <%= (or (getf env :title) "Miniblog") %>
- <% @if year %>
- - <% @var year %>/<% @var month %>
- <% @endif %>
- </title>
- <style>
- header#miniblog-header { width: 100%; }
- section#miniblog-left { float: left; width: 15%; }
- section#miniblog-main { float: left; width: <%= (if (getf env :links) "65%" "80%") %>; }
- nav#miniblog-nav { float: left; width: 20%; }
- div#miniblog-rss { clear: both; }
- table.calendar { padding: 10px; float: left; }
- table.calendar td { width: 25%; }
- ul.page-list { list-style: none; margin: 0; padding: 0 0 0 10px; }
- @media screen and (max-aspect-ratio: 1/1) {
- section#miniblog-left { float: none; width: 100%; }
- section#miniblog-main { float: none; width: 100%; }
- nav#miniblog-nav { float: none; width: 100%; }
- }
- </style>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <% @if stylesheet %>
- <link rel="stylesheet" type="text/css" href="<% @var stylesheet %>">
- <% @endif %>
- <% @if enable-rss %>
- <link rel="alternate" type="application/rss+xml"
- title="RSS feed for <%= (or (getf env :title) "Miniblog") %>"
- href="<% @var root-uri %>rss.xml">
- <% @endif %>
-</head>
diff --git a/templates/left-column.lhtml b/templates/left-column.lhtml
@@ -1,28 +0,0 @@
-<section id="miniblog-left">
- <a href="/">Home</a><br>
- <% @if pages %><%
- (labels ((descend (parent-path child-path pages)
- (format t "<ul class=\"page-list\">~%")
- (loop for page in (getf pages :children)
- do (let* ((next-name (car child-path))
- (descendents (cdr child-path))
- (page-name (getf page :name))
- (page-path (append parent-path (list page-name)))
- (page-path-str (str:join "/" page-path)))
- (format t "<li><a href=\"~apage/~a\">~a</a></li>~%" (getf env :root-uri) page-path-str (getf page :title))
- (if (string= page-name next-name)
- (descend page-path descendents page))))
- (format t "</ul>~%")))
- (descend nil (getf env :path) (getf env :pages)))
- %><% @endif %>
- <br>
- <% @if links %>
- <% @loop links %>
- <% @if link %>
- <a href="<% @var link %>"><% @var text %></a><br>
- <% @else %>
- <br>
- <% @endif %>
- <% @endloop %>
- <% @endif %>
-</section>
diff --git a/templates/nav.lhtml b/templates/nav.lhtml
@@ -1,36 +0,0 @@
-<nav id="miniblog-nav">
- <% @if archive-date-list %>
- <%
- (let ((arc (copy-list (getf env :archive-date-list))))
- (loop while arc do
- (format t "<table class=\"calendar\"><tr><th colspan=\"4\">~a</th></tr>~%" (caar arc))
- (let ((month-entries '())
- (month-names '("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")))
- (loop for cal-month downfrom 12 to 1 do
- (if (and arc (= cal-month (cdar arc)))
- (progn
- (push (format nil "~d/~2,'0d/index.html" (caar arc) (cdar arc)) month-entries)
- (pop arc))
- (push nil month-entries)))
- (loop for row from 0 to 2 do
- (format t "<tr>~%")
- (loop for cal-month from (* row 4) to (+ (* row 4) 3) do
- (format t "<td>")
- (if (nth cal-month month-entries)
- (format t "<a href=\"~a~a\">~a</a>"
- (or (getf env :root-uri) "")
- (nth cal-month month-entries)
- (nth cal-month month-names))
- (format t "~A" (nth cal-month month-names)))
- (format t "</td>~%"))
- (format t "</tr>~%")))
- (format t "</table>~%"))) %>
- <% @endif %>
- <% @if enable-rss %>
- <div id="miniblog-rss">
- <a href="<% @var root-uri %>rss.xml" target="_blank">
- Subscribe to <%= (or (getf env :title) "Miniblog") %>
- </a>
- </div>
- <% @endif %>
-</nav>
diff --git a/templates/page.dtl b/templates/page.dtl
@@ -0,0 +1,18 @@
+{% extends "template.dtl" %}
+{% block main %}
+ {% if page.content %}
+ <h2>{{ page.title }}</h2>
+ <article>{{ page.content|safe|markdown }}</article>
+ <p>
+ <small>
+ Posted by {{ page.created-by }} on {{ page.created-at-long }}
+ {% ifnotequal page.created-at-long page.last-updated-at-long %}
+ <br>
+ Last updated by {{ page.last-updated-by }} on {{ page.last-updated-at-long }}
+ {% endifnotequal %}
+ </small>
+ </p>
+ {% else %}
+ No page found.
+ {% endif %}
+{% endblock %}
diff --git a/templates/pagetemplate.lhtml b/templates/pagetemplate.lhtml
@@ -1,31 +0,0 @@
-<!DOCTYPE html>
-<html>
- <% @include html-head.lhtml %>
- <body>
- <% @include header.lhtml %>
- <% @include left-column.lhtml %>
- <section id="miniblog-main">
- <% @if post/content %>
- <% (destructuring-bind (&key ((:post (&key content created-at last-updated-at created-at-long last-updated-at-long last-updated-by &allow-other-keys))) content-formatter &allow-other-keys) env %>
- <h2><% @var post/title %></h2>
- <article>
- <%= (funcall content-formatter content) %>
- </article>
- <p>
- <small>
- Posted by <% @var post/created-by %> on
- <% @var post/created-at-long %>
- <% (if (local-time:timestamp/= created-at last-updated-at)
- (format t "<br>~%Last updated by ~A on ~A~%"
- last-updated-by
- last-updated-at-long)) %>
- </small>
- </p>
- <% ) %>
- <% @else %>
- No page found.
- <% @endif %>
- </section>
- <% @include nav.lhtml %>
- </body>
-</html>
diff --git a/templates/posts.dtl b/templates/posts.dtl
@@ -0,0 +1,27 @@
+{% extends "template.dtl" %}
+{% block main %}
+ {% if posts %}
+ {% for post in posts %}
+ {% if not forloop.first %}
+ <hr>
+ {% endif %}
+ {% ifchanged post.created-at-short %}
+ <h1>{{ post.created-at-short }}</h1>
+ {% endifchanged %}
+ <a name="{{ post.id }}"></a>
+ <h2>{{ post.title }}</h2>
+ <article>{{ post.content|safe|markdown }}</article>
+ <p>
+ <small>
+ Posted by {{ post.created-by }} on {{ post.created-at-long }}
+ {% ifnotequal post.created-at-long post.last-updated-at-long %}
+ <br>
+ Last updated by {{ post.last-updated-by }} on {{ post.last-updated-at-long }}
+ {% endifnotequal %}
+ </small>
+ </p>
+ {% endfor %}
+ {% else %}
+ No posts found.
+ {% endif %}
+{% endblock %}
diff --git a/templates/rss.dtl b/templates/rss.dtl
@@ -0,0 +1,47 @@
+<rss version="2.0">
+ <channel>
+ <title>{{ title }}</title>
+ <link>{{ link }}</link>
+ <description>{{ description }}</description>
+ <generator>Miniblog</generator>
+ <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
+ <lastBuildDate>
+ {{ build-date }}
+ </lastBuildDate>
+ {% if image-url %}
+ <image>
+ <url>{{ image-url }}</url>
+ <title>{{ title }}</title>
+ <link>{{ link }}</link>
+ </image>
+ {% endif %}
+ {% if language %}
+ <language>{{ language }}</language>
+ {% endif %}
+ {% if copyright %}
+ <copyright>{{ copyright }}</copyright>
+ {% endif %}
+ {% if managing-editor %}
+ <managingEditor>{{ managing-editor }}</managingEditor>
+ {% endif %}
+ {% if webmaster %}
+ <webmaster>{{ webmaster }}</webmaster>
+ {% endif %}
+ {% if category %}
+ <category>{{ category }}</category>
+ {% endif %}
+ {% if posts %}
+ <pubDate>
+ {{ posts.0.created-at-rfc-822 }}
+ </pubDate>
+ {% for post in posts %}
+ <item>
+ <title>{{ post.title }}</title>
+ <link>{{ link }}#{{ post.id }}</link>
+ <description>{{ post.content|markdown|strip-html|truncatechars:200 }}</description>
+ <pubDate>{{ post.last-updated-at-rfc-822 }}</pubDate>
+ </item>
+ {% endfor %}
+ {% endif %}
+ </channel>
+</rss>
diff --git a/templates/rss.lxml b/templates/rss.lxml
@@ -1,53 +0,0 @@
-<rss version="2.0">
- <channel>
- <title><% @var title %></title>
- <link><% @var link %></link>
- <description><% @var description %></description>
- <generator>Miniblog</generator>
- <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
- <lastBuildDate>
- <% @var build-date %>
- </lastBuildDate>
- <% @if image-url %>
- <image>
- <url><% @var image-url %></url>
- <title><% @var title %></title>
- <link><% @var link %></link>
- </image>
- <% @endif %>
- <% @if language %>
- <language><% @var language %></language>
- <% @endif %>
- <% @if copyright %>
- <copyright><% @var copyright %></copyright>
- <% @endif %>
- <% @if managing-editor %>
- <managingEditor><% @var managing-editor %></managingEditor>
- <% @endif %>
- <% @if webmaster %>
- <webmaster><% @var webmaster %></webmaster>
- <% @endif %>
- <% @if category %>
- <category><% @var category %></category>
- <% @endif %>
- <% @if posts %>
- <pubDate>
- <% (let* ((latest (first (getf env :posts))) (pub-date (getf latest :last-updated-at-rfc-822))) %>
- <%= pub-date %>
- <% ) %>
- </pubDate>
- <% (loop for post in (getf env :posts) do %>
- <% (destructuring-bind (&key id title content last-updated-at &allow-other-keys) post %>
- <item>
- <% (let* ((formatted-content (funcall (getf env :content-formatter) content)) (stripped-content (funcall (getf env :content-stripper) formatted-content)) (truncated-content (str:substring 0 200 stripped-content))) %>
- <title><%= title %></title>
- <link><% @var link %>#<%= id %></link>
- <description><%= (if (equal stripped-content truncated-content) stripped-content (concatenate 'string truncated-content "...")) %></description>
- <pubDate><% @var last-updated-at-rfc-822 %></pubDate>
- <% ) %>
- </item>
- <% ) %>
- <% ) %>
- <% @endif %>
- </channel>
-</rss>
diff --git a/templates/template.dtl b/templates/template.dtl
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ {% block html-head %}
+ <title>
+ {{ title }}
+ {% if year %}
+ - {{ year }}/{{ month }}
+ {% endif %}
+ </title>
+ <style>
+ header#miniblog-header { width: 100%; }
+ section#miniblog-left { float: left; width: 15%; }
+ section#miniblog-main { float: left; width: {% if links %}65%{% else %}80%{% endif %}; }
+ nav#miniblog-nav { float: left; width: 20%; }
+ div#miniblog-rss { clear: both; }
+ table.calendar { padding: 10px; float: left; }
+ table.calendar td { width: 25%; }
+ ul.page-list { list-style: none; margin: 0; padding: 0 0 0 10px; }
+ @media screen and (max-aspect-ratio: 1/1) {
+ section#miniblog-left { float: none; width: 100%; }
+ section#miniblog-main { float: none; width: 100%; }
+ nav#miniblog-nav { float: none; width: 100%; }
+ }
+ </style>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ {% if stylesheet %}
+ <link rel="stylesheet" type="text/css" href="{{ stylesheet }}">
+ {% endif %}
+ {% if enable-rss %}
+ <link rel="alternate" type="application/rss+xml"
+ title="RSS feed for {{ title }}"
+ href="{{ root-uri }}rss.xml">
+ {% endif %}
+ {% endblock %}
+ </head>
+ <body>
+ <header id="miniblog-header">
+ {% block header %}
+ {% if header %}
+ {% include header %}
+ {% endif %}
+ {% endblock %}
+ </header>
+ <section id="miniblog-left">
+ {% block left %}
+ <a href="/">Home</a><br>
+ {% if pages %}
+ {% page-tree path pages root-uri %}
+ {% endif %}
+ <br>
+ {% if links %}
+ {% for link in links %}
+ {% if link.link %}
+ <a href="{{ link.link }}">{{ link.text }}</a><br>
+ {% else %}
+ <br>
+ {% endif %}
+ {% endfor %}
+ {% endif %}
+ {% endblock %}
+ </section>
+ <section id="miniblog-main">
+ {% block main %}
+ {% endblock %}
+ </section>
+ <nav id="miniblog-nav">
+ {% block nav %}
+ {% if archive-date-list %}
+ {% nav-calendar archive-date-list root-uri %}
+ {% endif %}
+ {% if enable-rss %}
+ <div id="miniblog-rss">
+ <a href="{{ root-uri }}rss.xml" target="_blank">
+ Subscribe to {{ title }}
+ </a>
+ </div>
+ {% endif %}
+ {% endblock %}
+ </nav>
+ </body>
+</html>
diff --git a/templates/template.lhtml b/templates/template.lhtml
@@ -1,45 +0,0 @@
-<!DOCTYPE html>
-<html>
- <% @include html-head.lhtml %>
- <body>
- <% @include header.lhtml %>
- <% @include left-column.lhtml %>
- <section id="miniblog-main">
- <% @if posts %>
- <% (let ((short-date) (render-hr nil)) %>
- <% (loop for post in (getf env :posts) do %>
- <% (destructuring-bind (&key id created-at created-at-short created-at-long last-updated-at last-updated-at-long title content created-by last-updated-by &allow-other-keys) post %>
- <% (let ((curr-short-date created-at-short)) %>
- <% (if render-hr %>
- <hr>
- <% ) %>
- <% (setf render-hr t) %>
- <% (if (string/= short-date curr-short-date)
- (progn
- (setf short-date curr-short-date)
- (format t "<h1>~A</h1>~%" short-date)))) %>
- <a name="<%= id %>"></a>
- <h2><%= title %></h2>
- <article>
- <%= (funcall (getf env :content-formatter) content) %>
- </article>
- <p>
- <small>
- Posted by <%= created-by %> on
- <%= created-at-long %>
- <% (if (local-time:timestamp/= created-at last-updated-at)
- (format t "<br>~%Last updated by ~A on ~A~%"
- last-updated-by
- last-updated-at-long)) %>
- </small>
- </p>
- <% ) %>
- <% ) %>
- <% ) %>
- <% @else %>
- No posts found.
- <% @endif %>
- </section>
- <% @include nav.lhtml %>
- </body>
-</html>