Das Notes Forum

Best Practices => Diskussionen zu Best Practices => Thema gestartet von: pram am 30.06.11 - 01:38:03

Titel: [LotusScript] Der Umgang mit Mime-Items - oder wie erstelle ich ein HTML Mail
Beitrag von: pram am 30.06.11 - 01:38:03
Es wird ja öfters hier gefragt, wie erstelle ich eine Mail mit einem Bild oder eigenem HTML, welche auch bei anderen Mailclients vernünftig dargestellt werden. Ich habe mir deshalb gedacht, ich schreib mal einen kleinen Artikel. Verbesserungsvorschläge, Lob und Kritik nehme ich gerne an.


Wie erstelle ich eine Mail mit einem Bild oder eigenem HTML, welche auch bei anderen Mailclients vernünftig dargestellt werden?
Die einfache Lösung ist, ein entsprechendes RichtextItem zu erzeugen und dem Mailrouter die Konvertierung nach HTML zu überlassen.
Das Ergebnis ist leider oft nicht akzeptabel.

Die Alternative ist, man erzeugt das HTML selber und packt es in eine Mail. Doch Wie?
LotusScript bietet für diesen Zweck eine ganze Reihe von NotesMime-Klassen an, mit denen sich dieses Vorhaben umsetzen lässt.

WARNUNG
Der erste Kontakt mit diesen Klassen ist meist frustrierend, weil als Ergebnis nicht das herauskommt was man will. Die Klassen sind zwar relativ gut dokumentiert, aber man muss die Hilfe schon sehr genau lesen. In dem ein oder anderen Nebensatz ist oft erwähnt, was man nicht machen soll oder machen muss...

Ich habe deshalb hier mal die wichtigsten Fallstricke zusammengefasst:


So, genug gewarnt. Wie erstelle ich jetzt so ein MIME-Item?

Zuvor noch ein paar Grundlagen:

