2023-12-03 18:57:36 +00:00
#+title : Publishing my Website with Org
#+author : Howard X. Abrams
#+date : 2020-12-22
#+tags : emacs org
2021-11-12 04:59:22 +00:00
A literate programming file for publishing my website using org.
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp :exports none
2022-03-09 18:45:37 +00:00
;;; org-publishing --- Publishing my website using org. -*- lexical-binding: t; -* -
;;
2023-02-23 17:35:36 +00:00
;; © 2020-2023 Howard X. Abrams
2022-06-18 00:25:47 +00:00
;; Licensed under a Creative Commons Attribution 4.0 International License.
2022-03-09 18:45:37 +00:00
;; 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:
2024-10-19 20:34:01 +00:00
;; ~/src/hamacs/org-publishing.org
2022-03-09 18:45:37 +00:00
;; And tangle the file to recreate this one.
;;
;;; Code:
2022-06-18 00:25:47 +00:00
#+end_src
2021-11-12 04:59:22 +00:00
* Introduction
2022-06-18 00:25:47 +00:00
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.
2021-11-12 04:59:22 +00:00
While the following packages come with Emacs, they aren't necessarily loaded:
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp :results silent
2024-08-11 04:59:26 +00:00
(use-package org
:config
(require 'ox-html)
(require 'ox-rss)
(require 'ox-publish))
2022-06-18 00:25:47 +00:00
#+end_src
2021-11-12 04:59:22 +00:00
2024-05-17 04:34:11 +00:00
Render my code with my font colors:
#+begin_src emacs-lisp :results silent
(use-package htmlize)
#+end_src
2024-08-11 04:59:26 +00:00
Also, we need Jack, and his HTML prowess:
#+begin_src emacs-lisp
(use-package jack
:straight (:host github :repo "tonyaldon/jack")
:commands (jack-html))
#+end_src
2021-11-12 04:59:22 +00:00
Variable settings:
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp
2023-12-21 03:54:30 +00:00
(setq org-publish-project-alist nil ; filled in below
org-export-with-broken-links t
2022-12-03 19:14:42 +00:00
org-mode-websrc-directory "~/website"
org-mode-publishing-directory (concat (getenv "HOME") "/website-pub/ "))
2022-06-18 00:25:47 +00:00
#+end_src
2023-12-20 04:14:22 +00:00
2023-12-21 03:54:30 +00:00
Since I have two specific websites, I create two variables for those destinations:
#+begin_src emacs-lisp
(setq ha-publishing-howardabrams (concat org-mode-publishing-directory "howardabrams")
ha-publishing-howardism (concat org-mode-publishing-directory "howardisms"))
#+end_src
2021-11-12 04:59:22 +00:00
* The Projects
I separate my /website/ into distinct projects separately built:
- =blog-content= :: The bulk of rendering my =website= org files into HTML
2023-12-22 04:26:51 +00:00
- =blog-static= :: Copies all assets, like images, in place
2021-11-12 04:59:22 +00:00
- =blog-rss= :: Regenerate the feeder files
- =org-notes= :: Optionally render a non-web site collection of notes.
2023-12-21 03:54:30 +00:00
** The Website
2023-12-22 04:26:51 +00:00
Years of having two domain names can be confusing, but I have to keep them going now. My =howardabrams.com= is essentially a single static page (oh, and email).
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp
2023-12-21 03:54:30 +00:00
(add-to-list 'org-publish-project-alist
`("website"
:base-directory "~/dropbox/website-howardabrams"
:publishing-directory ,ha-publishing-howardabrams))
#+end_src
** The Blog
2023-12-22 04:26:51 +00:00
My main blog made up a collection of org files:
2023-12-21 03:54:30 +00:00
#+begin_src emacs-lisp
(add-to-list 'org-publish-project-alist
`("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
2024-01-19 04:52:06 +00:00
:auto-preamble nil
2023-12-21 03:54:30 +00:00
:auto-postamble nil
:auto-sitemap t
:sitemap-title "Howardisms"
:section-numbers nil
:table-of-contents nil
:with-toc nil
2024-05-19 16:28:39 +00:00
:with-title t
2023-12-21 03:54:30 +00:00
:with-author nil
:with-creator nil
:with-tags nil
:with-smart-quotes t
2021-11-12 04:59:22 +00:00
2023-12-21 03:54:30 +00:00
:html-doctype "html5"
:html-html5-fancy t
2024-01-19 04:52:06 +00:00
:html-head-include-default-style nil
2023-12-21 03:54:30 +00:00
;; :html-preamble org-mode-blog-preamble
2024-01-19 04:52:06 +00:00
:html-postamble org-mode-blog-postamble
2023-12-21 03:54:30 +00:00
;; :html-postamble "<hr ><div id='comments' ></div >"
2024-01-19 04:52:06 +00:00
:html-head-extra
2023-12-21 03:54:30 +00:00
,(jack-html
2024-01-19 04:52:06 +00:00
'((:link (@ :rel "stylesheet"
2023-12-20 04:14:22 +00:00
:type "text/css"
:href "http://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700&subset=latin,latin-ext"))
2023-12-21 03:54:30 +00:00
(:link (@ :rel "stylesheet"
2023-12-20 04:14:22 +00:00
:type "text/css"
:href "http://fonts.googleapis.com/css?family=Source+Serif+Pro:400,700&subset=latin,latin-ext"))
2023-12-21 03:54:30 +00:00
(:link (@ :rel "stylesheet"
2023-12-20 04:14:22 +00:00
:type "text/css"
:href "http://fonts.googleapis.com/css?family=Source+Code+Pro:400,700"))
2023-12-21 03:54:30 +00:00
(:link (@ :rel "stylesheet"
2023-12-20 04:14:22 +00:00
:type "text/css"
:href "/css/styles.css"))
2023-12-21 03:54:30 +00:00
(:script (@ :type "text/javascript"
:src "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"))
2024-01-19 04:52:06 +00:00
(:script (@ :src "/js/magic.js" :type "text/javascript"))
(:meta (@ :name "viewport" :content "width=device-width, initial-scale=1"))
(:link (@ :rel "shortcut icon" :href "/img/dragon-head.svg"))
(:link (@ :rel "icon" :href "/img/dragon.svg"))
(:link (@ :rel "me" :href "https://emacs.ch/ @howard"))
(:meta (@ :http-equiv "X-Clacks-Overhead" :content "GNU Terry Pratchett"))))))
2023-12-21 03:54:30 +00:00
#+end_src
2021-11-12 04:59:22 +00:00
2023-12-21 03:54:30 +00:00
Why not break out the images and other static files into a separate project:
#+begin_src emacs-lisp
(add-to-list 'org-publish-project-alist
`("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))
#+end_src
2021-11-12 04:59:22 +00:00
2023-12-21 03:54:30 +00:00
The RSS generation seems to be something I do /later/ once I have my site working:
#+begin_src emacs-lisp
(add-to-list 'org-publish-project-alist
`("blog-rss"
2021-11-12 04:59:22 +00:00
:base-directory ,org-mode-websrc-directory
:base-extension "org"
2022-12-27 05:52:12 +00:00
:rss-image-url "https://howardism.org/img/dragon-head.png"
2021-11-12 04:59:22 +00:00
:publishing-directory ,org-mode-publishing-directory
:publishing-function (org-rss-publish-to-rss)
2022-12-27 05:52:12 +00:00
:html-link-home "https://www.howardism.org/ "
2021-11-12 04:59:22 +00:00
:html-link-use-abs-url t
:with-toc nil
:exclude ".*"
2023-12-21 03:54:30 +00:00
:include ("index.org")))
#+end_src
2021-11-12 04:59:22 +00:00
2023-12-21 03:54:30 +00:00
And let’ s make some blends of the individual projects:
#+begin_src emacs-lisp
(add-to-list 'org-publish-project-alist
`("blog" :components ("blog-content" "blog-static" "blog-rss")))
#+end_src
** Technical Notes
I take notes on a variety of technical subjects, and since I can share these notes with others, I feel like I can publish those:
#+begin_src emacs-lisp
(add-to-list 'org-publish-project-alist
`("tech-notes"
2021-11-12 04:59:22 +00:00
:base-directory "~/technical/ "
:base-extension "org"
2023-12-21 03:54:30 +00:00
:publishing-directory ,(concat org-mode-publishing-directory "notes/")
2021-11-12 04:59:22 +00:00
:recursive t
:publishing-function org-html-publish-to-html
2023-12-22 04:26:51 +00:00
:headline-levels 4
2021-11-12 04:59:22 +00:00
:auto-preamble t
:auto-sitemap t ; Generate sitemap.org automagically...
:makeindex t
:section-numbers nil
2023-12-20 04:14:22 +00:00
: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")))
2021-11-12 04:59:22 +00:00
:table-of-contents nil
2024-05-19 16:28:39 +00:00
:with-title t
2021-11-12 04:59:22 +00:00
:with-author nil
:with-creator nil
2023-12-21 03:54:30 +00:00
:with-tags nil))
#+end_src
2021-11-12 04:59:22 +00:00
2023-12-21 03:54:30 +00:00
As above, we can separate the publishing of the images and other static files:
#+begin_src emacs-lisp
(add-to-list 'org-publish-project-alist
`("tech-notes-static"
2021-11-12 04:59:22 +00:00
:base-directory "~/technical/ "
2024-05-17 04:34:11 +00:00
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|svg"
2024-10-19 20:34:01 +00:00
:publishing-directory ,(concat org-mode-publishing-directory "/src/ ")
2021-11-12 04:59:22 +00:00
:recursive t
2023-12-21 03:54:30 +00:00
:publishing-function org-publish-attachment))
#+end_src
** Literate Emacs Configuration
2023-12-22 04:26:51 +00:00
I’ ve been committing my literate-style Emacs configuration for years now, and Github has rendered it well, but I felt I could publish this to my own web site as a /cleaner version/ .
2023-12-21 03:54:30 +00:00
#+begin_src emacs-lisp
(add-to-list 'org-publish-project-alist
2023-12-22 04:26:51 +00:00
`("hamacs"
2024-10-19 20:34:01 +00:00
:base-directory "~/src/hamacs"
2023-12-22 04:26:51 +00:00
:publishing-directory ,(concat org-mode-publishing-directory "hamacs/")
:publishing-function org-html-publish-to-html
:recursive t
:auto-preamble nil
:auto-sitemap nil
:makeindex nil
:section-numbers nil
:html-head-include-default-style nil
:html-head ,(jack-html
'(:link (@ :rel "stylesheet" :type "text/css"
:href "../css/styles.css")))
:html-head-extra nil
:table-of-contents t
2024-05-19 16:28:39 +00:00
:with-title t
2023-12-22 04:26:51 +00:00
:with-author nil
:with-creator nil
:with-tags nil))
(add-to-list 'org-publish-project-alist
`("hamacs-static"
2024-10-19 20:34:01 +00:00
:base-directory "~/src/hamacs"
2024-05-17 04:34:11 +00:00
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|svg"
2023-12-22 04:26:51 +00:00
:publishing-directory ,(concat org-mode-publishing-directory "hamacs")
:recursive t
:publishing-function org-publish-attachment))
2022-06-18 00:25:47 +00:00
#+end_src
2024-01-19 04:52:06 +00:00
** Airbnb
I have an ADU on my property that I rent out through Airbnb. The place is full of QR Codes that display everything from local restaurants to how to play the Raspberry Pi Arcade my son and I built.
#+begin_src emacs-lisp
(add-to-list 'org-publish-project-alist
`("airbnb"
:base-directory "~/website/airbnb"
:publishing-directory ,(concat org-mode-publishing-directory "airbnb/")
:publishing-function org-html-publish-to-html
:recursive t
:auto-preamble nil
:auto-sitemap nil
:makeindex nil
:section-numbers nil
:html-head-include-default-style nil
:html-head ,(jack-html
'(:link (@ :rel "stylesheet" :type "text/css"
:href "airbnb.css")))
:html-head-extra nil
:table-of-contents nil
2024-05-19 16:28:39 +00:00
:with-title t
2024-01-19 04:52:06 +00:00
:with-author nil
:with-creator nil
:with-tags nil))
(add-to-list 'org-publish-project-alist
`("airbnb-static"
:base-directory "~/website/airbnb"
2024-05-17 04:34:11 +00:00
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|svg"
2024-01-19 04:52:06 +00:00
:publishing-directory ,(concat org-mode-publishing-directory "airbnb/")
:recursive t
:publishing-function org-publish-attachment))
#+end_src
2021-11-12 04:59:22 +00:00
* Including Sections
In the project definitions, I reference a =pre-= and =postamble= that allow me to inject some standard HTML file headers and footers:
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp
2021-11-12 04:59:22 +00:00
(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))))
2022-06-18 00:25:47 +00:00
#+end_src
2021-11-12 04:59:22 +00:00
Another helper function for the content of website is to make sure to update =index.org= , so that the RSS gets generated.
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp
2021-11-12 04:59:22 +00:00
(defun org-mode-blog-prepare (&optional options)
2023-12-22 04:26:51 +00:00
"Change modification of `index.org' before publishing."
2021-11-12 04:59:22 +00:00
(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)))
2022-06-18 00:25:47 +00:00
#+end_src
2023-12-21 03:54:30 +00:00
* Uploading
Using =rsync= to keep published files in sync with my website:
#+begin_src emacs-lisp
(defun ha-sync-site (project)
"Sync PROJECT (an org publish project) with my website."
(interactive (list (completing-read "Publish project: "
org-publish-project-alist)))
(let* ((host "gremlin.howardabrams.com")
(conf (thread-last org-publish-project-alist
(seq-filter (lambda (lst) (string-equal (car lst) project)))
(car)
(cdr)))
2024-01-19 04:52:06 +00:00
(parent (plist-get conf :publishing-directory))
(combos (cond
2024-02-24 06:20:25 +00:00
((s-starts-with? "blog" project)
2024-01-19 04:52:06 +00:00
'("Technical" "howardism"
"Personal" "howardism"
2024-05-17 04:34:11 +00:00
"RPG" "howardism"
2024-01-19 04:52:06 +00:00
"index.html" "howardism"
"about-me.html" "howardabrams"))
2024-02-28 04:54:22 +00:00
((s-starts-with? "tech" project) '("" "howardabrams"))
2024-02-24 06:20:25 +00:00
((s-starts-with? "hamacs" project) '("" "howardabrams"))
((s-starts-with? "airbnb" project) '("" "howardabrams")))))
2024-01-19 04:52:06 +00:00
;; (dolist (tuple (seq-partition combos 2))
;; (seq-let (src dest) tuple
;; (format "rsync -az %s/%s %s:%s" parent src host dest)))
(thread-last (seq-partition combos 2)
(seq-map (lambda (tuple) (seq-let (src dest) tuple
(format "rsync -avz %s%s %s:%s" parent src host dest))))
(s-join "; ")
2024-02-24 06:20:25 +00:00
(message)
2024-01-19 04:52:06 +00:00
(async-shell-command))))
2023-12-21 03:54:30 +00:00
#+end_src
2024-08-11 04:59:26 +00:00
** Workflows for Hamacs
A single function to publish and sync my literate initialization of Emacs.
The idea is that pushing a Git commit to this project, triggers a Forgejo Workflow, that /exports/ the literate text as HTML to [[https://www.howardabrams.com/hamacs ][my website ]].
#+begin_src emacs-lisp
(defun hamacs-publishing-workflow ()
"Render and push a new web version of my Emacs initialization."
(interactive)
(org-publish-project "hamacs")
(org-publish-project "hamacs-static")
(ha-sync-site "hamacs"))
#+end_src
2021-11-12 04:59:22 +00:00
* Keybindings
2023-12-22 04:26:51 +00:00
Make it easy to publish all projects or single project:
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp
2024-07-26 04:44:18 +00:00
(ha-leader :keymaps 'org-mode-map
"o p" '(:ignore t :which-key "publish")
"o p a" '("all" . org-publish-all)
"o p p" '("project" . org-publish-project)
"o p s" '("sync site" . ha-sync-site)
"o p h" '("hamacs" . (lambda () (interactive)
(org-publish-project "hamacs")
(sit-for 30)
(ha-sync-site "hamacs"))))
2022-06-18 00:25:47 +00:00
#+end_src
2021-11-12 04:59:22 +00:00
And let's put a /leader key/ sequence for my favorite file on my website:
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp
2021-11-12 04:59:22 +00:00
(ha-leader
2023-10-12 22:51:12 +00:00
"f h" '(:ignore t :which-key "howards")
"f h i" '("website index" . (lambda ()
(find-file (expand-file-name "index.org" "~/website")))))
2022-06-18 00:25:47 +00:00
#+end_src
2021-11-12 04:59:22 +00:00
* Technical Artifacts :noexport:
Let's =provide= a name so we can =require= it:
2022-06-18 00:25:47 +00:00
#+begin_src emacs-lisp :exports none
2021-11-12 04:59:22 +00:00
(provide 'ha-org-publishing)
;;; ha-org-publishing.el ends here
2022-06-18 00:25:47 +00:00
#+end_src
2021-11-12 04:59:22 +00:00
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~
2024-03-07 04:02:25 +00:00
#+description : A literate programming version for publishing my website using org.
2021-11-12 04:59:22 +00:00
2024-03-07 04:02:25 +00:00
#+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
2021-11-12 04:59:22 +00:00
2024-03-07 04:02:25 +00:00
#+options : num:nil toc:t todo:nil tasks:nil tags:nil date:nil
#+options : skip:nil author:nil email:nil creator:nil timestamp:nil
#+infojs_opt : view:nil toc:t ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js