Send HTML email from Cocoa using Mail.app

I am trying to send an html email address from a Cocoa application via Mail.app. I want to open a new message in Mail.app, include the subject, recipient, and add an HTML body with links and other content. But I can’t find a way to do this. I already tried Scripting Bridge, but the MailOutgoingMessage class does not have a content type, I can add content to plain text.

tried AppleScript, something like this:

set htmlContent to read "/Path/index.html" set recipientList to {" mail@mail.com "} tell application "Mail" set newMessage to make new outgoing message with properties {subject:"qwerty", visible:true} tell newMessage make new to recipient at end of to recipients with properties {address:" mail@s.com "} set html content to htmlContent --send end tell end tell 

this code sends an email with html only if I change -send to send. But I need to send an email later, after the user has made some changes.

+7
objective-c applescript scripting-bridge
source share
3 answers

To repeat the problem : using AppleScript to create a message with HTML content for interactive editing does not work (with OS X 10.9.2): an empty body appears in the form of a new message.

This should be considered a mistake , and I urge everyone to inform Apple at http://bugreport.apple.com - caveat: the class property html content message not defined in the Mail.sdef , Mail.app AppleScript dictionary, so HTML assignment is not officially supported.

There is a workaround , but it is not very good:

  • Create a message seamlessly.
  • Save it as a draft.
  • Open the draft message and HTML content will appear.

Realizing this task is challenging because it requires several workarounds. The following code tries harder:

Note. Since the code uses the graphical GUI, Access for assistive devices must be enabled (through System Preferences > Security & Privacy > Accessibility ) for the application that runs this code (for example, AppleScript Editor or, if it is executed through osascript , Terminal.app ).

 # Example values; use `read someFile` to read HTML from a file. set htmlContent to "<html><body><h1>Hello,</h1><p>world.</p></body></html>" set recipientList to {" person1@example.com ", " person2@example.com "} set msgSubject to "qwerty" tell application "Mail" # Create the message *invisibly*, and assign subject text # as well as the HTML content. set newMessage to make new outgoing message with properties Β¬ {visible:false, subject:msgSubject, html content:htmlContent} # Add recipients. # !! Given the workaround below, this is currently pointless. tell newMessage repeat with toRcpt in recipientList make new to recipient at end of to recipients with properties {address:toRcpt} end repeat end tell # Save the current number of drafts messages. set draftCountBefore to count messages of drafts mailbox # !! Save the new message as a *draft* - this is necessary # for the HTML content to actually appear in the message # body when we open the message interactively later. save newMessage # !! Sadly, it takes a little while for the new message # !! to appear in the drafts mailbox, so we must WAIT. set newMessageAsDraft to missing value repeat with i from 1 to 30 # give up after n * 0.1 secs. set draftCountNow to (count messages of drafts mailbox) if draftCountNow > draftCountBefore then set newMessageAsDraft to message 1 of drafts mailbox exit repeat end if delay 0.1 # sleep a little end repeat # Abort, if the draft never appeared. if newMessageAsDraft is missing value then error "New message failed to appear in the drafts mailbox within the timeout period." # Open the new message as a *draft* message - this ensures that # the HTML content is displayed and editable in the message body. # !! The ONLY solution I found is to use `redirect`, which, unfortunately, # !! *wipes out the recipients*. # !! It does, however, ensure that the draft is deleted once the message is sent. redirect newMessageAsDraft with opening window # Activate Mail.app and thus the draft message window. activate # !! Since the recipients have been wiped out, we need to # !! add them again - unfortunately, the only way we can do that is to # !! *GUI scripting* - simulating invocation of a menu command or # !! sending keystrokes. tell application "System Events" # We must make sure that the target window is active before # we can perform GUI scripting on it. set newMessageWindow to missing value repeat with i from 1 to 30 # give up after n * 0.1 secs. tell (first window of (first process whose frontmost is true) whose subrole is not "AXFloatingWindow") if name is msgSubject then set newMessageWindow to it exit repeat end if end tell delay 0.1 # sleep a little end repeat if newMessageWindow is missing value then error "New message failed to become the active window within the timeout period." # Turn the list of recipients into comma-delimited *string* for pasting into the To field. set {orgTIDs, AppleScript text item delimiters} to {AppleScript text item delimiters, {","}} set recipientListString to (recipientList as text) set AppleScript text item delimiters to orgTIDs # Save the current clipboard content. set prevClipboardContents to the clipboard # Cursor is in the "To:" field, so use GUI scripting to send the Edit > Paste command now. # NOTE: Access for assistive devices must be enabled via System Preferences > Security & Privacy > Accessibility. set the clipboard to recipientListString my pasteFromClipboard("") # Restore the previous clipboard content. # !! We mustn't do this instantly, as our temporary content may not have # !! finished pasting yet. It would be non-trivial to determine # !! when pasting has finished (examining `count of to recipients` doesn't work), # !! so we take our chances with a fixed, small delay. delay 0.1 set the clipboard to prevClipboardContents # Place the cursor in the message *body*. # !! This works as of Mail.app on OS X 10.9.2, but may break in the future. try tell newMessageWindow tell UI element 1 of scroll area 1 set value of attribute "AXFocused" to true end tell end tell end try end tell end tell (* Pastes form the clipboard into the active window of the specified application (process) using GUI scripting rather than keyboard shortcuts so as to avoid conflicts with keyboard shortcuts used to invoke this handler. Specify "" or `missing value` to paste into the currently active (frontmost) application. The target process may be specified by either name or as a process object. CAVEAT: While this subroutine IS portable across *UI languages*, it does make an assumption that will hopefully hold for all applications: that the "Edit" menu is the *4th* menu from the left (Apple menu, app menu, File, Edit). Examples: my pasteFromClipboard("") # paste into frontmost app my pasteFromClipboard("TextEdit") *) on pasteFromClipboard(targetProcess) tell application "System Events" if targetProcess is missing value or targetProcess = "" then set targetProcess to first process whose frontmost is true else if class of targetProcess is text then set targetProcess to process targetProcess end if -- Activate the application (make it frontmost), otherwise pasting will not work. set frontmost of targetProcess to true end if tell menu 1 of menu bar item 4 of menu bar 1 of targetProcess -- Find the menu item whose keyboard shortcut is Cmd-V set miPaste to first menu item whose value of attribute "AXMenuItemCmdChar" is "V" and value of attribute "AXMenuItemCmdModifiers" is 0 click miPaste end tell end tell end pasteFromClipboard 
