diff --git a/fun/elblog/.gitignore b/fun/elblog/.gitignore new file mode 100644 index 000000000..c531d9867 --- /dev/null +++ b/fun/elblog/.gitignore @@ -0,0 +1 @@ +*.elc diff --git a/fun/elblog/README.md b/fun/elblog/README.md new file mode 100644 index 000000000..994b1138e --- /dev/null +++ b/fun/elblog/README.md @@ -0,0 +1,11 @@ +elblog +====== + +This is a simple blogging software written in Emacs Lisp. + +The idea is that it should be able to do most of the things [my actual blog][] +does at the moment. + +No documentation exists for now besides the commit messages, but it works! + +[my actual blog]: https://www.tazj.in/ diff --git a/fun/elblog/blog.css b/fun/elblog/blog.css new file mode 100644 index 000000000..0d021f78e --- /dev/null +++ b/fun/elblog/blog.css @@ -0,0 +1,37 @@ + diff --git a/fun/elblog/blog.el b/fun/elblog/blog.el new file mode 100644 index 000000000..102aa3791 --- /dev/null +++ b/fun/elblog/blog.el @@ -0,0 +1,123 @@ +;;; blog.el --- A simple org-mode & elnode blog software. +;;; -*- lexical-binding: t; -*- + +(require 'dash) +(require 'elnode) +(require 'f) +(require 'ht) + +;; Definition of customization options + +(defgroup elblog nil + "Configuration for the Emacs Lisp blog software" + :link '(url-link "https://github.com/tazjin/elblog")) + +(defcustom elblog-port 8010 + "Port to run elblog's HTTP server on" + :group 'elblog + :type 'integer) + +(defcustom elblog-host "localhost" + "Host for elblog's HTTP server to listen on" + :group 'elblog + :type 'string) + +(defcustom elblog-title "Elblog" + "Title text for this elblog instance" + :group 'elblog + :type 'string) + +(defcustom elblog-article-directory nil + "Directory in which elblog articles are stored" + :group 'elblog + :type 'string) + +(defcustom elblog-additional-routes '() + "Additional Elnode routes to register in the Elblog instance" + :group 'elblog + :type '(alist :key-type regexp :value-type function)) + +;; Declare user-configurable variables needed at runtime. + +(defvar elblog-articles (ht-create) + "A hash-table of blog articles. This is used for looking up articles from + URL fragments as well as for rendering the index.") + +;; HTML templating setup + +(defun template-preamble () + "Templates the preamble snippet with the correct blog title." + (format (f-read-text "preamble.html") elblog-title)) + +(defun configure-org-html-export () + "Configure org-mode settings for elblog's HTML templating to work correctly." + (setq org-html-postamble t) + (setq org-html-doctype "html5") + (setq org-html-head-include-scripts nil) + (setq org-html-style-default (f-read-text "blog.css")) + (setq org-html-preamble-format `(("en" ,(template-preamble)))) + (setq org-html-postamble-format `(("en" ,(f-read-text "postamble.html"))))) + +;; Article fetching & rendering functions + +(defun render-org-buffer (input-buffer &optional force) + "Renders an org-mode buffer as HTML and returns the name of the output buffer." + (letrec ((output-buffer (concat (buffer-name input-buffer) "-rendered")) + ;; Don't re-render articles unless forced. + (must-render (or force + (not (get-buffer output-buffer))))) + (if (and input-buffer must-render) + (with-current-buffer input-buffer + (org-export-to-buffer 'html output-buffer nil nil t))) + (if input-buffer output-buffer nil))) + +(defun get-buffer-string (buffer) + "Returns the contents of the specified buffer as a string." + (with-current-buffer (get-buffer buffer) + (buffer-string))) + +(defvar-local article-not-found + '(404 . "

Oh no, the article was not found.

")) + +(defvar-local text-html '("Content-Type" . "text/html")) + +(defun render-article (article) + "Renders an article, if it exists." + (letrec ((rendered (-some->> + (ht-get elblog-articles article) + (concat elblog-article-directory) + (find-file) + (render-org-buffer)))) + (if rendered `(200 . ,(get-buffer-string rendered)) + article-not-found))) + +(defun blog-post-handler (httpcon) + "This handler servers a blog post from the configured blog post directory." + (let ((response (render-article (elnode-http-mapping httpcon 1)))) + (elnode-http-start httpcon (car response) text-html) + (elnode-http-return httpcon (cdr response)))) + +;; Web server implementation + +(defvar elblog-routes + '(("^.*//\\(.*\\)" . blog-post-handler)) + "The default routes available in elblog. They can be extended by the user +by setting the elblog-additional-routes customize option.") + +(defun elblog-handler (httpcon) + (elnode-hostpath-dispatcher + httpcon + (-concat elblog-additional-routes elblog-routes))) + +(defun start-elblog () + (interactive) + (configure-org-html-export) + (elnode-start 'elblog-handler + :port elblog-port + :host elblog-host)) + +(defun stop-elblog () + (interactive) + (elnode-stop elblog-port)) + +(provide 'elblog) diff --git a/fun/elblog/postamble.html b/fun/elblog/postamble.html new file mode 100644 index 000000000..16a26218a --- /dev/null +++ b/fun/elblog/postamble.html @@ -0,0 +1,9 @@ +
+ diff --git a/fun/elblog/preamble.html b/fun/elblog/preamble.html new file mode 100644 index 000000000..be74b9207 --- /dev/null +++ b/fun/elblog/preamble.html @@ -0,0 +1,6 @@ +
+

+ %s +

+
+