Link to home
Start Free TrialLog in
Avatar of Sjef Bosman
Sjef BosmanFlag for France

asked on

Extension to MIME emails in Lotusscript needed

I want to create a multipart MIME message, with the following sections:
- plain section
  - plain text
- html section
  - html code
  - image-1
  - image-n

I want the images as inline images, i.e. with code like <img src="cid:image_123">

I found the following class, created by Jake Howlett:
http://www.codestore.net/store.nsf/unid/BLOG-20091022-0419/

I'd like to have it adapted with a call to add an inline image. For that purpose I now have the code below, which doesn't work.

Please help me to improve the code so that I can create html mails with inline internal images.

Thanks!!
Const ERR_EMAIL_MODIFICATION_NOT_ALLOWED = "You can not make changes to an email once it has been sent."
Const ERR_EMAIL_NO_CONTENT = "Email has no body."

Class Email
	Private session As NotesSession
	Private doc As NotesDocument
	Private body As NotesMIMEEntity
	Private mh As NotesMIMEHeader
	Private mc As NotesMIMEEntity
	Private stream As NotesStream
	
	Private isTextSet As Boolean
	Private isHTMLSet As Boolean
	Private isStyleSet As Boolean
	
	Private isRebuildNeeded As Boolean
	Private isMailBuilt As Boolean
	
	Private rtitem As NotesRichTextItem
	
	Private str_TextPart As String
	Private str_HTMLPart As String
	Private str_DefaultStyles As String
	Private str_Styles As String
	
	Private FromName(0 To 2) As String 
	Private images() As String
	Private nimages As Integer
	
	Sub New()
		Set Me.session = New NotesSession()
		
		Set Me.doc = Me.session.Currentdatabase.CreateDocument
		Me.doc.Form = "Memo"
		
		Me.FromName(0) = "Sender's Name"
		Me.FromName(1) = "foo@bar.net"
		Me.FromName(2) = "DOMAIN"
		
		Me.str_DefaultStyles = "body{margin:10px;font-family:verdana,arial,helvetica,sans-serif;}"
		
		Me.isTextSet = False
		Me.isHTMLSet = False
		
		Me.isRebuildNeeded = True
		Me.isMailBuilt = False
		
	End Sub
	
%REM
                Property Set Subject
                Description: Comments for Property Set
%END REM
	Property Set Subject As String
		Me.doc.subject = subject
	End Property
	
%REM
                Sub setTextPart
                Description: Comments for Sub
%END REM
	Property Set Plain
		Me.str_TextPart = Plain
		Me.isTextSet = True
		
		Me.isRebuildNeeded = True
	End Property
	
	Property Get Plain
		Plain = Me.str_TextPart
	End Property
	
%REM
                Sub setHTMLPart
                Description: Comments for Sub
