This is part of the Emacs Starter Kit.
(if (executable-find "mu")
(progn
;; (add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e")
(require 'mu4e)
(require 'smtpmail)
;;(setq mu4e-maildir "/home/garrido/.Maildir")
;; set mu4e as default email client
(setq mail-user-agent 'mu4e-user-agent)
;; allow for updating mail using 'U' in the main view:
(when (executable-find "myofflineimap")
(setq mu4e-get-mail-command "myofflineimap"))
;; .offlineimaprc is a unix conf file
(add-to-list 'auto-mode-alist '("\\.offlineimaprc" . conf-mode))
;; don't save message to Sent Messages, Gmail/IMAP takes care of this
;; (setq mu4e-sent-messages-behavior 'delete)
;; save attachment to my desktop (this can also be a function)
(setq mu4e-attachment-dir "/tmp/")
;; attempt to show images when viewing messages
(setq mu4e-view-show-images t)
(setq mu4e-confirm-quit nil
mu4e-headers-date-format "%d-%m-%Y %H:%M")
;; don't keep message buffers around
(setq message-kill-buffer-on-exit t)
;; Check for mail every 3 minutes
(setq mu4e-update-interval (* 3 60))
;; disable minibuffer message
(setq mu4e-hide-index-messages t)
;; always show mail adress
(setq mu4e-view-show-addresses t)
;; remove duplicate message in header view
(setq mu4e-headers-skip-duplicates t)
;; try to render html mail
;;(setq mu4e-view-prefer-html t)
;; (when (executable-find "html2text")
;; (setq mu4e-html2text-command "html2text -utf8 -nobs -width 72"))
(require 'mu4e-contrib)
(setq mu4e-html2text-command 'mu4e-shr2text)
(add-hook 'mu4e-view-mode-hook
(lambda()
;; try to emulate some of the eww key-bindings
(local-set-key (kbd "<tab>") 'shr-next-link)
(local-set-key (kbd "<backtab>") 'shr-previous-link)))
(add-to-list 'mu4e-view-actions
'("ViewInBrowser" . mu4e-action-view-in-browser) t)
;; split vertically when reading mail
(setq mu4e-split-view 'vertical)
;; header fields
(setq mu4e-headers-fields
'( (:date . 18)
(:flags . 4)
(:from-or-to . 22)
(:subject . nil)))
;; (setq mu4e-view-use-gnus t)
;; (setq
;; mu4e-index-cleanup nil ;; don't do a full cleanup check
;; mu4e-index-lazy-check t) ;; don't consider up-to-date dirs
(setq doc-view-cache-directory "/tmp/docview")
Basically change nothing except uparrow and downarrow (flags are hidden)
(defconst mu4e-headers-from-or-to-prefix '("" . ""))
(setq mu4e-use-fancy-chars t)
(setq mu4e-headers-has-child-prefix '("+" . "+"))
(setq mu4e-headers-empty-parent-prefix '("-" . "-"))
(setq mu4e-headers-first-child-prefix '("\\" . "\\"))
(setq mu4e-headers-duplicate-prefix '("=" . "="))
(setq mu4e-headers-default-prefix '("|" . "|"))
(setq mu4e-headers-unread-mark '("u" . ""))
(setq mu4e-headers-attach-mark '("a" . "+"))
(setq mu4e-headers-flagged-mark '("F" . "!"))
(setq mu4e-headers-new-mark '("N" . "*"))
(setq mu4e-headers-passed-mark '("P" . "»"))
(setq mu4e-headers-replied-mark '("R" . "«"))
(setq mu4e-headers-seen-mark '("S" . ""))
(setq mu4e-headers-trashed-mark '("T" . ""))
(setq mu4e-headers-signed-mark '("s" . ""))
(setq mu4e-headers-thread-child-prefix '("├>" . "↳ "))
(setq mu4e-headers-thread-last-child-prefix '("└>" . "↳ "))
(setq mu4e-headers-thread-connection-prefix '("│" . " "))
(setq mu4e-headers-thread-orphan-prefix '("┬>" . "↳ "))
(setq mu4e-headers-thread-single-orphan-prefix '("─>" . "↦ "))
(setq mu4e-headers-threaded-label '("T" . ""))
(setq mu4e-headers-full-label '("F" . "∀"))
(setq mu4e-headers-related-label '("R" . ""))
(setq mu4e-headers-list-mark '("s" . ""))
(setq mu4e-headers-personal-mark '("p" . ""))
(setq mu4e-headers-unread-mark '("u" . "📩 "))
(setq mu4e-headers-draft-mark '("D" . "🚧 "))
(setq mu4e-headers-flagged-mark '("F" . "🚩 "))
(setq mu4e-headers-new-mark '("N" . "📩 "))
(setq mu4e-headers-passed-mark '("P" . "↪ "))
(setq mu4e-headers-replied-mark '("R" . "↩ "))
(setq mu4e-headers-seen-mark '("S" . "📩 "))
(setq mu4e-headers-trashed-mark '("T" . "🗑️"))
(setq mu4e-headers-attach-mark '("a" . "📎 "))
(setq mu4e-headers-encrypted-mark '("x" . "🔑 "))
;; (setq
;; mu4e-headers-draft-mark '("D" . "💈")
;; mu4e-headers-flagged-mark '("F" . "📍")
;; mu4e-headers-new-mark '("N" . "🔥")
;; mu4e-headers-passed-mark '("P" . "❯")
;; mu4e-headers-replied-mark '("R" . "❮")
;; mu4e-headers-seen-mark '("S" . "☑")
;; mu4e-headers-trashed-mark '("T" . "💀")
;; mu4e-headers-attach-mark '("a" . "📎")
;; mu4e-headers-encrypted-mark '("x" . "🔒")
;; mu4e-headers-signed-mark '("s" . "🔑")
;; mu4e-headers-unread-mark '("u" . "⎕")
;; mu4e-headers-list-mark '("s" . "🔈")
;; mu4e-headers-personal-mark '("p" . "👨")
;; mu4e-headers-calendar-mark '("c" . "📅"))
(defun sk-select-and-insert-contact (&optional start)
(interactive)
(let ((mail-abbrev-mode-regexp mu4e~compose-address-fields-regexp)
(eoh ;; end-of-headers
(save-excursion
(goto-char (point-min))
(search-forward-regexp mail-header-separator nil t))))
(when (and eoh (> eoh (point)) (mail-abbrev-in-expansion-header-p))
(let* ((end (point))
(start
(or start
(save-excursion
(re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*")
(goto-char (match-end 0))
(point))))
(contact
(ido-completing-read "Contact: "
mu4e~contacts-for-completion
nil
nil
(buffer-substring-no-properties start end))))
(unless (equal contact "")
(kill-region start end)
(insert contact))))))
;;store org-mode links to messages
;; (require 'org-mu4e)
;;store link to message if in header view, not to header query
(setq org-mu4e-link-query-in-headers-mode nil)
;; when mail is sent, automatically convert org body to HTML
(setq org-mu4e-convert-to-html t)
(define-key mu4e-headers-mode-map (kbd "C-c l") 'org-store-link)
(add-hook 'message-mode-hook
(lambda ()
(yas-minor-mode t)
(auto-complete-mode t)
(set-input-method "TeX")))
(require 'org-mime)
(defun org-mime-org-buffer-htmlize ()
"Create an email buffer containing the current org-mode file
exported to html and encoded in both html and in org formats as
mime alternatives."
(interactive)
(org-mime-send-buffer 'html)
(message-goto-to))
(defun org-mime-subtree ()
"Create an email buffer containing the current org-mode subtree
exported to a org format or to the format specified by the
MAIL_FMT property of the subtree."
(interactive)
(org-mime-send-subtree
(or (org-entry-get nil "MAIL_FMT" org-mime-use-property-inheritance) 'org))
(message-goto-to))
(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)
(message-send-and-exit)))
(defun org-mime-compose (body fmt file &optional to subject headers)
(require 'message)
(let ((bhook
(lambda (body fmt)
(let ((hook (intern (concat "org-mime-pre-"
(symbol-name fmt)
"-hook"))))
(if (> (eval `(length ,hook)) 0)
(with-temp-buffer
(insert body)
(goto-char (point-min))
(eval `(run-hooks ',hook))
(buffer-string))
body))))
(fmt (if (symbolp fmt) fmt (intern fmt)))
(files (org-element-map (org-element-parse-buffer) 'link
(lambda (link)
(when (string= (org-element-property :type link) "file")
(file-truename (org-element-property :path link)))))))
(compose-mail to subject headers nil)
(message-goto-body)
(cond
((eq fmt 'org)
(require 'ox-org)
(insert (org-export-string-as
(org-babel-trim (funcall bhook body 'org)) 'org t)))
((eq fmt 'ascii)
(require 'ox-ascii)
(insert (org-export-string-as
(concat "#+Title:\n" (funcall bhook body 'ascii)) 'ascii t)))
((or (eq fmt 'html) (eq fmt 'html-ascii))
(require 'ox-ascii)
(require 'ox-org)
(let* ((org-link-file-path-type 'absolute)
;; we probably don't want to export a huge style file
(org-export-htmlize-output-type 'inline-css)
(org-html-with-latex 'dvisvgm)
(html-and-images
(org-mime-replace-images
(org-export-string-as (funcall bhook body 'html) 'html t)))
(images (cdr html-and-images))
(html (org-mime-apply-html-hook (car html-and-images))))
(insert (org-mime-multipart
(org-export-string-as
(org-babel-trim
(funcall bhook body (if (eq fmt 'html) 'org 'ascii)))
(if (eq fmt 'html) 'org 'ascii) t)
html)
(mapconcat 'identity images "\n")))))
(mapc #'mml-attach-file files)))
(add-hook 'org-ctrl-c-ctrl-c-hook 'htmlize-and-send t)
;; (define-key mu4e-compose-mode-map (kbd "C-c o") 'org-mu4e-compose-org-mode)
(global-set-key (kbd "C-c o") 'org-mu4e-compose-org-mode)
;need this for hash access
(require 'subr-x)
(defun bjm/read-contact-list ()
"Return a list of email addresses"
(with-temp-buffer
(split-string (buffer-string) "\n" t)))
;; code from https://github.com/abo-abo/swiper/issues/596
(defun bjm/counsel-email-action (contact)
(with-ivy-window
(insert contact)))
;; bind comma to launch new search
(defvar bjm/counsel-email-map
(let ((map (make-sparse-keymap)))
(define-key map "," 'bjm/counsel-email-more)
map))
(defun bjm/counsel-email-more ()
"Insert email address and prompt for another."
(interactive)
(ivy-call)
(with-ivy-window
(insert ", "))
(delete-minibuffer-contents)
(setq ivy-text ""))
;; ivy contacts
;; based on http://kitchingroup.cheme.cmu.edu/blog/2015/03/14/A-helm-mu4e-contact-selector/
(defun bjm/ivy-select-and-insert-contact (&optional start)
(interactive)
;; make sure mu4e contacts list is updated - I was having
;; intermittent problems that this was empty but couldn't see why
(mu4e~request-contacts-maybe)
(let ((eoh ;; end-of-headers
(save-excursion
(goto-char (point-min))
(search-forward-regexp mail-header-separator nil t)))
;; append full sorted contacts list to favourites and delete duplicates
(contacts-list
(delq nil (delete-dups (mu4e~sort-contacts-for-completion (hash-table-keys mu4e~contacts))))))
;; only run if we are in the headers section
(when (and eoh (> eoh (point)) (mail-abbrev-in-expansion-header-p))
(let* ((end (point))
(start
(or start
(save-excursion
(re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*")
(goto-char (match-end 0))
(point))))
(initial-input (buffer-substring-no-properties start end)))
(kill-region start end)
(ivy-read "Contact: "
contacts-list
:re-builder #'ivy--regex
:sort nil
:initial-input initial-input
:action 'bjm/counsel-email-action
:keymap bjm/counsel-email-map)
))))
;;launch automatically
(add-hook 'mu4e-compose-mode-hook 'bjm/ivy-select-and-insert-contact)
;;ivy contacts for use anywhere
;;based on http://kitchingroup.cheme.cmu.edu/blog/2015/03/14/A-helm-mu4e-contact-selector/
(defun bjm/ivy-select-and-insert-contact-anywhere ()
(interactive)
(let (contacts-list contact)
;;append full sorted contacts list to favourites and delete duplicates
(setq contacts-list
(delq nil (delete-dups (mu4e~sort-contacts-for-completion (hash-table-keys mu4e~contacts)))))
(setq contact
(ivy-read "Contact: "
contacts-list
:re-builder #'ivy--regex
:sort nil))
(unless (equal contact "")
(insert contact))))
(defun mu4e~headers-jump-to-maildir (maildir)
"Show the messages in maildir (user is prompted to ask what
maildir)."
(interactive
(let ((maildir (mu4e-ask-maildir "Jump to maildir: ")))
(list maildir)))
(when maildir
(mu4e-mark-handle-when-leaving)
(mu4e-headers-search
(format "%s" maildir))))
;;; mu4e-view-save-all-attachments.el -- Save all attachments from view mode.
;;; Stephen J Eglen 2021
;; I've created this based on the work of Phil Jackson that required
;; an older version of mu4e. This version requires the GNUS article
;; code for reading mu4e messages.
;; https://gist.github.com/philjackson/aecfab1706f05079aec7000e328fd183
(defvar bulk-saved-attachments-dir (expand-file-name "/tmp"))
(defun cleanse-subject (sub)
(replace-regexp-in-string
"[^A-Z0-9]+"
"-"
(downcase sub)))
(defun mu4e-view-save-all-attachments (&optional arg)
"Save all MIME parts from current mu4e gnus view buffer."
;; Copied from mu4e-view-save-attachments
(interactive "P")
(cl-assert (and (eq major-mode 'mu4e-view-mode)
(derived-mode-p 'gnus-article-mode)))
(let* ((msg (mu4e-message-at-point))
(id (cleanse-subject (mu4e-message-field msg :subject)))
(attachdir (concat bulk-saved-attachments-dir "/" id))
(parts (mu4e~view-gather-mime-parts))
(handles '())
(files '())
dir)
(mkdir attachdir t)
(dolist (part parts)
(let ((fname (or
(cdr (assoc 'filename (assoc "attachment" (cdr part))))
(seq-find #'stringp
(mapcar (lambda (item) (cdr (assoc 'name item)))
(seq-filter 'listp (cdr part)))))))
(when fname
(push `(,fname . ,(cdr part)) handles)
(push fname files))))
(if files
(progn
(setq dir
(if arg (read-directory-name "Save to directory: ")
attachdir))
(cl-loop for (f . h) in handles
when (member f files)
do (mm-save-part-to-file h (expand-file-name f dir))))
(mu4e-message "No attached files found"))))
(defvar sk-mu4e-account-alist
'(("LAL"
(user-mail-address "xavier.garrido@ijclab.in2p3.fr")
(user-full-name "Xavier Garrido")
(mu4e-drafts-folder "/LAL/drafts")
(mu4e-sent-folder "/LAL/sent")
(mu4e-compose-signature (concat
" GARRIDO Xavier Laboratoire de l'Accélérateur Linéaire\n"
" NEMO Université Paris-Sud 11 \n"
" garrido@lal.in2p3.fr UMR 8607 \n"
" garrido@in2p3.fr Batiment 200 \n"
" +33 1.64.46.84.28 91898 Orsay Cedex, France \n"
))))
("Gmail"
(user-mail-address "xavier.garrido@gmail.com")
(user-full-name "Xavier Garrido")
(mu4e-drafts-folder "/Gmail/drafts")
(mu4e-sent-folder "/Gmail/sent")
(mu4e-compose-signature (concat
"Xavier Garrido\n"
"http://xgarrido.github.io\n"))))
(setq mu4e-user-mail-address-list
(mapcar (lambda (account) (cadr (assq 'user-mail-address account)))
sk-mu4e-account-alist))
(defun sk-mu4e-set-account ()
"Set the account for composing a message."
(let* ((account
(if mu4e-compose-parent-message
(let ((maildir (mu4e-message-field mu4e-compose-parent-message :maildir)))
(string-match "/\\(.*?\\)/" maildir)
(match-string 1 maildir))
(completing-read (format "Compose with account: (%s) "
(mapconcat #'(lambda (var) (car var))
sk-mu4e-account-alist "/"))
(mapcar #'(lambda (var) (car var)) sk-mu4e-account-alist)
nil t nil nil (caar sk-mu4e-account-alist))))
(account-vars (cdr (assoc account sk-mu4e-account-alist))))
(if account-vars
(mapc #'(lambda (var)
(set (car var) (cadr var)))
account-vars)
(error "No email account found"))))
;; ask for account when composing mail
;; (add-hook 'mu4e-compose-pre-hook 'sk-mu4e-set-account)
(defun sk-email-gmail ()
(setq message-send-mail-function 'smtpmail-send-it)
(setq smtpmail-stream-type 'starttls)
(setq smtpmail-default-smtp-server "smtp.gmail.com")
(setq smtpmail-smtp-server "smtp.gmail.com")
(setq smtpmail-smtp-service 587)
(setq mu4e-drafts-folder "/Gmail/drafts")
(setq mu4e-sent-folder "/Gmail/sent")
(setq mu4e-trash-folder "/Gmail/trash")
(setq mu4e-refile-folder (lambda (msg)
(cond
;; messages to the mu mailing list go to the /org-mode folder
((or (mu4e-message-contact-field-matches msg :cc "emacs-orgmode@gnu.org")
(mu4e-message-contact-field-matches msg :to "emacs-orgmode@gnu.org"))
"/Gmail/ml/org-mode")
((mu4e-message-contact-field-matches msg :from "notifications@github.com")
"/Gmail/ml/github")
;; everything else goes to /archive
;; important to have a catch-all at the end!
(t "/Gmail/archive"))))
(setq mu4e-maildir-shortcuts '(("/Gmail/inbox" . ?i)
("/Gmail/sent" . ?s)
("/Gmail/trash" . ?t)
("/Gmail/archive" . ?a)))
)
(defun sk-email-lal ()
(setq user-mail-address "xavier.garrido@ijclab.in2p3.fr")
(setq user-full-name "Xavier Garrido")
(setq message-send-mail-function 'smtpmail-send-it)
(setq smtpmail-stream-type 'ssl)
(setq smtpmail-default-smtp-server "zrelay.in2p3.fr")
(setq smtpmail-smtp-server "zrelay.in2p3.fr")
(setq smtpmail-smtp-service 465)
(setq mu4e-compose-signature (concat
" GARRIDO Xavier IJCLab \n"
" CMB Université Paris-Saclay \n"
" garrido@lal.in2p3.fr Batiment 200 \n"
" +33 1.64.46.84.28 91898 Orsay Cedex, France \n"
))
(setq mu4e-drafts-folder "/LAL/drafts")
(setq mu4e-sent-folder "/LAL/sent")
(setq mu4e-trash-folder "/LAL/trash")
(setq mu4e-refile-folder (lambda (msg)
(cond
;; messages to the mu mailing list go to the /na61 folder
;; ((or (mu4e-message-contact-field-matches msg :cc "na61-all@cern.ch")
;; (mu4e-message-contact-field-matches msg :to "na61-all@cern.ch"))
;; "/LAL/inbox/Experiment/NA61")
;; everything else goes to /archive
;; important to have a catch-all at the end!
(t (concat "/LAL/archives/" (format-time-string "%Y" (current-time)))))))
;; (setq mu4e-refile-folder "/LAL/archives/2016" )
(setq mu4e-maildir-shortcuts '(("/LAL/inbox" . ?i)
("/LAL/sent" . ?s)
("/LAL/trash" . ?t)
("/LAL/archives/2024" . ?a)))
)
(defun sk-mu4e-lal ()
(interactive)
(sk-email-lal)
(mu4e)
(mu4e~headers-jump-to-maildir "/LAL/inbox"))
;; (defun sk-mu4e-gmail()
;; (interactive)
;; (sk-email-gmail)
;; (mu4e)
;; (mu4e~headers-jump-to-maildir "/Gmail/inbox"))
;; (key-chord-define-global
;; "!!"
;; (defhydra hydra-email (:color blue :hint nil)
;; "
;; [mu4e] _g_mail _l_al"
;; ("g" sk-mu4e-gmail)
;; ("l" sk-mu4e-lal)))
(key-chord-define-global "!!" 'sk-mu4e-lal)
(require 'org-msg)
(setq org-msg-options "html-postamble:nil H:5 num:nil ^:{} toc:nil"
org-msg-startup "hidestars indent inlineimages"
org-msg-greeting-fmt "\nBonjour *%s*,\n\n"
org-msg-greeting-name-limit 3
org-msg-signature "
#+begin_signature
GARRIDO Xavier Laboratoire de l'Accélérateur Linéaire\n
NEMO Université Paris-Sud 11 \n
garrido@lal.in2p3.fr UMR 8607 \n
garrido@in2p3.fr Batiment 200 \n
+33 1.64.46.84.28 91898 Orsay Cedex, France \n
#+end_signature"
)
(org-msg-mode)
))