Category Archives: emacs

Org-Mode: Run Code Live in a Reveal slideshow with Klipse

Slide-based presentations are pretty useful and in any case de rigeur for contemporary lecturing, but it can be a REAL pain to switch form a slideshow to a code editor and then back again to a web browser to show the results of the code… Much better would be to have all the code embedded directly in your presentation. This also makes it easier to check over your code when you’re rewriting your lectures from year to year, and also helps with literate programming.

It turns out that there are actually several solutions for doing this with Org. They are:

  • directly embed JSBin in your slideshow
  • Use RevealEditor for HTML, CSS, and JS that you want to render in a web page.
  • Use the new klipse plugin for code that you want to evaluate (rather than render)

Each method has advantages and disadvantages, and each requires a certain amount of setup in your config. In this post I’m talking about using Klipse because I’ve bene able to make it actually work!

The Basics: ox-reveal

To start with, you will need to install org-reveal as well as its dependencies (org-mode and reveal.js). I install org-reveal from github instead of from ELPA, in part because I need to make modifications to both it and reveal.js.

Follow the detailed instructions until you have a working export to reveal. Make sure that you have activated the “highlight” plugin in org-reveal-plugins, which you can do with M-x customize-variable org-reveal-plugins. None of the tricks described below will work without that plugin!

Running Code in Klipse

Klipse is a web repl that supports a handful of languages, and can be extended to support more. Code is embedded in a CodeMirror instance and evaluated as you type (pretty cool).

Unfortunately CodeMirror and Reveal don’t get along very well so it’s not completely straightforward to embed Klipse directly into a reveal.js slideshow. The workaround is to replace a code-snippet tag with an iframe element containing the code in a form that klipse can read. Klipse’s author @viebel has kindly provided an example of how to do this here.

The next trick is to set up org-reveal to use this snippet. After messing around for a bit it became obvious that org-mode’s standard Export filters were going to be pretty hard to use, so I decided to rewrite some of org-reveal’s functionality:

Add Support for Third-Party Plugins

Org-reveal has excellent support for reveal.js’s “build-in” plugins, but using third-party plugins requires you to use the or-reveal~postamble variable~, which didn’t seem right to me. So I added a defcustom and a few small generic tricks to support loading third-party plugins. Then I downloaded @viebel’s klipse_reveal.js script and saved it to the plugin folder of my reveal.js installation. Finally, I set org-reveal-external-plugins to ((klipse . "{src: '%splugin/klipse_reveal.js'}")) using the customize-variable interface.

Rewrite org-reveal-src-block

