Emacs: 使用org-mode制作网站
- TAG: 编码
使用 ox-publish
导出我的网站。
如果你折腾过各种建站方式,一定少不了这个。本文介绍一种org-mode自带的网页导出器来生成网站,希望你感觉兴趣!
关于这个网站
org-mode 很强大,可以写作、做计划、做幻灯片、做网站。本站就是用org-mode内置的 ox-publish
导出器制作而成。
以下是制作过程,希望对感兴趣的朋友有所帮助。
网站结构
$ tree -L 2 . ├── Makefile #编译文件 ├── Org-mode # 文章目录 │ ├── 404.org │ ├── about.org │ ├── index.org #首页 │ ├── microtalk │ ├── microtalk.org │ ├── reading │ ├── rss.org #自动生产的rss文件 │ ├── sitemap.org #网站地图 │ ├── skills.org #技能树 │ └── talk.org ├── assets #静态文件 │ ├── distro-head.html │ ├── distro-html_preamble.html │ ├── dj-head.html │ ├── dj-html_preamble.html │ ├── favicon.ico │ ├── fonts #字体文件目录 │ ├── prot-head.html #页面头部信息,如导航栏、加载的css文件字体文件、广告代码等 │ ├── prot-html_postamble.html #页脚信息 │ ├── prot-html_preamble.html #页首信息 │ ├── rss.png │ ├── static #css文件目录 │ ├── systemcrafters-head.html │ ├── systemcrafters-html_postamble.html │ └── systemcrafters-html_preamble.html ├── build-site.el #主配置,网页导出 ├── build.sh #命令行网页导出 assets/static # 网站样式根据兴趣收集 $ tree . ├── distro-htmlize.css ├── distro-jquery.stickytableheaders.min.js ├── distro-readtheorg.css ├── distro-readtheorg.js ├── dj-style.css ├── font.css ├── images │ ├── header_bg.png │ ├── icon-beian.png │ ├── made-with-emacs.png │ └── made-with-emacs.webp ├── orgcss-style.css ├── prot-style-print.css ├── prot-style.css ├── systemcrafters-style-code.css ├── systemcrafters-style.css └── worg-style.css assets/fonts # 网站字体使用仓耳今楷 $ tree . ├── angelina.ttf ├── jinkai.ttf └── jinkai.woff2
提前准备
build-site.el 配置
emacs 加载配置文件
emacs仅使用 build-site.el
做配置导出网页。所以配置文件包含初始化包源、引用 ox-publish
包导出网页、引用 ox-rss
生成rss文件
;;; packages ;;;; Initialize the package system (setq make-backup-files nil) (require 'package) (setq package-user-dir (expand-file-name "./.packages")) ;; (setq package-archives '(("melpa" . "https://melpa.org/packages/") ;; ("elpa" . "https://elpa.gnu.org/packages/"))) (setq package-archives '( ("melpa" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/melpa/") ("gnu" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/gnu/") ("org" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/org/"))) (unless (bound-and-true-p package--initialized) (package-initialize)) (when (not package-archive-contents) (package-refresh-contents)) ;; Check and install dependencies (dolist (package '(htmlize ox-rss)) (unless (package-installed-p package) (package-install package))) ;; Load publishing system (require 'ox-publish) ;; (require 'htmlize) (require 'ox-rss) (setq org-export-html-coding-system 'utf-8-unix)
生成站点地图
os-publish
自带生成站点地图配置,且支持自定义方法。这里使用自定方法生成站点地图。包含文章预览功能
;;; Sitemap preprocessing ;;;; Get Preview (defun my/get-preview (file) "get preview text from a file Uses the function here as a starting point: https://ogbe.net/blog/blogging_with_org.html" (with-temp-buffer (insert-file-contents file) (goto-char (point-min)) (when (re-search-forward "^#\\+BEGIN_PREVIEW$" nil 1) (goto-char (point-min)) (let ((beg (+ 1 (re-search-forward "^#\\+BEGIN_PREVIEW$" nil 1))) (end (progn (re-search-forward "^#\\+END_PREVIEW$" nil 1) (match-beginning 0)))) (buffer-substring beg end))))) ;;;; Format Sitemap (defun my/org-publish-org-sitemap (title list) "Sitemap generation function." (concat "#+OPTIONS: toc:nil") (org-list-to-subtree list)) (setq org-export-global-macros '(("timestamp" . "@@html:<span class=\"timestamp\">$1</span>@@"))) (defun my/org-publish-org-sitemap-format (entry style project) "Custom sitemap entry formatting: add date" (cond ((not (directory-name-p entry)) (let ((filename (org-publish-find-title entry project)) (preview (if (my/get-preview (concat "Org-mode/" entry)) (my/get-preview (concat "Org-mode/" entry)) ""))) (format "{{{timestamp(%s)}}} [[file:%s][%s]]\n%s" (format-time-string "%Y-%m-%d" (org-publish-find-date entry project)) entry filename preview) )) ((eq style 'tree) (file-name-nondirectory (directory-file-name entry))) (t entry)))
只需要把方法添加到网页导出配置里就可以了,如
;;; define publishing project (setq org-publish-project-alist `( ("org-site:main" :recursive t ... :auto-sitemap t :sitemap-title nil;"Blog" :sitemap-format-entry my/org-publish-org-sitemap-format :sitemap-function my/org-publish-org-sitemap :sitemap-sort-files anti-chronologically :sitemap-filename "sitemap.org" :sitemap-style tree ...
载入生成的站点地图的方法,只需要在文件中输入 #INCLUDE
指定加载的范围。 如加载sitemap.org文件中microtalk标题下前100个主题
#+INCLUDE: sitemap.org::*microtalk :lines "-100" :only-contents t
配置网页统一样式
ox-publish
默认导出的网页样式并不好看。你完全可以自己设计。
默认配置
;; 设置 org-publish 的项目列表 (setq org-publish-project-alist '( ;; 笔记部分 ("org-notes" :base-directory "~/org/" :base-extension "org" :exclude "\\(tasks\\|test\\|scratch\\|diary\\|capture\\|mail\\|habits\\|resume\\|meetings\\|personal\\|org-beamer-example\\)\\.org\\|test\\|article\\|roam\\|hugo" :publishing-directory "~/public_html/" :recursive t ; include subdirectories if t :publishing-function org-html-publish-to-html :headline-levels 6 :auto-preamble t :auto-sitemap t :sitemap-filename "sitemap.org" :sitemap-title "Sitemap" ;; 静态资源部分 ("org-static" :base-directory "~/org/" :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|mov" :publishing-directory "~/public_html/" :recursive t :publishing-function org-publish-attachment) ;; 项目集合 ("org" :components ("org-notes" "org-static")) )) )
配置说明:
这将配置变量 org-publish-project-alist
以定义我们网站的发布项目。阅读此变量的文档, M-x describe-variable
以获取更多信息!
这里最重要的两个配置是: sitemap-format-entry
以及 org-publish-project-alist
。前者是生成 sitemap 的方式,后者是定义publish要到哪个文件夹,把哪些org文件进行发布。
指定源和目标:
:base-directory
指定包含源文件的目录,即需要发布文件的基本目录:publishing-directory
指定转化后存放的目录:preparation-function
指定转化文件前需要调用的函数, 例如发布前需要 make 一下源文件:completion-function
指定转化文件完成后需要调用的函数
选择文件(用于识别需要发布的文件):
:base-extension
指定识别的文件拓展名, 不需要点(.)后缀 ,如:*:base-extension “css”*:exclude
用于排除指定文件:include
引入指定文件,存在该文件都会进行发布转化:recursive
为真时, 将递归检查发布 base-directory 指定目录下的文件
发布触发执行动作:
:publishing-function
执行发布操作将触发指定函数来完成工作org-html-publish-to-htm
默认 org 文件转化成 HTMLorg-publish-attachment
复制操作
我们配置完 ox-publish 后,可以通过两种方式来发布静态站点:
发布
- M-x org-publish-all 来发布(全部)站点
- 按 C-c C-e 后,再按 P a 来发布(全部)站点
当写完一篇文章之后需要手动调用 org-export-dispatch
选择 p f
,将当前 org 文档生成 HTML 网页文件。
测试
一个简单的网页就生成了,可以使用浏览器打开此 HTML 文件进行预览,也可以使用 Simple-Httpd
这个插件在 website-directory 文件下启动一个 server,之后在浏览器中打开 127.0.0.1:19999 就可以查看生成的博客内容啦。
使用如下命令生成网站,也可以将命令添加到 build.sh
文件中执行
emacs -Q --script build-site.el
本地测试:
python3 -m http.server --directory=~/public_html
Open http://localhost:8000/ in the Web browser
自定义页面展出配置
与手动写 html,css 一样这些在 org 里都是可自定义的,修改 org-publish 中的属性参 数和直接修改对应的 org 的 html 页面展出参数是等效的,请仔细阅读官方文档:https://orgmode.org/manual/Publishing-options.html
;; Formatting (defun file-contents (file) (with-temp-buffer (insert-file-contents file) (buffer-string))) ;;; define publishing project (setq org-publish-project-alist `( ("org-site:main" :recursive t :base-directory "./Org-mode" :publishing-directory "./public" :publishing-function org-html-publish-to-html :auto-sitemap t :sitemap-title nil;"Blog" :sitemap-format-entry my/org-publish-org-sitemap-format :sitemap-function my/org-publish-org-sitemap :sitemap-sort-files anti-chronologically :sitemap-filename "sitemap.org" :sitemap-style tree :exclude "^demo.org\\|^up.org\\|.*/homework/.*" ; Exclude drafts directory from publishing ;; https://orgmode.org/manual/Publishing-options.html#Generic-properties-1 :with-author nil ;; Don't include author name :with-creator t ;; Include Emacs and Org versions in footer :with-toc t ;; Include a table of contents :section-numbers nil ;; Don't include section numbers :time-stamp-file nil ;; Don't include time stamp in file :headline-levels 4 ;; Number of headline levels for export :with-sub-superscript nil ;; Don't include TeX-like syntax for sub- and superscripts ;; https://orgmode.org/manual/Publishing-options.html#ASCII-specific-properties-1 ;;; prot :html-preamble ,(file-contents "assets/prot-html_preamble.html") :html-postamble ,(file-contents "assets/prot-html_postamble.html") :html-head ,(file-contents "assets/prot-head.html") ;; ;;; dj ;; :html-preamble ,(file-contents "assets/dj-html_preamble.html") ;; :html-postamble ,(file-contents "assets/prot-html_postamble.html") ;; :html-head ,(file-contents "assets/dj-head.html") ;; ;;; systemcrafters ;; :html-preamble ,(file-contents "assets/systemcrafters-html_preamble.html") ;; :html-postamble ,(file-contents "assets/systemcrafters-html_postamble.html") ;; :html-head ,(file-contents "assets/systemcrafters-head.html") ;; ;;; distro ;; :html-preamble ,(file-contents "assets/distro-html_preamble.html") ;; :html-postamble ,(file-contents "assets/systemcrafters-html_postamble.html") ;; :html-head ,(file-contents "assets/distro-head.html") :html-head-include-default-style nil ;; ;;; https://emacs.stackexchange.com/questions/76599/how-to-adjust-html-elements-using-org-publish-in-emacs ;; :html-container "div class=\"container\"" :html-divs ((preamble "header" "preamble") (content "main class=\"container\"" "content") (postamble "footer" "postamble")) :html-container "section" :html-validation-link nil :html-html5-fancy t :html-doctype "html5" ;; :time-stamp-file nil :htmlized-source t ) ("org-site:static" :base-directory "./Org-mode/" :base-extension "png\\|jpg\\|webp\\|gif\\|mp3\\|m4a\\|ogg\\|swf\\|ico" :publishing-directory "./public" :recursive t :publishing-function org-publish-attachment :exclude "\\(^demo\\|^up\\)\\.org\\|.*/homework/.*" ; Exclude drafts directory from publishing ) ("org-site:assets" :base-directory "./assets/" :base-extension "css\\|js\\|ttf\\|woff2\\|png\\|jpg\\|webp\\|gif\\|pdf\\|mp3\\|m4a\\|ogg\\|swf\\|ico" :publishing-directory "./public/" :recursive t :publishing-function org-publish-attachment) ("org-site:rss" :recursive nil :base-directory "./Org-mode/" :base-extension "org" :exclude ".*" :include ("talk.org") :publishing-function my/org-rss-publish-to-rss :publishing-directory "./public" :rss-extension "xml" :auto-sitemap t :sitemap-filename "rss.org" :sitemap-title "Jasper Hsu' Blog" :sitemap-style list :sitemap-sort-files anti-chronologically :sitemap-function my/format-rss-feed ;;:sitemap-format-entry my/format-rss-feed-entry :html-link-home "https://xuchangwei.com/" :html-link-use-abs-url t :html-link-org-files-as-html t ) ("site" :components ("org-site:main" "org-site:static" "org-site:assets" "org-site:rss")) )) ;;; generate site output ;;(org-publish-all t) (org-publish-project "site") ;; del cache org-publish-timestamp-directory ~/.org-timestamps/ (message "Build Complete!")
结束
(provide 'build-site) ;; C-x C-c ;;(save-buffers-kill-terminal) ;;; build-site.el ends here
我们可以在配置文件的开头部分加入一些开源协议,如
;;; build-site.el --- Building a website -*- lexical-binding: t -*- ;; Copyright (C) 2024 Jasper Hsu ;; Author: Jasper Hsu <[email protected]> ;; Maintainer: Jasper Hsu <[email protected]> ;; URL: https://xuchangwei.com ;; Version: 0.1 ;; Keywords: ;; This file is NOT part of GNU Emacs. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or (at ;; your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see <https://www.gnu.org/licenses/>. ;; Commentary: ;; Comand: emacs -Q --script build-site.el ;;; Code: