242 lines
10 KiB
Org Mode
242 lines
10 KiB
Org Mode
#+title: Publishing my Website with Org
|
||
#+author: Howard X. Abrams
|
||
#+date: 2020-12-22
|
||
#+tags: emacs org
|
||
|
||
A literate programming file for publishing my website using org.
|
||
|
||
#+begin_src emacs-lisp :exports none
|
||
;;; org-publishing --- Publishing my website using org. -*- lexical-binding: t; -*-
|
||
;;
|
||
;; © 2020-2023 Howard X. Abrams
|
||
;; Licensed under a Creative Commons Attribution 4.0 International License.
|
||
;; See http://creativecommons.org/licenses/by/4.0/
|
||
;;
|
||
;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
|
||
;; Maintainer: Howard X. Abrams
|
||
;; Created: December 22, 2020
|
||
;;
|
||
;; This file is not part of GNU Emacs.
|
||
;;
|
||
;; *NB:* Do not edit this file. Instead, edit the original literate file at:
|
||
;; ~/other/hamacs/org-publishing.org
|
||
;; And tangle the file to recreate this one.
|
||
;;
|
||
;;; Code:
|
||
#+end_src
|
||
* Introduction
|
||
While the Emacs community have a plethora of options for generating a static website from org-formatted files, I keep my pretty simple, and use the standard =org-publish= feature.
|
||
|
||
While the following packages come with Emacs, they aren't necessarily loaded:
|
||
|
||
#+begin_src emacs-lisp :results silent
|
||
(require 'ox-rss)
|
||
(require 'ox-publish)
|
||
#+end_src
|
||
|
||
Variable settings:
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq org-export-with-broken-links t
|
||
org-mode-websrc-directory "~/website"
|
||
org-mode-publishing-directory (concat (getenv "HOME") "/website-pub/"))
|
||
#+end_src
|
||
|
||
I’m not afraid of HTML, however, I like the idea of doing my HTML work in a Lisp-like way using the [[https://github.com/tonyaldon/jack][jack-html project]]:
|
||
#+begin_src emacs-lisp
|
||
(use-package jack)
|
||
#+end_src
|
||
So the Lisp code:
|
||
#+begin_src emacs-lisp :tangle no
|
||
(jack-html '(:p "Hello there"))
|
||
#+end_src
|
||
Returns the string:
|
||
#+begin_example
|
||
<p>Hello there</p>
|
||
#+end_example
|
||
* The Projects
|
||
I separate my /website/ into distinct projects separately built:
|
||
|
||
- =blog-content= :: The bulk of rendering my =website= org files into HTML
|
||
- =blog-static= :: All of the assets, like images are easily copied in place
|
||
- =blog-rss= :: Regenerate the feeder files
|
||
- =org-notes= :: Optionally render a non-web site collection of notes.
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq org-publish-project-alist
|
||
`(("all"
|
||
:components ("blog-content" "blog-static" "org-notes" "blog-rss"))
|
||
|
||
("blog-content"
|
||
:base-directory ,org-mode-websrc-directory
|
||
:base-extension "org"
|
||
:publishing-directory ,org-mode-publishing-directory
|
||
:recursive t
|
||
:publishing-function org-html-publish-to-html
|
||
:preparation-function org-mode-blog-prepare
|
||
:export-with-tags nil
|
||
:headline-levels 4
|
||
:auto-preamble t
|
||
:auto-postamble nil
|
||
:auto-sitemap t
|
||
:sitemap-title "Howardisms"
|
||
:section-numbers nil
|
||
:table-of-contents nil
|
||
:with-toc nil
|
||
:with-author nil
|
||
:with-creator nil
|
||
:with-tags nil
|
||
:with-smart-quotes t
|
||
|
||
:html-doctype "html5"
|
||
:html-html5-fancy t
|
||
;; :html-preamble org-mode-blog-preamble
|
||
;; :html-postamble org-mode-blog-postamble
|
||
;; :html-postamble "<hr><div id='comments'></div>"
|
||
:html-head
|
||
,(jack-html
|
||
'(:meta (@ :http-equiv "X-Clacks-Overhead"
|
||
:content "GNU Terry Pratchett")
|
||
(:link (@ :rel "stylesheet"
|
||
:type "text/css"
|
||
:href "http://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700&subset=latin,latin-ext"))
|
||
(:link (@ :rel "stylesheet"
|
||
:type "text/css"
|
||
:href "http://fonts.googleapis.com/css?family=Source+Serif+Pro:400,700&subset=latin,latin-ext"))
|
||
(:link (@ :rel "stylesheet"
|
||
:type "text/css"
|
||
:href "http://fonts.googleapis.com/css?family=Source+Code+Pro:400,700"))
|
||
(:link (@ :rel "stylesheet"
|
||
:type "text/css"
|
||
:href "/css/styles.css"))
|
||
(:script (@ :src "/js/magic.js"
|
||
:type "text/javascript"))
|
||
(:link (@ :rel "icon"
|
||
:href "/img/dragon.svg"))
|
||
(:link (@ :rel "shortcut icon"
|
||
:href "/img/dragon-head.svg"))
|
||
(:meta (@ :name "viewport"
|
||
:content "width=device-width, initial-scale=1"))))
|
||
:html-head-include-default-style nil)
|
||
|
||
("blog-static"
|
||
:base-directory ,org-mode-websrc-directory
|
||
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|svg"
|
||
:publishing-directory ,org-mode-publishing-directory
|
||
:recursive t
|
||
:publishing-function org-publish-attachment)
|
||
|
||
("blog-rss"
|
||
:base-directory ,org-mode-websrc-directory
|
||
:base-extension "org"
|
||
:rss-image-url "https://howardism.org/img/dragon-head.png"
|
||
:publishing-directory ,org-mode-publishing-directory
|
||
:publishing-function (org-rss-publish-to-rss)
|
||
:html-link-home "https://www.howardism.org/"
|
||
:html-link-use-abs-url t
|
||
:with-toc nil
|
||
:exclude ".*"
|
||
:include ("index.org"))
|
||
|
||
("org-notes"
|
||
:base-directory "~/technical/"
|
||
:base-extension "org"
|
||
:publishing-directory ,(concat org-mode-publishing-directory "/notes/")
|
||
:recursive t
|
||
:publishing-function org-html-publish-to-html
|
||
:headline-levels 4 ; Just the default for this project.
|
||
:auto-preamble t
|
||
:auto-sitemap t ; Generate sitemap.org automagically...
|
||
:makeindex t
|
||
:section-numbers nil
|
||
:style ,(jack-html
|
||
'(:link (@ :rel "stylesheet"
|
||
:type "text/css"
|
||
:href "../css/styles.css")
|
||
:script (@ :type "text/javascript"
|
||
:src "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js")
|
||
:link (@ :ref "stylesheet"
|
||
:type "text/css"
|
||
:href "http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/smoothness/jquery-ui.css")
|
||
:script (@ :type "text/javascript"
|
||
:src "https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js")
|
||
:script (@ :type "text/javascript"
|
||
:src "js/magic.js")))
|
||
:table-of-contents nil
|
||
:with-author nil
|
||
:with-creator nil
|
||
:with-tags nil)
|
||
|
||
("org-notes-static"
|
||
:base-directory "~/technical/"
|
||
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
|
||
:publishing-directory ,(concat org-mode-publishing-directory "/other/")
|
||
:recursive t
|
||
:publishing-function org-publish-attachment)))
|
||
#+end_src
|
||
* Including Sections
|
||
In the project definitions, I reference a =pre-= and =postamble= that allow me to inject some standard HTML file headers and footers:
|
||
|
||
#+begin_src emacs-lisp
|
||
(defun org-mode-blog-preamble (options)
|
||
"The function that creates the preamble top section for the blog.
|
||
OPTIONS contains the property list from the org-mode export."
|
||
(message "Preamble options: %s" (princ options))
|
||
(let ((base-directory (plist-get options :base-directory)))
|
||
(org-babel-with-temp-filebuffer (expand-file-name "top-bar.html" base-directory) (buffer-string))))
|
||
|
||
(defun org-mode-blog-postamble (options)
|
||
"The function that creates the postamble, or bottom section for the blog.
|
||
OPTIONS contains the property list from the org-mode export."
|
||
(let ((base-directory (plist-get options :base-directory)))
|
||
(org-babel-with-temp-filebuffer (expand-file-name "bottom.html" base-directory) (buffer-string))))
|
||
#+end_src
|
||
|
||
Another helper function for the content of website is to make sure to update =index.org=, so that the RSS gets generated.
|
||
#+begin_src emacs-lisp
|
||
(defun org-mode-blog-prepare (&optional options)
|
||
"`index.org' should always be exported so touch the file before publishing."
|
||
(let* ((base-directory (plist-get options :base-directory))
|
||
(buffer (find-file-noselect (expand-file-name "index.org" base-directory) t)))
|
||
(with-current-buffer buffer
|
||
(set-buffer-modified-p t)
|
||
(save-buffer 0))
|
||
(kill-buffer buffer)))
|
||
#+end_src
|
||
* Keybindings
|
||
Make it easy to publish all or just some of my website:
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'ha-org
|
||
(ha-leader :keymaps 'org-mode-map
|
||
;; "p" '(:ignore t :which-key "publishing")
|
||
"o p a" '("all" . org-publish-all)
|
||
"o p p" '("project" . org-publish-project)))
|
||
#+end_src
|
||
|
||
And let's put a /leader key/ sequence for my favorite file on my website:
|
||
#+begin_src emacs-lisp
|
||
(ha-leader
|
||
"f h" '(:ignore t :which-key "howards")
|
||
"f h i" '("website index" . (lambda ()
|
||
(find-file (expand-file-name "index.org" "~/website")))))
|
||
#+end_src
|
||
* Technical Artifacts :noexport:
|
||
Let's =provide= a name so we can =require= it:
|
||
|
||
#+begin_src emacs-lisp :exports none
|
||
(provide 'ha-org-publishing)
|
||
;;; ha-org-publishing.el ends here
|
||
#+end_src
|
||
|
||
Before you can build this on a new system, make sure that you put the cursor over any of these properties, and hit: ~C-c C-c~
|
||
|
||
#+DESCRIPTION: A literate programming version for publishing my website using org.
|
||
|
||
#+PROPERTY: header-args:sh :tangle no
|
||
#+PROPERTY: header-args:emacs-lisp :tangle yes
|
||
#+PROPERTY: header-args :results none :eval no-export :comments no mkdirp yes
|
||
|
||
#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil
|
||
#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil
|
||
#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
|