hamacs/ha-org-publishing.org
2024-02-23 22:20:25 -08:00

376 lines
17 KiB
Org Mode
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#+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-publish-project-alist nil ; filled in below
org-export-with-broken-links t
org-mode-websrc-directory "~/website"
org-mode-publishing-directory (concat (getenv "HOME") "/website-pub/"))
#+end_src
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
** You Dont Know Jack
Im not afraid of HTML, but 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= :: Copies all assets, like images, in place
- =blog-rss= :: Regenerate the feeder files
- =org-notes= :: Optionally render a non-web site collection of notes.
** The Website
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).
#+begin_src emacs-lisp
(add-to-list 'org-publish-project-alist
`("website"
:base-directory "~/dropbox/website-howardabrams"
:publishing-directory ,ha-publishing-howardabrams))
#+end_src
** The Blog
My main blog made up a collection of org files:
#+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
:auto-preamble nil
: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-head-include-default-style nil
;; :html-preamble org-mode-blog-preamble
:html-postamble org-mode-blog-postamble
;; :html-postamble "<hr><div id='comments'></div>"
:html-head-extra
,(jack-html
'((: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 (@ :type "text/javascript"
:src "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"))
(: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"))))))
#+end_src
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
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"
: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")))
#+end_src
And lets 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"
: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
: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))
#+end_src
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"
: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
** Literate Emacs Configuration
Ive 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/.
#+begin_src emacs-lisp
(add-to-list 'org-publish-project-alist
`("hamacs"
:base-directory "~/other/hamacs"
: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
:with-author nil
:with-creator nil
:with-tags nil))
(add-to-list 'org-publish-project-alist
`("hamacs-static"
:base-directory "~/other/hamacs"
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
:publishing-directory ,(concat org-mode-publishing-directory "hamacs")
:recursive t
:publishing-function org-publish-attachment))
#+end_src
** 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
:with-author nil
:with-creator nil
:with-tags nil))
(add-to-list 'org-publish-project-alist
`("airbnb-static"
:base-directory "~/website/airbnb"
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
:publishing-directory ,(concat org-mode-publishing-directory "airbnb/")
: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)
"Change modification of `index.org' 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
* 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)))
(parent (plist-get conf :publishing-directory))
(combos (cond
((s-starts-with? "blog" project)
'("Technical" "howardism"
"Personal" "howardism"
"index.html" "howardism"
"about-me.html" "howardabrams"))
((s-starts-with? "tech" project)
'("" "howardabrams"))
((s-starts-with? "hamacs" project) '("" "howardabrams"))
((s-starts-with? "airbnb" project) '("" "howardabrams")))))
;; (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 "; ")
(message)
(async-shell-command))))
#+end_src
* Keybindings
Make it easy to publish all projects or single project:
#+begin_src emacs-lisp
(with-eval-after-load 'ha-org
(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")))))
#+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: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