AtNotes Übersicht Willkommen Gast. Bitte einloggen oder registrieren.
16.11.18 - 16:39:39
Übersicht Hilfe Regeln Glossar Suche Einloggen Registrieren
News: Die Boards zu Version 10 sind verfügbar!
Schnellsuche:
+  Das Notes Forum
|-+  Best Practices
| |-+  Diskussionen zu Best Practices (Moderatoren: Axel, MartinG, animate, koehlerbv)
| | |-+  [LotusScript] Der Umgang mit Mime-Items - oder wie erstelle ich ein HTML Mail
« vorheriges nächstes »
Seiten: [1] Nach unten Drucken
Autor Thema: [LotusScript] Der Umgang mit Mime-Items - oder wie erstelle ich ein HTML Mail  (Gelesen 37303 mal)
pram
Gold Platin u.s.w. member:)
*****
Offline Offline

Geschlecht: Männlich
Beiträge: 1169



WWW
« 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:

  • session.convertMime steht standardmäßig auf TRUE.
    Notes konvertiert dann ein MIME-Item beim Zugriff über das Item-Interface zurück auf ein Richtext-Item. Dadurch geht oft die ein oder andere Formatierung verloren.
    Viel schlimmer ist aber, dass der Richtext beim Senden der Mail wieder vom Mailrouter nach HTML/MIME konvertiert wird und somit wenig Ähnlichkeit zum Original-MIME besteht.
    (Man kann aber dieses Verhalten auch nutzen, wenn man z.B. eine komplizierte Tabellenstruktur o.ä. in Richtext benötigt. Diese erzeugt man sich mit HTML und überlässt dann Notes die Rückkonvertierung)

  • Wenn man session.convertMime verstellt, sollte man anschließend den ursprünglichen Wert (welcher nicht zwangsläufig TRUE ist) wiederherstellen.
    Zitat Hilfe: This setting is initially True but persists across Notes client events. In general, if you change this setting, you should restore it before exiting.

  • Die Mime-Verarbeitung darf nicht durch andere Zugriffe auf das Dokument gestört werden.
    Nach einem get/createMimeEntity dürfen im Dokument keine Felder gelesen/gesetzt werden oder weitere Mime-Items parallel bearbeitet werden.
    Man muss mit CloseMimeEntities die Bearbeitung abschließen. Ansonsten treten Abstürze auf.
    Zitat Hilfe: Avoid processing items as both NotesItem and NotesMIMEEntity objects concurrently. Once you start using NotesMIMEEntity properties and methods, do not use other methods that access items in the same document until you terminate MIME processing with ComputeWithForm, Encrypt, Save, Send, or Sign, or CloseMIMEEntities in NotesDocument.

  • Header wie "Subject", "To", "Recieved" direkt über die entsprechenden Items setzen
    Zitat Hilfe: In a mail message, certain items correspond to MIME headers, for example, Subject ("Subject" header), SendTo ("To" header), and Received ("Received" header).

  • Encoding-Probleme sind an der Tagesordnung
    Ein "charset=UTF-8" an der richtigen Stelle wirkt oft Wunder. Den Code sollte man in jedem Fall mit Umlauten und ggf. auch weiteren Sonderzeichen testen.
    Vor dem Auslesen eines Mime-Items (auch Child-Items) sollte man das Item mit mime.DecodeContent dekodieren. Dabei werden Koduerungen wie "quoted-printable" etc. entfernt. (Ansonsten erhält man z.B. =FC für ein ü)

  • Mime-Items lassen sich nicht richtig mit copyItemToDocument kopieren.
    Siehe hier: http://atnotes.de/index.php/topic,50380.0.html

  • Mime-Items können bei falscher Handhabung korrupte Dokumente erzeugen
    Dies tritt z.B. auf, wenn man nur ein "multipart/related" Root-Item hat, aber keine weiteren.
    Es kann sogar passieren dass man die Dokumente nicht mehr löschen kann: http://atnotes.de/index.php/topic,51837.0.html (wenngleich die Fehlerursache hier eine andere war)

  • OffTopic:Ein Mime-Item lässt sich zur Base64 (de)kodierung zweckentfremden
    Set doc=New NotesDocument(db)
    Set miment=doc.CreateMIMEEntity
    Call miment.SetContentFromBytes(myStream, "", ENC_IDENTITY_BINARY)
    Call miment.EncodeContent(ENC_BASE64)
    EncodeFile=miment.ContentAsText()
    (Alternativ kann auch die undokumentierte Funktion notesStream.readEncoded/writeDecoded verwendet werden)


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



