Sending multi-part MIME emails (HTML and Plain Text)

This is an example of our current GigaChef newsletter. This agent is used for testing and pulls the content and imagery from the document. Both the HTML version and Plain text version are contained within the single email. Emails clients with HTML enabled will automatically display that version, otherwise the plain text version is displayed.
CSS • HTML • LotusScript

(Declarations)

Dim session As NotesSession
Dim db As notesdatabase


Sub Initialize
	
	'Dim session As NotesSession			'Dim'd as global
	Set session = New NotesSession
	'Dim db As notesdatabase				'Dim'd as global
	Set db = session.currentdatabase
	Dim doc As notesdocument
	Set doc = session.documentcontext
		
	'send
	SendEmail(doc, "frank.brefere@gigachef.com" , "fbrefere")
	
End Sub

Note that the call to build the plain text version must come before the HTML part.

Sub SendEmail(doc As notesdocument, sendto As String, uname As String)
	
	session.ConvertMIME = False 	' Do not convert MIME to rich text  
	
	Dim edoc As NotesDocument
	Set edoc = db.CreateDocument
	Dim body As NotesMIMEEntity
	Dim mh As NotesMimeHeader
	Dim mc As NotesMIMEEntity
	Dim stream As NotesStream
	edoc.Form="memo"
	'//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	Dim sender As String
	sender = |GigaChef.com <support@gigachef.com>|
	'//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	edoc.SendTo= sendto
	edoc.Subject= "Newsletter: " & doc.subject(0)
	'Create the MIME headers
	Set body = edoc.CreateMIMEEntity
	Set mh = body.CreateHeader({MIME-Version})
	Call mh.SetHeaderVal("1.0")
	Set mh = body.CreateHeader("Content-Type")
	Call mh.SetHeaderValAndParams({multipart/alternative;boundary="=NextPart_8675309"})	
	'//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	Set mh = body.CreateHeader("Sender")
	Call mh.SetHeaderVal(sender)
	Set mh = body.CreateHeader("INetFrom")
	Call mh.SetHeaderVal(sender)	
	'//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	'Send the plain text part first
	Set mc = body.createChildEntity()
	Set stream = session.createStream()
	Call WritePlainText(stream, doc, uname)
	Call mc.setContentFromText(stream, {text/plain;charset="UTF-8"}, ENC_NONE)
	'Send the HTML part. Order is important!
	Set mc = body.createChildEntity()
	Set stream = session.createStream()
	Call WriteHTML(stream, doc, uname)
	Call mc.setContentFromText(stream, {text/html;charset="iso-8859-1"}, ENC_NONE)
	'Close the stream and Send it
	Call stream.Close
	Call edoc.Send(False)	
	'Reset session	
	session.ConvertMIME = True ' Restore conversion
	
End Sub

HTML CONTENT: Constructs the HTML content from the document.

Note 1: that it is ideal to place carriage returns "Chr(10)" in your code since many POP3 mail servers have a limitation of 655 characters per line of code.

Note 2: Also it's recommended to have all class declarations in the header CSS be explicitly stated (div.classname {color:#FF3300;} instead of just (.classname{color:#FF3300;}) since many mail servers/filters will strip out all non-explicit CSS code when the users forwards the email.

Sub WriteHTML(stream As NotesStream, doc As notesdocument, uname As String)
	
	Dim imgrootpath As String
	imgrootpath = "http://gigachef.com/gc/newsletters.nsf/newsletters/" + Cstr(doc.UniversalID) + "/$FILE/"
	Dim content As Variant
	
