I encountered a few problems with org-mac-mail bundled with emacs 24.3.1; some errors interfacing with Growl (2.1) so the patch below works for me and I wondered whether it would be of use to anyone else.
I adapted the insertion of flagged mail to look at all active mail accounts rather than having to name any particular one. This is how I use org-mac-message: and below is the patch. (require 'org) (load "org-mac-message") (global-set-key [(shift f10)] (lambda() (interactive) (progn (find-file-noselect "~/Dropbox/org/refile.org") (org-mac-message-insert-flagged "refile.org" "Flagged mail")))) diff --git a/org-mac-message.el b/org-mac-message.el index 5df68f5..be2a654 100644 --- a/org-mac-message.el +++ b/org-mac-message.el @@ -95,86 +95,89 @@ This will use the command `open' with the message URL." "return theLinkList as string\n" "end tell"))) -(defun as-get-flagged-mail () - "AppleScript to create links to flagged messages in Mail.app." - (do-applescript - (concat - ;; Is Growl installed? - "tell application \"System Events\"\n" - "set growlHelpers to the name of every process whose creator type contains \"GRRR\"\n" - "if (count of growlHelpers) > 0 then\n" - "set growlHelperApp to item 1 of growlHelpers\n" - "else\n" - "set growlHelperApp to \"\"\n" - "end if\n" - "end tell\n" - - ;; Get links - "tell application \"Mail\"\n" - "set theMailboxes to every mailbox of account \"" org-mac-mail-account "\"\n" - "set theLinkList to {}\n" - "repeat with aMailbox in theMailboxes\n" - "set theSelection to (every message in aMailbox whose flagged status = true)\n" - "repeat with theMessage in theSelection\n" - "set theID to message id of theMessage\n" - "set theSubject to subject of theMessage\n" - "set theLink to \"message://\" & theID & \"::split::\" & theSubject & \"\n\"\n" - "copy theLink to end of theLinkList\n" - - ;; Report progress through Growl - ;; This "double tell" idiom is described in detail at - ;; http://macscripter.net/viewtopic.php?id=24570 The - ;; script compiler needs static knowledge of the - ;; growlHelperApp. Hmm, since we're compiling - ;; on-the-fly here, this is likely to be way less - ;; portable than I'd hoped. It'll work when the name - ;; is still "GrowlHelperApp", though. - "if growlHelperApp is not \"\" then\n" - "tell application \"GrowlHelperApp\"\n" - "tell application growlHelperApp\n" - "set the allNotificationsList to {\"FlaggedMail\"}\n" - "set the enabledNotificationsList to allNotificationsList\n" - "register as application \"FlaggedMail\" all notifications allNotificationsList default notifications enabledNotificationsList icon of application \"Mail\"\n" - "notify with name \"FlaggedMail\" title \"Importing flagged message\" description theSubject application name \"FlaggedMail\"\n" - "end tell\n" - "end tell\n" - "end if\n" - "end repeat\n" - "end repeat\n" - "return theLinkList as string\n" - "end tell"))) +(defun as-get-flagged-mail () + "AppleScript to create links to flagged messages in all Mail.app accounts" + ;; Revised use of Growl interface and no need for variable org-mac-mail-account + (do-applescript + (concat + ;; Is Growl running? Use API as recommended at http://growl.info/documentation/applescript-support.php + "tell application \"System Events\"\n" + "set growlIsRunning to (count of (every process whose bundle identifier is \"com.Growl.GrowlHelperApp\")) > 0\n" + "end tell\n" + + "set theLinkList to {}\n" + "set isNotRegisteredToGrowl to true\n" + "tell application \"Mail\"\n" + "set theAccountList to the name of every account\n" + "repeat with theMailAccount in theAccountList\n" + "set theMailboxes to every mailbox of account theMailAccount\n" + "repeat with aMailbox in theMailboxes\n" + "set theSelection to (every message in aMailbox whose flagged status = true)\n" + "repeat with theMessage in theSelection\n" + "set theID to message id of theMessage\n" + "set theSubject to subject of theMessage\n" + "set theLink to \"message://\" & theID & \"::split::\" & theSubject & \"\n\"\n" + "copy theLink to end of theLinkList\n" + + "if growlIsRunning then\n" + "tell application id \"com.Growl.GrowlHelperApp\"\n" + "if isNotRegisteredToGrowl then\n" + "set the allNotificationsList to {\"FlaggedMail\"}\n" + "set the enabledNotificationsList to allNotificationsList\n" + "register as application \"FlaggedMail\" all notifications allNotificationsList default notifications enabledNotificationsList icon of application \"Mail\"\n" + "set isNotRegisteredToGrowl to false\n" + "end if\n" + "notify with name \"FlaggedMail\" title \"Importing flagged message\" description theSubject application name \"FlaggedMail\"\n" + "end tell\n" + "end if\n" + + "end repeat\n" + "end repeat\n" + "end repeat\n" + "end tell\n" + "return theLinkList as string\n" + )) + ) (defun org-mac-message-get-links (&optional select-or-flag) - "Create links to the messages currently selected or flagged in Mail.app. + "Overridden function. +Create links to the messages currently selected or flagged in Mail.app. This will use AppleScript to get the message-id and the subject of the messages in Mail.app and make a link out of it. When SELECT-OR-FLAG is \"s\", get the selected messages (this is also the default). When SELECT-OR-FLAG is \"f\", get the flagged messages. The Org-syntax text will be pushed to the kill ring, and also returned." - (interactive "sLink to (s)elected or (f)lagged messages: ") - (setq select-or-flag (or select-or-flag "s")) - (message "AppleScript: searching mailboxes...") - (let* ((as-link-list - (if (string= select-or-flag "s") - (as-get-selected-mail) - (if (string= select-or-flag "f") - (as-get-flagged-mail) - (error "Please select \"s\" or \"f\"")))) - (link-list - (mapcar - (lambda (x) (if (string-match "\\`\"\\(.*\\)\"\\'" x) (setq x (match-string 1 x))) x) - (split-string as-link-list "[\r\n]+"))) - split-link URL description orglink orglink-insert rtn orglink-list) - (while link-list - (setq split-link (split-string (pop link-list) "::split::")) - (setq URL (car split-link)) - (setq description (cadr split-link)) - (when (not (string= URL "")) - (setq orglink (org-make-link-string URL description)) - (push orglink orglink-list))) - (setq rtn (mapconcat 'identity orglink-list "\n")) - (kill-new rtn) - rtn)) + (interactive "sLink to (s)elected or (f)lagged messages: ") + (setq select-or-flag (or select-or-flag "s")) + (let* ((as-link-list + (if (string= select-or-flag "s") + (progn + (message "AppleScript: searching mailboxes for selected message") + (as-get-selected-mail)) + (if (string= select-or-flag "f") + (progn + ;; searching for all flagged messages can take tens of seconds + ;; so user needs to be told of progress even if Growl is also notifying + (message "AppleScript: searching mailboxes for flagged messages") + (as-get-flagged-mail)) + (error "Please select \"s\" or \"f\"")))) + (link-list + (mapcar + (lambda (x) (if (string-match "\\`\"\\(.*\\)\"\\'" x) (setq x (match-string 1 x))) x) + (split-string as-link-list "[\r\n]+"))) + split-link URL description orglink orglink-insert rtn orglink-list) + (while link-list + (setq split-link (split-string (pop link-list) "::split::")) + (setq URL (car split-link)) + (setq description (cadr split-link)) + (when (not (string= URL "")) + (setq orglink (org-make-link-string URL description)) + (push orglink orglink-list))) + (setq rtn (mapconcat 'identity orglink-list "\n")) + (kill-new rtn) + ;; "searching" message will remain in echo area until replaced + (message "AppleScript: finished searching mailboxes.") + rtn)) (defun org-mac-message-insert-selected () "Insert a link to the messages currently selected in Mail.app. @@ -186,30 +189,41 @@ active mail in Mail.app and make a link out of it." ;; The following line is for backward compatibility (defalias 'org-mac-message-insert-link 'org-mac-message-insert-selected) -(defun org-mac-message-insert-flagged (org-buffer org-heading) - "Asks for an org buffer and a heading within it, and replace message links. +(eval-after-load "org-mac-message" + '(defun org-mac-message-insert-flagged (org-buffer org-heading) + "Asks for an org buffer and a heading within it, and replace message links. If heading exists, delete all message:// links within heading's first level. If heading doesn't exist, create it at point-max. Insert list of message:// links to flagged mail after heading." - (interactive "bBuffer in which to insert links: \nsHeading after which to insert links: ") - (with-current-buffer org-buffer - (goto-char (point-min)) - (let ((isearch-forward t) - (message-re "\\[\\[\\(message:\\)\\([^]]+\\)\\]\\(\\[\\([^]]+\\)\\]\\)?\\]")) - (if (org-goto-local-search-headings org-heading nil t) - (if (not (eobp)) - (progn - (save-excursion - (while (re-search-forward - message-re (save-excursion (outline-next-heading)) t) - (delete-region (match-beginning 0) (match-end 0))) - (insert "\n" (org-mac-message-get-links "f"))) - (flush-lines "^$" (point) (outline-next-heading))) - (insert "\n" (org-mac-message-get-links "f"))) - (goto-char (point-max)) - (insert "\n") - (org-insert-heading nil t) - (insert org-heading "\n" (org-mac-message-get-links "f")))))) + (interactive "bBuffer in which to insert links: \nsHeading after which to insert links: ") + (with-current-buffer org-buffer + (goto-char (point-min)) + (let ((isearch-forward t) + (message-re "\\[\\[\\(message:\\)\\([^]]+\\)\\]\\(\\[\\([^]]+\\)\\]\\)?\\]")) + (if (org-goto-local-search-headings org-heading nil t) + (if (not (eobp)) + (progn + (save-excursion + ;; delete previously recorded flagged messages + (while (re-search-forward + message-re (save-excursion (outline-next-heading)) t) + (delete-region (match-beginning 0) (match-end 0))) + (insert "\n" (org-mac-message-get-links "f"))) + ;; apparently also delete empty lines. + ;; allow for situation where there is no next-heading + ;; to avoid previously ocurring Lisp errors + (let ( + (frm (point)) + (tom (outline-next-heading))) + (if (null tom) (setq tom (point-max))) + (flush-lines "^$" frm tom))) + + (insert "\n" (org-mac-message-get-links "f"))) + (goto-char (point-max)) + (insert "\n") + (org-insert-heading nil t) + (insert org-heading "\n" (org-mac-message-get-links "f")))))) + ) (provide 'org-mac-message)