diff options
-rw-r--r-- | ennu-html.el | 4 | ||||
-rw-r--r-- | ennu.el | 193 |
2 files changed, 92 insertions, 105 deletions
diff --git a/ennu-html.el b/ennu-html.el index 36796d1..9a24f9c 100644 --- a/ennu-html.el +++ b/ennu-html.el @@ -18,11 +18,11 @@ ;; TODO: Pass title through org-export-data-with-backend or something ;; similar in order to export org syntax in title (defun ennu-export-post (path desc backend) - (let ((post (concat (expand-file-name path (ennu-setting :posts-directory)) + (let ((filename (concat (expand-file-name path (ennu-setting :posts-directory)) ".org"))) (xmlgen `(a :href ,(expand-file-name* path (ennu-setting :posts-directory)) - ,(or desc (plist-get (ennu-post-metadata post) :title)))))) + ,(or desc (ennu-post-title (ennu-read-post filename))))))) (org-link-set-parameters "post" :export 'ennu-export-post) @@ -12,6 +12,64 @@ (defvar ennu-version "0.1.0" "Ennu version string") +(cl-defstruct (ennu-post (:constructor ennu-make-post) + (:copier nil)) + filename author date language links + summary tags thumbnail title translation-group) + +(defun ennu-posts (posts-directory) + (sort (seq-map 'ennu-read-post + (file-expand-wildcards + (concat (file-name-as-directory + (ennu-setting :posts-directory)) + "*.org"))) + 'ennu-later-post-p)) + +(defun ennu-later-post-p (post1 post2) + (time-less-p (ennu-post-date post2) + (ennu-post-date post1))) + +(defun ennu-read-post (filename) + (ennu--read-post + filename (file-attribute-modification-time + (file-attributes filename)))) + +(defmemoize ennu--read-post (filename last-modified) + (ennu-with-file-contents filename + (let ((metadata (org-export-get-environment 'ennu-html)) + (export (apply-partially 'org-export-with-backend 'ennu-html))) + (seq-do (lambda (key) + (unless (plist-member metadata key) + (user-error "Metadata %s not specified" key))) + ennu-mandatory-metadata) + (let ((links (org-element-map (org-element-parse-buffer) 'link + (lambda (link) + (pcase link + (`(link ,properties . ,_) + (let ((link-type (org-element-property :type link))) + (when (member link-type (list "image" "static" "video")) + (cons link-type (org-element-property :path link)))))))))) + (ennu-make-post + :filename filename + :author (funcall export (first (plist-get metadata :author))) + :date (org-timestamp-to-time (first (plist-get metadata :date))) + :language (plist-get metadata :language) + :links links + :summary (funcall export (first (plist-get metadata :summary))) + :tags (plist-get metadata :filetags) + :thumbnail (or (plist-get metadata :thumbnail) + (seq-some (lambda (link) + (pcase link + (`("image" . ,path) path) + (`("video" . ,path) (ennu-video-poster path)))) + links)) + :title (funcall export (first (plist-get metadata :title))) + :translation-group (or (plist-get metadata :translation-group) + (file-name-base filename))))))) + +(defvar ennu-mandatory-metadata + (list :title :date)) + (defmacro ennu-with-file-contents (file &rest body) "Create a temporary buffer, insert contents of FILE into that buffer and evaluate BODY. The value returned is the value of the @@ -26,10 +84,10 @@ last form in BODY." (defun ennu-publish-post (post) ;; TODO: Tangle post - `((,post) - (,(ennu--org-output-filename post)) + `((,(ennu-post-filename post)) + (,(ennu--org-output-filename (ennu-post-filename post))) ,(lambda (output-file) - (ennu-with-file-contents post + (ennu-with-file-contents (ennu-post-filename post) (org-export-to-file 'ennu-html output-file)) ;; (when-let (tangle-dir (or (plist-get posts-plist :valai-tangle-directory) ;; valai-tangle-directory)) @@ -61,14 +119,6 @@ last form in BODY." (`(,poster . ,_) poster) (`() (user-error "Poster for %s not found" video)))) -(defun ennu-post-thumbnail (post) - (or (plist-get (ennu-post-metadata post) :thumbnail) - (seq-some (lambda (link) - (pcase link - (`("image" . ,path) path) - (`("video" . ,path) (ennu-video-poster path)))) - (ennu-post-links post)))) - (defun ennu-add-tongue-suffix (filename tongue) (pcase tongue ("en" filename) @@ -88,7 +138,7 @@ last form in BODY." (defun ennu-publish-index (filename-prefix tongue title posts-per-page posts) (let* ((number-of-pages (ceiling (length posts) posts-per-page)) (page-numbers (number-sequence 1 number-of-pages))) - `(,posts + `(,(seq-map 'ennu-post-filename posts) ,(cons (ennu-add-tongue-suffix (format "%s.html" filename-prefix) tongue) (seq-map (apply-partially 'ennu-index-filename filename-prefix tongue "html") page-numbers)) @@ -100,23 +150,22 @@ last form in BODY." (insert (format "#+LANGUAGE: %s\n" tongue)) (insert "#+OPTIONS: num:nil toc:nil\n\n") (seq-do (lambda (post) - (let ((metadata (ennu-post-metadata post))) - (insert (format "* [[post:%s]]\n" (file-name-base post))) - (insert (format-time-string - "/%b %e, %Y/\n\n" - (plist-get metadata :date))) - (when-let ((thumbnail (ennu-post-thumbnail post))) - (insert (format "[[thumbnail:%s]]\n\n" thumbnail))) - (when-let ((summary (plist-get metadata :summary))) - (insert summary) - (insert "\n\n")) - (when-let ((tags (plist-get metadata :filetags))) - (insert "Tags: ") - (insert - (string-join (seq-map (apply-partially 'format "[[tag:%s]]") - tags) - ", ")) - (insert "\n\n")))) + (insert (format "* [[post:%s]]\n" (file-name-base (ennu-post-filename post)))) + (insert (format-time-string + "/%b %e, %Y/\n\n" + (ennu-post-date post))) + (when-let ((thumbnail (ennu-post-thumbnail post))) + (insert (format "[[thumbnail:%s]]\n\n" thumbnail))) + (when-let ((summary (ennu-post-summary post))) + (insert summary) + (insert "\n\n")) + (when-let ((tags (ennu-post-tags post))) + (insert "Tags: ") + (insert + (string-join (seq-map (apply-partially 'format "[[tag:%s]]") + tags) + ", ")) + (insert "\n\n"))) posts) (unless (= page-number 1) (insert (format "[[./%s][Newer posts]]\n" @@ -142,7 +191,7 @@ last form in BODY." (format-time-string "%Y-%m-%dT%H:%M:%SZ" date)) (defun ennu-publish-feed (feed-file title rights posts) - `(,posts + `(,(seq-map 'ennu-post-filename posts) (,feed-file) ,(lambda (output-file) (with-temp-file output-file @@ -151,7 +200,7 @@ last form in BODY." `(feed :xmlns "http://www.w3.org/2005/Atom" (id ,(ennu--absolute-uri "")) (title ,title) - (updated ,(ennu--atom-date (plist-get (ennu-post-metadata (first posts)) :date))) + (updated ,(ennu--atom-date (ennu-post-date (first posts)))) (link :rel "self" :href ,(ennu--absolute-uri feed-file)) (generator ,(format "Emacs %d.%d Org-mode %s ennu %s" @@ -160,21 +209,20 @@ last form in BODY." ,@(seq-map 'ennu--feed-entry posts)))))))) (defun ennu--feed-entry (post) - (let* ((metadata (ennu-post-metadata post)) - (lang (plist-get metadata :lang)) - (link (ennu--absolute-uri (ennu--org-output-filename post)))) + (let ((link (ennu--absolute-uri (ennu--org-output-filename + (ennu-post-filename post))))) `(entry (id ,link) - (title :xml:lang ,lang ,(plist-get metadata :title)) - (updated ,(ennu--atom-date (plist-get metadata :date))) + (title :xml:lang ,(ennu-post-language post) ,(ennu-post-title post)) + (updated ,(ennu--atom-date (ennu-post-date post))) (author - (name ,(plist-get metadata :author)) + (name ,(ennu-post-author post)) (email ,user-mail-address)) - (content :type "html" :xml:lang ,lang - ,(ennu-with-file-contents post + (content :type "html" :xml:lang ,(ennu-post-language post) + ,(ennu-with-file-contents (ennu-post-filename post) (org-export-as 'ennu-html nil nil t))) (link :rel "alternate" :href ,link) ,@(seq-map (lambda (tag) `(category :term ,tag)) - (plist-get metadata :filetags))))) + (ennu-post-tags post))))) (defun ennu-setting (property) (pcase property @@ -233,50 +281,6 @@ last form in BODY." (defun ennu-publish-copy (file) `((,file) (,file) ,(apply-partially 'copy-file file))) -(defun ennu-post-links (post) - (ennu-with-file-contents post - (org-element-map (org-element-parse-buffer) 'link - (lambda (link) - (pcase link - (`(link ,properties . ,_) - (let ((link-type (org-element-property :type link))) - (when (member link-type (list "image" "static" "video")) - (cons link-type (org-element-property :path link)))))))))) - -(defun ennu-plist-map-to-plist (function plist) - "Apply FUNCTION to each key-value pair of PLIST and return the -result as a plist with keys being the keys in PLIST and the -values being the values returned by FUNCTION. FUNCTION is called -with two arguments -- the key and the value." - (seq-mapcat - (pcase-lambda (`(,key ,value)) - (list key (funcall function key value))) - (seq-partition plist 2))) - -(defun ennu-post-metadata (post) - (ennu--post-metadata-memoized - post (file-attribute-modification-time - (file-attributes post)))) - -(defmemoize ennu--post-metadata-memoized (post last-modified) - (ennu-with-file-contents post - (let ((metadata (org-export-get-environment 'ennu-html)) - (export (apply-partially 'org-export-with-backend 'ennu-html))) - (seq-do (lambda (key) - (unless (plist-member metadata key) - (user-error "Metadata %s not specified" key))) - ennu-mandatory-metadata) - (ennu-plist-map-to-plist - (lambda (key value) - (pcase key - ((or :author :summary :title) - (funcall export (first (plist-get metadata key)))) - (:date (org-timestamp-to-time (first (plist-get metadata :date)))))) - metadata)))) - -(defvar ennu-mandatory-metadata - (list :title :date)) - (defun newest-file (files) (pcase files (`(,head . ,tail) @@ -318,20 +322,9 @@ with two arguments -- the key and the value." (seq-map 'file-name-directory absolute-output-files))) (apply publish absolute-output-files)))))))) -(defun ennu--later-post (post1 post2) - (time-less-p (plist-get (ennu-post-metadata post2) :date) - (plist-get (ennu-post-metadata post1) :date))) - (defun ennu-publish-static-file (file) `((,file) (,file) ,(apply-partially 'copy-file file))) -(defun ennu-posts (posts-directory) - (sort (file-expand-wildcards - (concat (file-name-as-directory - (ennu-setting :posts-directory)) - "*.org")) - 'ennu--later-post)) - (defun ennu-publish-link (link) (pcase link (`("image" . ,path) @@ -346,12 +339,6 @@ with two arguments -- the key and the value." (ennu-publish-copy (ennu--expand-relative (ennu-video-poster path) (ennu-setting :images-directory)))))) -(defun ennu-post-tags (post) - (plist-get (ennu-post-metadata post) :filetags)) - -(defun ennu-post-tongue (post) - (plist-get (ennu-post-metadata post) :language)) - (defmacro ennu-with-current-directory (directory &rest body) "Change to DIRECTORY, evaluate BODY and restore the current working directory. The value returned is the value of the last @@ -412,7 +399,7 @@ as keys. Keys are compared using `equal'." (seq-map (pcase-lambda (`(,tongue . ,posts)) (ennu-publish-index "index" tongue blog-title posts-per-page posts)) - (seq-group-by 'ennu-post-tongue posts)) + (seq-group-by 'ennu-post-language posts)) (seq-mapcat (pcase-lambda (`(,tag . ,posts)) (seq-map @@ -420,7 +407,7 @@ as keys. Keys are compared using `equal'." (ennu-publish-index (ennu--expand-relative tag (ennu-setting :tag-directory)) tongue tag posts-per-page posts)) - (seq-group-by 'ennu-post-tongue posts))) + (seq-group-by 'ennu-post-language posts))) (ennu-many-to-many-group-by 'ennu-post-tags posts)) ;; Publish links (seq-map 'ennu-publish-link |