At this point, the plugin loads, but it couldn’t find the exported src blocks. As I mentioned above, I was unable to find a satisfactory solution using export filters, so in the end I just rewrote the function org-reveal-src-block, which ox-reveal uses to process src blocks. I decided to keep the original <pre><code> syntax and simply add on an additional <klipse-snippet block, using the display:none CSS property for the old code block. This ensures compatibility with the reveal-editor solution (which isn’t currently working for me, but offers some other features.

Here’s my modified function:

(defun org-reveal-src-block (src-block contents info)
  "Transcode a SRC-BLOCK element from Org to Reveal.
CONTENTS holds the contents of the item.  INFO is a plist holding
contextual information."
  (if (org-export-read-attribute :attr_html src-block :textarea)
      (org-html--textarea-block src-block)
    (let* ((use-highlight (org-reveal--using-highlight.js info))
           (lang (org-element-property :language src-block))
           (caption (org-export-get-caption src-block))
           (code (if (not use-highlight)
                     (org-html-format-code src-block info)
                   (cl-letf (((symbol-function 'org-html-htmlize-region-for-paste)
                              #'buffer-substring))
                     (org-html-format-code src-block info))))
           (frag (org-export-read-attribute :attr_reveal src-block :frag))
           (code-attribs (or (org-export-read-attribute
                         :attr_reveal src-block :code_attribs) ""))
           (label (let ((lbl (org-element-property :name src-block)))
                    (if (not lbl) ""
                      (format " id=\"%s\"" lbl))))
           (klipsify  (and (assoc 'klipse org-reveal-external-plugins)
                           (member lang '("javascript" "ruby" "scheme" "clojure" "php"))))
                      )
      (if (not lang)
          (format "<pre %s%s>\n%s</pre>"
                  (or (frag-class frag info) " class=\"example\"")
                  label
                  code)
        (format
         "<div %sclass=\"org-src-container\">\n%s%s\n</div>%s"
         (if klipsify "style=\"display:none;\" " "")
         (if (not caption) ""
           (format "<label class=\"org-src-name\">%s</label>"
                   (org-export-data caption info)))
         (if use-highlight
             (format "\n<pre%s%s><code class=\"%s\" %s>%s</code></pre>"
                     (or (frag-class frag info) "")
                     label lang code-attribs code)
           (format "\n<pre %s%s>%s</pre>"
                   (or (frag-class frag info)
                       (format " class=\"src src-%s\"" lang))
                   label code)
           )
         (if klipsify (format "<klipse-snippet data-language=\"%s\">%s</klipse-snippet>"
                              lang code) ""))))))

As you can see, it now decides whether or not to “klipsify” the code aafter checking if the klipse plugin is loaded, and if it supports the language of the src block. Klipsifying code just means hiding the original code block and instead showing the klipse-snippet, which will be replaced by an iframe in javascript. I guess I could just do that work right in this same elisp function! Oh well, maybe next time.

So far, this seems to work great, though I don’t have access to the DOM of the larger document ,which is sort of a drag.

[UPDATE 2016-12-11 Sun]: However, @viebel pointed out to me that you can access the parent frame with:

var d = parent.document

Pretty cool! Note: klipse will hang if you try to type this in directly. It will only work if you pass the completed line in as part of the initial code block.

Also, this is not a solution that works for rendering HTML pages. For this we need something else. I would like to use RevealEditor, but am not quite there yet (can’t get its code to load properly). So for now I’m embedding a JSBin instance with preoaded code – I just add an iframe tag directly in my org-mode file. It’s not a s pretty or robust, and I can’t see my code when I’m writing, which is too bad, but it’s still pretty useful.

I would really like to hear what other people think! I’d especially like to hear about alternatives or improvements.

Using mu4e and org-mime together

I use org-mime in my grading system, to email my comments on student papers. One frustrating element has always been that messages sent by org-mime were never saved to my sent-mail folder. I realized just recently that this was because I had failed to set emacs’s mail-user-agent, which I now do in my initial mu4e setup:

(setq mail-user-agent 'mu4e-user-agent)

Now org-mime attempts to send messages using mu4e’s internal compose functions. Unfortunately some of the information passed by org-mime is in a format that mu4e~compose-mail doesn’t like, so I had to make some very slight changes to that function:

diff --git a/mu4e/mu4e-compose.el b/mu4e/mu4e-compose.el
index a24e74a..0c7ec3c 100644
--- a/mu4e/mu4e-compose.el
+++ b/mu4e/mu4e-compose.el
@@ -780,7 +780,14 @@ draft message."

   ;; add any other headers specified
   (when other-headers
-    (message-add-header other-headers))
+    (dolist (h other-headers other-headers)
+      (if (symbolp (car h)) (setcar h (symbol-name (car h))))
+      (message-add-header (concat (capitalize (car h)) ": " (cdr h) "\n"  ))
+      )
+    ;; (dolist (h other-headers)
+    ;;  (message-add-header h) )
+    ;;(message-add-header other-headers)
+    )

   ;; yank message
   (if (bufferp yank-action)

The commit is in its my org-mu4e-compose branch, contianing a couple of other fixes, and in its own branch on github, if prefer to pull from there. A patch has been submitted, we’ll see what dcjb thinks of it.

With these changes, org-mime now works perfectly for me.

Sending HTML Mail with mu4e

John Kitchin has a terrific post detailing some configuration/improvements to mu4e that make it easier to send html mail. This original post is really great, but I ran into quite a bit of trouble following this advice.

He uses a cool feature of mu4e, org-mu4e-compose-org-mode, which toggles the major mode of the message buffer between org-mode (when you’re in the message body) and mu4e-compose-mode (when you’re in the headers area). With a couple of custom functions, it’s easy to convert the org text to html and send a mime-multipart email from Emacs, which is quite convenient. If you add org-mu4e-compose-org-mode as a hook to mu4e-compose-mode, you can compose in html by default, which is great.

Unfortunately, org-mu4e-compose-org-mode is deprecated on account of its instability, and while John doesn’t have any problems with it, for me it was unworkable. It turns out that this “mode” isn’t really a standard emacs mode at all – instead, it’s a sly workaround that trickily adds an internal function to the ’post-command-hook in the draft buffer and switches major modes based on position. This is a neat hack, but since the function invokes the major modes directly, setting thefunction as a hook to mu4e-compose-mode leads to some funky, inadvertent looping effects. On my machine, for some reason, those effects send the underlying message-send function crazy, and instead of sending directly, I get this amazingly annoying question:

Already sent message via mail; resend? (y or n) y

On its own, that is already annoying; but worse, the sent message doesn’t get saved to my Sent folder. Instead, it’s lost completely.

Anyway, after fruitless hours of paying around with this, I realized that the problem could be fixed by adding a new hook to the mu4e compose functions (rather than to the compose mode). I’ve submitted those changes as a pull request and hopefully they will be accepted; if not, though, feel free to navigate back to my branch and pull/install mu from there. With those small changes, I now have frictionless html email working very quickly within emacs, using this small bit of code. It is at least 90% stolen:

;; this is stolen from John but it didn't work for me until I
;; made those changes to mu4e-compose.el
(defun htmlize-and-send ()
  "When in an org-mu4e-compose-org-mode message, htmlize and send it."
  (interactive)
  (when (member 'org~mu4e-mime-switch-headers-or-body post-command-hook)
    (org-mime-htmlize)
    (org-mu4e-compose-org-mode)
    (mu4e-compose-mode)
    (message-send-and-exit)))


;; This overloads the amazing C-c C-c commands in org-mode with one more function
;; namely the htmlize-and-send, above.
(add-hook 'org-ctrl-c-ctrl-c-hook 'htmlize-and-send t)

;; Originally, I set the `mu4e-compose-mode-hook' here, but
;; this new hook works much, much better for me.  
(add-hook 'mu4e-compose-post-hook
          (defun do-compose-stuff ()
            "My settings for message composition."
            (org-mu4e-compose-org-mode)))

It feels great to have gotten this far. There are still some small things I’d like to be able to improve; and I think I would like to add a few wrapper functions and keybindings to my setup, but for now I’m pretty efficient.

Still missing:

  • a better HTML viewing interface! right now, html mails render as mostly text – it would be nice to have a rendered html message by default in emacs. This is an issue several times a day when I get promotional emails from organizations I work with – usually the html part is really important. I can access these in the browser but it’s comparatively awkward.
  • a way to forward these html emails to someone intact – right now, the html parts are discarded. No idea how hard it would be to do this.

Thanks John and Dirk-Jan for these great tools!

Elfeed

I’ve become pretty addicted to the Elfeed RSS reader. However, I haven’t made much use of its tagging capabilities until recently. I noticed that, whenever I thought I might want to keep track of the content of a blog, I would mark it “unread” after I’d read it, to make sure I didn’t lose it in the deep mists of time. This worked for the first 4 months or so, but now my list of unreads is growing enormously. So I wanted to have a “starred” tag which would do this work for me. I also wanted a keybinding that would apply the tag right away.

The code below does that for me.

(eval-after-load 'elfeed-search 
  '(define-key elfeed-search-mode-map (kbd "<tab>") 'mwp/elfeed-star))

(defun mwp/elfeed-star ()
  "add a star tag to marked"

  (interactive)
  (elfeed-search-tag-all (list starred))
  )

(defun mwp/elfeed-star ()
  "Apply TAG to all selected entries."
  (interactive )
  (let* ((entries (elfeed-search-selected))
         (tag (intern "starred")))

    (cl-loop for entry in entries do (elfeed-tag entry tag))
    (mapc #'elfeed-search-update-entry entries)
    (unless (use-region-p) (forward-line))))

I also wanted a visual cue to tell me the tagging had been successful – now easy to do, thanks to a very recent commit. I filed an issue as a question – can I do this? – and within 24 hours skeeto had added the functionality. First you have to define an appropriate face for the tag:

(defface elfeed-search-starred-title-face
 '((t :foreground "#f77"))
 "Marks a starred Elfeed entry.")

And then simply add an entry to elfeed-search-face-alist. You can do this via Customize, or follow the instructions in the README, which instruct you to (push '(starred elfeed-search-starred-title-face) elfeed-search-face-alist).

All of this works great for me, and simplifies things quite a bit.

Note Taking with PDF Tools

NOTE: This post has been modified as of 2015-11-22 Sun – the new code is a little cleaner, and I think the discussion a little fuller.

Almost all of my job-related reading is now done on a screen. There are still disadvantages – I find it much harder to concentrate when reading online – but in other ways it is markedly more convenient.

In particular, it is now much easier to assemble quotations from sources; and now that I’ve found PDF Tools, it has become even easier. I’ve just started to use it to extract annotations from my PDF’s, and it works much better than the lousy command-line hack I was using previously.

As we’re mid-semester, most of my reading is for classes I teach. My current workflow is as follows:

  • Assemble the relevant readings in a Dropbox-synced directory (ClassName/Readings)
  • Using Repligo Reader (apparently no longer available in the app store?), highlight the passages I’m interested in.
  • execute code block (see below) to insert org headings with all highlights from one or more pdfs
  • Assemble reveal.js lecture presentation around those highlights, using org-reveal or Pandoc.

Activating PDF Tools

Begin by installing pdf-tools and org-pdfview from ELPA with package-list~packages or package-install.

Then make sure they are activated by adding these lines in your init file:

(pdf-tools-install)
(eval-after-load 'org '(require 'org-pdfview))
(add-to-list 'org-file-apps '("\\.pdf\\'" . org-pdfview-open))
(add-to-list 'org-file-apps '("\\.pdf::\\([[:digit:]]+\\)\\'" . org-pdfview-open))

Switching to PDF Tools for annotating and extracting PDF’s

Last month Penguim proposed some changes in a pull request, that export annotations as a set of org headlines. It’s potentially very interesting but not quite what I want to do, so I modified this code. pdf-annot-markups-as-org-text extracts the text of an annotation (stored as the subject attribute in an alist), and also generates a link back to the page in the pdf. mwp/pdf-multi-extract is just a helper function that makes it easier to construct elisp source blocks the way I’m used to doing:

;; modified from https://github.com/politza/pdf-tools/pull/133 

(defun mwp/pdf-multi-extract (sources)
  "Helper function to print highlighted text from a list of pdf's, with one org header per pdf, 
and links back to page of highlight."
  (let (
        (output ""))
    (dolist (thispdf sources)
      (setq output (concat output (pdf-annot-markups-as-org-text thispdf nil level ))))
    (princ output))
  )

;; this is stolen from https://github.com/pinguim06/pdf-tools/commit/22629c746878f4e554d4e530306f3433d594a654
(defun pdf-annot-edges-to-region (edges)
  "Attempt to get 4-entry region \(LEFT TOP RIGHT BOTTOM\) from several edges.
We need this to import annotations and to get marked-up text, because annotations
are referenced by its edges, but functions for these tasks need region."

  (let ((left0 (nth 0 (car edges)))
        (top0 (nth 1 (car edges)))
        (bottom0 (nth 3 (car edges)))
        (top1 (nth 1 (car (last edges))))
        (right1 (nth 2 (car (last edges))))
        (bottom1 (nth 3 (car (last edges))))
        (n (safe-length edges)))
    ;; we try to guess the line height to move
    ;; the region away from the boundary and
    ;; avoid double lines
    (list left0
          (+ top0 (/ (- bottom0 top0) 2))
          right1
          (- bottom1 (/ (- bottom1 top1) 2 )))))

(defun pdf-annot-markups-as-org-text (pdfpath &optional title level)
  "Acquire highligh annotations as text, and return as org-heading"

  (interactive "fPath to PDF: ")  
  (let* ((outputstring "") ;; the text to be returned
         (title (or title (replace-regexp-in-string "-" " " (file-name-base pdfpath ))))
         (level (or level (1+ (org-current-level)))) ;; I guess if we're not in an org-buffer this will fail
         (levelstring (make-string level ?*)) ;; set headline to proper level
         (annots (sort (pdf-info-getannots nil pdfpath)  ;; get and sort all annots
                       'pdf-annot-compare-annotations))
         )
    ;; create the header
    (setq outputstring (concat levelstring " Quotes From " title "\n\n")) ;; create heading

    ;; extract text
    (mapc
     (lambda (annot) ;; traverse all annotations
       (if (eq 'highlight (assoc-default 'type annot))
           (let* ((page (assoc-default 'page annot))
                  ;; use pdf-annot-edges-to-region to get correct boundaries of highlight
                  (real-edges (pdf-annot-edges-to-region
                               (pdf-annot-get annot 'markup-edges)))
                  (text (or (assoc-default 'subject annot) (assoc-default 'content annot)
                            (replace-regexp-in-string "\n" " " (pdf-info-gettext page real-edges nil pdfpath)
                                                      ) ))

                  (height (nth 1 real-edges)) ;; distance down the page
                  ;; use pdfview link directly to page number
                  (linktext (concat "[[pdfview:" pdfpath "::" (number-to-string page) 
                                    "++" (number-to-string height) "][" title  "]]" ))
                  )
             (setq outputstring (concat outputstring text " ("
                                        linktext ", " (number-to-string page) ")\n\n"))
             )))
     annots)
    outputstring ;; return the header
    )
  )

Using in Org with a Source Block

Now it’s more or less trivial to quickly generate the org headers using a source block:

#+BEGIN_SRC elisp :results output raw :var level=(1+ (org-current-level))
(mwp/pdf-multi-extract '(
                   "/home/matt/HackingHistory/readings/Troper-becoming-immigrant-city.pdf"  "/home/matt/HackingHistory/readings/historical-authority-hampton.pdf"))

#+END_SRC

And the output gives something like

#+BEGIN_EXAMPLE

Quotes From Troper becoming immigrant city

Included in the Greater Toronto Area multiethnic mix are an estimated 450,000 Chinese, 400,000 Italians, and 250,000 African Canadians, the largest component of which are ofCar- ibbean background, although a separate and distinct infusion of Soma- lis, Ethiopians, and other Africans is currently taking place. (Troper becoming immigrant city, 3)

Although Toronto is Canada’s leading immigrant-receiving centre, city officials have neither a hands-on role in immigrant selection nor an official voice in deciding immigration policy. In Canada, immigration policy and administration is a constitutional responsibility of the fed- eral government, worked out in consultation with the provinces. (Troper becoming immigrant city, 4)

#+END_EXAMPLE

Alternative: Temporary buffer with custom link type

An alternative workflow would be to pop to a second, temporary buffer and insert the annotations there; one could do this with a custom link type. PDF-Tools already has a mechanism for listing annotations in a separate buffer, but it’s not designed for quick access to all annotations at once. Anyway, here’s one way to do this; I’m not really using it at the moment.

(org-add-link-type "pdfquote" 'org-pdfquote-open 'org-pdfquote-export)

(defun org-pdfquote-open (link)
  "Open a new buffer with all markup annotations in an org headline."
  (interactive)
  (pop-to-buffer
   (format "*Quotes from %s*"
           (file-name-base link)))
  (org-mode)
  (erase-buffer)
  (insert (pdf-annot-markups-as-org-text link nil 1))
  (goto-char 0)
  )

(defun org-pdfquote-export (link description format)
  "Export the pdfview LINK with DESCRIPTION for FORMAT from Org files."
  (let* ((path (when (string-match "\\(.+\\)::.+" link)
                 (match-string 1 link)))
         (desc (or description link)))
    (when (stringp path)
      (setq path (org-link-escape (expand-file-name path)))
      (cond
       ((eq format 'html) (format "<a href=\"%s\">%s</a>" path desc))
       ((eq format 'latex) (format "\href{%s}{%s}" path desc))
       ((eq format 'ascii) (format "%s (%s)" desc path))
       (t path)))))

(defun org-pdfquote-complete-link ()
  "Use the existing file name completion for file.
Links to get the file name, then ask the user for the page number
and append it."                                  

  (replace-regexp-in-string "^file:" "pdfquote:" (org-file-complete-link)))

I’ve also added two bindings to make highlighting easier from the PDF buffer:

(eval-after-load 'pdf-view 
                    '(define-key pdf-view-mode-map (kbd "M-h") 'pdf-annot-add-highlight-markup-annotation))
(eval-after-load 'pdf-view 
                    '(define-key pdf-view-mode-map (kbd "<tab>") 'pdf-annot-add-highlight-markup-annotation))

All of this is getting me very close to using Emacs for all my PDF work. I am doing maybe 50% of my PDF work in Emacs instead of on my tablet. It’s incredibly convenient, although I still find it a little harder to concentrate on my laptop than on the tablet (for reasons ergonomic, optical, and psychological). Here are the remaining papercuts from my perspective:

  • Highlighting text with the mouse is more awkward and less intimate than using my fingertip on a laptop. I often find mouse movement a little awkward in Emacs, but pdf-view purposely relies on the mouse for movement (for good reasons).
  • Scrolling in pdf-view is also a bit awkward, and there’s no “continuous” mode as one might find in Evince or acroread. Again, I often find scrolling an issue in Emacs, so this might not be so easy to fix.
  • Finally, the laptop screen is just harder on my eyes than my high-res tablet. pdf-view hasa “midnight mode” which makes it a little easier to read, but it’s not quite enough.

So, for the time being I will probably do much of my reading on the tablet. But for short pieces and for review (e.g., papers that I’m reading for the third year in a row in a graduate seminar…) PDF Tools is now my main interface. Which is pretty sweet.

Todo

UPDATE: I would like to extend the pdfview link type (in org-pdfview) to permit me to specify the precise location of an annotation, so I can jump precisely to that part of the page. This has now been done and the code above has been updated to reflect the new syntax.

UPDATE: Also, now that I think about it, it might be interesting to just have a link type that pops up a temporary buffer with all of the annotations; I could then cut and paste the annotations into the master document. This might be even more convenient. OK, I’ve implemented this, see above!

Update 2015-11-22 Sun: I’ve cleaned up some of the code, and added a bit more commentary at the end.

Mailing subtrees with Attachments

This is pretty exciting; I’ve figured out how to quickly send org-mode subtrees as MIME-encoded emails. That means that, essentially, I can write in org, as plain text, and very quickly export to HTML, add attachments, and send. The exciting part about this for me is that it should streamline my communications with students, while also letting me stay in Org and keep my records in order. Let’s walk through the process.

Use-case

For the moment, I still use Thunderbird as my primary MUA. It’s pretty easy to use, minimal configuration compared to all things Emacs, and if something goes wrong with it I don’t have to quit Emacs (!), just Thunderbird.

In some cases, though, Thunderbird makes for an awkward workflow. That’s certainly the case for grading, whih has many, poorly-integrated elements. To mark an assignment I need to:

  • log in to Blackboard (in Firefox; I wonder if I could do that in Emacs?)
  • download the set of student papers (one at a time, 2 clicks per paper) to Downlads
  • move papers to a directory (usually ~/COURSENAME/Grading/ASSIGNMENTNAME )
  • Read papers in LigreOffice, comment inline
  • record mark in Libreoffice Calc spreadsheet
  • email paper back to student with comments
  • upload marks back into Blackboard
  • find a place to archive the student paper in case I need it later, e.g. for a contested grade.

The while process basically sucks. I spend maybe 20% of my time fussing with paths and mouse clicks and email addresses. So I am experimenting with moving as much of this process into Emacs. So far, I don’t think there’s any way at all to bulk-download the papers – that sucks, but I can live with it I guess (I have to!). So I start the optimization at the point where I have all my papers ready to go in a subdir.

Org-mime

Org-mime is the library that allows org buffers and other elements to be quickly converted to HTML and prepared for multi-part messaging. Load it and set it up (see http://orgmode.org/worg/org-contrib/org-mime.html):

;; enable HTML email from org
(require 'org-mime)
;; setup org-mime for wanderlust
;; (setq org-mime-library 'semi)
;; or for gnus/message-mode
(setq org-mime-library 'mml)

;; easy access to htmlize in message-mode
(add-hook 'message-mode-hook
          (lambda ()
            (local-set-key "\C-c\M-o" 'org-mime-htmlize)))

;; uncomment this to use the org-mome native functions for htmlizing.
;; (add-hook 'org-mode-hook
;;           (lambda ()
;;             (local-set-key "\C-c\M-o" 'org-mime-org-buffer-htmlize)))

;; uncomment to displyay src blocks with a dark background
;; (add-hook 'org-mime-html-hook
;;           (lambda ()
;;             (org-mime-change-element-style
;;              "pre" (format "color: %s; background-color: %s; padding: 0.5em;"
;;                            "#E6E1DC" "#232323"))))

;; pretty blockquotes
(add-hook 'org-mime-html-hook
          (lambda ()
            (org-mime-change-element-style

             "blockquote" "border-left: 2px solid gray; padding-left: 4px;")))

Fix htmlization

Upstream org-mime-htmlize unfortunately can’t be called uninteractively (bummer!), so we have to rewrite it to make programmatic calls work properly. I found the solution Emacs Stackexchange.

(defun org-mime-htmlize (&optional arg)
"Export a portion of an email body composed using `mml-mode' to
html using `org-mode'.  If called with an active region only
export that region, otherwise export the entire body."
  (interactive "P")
  (require 'ox-org)
  (require 'ox-html)
  (let* ((region-p (org-region-active-p))
         (html-start (or (and region-p (region-beginning))
                         (save-excursion
                           (goto-char (point-min))
                           (search-forward mail-header-separator)
                           (+ (point) 1))))
         (html-end (or (and region-p (region-end))
                       ;; TODO: should catch signature...
                       (point-max)))
         (raw-body (concat org-mime-default-header
                           (buffer-substring html-start html-end)))
         (tmp-file (make-temp-name (expand-file-name
                                    "mail" temporary-file-directory)))
         (body (org-export-string-as raw-body 'org t))
         ;; because we probably don't want to export a huge style file
         (org-export-htmlize-output-type 'inline-css)
         ;; makes the replies with ">"s look nicer
         (org-export-preserve-breaks org-mime-preserve-breaks)
         ;; dvipng for inline latex because MathJax doesn't work in mail
         (org-html-with-latex 'dvipng)
         ;; to hold attachments for inline html images
         (html-and-images
          (org-mime-replace-images
           (org-export-string-as raw-body 'html t) tmp-file))
         (html-images (unless arg (cdr html-and-images)))
         (html (org-mime-apply-html-hook
                (if arg
                    (format org-mime-fixedwith-wrap body)
                  (car html-and-images)))))
    (delete-region html-start html-end)
    (save-excursion
      (goto-char html-start)
      (insert (org-mime-multipart
               body html (mapconcat 'identity html-images "\n"))))))

Actually perform the export!

These functions are crude helpers that gather extra information about the org subtree, of which org-mime is unaware.

  • mwp-org-get-parent-headline traverses the tree to the ancestor headline, because that’s what I want to set the subject to.
  • mwp-org-attachment-list is stolen directly from the Gnorb package, which looks cool, awesome ,and kinda complex; it just iterates through a subtree’s attachments and grabs URLs.
  • mwp-send-subtree-with-attachments performs the export and is bound to C-c M-o

So, if I want to mail a subtree, I just C-c M-o and I’m almost done – the html mail is ready to go, and all org attachments are also attached to the email.

Note there are some real weaknesses here: mwp-org-get-parent-headline actually gets the top-level ancestor – which only happens to be what I want right now. Better would be to use org-element to locate the parent (and other headline attributes) directly, but I’m not sure how to do that.

Similarly, the initial greeting is generated from the current headline value – so this only works because I name my subtrees after the addressee (which I only do because of my use case).

(defun mwp-org-get-parent-headline ()
  "Acquire the parent headline & return."
  (save-excursion
    (org-mark-subtree)
    (re-search-backward  "^\\* ")
    (nth 4 (org-heading-components))))

(defun mwp-send-subtree-with-attachments ()
  "org-mime-subtree and HTMLize"
  (interactive)
  (org-mark-subtree)
  (let ((attachments (mwp-org-attachment-list))
        (subject  (mwp-org-get-parent-headline)))
    (insert "Hello " (nth 4 org-heading-components) ",\n")
    (org-mime-subtree)
    (insert "\nBest,\nMP.\n")
    (message "subject is" )
    (message subject)
    ;;(message-to)
    (org-mime-htmlize)
    ;; this comes from gnorb
    ;; I will reintroduce it if I want to reinstate questions.
    ;; (map-y-or-n-p
    ;;  ;; (lambda (a) (format "Attach %s to outgoing message? "
    ;;  ;;                    (file-name-nondirectory a)))
    ;; (lambda (a)
    ;;   (mml-attach-file a (mm-default-file-encoding a)
    ;;                    nil "attachment"))
    ;; attachments
    ;; '("file" "files" "attach"))
    ;; (message "Attachments: %s" attachments)
    (dolist (a attachments) (message "Attachment: %s" a) (mml-attach-file a (mm-default-file-encoding a) nil "attachment"))
    (message-goto-to)
    ))

;; add a keybinding for org-mode
(add-hook 'org-mode-hook
          (lambda ()
            (local-set-key "\C-c\M-o" 'mwp-send-subtree-with-attachments)))

;; stolen from gnorb; finds attachments in subtree
(defun mwp-org-attachment-list (&optional id)
  "Get a list of files (absolute filenames) attached to the
current heading, or the heading indicated by optional argument ID."
  (when (featurep 'org-attach)
    (let* ((attach-dir (save-excursion
                         (when id
                           (org-id-goto id))
                         (org-attach-dir t)))
           (files
            (mapcar
             (lambda (f)
               (expand-file-name f attach-dir))
             (org-attach-file-list attach-dir))))
      files)))

Contacts

That’s a good start, but there are still some steps to make this truly convenient. For instance, I certainly don’t want to type in students’ email addresses by hand. So I imported my contacts from thunderbird to org-contacts. This was a pain – the process was Thunderbird → Gmail (via gsync plugin) → vcard (via gmail export) → org-contacts (via Titus’s python importer). I wish there was a CSV importer for org-contacts; probably this would be easy to write but I’m so slooooowwww at coding. My org contacts live in GTD/Contacts.org, which is set in Customize, and org reads them on startup with this line

(require 'org-contacts)

With this single line, org-contacts now provides TAB completion in message-mode to headers. It’s very fast, so feels more convenient than Thunderbird.

Making it better
I wish I could get org-contacts to provide tab completion in my subtrees (see below). I would need to access the completion function directly and somehow set the binding for TAB to that completion function.

Adding Attachments with Drag & Drop

After I make inline comments, I fill out a grading template and attach the paper to the resultant subtree (C-c C-a a PATH). This is OK, but sometimes it would nice to be able to drag and drom the files, so I am working on these functions.

Even better
an even better solution be to add the attachments programmatically. The studnet papes follow a strict naming convention, so I should be able to crawl the directory and find the most recent paper with the student’s name in it… I’m worried it wil lbe too error prone though.

Anyway: unfortunately the following code doesn’t work right, so don’t just cut and paste this code!). I *ought to be able to bind the drag and drop action to a function – even several functions – and, if conditions are right, attach the dragged file to the current org header. John Kitchin describes this method here. But I do the following instead, which is also broken right now:

Start by loading org-download, which downloads dragged images as attachments and inserts a link. (yay). THen a modification which fixes handling of file links allowing me to drag-n-drop files links onto org as attachments. Unfortunately, I can’t get org-attach to process the URI’s properly. Darn it.

(require 'org-download)
(require 'org-attach)
;; extending the dnd functionality
;; but doesn't actually work... 
(defun mwp-org-file-link-dnd (uri action)
    "When in `org-mode' and URI points to local file, 
  add as attachment and also add a link. Otherwise, 
  pass URI and Action back to dnd dispatch"
    (let ((img-regexp "\\(png$\\|jp[e]?g$\\)")
          (newuri (replace-regexp-in-string "file:///" "/" uri)))
      (cond ((eq major-mode 'org-mode)
             (message "Hi! newuri: %s " (file-relative-name newuri))
             (cond ((string-match img-regexp newuri)
                    (insert "#+ATTR_ORG: :width 300\n")
                    (insert (concat  "#+CAPTION: " (read-input "Caption: ") "\n"))
                    (insert (format "[[%s]]" uri))
                    (org-display-inline-images t t))
                   (t 
                    (org-attach-new newuri)
                    (insert (format "[[%s]]" uri))))
             )
            (t
             (let ((dnd-protocol-alist
                    (rassq-delete-all
                     'mwp-org-file-link-dnd
                     (copy-alist dnd-protocol-alist))))
               (dnd-handle-one-url nil action uri)))
            )))

  ;; add a new function that DOESN'T open the attachment!
(defun org-attach-new-dont-open (file)
    "Create a new attachment FILE for the current task.
  The attachment is created as an Emacs buffer."
    (interactive "sCreate attachment named: ")
    (when (and org-attach-file-list-property (not org-attach-inherited))
      (org-entry-add-to-multivalued-property
       (point) org-attach-file-list-property file))
    )

(defun mwp-org-file-link-enable ()
    "Enable file drag and drop attachments."
    (unless (eq (cdr (assoc "^\\(file\\)://" dnd-protocol-alist))
                'mwp-org-file-link-dnd)
      (setq dnd-protocol-alist
            `(("^\\(file\\)://" . mwp-org-file-link-dnd) ,@dnd-protocol-alist))))

(defun mwp-org-file-link-disable ()
  "Enable file drag and drop attachments."
  (if (eq (cdr (assoc "^\\(file\\)://" dnd-protocol-alist))
              'mwp-org-file-link-dnd)
      (rassq-delete-all
       'mwp-org-file-link-dnd
       dnd-protocol-alist)

    ))

(mwp-org-file-link-enable)

Exporting org-files to a temporary location

I have a private journal, which lives in an encrypted file in a Dropbox-backed-up directory. I use html export to examine the contents sometimes – there are some big old tables that are hard to read in org-mode – but I don’t want the html file to end up in Dropbox.

So I just copied the definition of org-export-html-as-html and made trivial modifications. There’s probably a better way to do this.

;; export html to tmp dir
(defun mwp-org-html-to-tmp
    (&optional async subtreep visible-only body-only ext-plist)
  "Export current buffer to a HTML file in the tmp directory.

If narrowing is active in the current buffer, only export its
narrowed part.

If a region is active, export that region.

A non-nil optional argument ASYNC means the process should happen
asynchronously.  The resulting file should be accessible through
the `org-export-stack' interface.

When optional argument SUBTREEP is non-nil, export the sub-tree
at point, extracting information from the headline properties
first.

When optional argument VISIBLE-ONLY is non-nil, don't export
contents of hidden elements.

When optional argument BODY-ONLY is non-nil, only write code
between \"<body>\" and \"</body>\" tags.


EXT-PLIST, when provided, is a property list with external
parameters overriding Org default settings, but still inferior to
file-local settings.

Return output file's name."
  (interactive)
  (let* ((extension (concat "." (or (plist-get ext-plist :html-extension)
                                    org-html-extension
                                    "html")))
;; this is the code I've changed from the original function. 
         (file (org-export-output-file-name extension subtreep "/home/matt/tmp/"))

         (org-export-coding-system org-html-coding-system))
    (org-export-to-file 'html file
      async subtreep visible-only body-only ext-plist)
    (org-open-file file)))

(org-defkey org-mode-map
            (kbd "C-c 0") 'mwp-org-html-to-tmp)

Creating and Publishing Presentations with Org-reveal

For several years, I’ve been using Org-mode to compose slides for my lectures. This method is great, because I get to work in plain-text and focus on the content of my lectures rather than animations; but it’s meant that when I want to share my presentations with others, there’s a certain amount of work involved as I move from a local copy on my computer to a web-based version. (This has largely been an issue because I sometimes compose my lectures sitting in a café with lousy Internet, and I sometimes give my lectures in a horrible classroom at U of T with terrible Internet reception.) I’ve now largely solved this problem, though there is hopefully an improvement coming down the pipe which will make it even easier.

Org-mode has the capacity to export to a number of slide-like formats, including the LaTeX-based Beamer format, which also makes good PDF presentations, a couple of Emacs-based presentation tools, and a number of HTML5 formats. Since I teach about the web all the time, the HTML5 formats have always been the most appealing to me.

Org-Reveal Setup

I have used and still very much like deck.js (exporter here), but have recently switched to org-reveal, which I really like a lot. It’s not part of the official org distribution, so installation and setup are a little more involved, but not difficult. I just cloned the org-reveal and reveal.js repositories:

cd ~/src
git clone https://github.com/hakimel/reveal.js.git
git clone https://github.com/yjwen/org-reveal.git

and put this in my emacs-init.el:

;; org-reveal
(add-to-list 'load-path "~/src/org-reveal")
(require 'ox-reveal)
;; set local root
(setq org-reveal-root "file:///home/matt/src/reveal.js")

That’s all that’s needed to get export working! I find it’s really fast to prepare lectures.

Publishing

That’s great for giving lectures, and is all I really need at 9:55 when I’m trying to type my lecture and walk to class at the same time. But after lecture I want to put my slides somewhere my students can see them. Even if I wanted to, it would be impossible for me to post to Blackboard, which turns these files into garbage. What I want to do is publish them to the web; but I need to make sure that all the JS and CSS links are pointing to the web-based libraries and not my local copies, which of course no one but me can see. To do this I had to make one small change to org-reveal.el, which I have submitted as a pull request. This creates a new variable, org-reveal-extra-css, which I can refer to in my own functions.

Then I use org-mode’s fantastic built-in Publishing functions to push my slides to a public website. Publishing allows you to perform an export on many files, and customize the output in powerful ways that are mostly beyond me, actually. Still, I have a setup that I like a lot.

First, my org-publish-project-alist, which defines the publishing targets. Note especially the top part, which defines “meta-projects”: for instance, I can publish all the slides and source files for all my classes with one command, M-x org-publish-projects [RET] courses.

(setq org-publish-project-alist
      '(
        ("courses"
         :components ("dh" "rlg231"))
        ("rlg231"
         :components ("rlg231-lecture-slides" "rlg231-lecture-source"))
        ("dh"
         :components ("digital-history-lecture-slides" "digital-history-lecture-source"))

        ("rlg231-lecture-slides"
         :base-directory "~/RLG231/Lectures/"
         :base-extension "org"
         :publishing-directory "/ssh:matt@shimano:/var/www/sandbox/RLG231/Lectures/Slides"
         :recursive t
         :publishing-function mwp-org-reveal-publish-to-html
         :preparation-function nil 
         :completion-function nil
         :headline-levels 4             ; Just the default for this project.
         :exclude "LectureOutlines.org"
         :exclude-tags note noexport
         :auto-preamble t)

        ("rlg231-lecture-source"
         :base-directory "~/RLG231/Lectures/"
         :base-extension "org"
         :publishing-directory "/ssh:matt@shimano:/var/www/sandbox/RLG231/Lectures/Source"
         :recursive t
         :publishing-function org-org-publish-to-org
         :preparation-function nil
         :completion-function nil
         :headline-levels 4             ; Just the default for this project.
         :exclude "LecturePlans.org"
         ;; :exclude "LectureOutlines.org"
         :exclude-tags note noexport
         :auto-preamble t)

        ("digital-history-lecture-source"
         :base-directory "~/DH/Lectures"
         :base-extension "org"
         :publishing-directory "/ssh:matt@shimano:/var/www/sandbox/DigitalHistory/Lectures/Source"
         :recursive t
         :publishing-function org-org-publish-to-org
         :preparation-function 
         :completion-function 
         :headline-levels 4             ; Just the default for this project.
         ;; :exclude "LecturePlans.org"
         :exclude "LectureOutlines.org"
         :exclude-tags note noexport
         :auto-preamble t)

        ("digital-history-lecture-slides"
         :base-directory "~/DH/Lectures"
         :base-extension "org"
         :publishing-directory "/ssh:matt@shimano:/var/www/sandbox/DigitalHistory/Lectures/Slides"
         :recursive t
         :publishing-function mwp-org-reveal-publish-to-html
         :preparation-function 
         :completion-function 
         :headline-levels 4             ; Just the default for this project.
         ;; :exclude "LecturePlans.org"
         :exclude "LectureOutlines.org"
         :exclude-tags note noexport
         :auto-preamble t)

        ;; ("newone-lecture-slides"
        ;;  :base-directory "~/NewOne/Lectures/"
        ;;  :base-extension "org"
        ;;  :publishing-directory "/ssh:matt@shimano:/var/www/sandbox/NewOne/Lectures"
        ;;  :recursive t
        ;;  :publishing-function org-deck-publish-to-html
        ;;  :headline-levels 4             ; Just the default for this project.
        ;;  :exclude-tags note noexport
        ;;  :auto-preamble t)

        ;; ("newone-lecture-notes"
        ;;  :base-directory "~/NewOne/Lectures/"
        ;;  :base-extension "org"
        ;;  :publishing-directory "/ssh:matt@shimano:/var/www/sandbox/NewOne/Lectures-with-notes"
        ;;  :recursive t
        ;;  :publishing-function org-html-publish-to-html
        ;;  :headline-levels 4             ; Just the default for this project.
        ;;  :exclude-tags noexport
        ;;  :auto-preamble t)

        ;;  ("newone-images"
        ;;        :base-directory "~/NewOne/Images/"
        ;;        :base-extension "jpg\\|gif\\|png"
        ;;        :publishing-directory "/ssh:matt@shimano:/var/www/sandbox/NewOne/Images"
        ;;        :publishing-function org-publish-attachment)

        ;;  ("newone" :components ("newone-lecture-slides" "newone-lecture-notes" "newone-images") )

        ;;  ("presentations"
        ;;   :base-directory "~/Dropbox/Work/Talks/"
        ;;   :base-extension "org"
        ;;   :publishing-directory "/ssh:matt@shimano:/var/www/sandbox/Presentations"
        ;;   :headline-levels 4 ; just the default for this project
        ;;   :exclude-tags noexport
        ;;   :auto-preamble t
        ;;   :publishing-function mwp-org-deck-publish-to-html
        ;;   ;; :completion-function mwp-update-published-paths
        ;;   )

      ))

Notice the publishing function, which is set to mwp-org-deck-publish-to-html. This is a simple function that resets the base url and extra-css values to web-based ones before publication, so that the presentations work when online. Notice I’ve also reset the deck.js base url, in case I ever decide to change back to deck.

(defun mwp-org-reveal-publish-to-html (plist filename pub-dir)
  "Publish an org file to reveal.js HTML Presentation.
FILENAME is the filename of the Org file to be published.  PLIST
is the property list for the given project.  PUB-DIR is the
publishing directory. Returns output file name."
  (let ((org-deck-base-url "http://sandbox.hackinghistory.ca/Tools/deck.js/")
        (org-reveal-root "http://sandbox.hackinghistory.ca/Tools/reveal.js/")
        (org-reveal-extra-css "http://sandbox.hackinghistory.ca/Tools/reveal.js/css/local.css"))

    (org-publish-org-to 'reveal filename ".html" plist pub-dir))
  )

And that’s it, magic!

Still to do

I like this a lot, but there are a couple of pieces I’d still like to implement.

Fix all local file URL’s
I’d like to write a function to take a final pass through all the links and change file:/// links to HTML relative links. That will take some work though.
Export as standalone
There is work underway to allow presentations to be generated as stand-alone files that can be, e.g, sent by email. I like this idea a lot. See this Github issue.
Standardize notes, fragments
Every time I switch from one presentation framework to another, I have to learn a whole different syntax for things like fragments (bits of content that don’t appear on the slide immediately, but are instead stepped through) and speaker notes (that don’t appear on the slide that your viewers see, but are only visible to you in some kind of preview mode). It would be great if the various slide modes could work towards a common syntax for these things. If I have time, energy, and skills, I would like to help develop this a little.

See my slides

If you want to see some examples of the end product, here is a link to my Digital History lecture archive (still being built!). Many of my course materials are also online at Github.