« Letzte Änderung: 18.04.14 - 23:53:07 von pram » Gespeichert

Roland Praml

IBM Certified Application Developer - Lotus Notes and Domino 8
Ich verwende das Foconis Object Framework
ata
Moderatoren
Gold Platin u.s.w. member:)
*****
Offline Offline

Geschlecht: Männlich
Beiträge: 5092


drenaiondrufflos


WWW
« Antworten #1 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 Grin Grin Grin
Gespeichert

Grüßle Toni Smiley
Bruce Willis
Gold Platin u.s.w. member:)
*****
Offline Offline

Geschlecht: Männlich
Beiträge: 945


Wer nicht wagt...


« Antworten #2 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
Gespeichert

nobody is perfect but i'm pretty close 
ata
Moderatoren
Gold Platin u.s.w. member:)
*****
Offline Offline

Geschlecht: Männlich
Beiträge: 5092


drenaiondrufflos


WWW
« Antworten #3 am: 11.03.12 - 21:54:56 »

... kann eigentlich nur an deiner Variablen HTML liegen...

Bitte nicht so alte Threads reanimieren - danke

Toni ;-)))
Gespeichert

Grüßle Toni Smiley
Bruce Willis
Gold Platin u.s.w. member:)
*****
Offline Offline

Geschlecht: Männlich
Beiträge: 945


Wer nicht wagt...


« Antworten #4 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...  Wink

Gruß
Leo
Gespeichert

nobody is perfect but i'm pretty close 
koehlerbv
Moderator
Gold Platin u.s.w. member:)
*****
Offline Offline

Geschlecht: Männlich
Beiträge: 20457



« Antworten #5 am: 11.03.12 - 22:06:34 »

Schalte erstens Option Declare ein, Leo.

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

Bernhard
Gespeichert
Bruce Willis
Gold Platin u.s.w. member:)
*****
Offline Offline

Geschlecht: Männlich
Beiträge: 945


Wer nicht wagt...


« Antworten #6 am: 11.03.12 - 22:15:34 »

Hallo Bernhard,

Danke für die Teilnahme!  Smiley

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

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

Kannst Du bitte eine andere evtl. funktionierende vorschlagen?

Gruß
Leo
Gespeichert

nobody is perfect but i'm pretty close 
koehlerbv
Moderator
Gold Platin u.s.w. member:)
*****
Offline Offline

Geschlecht: Männlich
Beiträge: 20457



« Antworten #7 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
Gespeichert
Bruce Willis
Gold Platin u.s.w. member:)
*****
Offline Offline

Geschlecht: Männlich
Beiträge: 945


Wer nicht wagt...


« Antworten #8 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...
 Huh Roll Eyes

Jetzt sehe ich in rot:

Call stream.Writetext(LARRY)

LARRY müsste noch irgenwie deklariert werden.
Weißt Du, wie?
Gespeichert

nobody is perfect but i'm pretty close 
koehlerbv
Moderator
Gold Platin u.s.w. member:)
*****
Offline Offline

Geschlecht: Männlich
Beiträge: 20457



« Antworten #9 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
Gespeichert
Bruce Willis
Gold Platin u.s.w. member:)
*****
Offline Offline

Geschlecht: Männlich
Beiträge: 945


Wer nicht wagt...


« Antworten #10 am: 11.03.12 - 22:37:56 »

Danke für den Hinweis!
Jetzt bekomme ich aber eine andere Meldung...  Sad
Ich gehe lieber schlafen...

Gruß
Leo
« Letzte Änderung: 12.03.12 - 08:57:26 von Bruce Willis » Gespeichert

nobody is perfect but i'm pretty close 
koehlerbv
Moderator
Gold Platin u.s.w. member:)
*****
Offline Offline

Geschlecht: Männlich
Beiträge: 20457



« Antworten #11 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
Gespeichert
Bruce Willis
Gold Platin u.s.w. member:)
*****
Offline Offline

Geschlecht: Männlich
Beiträge: 945


Wer nicht wagt...


« Antworten #12 am: 12.03.12 - 10:58:50 »

Ich habe ausgeschlafen, und der Script funktioniert einwandfrei.   Smiley
Danke an alle Beteiligten!

Gruß
Leo
Gespeichert

nobody is perfect but i'm pretty close 
Seiten: [1] Nach oben Drucken 
« vorheriges nächstes »
Gehe zu:  


Einloggen mit Benutzername, Passwort und Sitzungslänge

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2006, Simple Machines Prüfe XHTML 1.0 Prüfe CSS
Impressum Atnotes.de - Powered by Syslords Solutions - Datenschutz | Partner: