;;; publish.el --- Publishing configuration for ryuslash.org -*- lexical-binding: t; -*- ;; Copyright (C) 2020 Tom Willemse ;; Author: Tom Willemse ;; Keywords: ;; Version: 1 ;; Package-Requires: (dockerfile-mode htmlize org org-contrib rainbow-delimiters ox-rss) ;; 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 . ;;; Commentary: ;; ;;; Code: (require 'dockerfile-mode) (require 'ob-dot) (require 'ox-publish) (require 'ox-rss) (require 'rainbow-delimiters) (require 'subr-x) (defconst publish-root (file-name-directory (or load-file-name (buffer-file-name))) "The directory where ‘oni-org’ was loaded from.") (add-hook 'prog-mode-hook 'rainbow-delimiters-mode) (defun publish-calculate-reading-time (buffer) "Calculate the amount of minutes it would take to read the contents of BUFFER." (with-current-buffer buffer (max 1 (/ (count-words (point-min) (point-max)) 228)))) (defun publish-format-reading-time (time) "Return a string describing TIME." (format "%d minute%s" time (if (= time 1) "" "s"))) (defun publish-extract-summary-from-file (file props) "Extract a summary from FILE. PROPS is used as an aid in getting the right information from the file." (format "* %s\n:PROPERTIES:\n:CUSTOM_ID: %s\n:PUBDATE: %s\n:RSS_PERMALINK: %s\n:END:\n\n%s\n\n[[file:%s][Read More]]\n\n" (car (org-publish-find-property file :title props)) (file-name-nondirectory file) (format-time-string "[%Y-%m-%d %a %H:%M]" (org-timestamp-to-time (car (org-publish-find-property file :date props)))) (file-name-nondirectory file) (car (org-map-entries (lambda () (let ((element-data (cadr (org-element-at-point)))) (buffer-substring-no-properties (map-elt element-data :contents-begin) (map-elt element-data :contents-end)))) "summary" (list file))) (file-name-nondirectory file))) (defun publish-get-latest-modified-time (files) "Get the latest modification time of any file from the list FILES." (car (last (sort (mapcar #'org-publish-cache-mtime-of-src files) #'time-less-p)))) (defun publish-time>= (a b) "Check if time A is greater than or equal to time B." (not (time-less-p a b))) (defun publish-empty-time () "Get an empty time value." (let ((current-time (current-time))) (time-subtract current-time current-time))) (defun publish-generate-index (props) "Generate an index from my posts. Argument PROPS ." (let* ((index-file (expand-file-name "posts/index.org")) (index-generated-time (or (and (file-exists-p index-file) (org-publish-cache-mtime-of-src index-file)) (publish-empty-time))) (files (directory-files "posts/" t (rx bos (= 8 digit) "-" (= 4 digit) "-" (one-or-more nonl) (not "~") eos))) (latest-modification-time (publish-get-latest-modified-time files))) (if (publish-time>= index-generated-time latest-modification-time) (message "Not generating index...") (progn (message "Generating index...") (with-temp-buffer (insert "#+title: ryuslash's blog\n") (insert "#+options: num:nil\n") (insert "\n") (apply 'insert (mapcar (lambda (file) (publish-extract-summary-from-file file props)) (take 30 (reverse files)))) (write-file "posts/index.org")))))) (setq org-export-exclude-tags '("noexport" "draft")) (setq org-confirm-babel-evaluate nil) (setq org-html-head-include-default-style nil) (setq org-html-htmlize-output-type 'css) (setq org-publish-timestamp-directory (concat default-directory "/.org-timestamps/")) (setq org-html-html5-fancy t) (setq org-html-doctype "html5") (setq org-publish-project-alist `(("index" :base-directory "." :base-extension "org" :publishing-directory "public/" :recursive t :exclude ,(rx string-start (or "posts/" (and "README.org" string-end))) :publishing-function org-html-publish-to-html :html-head "" :html-postamble t :html-postamble-format (("en" "

Find me on Mastodon

Date: %C

%c

"))) ("posts" :base-directory "posts/" :base-extension "org" :publishing-directory "public/posts/" :recursive t :publishing-function org-html-publish-to-html :html-head "" :html-preamble (lambda (project) (let ((buffer (find-file-noselect (buffer-file-name)))) (unless (member (car (org-publish-find-property (buffer-file-name) :title project)) '("ryuslash.org" "ryuslash's blog")) (publish-format-reading-time (publish-calculate-reading-time buffer)))))) ("rss" :base-directory "posts/" :base-extension "org" :rss-extension "xml" :preparation-function publish-generate-index :publishing-directory "public/posts/" :publishing-function (org-rss-publish-to-rss) :html-link-home "https://ryuslash.org/posts/" :html-link-use-abs-url t :section-numbers nil :exclude ".*" :include ("index.org") :table-of-contents nil) ("assets" :base-directory "." :recursive t :exclude "^public/" :base-extension "svg\\|png\\|jpg" :publishing-function org-publish-attachment :publishing-directory "public/") ("all" :components ("index" "posts" "rss" "assets")))) (defvar publish-feed-url-format ;"https://gitlab.com/ryuslash/ryuslash.org/-/commits/master/%s?feed_token=Rf8otgpS8YEiYakJN4NR&format=atom" "https://code.ryuslash.org/new-ryuslash.org/atom/%s?h=master" "Format string for the URL to the page’s atom feed.") (provide 'publish) ;;; publish.el ends here