From 1078fee2c5fc67bdb8c2aab2c8082b0d9b17ac78 Mon Sep 17 00:00:00 2001 From: Christophe Troestler Date: Sat, 13 Apr 2019 00:53:36 +0200 Subject: [PATCH] mu4e (ical): Allow to reply to icalendar invitations Fixes https://github.com/djcb/mu/issues/994 --- mu4e/Makefile.am | 1 + mu4e/mu4e-draft.el | 19 +++++---- mu4e/mu4e-icalendar.el | 92 ++++++++++++++++++++++++++++++++++++++++++ mu4e/mu4e-vars.el | 8 ++++ mu4e/mu4e-view.el | 3 +- 5 files changed, 114 insertions(+), 9 deletions(-) create mode 100644 mu4e/mu4e-icalendar.el diff --git a/mu4e/Makefile.am b/mu4e/Makefile.am index ebccf26c..b84bb487 100644 --- a/mu4e/Makefile.am +++ b/mu4e/Makefile.am @@ -28,6 +28,7 @@ dist_lisp_LISP= \ mu4e-contrib.el \ mu4e-draft.el \ mu4e-headers.el \ + mu4e-icalendar.el \ mu4e-lists.el \ mu4e-main.el \ mu4e-mark.el \ diff --git a/mu4e/mu4e-draft.el b/mu4e/mu4e-draft.el index 3b7679c1..2b221644 100644 --- a/mu4e/mu4e-draft.el +++ b/mu4e/mu4e-draft.el @@ -413,14 +413,17 @@ You can append flags." Replying-to-self is special; in that case, the To and Cc fields will be the same as in the original." (let* ((reply-to-self (mu4e-message-contact-field-matches-me origmsg :from)) - (recipnum - (+ (length (mu4e~draft-create-to-lst origmsg)) - (length (mu4e~draft-create-cc-lst origmsg t)))) - ;; reply-to-self implies reply-all - (reply-all (or reply-to-self (mu4e~draft-reply-all-p origmsg))) - (old-msgid (plist-get origmsg :message-id)) - (subject (concat mu4e~draft-reply-prefix - (message-strip-subject-re + (recipnum + (+ (length (mu4e~draft-create-to-lst origmsg)) + (length (mu4e~draft-create-cc-lst origmsg t)))) + ;; reply-to-self implies reply-all + (reply-all (or reply-to-self + (eq mu4e-compose-reply-recipients 'all) + (and (not (eq mu4e-compose-reply-recipients 'sender)) + (mu4e~draft-reply-all-p origmsg)))) + (old-msgid (plist-get origmsg :message-id)) + (subject (concat mu4e~draft-reply-prefix + (message-strip-subject-re (or (plist-get origmsg :subject) ""))))) (concat (mu4e~draft-header "From" (or (mu4e~draft-from-construct) "")) diff --git a/mu4e/mu4e-icalendar.el b/mu4e/mu4e-icalendar.el new file mode 100644 index 00000000..84f5d58a --- /dev/null +++ b/mu4e/mu4e-icalendar.el @@ -0,0 +1,92 @@ +;;; mu4e-icalendar.el --- reply to iCalendar meeting requests + +;;; Commentary: + +;; To install: +;; (require 'gnus-icalendar) +;; (gnus-icalendar-setup) + +;; to enable optional iCalendar->Org sync functionality +;; NOTE: both the capture file and the headline(s) inside must already exist +;; (setq gnus-icalendar-org-capture-file "~/org/notes.org") +;; (setq gnus-icalendar-org-capture-headline '("Calendar")) +;; (gnus-icalendar-org-setup) + +(require 'mu4e) +(require 'gnus-icalendar) + +(eval-when-compile (require 'cl)) + + +(defun mu4e-icalendar-setup () + (gnus-icalendar-setup) + (cl-defmethod gnus-icalendar-event:inline-reply-buttons :around + ((event gnus-icalendar-event) handle) + (if (and (boundp 'mu4e~view-rendering) + (gnus-icalendar-event:rsvp event)) + `(("Accept" mu4e-icalendar-reply (,handle accepted ,event)) + ("Tentative" mu4e-icalendar-reply (,handle tentative ,event)) + ("Decline" mu4e-icalendar-reply (,handle declined ,event))) + (cl-call-next-method event handle)))) + +(defun mu4e-icalendar-reply (data) + "Reply to a text/calendar event." + ;; Based on `gnus-icalendar-reply'. + (let* ((handle (car data)) + (status (cadr data)) + (event (caddr data)) + (reply (gnus-icalendar-with-decoded-handle handle + (gnus-icalendar-event-reply-from-buffer + (current-buffer) status (gnus-icalendar-identities)))) + (msg (mu4e-message-at-point 'noerror))) + + (when reply + (cl-labels + ((fold-icalendar-buffer + () + (goto-char (point-min)) + (while (re-search-forward "^\\(.\\{72\\}\\)\\(.+\\)$" nil t) + (replace-match "\\1\n \\2") + (goto-char (line-beginning-position))))) + (let ((subject (concat (capitalize (symbol-name status)) + ": " (gnus-icalendar-event:summary event)))) + + (with-current-buffer (get-buffer-create gnus-icalendar-reply-bufname) + (delete-region (point-min) (point-max)) + (insert reply) + (fold-icalendar-buffer) + (mu4e-icalendar-reply-with-buffer msg subject (buffer-name))) + + ;; Back in article buffer + (setq-local gnus-icalendar-reply-status status) + (when gnus-icalendar-org-enabled-p + (gnus-icalendar--update-org-event event status) + ;; refresh article buffer to update the reply status + (with-current-buffer mu4e~headers-buffer-name + (mu4e-headers-rerun-search)))))))) + +(defun mu4e~icalendar-delete-citation () + (delete-region (point-min) (point-max))) + +(defun mu4e-icalendar-reply-with-buffer (original-msg subject buffer-name) + (let ((message-signature nil)) + (let ((mu4e-compose-cite-function #'mu4e~icalendar-delete-citation) + (mu4e-sent-messages-behavior 'delete) + (mu4e-compose-reply-recipients 'sender)) + ;; FIXME: only reply to the original sender (do not ask) + (mu4e~compose-handler 'reply original-msg)) + (message-goto-body) + (insert "\n\n") + (mml-insert-multipart "alternative") + (mml-insert-empty-tag 'part 'type "text/plain") + (mml-attach-buffer buffer-name "text/calendar; method=REPLY; charset=UTF-8") + (message-goto-subject) + (delete-region (line-beginning-position) (line-end-position)) + (insert "Subject: " subject) +; (message-send-and-exit) + )) + + + +(provide 'mu4e-icalendar) +;;; mu4e-icalendar.el ends here diff --git a/mu4e/mu4e-vars.el b/mu4e/mu4e-vars.el index afae182d..5a0dd0f8 100644 --- a/mu4e/mu4e-vars.el +++ b/mu4e/mu4e-vars.el @@ -405,6 +405,14 @@ predicate function. A value of nil keeps all the addresses." (repeat string)) :group 'mu4e-compose) +(defcustom mu4e-compose-reply-recipients 'ask + "Which recipients to use when replying to a message. +May be 'ask, 'all, 'sender." + :type '(choice ask + all + sender) + :group 'mu4e-compose) + (defcustom mu4e-compose-reply-to-address nil "The Reply-To address. Useful when this is not equal to the From: address." diff --git a/mu4e/mu4e-view.el b/mu4e/mu4e-view.el index 0da6e0ad..3f4bcc21 100644 --- a/mu4e/mu4e-view.el +++ b/mu4e/mu4e-view.el @@ -381,7 +381,8 @@ article-mode." gnus-summary-buffer (get-buffer-create " *appease-gnus*") gnus-original-article-buffer (current-buffer)) (run-hooks 'gnus-article-decode-hook) - (let ((max-specpdl-size mu4e-view-max-specpdl-size)) + (let ((mu4e~view-rendering t) ; customize gnus in mu4e + (max-specpdl-size mu4e-view-max-specpdl-size)) (gnus-article-prepare-display)) (mu4e-view-mode) (setq mu4e~view-message msg)