MIME = Multipurpose Internet Mail Extension. Erweitert das Email-Format, so dass nicht nur normaler ASCII-Text genutzt werden kann.
Ein Mime-Item ist eine Art Baumstruktur,besteht aus einem Wurzelknoten, welche Kindknoten besitzen können. Ein Kindkoten kann weitere Kindknoten besitzen.
Jeder Knoten hat einen ContentType und ein oder mehrere Header. (Mehr dazu findet man http://de.wikipedia.org/wiki/MIME und in den RFCs)

Im Beispiel beschränke ich mich auf folgende Baumstruktur
Code
+ multipart/related
   + multipart/alternative (es wird der letzte Part, sowit unterstützt, angezeigt)
       + text/plain
       + text/html
   + image/gif (optional)
   + ... weiter Anhänge etc.

Hier habe ich einen Beispiel, wie man eine HTML-Email erzeugt, ein Bild einfügt und versendet. Das Beispiel sollte relativ selbsterklärend sein.

Code
Option Public
Option Declare
%Include "LSCONST.LSS"

Private Const LARRY = |R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAw
AAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapyuvUUlvONmOZtfzgFz
ByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSp
a/TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJl
ZeGl9i2icVqaNVailT6F5iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uis
F81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97Vriy/Xl4/f1cf5VWzXyym7PH
hhx4dbgYKAAA7|

Private Const HTML = |Dieses Bild zeigt <img src="%IMAGE%"> Larry von der <a href="http://www.ietf.org/rfc/rfc2397.txt">Network Working Group</a><br>|
'/**
' * Diese Klasse dient zum Erstellen von Multipart-Related Mime-Items
' * @author: Roland Praml/01/int/FOCONIS
' */
Public Class MultipartRelatedMime
	Private doc 		As NotesDocument
	Private itemName 	As String
	Private session	As NotesSession
	Private oldCvtMime	As Integer

	Private rootMime	As NotesMIMEEntity
	Private contentMime As NotesMIMEEntity

	'/**
	' * Erzeuge ein neues Objekt dieser Klasse. Im Anschluss können mittels
	' * "attach" beliebig viele Attachments angelegt werden. Will man diese
	' * im HTML anzeigen, so muss man sich den Rückgabewert von Attach merken
	' * und als srv-URL im IMG-Tag verwenden (siehe Beispiel)
	' * @param doc das Dokument, auf dem das MIME-Processing statt findet
	' * @param itemName das MIME-Item (Normalerweise Body)
	' * @return ein Objekt vom Typ MultipartRelatedMime
	' */
	Public Sub new(doc As NotesDocument, itemName As String)
		Set me.doc 	= doc
		Me.itemName 	= itemName
		If doc.Hasitem(Itemname) Then 		' Sicherheitsprüfung
			Error 1000, "The Item " + itemName +" already exists."
		End if
		Set session 	= New NotesSession
		oldCvtMime 	= session.convertMime 	' alte Einstellung merken
		session.convertMime = False			' Konvertierung abschalten
		
		' -- Anlegegen des Root-Mimes
		Dim stream	As NotesStream
		Set rootMime 	= doc.CreateMIMEEntity(itemName)
		Set  stream 	=  session.CreateStream()
		Call stream.WriteText("This is a multipart message in MIME format.")
		Call rootMime.SetContentFromText(stream, "multipart/related", ENC_NONE )

		' !! Es wird auch gleich ein Dummy-content angelegt, denn wenn man
		' !! das Dokument jetzt speichert, dann wird es korrupt
		Set contentMime = rootMime.Createchildentity()
		Call stream.truncate()
		Call stream.WriteText("empty")	
		Call contentMime.SetContentFromText(stream, "text/plain", ENC_NONE )
		Call stream.Close()
	End Sub

	'/**
	' * Destruktor, erledigt noch ein paar Aufräumarbeiten. 
	' * !! Achtung, danach ist die MIME-Konvertierung wieder aktiviert. !!
	' */
	Public Sub Delete
		Call closeMime() ' sicherheitshalber nochmals close aufrufen
		session.Convertmime = oldCvtMime ' 
	End Sub

	'/**
	' * Diese Methode beendet die MIME-Verarbeitung, erst danach darf wieder
	' * auf das Dokument zugegriffen werden.
	' */
	Public Function closeMime()
		If Not doc Is Nothing Then 
			Set rootMime 		= Nothing
			Set contentMime 	= Nothing
			Call doc.Closemimeentities(true, itemName)
			Set doc = nothing
		End If
	End Function

	'/**
	' * "attach" hängt eine Datei an das MIME an. Die FileData werden idR. 
	' * binär oder base64-codiert übergeben. Gibt man einen Filenamen an,
	' * so erscheint die Datei als Attachment, andernfalls erscheint bei 
	' * Bildern, welche im HTML referenziert werden, kein zusätzliches 
	' * Attachmentsymbol/"Büroklammer"
	' * @param fileData Die Filedaten
	' * @param encoding Das Enoding, z.B. ENC_BASE64
	' * @param filename Der Dateiname, kann auch leer sein, s.o.
	' * @param contentType z.B.: gif/jpeg
	' * @return content-identifier 
	' */
	Public Function attach(fileData As NotesStream, encoding As Integer, _
	filename As String, ByVal contentType As String) As String
		' -- es wird ein neues Kind angelegt
		Dim mimePart 	As NotesMIMEEntity
		Set mimePart 	=  rootMime.CreateChildEntity()
		
		If contentType = "" then	
			contentType = "application/octet-stream" ' Default
		End if
		
		If filename <> "" Then
			' Wichtig: Dateinamen sollten nicht doppelt vorkommen.
			' 18.04.14/RPr: Encoding ergänzt. Dateinamen dürfen
			' nun auch Umlaute enthalten
			Call mimePart.CreateHeader("Content-Disposition"). _
			SetheaderValAndParams({attachment; filename="} + _
			encodeFileNameRFC2047(filename) + {"}) 
		End If
		
		' -- eine zufällige Content-ID erzeugen
		Dim cid As String
		Dim randomGenerator As New NotesDocument(doc.Parentdatabase)
		cid = randomGenerator.Universalid+"@mydomain"
		Call mimePart.CreateHeader("Content-ID")._
		SetheaderValAndParams("<" + cid + ">")

		' -- und die Daten schreiben
		Call mimePart.SetContentFromText(fileData, contentType, encoding)
		attach = "cid:" + cid
	End Function
	
	'/**
	' * Schreibt das HTML, sowie einen alternativen Plaintext. Dieser sollte
	' * immer geschrieben werden und ggf die Information enthalten, dass ein
	' * HTML-Fähiger Mailclient vorhanden sein muss.
	' * @param html der HTML-Quelltext
	' * @param plain der Plain-Text
	' */
	Public Function setHtml(html As String, plain As String)

		' - das ursprüngliche ContentMime wird ersetzt
		Dim nextSibling As NotesMIMEEntity
		Set nextSibling = contentMime.Getnextsibling()
		Call contentMime.Remove() 	' in dem man es löscht

		Set contentMime = rootMime.Createchildentity(nextSibling) ' und wieder anlegt
		Dim stream As NotesStream
		Set stream	= session.createStream()
		Call stream.WriteText("This is a message with multiple parts in MIME format.")
		Call contentMime.SetContentFromText(stream, "multipart/alternative", ENC_NONE )

		' -- darunter kommt der Plaintext		
		Dim  textPlain		As NotesMIMEEntity
		Set  textPlain		=  contentMime.CreateChildEntity()
		Call stream.truncate()
		Call stream.WriteText(plain)	
		Call textPlain.SetContentFromText(stream, "text/plain", ENC_NONE )

		' -- und der HTML-Text
		Dim textHtml As NotesMIMEEntity
		Set textHtml =  contentMime.CreateChildEntity()
		Call stream.truncate()
		Call stream.WriteText(html)	
		Call textHtml.setContentFromText(stream, "text/html; charset=UTF-8", ENC_NONE)
		Call stream.Close
	End Function
End Class

'/**
' * Diese Prozedur erstellt ein neues Dokument, erzeugt mit obiger Klasse ein MIME-Item und versendet das Dokument dann
' */
Sub Initialize
	Dim session As New NotesSession
	Dim stream As NotesStream
	Dim doc As New NotesDocument(session.Currentdatabase)
	Dim cid As String
	doc.Subject = "Dies ist ein Test"
	
	Dim mime As New MultipartRelatedMime(doc, "Body")		' Klasse anlegen
	Set stream = session.createStream
	Call stream.Writetext(LARRY)
	cid = mime.attach(stream, ENC_BASE64, "", "image/gif")	' Bild anhängen und CID merken
	Call stream.close()
	Call mime.setHtml(Replace(HTML, "%IMAGE%", cid), "bitte benutzen Sie einen HTML-fähigen Mailclient") ' CID im HTML ersetzen
	
	Call mime.closeMime()
	
	Call doc.send(False,session.Effectiveusername) ' Mail an den aktuellen User senden (zum Testen senden wir uns das Mail selber)
	
End Sub

Tipps
- Durch kleine Modifikationen lassen sich auch .ics - Einladungen an Outlook versenden: http://atnotes.de/index.php/topic,53222.0.html

- Durch Setzen von session.convertMime = true wird beim nächsten Zugriff auf das Item (über doc.getFirstItem) das Mime-Item von Notes wieder in einen Richtext konvertiert. Dieses Item kann dann an ein anderes Richtext-Item angefügt werden.
Damit kann man z.B. speziell formatierte Texte/Links/Bilder/... an ein bestehendes RICHTEXT-Item anhängen.

Gruß
Roland

/Edit 18.4.2014

Damit auch Umlaute in Dateinamen unterstüzt werden, muss man diese encodieren. Dazu verwende ich folgende Methode:
Code
	Public Function encodeFileNameRFC2047(fileName As String) As String
		Dim c As Integer
		Dim i As Integer
		Dim ret$
		Dim mode$
		For i = 1 To Len(fileName)
			c = Uni(Mid(filename, i, 1))
			If (32 <= c And c <= 60) Or (62 <= c And c <= 126) Then
				' printable
				ret = ret + Chr(c)
			ElseIf c < 256 Then
				mode = "Q"
				ret = ret + "=" + Right("00" + Hex(c),2)
			Else 
				mode = "B"
				Exit For
			End If
		Next
		If mode = "" Then
			encodeFileNameRFC2047 = fileName
		ElseIf mode = "Q" Then
			encodeFileNameRFC2047 = "=?iso-8859-1?Q?" + ret+ "?="
		ElseIf mode = "B" Then
			Dim stream 	As NotesStream

			Set stream 	= FocUtil.getSession().createStream()
			Call stream.writetext(filename)
		
			Dim mime As NotesMIMEEntity
			Dim tmpDoc As New NotesDocument(FocUtil.getSession().currentDatabase)
			Set  mime = tmpDoc.Createmimeentity("Roland")
			Call mime.Setcontentfromtext(stream, "text/html; charset=UTF-8", ENC_NONE)
			stream.position = 0
			Call mime.Encodecontent(ENC_BASE64)
		
			Set stream 	= FocUtil.getSession().createStream()
			Call mime.Getcontentastext(Stream, False)
			stream.position = 0
			stream.position = 0
			Dim  s(1) As String, r(1) As String
			s(0) = Chr(10)
			s(1) = Chr(13)
			ret = Replace(stream.readText, s, r)
			encodeFileNameRFC2047 = "=?UTF-8?B?" + ret+ "?="
			Call tmpDoc.Closemimeentities(False)
		End If
	End Function



Titel: Re: [LotusScript] Der Umgang mit Mime-Items
Beitrag von: ata am 30.06.11 - 07:56:07
Hallo Roland,

danke für die schöne Einführung, die Hinweise und den Beispielcode - das werde ich mir mal in Ruhe zu Gemüte führen und ein wenig damit spielen.

Toni ;D ;D ;D
Titel: Re: [LotusScript] Der Umgang mit Mime-Items - oder wie erstelle ich ein HTML Mail
Beitrag von: Bruce Willis am 11.03.12 - 20:08:33
...Verbesserungsvorschläge, Lob und Kritik nehme ich gerne an...

Hallo Roland,

super Sache, vielen Dank!

Weißt Du, wie man die hier angehängte Fehlermeldung beseitigen kann?

Gruß
Leo
Titel: Re: [LotusScript] Der Umgang mit Mime-Items - oder wie erstelle ich ein HTML Mail
Beitrag von: ata am 11.03.12 - 21:54:56
... kann eigentlich nur an deiner Variablen HTML liegen...

Bitte nicht so alte Threads reanimieren - danke

Toni ;-)))
Titel: Re: [LotusScript] Der Umgang mit Mime-Items - oder wie erstelle ich ein HTML Mail
Beitrag von: Bruce Willis am 11.03.12 - 21:58:50
Hallo Toni,

Danke für dein Input!

... kann eigentlich nur an deiner Variablen HTML liegen...
Kannst du bitte deine (funktionierende) vorschlagen?

Bitte nicht so alte Threads reanimieren
Ich werde mich bemühen...  ;)

