Creating meeting request in Lotus Notes from .NET
At my present gig, there is this .NET web application for scheduling and requesting approvals for vacation and other absences, that I wrote two years ago. The application gets a bit of an upgrade from time to time, so that it runs smoothly and so that we improve user experience. But there was one thing, that I really missed which we had at previous gig. At previous gig, whenever your vacation request was approved, you got this nice meeting request in your Lotus Notes mailbox, which you accepted and “pooof”, the calendar entry was created. This was good for two things. One was, that you actually got reminded that you are not at work on said day, and thus prevented you to make any false promises. Second one was that anyone scheduling a meeting saw that you were absent and could schedule it when you were at work.
Back to present day. I want our application to have that feature! And as much as it is piece of cake to send a Lotus Notes meeting request inside Lotus Notes environment, you get into all sorts of trouble in a mixed Lotus Notes and .NET environment like we do at my present gig. Now, if you do a search for how to send a Lotus Notes meeting request from .NET, you will get plenty of hits where people use Lotus Notes COM objects to connect to mail database, generate a notes document and send it. This was useless to my problem, because to access user’s mailbox and create a meeting request, you need to run application on user’s computer and get his password. Somehow. Also, we are inclining to leave Lotus Notes for good, so any day now, we might switch to Exchange server and as god is my witness, I have no desire to write same code twice.
So, after some serious research, I came to a conclusion that using COM objects is definitely out of the question. I also noticed that both Lotus Notes Client and Outlook support iCalendar format described here. Thus, if I was somehow able to send an iCalendar file as an inline attachment, it should work well on both systems. Sure, it may not be the preferred way of creating a meeting in either client, but iCalendar is a standard to stay, so I doubt there will be a need to recode the darn thing in my life time. Anyway… enough gibberish. On to the code, which is, surprisingly in VB.NET!
Public Class SMTPAppointmentDispatcher Implements IAppointmentDispatcher #Region "Properties" Private Property Server As String #End Region #Region "Constructors" Public Sub New(strSmtpServer As String) If (String.IsNullOrWhiteSpace(strSmtpServer)) Then _ Throw New ArgumentNullException("SMTP server address") Me.Server = strSmtpServer End Sub #End Region #Region "IAppointmentDispatcher methods" Public Sub Dispatch(data As AppointmentData) Implements IAppointmentDispatcher.Dispatch Dim mail As MailMessage = InitMessage(data) Dim strCalendarEntry As String = CreateVCalendarBody(data) SendMessage(mail, strCalendarEntry) End Sub #End Region #Region "Private methods" Private Function InitMessage(data As AppointmentData) As MailMessage Dim mail As MailMessage = New MailMessage() mail.To.Add(data.RecipientAddress) If (data.DateFrom.Date = data.DateTo.Date) Then mail.Subject = String.Format("{0} od {1:dd.MM.yyyy} do {2:dd.MM.yyyy}", _ data.Subject, data.DateFrom, data.DateTo) Else mail.Subject = String.Format("{0} od {1:dd.MM.yyyy hh:mm} do {2:dd.MM.yyyy hh:mm}", _ data.Subject, data.DateFrom, data.DateTo) End If mail.From = New MailAddress("myapplication@mycompany.com") Return mail End Function Private Function CreateVCalendarBody(data As AppointmentData) As String Dim strCalDateFormat As String = "yyyyMMddTHHmmssZ" Dim strBodyText As String = "BEGIN:VCALENDAR" + vbNewLine + _ "METHOD:REQUEST" + vbNewLine + _ "BEGIN:VEVENT" + vbNewLine + _ "DTSTAMP:{0}" + vbNewLine + _ "DTSTART:{1}" + vbNewLine + _ "DTEND:{2}" + vbNewLine + _ "SUMMARY:{3}" + vbNewLine + _ "UID:{4}" + vbNewLine + _ "ORGANIZER;CN=""{5}"":MAILTO:{6}" + vbNewLine + _ "LOCATION:" + vbNewLine + _ "DESCRIPTION:{3}" + vbNewLine + _ "SEQUENCE:0" + vbNewLine + _ "PRIORITY:5" + vbNewLine + _ "CLASS:{7}" + vbNewLine + _ "CREATED:{0}" + vbNewLine + _ "STATUS:CONFIRMED" + vbNewLine + _ "TRANSP:OPAQUE" + vbNewLine + _ "END:VEVENT" + vbNewLine + _ "END:VCALENDAR" strBodyText = String.Format(strBodyText, _ DateTime.Now.ToUniversalTime().ToString(strCalDateFormat), _ data.DateFrom.ToUniversalTime().ToString(strCalDateFormat), _ data.DateTo.ToUniversalTime().ToString(strCalDateFormat), _ data.Subject, _ Guid.NewGuid().ToString("N"), _ data.RecipientFullName, _ data.RecipientAddress, _ IIf(data.IsPrivate, "PRIVATE", "PUBLIC") ) Return strBodyText End Function Private Sub SendMessage(mail As MailMessage, strCalendarEntry As String) Using smtp As SmtpClient = New SmtpClient(Me.Server) Dim bytesBody As Byte() = System.Text.Encoding.UTF8.GetBytes(strCalendarEntry) Using ms As System.IO.MemoryStream = New System.IO.MemoryStream(bytesBody) Using a As Attachment = New Attachment(ms, "meeting.ics", "text/calendar") a.ContentDisposition.Inline = True mail.Attachments.Add(a) smtp.Send(mail) End Using End Using End Using mail.Dispose() End Sub #End Region End Class
What every method does is, I hope, pretty straight forward.
- Class constructor is simple. It takes a name of a SMTP server, which we will use to send e-mail from.
- Public method Dispatch takes care of creating an email, populates it with data and sends it. This method also takes a type-class I used to store information necessary to send a valid e-mail.
- Private method InitMessage creates MailMessage objects and populates it with Subject, To and From field.
- Private method CreateVCalendarBody returns a string that suits iCalendar standards. You may notice that this one only has organizer field filled, which means organizer will receive a Calendar broadcast. If you want to send a real meeting request, you also need to add:
ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN="John the Recipient":MAILTO:recipient@mycompany.com
- Private method SendMessage creates an inline attachment with file named meeting.ics.
This works well and not only that, it also work well over the internet. Remember said Lotus Notes application? It had an issue with people not using Lotus Notes client. This solution, however, works if your clients are using GMail, Hotmail, Outlook, Lotus Notes or any other mail client with iCalendar support.
There are couple of things you need to watch out for.
- Organizer must not be an attendee as well. If one of your recipients has the same email address as organizer, recipients will not see calendar request. In fact, they will not see anything, as e-mail will not show up in their mailbox.
- If you try to fake an organizer, you need to make sure that email address is valid, as otherwise all attendees will get mail bounce notifications at any of their actions upon said calendar entry.
August 8th, 2014 at 08:08
[…] several years ago that is in use for planning and reporting absences and vacation (also discussed here and most likely in some other article as well). Lately, I noticed the very same application working […]
November 28th, 2014 at 10:24
Dear Writer,
It worked like a charm 🙂
Thanks for this post.
AJ
March 25th, 2018 at 10:48
I used the code on vb.net and it shows “Type ‘IAppointmentDispatcher’ is not defined”. Can you please let us know which reference to be used for this?
Or is there anything else that I am doing wrong.
Thanks in advance
March 25th, 2018 at 22:48
Hi. Of course it does, since code for IAppointmentDispatcher is not included in the sample. I only used it to create a possibility to add some other type of AppointmentDispatcher than SMTPAppointmentDispatcher, if needed.
So, basically, there are two solutions:
Implements IAppointmentDispatcher
from class andImplemnents IAppointmentDispatcher.Dispatch
from Dispatch method, and it should compile without a problem.Also, at this point, may I emphasize the fact that this code is only demo-proof, meaning you still need to add logging and checks around the code.