












关于如何使用 org-publish 发布博客,见 使用 org-publish 发布博客,这里不再赘述。
对于 org-publish,由于我将所有的博客文件都放在了一个目录,要区分 draft 和 published,就不能通过原来的文件目录了。
尽管 org-publish 提供了 :inlucde 关键字,但它只支持一个文件列表的字符串,无法用正则匹配 denote 文件名上的 keyword,所以我目前只能用 :exclude 关键字,将不需要的 keyword 排除掉。
感谢 Tommie Mami 的回复,可以先 exlucde 所有文件,再 include 对应的 keyword,从而过滤需要的文件。
:include 需要传入一个文件列表,可以通过 denote 提供的方法 denote-diretory-files 获得,不过由于我用了 silos, denote-diretory-files 是不会去找 silos 中的文件的,我还需要临时改变一下 denote-directory 的值,使其指向 silos 的路径。
另外,因为 setq org-publish-project-alist 只执行了一次,后续新增了对应 keyword 的文章,org-publish-project-alist 里依然是上次设置的文件列表,而无法获取最新的文件列表。
因此,还需要在执行 org-publish 之前,再次更新 org-publish-project-alist 。
源代码见: init-org-publish.el。
重点关注 include 部分。
(defun spike-leung/get-file-list-from-denote-silo (silos tag)
"Return files in SILOS match TAG.
SILO is a file path from `denote-silo-directories'.
TAG is string."
(cl-letf ((denote-directory (expand-file-name silos)))
(denote-directory-files tag)))
(defun spike-leung/setup-org-publish-project-alist (&rest _args)
"Setup `org-publish-project-alist'."
(message "setup org-publish-project-alist")
(setq org-publish-project-alist
`(("orgfiles"
:base-directory "~/git/taxodium/posts"
:base-extension "org"
:exclude ".*" ;; 排除所有文件
:include ,(spike-leung/get-file-list-from-denote-silo "~/git/taxodium/posts" "_published") ;; 过滤获取对应 keyword 的文件
:publishing-directory "~/git/taxodium/publish"
:publishing-function spike-leung/org-html-publish-to-html-orgfiles
:section-numbers nil
:with-toc t
:with-tags t
:time-stamp-file nil
:html-head ,spike-leung/html-head
:html-preamble ,spike-leung/html-preamble-content
:html-postamble ,spike-leung/html-postamble
:auto-sitemap t
:sitemap-filename "index.org"
:sitemap-title "Taxodium"
:sitemap-format-entry spike-leung/sitemap-format-entry
:sitemap-sort-files anti-chronologically
:sitemap-function spike-leung/sitemap-function
:author "Spike Leung"
:email "l-yanlei@hotmail.com")
;; 省略其他代码,具体可以看源码
)))
(spike-leung/setup-org-publish-project-alist) ;; 初始化 org-publish-project-alist
;; 在执行 org-publish 之前更新 org-publish-project-alist
(advice-add 'org-publish :before #'spike-leung/setup-org-publish-project-alist)
另外,生成 sitemap 的时候,我希望使用 export_file_name 的作为链接名称,而不是 denote 的文件名,这需要调整 orgfiles 中 :sitemap-format-entry 的逻辑。
(setq org-publish-project-alist
`(("orgfiles"
:base-directory "~/git/taxodium/posts"
:base-extension "org"
:exclude ,(spike-leung/org-publish-build-exclude-regexp '("draft" "blackhole") '("rss.org"))
:publishing-directory "~/git/taxodium/publish"
:publishing-function spike-leung/org-html-publish-to-html-orgfiles
:section-numbers nil
:with-toc t
:with-tags t
:time-stamp-file nil
:html-head ,spike-leung/html-head
:html-preamble ,spike-leung/html-preamble-content
:html-postamble ,spike-leung/html-postamble
:auto-sitemap t
:sitemap-filename "index.org"
:sitemap-title "Taxodium"
:sitemap-format-entry spike-leung/sitemap-format-entry ;; <-- 调整 entry 的返回
:sitemap-sort-files anti-chronologically
:sitemap-function spike-leung/sitemap-function
:author "Spike Leung"
:email "l-yanlei@hotmail.com")
;; 省略部分代码
))
(defun spike-leung/sitemap-format-entry (entry style project)
"Custom format for site map ENTRY, as a string.
ENTRY is a file name. STYLE is the style of the sitemap.
PROJECT is the current project."
(let* ((export-file-name (spike-leung/org-publish-get-org-keyword entry project "export_file_name")))
(cond ((not (directory-name-p entry))
(format "[[file:%s][%s]]"
(or
(if export-file-name
(format "%s.org" export-file-name)
nil)
entry)
(org-publish-find-title entry project)))
((eq style 'tree)
;; Return only last subdir.
(file-name-nondirectory (directory-file-name entry)))
(t entry))))
(defun spike-leung/org-publish-get-org-keyword (entry project keyword)
"Get the value of the given KEYWORD in the current Org file.
KEYWORD is case-insensitive."
(let ((file (org-publish--expand-file-name entry project)))
(when (and (file-readable-p file) (not (directory-name-p file)))
(org-with-file-buffer file
(let* ((normalized-keyword (s-upcase keyword))
(keywords (org-collect-keywords (list normalized-keyword))))
(car (cdr (assoc normalized-keyword keywords))))))))
Denote Org 可以使用 dynamic blocks 插入 denote 链接,我现在的 sitemap(主页)是手工编写的,通过 dynamic blocks 插入对应的链接内容(你可以将首页 index.html 替换为 index.org 查看 org 文件)。
有的文章我会设置 #+subtitle ,我需要 denote 插入链接的时候能够将 subtitle 也显示在链接里。这可以通过自定义 denote-link-description-format 实现。
(use-package denote
:custom
;; 修改 `denote-link-description-format' 的返回,添加 subtitle
(denote-link-description-format #'spike-leung/denote-link-description-with-signature-and-title-and-subtitle)
:config
(defun spike-leung/denote-link-description-with-signature-and-title-and-subtitle (file &optional file-type)
"Return link description for FILE with FILE-TYPE.
For backward compatibility, FILE-TYPE is an optional parameter. If it
is nil, then compute FILE-TYPE internally.
- If the region is active, use it as the description.
- If FILE has a signature, then format the description as a sequence of
the signature text and the title with two spaces between them.
- If FILE does not have a signature, then use its title as the
description.
- If FILE has subtitle, append subtitle
- If none of the above works, return an empty string.
This function is useful as the value of the user option
`denote-link-description-format' (which can optionally be bound to a
function)."
(let* ((type (or file-type (denote-filetype-heuristics file)))
(signature (denote-retrieve-filename-signature file))
(title (denote-retrieve-title-or-filename file type))
(region-text (denote--get-active-region-content))
(subtitle (spike-leung/my-denote--get-subtitle file)))
(cond
(region-text region-text)
((and signature title) (format "%s %s" signature title))
((and title subtitle) (format "%s - %s" title subtitle))
(title (format "%s" title))
(signature (format "%s" signature))
(t ""))))
)
(defun spike-leung/my-denote--get-subtitle (file)
"Find #+subtitle in FILE and return its value.
Return nil if not found or FILE does not exist."
(when (and file (file-exists-p file))
(with-temp-buffer
(insert-file-contents file)
(goto-char (point-min))
(when (re-search-forward "^#\\+subtitle: \\(.*\\)$" nil t)
(string-trim (match-string-no-properties 1))))))
完整的 denote 设置见 init-denote.el。
将 index.org 转换成 index.html 时,我还希望将 subtitle 从链接中移除,放到链接下面呈现。我是通过在导出之后,修改 HTML 内容实现的。
我调整了生成 sitemap 的 :publishing-function 实现:
(defun spike-leung/setup-org-publish-project-alist (&rest _args)
"Setup `org-publish-project-alist'."
(message "setup org-publish-project-alist")
(setq org-html-htmlize-output-type 'css)
(setq org-publish-project-alist
`(("sitemap"
:base-directory "~/git/taxodium/posts"
:base-extension "org"
:include ("index.org")
:exclude ".*"
:publishing-directory ,spike-leung/org-publish-default-publishing-directory
:time-stamp-file nil
:section-numbers nil
:html-head ,spike-leung/html-head-sitemap
:html-preamble ,spike-leung/html-preamble-content
:html-postamble ,spike-leung/html-postamble-sitemap
:publishing-function spike-leung/org-html-publish-sitemap (publishing-function)
:html-htmlize-output-type css
:author "Spike Leung"
:email "l-yanlei@hotmail.com"))))
(defun spike-leung/org-html-publish-sitemap (plist filename pub-dir)
"`org-publish' `:publishing-function' for sitemap.
Add subtitle to links which has subtitle.
See `org-html-publish-to-html' for param PLIST,FILENAME,PUB-DIR."
(let ((output-filename (org-html-publish-to-html plist filename pub-dir)))
;; 这里应该用 `with-temp-buffer' 而不要用 `with-current-buffer' 和 '`find-file-noselect'
;; see: https://emacs.stackexchange.com/questions/2868/whats-wrong-with-find-file-noselect
(with-temp-buffer
(insert-file-contents output-filename)
(goto-char (point-min))
(while (re-search-forward
(rx (group "<a" (*? anychar) ">" (*? anychar)) " - " (group (*? anychar)) (group "</a>"))
nil t)
(let ((subtitle (match-string 2)))
(replace-match (format "\\1\\3<span class=\"sitemap-subtitle\">%s</span>" subtitle))))
(write-region (point-min) (point-max) output-filename))
output-filename))
完整的 org-publish 实现见: init-org-publish.el。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。