Gruß
Leo
Titel: Re: [LotusScript] Der Umgang mit Mime-Items - oder wie erstelle ich ein HTML Mail
Beitrag von: koehlerbv am 11.03.12 - 22:06:34
Schalte erstens Option Declare ein, Leo.

Weiters: Roland hat die Constant HTML definiert. Du auch?

Bernhard
Titel: Re: [LotusScript] Der Umgang mit Mime-Items - oder wie erstelle ich ein HTML Mail
Beitrag von: Bruce Willis am 11.03.12 - 22:15:34
Hallo Bernhard,

Danke für die Teilnahme!  :)

Schalte erstens Option Declare ein, Leo.
Immer an!
Bei Dir nicht?  :P

Roland hat die Constant HTML definiert. Du auch?
Klar! Ich hab 1zu1 seinen Script zum Testen übernommen, um nichts kaputt zu machen.
 :-:

Kannst Du bitte eine andere evtl. funktionierende vorschlagen?

Gruß
Leo
Titel: Re: [LotusScript] Der Umgang mit Mime-Items - oder wie erstelle ich ein HTML Mail
Beitrag von: koehlerbv am 11.03.12 - 22:19:23
Na, wenn Du das schon so fein machst (vielleicht besser als ich?), dann zeige doch mal im Debugger den Inhalt von "HTML" zu Zeitpunkt, wenn es "bumm" macht" ...

