2012-04-24 17:11:56 +02:00
|
|
|
|
;;; mu4e-actions.el -- part of mu4e, the mu mail user agent
|
|
|
|
|
;;
|
|
|
|
|
;; Copyright (C) 2011-2012 Dirk-Jan C. Binnema
|
|
|
|
|
|
|
|
|
|
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
|
|
|
|
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
|
|
|
|
|
|
|
|
|
;; This file is not part of GNU Emacs.
|
|
|
|
|
;;
|
|
|
|
|
;; GNU Emacs is free software: you can redistribute it and/or modify
|
|
|
|
|
;; it under the terms of the GNU General Public License as published by
|
|
|
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
;; (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
;; GNU Emacs is distributed in the hope that it will be useful,
|
|
|
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
;; GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
|
|
|
|
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
|
|
;; Example actions for messages, attachments (see chapter 'Actions' in the
|
|
|
|
|
;; manual)
|
|
|
|
|
|
|
|
|
|
;;; Code:
|
2012-06-10 15:14:21 +02:00
|
|
|
|
(eval-when-compile (byte-compile-disable-warning 'cl-functions))
|
2012-04-24 17:11:56 +02:00
|
|
|
|
(require 'cl)
|
2013-10-08 05:52:29 +02:00
|
|
|
|
(require 'ido)
|
2012-06-10 15:14:21 +02:00
|
|
|
|
|
2012-09-26 16:28:30 +02:00
|
|
|
|
(require 'mu4e-message)
|
2012-04-24 17:11:56 +02:00
|
|
|
|
(require 'mu4e-meta)
|
|
|
|
|
|
2012-09-26 16:28:30 +02:00
|
|
|
|
|
2012-05-12 16:10:57 +02:00
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-04-24 17:11:56 +02:00
|
|
|
|
(defun mu4e-action-count-lines (msg)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Count the number of lines in the e-mail message.
|
|
|
|
|
Works for headers view and message-view."
|
2012-04-24 17:11:56 +02:00
|
|
|
|
(message "Number of lines: %s"
|
|
|
|
|
(shell-command-to-string
|
2012-09-26 16:28:30 +02:00
|
|
|
|
(concat "wc -l < " (shell-quote-argument (mu4e-message-field msg :path))))))
|
2012-05-12 16:10:57 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-04-24 17:11:56 +02:00
|
|
|
|
|
|
|
|
|
|
2012-04-24 17:48:07 +02:00
|
|
|
|
|
|
|
|
|
|
2012-05-12 16:10:57 +02:00
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-04-24 17:11:56 +02:00
|
|
|
|
(defvar mu4e-msg2pdf (concat mu4e-builddir "/toys/msg2pdf/msg2pdf")
|
|
|
|
|
"Path to the msg2pdf toy.")
|
|
|
|
|
|
|
|
|
|
(defun mu4e-action-view-as-pdf (msg)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Convert the message to pdf, then show it.
|
|
|
|
|
Works for the message view."
|
2012-04-24 17:11:56 +02:00
|
|
|
|
(unless (file-executable-p mu4e-msg2pdf)
|
2012-07-10 11:11:06 +02:00
|
|
|
|
(mu4e-error "msg2pdf not found; please set `mu4e-msg2pdf'"))
|
2012-04-24 17:11:56 +02:00
|
|
|
|
(let* ((pdf
|
|
|
|
|
(shell-command-to-string
|
|
|
|
|
(concat mu4e-msg2pdf " "
|
2012-09-26 16:28:30 +02:00
|
|
|
|
(shell-quote-argument (mu4e-message-field msg :path))
|
2012-04-29 16:32:34 +02:00
|
|
|
|
" 2> /dev/null")))
|
|
|
|
|
(pdf (and pdf (> (length pdf) 5)
|
|
|
|
|
(substring pdf 0 -1)))) ;; chop \n
|
|
|
|
|
(unless (and pdf (file-exists-p pdf))
|
2012-09-26 16:28:30 +02:00
|
|
|
|
(mu4e-warn "Failed to create PDF file"))
|
2012-04-24 17:11:56 +02:00
|
|
|
|
(find-file pdf)))
|
2012-05-12 16:10:57 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
(defun mu4e-action-view-in-browser (msg)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"View the body of the message in a web browser.
|
|
|
|
|
You can influence the browser to use with the variable
|
|
|
|
|
`browse-url-generic-program'."
|
2012-09-26 16:28:30 +02:00
|
|
|
|
(let* ((html (mu4e-message-field msg :body-html))
|
|
|
|
|
(txt (mu4e-message-field msg :body-txt))
|
2012-09-25 06:38:57 +02:00
|
|
|
|
(tmpfile (format "%s%x.html" temporary-file-directory (random t))))
|
2012-08-30 11:54:26 +02:00
|
|
|
|
(unless (or html txt)
|
|
|
|
|
(mu4e-error "No body part for this message"))
|
|
|
|
|
(with-temp-buffer
|
2012-09-25 06:38:57 +02:00
|
|
|
|
;; simplistic -- but note that it's only an example...
|
2012-08-30 11:54:26 +02:00
|
|
|
|
(insert (or html (concat "<pre>" txt "</pre>")))
|
2012-09-25 06:38:57 +02:00
|
|
|
|
(write-file tmpfile)
|
|
|
|
|
(browse-url (concat "file://" tmpfile)))))
|
2012-05-12 16:10:57 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
|
|
|
|
|
2012-04-24 17:11:56 +02:00
|
|
|
|
|
2012-08-30 11:54:26 +02:00
|
|
|
|
|
2012-05-12 16:10:57 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-08-30 11:54:26 +02:00
|
|
|
|
(defconst mu4e-text2speech-command "festival --tts"
|
|
|
|
|
"Program that speaks out text it receives on standard-input.")
|
2012-04-24 17:11:56 +02:00
|
|
|
|
|
2012-05-12 16:10:57 +02:00
|
|
|
|
(defun mu4e-action-message-to-speech (msg)
|
|
|
|
|
"Pronounce the message text using `mu4e-text2speech-command'."
|
2012-09-26 16:28:30 +02:00
|
|
|
|
(unless (mu4e-message-field msg :body-txt)
|
|
|
|
|
(mu4e-warn "No text body for this message"))
|
2012-05-12 16:10:57 +02:00
|
|
|
|
(with-temp-buffer
|
2012-09-26 16:28:30 +02:00
|
|
|
|
(insert (mu4e-message-field msg :body-txt))
|
2012-05-12 16:10:57 +02:00
|
|
|
|
(shell-command-on-region (point-min) (point-max)
|
|
|
|
|
mu4e-text2speech-command)))
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-04-24 17:11:56 +02:00
|
|
|
|
|
2012-04-24 17:48:07 +02:00
|
|
|
|
|
|
|
|
|
|
2012-05-12 16:10:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-04-24 17:48:07 +02:00
|
|
|
|
(defvar mu4e-captured-message nil
|
|
|
|
|
"The last-captured message (the s-expression).")
|
|
|
|
|
|
2012-04-24 17:11:56 +02:00
|
|
|
|
(defun mu4e-action-capture-message (msg)
|
|
|
|
|
"Remember MSG; we can create a an attachment based on this msg
|
2012-04-24 17:48:07 +02:00
|
|
|
|
with `mu4e-compose-attach-captured-message'."
|
2012-04-24 17:11:56 +02:00
|
|
|
|
(setq mu4e-captured-message msg)
|
|
|
|
|
(message "Message has been captured"))
|
2012-05-12 16:10:57 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-04-24 17:11:56 +02:00
|
|
|
|
|
|
|
|
|
|
2012-05-12 16:10:57 +02:00
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
2012-04-24 17:11:56 +02:00
|
|
|
|
(defvar mu4e-org-contacts-file nil
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"File to store contact information for org-contacts.
|
|
|
|
|
Needed by `mu4e-action-add-org-contact'.")
|
2012-04-24 17:11:56 +02:00
|
|
|
|
|
|
|
|
|
(eval-when-compile ;; silence compiler warning about free variable
|
|
|
|
|
(unless (require 'org-capture nil 'noerror)
|
|
|
|
|
(defvar org-capture-templates nil)))
|
|
|
|
|
|
|
|
|
|
(defun mu4e-action-add-org-contact (msg)
|
|
|
|
|
"Add an org-contact entry based on the From: address of the
|
|
|
|
|
current message (in headers or view). You need to set
|
|
|
|
|
`mu4e-org-contacts-file' to the full path to the file where you
|
|
|
|
|
store your org-contacts."
|
2012-09-03 10:15:27 +02:00
|
|
|
|
(unless (require 'org-capture nil 'noerror)
|
2012-07-10 11:11:06 +02:00
|
|
|
|
(mu4e-error "org-capture is not available."))
|
2012-04-24 17:11:56 +02:00
|
|
|
|
(unless mu4e-org-contacts-file
|
2012-07-10 11:11:06 +02:00
|
|
|
|
(mu4e-error "`mu4e-org-contacts-file' is not defined."))
|
2012-09-26 16:28:30 +02:00
|
|
|
|
(let* ((sender (car-safe (mu4e-message-field msg :from)))
|
2012-04-24 17:11:56 +02:00
|
|
|
|
(name (car-safe sender)) (email (cdr-safe sender))
|
|
|
|
|
(blurb
|
|
|
|
|
(format
|
|
|
|
|
(concat
|
|
|
|
|
"* %s%%?\n"
|
|
|
|
|
":PROPERTIES:\n"
|
|
|
|
|
":EMAIL:%s\n"
|
|
|
|
|
":NICK:\n"
|
|
|
|
|
":BIRTHDAY:\n"
|
|
|
|
|
":END:\n\n")
|
|
|
|
|
(or name email "")
|
|
|
|
|
(or email "")))
|
|
|
|
|
(key "mu4e-add-org-contact-key")
|
|
|
|
|
(org-capture-templates
|
|
|
|
|
(append org-capture-templates
|
|
|
|
|
(list (list key "contacts" 'entry
|
|
|
|
|
(list 'file mu4e-org-contacts-file) blurb)))))
|
|
|
|
|
(message "%S" org-capture-templates)
|
2012-06-10 11:21:41 +02:00
|
|
|
|
(when (fboundp 'org-capture)
|
|
|
|
|
(org-capture nil key))))
|
2012-05-12 16:10:57 +02:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
|
|
|
|
|
2012-10-29 11:26:17 +01:00
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
(defun mu4e-action-git-apply-patch (msg)
|
|
|
|
|
"Apply the git [patch] message."
|
2013-10-03 10:21:09 +02:00
|
|
|
|
(let ((path (ido-read-directory-name "Target directory: "
|
|
|
|
|
(car ido-work-directory-list)
|
|
|
|
|
"~/" t)))
|
|
|
|
|
(add-to-list 'ido-work-directory-list path)
|
2012-10-29 11:26:17 +01:00
|
|
|
|
(shell-command
|
|
|
|
|
(format "cd %s; git apply %s"
|
|
|
|
|
path
|
|
|
|
|
(mu4e-message-field msg :path)))))
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
2012-04-24 17:11:56 +02:00
|
|
|
|
|
2012-12-06 19:14:05 +01:00
|
|
|
|
|
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
(defvar mu4e-action-tags-header "X-Keywords"
|
2012-12-15 11:06:32 +01:00
|
|
|
|
"Header where tags are stored. Used by `mu4e-action-retag-message'.
|
|
|
|
|
Make sure it is one of the headers mu recognizes for storing
|
|
|
|
|
tags: X-Keywords, X-Label, Keywords. Also note that changing
|
|
|
|
|
this setting on already tagged messages can lead to messages
|
2013-03-17 10:58:08 +01:00
|
|
|
|
with multiple tags headers.")
|
2012-12-06 19:14:05 +01:00
|
|
|
|
|
2013-03-16 18:42:41 +01:00
|
|
|
|
(defun mu4e~contains-line-matching (regexp path)
|
2013-03-11 22:36:36 +01:00
|
|
|
|
"Determine whether the file at path contains a line matching
|
|
|
|
|
the given regexp."
|
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(insert-file-contents path)
|
2013-03-17 18:45:16 +01:00
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(if (re-search-forward regexp nil t)
|
|
|
|
|
t
|
|
|
|
|
nil))))
|
2013-03-11 22:36:36 +01:00
|
|
|
|
|
2013-03-16 18:42:41 +01:00
|
|
|
|
(defun mu4e~replace-first-line-matching (regexp to-string path)
|
2013-03-11 22:36:36 +01:00
|
|
|
|
"Replace the first line in the file at path that matches regexp
|
2013-03-17 10:58:08 +01:00
|
|
|
|
with the string replace."
|
2013-03-11 22:36:36 +01:00
|
|
|
|
(with-temp-file path
|
|
|
|
|
(insert-file-contents path)
|
2013-03-17 18:45:16 +01:00
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char (point-min))
|
2013-03-17 10:58:08 +01:00
|
|
|
|
(if (re-search-forward regexp nil t)
|
|
|
|
|
(replace-match to-string nil nil)))))
|
2013-03-11 22:36:36 +01:00
|
|
|
|
|
2012-12-15 11:06:32 +01:00
|
|
|
|
(defun mu4e-action-retag-message (msg &optional retag-arg)
|
2012-12-06 19:14:05 +01:00
|
|
|
|
"Change tags of a message. Example: +tag \"+long tag\" -oldtag
|
|
|
|
|
adds 'tag' and 'long tag', and removes oldtag."
|
2012-12-15 11:06:32 +01:00
|
|
|
|
(let* ((retag (or retag-arg (read-string "Tags: ")))
|
2012-12-09 14:07:21 +01:00
|
|
|
|
(path (mu4e-message-field msg :path))
|
|
|
|
|
(maildir (mu4e-message-field msg :maildir))
|
|
|
|
|
(oldtags (mu4e-message-field msg :tags))
|
|
|
|
|
(header mu4e-action-tags-header)
|
|
|
|
|
(sep (cond ((string= header "Keywords") " ")
|
|
|
|
|
((string= header "X-Label") " ")
|
|
|
|
|
((string= header "X-Keywords") ", ")
|
|
|
|
|
(t ", ")))
|
|
|
|
|
(taglist (if oldtags (copy-sequence oldtags) '()))
|
|
|
|
|
tagstr)
|
2012-12-06 19:14:05 +01:00
|
|
|
|
(dolist (tag (split-string-and-unquote retag) taglist)
|
2013-03-16 16:58:17 +01:00
|
|
|
|
(cond
|
2013-03-17 10:27:42 +01:00
|
|
|
|
((string-match "^\\+\\(.+\\)" tag)
|
2013-03-16 16:58:17 +01:00
|
|
|
|
(setq taglist (push (match-string 1 tag) taglist)))
|
2013-03-17 10:27:42 +01:00
|
|
|
|
((string-match "^\\-\\(.+\\)" tag)
|
2012-12-09 14:07:21 +01:00
|
|
|
|
(setq taglist (delete (match-string 1 tag) taglist)))
|
|
|
|
|
(t
|
|
|
|
|
(setq taglist (push tag taglist)))))
|
2013-03-17 18:45:16 +01:00
|
|
|
|
|
2012-12-06 19:14:05 +01:00
|
|
|
|
(setq taglist (sort (delete-dups taglist) 'string<))
|
|
|
|
|
(setq tagstr (mapconcat 'identity taglist sep))
|
2013-03-17 18:45:16 +01:00
|
|
|
|
|
2013-03-11 22:36:36 +01:00
|
|
|
|
(setq tagstr (replace-regexp-in-string "[\\&]" "\\\\\\&" tagstr))
|
|
|
|
|
(setq tagstr (replace-regexp-in-string "[/]" "\\&" tagstr))
|
2013-03-17 18:45:16 +01:00
|
|
|
|
|
2013-03-16 18:42:41 +01:00
|
|
|
|
(if (not (mu4e~contains-line-matching (concat header ":.*") path))
|
2013-03-17 10:58:08 +01:00
|
|
|
|
;; Add tags header just before the content
|
|
|
|
|
(mu4e~replace-first-line-matching
|
|
|
|
|
"^$" (concat header ": " tagstr "\n") path)
|
2013-03-17 18:45:16 +01:00
|
|
|
|
|
2013-03-11 22:36:36 +01:00
|
|
|
|
;; replaces keywords, restricted to the header
|
2013-03-16 18:42:41 +01:00
|
|
|
|
(mu4e~replace-first-line-matching
|
2013-03-17 10:58:08 +01:00
|
|
|
|
(concat header ":.*")
|
|
|
|
|
(concat header ": " tagstr)
|
2013-03-11 22:36:36 +01:00
|
|
|
|
path))
|
2013-03-17 18:45:16 +01:00
|
|
|
|
|
2012-12-15 11:06:32 +01:00
|
|
|
|
(mu4e-message (concat "tagging: " (mapconcat 'identity taglist ", ")))
|
2012-12-09 14:07:21 +01:00
|
|
|
|
(mu4e-refresh-message path maildir)))
|
2012-12-06 19:14:05 +01:00
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
|
|
|
|
|
2012-04-24 17:11:56 +02:00
|
|
|
|
(provide 'mu4e-actions)
|