'BEGIN: Header
	Call stream.WriteText(|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">| & Chr(10) )
	Call stream.WriteText(|<html>| & Chr(10))
	Call stream.WriteText(|<head>| & Chr(10))
	
'BEGIN: Inline Stylesheet
	Call stream.WriteText (|<style type="text/css">| & Chr(10))
	Call stream.WriteText(|div.bodydiv {margin:20px0 10px 0;padding:0;background-color:#FFFFFF;font-family: Arial, Verdana;font-size:14px;color:#000000;}| & Chr(10) )
	Call stream.WriteText(|div.ndate {	color:#6b5d4b; font-size:12px; float:right;margin:65px 25px 0 0;}| & Chr(10))
	Call stream.WriteText(|td.LPcell {padding:0 10px 10px 10px; border-right:1px solid #e4e4e4;} | & Chr(10))
	Call stream.WriteText(|td.RPcell {padding:0 20px 10px 10px;}| & Chr(10))
	Call stream.WriteText(|table.LPtbl {width:469px;}| & Chr(10))
	Call stream.WriteText(|table.RPtbl {width:180px;}| & Chr(10))
	Call stream.WriteText(|table.LPtbl td, table.RPtbl td {padding:10px 0 10px 0;border-bottom:1px solid #e4e4e4;}| & Chr(10))
	Call stream.WriteText(|div.LPtitle {font-size:16px; font-weight:bold; margin-bottom:10px; background-color:#e9e9e9; padding:4px; color:#333333;}| & Chr(10))
	Call stream.WriteText(|div.RPtitle {font-size:14px;font-weight:bold;margin-bottom:10px;background-color:#e9e9e9;padding:4px;color:#333333;}| & Chr(10))
	Call stream.WriteText(|span.fldtext {font-size:14px;}| & Chr(10))
	Call stream.WriteText(|span.fldtext a {color:#000000; text-decoration:underline;}| & Chr(10))
	Call stream.WriteText(|span.fldtext a:hover {text-decoration:none;}| & Chr(10))
	Call stream.WriteText(|a.link {color:#6A942b; font-size:12px; text-decoration:none; float:right;}| & Chr(10))
	Call stream.WriteText(|a.link:hover {text-decoration:underline;}| & Chr(10))
	Call stream.WriteText(|a.link img {position:relative; top:3px;}| & Chr(10))
	Call stream.WriteText(|img.simg {border:1px solid #e4e4e4;}| & Chr(10))
	Call stream.WriteText(|span.signup {font-weight:bold; font-size:14px;}| & Chr(10))
	Call stream.WriteText(|span.signup a {color:#000000;}| & Chr(10))
	Call stream.WriteText(|span.signup a:hover {text-decoration:none;}| & Chr(10))
	Call stream.WriteText(|span.unsubscribe {font-size:12px;}| & Chr(10))
	Call stream.WriteText(|span.unsubscribe a {font-size:12px;color:#000000;}| & Chr(10))
	Call stream.WriteText(|span.unsubscribe a:hover {text-decoration:none;}| & Chr(10))
	Call stream.WriteText(|span.disclaimer {font-size:9px;}| & Chr(10))
	Call stream.WriteText(|</style>| & Chr(10))
 'END: Inline Stylesheet
	
'END: HEADER
	Call stream.WriteText (|</head>| & Chr(10) )
	
' BEGIN: HTML body
	Call stream.WriteText (|<body>| & Chr(10) )
	Call stream.WriteText (|<div class="bodydiv">|)	
'BEGIN: Content	
	
	Call stream.WriteText (||)
	
	Call stream.WriteText (|<center>| & Chr(10) )
	Call stream.WriteText (|<table style="margin:10px 0 20px 0;" border="0" width="700" cellpadding="0" cellspacing="0">| & Chr(10) )
	Call stream.WriteText (|<tr>| & Chr(10) )
	Call stream.WriteText (|<td colspan="2" height="100" background="http://gigachef.com/gc/Newsletters.nsf/Banner.jpg" valign="top">| & Chr(10) )
	Call stream.WriteText (|<a href="| &_
	|http://gigachef.com/gc/newsletters.nsf/nlink?openagent&uid=| & doc.UniqueID(0) & |&sec=Logo&rec=| & uname & |&url=http://gigachef.com| &_
	|"><img style="margin:8px 0 0 10px;" border="0" src="http://gigachef.com/gc/Newsletters.nsf/GCNewsletterLogo.gif"></a>| & Chr(10) )
	Call stream.WriteText (|<div class="ndate">| & doc.htmldate(0) & |</div>| & Chr(10) )
	Call stream.WriteText (|</td>| & Chr(10) )
	Call stream.WriteText (|</tr>| & Chr(10) )
	Call stream.WriteText (|<tr>| & Chr(10) )
	Call stream.WriteText (|<td width="499" class="LPcell" valign="top">| & Chr(10) )
	Call stream.WriteText (|<table border="0" class="LPtbl" cellpadding="0" cellspacing="0">| & Chr(10) )
	'############# SECTION 1 ############################
	If doc.section1enabled(0)="Yes" Then 
		Call stream.WriteText (|<tr><td><div class="LPtitle">| & Chr(10) )
		Call stream.WriteText ( doc.section1title(0) )
		Call stream.WriteText (|</div>| & Chr(10) )
		'photo
		If doc.section1photo(0)<>"" Then
			If doc.section1photoAlign(0)="Left" Then
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section1photo(0) & |" align="Left" style="margin:0 10px 5px 0;">| & Chr(10) )
			Elseif doc.section1photoAlign(0)="Right" Then
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section1photo(0) & |" align="Right" style="margin:0 0 5px 10px;">| & Chr(10) )
			Else
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section1photo(0) & |"><br><br>| & Chr(10) )
			End If
		End If
		'content
		content = Evaluate("@Explode( @Implode(section1content; '<br>') ; @NewLine ; @True ; @True )", doc)
		Call stream.WriteText (|<span class="fldtext">| & content(0) & |</span>| & Chr(10) )		
		'link
		If doc.section1link(0)<>"" Then
			Call stream.WriteText (|<br><a class="link" href="| &_
			|http://gigachef.com/gc/newsletters.nsf/nlink?openagent&uid=| & doc.UniqueID(0) & |&sec=1&rec=| & uname & |&url=| & doc.section1link(0) &	|">| & doc.section1linkText(0) &_
			|<img border="0" src="http://gigachef.com/gc/newsletters.nsf/silk_bullet_go.gif"></a>| & Chr(10) )
		End If	
		Call stream.WriteText (|</td></tr>| & Chr(10) )
	End If
	'############# SECTION 2 ##############################
	If doc.section2enabled(0)="Yes" Then 
		Call stream.WriteText (|<tr><td><div class="LPtitle">| & Chr(10) )
		Call stream.WriteText ( doc.section2title(0) )
		Call stream.WriteText (|</div>| & Chr(10) )
		'photo
		If doc.section2photo(0)<>"" Then
			If doc.section2photoAlign(0)="Left" Then
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section2photo(0) & |" align="Left" style="margin:0 10px 5px 0;">| & Chr(10) )
			Elseif doc.section2photoAlign(0)="Right" Then
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section2photo(0) & |" align="Right" style="margin:0 0 5px 10px;">| & Chr(10) )
			Else
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section2photo(0) & |"><br><br>| & Chr(10) )
			End If
		End If
		'content
		content = Evaluate("@Explode( @Implode(section2content; '<br>') ; @NewLine ; @True ; @True )", doc)
		Call stream.WriteText (|<span class="fldtext">| & content(0) & |</span>| & Chr(10) )		
		'link
		If doc.section2link(0)<>"" Then
			Call stream.WriteText (|<br><a class="link" href="| &_
			|http://gigachef.com/gc/newsletters.nsf/nlink?openagent&uid=| & doc.UniqueID(0) & |&sec=2&rec=| & uname & |&url=| & doc.section2link(0) &	|">| & doc.section2linkText(0) &_
			|<img border="0" src="http://gigachef.com/gc/newsletters.nsf/silk_bullet_go.gif"></a>| & Chr(10) )
		End If	
		Call stream.WriteText (|</td></tr>| & Chr(10) )
	End If
	'############# SECTION 3 ###############################
	If doc.section3enabled(0)="Yes" Then 
		Call stream.WriteText (|<tr><td><div class="LPtitle">| & Chr(10) )
		Call stream.WriteText ( doc.section3title(0) )
		Call stream.WriteText (|</div>| & Chr(10) )
		'photo
		If doc.section3photo(0)<>"" Then
			If doc.section3photoAlign(0)="Left" Then
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section3photo(0) & |" align="Left" style="margin:0 10px 5px 0;">| & Chr(10) )
			Elseif doc.section3photoAlign(0)="Right" Then
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section3photo(0) & |" align="Right" style="margin:0 0 5px 10px;">| & Chr(10) )
			Else
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section3photo(0) & |"><br><br>| & Chr(10) )
			End If
		End If
		'content
		content = Evaluate("@Explode( @Implode(section3content; '<br>') ; @NewLine ; @True ; @True )", doc)
		Call stream.WriteText (|<span class="fldtext">| & content(0) & |</span>| & Chr(10) )		
		'link
		If doc.section3link(0)<>"" Then
			Call stream.WriteText (|<br><a class="link" href="| &_
			|http://gigachef.com/gc/newsletters.nsf/nlink?openagent&uid=| & doc.UniqueID(0) & |&sec=3&rec=| & uname & |&url=| & doc.section3link(0) &	|">| & doc.section3linkText(0) &_
			|<img border="0" src="http://gigachef.com/gc/newsletters.nsf/silk_bullet_go.gif"></a>| & Chr(10) )
		End If	
		Call stream.WriteText (|</td></tr>| & Chr(10) )
	End If
	'####################################################
	Call stream.WriteText (|</table>| & Chr(10) )
	Call stream.WriteText (|</td>| & Chr(10) )
	Call stream.WriteText (|<td width="200" class="RPcell" valign="top">| & Chr(10) )
	Call stream.WriteText (|<table border="0" class="RPtbl" cellpadding="0" cellspacing="0">| & Chr(10) )
	'############# SECTION 4 #############################
	If doc.section4enabled(0)="Yes" Then 
		Call stream.WriteText (|<tr><td><div class="RPtitle">| & Chr(10) )
		Call stream.WriteText ( doc.section4title(0) )
		Call stream.WriteText (|</div>| & Chr(10) )
		'photo
		If doc.section4photo(0)<>"" Then
			If doc.section4photoAlign(0)="Left" Then
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section4photo(0) & |" align="Left" style="margin:0 10px 5px 0;">| & Chr(10) )
			Elseif doc.section4photoAlign(0)="Right" Then
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section4photo(0) & |" align="Right" style="margin:0 0 5px 10px;">| & Chr(10) )
			Else
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section4photo(0) & |"><br><br>| & Chr(10) )
			End If
		End If
		'content
		content = Evaluate("@Explode( @Implode(section4content; '<br>') ; @NewLine ; @True ; @True )", doc)
		Call stream.WriteText (|<span class="fldtext">| & content(0) & |</span>| & Chr(10) )		
		'link
		If doc.section4link(0)<>"" Then
			Call stream.WriteText (|<br><a class="link" href="| &_
			|http://gigachef.com/gc/newsletters.nsf/nlink?openagent&uid=| & doc.UniqueID(0) & |&sec=4&rec=| & uname & |&url=| & doc.section4link(0) &	|">| & doc.section4linkText(0) &_
			|<img border="0" src="http://gigachef.com/gc/newsletters.nsf/silk_bullet_go.gif"></a>| & Chr(10) )
		End If	
		Call stream.WriteText (|</td></tr>| & Chr(10) )
	End If
	'############# SECTION 5 #########################
	If doc.section5enabled(0)="Yes" Then 
		Call stream.WriteText (|<tr><td><div class="RPtitle">| & Chr(10) )
		Call stream.WriteText ( doc.section5title(0) )
		Call stream.WriteText (|</div>| & Chr(10) )
		'photo
		If doc.section5photo(0)<>"" Then
			If doc.section5photoAlign(0)="Left" Then
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section5photo(0) & |" align="Left" style="margin:0 10px 5px 0;">| & Chr(10) )
			Elseif doc.section5photoAlign(0)="Right" Then
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section5photo(0) & |" align="Right" style="margin:0 0 5px 10px;">| & Chr(10) )
			Else
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section5photo(0) & |"><br><br>| & Chr(10) )
			End If
		End If
		'content
		content = Evaluate("@Explode( @Implode(section5content; '<br>') ; @NewLine ; @True ; @True )", doc)
		Call stream.WriteText (|<span class="fldtext">| & content(0) & |</span>| & Chr(10) )		
		'link
		If doc.section5link(0)<>"" Then
			Call stream.WriteText (|<br><a class="link" href="| &_
			|http://gigachef.com/gc/newsletters.nsf/nlink?openagent&uid=| & doc.UniqueID(0) & |&sec=5&rec=| & uname & |&url=| & doc.section5link(0) &	|">| & doc.section5linkText(0) &_
			|<img border="0" src="http://gigachef.com/gc/newsletters.nsf/silk_bullet_go.gif"></a>| & Chr(10) )
		End If	
		Call stream.WriteText (|</td></tr>| & Chr(10) )
	End If	
	'############# SECTION 6 #################################
	If doc.section6enabled(0)="Yes" Then 
		Call stream.WriteText (|<tr><td><div class="RPtitle">| & Chr(10) )
		Call stream.WriteText ( doc.section6title(0) )
		Call stream.WriteText (|</div>| & Chr(10) )
		'photo
		If doc.section6photo(0)<>"" Then
			If doc.section6photoAlign(0)="Left" Then
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section6photo(0) & |" align="Left" style="margin:0 10px 5px 0;">| & Chr(10) )
			Elseif doc.section6photoAlign(0)="Right" Then
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section6photo(0) & |" align="Right" style="margin:0 0 5px 10px;">| & Chr(10) )
			Else
				Call stream.WriteText (|<img class="simg" border="0" src="| & imgrootpath + doc.section6photo(0) & |"><br><br>| & Chr(10) )
			End If
		End If
		'content
		content = Evaluate("@Explode( @Implode(section6content; '<br>') ; @NewLine ; @True ; @True )", doc)
		Call stream.WriteText (|<span class="fldtext">| & content(0) & |</span>| & Chr(10) )		
		'link
		If doc.section6link(0)<>"" Then
			Call stream.WriteText (|<br><a class="link" href="| &_
			|http://gigachef.com/gc/newsletters.nsf/nlink?openagent&uid=| & doc.UniqueID(0) & |&sec=6&rec=| & uname & |&url=| & doc.section6link(0) &	|">| & doc.section6linkText(0) &_
			|<img border="0" src="http://gigachef.com/gc/newsletters.nsf/silk_bullet_go.gif"></a>| & Chr(10) )
		End If	
		Call stream.WriteText (|</td></tr>| & Chr(10) )
	End If
	'############################################
	Call stream.WriteText (|</table>| & Chr(10) )
	Call stream.WriteText (|</td>| & Chr(10) )
	Call stream.WriteText (|</tr>| & Chr(10) )
	Call stream.WriteText (|<tr>| & Chr(10) )
	'Opened tracker
	Call stream.WriteText (|<td colspan="2" height="100" background="| &_
	|http://gigachef.com/gc/newsletters.nsf/getbannerimage?openagent&uid=| & doc.UniqueID(0) & |&rec=| & uname &_
	|" align="center">| & Chr(10) )
	Call stream.WriteText (|<span class="signup">Forward this email to a friend - Not a member? <a href="| &_
	|http://gigachef.com/gc/newsletters.nsf/nlink?openagent&uid=| & doc.UniqueID(0) & |&sec=Register&rec=| & uname & |&url=http://gigachef.com/gc/reg.nsf/register| &_
	|">Register Now!</a></span><br><br>| & Chr(10) )
	Call stream.WriteText (|<span class="unsubscribe"><b>To unsubscribe:</b> Login and change the newsletter preference in <a href="| &_
	|http://gigachef.com/gc/newsletters.nsf/nlink?openagent&uid=| & doc.UniqueID(0) & |&sec=Unsubscribe&rec=| & uname & |&url=http://gigachef.com/gc/ghome.nsf/preferences/| & uname & |?editdocument| &_
	|">your profile</a>.</span><br>| & Chr(10) )
	Call stream.WriteText (|<span class="disclaimer">GigaChef.com and the GigaChef Logo are registered trademarks of GigaChef, LLC.</span>| & Chr(10) )
	Call stream.WriteText (|</td>| & Chr(10) )
	Call stream.WriteText (|</tr>| & Chr(10) )
	Call stream.WriteText (|</table>| & Chr(10) )
	Call stream.WriteText (|</center>| & Chr(10) )	
	
' END: Content	
	Call stream.WriteText (|</div>| & Chr(10) )
	Call stream.WriteText (|</body>| & Chr(10) )
' END: HTML bodydiv	
	
'Close the HTML
	Call stream.WriteText (|</html>| & Chr(10) )
	
End Sub

PLAIN TEXT CONTENT

Sub WritePlainText(stream As NotesStream, doc As notesdocument, uname As String)
	
	'copy the main body message
	Call stream.WriteText(doc.Body)
	
	'write the footer
	Call stream.WriteText("" , EOL_LF)
	Call stream.WriteText("" , EOL_LF)
	Call stream.WriteText("-------------------------------------------------------------------------------" , EOL_CRLF)
	Call stream.WriteText("The GigaChef Team" , EOL_CR)
	Call stream.WriteText("http://gigachef.com")
	
End Sub

Images/Screenshots:










Written by Frank Joseph Brefere III

Posted by fbrefere001 on Sunday March 7, 2010