Bernhard
Titel: Re: [LotusScript] Der Umgang mit Mime-Items - oder wie erstelle ich ein HTML Mail
Beitrag von: Bruce Willis am 11.03.12 - 22:20:02
Ooooh, sorry, Du hast recht!
option declare war doch nicht bei dem Button sondern nur in der Maske...
 ??? ::)

Jetzt sehe ich in rot:

Call stream.Writetext(LARRY)

LARRY müsste noch irgenwie deklariert werden.
Weißt Du, wie?
Titel: Re: [LotusScript] Der Umgang mit Mime-Items - oder wie erstelle ich ein HTML Mail
Beitrag von: koehlerbv am 11.03.12 - 22:23:48
Benutze doch einfach die Suche in diesem Threadtext (Ctrl-G im Firefox, Ctrl-F im IE) und gib LARRY ein. Und HTML dann ... Herrjeh, Leo - Du kannst das doch. Ist es zu spät heute?

Bernhard
Titel: Re: [LotusScript] Der Umgang mit Mime-Items - oder wie erstelle ich ein HTML Mail
Beitrag von: Bruce Willis am 11.03.12 - 22:37:56
Danke für den Hinweis!
Jetzt bekomme ich aber eine andere Meldung...  :(
Ich gehe lieber schlafen...

Gruß
Leo
Titel: Re: [LotusScript] Der Umgang mit Mime-Items - oder wie erstelle ich ein HTML Mail
Beitrag von: koehlerbv am 11.03.12 - 22:41:29
Und jetzt ist die Zeit, die DesignerHelp auszupacken oder ein gutes Buch und die Grundlagen dessen zu erlernen, was man verwenden möchte. Du kennst ja die Prinzipien von AtNotes, Leo: Hilfe immer gerne, aber keine Vorkauen und vor allem keine Übernahme von Arbeiten.

Bernhard
Titel: Re: [LotusScript] Der Umgang mit Mime-Items - oder wie erstelle ich ein HTML Mail
Beitrag von: Bruce Willis am 12.03.12 - 10:58:50
Ich habe ausgeschlafen, und der Script funktioniert einwandfrei.   :)
Danke an alle Beteiligten!

Gruß
Leo