aboutsummaryrefslogtreecommitdiff
path: root/ennum.el
diff options
context:
space:
mode:
authorArun Isaac2020-07-31 03:32:44 +0530
committerArun Isaac2020-07-31 03:32:44 +0530
commitcb070712c9326a97c750c9ccb5958fa0fa53e41b (patch)
tree3306e80a9d9371c3fde987796e0818f9fb4482c8 /ennum.el
parent44f86d8131d21097311cf29b5127bb33a897f3c4 (diff)
downloadennum-cb070712c9326a97c750c9ccb5958fa0fa53e41b.tar.gz
ennum-cb070712c9326a97c750c9ccb5958fa0fa53e41b.tar.lz
ennum-cb070712c9326a97c750c9ccb5958fa0fa53e41b.zip
Switch from ennum-operation to ennum-exp.
ennum-exp is a simple embedded domain specific language to express blog publishing in a more expression oriented form. * ennum.el (ennum-operation): Delete type. (ennum--do-operation): Delete function. (ennum-intern, ennum--hash, ennum-file-hash, ennum--file-hash, ennum--set-file-modes-recursively, ennum--rewrite-inputs, ennum-eval, ennum-store-item-union): New functions. (ennum-exp): New macro. (ennum-setting): Introduce :store setting. (ennum-publish-post, ennum-publish-generic, ennum-publish-index, ennum-publish-feed, ennum--feed-entry, ennum-publish-image, ennum-publish-copy, ennum-publish-link, ennum-publish): Switch from ennum-operation to ennum-exp.
Diffstat (limited to 'ennum.el')
-rw-r--r--ennum.el542
1 files changed, 312 insertions, 230 deletions
diff --git a/ennum.el b/ennum.el
index a1fac83..adba985 100644
--- a/ennum.el
+++ b/ennum.el
@@ -15,15 +15,126 @@
(defvar ennum-blog nil
"Property list specifying ennum publish settings")
-(cl-defstruct (ennum-post (:constructor ennum-make-post)
+;; TODO: Should the store have an absolute path to deal with directory
+;; changes? Yes, since we ask for an absolute working directory, we
+;; should.
+(defun ennum-intern (filename)
+ (let* ((store-item
+ (expand-file-name (ennum-file-hash filename)
+ (ennum-setting :store)))
+ (interned-path (expand-file-name (file-name-nondirectory filename)
+ store-item)))
+ (unless (file-exists-p store-item)
+ (ennum-copy filename interned-path)
+ (ennum--set-file-modes-recursively store-item #o555 #o444 #o555))
+ interned-path))
+
+(defun ennum--hash ()
+ "Return SHA256 hash of buffer contents encoded using
+filename-safe base64 encoding. See RFC 4648ยง5. Briefly, this is a
+variant of base64 encoding where characters + and / are replaced
+respectively by - and _, and the pad character = is optional."
+ (replace-regexp-in-string
+ (rx (any ?+ ?/ ?=))
+ (lambda (str)
+ (pcase str
+ ("+" "-")
+ ("/" "_")
+ ("=" "")))
+ (base64-encode-string
+ (secure-hash 'sha256 (current-buffer) nil nil t))))
+
+(defun ennum-file-hash (file)
+ (ennum--file-hash file (file-attribute-modification-time
+ (file-attributes file))))
+
+(defmemoize ennum--file-hash (file last-modified)
+ (with-temp-buffer
+ ;; TODO: Use ennum-with-file-contents
+ (set-buffer-multibyte nil)
+ (insert-file-contents-literally file)
+ (ennum--hash)))
+
+(defun ennum--set-file-modes-recursively (directory directory-mode file-mode executable-file-mode)
+ (chmod directory directory-mode)
+ (seq-do (lambda (file)
+ (cond
+ ((file-directory-p file) (chmod file directory-mode))
+ ((file-executable-p file) (chmod file executable-file-mode))
+ (t (chmod file file-mode))))
+ (ennum-directory-files directory t t)))
+
+(cl-defstruct (ennum-exp (:constructor ennum-make-exp)
(:copier nil))
+ inputs proc)
+
+(defun ennum--rewrite-inputs (exp)
+ (pcase exp
+ (`(ennum-input ,arg)
+ (let ((input-identifier (gensym "input")))
+ (vector input-identifier (list arg) (list input-identifier))))
+ (`(,parent . ,childern)
+ (seq-reduce (pcase-lambda (`[,result ,inputs ,input-identifiers] node)
+ (pcase (ennum--rewrite-inputs node)
+ (`[,modified-tree ,new-inputs ,new-input-identifiers]
+ (vector (append result (list modified-tree))
+ (append inputs new-inputs)
+ (append input-identifiers new-input-identifiers)))))
+ exp
+ (vector nil nil nil)))
+ (leaf (vector exp nil nil))))
+
+(defmacro ennum-exp (&rest body)
+ (let ((raw-expression `(progn ,@body)))
+ (pcase (ennum--rewrite-inputs raw-expression)
+ (`[,rewritten-body ,inputs ,input-identifiers]
+ `(ennum-eval
+ (ennum-make-exp
+ :inputs (list ,@inputs)
+ :proc (lambda ,input-identifiers ,rewritten-body)))))))
+
+(defun ennum-eval (exp)
+ (let ((output
+ (expand-file-name
+ (with-temp-buffer
+ (let ((print-length nil)
+ (print-level nil))
+ (print exp (current-buffer)))
+ (ennum--hash))
+ (ennum-setting :store))))
+ ;; Create store if it doesn't exist
+ (ennum-mkdir-p (ennum-setting :store))
+ ;; Create store item if it doesn't already exist
+ (if (file-exists-p output)
+ (message "Skipping build of %s" output)
+ (message "Building %s" output)
+ (ennum-with-temporary-directory temporary-directory
+ (ennum-with-current-directory temporary-directory
+ (apply (ennum-exp-proc exp)
+ (ennum-exp-inputs exp)))
+ (rename-file temporary-directory output))
+ (ennum--set-file-modes-recursively output #o555 #o444 #o555))
+ output))
+
+(defun ennum-store-item-union (items)
+ "Return a store item that is the union of ITEMS."
+ (ennum-exp
+ (seq-do (lambda (item)
+ (seq-do (lambda (destination)
+ ;; TODO: Print warning about overwriting files?
+ (let ((source (expand-file-name destination item)))
+ (unless (file-exists-p destination)
+ (if (file-directory-p source)
+ (make-directory destination)
+ (copy-file source destination t)))))
+ (reverse (ennum-directory-files item nil t))))
+ (ennum-input items))))
+
+(cl-defstruct (ennum-post (:constructor ennum-make-post)
+ (:copier nil))
filename slug author date language links tangle
summary tags thumbnail title translation-group video-posters)
-(cl-defstruct (ennum-operation (:constructor ennum-make-operation)
- (:copier nil))
- inputs outputs publish)
-
(defun ennum-posts (posts-directory)
(sort (ennum--filter-map (lambda (file)
(when (string= (file-name-extension file) "org")
@@ -126,63 +237,56 @@ non-nil, include directories in the output."
(concat (file-name-sans-extension filename) ".html"))
(defun ennum-publish-post (posts)
- (let ((link-publish-operations
- (seq-mapcat 'ennum-publish-link (seq-mapcat 'ennum-post-links posts)))
- (input-post-files (seq-map 'ennum-post-filename posts)))
- (append
- (list
- (ennum-make-operation
- :inputs (append input-post-files
- (seq-mapcat 'ennum-operation-inputs link-publish-operations))
- :outputs (seq-map 'ennum--org-output-filename input-post-files)
- :publish
- (lambda (&rest output-files)
- (seq-mapn
- (lambda (post output-file)
- (let ((system-time-locale (map-elt (ennum-setting :locale-alist)
- (ennum-post-language post) nil 'string=)))
- (ennum-with-file-contents (ennum-post-filename post)
- (org-export-to-file
- 'ennum-html output-file nil nil nil nil
- (list :ennum-translations (seq-remove (apply-partially 'equal post) posts)
- :ennum-video-posters (ennum-post-video-posters post))))))
- posts
- output-files))))
- (ennum--filter-map
- (lambda (post)
- (when (ennum-post-tangle post)
- (ennum-make-operation
- :inputs (list (ennum-post-filename post))
- :outputs (seq-map (lambda (tangle-output)
- (ennum--expand-relative tangle-output
- (ennum-setting :static-directory)))
- (ennum-post-tangle post))
- :publish (lambda (&rest output-files)
- ;; TODO: Handle tangle outputs that are nested
- ;; into directories, and when each tangle output
- ;; is nested into a different directory.
- (let ((post-file-copy (concat
- (file-name-directory (first output-files))
- (file-name-nondirectory (ennum-post-filename post)))))
- (copy-file (ennum-post-filename post) post-file-copy)
- (org-babel-tangle-file post-file-copy)
- (delete-file post-file-copy))))))
- posts)
- link-publish-operations)))
+ (seq-mapcat (lambda (post)
+ (append
+ (list
+ (ennum-exp
+ (ennum-input (seq-map (lambda (post)
+ (ennum-intern (ennum-post-filename post)))
+ posts))
+ (let ((input-org-file (ennum-input (ennum-intern (ennum-post-filename post))))
+ (output-file
+ (ennum--org-output-filename (ennum-post-filename post))))
+ (ennum-mkdir-p (file-name-directory output-file))
+ (let ((system-time-locale (map-elt (ennum-setting :locale-alist)
+ (ennum-post-language post) nil 'string=)))
+ (ennum-with-file-contents input-org-file
+ ;; TODO: Centralize these with feed generation
+ (org-export-to-file
+ 'ennum-html output-file nil nil nil nil
+ (list :ennum-translations (seq-remove (apply-partially 'equal post) posts)
+ :ennum-video-posters (ennum-post-video-posters post))))))))
+ (when (ennum-post-tangle post)
+ (list
+ (ennum-exp
+ ;; TODO: Handle tangle outputs that are nested
+ ;; into directories, and when each tangle output
+ ;; is nested into a different directory.
+ (let* ((input-org-file (ennum-input (ennum-intern (ennum-post-filename post))))
+ (post-file-copy
+ (expand-file-name
+ (file-name-nondirectory input-org-file)
+ (ennum-setting :static-directory))))
+ (ennum-copy input-org-file post-file-copy)
+ (org-babel-tangle-file post-file-copy)
+ (delete-file post-file-copy)))))
+ (seq-mapcat 'ennum-publish-link (ennum-post-links post))))
+ posts))
(defun ennum-publish-generic (other-files-directory file)
- (ennum-make-operation
- :inputs (list (ennum--expand-relative file other-files-directory))
- :outputs
- (list (pcase (file-name-extension file)
- ("org" (ennum--org-output-filename file))
- (_ file)))
- :publish (lambda (output-file)
- (pcase (file-name-extension file)
- ("org" (ennum-with-file-contents file
- (org-export-to-file 'html output-file)))
- (_ (ennum-copy (ennum--expand-relative file other-files-directory)
- output-file))))))
+ (ennum-exp
+ (let ((interned-file (ennum-input (ennum-intern (ennum--expand-relative file other-files-directory))))
+ (output-file
+ (pcase (file-name-extension file)
+ ("org" (ennum--org-output-filename file))
+ (_ file))))
+ (pcase (file-name-extension interned-file)
+ ("org"
+ (when (file-name-directory output-file)
+ (ennum-mkdir-p (file-name-directory output-file)))
+ (ennum-with-file-contents interned-file
+ (org-export-to-file 'html output-file)))
+ (_ (ennum-copy interned-file output-file))))))
(defun ennum-video-poster (video)
(or (seq-find (lambda (file)
@@ -211,51 +315,55 @@ non-nil, include directories in the output."
(let* ((tongue (ennum-post-language (first posts)))
(number-of-pages (ceiling (length posts) posts-per-page))
(page-numbers (number-sequence 1 number-of-pages)))
- (ennum-make-operation
- :inputs (seq-map 'ennum-post-filename posts)
- :outputs (cons (ennum-add-tongue-suffix (format "%s.html" filename-prefix) tongue)
- (seq-map (apply-partially 'ennum-index-filename filename-prefix tongue "html")
- page-numbers))
- :publish
- (lambda (home-page &rest output-files)
- (let ((system-time-locale (map-elt (ennum-setting :locale-alist) tongue nil 'string=)))
- (seq-mapn
- (lambda (posts page-number output-file)
- (with-temp-buffer
- (insert (format "#+TITLE: %s\n" title))
- (insert (format "#+LANGUAGE: %s\n" tongue))
- (insert "#+OPTIONS: num:nil toc:nil\n\n")
- (seq-do (lambda (post)
- (insert (format "* [[post:%s][%s]]\n" (ennum-post-slug post)))
- (insert (format-time-string "/%b %e, %Y/\n\n" (ennum-post-date post)))
- (when-let ((thumbnail (ennum-post-thumbnail post)))
- (insert (format "[[thumbnail:%s]]\n\n" thumbnail)))
- (when-let ((summary (ennum-post-summary post)))
- (insert summary)
- (insert "\n\n"))
- (when-let ((tags (ennum-post-tags post)))
- (insert "Tags: ")
- (insert
- (string-join
- (seq-map (lambda (tag)
- (format "[[tag:%s][%s]]" (ennum-add-tongue-suffix tag tongue) tag))
- tags)
- ", "))
- (insert "\n\n")))
- posts)
- (unless (= page-number 1)
- (insert (format "[[./%s][Newer posts]]\n\n"
- (ennum-index-filename (file-name-nondirectory filename-prefix)
- tongue nil (1- page-number)))))
- (unless (= page-number number-of-pages)
- (insert (format "[[./%s][Older posts]]\n"
- (ennum-index-filename (file-name-nondirectory filename-prefix)
- tongue nil (1+ page-number)))))
- (org-export-to-file 'html output-file)))
- (seq-partition posts posts-per-page)
- page-numbers
- output-files))
- (copy-file (first output-files) home-page)))))
+ (ennum-exp
+ (ennum-input (seq-map (lambda (post)
+ (ennum-intern (ennum-post-filename post)))
+ posts))
+ (let ((output-files (seq-map (apply-partially 'ennum-index-filename filename-prefix tongue "html")
+ page-numbers))
+ (system-time-locale (map-elt (ennum-setting :locale-alist) tongue nil 'string=)))
+ (seq-mapn
+ (lambda (posts page-number output-file)
+ (when (file-name-directory output-file)
+ (ennum-mkdir-p (file-name-directory output-file)))
+ (with-temp-buffer
+ (insert (format "#+TITLE: %s\n" title))
+ (insert (format "#+LANGUAGE: %s\n" tongue))
+ (insert "#+OPTIONS: num:nil toc:nil\n\n")
+ (seq-do (lambda (post)
+ (insert (format "* [[post:%s][%s]]\n"
+ (ennum-post-slug post)
+ (ennum-post-title post)))
+ (insert (format-time-string "/%b %e, %Y/\n\n" (ennum-post-date post)))
+ (when-let ((thumbnail (ennum-post-thumbnail post)))
+ (insert (format "[[thumbnail:%s]]\n\n" thumbnail)))
+ (when-let ((summary (ennum-post-summary post)))
+ (insert summary)
+ (insert "\n\n"))
+ (when-let ((tags (ennum-post-tags post)))
+ (insert "Tags: ")
+ (insert
+ (string-join
+ (seq-map (lambda (tag)
+ (format "[[tag:%s][%s]]" (ennum-add-tongue-suffix tag tongue) tag))
+ tags)
+ ", "))
+ (insert "\n\n")))
+ posts)
+ (unless (= page-number 1)
+ (insert (format "[[./%s][Newer posts]]\n\n"
+ (ennum-index-filename (file-name-nondirectory filename-prefix)
+ tongue nil (1- page-number)))))
+ (unless (= page-number number-of-pages)
+ (insert (format "[[./%s][Older posts]]\n"
+ (ennum-index-filename (file-name-nondirectory filename-prefix)
+ tongue nil (1+ page-number)))))
+ (org-export-to-file 'html output-file)))
+ (seq-partition posts posts-per-page)
+ page-numbers
+ output-files)
+ (copy-file (first output-files)
+ (ennum-add-tongue-suffix (format "%s.html" filename-prefix) tongue))))))
(defun ennum--absolute-uri (path)
(format "%s://%s/%s"
@@ -267,26 +375,30 @@ non-nil, include directories in the output."
(format-time-string "%Y-%m-%dT%H:%M:%SZ" date))
(defun ennum-publish-feed (feed-file title rights posts)
- (ennum-make-operation
- :inputs (seq-map 'ennum-post-filename posts)
- :outputs (list feed-file)
- :publish
- (lambda (output-file)
- (with-temp-file output-file
- (insert
- (xmlgen
- `(feed :xmlns "http://www.w3.org/2005/Atom"
- (id ,(ennum--absolute-uri ""))
- (title ,title)
- (updated ,(ennum--atom-date (ennum-post-date (first posts))))
- (link :rel "self" :href ,(ennum--absolute-uri feed-file))
- (generator
- ,(format "Emacs %d.%d Org-mode %s ennum %s"
- emacs-major-version emacs-minor-version (org-version) ennum-version))
- (rights ,rights)
- ,@(seq-map 'ennum--feed-entry posts))))))))
-
-(defun ennum--feed-entry (post)
+ (ennum-exp
+ ;; TODO: Create ennu-mkdir-p-for-file
+ (when (file-name-directory feed-file)
+ (ennum-mkdir-p (file-name-directory feed-file)))
+ (with-temp-file feed-file
+ (insert
+ (xmlgen
+ `(feed :xmlns "http://www.w3.org/2005/Atom"
+ (id ,(ennum--absolute-uri ""))
+ (title ,title)
+ (updated ,(ennum--atom-date (ennum-post-date (first posts))))
+ (link :rel "self" :href ,(ennum--absolute-uri feed-file))
+ (generator
+ ,(format "Emacs %d.%d Org-mode %s ennum %s"
+ emacs-major-version emacs-minor-version (org-version) ennum-version))
+ (rights ,rights)
+ ,@(seq-mapn (lambda (post interned-post-file)
+ (ennum--feed-entry post interned-post-file))
+ posts
+ (ennum-input (seq-map (lambda (post)
+ (ennum-intern (ennum-post-filename post)))
+ posts)))))))))
+
+(defun ennum--feed-entry (post interned-post-file)
(let ((link (ennum--absolute-uri (ennum--org-output-filename
(ennum-post-filename post)))))
`(entry (id ,link)
@@ -316,7 +428,7 @@ non-nil, include directories in the output."
:blog-scheme :default-image-width
:image-link-width :index-posts-per-page
:locale-alist :other-files-directory
- :tag-directory :thumbnail-image-width)
+ :store :tag-directory :thumbnail-image-width)
(plist-get (org-combine-plists
(list :atom-feed-number-of-posts 12
:atom-feed-file "blog.atom"
@@ -325,6 +437,7 @@ non-nil, include directories in the output."
:image-link-width 1024
:index-posts-per-page 12
:locale-alist '(("en" . "C"))
+ :store ".ennum"
:tag-directory "tag"
:thumbnail-image-width 320)
ennum-blog)
@@ -339,23 +452,22 @@ non-nil, include directories in the output."
(defun ennum--expand-relative (name directory)
(concat (file-name-as-directory directory) name))
-(defun ennum-publish-image (widths image)
- (ennum-make-operation
- :inputs (list image)
- :outputs (seq-map (apply-partially 'ennum-image-output-filename image)
- widths)
- :publish
- (lambda (&rest output-files)
- (seq-mapn (lambda (output-file width)
- (ennum-image-optimize-image
- (ennum-image-resize-image image output-file width)))
- output-files widths))))
+(defun ennum-publish-image (image width)
+ (ennum-exp
+ (let ((input-image (ennum-input (ennum-intern image))))
+ (ennum-mkdir-p (ennum-setting :images-directory))
+ (ennum-image-optimize-image
+ (ennum-image-resize-image
+ input-image
+ (ennum--expand-relative
+ (ennum-image-output-filename
+ (file-name-nondirectory input-image) width)
+ (ennum-setting :images-directory))
+ width)))))
(defun ennum-publish-copy (file)
- (ennum-make-operation
- :inputs (list file)
- :outputs (list file)
- :publish (apply-partially 'ennum-copy file)))
+ (ennum-exp
+ (ennum-copy (ennum-input (ennum-intern file)) file)))
(defun newest-file (files)
(pcase files
@@ -380,55 +492,25 @@ if DESTINATION already exists."
(defun ennum--filter-map (function sequence)
(seq-filter 'identity (seq-map function sequence)))
-;; TODO: What if a file was removed from the inputs? Detect that
-;; change as well.
-
-;; Two separate problems
-;; - tracking of list of inputs
-;; - depending on a function of inputs, or equivalently intermediate files
-
-;; Solve both problems with an "ennum store"
-(defun ennum--do-operation (temporary-directory operation)
- ;; TODO: Check all outputs were created correctly.
- (let* ((expand (lambda (directory file)
- (expand-file-name file directory)))
- (inputs (ennum-operation-inputs operation))
- (outputs (ennum-operation-outputs operation))
- (absolute-outputs
- (seq-map (apply-partially expand temporary-directory)
- outputs))
- (previous-outputs
- (seq-map (apply-partially expand (ennum-setting :output-directory))
- outputs)))
- (cond
- ((and (seq-every-p 'file-exists-p previous-outputs)
- (file-newer-than-file-p (newest-file previous-outputs)
- (newest-file inputs)))
- (message "Skipping publishing %s to %s" inputs outputs)
- (seq-mapn 'ennum-copy previous-outputs absolute-outputs))
- (t (message "Publishing %s to %s" inputs outputs)
- (seq-do 'ennum-mkdir-p
- (seq-uniq
- (seq-map 'file-name-directory absolute-outputs)))
- (apply (ennum-operation-publish operation) absolute-outputs)))))
-
-
(defun ennum-publish-link (link)
(pcase link
(`("image" . ,path)
- (list
- (ennum-publish-image
- (list (ennum-setting :default-image-width)
- (ennum-setting :image-link-width))
- (ennum--expand-relative path (ennum-setting :images-directory)))))
+ (seq-map (lambda (width)
+ (ennum-publish-image
+ (ennum--expand-relative path (ennum-setting :images-directory))
+ width))
+ (list (ennum-setting :default-image-width)
+ (ennum-setting :image-link-width))))
(`("static" . ,path)
(list
- (ennum-publish-copy (ennum--expand-relative path (ennum-setting :static-directory)))))
+ (ennum-publish-copy
+ (ennum--expand-relative path (ennum-setting :static-directory)))))
(`("video" . ,path)
- (list
- (ennum-publish-copy (ennum--expand-relative path (ennum-setting :video-directory)))
- (ennum-publish-copy (ennum--expand-relative (ennum-video-poster path)
- (ennum-setting :images-directory)))))))
+ (seq-map 'ennum-publish-copy
+ (list
+ (ennum--expand-relative path (ennum-setting :video-directory))
+ (ennum--expand-relative (ennum-video-poster path)
+ (ennum-setting :images-directory)))))))
(defmacro ennum-with-current-directory (directory &rest body)
"Change to DIRECTORY, evaluate BODY and restore the current
@@ -469,53 +551,53 @@ as keys. Keys are compared using `equal'."
(defun ennum-publish ()
(interactive)
- (let ((make-backup-files nil)
- (blog-title (ennum-setting :blog-title))
- (posts-per-page (ennum-setting :index-posts-per-page)))
- (ennum-with-current-directory (ennum-setting :working-directory)
- (ennum-with-temporary-directory temporary-directory
- (seq-do
- (apply-partially 'ennum--do-operation temporary-directory)
- (append
- (let ((posts (ennum-posts (ennum-setting :posts-directory))))
- (append
- ;; Publish posts
- (seq-mapcat (pcase-lambda (`(,translation-group . ,posts))
- (ennum-publish-post posts))
- (seq-group-by 'ennum-post-translation-group posts))
- ;; Publish feed
- (list (ennum-publish-feed (ennum-setting :atom-feed-file)
- blog-title
- (ennum-setting :blog-license)
- (seq-take posts (ennum-setting :atom-feed-number-of-posts))))
- ;; Publish indices
- (seq-map
- (pcase-lambda (`(,tongue . ,posts))
- (ennum-publish-index "index" blog-title posts-per-page posts))
- (seq-group-by 'ennum-post-language posts))
- (seq-mapcat
- (pcase-lambda (`(,tag . ,posts))
- (seq-map
- (pcase-lambda (`(,tongue . ,posts))
- (ennum-publish-index
- (ennum--expand-relative tag (ennum-setting :tag-directory))
- tag posts-per-page posts))
- (seq-group-by 'ennum-post-language posts)))
- (ennum-many-to-many-group-by 'ennum-post-tags posts))
- ;; Publish thumbnails
- (seq-map
- (apply-partially 'ennum-publish-image (list (ennum-setting :thumbnail-image-width)))
+ (ennum-with-current-directory (ennum-setting :working-directory)
+ (let* ((blog-title (ennum-setting :blog-title))
+ (posts (ennum-posts (ennum-setting :posts-directory)))
+ (posts-per-page (ennum-setting :index-posts-per-page))
+ (other-files-directory (ennum-setting :other-files-directory))
+ (result
+ (ennum-store-item-union
+ (append
+ ;; Publish posts
+ (seq-mapcat (pcase-lambda (`(,translation-group . ,posts))
+ (ennum-publish-post posts))
+ (seq-group-by 'ennum-post-translation-group posts))
+ ;; Publish feed
+ (list (ennum-publish-feed (ennum-setting :atom-feed-file)
+ blog-title
+ (ennum-setting :blog-license)
+ (seq-take posts (ennum-setting :atom-feed-number-of-posts))))
+ ;; Publish indices
+ (seq-map
+ (pcase-lambda (`(,tongue . ,posts))
+ (ennum-publish-index "index" blog-title posts-per-page posts))
+ (seq-group-by 'ennum-post-language posts))
+ ;; Publish tag indices
+ (seq-mapcat
+ (pcase-lambda (`(,tag . ,posts))
+ (seq-map
+ (pcase-lambda (`(,tongue . ,posts))
+ (ennum-publish-index
+ (ennum--expand-relative tag (ennum-setting :tag-directory))
+ tag posts-per-page posts))
+ (seq-group-by 'ennum-post-language posts)))
+ (ennum-many-to-many-group-by 'ennum-post-tags posts))
+ ;; Publish thumbnails
(seq-map (lambda (image)
- (ennum--expand-relative image (ennum-setting :images-directory)))
- (seq-uniq (ennum--filter-map 'ennum-post-thumbnail posts))))))
- ;; Publish other files
- (when-let ((other-files-directory (ennum-setting :other-files-directory)))
- (seq-map (apply-partially 'ennum-publish-generic other-files-directory)
- (ennum-directory-files other-files-directory)))))
- ;; Replace old output directory
- (let ((output (ennum-setting :output-directory)))
- (delete-directory output t)
- (rename-file temporary-directory output t))))))
+ (ennum-publish-image
+ (ennum--expand-relative image (ennum-setting :images-directory))
+ (ennum-setting :thumbnail-image-width)))
+ (ennum--filter-map 'ennum-post-thumbnail posts))
+ ;; Publish other files
+ (seq-map (apply-partially 'ennum-publish-generic other-files-directory)
+ (ennum-directory-files other-files-directory))))))
+ ;; Replace old output directory
+ (when (file-exists-p (ennum-setting :output-directory))
+ (ennum--set-file-modes-recursively (ennum-setting :output-directory) #o755 #o644 #o755)
+ (delete-directory (ennum-setting :output-directory) t))
+ (copy-directory result (ennum-setting :output-directory))
+ (message "Ennum published to %s" (expand-file-name (ennum-setting :output-directory))))))
;;; Server
;;;