[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/org-mime 10098b001c 049/118: Fix file attachments and equa
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/org-mime 10098b001c 049/118: Fix file attachments and equations and enhance emails from org-files |
Date: |
Wed, 5 Jan 2022 07:58:46 -0500 (EST) |
branch: elpa/org-mime
commit 10098b001c3926f0fb128c85fc64d9016a195868
Author: Chen Bin <chenbin.sh@gmail.com>
Commit: Chen Bin <chenbin.sh@gmail.com>
Fix file attachments and equations and enhance emails from org-files
Thanks to John Kitchin (AKA jkitchin) for this patch.
See https://github.com/org-mime/org-mime/pull/20
Major changes:
- I think that file links should be attachments in an email. This commit
makes
that happen. It should work in all three ways of making/sending messages.
- Add file keyword support for email headers when using
org-mime-org-buffer-htmlize, and update docstrings.
- Equations were not getting correctly handled in
org-mime-org-buffer-htmlize
and org-mime-org-subtree-htmlize. The images were not getting created and
attached correctly. This commit fixes that.
- org-mim--chomp is too aggressive, and does more than the docstring
suggests.
I changed the docstring, and removed the call to it in org-mime-htmlize.
Now the
function is not used anywhere, so it could be removed. It would be
preferrable
to use something like s-trim.
- It should be possible to use relative paths to images.
- Add an ascii export to org-mime-htmlize. Previously no export was done for
the plain mimepart. Some links don't make sense in the email then, so
adding the
ascii export allows them to be re-rendered in a way that does make sense.
The
most concrete example is a cite/bibliography link from org-ref, which now
get
converted to ascii representations when the email is htmlized.
Minor fixes:
- org-export-htmlize-output-type is no longer a variable in org-9. It is now
org-html-htmlize-output-type. Both variables are still used for backward
compatibility.
- a few typos
- Some comment clarifications
---
org-mime.el | 337 ++++++++++++++++++++++++++++++++++++++++--------------------
1 file changed, 224 insertions(+), 113 deletions(-)
diff --git a/org-mime.el b/org-mime.el
index abb77ac467..aa79097b72 100644
--- a/org-mime.el
+++ b/org-mime.el
@@ -7,7 +7,7 @@
;; Keywords: mime, mail, email, html
;; Homepage: http://github.com/org-mime/org-mime
;; Version: 0.1.1
-;; Package-Requires: ((emacs "24.3") (cl-lib "0.5"))
+;; Package-Requires: ((emacs "24.4") (cl-lib "0.5"))
;; This file is not part of GNU Emacs.
@@ -106,6 +106,7 @@
(require 'cl-lib)
(require 'xml)
(require 'org)
+(require 'ox-org)
(defcustom org-mime-beautify-quoted-mail t
"Beautify quoted mail in more clean HTML, like Gmail."
@@ -175,37 +176,43 @@ buffer holdin the text to be exported.")
You could use either `org-up-heading-safe' or `org-back-to-heading'.")
-(defun org-mime--chomp (str)
- "Chomp leading and trailing whitespace from STR.
-This also chomps multiple line-endings to a single line-ending."
- (while (string-match "\\`[\n\r]+\\|^\\s-+\\|\\s-+$\\|[\n\r]+\\'"
- str)
- (setq str (replace-match "" t t str)))
- str)
+(defun org-mime-get-export-options (subtreep)
+ "SUBTREEP is t if current node is subtree."
+ (cond
+ (subtreep
+ (or org-mime-export-options
+ (if (fboundp 'org-export--get-subtree-options)
+ (org-export--get-subtree-options))))
+ (t
+ (or org-mime-export-options
+ (if (fboundp 'org-export--get-inbuffer-options)
+ (org-export--get-inbuffer-options))))))
+
+(defun org-mime-get-exported-content (fmt subtreep)
+ "Similar to `org-html-export-as-html' and `org-org-export-as-org'.
+FMT is either 'org or 'html.
+SUBTREEP is t if current node is subtree."
+ (let* ((buf (org-export-to-buffer fmt "*Org Mime Export*"
+ nil subtreep nil (org-mime-get-export-options subtreep)))
+ (body (prog1
+ (with-current-buffer buf
+ (format "#+BEGIN_EXPORT %s\n%s\n#+END_EXPORT"
+ (symbol-name fmt)
+ (buffer-string)))
+ (kill-buffer buf))))
+ body))
(defun org-mime--export-string (s fmt &optional opts)
"Export string S using FMT as the backend.
OPTS is export options."
- (let* (rlt
- ;; Emacs 25+ prefer exporting drawer by default
- ;; obviously not acceptable in exporting to mail body
- (org-export-with-drawers nil))
+ ;; Emacs 25+ prefer exporting drawer by default
+ ;; obviously not acceptable in exporting to mail body
+ (let* ((org-export-with-drawers nil))
(when org-mime-debug (message "org-mime--export-string called => %s" opts))
;; we won't export title from org file anyway
(if opts (setq opts (plist-put opts 'title nil)))
- (if (fboundp 'org-export-string-as)
- ;; emacs24.4+
- (setq rlt (org-export-string-as s fmt t (or org-mime-export-options
opts)))
- ;; emacs 24.3
- (setq rlt (org-export-string s (symbol-name fmt)))
- ;; manually remove the drawers, see
https://github.com/org-mime/org-mime/issues/3
- ;; Only happens on Emacs 24.3
- (let* ((b (string-match ":END:" rlt)))
- (if (and b (> b 0))
- (setq rlt (substring-no-properties rlt
- (+ b (length ":END:"))
- (length rlt))))))
- rlt))
+ ;; emacs24.4+
+ (org-export-string-as s fmt t (or org-mime-export-options opts))))
;; example hook, for setting a dark background in
;; <pre style="background-color: #EEE;"> elements
@@ -343,6 +350,19 @@ If html portion of message includes IMAGES they are
wrapped in multipart/related
"--" "}-<<alternative>>\n"))
(vm "?")))
+(defun org-mime-url-to-path (url current-file)
+ "If URL is file path, convert to valid path.
+Or else use CURRENT-FILE to calculate path."
+ (let* ((dir (file-name-directory current-file))
+ (path (expand-file-name url dir)))
+ (cond
+ ((string-match-p "^file:///" url)
+ (replace-regexp-in-string "^file://" "" url))
+ ((file-exists-p path)
+ path)
+ (t
+ (expand-file-name url default-directory)))))
+
(defun org-mime-replace-images (str current-file)
"Replace images in STR with cid links.
CURRENT-FILE is used to calculate full path of images."
@@ -356,22 +376,84 @@ CURRENT-FILE is used to calculate full path of images."
"src=\"cid:%s\""
(let* ((url (and (string-match "src=\"\\([^\"]+\\)\"" text)
(match-string 1 text)))
- (path (if (string-match-p "^file:///" url)
(replace-regexp-in-string "^file://" "" url)
- (expand-file-name url (file-name-directory
current-file))))
+ (path (org-mime-url-to-path url current-file))
(ext (file-name-extension path))
(id (replace-regexp-in-string "[\/\\\\]" "_" path)))
+
+ ;; Catch non-existent files here. Otherwise users get an error on
sending.
+ (unless (file-exists-p path)
+ (user-error "path: %s does not exist" path))
+
+ ;; Do it
(add-to-list 'html-images
(org-mime-file (concat "image/" ext) path id))
id)))
str)
html-images)))
+(defun org-mime-extract-non-image-files ()
+ "Extract non-image links in current buffer."
+ (cond
+ ((>= (org-mime-org-major-version) 9)
+ (org-element-map (org-element-parse-buffer) 'link
+ (lambda (link)
+ (when (and (string= (org-element-property :type link) "file")
+ (not (string-match
+ (cdr (assoc "file" org-html-inline-image-rules))
+ (org-element-property :path link))))
+ (org-element-property :path link)))))
+ (t
+ (message "Warning: org-element-map is not available. File links will not
be attached.")
+ nil)))
+
+(defun org-mime-insert-html-content (body file s opts)
+ (let* ((files (org-mime-extract-non-image-files))
+ ;; dvipng for inline latex because MathJax doesn't work in mail
+ ;; Also @see https://github.com/org-mime/org-mime/issues/16
+ ;; (setq org-html-with-latex nil) sometimes useful
+ (org-html-with-latex org-mime-org-html-with-latex-default)
+ ;; we don't want to convert org file links to html
+ (org-html-link-org-files-as-html nil)
+ (org-link-file-path-type 'absolute)
+ ;; makes the replies with ">"s look nicer
+ (org-export-preserve-breaks org-mime-preserve-breaks)
+ (plain (org-mime--export-string body 'org))
+ ;; org 9
+ (org-html-htmlize-output-type 'inline-css)
+ ;; org 8
+ (org-export-htmlize-output-type 'inline-css)
+ (html-and-images (org-mime-replace-images (org-mime--export-string s
'html opts)
+ file))
+ (images (cdr html-and-images))
+ (html (org-mime-apply-html-hook (car html-and-images))))
+
+ ;; If there are files that were attached, we should remove the links,
+ ;; and mark them as attachments. The links don't work in the html file.
+ (when files
+ (mapc (lambda (f)
+ (setq html (replace-regexp-in-string
+ (format "<a href=\"%s\">%s</a>"
+ (regexp-quote f) (regexp-quote f))
+ (format "%s (attached)" (file-name-nondirectory f))
+ html)))
+ files))
+
+ (insert (org-mime-multipart plain
+ html
+ (mapconcat 'identity images "\n")))
+
+ ;; Attach any residual files
+ (when files
+ (mapc (lambda (f)
+ (when org-mime-debug (message "attaching: %s" f))
+ (mml-attach-file f))
+ files))))
+
;;;###autoload
-(defun org-mime-htmlize (arg)
+(defun org-mime-htmlize ()
"Export a portion of an email to html using `org-mode'.
-If called with an active region only export that region, otherwise entire body.
-If ARG is not nil, use `org-mime-fixedwith-wrap' to wrap the exported text."
- (interactive "P")
+If called with an active region only export that region, otherwise entire
body."
+ (interactive)
(when org-mime-debug (message "org-mime-htmlize called"))
(let* ((region-p (org-region-active-p))
(html-start (funcall org-mime-find-html-start
@@ -383,36 +465,22 @@ If ARG is not nil, use `org-mime-fixedwith-wrap' to wrap
the exported text."
(html-end (or (and region-p (region-end))
;; TODO: should catch signature...
(point-max)))
- (body (buffer-substring html-start html-end))
- (header-body (concat org-mime-default-header body))
- (tmp-file (make-temp-name (expand-file-name
- "mail" temporary-file-directory)))
- ;; because we probably don't want to export a huge style file
- (org-export-htmlize-output-type 'inline-css)
- ;; makes the replies with ">"s look nicer
- (org-export-preserve-breaks org-mime-preserve-breaks)
- ;; dvipng for inline latex because MathJax doesn't work in mail
- ;; Also @see https://github.com/org-mime/org-mime/issues/16
- ;; (setq org-html-with-latex nil) sometimes useful
- (org-html-with-latex org-mime-org-html-with-latex-default)
+ (body (buffer-substring
+ html-start html-end))
+ (str (concat org-mime-default-header body))
+ (file (make-temp-name (expand-file-name
+ "mail" temporary-file-directory)))
+
+
;; to hold attachments for inline html images
- (html-and-images
- (org-mime-replace-images
- (org-mime--export-string header-body
- 'html
- (if (fboundp
'org-export--get-inbuffer-options)
- (org-export--get-inbuffer-options)))
- tmp-file))
- (html-images (unless arg (cdr html-and-images)))
- (html (org-mime-apply-html-hook
- (if arg
- (format org-mime-fixedwith-wrap header-body)
- (car html-and-images)))))
+ (opts (if (fboundp 'org-export--get-inbuffer-options)
+ (org-export--get-inbuffer-options))))
+
+ ;; delete current region
(delete-region html-start html-end)
- (save-excursion
- (goto-char html-start)
- (insert (org-mime-multipart
- body html (mapconcat 'identity html-images "\n"))))))
+ (goto-char html-start)
+ ;; insert new current
+ (org-mime-insert-html-content body file str opts)))
(defun org-mime-apply-html-hook (html)
"Apply HTML hook."
@@ -433,67 +501,98 @@ If ARG is not nil, use `org-mime-fixedwith-wrap' to wrap
the exported text."
(set-text-properties 0 (length txt) nil txt)
txt))))
-(defun org-mime-compose (body file &optional to subject headers opts)
- "Create mail BODY in FILE with TO, SUBJECT, HEADERS and OPTS."
- (when org-mime-debug (message "org-mime-compose called => %s %s" file opts))
- (let* ((fmt 'html))
- (unless (featurep 'message)
- (require 'message))
- (message-mail to subject headers nil)
- (message-goto-body)
- (cl-labels ((bhook (body fmt)
- (let ((hook 'org-mime-pre-html-hook))
- (if (> (eval `(length ,hook)) 0)
- (with-temp-buffer
- (insert body)
- (goto-char (point-min))
- (eval `(run-hooks ',hook))
- (buffer-string))
- body))))
- (let* ((org-link-file-path-type 'absolute)
- (plain (org-mime--export-string (org-mime--chomp body) 'ascii))
- ;; we probably don't want to export a huge style file
- (org-export-htmlize-output-type 'inline-css)
- (html-and-images
- (org-mime-replace-images
- (org-mime--export-string (bhook body 'html) 'html opts)
- file))
- (images (cdr html-and-images))
- (html (org-mime-apply-html-hook (car html-and-images))))
- (insert (org-mime-multipart plain html)
- (mapconcat 'identity images "\n"))))))
+(defun org-mime-compose (body file to subject headers subtreep)
+ "Create mail BODY in FILE with TO, SUBJECT, HEADERS.
+If SUBTREEP is t, curret org node is subtree."
+ ;; start composing mail
+ (unless (featurep 'message)
+ (require 'message))
+ (message-mail to subject headers nil)
+ (message-goto-body)
+
+ ;; insert text
+ (let* ((str (with-temp-buffer
+ (insert body)
+ (goto-char (point-min))
+ (run-hooks 'org-mime-pre-html-hook)
+ (buffer-string))))
+ (org-mime-insert-html-content body
+ file
+ str
+ (org-mime-get-export-options subtreep))))
+
+(defun org-mime-extract-keywords ()
+ "Extract keyword from "
+ (cond
+ ((>= (org-mime-org-major-version) 9)
+ (org-element-map (org-element-parse-buffer) 'keyword
+ (lambda (keyword)
+ (cons (org-element-property :key keyword)
+ (org-element-property :value keyword)))))
+ (t
+ (message "Warning: org-element-map is not available. File keywords will
not work.")
+ '())))
+
+(defun org-mime-build-mail-other-headers (cc bcc)
+ "Build mail header from CC and BCC."
+ (cond
+ ((and cc bcc) ((cc . cc) (bcc . bcc)))
+ (cc ((cc . cc)))
+ (bcc ((bcc . bcc)))
+ (t nil)))
;;;###autoload
(defun org-mime-org-buffer-htmlize ()
"Create an email buffer of the current org buffer.
The email buffer will contain both html and in org formats as mime
-alternatives."
+alternatives.
+
+The following file keywords can be used to control the headers:
+#+MAIL_TO: some1@some.place
+#+MAIL_SUBJECT: a subject line
+#+MAIL_CC: some2@some.place
+#+MAIL_BCC: some3@some.place
+
+The cursor ends in the TO field."
(interactive)
(run-hooks 'org-mime-send-buffer-hook)
(let* ((region-p (org-region-active-p))
(file (buffer-file-name (current-buffer)))
- (subject (or (org-mime--get-buffer-title)
+ (keywords (org-mime-extract-keywords))
+ (subject (or (cdr (assoc "MAIL_SUBJECT" keywords))
+ (org-mime--get-buffer-title)
(if (not file) (buffer-name (buffer-base-buffer))
(file-name-sans-extension
(file-name-nondirectory file)))))
- (body-start (or (and region-p (region-beginning))
- (save-excursion (goto-char (point-min)))))
- (body-end (or (and region-p (region-end)) (point-max)))
- (temp-body-file (make-temp-file "org-mime-export"))
- (body (buffer-substring body-start body-end)))
- (org-mime-compose body
- file
- nil ; TO
- subject
- nil ; HEADERS (CC, BCC ...)
- (if (fboundp 'org-export--get-inbuffer-options)
- (org-export--get-inbuffer-options)))))
-
+ (body (org-mime-get-exported-content 'html nil))
+ (to (cdr (assoc "MAIL_TO" keywords)))
+ (cc (cdr (assoc "MAIL_CC" keywords)))
+ (bcc (cdr (assoc "MAIL_BCC" keywords)))
+ (other-headers (org-mime-build-mail-other-headers cc bcc)))
+ (org-mime-compose body file to subject other-headers nil)
+ (message-goto-to)))
+
+(defun org-mime-org-major-version ()
+ "Get Org major version."
+ (string-to-number (car (split-string (org-release) "\\."))))
+
+;; TODO integrating patch
;;;###autoload
(defun org-mime-org-subtree-htmlize ()
"Create an email buffer of the current subtree.
The buffer will contain both html and in org formats as mime
-alternatives."
+alternatives.
+
+The following headline properties can determine the headers.
+* subtree heading
+ :PROPERTIES:
+ :MAIL_SUBJECT: mail title
+ :MAIL_TO: person1@gmail.com
+ :MAIL_CC: person2@gmail.com
+ :MAIL_BCC: person3@gmail.com
+ :END:
+
+The cursor is left in the TO field."
(interactive)
(save-excursion
(funcall org-mime-up-subtree-heading)
@@ -503,15 +602,27 @@ alternatives."
(to (mp "MAIL_TO"))
(cc (mp "MAIL_CC"))
(bcc (mp "MAIL_BCC"))
- ;; Thanks for Matt Price improving handling of cc & bcc headers
- (other-headers (cond
- ((and cc bcc) `((cc . ,cc) (bcc . ,bcc)))
- (cc `((cc . ,cc)))
- (bcc `((bcc . ,bcc)))
- (t nil)))
- (opts (if (fboundp 'org-export--get-subtree-options)
(org-export--get-subtree-options)))
- (body (org-get-entry)))
- (org-mime-compose body file to subject other-headers opts)))))
+ ;; Thanks to Matt Price improving handling of cc & bcc headers
+ (other-headers (org-mime-build-mail-other-headers cc bcc))
+ (org-export-show-temporary-export-buffer nil)
+ (subtree-opts (when (fboundp 'org-export--get-subtree-options)
+ (org-export--get-subtree-options)))
+ (org-export-show-temporary-export-buffer nil)
+ (org-major-version (org-mime-org-major-version))
+ ;; I wrap these bodies in export blocks because in
org-mime-compose
+ ;; they get exported again. This makes each block conditionally
+ ;; exposed depending on the backend.
+ (org-body (save-restriction
+ (org-narrow-to-subtree)
+ (org-mime-get-exported-content 'org t)))
+ (html-body (save-restriction
+ (org-narrow-to-subtree)
+ (org-mime-get-exported-content 'html t)))
+ (body (concat org-body "\n" html-body)))
+ (save-restriction
+ (org-narrow-to-subtree)
+ (org-mime-compose body file to subject other-headers t))
+ (message-goto-to)))))
(provide 'org-mime)
;; Local Variables:
- [nongnu] elpa/org-mime c49eb00fc6 021/118: can create email from org subtree, (continued)
- [nongnu] elpa/org-mime c49eb00fc6 021/118: can create email from org subtree, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime e554d8821d 020/118: Merge pull request #1 from syohex/fix-package, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime 91d8f48775 023/118: can set org export options, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime 7c3483b2b0 030/118: sample for advanced setup, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime e3329c7c4d 032/118: README, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime 0a0aa9b110 034/118: README, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime ad30f199cb 036/118: provide org-mime-find-html-start callback, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime 5f253f541c 037/118: only use export header when creating html, keep out of email, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime c6e2b95664 047/118: clean code, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime 64a3f56651 041/118: (setq org-html-with-latex nil) sometimes useful #16, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime 10098b001c 049/118: Fix file attachments and equations and enhance emails from org-files,
ELPA Syncer <=
- [nongnu] elpa/org-mime 1e792ef061 059/118: Merge pull request #28 from titaniumbones/fix-klipse-error, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime f610861858 063/118: htmlize current subtree, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime 9506727ca7 072/118: README, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime b189976217 076/118: fixed CI, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime 778f818ad3 078/118: Add ASCII text/plain export test, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime 9f84446038 082/118: make emacs 25 test pass, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime 484bf3b0b8 083/118: can edit mail-body in org-mode buffer directly, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime 5980ccaace 080/118: fixed unit test, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime b129579986 087/118: Regexp search for sig sep, ELPA Syncer, 2022/01/05
- [nongnu] elpa/org-mime 30bb3e663d 117/118: Merge pull request #66 from skangas/gplv3, ELPA Syncer, 2022/01/05