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