%END REM
	Property Set HTML
		Me.str_HTMLPart = HTML
		Me.isHTMLSet = True
		
		Me.isRebuildNeeded = True
	End Property
	
	Sub EmbedImage(file As String)
		Dim ContentType As String
		Dim streamFile As NotesStream
		Dim filename As String
		
		Redim Preserve images(nimages)
		images(nimages)= file
		nimages= nimages+1
		filename= Strrightback(file, "\")
		If Instr(file, "/") Then filename= Strrightback(file, "/")
		Me.HTML= Me.HTML + |<img src="cid:image_| & nimages & |">|
	End Sub
	
	Private Sub AttachImage(file As String)
		Dim ContentType As String
		Dim streamFile As NotesStream
		Dim filename As String
		Dim n As Integer
		
		filename= Strrightback(file, "\")
		If Instr(file, "/") Then filename= Strrightback(file, "/")
		Select Case Lcase(Strright(filename, ".") )
		Case "gif"
			ContentType = "image/gif" 
		Case "jpeg", "jpg"
			ContentType = "image/jpeg"
		Case Else
			ContentType = "application/octet-stream"
		End Select
		Set Me.mc= Me.body.createChildEntity()
		Set Me.mh = Me.mc.CreateHeader("Content-Disposition")
		Call Me.mh.SetHeaderVal({inline; filename="} & filename & {"} )
'only required if we're embedding something we need to reference later on
		Set Me.mh = Me.mc.CreateHeader("Content-ID")
		n= n + 1
		Call Me.mh.SetHeaderVal( |<image_| & n & |>| )
		Set streamFile = session.CreateStream 
		If streamFile.Open(file) Then
			Call Me.mc.SetContentFromBytes(streamFile, ContentType & {; name="} & filename & {"}, ENC_IDENTITY_BINARY)
			Call streamFile.Close 
		End If
	End Sub
	
	Property Get HTML
		HTML = Me.str_HTMLPart
	End Property
%REM
                Sub Styles
                Description: Comments for Sub
%END REM
	Property Set Styles As String
		Me.str_Styles = Styles
		Me.isStyleSet = True
		
		Me.isRebuildNeeded = True
	End Property
	
%REM
                Sub CSS
                Description: Shortcut sub to Styles
%END REM
	Property Set CSS As String
		Me.Styles = CSS
	End Property
	
%REM
                Sub setFromAddress
                Description: Comments for Sub
%END REM
	Property Set Sender As Variant
		Me.FromName(0) = Sender(0)
		Me.FromName(1) = Sender(1)
		Me.FromName(2) = Sender(2)
		
		Me.isRebuildNeeded = True
	End Property
	
%REM
                Property Set replyTo
                Description: Comments for Property Set
%END REM
	Property Set ReplyTo As String
		Me.Doc.ReplyTo = ReplyTo
		
		Me.isRebuildNeeded = True
	End Property
	
%REM
                Property Set CopyTo
                Description: Comments for Property Set
%END REM
	Property Set CopyTo As String
		Me.Doc.CopyTo = CopyTo
		
		Me.isRebuildNeeded = True
	End Property
	
%REM
                Property Set BlindCopyTo
                Description: Comments for Property Set
%END REM
	Property Set BlindCopyTo As String
		Me.Doc.BlindCopyTo = BlindCopyTo
		
		Me.isRebuildNeeded = True
	End Property
	
%REM
                Sub Rebuild
                Description: Comments for Sub
%END REM
	Sub Rebuild
		
		If Me.doc.HasItem("Body") Then
			Call Me.doc.RemoveItem("Body")
			
                        'Is this needed to allow rebuilding between sends?
                        'Set Me.body = Me.Doc.GetMIMEEntity( "Body" )
                        'If Me.body Is Nothing Then
                        '       MsgBox "can't find mime entity"
                        'End If
                        'Call Me.body.Remove()
			
		End If
		
		If Me.isHTMLSet Then 'Send mulipart/alternative 
			
                        'Create the MIME headers
			
			Me.session.convertMIME = False 
			
                        'This is the line that errors if you try and rebuild the message between sends! 
                        'Error = "Object varaible not set"
			Set Me.body = Me.doc.CreateMIMEEntity("Body") 
			
			Set Me.mh = Me.body.CreateHeader({MIME-Version})
			Call Me.mh.SetHeaderVal("1.0")
			
			Set Me.mh = Me.body.CreateHeader("Content-Type")
			Call Me.mh.SetHeaderValAndParams( {multipart/mixed;boundary="=NextPart_="}) ' was multipart/alternative
			
			'Send the text part first
			If Me.isTextSet Then    
				Set Me.mc = Me.body.createChildEntity()
				Set Me.stream = Me.session.createStream()
				Call Me.stream.WriteText(Me.str_TextPart)
				Call Me.mc.setContentFromText(Me.stream, {text/plain}, ENC_NONE)
			End If
			
                        'Now send the HTML part. Order is important!
			Set Me.mc = Me.body.createChildEntity()
'			Set Me.mh = Me.mc2.CreateHeader("Content-Type")						
'			Call Me.mh.SetHeaderValAndParams( {multipart/related;boundary="=NextPart_rich="}) ' was multipart/alternative
			Set Me.stream = Me.session.createStream()
			
'			Set Me.mc2 = Me.body.createChildEntity()
			
			Call stream.WriteText("<html>", EOL_CR)
			
			Call stream.WriteText("<head>", EOL_CR)
			Call stream.WriteText("<style>", EOL_CR)
			Call stream.WriteText(Me.str_DefaultStyles, EOL_CR)
			
			If Me.isStyleSet Then
				Call stream.WriteText(Me.str_Styles, EOL_CR)
			End If
			
			Call stream.WriteText("</style>", EOL_CR)       
			Call stream.WriteText("</head>", EOL_CR)
			
			Call stream.WriteText("<body>", EOL_CR)
			Call stream.WriteText(Replace(Me.str_HTMLPart, ">", ">"+Chr(10)))
			Call stream.WriteText("</body>", EOL_CR)
			Call stream.WriteText("</html>", EOL_CR)
			Call Me.mc.setContentFromText(Me.stream, {text/html;charset="iso-8859-1"}, ENC_NONE)
			
			Forall image In images
				Call AttachImage(image)
			End Forall
			
			Call Me.doc.Closemimeentities(True)
			
			Me.session.convertMIME = True
			
		Elseif Me.isTextSet Then 'Just Text will do! 
			
			Set Me.rtitem = New NotesRichTextItem(Me.doc, "Body")
			Me.rtitem.Appendtext(Me.Str_TextPart)
			
		Else 'No content!
			Error 1000, ERR_EMAIL_NO_CONTENT
		End If
		
		Me.doc.Principal= Me.FromName(0) +" <"+Me.FromName(1)+"@"+Me.FromName(2)+">"
		Me.doc.InetFrom = Me.FromName(0) +" <"+Me.FromName(1)+">"
		
		Me.isMailBuilt = True   
		Me.isRebuildNeeded = False
	End Sub
	
%REM
                Sub Send
                Description: Comments for Sub
%END REM
	
	Sub Send(sendTo As String)
		
		If Me.isMailBuilt And Me.isRebuildNeeded Then
			Error 1000, ERR_EMAIL_MODIFICATION_NOT_ALLOWED
		Elseif Not Me.isMailBuilt Then
			Call Me.Rebuild()
		End If
		
		Me.Doc.SendTo = SendTo
		
		Call Me.Doc.Send(False)
	End Sub
End Class

Sub Initialize
	Dim mail As New Email()
	mail.Subject = "A multipart email generated " & Now
	mail.Sender= Split("Your name;yourname@yourdomain.com;ACME", ";")
	mail.Plain = "This is plain text"
	mail.HTML = "<p>This is <i>fancy</i> text</p>"
	mail.EmbedImage "c:\Windows\Cloud.gif"
	mail.HTML = mail.HTML + "<p>Woops. Forgot this</p>"
	mail.CSS = "p{margin:2em}"
'	mail.CopyTo= "someoneelse@somedomain.com"
	mail.Send("testaddress@destination.com")
End Sub

Open in new window

Avatar of mbonaci
mbonaci
Flag of Croatia image

You think that the part of e-mail can be html and other part plain text?
Why does it have to be plain text? Can you just format html so that it looks like plain text?
Avatar of Sjef Bosman

ASKER

Nope. It's like a newsletter, distributed via MIME mail. The plain text should just read:
If you see this text, please go to www.abcd.net/info to get the same document in PDF format
SOLUTION
Avatar of mbonaci
mbonaci
Flag of Croatia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Yes, that already explains better what I'm looking for. Ideally, what I'm after are the calls in LotusScript to prepare the multi-part stuff, with children and grandchildren and so. See the codestore-link above, I also posted there, maybe Jake has the time to look into it.
What happens when you run your code?
Where does it fail?
Which mail client are you using for testing?
Code doesn't fail, but code produces a mail I don't want :-))  Try to dump the code in an agent and send yourself a mail. Set your own mail addresses and select an existing image file on the disk.

