summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2024-04-26 17:05:19 -0500
committerCraig Jennings <c@cjennings.net>2024-04-26 17:05:19 -0500
commit95d3a96bf150b1349dd73479e2a117b1cf95c6bd (patch)
treea800d9c36e7e4f86bd5baa847a2e20033c9f3564
parent878031616404c1b6ad6ac63ca9db9a5951c297b3 (diff)
added mu4e view save all attachmentsv0.7
replaces extract keybinding so user saves all attachments instead of each singly. also add it to view action menu
-rw-r--r--TODO.org145
-rw-r--r--modules/mail-config.el121
2 files changed, 159 insertions, 107 deletions
diff --git a/TODO.org b/TODO.org
index 449ec7ea..5e1b1027 100644
--- a/TODO.org
+++ b/TODO.org
@@ -786,76 +786,7 @@ This is the result of overriding that function in cj/invincible-buffers in syste
*** DONE [#C] Bookmark-save-flag Replaces Bookmark-set-and-save
CLOSED: [2024-04-03 Wed 15:00]
remove cj/bookmark-set-and-save if setting bookmark-save-flat to 1 saves when adding/modifying a bookmark
-** DOING Open v0.7 Fit and Finish (ends 2024.04.20) [0/2]
-*** DOING [#B] Save All Attachments Working in Mu4e
-**** 2024-04-18 Thu @ 05:55:32 -0500 The Documentation Suggests A Path Forward
-This suggests that if I'm writing another function, I should
-- first query the user to select/create the directory
-- make sure mu4e-view-completion-minor-mode is on
-- call mu4e-view-complete-all to get all the files
-- within the context of mu4e-view-save-attachments
-
-#+BEGIN_QUOTE
-E-mail messages can be though as a series of “MIME-parts”, which are sections of the message. The most prominent is the ’body’, that is the main message your are reading. Many e-mail messages also contains attachments, which MIME-parts that contain files10.
-
-To save such attachments as files on your file systems, the mu4e message-view offers the command mu4e-view-save-attachments; default keybinding is e (think extract). After invoking the command, you can enter the file names to save, comma-separated, and using the completion support. Press RET to save the chosen files to your file-system.
-
-With a prefix argument, you get to choose the target-directory, otherwise, mu4e determines it following the variable mu4e-attachment-dir (which can be file-system path or a function; see its docstring for details.
-
-While completing, mu4e-view-completion-minor-mode is active, which offers mu4e-view-complete-all (bound to C-c C-a to complete all files11.
-#+END_QUOTE
-[[https://www.djcbsoftware.nl/code/mu/mu4e/MSGV-Attachments-and-MIME_002dparts.html][MSGV Attachments and MIME-parts (Mu4e 1.12.3 user manual)]]
-
-**** 2024-04-18 Thu @ 05:05:03 -0500 Etienne's config failed
-It relies on this line to find all the mime-parts:
-
-(let ((parts (mu4e--view-gather-mime-parts))
-
-and that doesn't exist any longer.
-**** Test Etienne's Configuration
-https://etienne.depar.is/emacs.d/mu4e.html
-(defun ed/mu4e-view-save-all-attachments (&optional arg)
- "Save all attachments of a given message.
-
-If ARG is nil, all attachments will be saved in
-`mu4e-attachment-dir'. When non-nil, user will be prompted to
-choose a specific directory where to save all the files."
- (interactive "P")
- (when (and (eq major-mode 'mu4e-view-mode)
- (derived-mode-p 'gnus-article-mode))
- (let ((parts (mu4e--view-gather-mime-parts))
- (handles '())
- (files '())
- (directory (if arg
- (read-directory-name "Save to directory: ")
- mu4e-attachment-dir)))
- (dolist (part parts)
- (let ((fname (or (cdr (assoc 'filename (assoc "attachment" (cdr part))))
- (cl-loop for item in part
- for name = (and (listp item) (assoc-default 'name item))
- thereis (and (stringp name) name)))))
- (when fname
- (push `(,fname . ,(cdr part)) handles)
- (push fname files))))
- (if files
- (cl-loop for (f . h) in handles
- when (member f files)
- do (mm-save-part-to-file
- h (let ((file (expand-file-name f directory)))
- (if (file-exists-p file)
- (let (newname (count 1))
- (while (and
- (setq newname
- (format "%s-%s%s"
- (file-name-sans-extension file)
- count
- (file-name-extension file t)))
- (file-exists-p newname))
- (cl-incf count))
- newname)
- file))))
- (mu4e-message "No attached files found")))))
-(define-key mu4e-view-mode-map "X" #'ed/mu4e-view-save-all-attachments)
+** DOING Open v0.7 Fit and Finish (ends 2024.04.20) [0/1]
*** TODO [#B] Get queued email working for both mail accounts
**** using sendmail for gmail
#+BEGIN_QUOTE
@@ -1269,6 +1200,80 @@ This involves changing webclipper. Webclipper as it is now doesn't return the co
What I want is something to return the content in the way that a template would expect it. This way, I can create templates that would work with org-roam as well, such as creating a separate org-roam node just by clicking an org-protocol bookmarklet.
This might be too large to fit as a bug fix for v0.7. I began doing some research (below) yesterday to help me size the work.
+**** DONE [#B] Save All Attachments Working in Mu4e
+CLOSED: [2024-04-26 Fri 17:04]
+:LOGBOOK:
+- State "DONE" from "DOING" [2024-04-26 Fri 17:04]
+:END:
+
+***** 2024-04-18 Thu @ 05:55:32 -0500 The Documentation Suggests A Path Forward
+This suggests that if I'm writing another function, I should
+- first query the user to select/create the directory
+- make sure mu4e-view-completion-minor-mode is on
+- call mu4e-view-complete-all to get all the files
+- within the context of mu4e-view-save-attachments
+
+#+BEGIN_QUOTE
+E-mail messages can be though as a series of “MIME-parts”, which are sections of the message. The most prominent is the ’body’, that is the main message your are reading. Many e-mail messages also contains attachments, which MIME-parts that contain files10.
+
+To save such attachments as files on your file systems, the mu4e message-view offers the command mu4e-view-save-attachments; default keybinding is e (think extract). After invoking the command, you can enter the file names to save, comma-separated, and using the completion support. Press RET to save the chosen files to your file-system.
+
+With a prefix argument, you get to choose the target-directory, otherwise, mu4e determines it following the variable mu4e-attachment-dir (which can be file-system path or a function; see its docstring for details.
+
+While completing, mu4e-view-completion-minor-mode is active, which offers mu4e-view-complete-all (bound to C-c C-a to complete all files11.
+#+END_QUOTE
+[[https://www.djcbsoftware.nl/code/mu/mu4e/MSGV-Attachments-and-MIME_002dparts.html][MSGV Attachments and MIME-parts (Mu4e 1.12.3 user manual)]]
+
+***** 2024-04-18 Thu @ 05:05:03 -0500 Etienne's config failed
+It relies on this line to find all the mime-parts:
+
+(let ((parts (mu4e--view-gather-mime-parts))
+
+and that doesn't exist any longer.
+***** Test Etienne's Configuration
+https://etienne.depar.is/emacs.d/mu4e.html
+(defun ed/mu4e-view-save-all-attachments (&optional arg)
+ "Save all attachments of a given message.
+
+If ARG is nil, all attachments will be saved in
+`mu4e-attachment-dir'. When non-nil, user will be prompted to
+choose a specific directory where to save all the files."
+ (interactive "P")
+ (when (and (eq major-mode 'mu4e-view-mode)
+ (derived-mode-p 'gnus-article-mode))
+ (let ((parts (mu4e--view-gather-mime-parts))
+ (handles '())
+ (files '())
+ (directory (if arg
+ (read-directory-name "Save to directory: ")
+ mu4e-attachment-dir)))
+ (dolist (part parts)
+ (let ((fname (or (cdr (assoc 'filename (assoc "attachment" (cdr part))))
+ (cl-loop for item in part
+ for name = (and (listp item) (assoc-default 'name item))
+ thereis (and (stringp name) name)))))
+ (when fname
+ (push `(,fname . ,(cdr part)) handles)
+ (push fname files))))
+ (if files
+ (cl-loop for (f . h) in handles
+ when (member f files)
+ do (mm-save-part-to-file
+ h (let ((file (expand-file-name f directory)))
+ (if (file-exists-p file)
+ (let (newname (count 1))
+ (while (and
+ (setq newname
+ (format "%s-%s%s"
+ (file-name-sans-extension file)
+ count
+ (file-name-extension file t)))
+ (file-exists-p newname))
+ (cl-incf count))
+ newname)
+ file))))
+ (mu4e-message "No attached files found")))))
+(define-key mu4e-view-mode-map "X" #'ed/mu4e-view-save-all-attachments)
** DOING Complete v0.7 Release Checklist [8/11]
*** DOING Fit and Finish Period
*** TODO Clean Launch from Archsetup
diff --git a/modules/mail-config.el b/modules/mail-config.el
index 2d9288ac..64437155 100644
--- a/modules/mail-config.el
+++ b/modules/mail-config.el
@@ -9,6 +9,73 @@
;;; Code:
+;; ----------------------- Mu4e View Save All Attachments ----------------------
+;; replacement for the extract function which saves all attachments
+
+(defun cj/mu4e-view-save-all-attachments (&optional msg)
+ "Save all attachments from the current email.
+Prompt user for directory, creating if necessary, select all attachments in the
+email, and save them in the specified directory. The optional MSG is only
+provided when calling this function from mu4e's action context. This function is
+intended to be used as a `mu4e-view-action' and/or bound to a key in
+mu4e-view-mode-map."
+ (interactive)
+ (let ((msg (or msg (mu4e-message-at-point))))
+ (let* ((parts (mu4e-view-mime-parts))
+ ;; build cons list of candidate mime parts
+ (candidates (seq-map
+ (lambda (attachment-part)
+ (cons ;; (filename . annotation)
+ (plist-get attachment-part :filename)
+ attachment-part))
+ ;; scope to parts with a filename
+ (seq-filter
+ (lambda (part) (plist-get part :attachment-like))
+ parts)))
+ (candidates (or candidates (mu4e-warn "No attachments for this message")))
+ (files (mapcar 'car candidates)) ;; Select all attachments
+ (custom-dir (read-directory-name "Save to directory: ")))
+ (unless (file-exists-p custom-dir)
+ (make-directory custom-dir t)) ;; t creates parent dirs if needed
+ ;; iterate over each file
+ (seq-do
+ (lambda (fname)
+ (let* ((part (cdr (assoc fname candidates)))
+ ;; build unique full file path
+ (path (mu4e--uniqify-file-name
+ (mu4e-join-paths
+ custom-dir
+ (plist-get part :filename)))))
+ ;; save the file
+ (mm-save-part-to-file (plist-get part :handle) path)))
+ files))))
+
+;; ------------------------- Mark All Headers ------------------------
+;; convenience function to mark all headers for an action
+
+(defun cj/mu4e-mark-all-headers ()
+ "Mark all headers for a later action.
+Prompts user for the action when executing."
+ (interactive)
+ (mu4e-headers-mark-for-each-if
+ (cons 'something nil)
+ (lambda (_msg _param) t)))
+
+;;; ------------------ Smtpmail & Easy PG Assistant -----------------
+;; send mail to smtp host from smtpmail temp buffer.
+(use-package smtpmail
+ :ensure nil ;; built-in
+ :defer .5
+ :config
+ (setq message-kill-buffer-on-exit t) ;; don't keep compose buffers after sending
+ (setq sendmail-program (executable-find "msmtp"))
+ (setq send-mail-function 'message-send-mail-with-sendmail
+ message-send-mail-function 'message-send-mail-with-sendmail)
+ (setq message-sendmail-envelope-from 'header)
+ (setq smtpmail-debug-info t))
+
+;; --------------------------------- Mu4e Email --------------------------------
+
(use-package mu4e
:ensure nil ;; mu4e gets installed by installing 'mu' via the system package manager
:load-path "/usr/share/emacs/site-lisp/mu4e/"
@@ -18,10 +85,11 @@
(:map mu4e-headers-mode-map
("M" . cj/mu4e-mark-all-headers)
("D" . mu4e-headers-mark-for-trash)
- ("d" . mu4e-headers-mark-for-delete))
+ ("d" . mu4e-headers-mark-for-delete))
(:map mu4e-view-mode-map
- ("r" . mu4e-compose-wide-reply)
- ("R" . mu4e-compose-reply))
+ ("r" . mu4e-compose-wide-reply)
+ ("R" . mu4e-compose-reply)
+ ("e" . cj/mu4e-view-save-all-attachments))
:hook
(mu4e-view-mode . turn-on-visual-line-mode)
:config
@@ -52,7 +120,6 @@
(setq mu4e-contexts
(list
-
(make-mu4e-context
:name "gmail.com"
:match-func
@@ -115,6 +182,10 @@
"My settings for message composition."
(set-fill-column 72)))
+ ;; add save-all-attachments to view actions list
+ (add-to-list 'mu4e-view-actions
+ '("save all attachments" . cj/mu4e-view-save-all-attachments))
+
;; Always BCC myself
;; http://www.djcbsoftware.nl/code/mu/mu4e/Compose-hooks.html
(defun cj/add-header ()
@@ -133,21 +204,22 @@
;; use imagemagick to render images, if available
(when (fboundp 'imagemagick-register-types)
- (imagemagick-register-types)))
+ (imagemagick-register-types))
+
+ ;; xwidgets not able to be built into emacs on linux
+ ;; ;; view in xwidget html rendererer
+ ;; (add-to-list 'mu4e-headers-actions
+ ;; '("xWidget" . mu4e-action-view-with-xwidget) t)
+ ;; (add-to-list 'mu4e-view-actions
+ ;; '("xWidget" . mu4e-action-view-with-xwidget) t))
-;; xwidgets not able to be built into emacs on linux
-;; ;; view in xwidget html rendererer
-;; (add-to-list 'mu4e-headers-actions
-;; '("xWidget" . mu4e-action-view-with-xwidget) t)
-;; (add-to-list 'mu4e-view-actions
-;; '("xWidget" . mu4e-action-view-with-xwidget) t))
+ ) ;; end use-package mu4e
(defun no-auto-fill ()
"Turn off \'auto-fill-mode\'."
(auto-fill-mode -1))
(add-hook 'mu4e-compose-mode-hook #'no-auto-fill)
-
;; ----------------------------- Compose Mode Hydra ----------------------------
;; WIP: menu available in compose mode
@@ -163,30 +235,5 @@
(local-set-key (kbd "C-c ?") 'hydra-mu4e-compose/body))
(add-hook 'mu4e-compose-mode-hook 'mu4e-compose-mode-hook-hydra-setup)
-;; ------------------------- Mark All Headers ------------------------
-;; convenience function to mark all headers for an action
-
-(defun cj/mu4e-mark-all-headers ()
- "Mark all headers for a later action.
-Prompts user for the action when executing."
- (interactive)
- (mu4e-headers-mark-for-each-if
- (cons 'something nil)
- (lambda (_msg _param) t)))
-
-;;; ------------------ Smtpmail & Easy PG Assistant -----------------
-
-;; send mail to smtp host from smtpmail temp buffer.
-(use-package smtpmail
- :ensure nil ;; built-in
- :defer .5
- :config
- (setq message-kill-buffer-on-exit t) ;; don't keep compose buffers after sending
- (setq sendmail-program (executable-find "msmtp"))
- (setq send-mail-function 'message-send-mail-with-sendmail
- message-send-mail-function 'message-send-mail-with-sendmail)
- (setq message-sendmail-envelope-from 'header)
- (setq smtpmail-debug-info t))
-
(provide 'mail-config)
;;; mail-config.el ends here