+5
source share

It is not clear what you are looking for, but I will do my best to help you.

If you leave a send comment, the message should already be open in Mail.app, waiting for further editing and sending.

By adding the save newMessage line, it will be saved in the draft folder. The user can open it and continue editing whenever he wants. If you want to send the project from the application, use:

 set sendMessage to first message of drafts mailbox send sendMessage 

Good luck

+1
source share

I did not see that you had to edit the message before sending, so my previous answer was incorrect. This time it will be correct.

It is basically

  • accepts a pre-formatted RTF file,
  • displays it and puts it on the clipboard,
  • creates a new message,
  • fills in the fields
  • moves focus to the body of the message,
  • pastes a formatted clipboard

Here is the code:

 set textSubject to "HTML Test" set toAddress to " john.doe@gmail.com " set toName to "John Doe" tell application "Mail" do shell script "cat ~/Documents/RTF\\ File.rtf | textutil -stdin -stdout -convert rtf | pbcopy" set refMessage to make new outgoing message with properties {name:toName, address:toAddress, subject:textSubject, visible:true} tell refMessage make new to recipient at end of to recipients with properties {name:toName, address:toAddress} end tell end tell tell application "System Events" tell application process "Mail" set frontmost to true set value of attribute "AXFocused" of scroll area 4 of window textSubject to true end tell keystroke "v" using {command down} end tell 

Again, this worked great on Snow Leopard

Hope this helped.

+1
source share

All Articles