After reading Murphy Mac’s article about some cool AppleScripts utilizing the Quick Look feature and inspecting the corresponding scripts on Apple’s website, my mind came up with the idea to write an AppleScript for Apple Mail that will automatically show all incoming eMails as a Quick Look preview.
If you also heavily rely on eMail in your business or just like the geeky idea behind it, then I invite you to download my little script:
previmail ¢ automatically preview incoming eMails with Quick Look (version 0.2, ca. 220.1 KB)
An incoming email message displayed by previmail
Requirements
previmail was tested under/with:
¢ Mac OS X 10.5.2
¢ Apple Mail 3.1
¢ Intel & PowerPC based Macs
Installation
To install previmail on your Mac, you must first download it and then create a custom mail rule in Apple Mail. In this mail rule you have to choose to run an AppleScript as shown below:
Apple Mail rule dialog
All rule-matching eMails will then be processed by previmail and displayed using Quick Look.
Background
previmail is a simple rule action script for Apple Mail, that creates a CSS styled HTML version of every matching eMail message. It then saves these HTML versions to single temporary text files and displays them to the user with Quick Look using the «qlmanage -p» command. Finally it deletes all generated temporary files.
It is not possible to directly display an incoming eMail message as an *.emlx file, as the mail rules are executed before the eMail messages get saved as *.emlx files on the hard drive.
Thoughts…
The «qlmanage» command is really interesting for everybody who scripts the Mac. If your scripts needs to display some detailed information, you can just create a HTML page/PDF file/etc. on the fly, save it in the temporary folder and use qlmanage -p to display it. Or think of displaying media like songs, movies, pictures, etc.! And you don’t need to rely on good but external tools like Growl.
Important: Opening and saving the below script code in Script Editor won’t result in a usable AppleScript! That is because previmail internally relies both on a HTML/CSS template file and Python script, which are located inside its Script bundle. Therefor please download the complete script here.
This is the AppleScript code triggered by the Apple Mail rule:
-- created: 19.03.2008
-- modified: 26.03.2008
-- history:
-- v0.2
-- ¢ instead of ugly plain text files, previmail
-- now displays incoming eMail messages using
-- a pretty HTML template
-- v0.1a
-- ¢ initial release
-- Installed together with an AppleScript-based Apple Mail mail rule,
-- this script displays all incoming rule-matching eMails
-- with QuickLook
-- The script was tested under/with:
-- ¢ Mac OS X 10.5.2
-- ¢ Apple Mail 3.1
-- ¢ Intel & PowerPC based Macs
property mytitle : "previmail"
property myversion : "0.2"
-- you can set this property to 'long' to see even more mail infos
-- in the plain text report
property defmsginfo : "short"
-- unique handler to perform AppleScript actions on rule-matching Apple Mail messages
using terms from application "Mail"
on perform mail action with messages matchmsgs for rule theRule
my quicklookshow(matchmsgs)
end perform mail action with messages
end using terms from
(*-- For debugging only...
on run
tell application "Mail"
set msgs to (selection as list)
end tell
my quicklookshow(msgs)
end run*)
-- main function controlling the script procedure
on quicklookshow(msgs)
try
-- this var contains all quoted posix file paths of the generated
-- temporary files separated by a space
set strfilepaths to ""
-- this var contains all paths of the generated temporary files
set tmpfilepaths to {}
-- reading the HTML message template from the template file
set htmptemplatefilepath to ((path to me) as Unicode text) & "Contents:Resources:msg.tpl"
set htmltemplate to my readfromfile(htmptemplatefilepath)
repeat with msg in msgs
-- converting the Apple Mail message to a HTML report
set msghtml to my msgtohtml(msg, htmltemplate)
-- finding and getting an unused temp file
set tmpfilepath to my TmpFile's newpath()
-- saving the HTML report in the temp file as UTF-8
my writetofile(msghtml, tmpfilepath)
-- adding the path of the temporary file to the giant string
-- which will be passed as an argument to the «qlmanage p» command
set strfilepaths to strfilepaths & (quoted form of (POSIX path of tmpfilepath)) & space
-- adding the temporary file to the list of all generated temporary files
-- as we want to delete them later
set tmpfilepaths to tmpfilepaths & tmpfilepath
end repeat
-- action! displaying the contents of the generated temporary files
-- to the user with QuickLook!
set cmd to "qlmanage -p" & space & strfilepaths & ">& /dev/null"
set cmd to cmd as «class utf8»
do shell script cmd
-- deleting the temporary files
repeat with tmpfilepath in tmpfilepaths
set cmd to "rm" & space & (quoted form of (POSIX path of tmpfilepath))
set cmd to cmd as «class utf8»
do shell script cmd
end repeat
on error errmsg number errnum
if errnum ≠-1728 then
set logmsg to ((current date) as Unicode text) & ": " & errmsg & " (" & errnum & ")"
set logfile to ((path to desktop) as Unicode text) & mytitle & ".log"
my writetofile(logmsg, logfile)
end if
end try
end quicklookshow
-- I am converting an Apple Mail eMail message to a HTML report
on msgtohtml(msg, htmltemplate)
-- getting infos about the eMail message
tell application "Mail"
--set msgid to id of msg
set msguid to message id of msg
set msgsize to my getstringsize(message size of msg)
--set msgmbox to name of mailbox of msg
set msgcont to my gethtmlcont(content of msg)
set msgsender to sender of msg
set msgrecvd to date received of msg
set msgsent to date sent of msg
set msgsubj to subject of msg
set msgtorec to my gettxtrecipients("TO", msg)
set msgccrec to my gettxtrecipients("CC", msg)
set msgbccrec to my gettxtrecipients("BCC", msg)
set msgatms to my gettxtatms(mail attachments of msg)
set msgreplyto to reply to of msg
end tell
-- creating the header, attachment & content part to be inserted into the HTML template
set msgheader to "<b>From:</b> " & msgsender & "<br />" & "<b>Subject:</b> " & msgsubj & "<br />" & "<b>Date:</b> " & msgsent & "<br />" & "<b>To:</b> " & msgtorec
if msgccrec is not missing value then
set msgheader to msgheader & "<br /><b>Cc:</b> " & msgccrec
end if
if msgbccrec is not missing value then
set msgheader to msgheader & "<br /><b>Bcc:</b> " & msgbccrec
end if
if defmsginfo is "long" then
set msgheader to msgheader & "<br /><b>Size:</b> " & msgsize & "<br /><b>Received:</b> " & msgsent & "<br /><b>Reply-To:</b> " & msgreplyto
end if
if msgatms is missing value then
set msgatms to ""
else
set msgatms to "<p class=\"atms\"><img src=\"file:///Applications/Mail.app/Contents/Resources/Attachment.tiff\">" & msgatms & "</p>"
end if
-- inserting the report parts into the HTML template
set msgtags to {"$msgheader$", "$msgatms$", "$msgcont$"}
set msginfos to {msgheader, msgatms, msgcont}
set msghtml to my tagstoinfos(htmltemplate, msgtags, msginfos)
return msghtml
end msgtohtml
-- I am replacing found tags in a text with the corresponding infos
-- I am a silly, but working template engine :)
on tagstoinfos(txt, tags, infos)
set counttags to length of tags
repeat with i from 1 to counttags
set tag to (item i of tags) as Unicode text
set info to (item i of infos) as Unicode text
set txt to my searchnreplace(tag, info, txt)
end repeat
return txt
end tagstoinfos
-- I am a very old search & replace function, that my master
-- Martin still likes to use
-- This time I am working as a slave for the template engine
-- above
on searchnreplace(searchstr, replacestr, txt)
considering case, diacriticals and punctuation
if txt contains searchstr then
set olddelims to AppleScript's text item delimiters
set AppleScript's text item delimiters to {searchstr}
set txtitems to text items of txt
set AppleScript's text item delimiters to {replacestr}
set txt to txtitems as Unicode text
set AppleScript's text item delimiters to olddelims
end if
end considering
return txt
end searchnreplace
-- I am returning the recipients of an eMail message with HTML decoration
on gettxtrecipients(rectype, msg)
-- I am returning the recipeints as a text list
-- If there are no recipients availabe, I return «missing value»
set textrecipients to ""
tell application "Mail"
if rectype is "TO" then
set recpnts to to recipients of msg
else if rectype is "CC" then
set recpnts to cc recipients of msg
else if rectype is "BCC" then
set recpnts to bcc recipients of msg
end if
if recpnts is {} then
return missing value
else
set countrecpnts to length of recpnts
repeat with i from 1 to countrecpnts
set recaddress to address of (item i of recpnts)
set recname to name of (item i of recpnts)
-- sometimes the «name» property is not available for a recipient,
-- so we have to use an ugly try...end try-block below:
if i is equal to countrecpnts then
try
set textrecipients to textrecipients & recname & " <<a href=\"mailto:" & recaddress & "\">" & recaddress & "</a>>"
on error
set textrecipients to textrecipients & " <a href=\"mailto:" & recaddress & "\">" & recaddress & "</a>>"
end try
else
try
set textrecipients to textrecipients & recname & " <<a href=\"mailto:" & recaddress & "\">" & recaddress & "</a>>, "
on error
set textrecipients to textrecipients & " <<a href=\"mailto:" & recaddress & "\">" & recaddress & "</a>>, "
end try
end if
end repeat
return textrecipients
end if
end tell
end gettxtrecipients
-- I am returning the eMail attachments with HTML decoration
on gettxtatms(atms)
set textatms to ""
if atms is {} then
return missing value
else
set countatms to length of atms
tell application "Mail"
repeat with i from 1 to countatms
set atm to item i of atms
set atmname to name of atm
set atmsize to my getstringsize(file size of atm)
set atmmime to MIME type of atm
set atmdl to downloaded of atm
set atmentry to "«" & atmname & "» (" & atmsize & ")"
if i is equal to countatms then
set textatms to textatms & atmentry
else
set textatms to textatms & atmentry & ",<br />"
end if
end repeat
end tell
return textatms
end if
end gettxtatms
-- I am returning the body/content of an eMail message in HTML format
on gethtmlcont(msgcont)
set htmlcont to ""
set msglines to paragraphs of msgcont
repeat with msgline in msglines
-- ignoring empty or blank lines
if length of msgline is not equal to 0 then
if character 1 of msgline is not (ASCII character 202) then
set msgline to "<p class=\"cont\">" & msgline & "</p>"
set htmlcont to htmlcont & msgline
end if
end if
end repeat
return htmlcont
log htmlcontent
end gethtmlcont
-- I am returning the given byte site in human readable form
on getstringsize(bytesize)
set pyscriptpath to ((path to me) as Unicode text) & "Contents:Resources:Scripts:utl.py"
set cmd to "python " & quoted form of (POSIX path of pyscriptpath) & space & bytesize
set cmd to cmd as «class utf8»
set shellresult to do shell script cmd
return shellresult
end getstringsize
-- I am writing given content to a given file using UTF-8 text encoding
on writetofile(cont, filepath)
try
set openfile to open for access filepath with write permission
set eof of openfile to 0
set BOM_UTF8 to ((ASCII character 239) & (ASCII character 187) & (ASCII character 191))
write cont to openfile as «class utf8»
close access openfile
return true
on error
try
close access openfile
end try
return false
end try
end writetofile
-- I am reading the content of a given file(path) and return it in UTF-8 text encoding
on readfromfile(filepath)
try
set openfile to open for access filepath
set filecont to read openfile as «class utf8»
close access openfile
return filecont
on error errmsg number errnum
try
close access filecont
end try
error errmsg number errnum
end try
end readfromfile
-- script object to manage a temporary file
script TmpFile
property filepath : missing value
-- I am creating a new, not yet existing file path in the temp folder
on newpath()
set tmpfolderpath to (path to temporary items folder from user domain) as Unicode text
repeat
set rndnum to random number from 1000 to 99999
set tmpfilepath to (tmpfolderpath & "eMail_" & (rndnum as Unicode text) & ".html")
try
set tmpfilepath to tmpfilepath as alias
on error
set filepath to tmpfilepath
exit repeat
end try
end repeat
return filepath
end newpath
-- I am returning the file path of the temporary file
on getpath()
return filepath
end getpath
-- I am trying to delete the temporary file using the Finder
on remove()
try
tell application "Finder"
delete (filepath as alias)
end tell
end try
end remove
end script