There are some other modifications to the code, at the start of the Rebuild method: the multipart/mixed, which causes all sections to be added to the content, which I do not want. What I need to know is how exactly should CreateChildEntity be called and how often to allow a) the plain-text section, b) the rich-text section with c) the embedded in-line internal image.

Oh, and I'm testing in R7.0.3 (but that shouldn't be very important).
Oh, mail client... I send mails to my Gmail address. I still have problems with Gmail, Notes looks better but isn't correct either. If I get the structure I want to have, I will test it in Thunderbird (and maybe Outlook Express).
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks, I'll study the code but I doubt it'll tell me much more.

What I really need to know is the exact calls to execute to generate a mail with images, embedded in a mail with a text and a rich-text part. Did nobody ever do that yet in Notes??
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
LOL !!
he he, I'm glad you liked it...
I do not think it is your code, I have tested it from my local PC and got the attach results, which looks fine to me. Where are you running this code from, is it an agent, button or action. If agent, is it scheduled or on click event . Another question from Web or Client
gmail-result.GIF
> which looks fine to me
Yes exactly, it 'looks' fine but it it's not what I want!

I want "This is plain text" to be invisible, in a separate section of the multi-part mail. There should be no attachment visible. For the rest you are right, it seems to work.

What I want to produce:
- a section with plain text, which only shows up in applications not supporting multi-part MIME mails
- another section with rich text plus one or more embedded images; the images should show up in-line (which essentially works)
- for the time being, no attachments

All multi-part MIME mail agents in LotusScript that I found create a mail with attachments, and in that case the mail can be one section only. I suppose I need to add more children, but I can't do that alone... ;-)
I must have cracked it, a long time ago, but I'd have to examine multiple databases to find how I exactly did it. If someone needs it, better post a new question.

Thanks guys!