104 lines
3.7 KiB
EmacsLisp
104 lines
3.7 KiB
EmacsLisp
;;; build-comments.el --- Comment manifest and export filter -*- lexical-binding: t; -*-
|
|
|
|
;;; Code:
|
|
|
|
(require 'cl-lib)
|
|
(require 'json)
|
|
(require 'org)
|
|
|
|
(defun z/comments-file-p (file)
|
|
"Return non-nil if FILE has #+COMMENTS: t."
|
|
(when (and file (file-exists-p file))
|
|
(with-temp-buffer
|
|
(insert-file-contents file)
|
|
(org-mode)
|
|
(let* ((keywords (org-collect-keywords '("COMMENTS")))
|
|
(val (cadr (assoc "COMMENTS" keywords))))
|
|
(and val
|
|
(string-match-p "^\\s-*t\\s-*$" (downcase val)))))))
|
|
|
|
(defun z/comments-file-slug (file)
|
|
"Return page slug from #+SLUG: or fallback to filename base."
|
|
(with-temp-buffer
|
|
(insert-file-contents file)
|
|
(org-mode)
|
|
(let* ((keywords (org-collect-keywords '("SLUG")))
|
|
(slug (cadr (assoc "SLUG" keywords))))
|
|
(if (and slug (string-match-p "\\S-" slug))
|
|
slug
|
|
(file-name-base file)))))
|
|
|
|
(defun z/comment-page-url (file)
|
|
"Return root-relative published HTML URL for source FILE."
|
|
(concat "/"
|
|
(file-name-sans-extension
|
|
(file-relative-name file z/site-root))
|
|
".html"))
|
|
|
|
(defun z/comment-page-title (file)
|
|
"Return exported page title for FILE, or the file basename."
|
|
(with-temp-buffer
|
|
(insert-file-contents file nil 0 4096)
|
|
(goto-char (point-min))
|
|
(if (re-search-forward "^#\\+TITLE:[ \t]*\\(.+?\\)[ \t]*$" nil t)
|
|
(match-string 1)
|
|
(file-name-base file))))
|
|
|
|
(defun z/comment-page-record (file)
|
|
"Return a JSON-ready comment page record for FILE."
|
|
`((slug . ,(z/comments-file-slug file))
|
|
(title . ,(z/comment-page-title file))
|
|
(url . ,(z/comment-page-url file))))
|
|
|
|
(defun z/generate-comment-pages-json ()
|
|
"Write a slug-to-page manifest for comment-enabled Org files."
|
|
(let* ((files (directory-files-recursively z/site-root "\\.org\\'"))
|
|
(records
|
|
(cl-loop for file in files
|
|
unless (or (string-prefix-p (site-path "output/") file)
|
|
(string-prefix-p (site-path "backups/") file)
|
|
(string-prefix-p (site-path ".org-timestamps/") file)
|
|
(string-prefix-p (site-path ".packages/") file))
|
|
when (z/comments-file-p file)
|
|
collect (z/comment-page-record file)))
|
|
(target (site-path "assets/content/comment-pages.json"))
|
|
(json-encoding-pretty-print t))
|
|
(make-directory (file-name-directory target) t)
|
|
(with-temp-file target
|
|
(insert (json-encode (vconcat records)))
|
|
(insert "\n"))
|
|
(z/log 'done "Wrote comment page manifest (%d pages)" (length records))))
|
|
|
|
(defun z/org-html-insert-comments-into-body (body backend info)
|
|
"Append a comments section to BODY for files that opt in."
|
|
(if (org-export-derived-backend-p backend 'html)
|
|
(let ((input-file (plist-get info :input-file)))
|
|
(if (and input-file (z/comments-file-p input-file))
|
|
(let* ((slug (z/comments-file-slug input-file))
|
|
(html (format
|
|
"\n<section id=\"comments\" class=\"comments\" data-slug=\"%s\">
|
|
<h2>Comments</h2>
|
|
<div id=\"comments-list\" class=\"comments-list\">
|
|
<noscript>Please enable JavaScript to view comments.</noscript>
|
|
</div>
|
|
<form id=\"comment-form\" class=\"comment-form\">
|
|
<label class=\"comment-author\">
|
|
<span>Name (optional)</span>
|
|
<input type=\"text\" name=\"author\"/>
|
|
</label>
|
|
<label class=\"comment-content\">
|
|
<span>Your comment</span>
|
|
<textarea name=\"content\" required></textarea>
|
|
</label>
|
|
<button type=\"submit\">Post comment</button>
|
|
</form>
|
|
</section>\n"
|
|
slug)))
|
|
(concat body html))
|
|
body))
|
|
body))
|
|
|
|
(provide 'build-comments)
|
|
|
|
;;; build-comments.el ends here
|