;;;; -*- coding: utf-8; mode: lisp -*- ;;;; Copyright 2007-2009, Matthias Andreas Benkard. ;;;------------------------------------------------------------------------ ;;; This file is part of The Mulkblog Project. ;;; ;;; The Mulkblog Project is free software. You can redistribute it and/or ;;; modify it under the terms of the Affero General Public License as ;;; published by Affero, Inc.; either version 1 of the License, or ;;; (at your option) any later version. ;;; ;;; The Mulkblog Project is distributed in the hope that it will be ;;; useful, but WITHOUT ANY WARRANTY; without even the implied warranty ;;; of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;;; Affero General Public License for more details. ;;; ;;; You should have received a copy of the Affero General Public ;;; License in the COPYING file that comes with The Mulkblog Project; if ;;; not, write to Affero, Inc., 510 Third Street, Suite 225, San ;;; Francisco, CA 94107 USA. ;;;------------------------------------------------------------------------ (in-package #:mulk.journal) (defun link-to (action &key comment-id post-id (absolute nil)) (with-output-to-string (out) (format out "~A" (if absolute "http://matthias.benkard.de/journal" ;; When testing on the local webserver, don't ;; use /journal as a relative URI, because it ;; won't work. (if (eq *site* :mst-plus) (http-getenv "SCRIPT_NAME") "/journal"))) (multiple-value-call #'(lambda (&rest args) (apply #'format out args)) (ecase action (:index "") (:full-index "/?full") (:view-atom-feed (values "/feed")) (:view-comment-feed (cond (post-id (values "/~D/comment-feed" post-id)) (t "/comment-feed"))) (:view (cond (comment-id (values "/~D#comment-~D" post-id comment-id)) (post-id (values "/~D" post-id)) (t "/"))) ((:edit :preview) (values "/~D/preview" post-id)) (:post-comment (values "/~D" post-id)) (:trackback (values "/~D/trackback" post-id)) (:view-atom-entry (values "/~D/atom" post-id)) (:save (values "/~D/save" post-id)) (:moderation-page "/moderate") (:css "/journal.css") (:prettify.css "/prettify/prettify.css") (:prettify.js "/prettify/prettify.js") (:prettify-lisp.js "/prettify/lang-lisp.js") (:pingback "/rpc"))))) (defun show-comment-feed () #.(locally-enable-sql-reader-syntax) (revalidate-cache-or-die "application/atom+xml; charset=UTF-8") (when (eq *mode* :http) (http-add-header "Last-Modified" (http-timestamp (compute-journal-last-modified-date))) (http-add-header "Content-Language" "de") (http-send-headers "application/atom+xml; charset=UTF-8")) (flet ((atom-time (time) (format-date nil "%4yr%-%2mon%-%2day%T%2hr%:%2min%:%2sec%Z" time 0))) (with-xml-output (*standard-output* :encoding "utf-8") (with-tag ("feed" '(("xmlns" "http://www.w3.org/2005/Atom"))) (with-tag ("title") (xml-as-is "Kommentare — Kompottkins Weisheiten")) (emit-simple-tags :updated (atom-time (max (or (single-object (select [max [slot-value 'journal-entry 'date]] :from [journal-entry] :flatp t)) 0) (or (single-object (select [max [slot-value 'journal-entry 'last-modification]] :from [journal-entry] :flatp t)) 0))) :id "urn:uuid:9cd7a24c-10a6-4895-a97b-8df6b426e4a0") (with-tag ("subtitle") (xml-as-is "Geschwafel zum Geschwafel eines libertärsozialistischen Geeks")) (with-tag ("author") (emit-simple-tags :name "Various")) (with-tag ("link" `(("rel" "alternate") ("type" "text/html") ("href" ,(link-to :index :absolute t))))) (with-tag ("link" `(("rel" "self") ("type" "application/atom+xml") ("href" ,(if *post-number* (link-to :view-comment-feed :post-id *post-number* :absolute t) (link-to :view-comment-feed :absolute t)))))) (let ((number 0)) (dolist (journal-comment (select 'journal-comment :where [= [slot-value 'journal-comment 'spam-p] "f"] :order-by '(([date] :desc)) :flatp t)) (with-slots (entry uuid date body author website spam-p id) journal-comment (when (and (not spam-p) (or (not *post-number*) (= (id-of entry) *post-number*))) (incf number) (with-tag ("entry") (emit-simple-tags :title (format nil "Kommentar zu: ~A" (title-of entry)) :id (format nil "urn:uuid:~(~A~)" uuid) :updated (atom-time date) :published (atom-time date)) (with-tag ("link" `(("rel" "alternate") ("type" "text/html") ("href" ,(link-to :view :comment-id id :post-id (id-of entry) :absolute t))))) (when (<= number 8) ;; We only include the body for the most recent ;; posts in order to save bandwidth. (with-tag ("content" `(("type" "xhtml") ("xml:lang" "de") ("xml:base" ,(link-to :index :absolute t)))) (with-tag ("div" '(("xmlns" "http://www.w3.org/1999/xhtml"))) (xml-as-is (with-yaclml-output-to-string (<:as-html (render-comment-body body))))))))))))))) #.(restore-sql-reader-syntax-state)) (defun show-atom-entry () #.(locally-enable-sql-reader-syntax) (revalidate-cache-or-die "application/atom+xml; charset=UTF-8") (when (eq *mode* :http) (http-add-header "Last-Modified" (http-timestamp (compute-journal-last-modified-date))) (http-add-header "Content-Language" "de") (http-send-headers "application/atom+xml; charset=UTF-8")) (with-xml-output (*standard-output* :encoding "utf-8") (show-atom-entry-xml (find-entry *post-number*) :full-content t :include-edit-links t))) (defun show-atom-entry-xml (journal-entry &key full-content include-edit-links) (flet ((atom-time (time) (format-date nil "%4yr%-%2mon%-%2day%T%2hr%:%2min%:%2sec%Z" time 0))) (with-slots (title date body categories last-modification id) journal-entry (with-tag ("entry") (with-tag ("title" `(("type" "html"))) (xml-as-is title)) (emit-simple-tags :id (format nil "urn:uuid:~(~A~)" (uuid-of journal-entry)) :updated (atom-time (or last-modification date)) :published (atom-time date)) (with-tag ("link" `(("rel" "alternate") ("type" "text/html") ("href" ,(link-to :view :post-id id :absolute t))))) (when include-edit-links (with-tag ("link" `(("rel" "service.edit") ("type" "application/atom+xml") ("href" ,(link-to :view-atom-entry :post-id id :absolute t)) ("title" ,title))))) (when full-content ;; Escaped HTML or embedded XHTML? What shall we prefer? #+(or) (with-tag ("content" `(("type" "html") ("xml:lang" "de") ("xml:base" ,(link-to :index :absolute t)))) (xml-out (if (equal (entry-type-of journal-entry) "html") (body-of journal-entry) (journal-markup->html (body-of journal-entry))))) (with-tag ("content" `(("type" "xhtml") ("xml:lang" "de") ("xml:base" ,(link-to :index :absolute t)))) (with-tag ("div" '(("xmlns" "http://www.w3.org/1999/xhtml"))) (xml-as-is (htmlise-entry journal-entry)))))))) #.(restore-sql-reader-syntax-state)) (defun htmlise-entry (journal-entry) (if (equal (entry-type-of journal-entry) "html") (body-of journal-entry) (journal-markup->html (body-of journal-entry)))) (defun show-atom-feed (&key include-edit-links full-content) #.(locally-enable-sql-reader-syntax) (revalidate-cache-or-die "application/atom+xml; charset=UTF-8") (when (eq *mode* :http) (http-add-header "Last-Modified" (http-timestamp (compute-journal-last-modified-date))) (http-add-header "Content-Language" "de") (http-send-headers "application/atom+xml; charset=UTF-8")) (flet ((atom-time (time) (format-date nil "%4yr%-%2mon%-%2day%T%2hr%:%2min%:%2sec%Z" time 0))) (with-xml-output (*standard-output* :encoding "utf-8") (with-tag ("feed" '(("xmlns" "http://www.w3.org/2005/Atom"))) (emit-simple-tags :title "Kompottkins Weisheiten" :updated (atom-time (max (or (single-object (select [max [slot-value 'journal-entry 'date]] :from [journal-entry] :flatp t)) 0) (or (single-object (select [max [slot-value 'journal-entry 'last-modification]] :from [journal-entry] :flatp t)) 0))) :id "urn:uuid:88ad4730-90bc-4cc1-9e1f-d4cdb9ce177c") (with-tag ("subtitle") (xml-as-is "Geschwafel eines libertärsozialistischen Geeks")) (with-tag ("author") (emit-simple-tags :name "Matthias Benkard")) (with-tag ("link" `(("rel" "alternate") ("type" "text/html") ("href" ,(link-to :index :absolute t))))) (with-tag ("link" `(("rel" "self") ("type" "application/atom+xml") ("href" ,(link-to :view-atom-feed :absolute t))))) (when include-edit-links (with-tag ("link" `(("rel" "service.post") ("type" "application/atom+xml") ("href" ,(link-to :view-atom-entry :absolute t))))) (with-tag ("link" `(("rel" "service.feed") ("type" "application/atom+xml") ("href" ,(link-to :view-atom-entry :absolute t))))) #+(or) (with-tag ("link" `(("rel" "service.categories") ("type" "application/atom+xml") ("href" ,(link-to :view-atom-entry :absolute t)))))) (let ((number 0)) (dolist (journal-entry (select 'journal-entry :order-by '(([date] :desc)) :flatp t)) ;; We only include the body for the most recent posts in ;; order to save bandwidth. (show-atom-entry-xml journal-entry :full-content (or (and (last-modification-of journal-entry) (> (last-modification-of journal-entry) (- (get-universal-time) (* 30 24 60 60)))) (<= number 8) full-content) :include-edit-links include-edit-links) (incf number)))))) #.(restore-sql-reader-syntax-state)) (let ((scanner (ppcre:create-scanner "(\\n|\\r|\\r\\n)(\\n|\\r|\\r\\n)+"))) (defun render-comment-body (text) (loop for last-position = 0 then (cadr matches) for matches = (ppcre:all-matches scanner text) then (cddr matches) while (not (endp matches)) do (<:p (<:as-html (subseq text last-position (car matches)))) finally (<:p (<:as-html (subseq text last-position)))))) (defun show-journal-entry (journal-entry &key (comments-p nil)) (with-slots (id title body categories date type) journal-entry (show-journal-entry-with-components id title body categories date type (comments-about journal-entry :ordered-p t :ham-p t) comments-p (trackbacks-about journal-entry :ordered-p t :ham-p t)))) (defun show-journal-entry-with-components (id title body categories posting-date type comments comments-p trackbacks) (unless *full-entry-view* (<:tr (<:td (<:a :href (link-to :view :post-id id) (<:as-is title))) (<:td :style "text-align: right" (<:as-is (format-date nil "%day%.%mon%.%yr%, %hr%:%2min%" posting-date))) (<:td (<:a :href (link-to :view :post-id id) (<:as-is (format nil "~D Kommentar~:*~[e~;~:;e~]" (length comments))))))) (when *full-entry-view* (
html body)))) (