Compare commits
No commits in common. "9bf6a6b78a370a54c1b1a460332cd5cf656e7c21" and "56090c4c65938178c797a7b13877c9810a07ca14" have entirely different histories.
9bf6a6b78a
...
56090c4c65
264
mfblog.el
264
mfblog.el
|
@ -1,264 +0,0 @@
|
||||||
;;; mfblog.el --- Microformats2 compatible notes site generator
|
|
||||||
|
|
||||||
;; Copyright (C) 2016 Free Software Foundation, Inc.
|
|
||||||
;; Author: Ryan Rix <ryan@whatthefuck.computer>
|
|
||||||
;; Version: 0.1
|
|
||||||
;; Package-Requires: ((json "0.1") (org "8.0"))
|
|
||||||
;; Keywords: web
|
|
||||||
;; URL: http://notes.whatthefuck.computer/
|
|
||||||
|
|
||||||
;;; Commentary:
|
|
||||||
|
|
||||||
;; This package generates a small blog from a single org-mode file, leveraging Microformats to
|
|
||||||
;; provide semantic information about the posts; you can use this information to automate sharing,
|
|
||||||
;; linkbacks, embedding images in 3rd party silso, and more; see http://indiewebcamp.com and
|
|
||||||
;; http://microformats.org for more information.
|
|
||||||
|
|
||||||
;; This package can be used by configuring the various variables and then calling [`mfblog:gen']
|
|
||||||
;; which will publish the blog index, an RSS feed and URLs for each entry. You can then have Brid.gy
|
|
||||||
;; automatically syndicate the content to silos by calling [`mfblog:syndicate-entry-at-point'].
|
|
||||||
|
|
||||||
;;; Code:
|
|
||||||
|
|
||||||
(require 'org-attach)
|
|
||||||
(require 'ox-html)
|
|
||||||
(require 'htmlize)
|
|
||||||
(require 'url-http)
|
|
||||||
(require 'url)
|
|
||||||
(require 'json)
|
|
||||||
|
|
||||||
(defgroup mfblog nil
|
|
||||||
"Microformats/IndieWeb blog.")
|
|
||||||
|
|
||||||
(defcustom mfblog:template-file "/home/rrix/Projects/notes/template.html"
|
|
||||||
"Template file for mfblog to use.
|
|
||||||
|
|
||||||
You should provide your own, but feel free to base it on the one in the package."
|
|
||||||
:group 'mfblog)
|
|
||||||
|
|
||||||
(defvar mfblog:postlist '())
|
|
||||||
|
|
||||||
(defcustom mfblog:preamble
|
|
||||||
"<p> This page is short notes, things I've read, and not full
|
|
||||||
length posts; long-form is posted on <a
|
|
||||||
href=\"http://whatthefuck.computer/\">my main site</a> and linked
|
|
||||||
to from here, as sort of a self-hosted broadcast-only
|
|
||||||
Twitter. This site uses semantic markup to automatically
|
|
||||||
syndicate to social networks (<a
|
|
||||||
href=\"http://indiewebcamp.com/POSSE\">POSSE</a>-style). Comments
|
|
||||||
and feedback are handled via <a
|
|
||||||
href=\"http://indiewebcamp.com/webmention\">webmention</a>.
|
|
||||||
Subscribe via <a
|
|
||||||
href=\"http://notes.whatthefuck.computer/index.xml\">RSS</a>.</p>"
|
|
||||||
"A preamble to post at the end of each page."
|
|
||||||
:group 'mfblog)
|
|
||||||
|
|
||||||
(defcustom mfblog:publish-config '(
|
|
||||||
:rss-image-url "http://notes.whatthefuck.computer/~rrix/25ZLKRlf.jpg"
|
|
||||||
:html-link-home "http://notes.whatthefuck.computer"
|
|
||||||
:html-link-use-abs-url t
|
|
||||||
:rss-extension "xml"
|
|
||||||
:select-tags ("EXPORT")
|
|
||||||
:publishing-directory "/ssh:li01.rix.si:/home/rrix/public_html/notes/"
|
|
||||||
:table-of-contents nil
|
|
||||||
:section-numbers nil)
|
|
||||||
"An `org-publish-project-alist' format plist containing shared state between all of the temporary publish projects."
|
|
||||||
:group 'mfblog)
|
|
||||||
|
|
||||||
(defun mfblog:gen ()
|
|
||||||
"Generate the mfblog and publish it."
|
|
||||||
(interactive)
|
|
||||||
(setq mfblog:postlist nil)
|
|
||||||
(message "Generating notes files")
|
|
||||||
(with-current-buffer (find-file-noselect "~/Projects/notes/index.org")
|
|
||||||
(goto-char (point-min))
|
|
||||||
(org-sort-entries nil ?T)
|
|
||||||
;; (org-map-entries 'mfblog:index-posts "EXPORT" 'file)
|
|
||||||
(org-map-entries 'mfblog:entry-to-page "EXPORT" 'file))
|
|
||||||
(let ((org-publish-project-alist
|
|
||||||
(list (append '("notes_rss")
|
|
||||||
(mfblog:plist-merge '(:base-directory "~/Projects/notes" :base-extension "org" :publishing-function (org-rss-publish-to-rss) :exclude ".*" :include ("index.org"))
|
|
||||||
mfblog:publish-config))
|
|
||||||
(append '("notes_other")
|
|
||||||
(mfblog:plist-merge '(:base-directory "/var/tmp/mfblog" :base-extension "JPG\\|js\\|html\\|jpg\\|gif\\|png" :publishing-function (org-publish-attachment))
|
|
||||||
mfblog:publish-config)))))
|
|
||||||
(copy-file "~/Projects/notes/webmention.js" "/var/tmp/mfblog/webmention.js" t)
|
|
||||||
(copy-file "~/Projects/notes/go.png" "/var/tmp/mfblog/go.png" t)
|
|
||||||
(copy-file "~/Projects/notes/favicon.gif" "/var/tmp/mfblog/favicon.gif" t)
|
|
||||||
(org-publish "notes_rss")
|
|
||||||
(mfblog:make-index)
|
|
||||||
(org-publish "notes_other")))
|
|
||||||
|
|
||||||
(defun mfblog:syndicate-entry-at-point ()
|
|
||||||
"Syndicate the org-mode entry at point, by calling out to Bridgy and storing the result."
|
|
||||||
(interactive)
|
|
||||||
(let* ((date (org-entry-get (point) "CLOSED"))
|
|
||||||
(date (org-time-string-to-seconds date))
|
|
||||||
(base-url (plist-get mfblog:publish-config :html-link-home))
|
|
||||||
(url (format "%s/%s-note.html" base-url date))
|
|
||||||
(syn-twitter (org-entry-get (point) "SYN-TWITTER"))
|
|
||||||
(syn-facebook (org-entry-get (point) "SYN-FACEBOOK"))
|
|
||||||
(realpoint (point)))
|
|
||||||
(unless syn-twitter
|
|
||||||
(let* ((url-request-data (format "source=%s&target=http://brid.gy/publish/twitter" url))
|
|
||||||
(url-request-method "POST")
|
|
||||||
(buf (url-retrieve-synchronously "https://brid.gy/publish/webmention")))
|
|
||||||
(org-entry-put realpoint "SYN-TWITTER"
|
|
||||||
(with-current-buffer buf
|
|
||||||
(goto-char url-http-end-of-headers)
|
|
||||||
(let* ((body (json-read))
|
|
||||||
(url (assoc 'url body)))
|
|
||||||
(when url
|
|
||||||
(cdr url)))))))
|
|
||||||
(unless syn-facebook
|
|
||||||
(let* ((url-request-data (format "source=%s&target=http://brid.gy/publish/facebook" url))
|
|
||||||
(url-request-method "POST")
|
|
||||||
(buf (url-retrieve-synchronously "https://brid.gy/publish/webmention")))
|
|
||||||
(org-entry-put realpoint "SYN-FACEBOOK"
|
|
||||||
(with-current-buffer buf
|
|
||||||
(goto-char url-http-end-of-headers)
|
|
||||||
(let* ((body (json-read))
|
|
||||||
(url (assoc 'url body)))
|
|
||||||
(when url
|
|
||||||
(cdr url)))))))))
|
|
||||||
|
|
||||||
(defun mfblog:heading-to-html (heading)
|
|
||||||
"HTMLize the given HEADING, cleaning up crap and generating in-reply-to URLs."
|
|
||||||
(with-temp-buffer
|
|
||||||
(insert heading)
|
|
||||||
(let* ((str (htmlize-region-for-paste (point-min) (point-max))))
|
|
||||||
(with-temp-buffer
|
|
||||||
(insert str)
|
|
||||||
(goto-char (point-min))
|
|
||||||
(replace-regexp "<pre>" "")
|
|
||||||
(goto-char (point-min))
|
|
||||||
(replace-regexp "</pre>" "")
|
|
||||||
(goto-char (point-min))
|
|
||||||
(replace-regexp "<a href=\"https://twitter" "<a class=\"u-in-reply-to\" href=\"https://twitter")
|
|
||||||
(buffer-string)))))
|
|
||||||
|
|
||||||
(defun mfblog:to-html ()
|
|
||||||
"Convert the heading at point to HTML, sprinkling in ID where necessary."
|
|
||||||
(let* ((id (org-id-get-create))
|
|
||||||
(location (first (org-property-values "LOCATION")))
|
|
||||||
(attach-dir (org-attach-dir))
|
|
||||||
(file-list (if attach-dir
|
|
||||||
(org-attach-file-list attach-dir)
|
|
||||||
'())))
|
|
||||||
(with-current-buffer (org-html-export-as-html nil t nil t)
|
|
||||||
(dolist (file file-list)
|
|
||||||
(let ((file- (concat attach-dir "/" file))
|
|
||||||
(newfile (concat "/var/tmp/mfblog/" id "-" file))
|
|
||||||
)
|
|
||||||
(unless (file-exists-p newfile)
|
|
||||||
(copy-file file- newfile)
|
|
||||||
(set-file-modes newfile 420))
|
|
||||||
(insert "<img style=\"width: 100%\" src=\"" id "-" file "\" class=\"u-photo\"/><br/>")))
|
|
||||||
(when location
|
|
||||||
(insert "<span class=\"p-location\">" location "</span>"))
|
|
||||||
(buffer-string))))
|
|
||||||
|
|
||||||
(defun mfblog:entry-to-page ()
|
|
||||||
"Convert the heading at POINT to an HTML page"
|
|
||||||
(save-window-excursion
|
|
||||||
(unless (file-exists-p "/var/tmp/mfblog")
|
|
||||||
(make-directory "/var/tmp/mfblog"))
|
|
||||||
(let* ((pubdate (org-entry-get (point) "CLOSED"))
|
|
||||||
(pubdate (org-time-string-to-seconds pubdate))
|
|
||||||
(id (org-id-get-create))
|
|
||||||
(filename (format "%s-note.html" pubdate))
|
|
||||||
(fullpath (format "/var/tmp/mfblog/%s" filename))
|
|
||||||
(title (mfblog:heading-to-html (org-get-heading)))
|
|
||||||
(content (mfblog:to-html))
|
|
||||||
(base-url (plist-get mfblog:publish-config :html-link-home))
|
|
||||||
(full-url (concat base-url "/" filename))
|
|
||||||
(syn-twitter-url (or (org-entry-get (point) "SYN-TWITTER") ""))
|
|
||||||
(syn-facebook-url (or (org-entry-get (point) "SYN-FACEBOOK") ""))
|
|
||||||
(syn-twitter-href (if (> (length syn-twitter-url) 0)
|
|
||||||
(format "<a rel=\"syndication\" href=\"%s\">On Twitter</a>" syn-twitter-url)
|
|
||||||
""))
|
|
||||||
(syn-facebook-href (if (> (length syn-facebook-url) 0)
|
|
||||||
(format "<a rel=\"syndication\" href=\"%s\">On Facebook</a>" syn-facebook-url)
|
|
||||||
"")))
|
|
||||||
(org-entry-put (point) "RSS_PERMALINK" filename)
|
|
||||||
(with-current-buffer (find-file-noselect fullpath)
|
|
||||||
(erase-buffer)
|
|
||||||
(insert-file mfblog:template-file)
|
|
||||||
|
|
||||||
(goto-char (point-min))
|
|
||||||
(replace-regexp "{{content}}" content)
|
|
||||||
(goto-char (point-min))
|
|
||||||
(replace-regexp "{{title}}" title)
|
|
||||||
(goto-char (point-min))
|
|
||||||
(replace-regexp "{{url}}" full-url)
|
|
||||||
(goto-char (point-min))
|
|
||||||
(replace-regexp "<a href=\"{{twitter}}\">On Twitter</a>" syn-twitter-href)
|
|
||||||
(goto-char (point-min))
|
|
||||||
(replace-regexp "<a href=\"{{facebook}}\">On Facebook</a>" syn-facebook-href)
|
|
||||||
|
|
||||||
(write-file fullpath)
|
|
||||||
(kill-buffer (current-buffer))
|
|
||||||
(add-to-ordered-list 'mfblog:postlist
|
|
||||||
(list pubdate fullpath title content filename)
|
|
||||||
pubdate)))))
|
|
||||||
|
|
||||||
(defun mfblog:index-posts ()
|
|
||||||
"Create an index of all the posts to speed up generation of index.html"
|
|
||||||
(save-window-excursion
|
|
||||||
(let* ((pubdate (org-entry-get (point) "CLOSED"))
|
|
||||||
(pubdate (org-time-string-to-seconds pubdate))
|
|
||||||
(id (org-id-get-create))
|
|
||||||
(filename (format "%s-note.html" pubdate))
|
|
||||||
(fullpath (format "/var/tmp/mfblog/%s" filename)))
|
|
||||||
(unless (alist-get pubdate 'mfblog:postlist)
|
|
||||||
(let* ((title-html (mfblog:heading-to-html (org-get-heading)))
|
|
||||||
(title-txt (mfblog:heading-to-txt (org-get-heading)))
|
|
||||||
(base-url (plist-get mfblog:publish-config :html-link-home))
|
|
||||||
(full-url (concat base-url "/" filename)))
|
|
||||||
(add-to-ordered-list 'mfblog:postlist
|
|
||||||
(list fullpath title-html title-text filename full-url)
|
|
||||||
pubdate))))))
|
|
||||||
|
|
||||||
(defun mfblog:plist-merge (&rest plists)
|
|
||||||
"Merge a bunch of PLISTS together."
|
|
||||||
(if plists
|
|
||||||
(let ((result (copy-sequence (car plists))))
|
|
||||||
(while (setq plists (cdr plists))
|
|
||||||
(let ((plist (car plists)))
|
|
||||||
(while plist
|
|
||||||
(setq result (plist-put result (car plist) (car (cdr plist)))
|
|
||||||
plist (cdr (cdr plist))))))
|
|
||||||
result)
|
|
||||||
nil))
|
|
||||||
|
|
||||||
(defun mfblog:make-index ()
|
|
||||||
"Generate the mfblog index page."
|
|
||||||
(let* ((index-path "/var/tmp/mfblog/index.html")
|
|
||||||
(all-content
|
|
||||||
(mapconcat
|
|
||||||
(lambda (post)
|
|
||||||
(let ((pubdate (pop post))
|
|
||||||
(fullpath (pop post))
|
|
||||||
(title (pop post))
|
|
||||||
(content (pop post))
|
|
||||||
(filename (pop post)))
|
|
||||||
(format "<li style=\"text-size: 0.8em;\">%s (<a href=\"/%s\">Permalink</a>)</li>" title filename)))
|
|
||||||
(reverse mfblog:postlist) "\n"))
|
|
||||||
(all-content (format "%s<ul>%s</ul>" mfblog:preamble all-content)))
|
|
||||||
(with-current-buffer (find-file-noselect "/var/tmp/mfblog/index.html")
|
|
||||||
(erase-buffer)
|
|
||||||
(insert-file mfblog:template-file)
|
|
||||||
(goto-char (point-min))
|
|
||||||
(replace-regexp "{{content}}" all-content)
|
|
||||||
(goto-char (point-min))
|
|
||||||
(replace-regexp "{{url}}" (concat (plist-get mfblog:publish-config :html-link-home) "/"))
|
|
||||||
(goto-char (point-min))
|
|
||||||
(replace-regexp "{{twitter}}" "https://twitter.com/rrrrrrrix")
|
|
||||||
(goto-char (point-min))
|
|
||||||
(replace-regexp "{{facebook}}" "https://facebook.com/rjrix")
|
|
||||||
(goto-char (point-min))
|
|
||||||
(replace-regexp "{{title}}" "Ryan's Shortnotes")
|
|
||||||
(write-file "/var/tmp/mfblog/index.html"))))
|
|
||||||
|
|
||||||
(provide 'mfblog)
|
|
||||||
;;; mfblog.el ends here
|
|
Loading…
Reference in New Issue