|
(in-package :cl-user) (defpackage :my-blog (:use :common-lisp)) (in-package :my-blog) (require :hunchentoot) (require :html-template) (require :elephant) (use-package :elephant) (defpclass blog-post () ((title :initarg :title :accessor title) (body :initarg :body :accessor body) (timestamp :initarg :timestamp :accessor timestamp :initform (get-universal-time) :index t) (url-part :initarg :url-part :accessor url-part :initform nil :index t))) (defmethod initialize-instance :after ((obj blog-post) &key) "If :url-part wasn't non-nil when making the instance, generate it automatically." (cond ((eq nil (url-part obj)) (setf (url-part obj) (make-url-part (title obj)))))) (defun make-url-part (title) "Generate a url-part from a title. A url-part should only have alphanumeric characters or dashes (in place of spaces)." (string-downcase (delete-if #'(lambda (x) (not (or (alphanumericp x) (char= #\- x)))) (substitute #\- #\Space title)))) (defun generate-index-page () "Generate the index page showing all the blog posts." (with-output-to-string (stream) (html-template:fill-and-print-template #P"index.tmpl" (list :blog-posts (loop for blog-post in (nreverse (get-instances-by-range 'blog-post 'timestamp nil nil)) collect (list :title (title blog-post) :body (body blog-post) :url-part (url-part blog-post)))) :stream stream))) (defun generate-blog-post-page (template) "Generate a page using blog post data." (let ((url-part (hunchentoot:query-string))) (with-output-to-string (stream) ; Create a stream that will give us a sting (let ((blog-post (get-instance-by-value 'blog-post 'url-part url-part))) ; Get the right blog post (html-template:fill-and-print-template template (list :title (title blog-post) :body (body blog-post) :url-part (url-part blog-post)) :stream stream))))) (defun view-blog-post-page () "Generate a page for viewing a blog post." (generate-blog-post-page #P"post.tmpl")) (defun edit-blog-post () (cond ((eq (hunchentoot:request-method) :GET) (generate-blog-post-page #P"post-edit.tmpl")) ((eq (hunchentoot:request-method) :POST) (save-blog-post)))) (defun save-blog-post () "Read POST data and modify blog post." (let ((blog-post (get-instance-by-value 'blog-post 'url-part (hunchentoot:query-string)))) (setf (title blog-post) (hunchentoot:post-parameter "title")) (setf (body blog-post) (hunchentoot:post-parameter "body")) (setf (url-part blog-post) (make-url-part (title blog-post))) (hunchentoot:redirect (url-part blog-post)))) ; Set the web server dispatch table (setq hunchentoot:*dispatch-table* (list (hunchentoot:create-regex-dispatcher "^/$" 'generate-index-page) (hunchentoot:create-regex-dispatcher "^/view/$" 'view-blog-post-page) (hunchentoot:create-regex-dispatcher "^/edit/$" 'edit-blog-post))) ; Make sure html-template looks for files in the right directory (setq html-template:*default-template-pathname* #P"/Users/vetler/Documents/devel/cl-webapp-intro/source/") ; Open the store where our data is stored (defvar *elephant-store* (open-store '(:clsql (:sqlite3 "/tmp/blog.db")))) ; Start the web server (defvar *ht-server* (hunchentoot:start-server :port 8080)) |