Posts Tagged ‘iCalendar’

HTML formatted content in the description field of an iCalendar

Friday, March 22nd, 2013

By default, the iCalendar specification allows only plain text to be used in the description of an Event object.

X-ALT-DESC header

However Outlook can recognize HTML formatted content. This is supported using an additional field in the Event object called “X-ALT-DESC”, rather than the existing field:

DESCRIPTION:Reminder
X-ALT-DESC;FMTTYPE=text/html:<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 3.2//E
 N""><HTML><BODY>\nhtml goes here\n</BODY></HTML>

Using Mail.dll you can set this field with no extra hassle:

// C#

Appointment appointment = new Appointment();
Event e = appointment.AddEvent();
e.XAltDescription = @"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 3.2//EN""><HTML><BODY>
html goes here
</BODY></HTML>";
' VB.NET

Dim appointment As New Appointment()
Dim e As [Event] = appointment.AddEvent()
e.XAltDescription = "<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 3.2//EN""><HTML><BODY>" _ 
 & vbCr & vbLf & "html goes here"  _ 
 & vbCr & vbLf & "</BODY></HTML>"

Adding custom headers

This is also good sample to show how to add a custom header to any PDI object:

// C#

Appointment appointment = new Appointment();
Event e = appointment.AddEvent();

const string html = @"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 3.2//EN""><HTML><BODY>
html goes here
</BODY></HTML>";

PdiHeader header = new PdiHeader("X-ALT-DESC", html);
header.KeyParameters.Add(new KeyValues("FMTTYPE", "text/html"));
e.AddCustomHeader(header);
' VB.NET

Dim appointment As New Appointment()
Dim e As [Event] = appointment.AddEvent()

Const  html As String = "<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 3.2//EN""><HTML><BODY>" _
 & vbCr & vbLf & "html goes here" _
 & vbCr & vbLf & "</BODY></HTML>"

Dim header As New PdiHeader("X-ALT-DESC", html)
header.KeyParameters.Add(New KeyValues("FMTTYPE", "text/html"))
e.AddCustomHeader(header)

ALTREP parameter

It is worth mentioning that RFC defines a way of specifying HTML content. The problem is that, it requires additional source (like email attachment or http address). Address of this resource, for example using “cid:” is set using ALTREP DESCRIPTION header:

DESCRIPTION;ALTREP="CID:part3.msg.970415T083000@example.com":
 Project XYZ Review Meeting will include the following agenda
   items: (a) Market Overview\, (b) Finances\, (c) Project Man
 agement

Mail.dll supports this feature also.

First we’ll define MIME object containing html data. Note that we are setting ContentId property.

// C#

MimeText html = new MimeFactory().CreateMimeText();
html.ContentType = ContentType.TextHtml;
html.Text = "<html><body>Html</body></html>";
html.ContentId = "part3.msg.970415T083000@example.com";
' VB.NET

Dim html As MimeText = New MimeFactory().CreateMimeText()
html.ContentType = ContentType.TextHtml
html.Text = "<html><body>Html</body></html>"
html.ContentId = "part3.msg.970415T083000@example.com"

Now we’ll create event:

// C#

Appointment appointment = new Appointment();
Event e = appointment.AddEvent();

e.Description = "Project XYZ Review Meeting will include the following agenda items: (a) Market Overview, (b) Finances, (c) Project Management";
e.DescriptionAltRep = "CID:" + html.ContentId;
' VB.NET

Dim appointment As New Appointment()
Dim e As [Event] = appointment.AddEvent()

e.Description = "Project XYZ Review Meeting will include the following agenda items: (a) Market Overview, (b) Finances, (c) Project Management"
e.DescriptionAltRep = "CID:" + html.ContentId

Finally we need to create an email with the event and html data:

// C#

MailBuilder builder = new MailBuilder();
builder.AddAttachment(html);
builder.AddAppointment(appointment);
IMail email = builder.Create();
' VB.NET

Dim builder As New MailBuilder()
builder.AddAttachment(html)
builder.AddAppointment(appointment)
Dim email As IMail = builder.Create()

Send iCalendar recurring meeting requests for different timezone

Saturday, June 9th, 2012

Usually you define time of an event in relation to UTC timezone or local timezone.

If you need to define event on 9:00 o’clock in Alaska you simply need to subtract 9 hours from event time to get the event time in UTC
(18:00:00). 18 – 9 = 9.

It all works great in December (Alaska is UTC-9), but in May, daylight saving time is in effect in Alaska (Alaska is UTC-8 then). In May, event is going to be held at 10:00 Alaska time (18 – 8 = 10). Which is most likely not what you want.

If you need to define a single instance of the event it is still not a problem. To get UTC time for the event you subtract 9 hours in the winter and 8 hours in the summer.

But if the event is recurring, and should be held always at 9 Alaska time, you need to specify the timezone along with the event.

As the timezones in different parts of world change way to often to reflect these changes in Mail.dll, you’ll need to specify the timezone when creating new event (including the daylight savings time).

Here’s the sample:

// C#

using Fluent = Limilabs.Mail.Fluent;
using Limilabs.Client.SMTP;
using Limilabs.Mail;
using Limilabs.Mail.Appointments;

Appointment appointment = new Appointment();

// Create time zone
VTimeZone alaska = appointment.AddTimeZone();
alaska.TimeZoneId = "America/Anchorage";

// Define standard time offset
var standardRecurring = new RecurringRule();
standardRecurring.Frequency = Frequency.Yearly;
standardRecurring.ByDay.Add(Weekday.FirstSunday);
standardRecurring.ByMonths.Add(11);

var standard = new StandardOffset
{
    Name = "AKST",
    Start = new DateTime(1970, 11, 01, 02, 00, 00),
    OffsetFrom = TimeSpan.FromHours(-8),
    OffsetTo = TimeSpan.FromHours(-9),
    RecurringRule = standardRecurring
};

// Define daylight time offset
var daylightRecurring = new RecurringRule();
daylightRecurring.Frequency = Frequency.Yearly;
daylightRecurring.ByDay.Add(new Weekday(2, Weekday.Sunday));
daylightRecurring.ByMonths.Add(3);

var daylight = new DaylightOffset
{
    Name = "AKDT",
    Start = new DateTime(1970, 03, 08, 02, 00, 00),
    OffsetFrom = TimeSpan.FromHours(-9),
    OffsetTo = TimeSpan.FromHours(-8),
    RecurringRule = daylightRecurring
};

alaska.Standard.Add(standard);
alaska.Daylight.Add(daylight);

// Define event
Event e = appointment.AddEvent();
e.Start = new DateTime(2007, 08, 17, 12, 00, 00);
e.End = new DateTime(2007, 08, 17, 12, 30, 00);
e.Summary = "At noon in Alaska";
e.InTimeZone(alaska);

// Make event recurring
RecurringRule rule = e.AddRecurringRule();
rule.Interval = 1;
rule.Until = new DateTime(2011, 12, 1); // -or- rule.Count = 10;

// Every week on  Mon, Tue, Wed, Thu Fri:
rule.Frequency = Frequency.Weekly;
rule.ByDay.AddRange(new[] {
    Weekday.Monday,
    Weekday.Tuesday,
    Weekday.Wednesday,
    Weekday.Thursday,
    Weekday.Friday });

// Set participants
e.SetOrganizer(new Person("Alice", "alice@example.org"));

e.AddParticipant(
    new Participant("Bob", "bob@example.org", ParticipationRole.Required, true));
e.AddParticipant(
    new Participant("Tom", "tom@example.org", ParticipationRole.Optional, true));
e.AddParticipant(
    new Participant("Alice", "alice@example.org", ParticipationRole.Required, true));

// Add alarm
Alarm alarm = e.AddAlarm();
alarm.BeforeStart(TimeSpan.FromMinutes(15));

IMail email = Fluent.Mail
    .Text("Status meeting at noon in Alaska.")
    .Subject("Status meeting")
    .From("alice@example.org")
    .To("bob@example.org")
    .To("tom@example.org")
    .AddAppointment(appointment)
    .Create();

using (Smtp smtp = new Smtp())
{
    smtp.Connect("smtp.example.org"); // or ConnectSSL
    smtp.UseBestLogin("user", "password");
    smtp.SendMessage(email);
    smtp.Close();
}
' VB.NET

Imports Fluent = Limilabs.Mail.Fluent
Imports Limilabs.Client.SMTP
Imports Limilabs.Mail
Imports Limilabs.Mail.Appointments

Dim appointment As New Appointment()

' Create time zone
Dim alaska As VTimeZone = appointment.AddTimeZone()
alaska.TimeZoneId = "America/Anchorage"

' Define standard time offset
Dim standardRecurring = New RecurringRule()
standardRecurring.Frequency = Frequency.Yearly
standardRecurring.ByDay.Add(Weekday.FirstSunday)
standardRecurring.ByMonths.Add(11)

Dim standard = New StandardOffset() With { _
	.Name = "AKST", _
	.Start = New DateTime(1970, 11, 1, 2, 0, 0), _
	.OffsetFrom = TimeSpan.FromHours(-8), _
	.OffsetTo = TimeSpan.FromHours(-9), _
	.RecurringRule = standardRecurring _
}

' Define daylight time offset
Dim daylightRecurring = New RecurringRule()
daylightRecurring.Frequency = Frequency.Yearly
daylightRecurring.ByDay.Add(New Weekday(2, Weekday.Sunday))
daylightRecurring.ByMonths.Add(3)

Dim daylight = New DaylightOffset() With { _
	.Name = "AKDT", _
	.Start = New DateTime(1970, 3, 8, 2, 0, 0), _
	.OffsetFrom = TimeSpan.FromHours(-9), _
	.OffsetTo = TimeSpan.FromHours(-8), _
	.RecurringRule = daylightRecurring _
}

alaska.Standard.Add(standard)
alaska.Daylight.Add(daylight)

' Define event
Dim e As [Event] = appointment.AddEvent()
e.Start = New DateTime(2007, 8, 17, 12, 0, 0)
e.[End] = New DateTime(2007, 8, 17, 12, 30, 0)
e.Summary = "At noon in Alaska"
e.InTimeZone(alaska)

' Make event recurring
Dim rule As RecurringRule = e.AddRecurringRule()
rule.Interval = 1
rule.Until = New DateTime(2011, 12, 1)
' -or- rule.Count = 10;
' Every week on  Mon, Tue, Wed, Thu Fri:
rule.Frequency = Frequency.Weekly
rule.ByDay.AddRange(New () {Weekday.Monday, Weekday.Tuesday, Weekday.Wednesday, Weekday.Thursday, Weekday.Friday})

' Set participants
e.SetOrganizer(New Person("Alice", "alice@example.org"))

e.AddParticipant( _
	New Participant("Bob", "bob@example.org", ParticipationRole.Required, True))
e.AddParticipant( _
	New Participant("Tom", "tom@example.org", ParticipationRole.[Optional], True))
e.AddParticipant( _
	New Participant("Alice", "alice@example.org", ParticipationRole.Required, True))

' Add alarm
Dim alarm As Alarm = e.AddAlarm()
alarm.BeforeStart(TimeSpan.FromMinutes(15))

Dim email As IMail = Fluent.Mail _
	.Text("Status meeting at noon in Alaska.") _
	.Subject("Status meeting") _
	.From("alice@example.org") _
	.[To]("bob@example.org") _
	.[To]("tom@example.org") _
	.AddAppointment(appointment) _
	.Create()

Using smtp As New Smtp()
	smtp.Connect("smtp.example.org") 	' or ConnectSSL
	smtp.UseBestLogin("user", "password")
	smtp.SendMessage(email)
	smtp.Close()
End Using

Western European Time Zone (WET)

Western European Time Zone (WET) with its Daylight Saving Time – Western European Summer Time (WEST) is defined below:

// Create time zone
VTimeZone western = appointment.AddTimeZone();
western.TimeZoneId = "W. Europe Standard Time";

// Define standard time offset
var standardRecurring = new RecurringRule();
standardRecurring.Frequency = Frequency.Yearly;
standardRecurring.ByDay.Add(new Weekday(-1, Weekday.Sunday));
standardRecurring.ByMonths.Add(10);

var standard = new StandardOffset
{
    Name = "WET", // Western European Time
    Start = new DateTime(1601,01,01,03,00,00),
    OffsetFrom = TimeSpan.FromHours(2),
    OffsetTo = TimeSpan.FromHours(1),
    RecurringRule = standardRecurring
};

// Define daylight time offset
var daylightRecurring = new RecurringRule();
daylightRecurring.Frequency = Frequency.Yearly;
daylightRecurring.ByDay.Add(new Weekday(-1, Weekday.Sunday));
daylightRecurring.ByMonths.Add(3);

var daylight = new DaylightOffset
{
    Name = "WEST", // Western European Summer Time
    Start = new DateTime(1601,01,01,02,00,00),
    OffsetFrom = TimeSpan.FromHours(1),
    OffsetTo = TimeSpan.FromHours(2),
    RecurringRule = daylightRecurring
};

western.Standard.Add(standard);
western.Daylight.Add(daylight);


Event e = appointment.AddEvent();
e.Start = new DateTime(2013, 04, 12, 15, 00, 00);
e.End = new DateTime(2013, 04, 12, 16, 00, 00);
e.Summary = "";
e.InTimeZone(western);

Receive iCalendar meeting request

Monday, January 30th, 2012

Mail.dll .NET email component makes receiving iCalendar meeting request fairly easy.

IMail object exposes Appointments collection that contains all appointments that were found while parsing an email.

You can use both IMAP or POP3 protocol to download emails from the server.

Here’s the simple sample showing how to process iCalendar appointments:

// C#

IMail email = new MailBuilder().CreateFromEml(client.GetMessageByUID(uid));

foreach (Appointment appointment in email.Appointments)
{
    if (appointment.Method == Method.Request)
    {
        // appointment was created
        string summary = appointment.Event.Summary;
        DateTime? start = appointment.Event.Start;
        DateTime? end = appointment.Event.End;
        string location = appointment.Event.Location;

        Console.WriteLine("{0} at {1} ({2}-{3})", summary, location, start, end);

        foreach (Participant participant in appointment.Event.Participants)
        {
            Console.WriteLine("Common name: " + participant.Cn);
            Console.WriteLine("Email: " + participant.Email);
            Console.WriteLine("Participation status: " + participant.Status);
        }
    }
    else if (appointment.Method == Method.Cancel)
    {
        // appointment was canceled
        Console.WriteLine("Event was cancelled: " + appointment.Event.UID);
        
    }
    else if (appointment.Method == Method.Reply)
    {
        // someone replied to the request
        foreach (Participant participant in appointment.Event.Participants)
        {
            if (participant.Status == ParticipationStatus.Accepted)
                Console.WriteLine("Event was accepted by: " + participant.Email);
            else if (participant.Status == ParticipationStatus.Declined)
                Console.WriteLine("Event was declined by: " + participant.Email);
        }

    }
}

' VB.NET

Dim email As IMail = New MailBuilder().CreateFromEml(client.GetMessageByUID(uid))

For Each appointment As Appointment In email.Appointments
	If appointment.Method = Method.Request Then
		' appointment was created
		Dim summary As String = appointment.[Event].Summary
		Dim start As System.Nullable(Of DateTime) = appointment.[Event].Start
		Dim [end] As System.Nullable(Of DateTime) = appointment.[Event].[End]
		Dim location As String = appointment.[Event].Location

		Console.WriteLine("{0} at {1} ({2}-{3})", summary, location, start, [end])

		For Each participant As Participant In appointment.[Event].Participants
			Console.WriteLine("Common name: " + participant.Cn)
			Console.WriteLine("Email: " + participant.Email)
			Console.WriteLine("Participation status: " + participant.Status)
		Next
	ElseIf appointment.Method = Method.Cancel Then
		' appointment was canceled

		Console.WriteLine("Event was cancelled: " + appointment.[Event].UID)
	ElseIf appointment.Method = Method.Reply Then
		' someone replied to the request
		For Each participant As Participant In appointment.[Event].Participants
			If participant.Status = ParticipationStatus.Accepted Then
				Console.WriteLine("Event was accepted by: " + participant.Email)
			ElseIf participant.Status = ParticipationStatus.Declined Then
				Console.WriteLine("Event was declined by: " + participant.Email)
			End If

		Next
	End If
Next

Send iCalendar meeting requests for different timezone

Sunday, January 1st, 2012

Usually you define time of an event in relation to current time zone. If you want to send such event to recipient in different time zone you should use UTC. This way you can be sure that you both meet at correct time.

First add all necessary namespaces:

// C# version

using System;
using Limilabs.Mail;
using Limilabs.Mail.Appointments;
using Fluent = Limilabs.Mail.Fluent;
using Limilabs.Client.SMTP;
' VB.NET version

Imports System;
Imports Limilabs.Mail;
Imports Limilabs.Mail.Appointments;
Imports Fluent = Limilabs.Mail.Fluent;
Imports Limilabs.Client.SMTP;

The easiest way to achieve this is to create specify time using local DateTime instance and invoke ToUniversalTime method, which translates it to UTC timezone. This also handles daylight saving time differences.

You can also use overloaded DateTime constructor that uses UTC timezone: DateTime(2012, 08, 17, 12, 00, 00, DateTimeKind.Utc).

// C#

DateTime start = new DateTime(2012, 08, 17, 12, 00, 00).ToUniversalTime();

Appointment appointment = new Appointment();
Event e = appointment.AddEvent();
e.Start = start;
e.End = start + TimeSpan.FromMinutes(30);
e.Summary = "At noon";

e.SetOrganizer(new Person("Alice", "alice@example.org"));

e.AddParticipant(
    new Participant("Bob", "bob@example.org", ParticipationRole.Required, true));
e.AddParticipant(
    new Participant("Tom", "tom@example.org", ParticipationRole.Optional, true));
e.AddParticipant(
    new Participant("Alice", "alice@example.org", ParticipationRole.Required, true));

Alarm alarm = e.AddAlarm();
alarm.BeforeStart(TimeSpan.FromMinutes(15));

IMail email = Fluent.Mail
    .Text("Status meeting at noon.")
    .Subject("Status meeting")
    .From("alice@example.org")
    .To("bob@example.org")
    .To("tom@example.org")
    .AddAppointment(appointment)
    .Create();

using (Smtp client = new Smtp())
{
    client.ConnectSSL("smtp.example.org");
    client.UseBestLogin("user", "password");
    client.SendMessage(email);
    client.Close();
}
' VB.NET

Dim start As DateTime = New DateTime(2012, 8, 17, 12, 0, 0).ToUniversalTime()

Dim appointment As New Appointment()
Dim e As [Event] = appointment.AddEvent()
e.Start = start
e.[End] = start + TimeSpan.FromMinutes(30)
e.Summary = "At noon"

e.SetOrganizer(New Person("Alice", "alice@example.org"))

e.AddParticipant( _
	New Participant("Bob", "bob@example.org", ParticipationRole.Required, True))
e.AddParticipant( _
	New Participant("Tom", "tom@example.org", ParticipationRole.[Optional], True))
e.AddParticipant( _
	New Participant("Alice", "alice@example.org", ParticipationRole.Required, True))

Dim alarm As Alarm = e.AddAlarm()
alarm.BeforeStart(TimeSpan.FromMinutes(15))

Dim email As IMail = Fluent.Mail.Text("Status meeting at noon.") _
	.Subject("Status meeting") _
	.From("alice@example.org") _
	.[To]("bob@example.org") _
	.[To]("tom@example.org") _
	.AddAppointment(appointment) _
	.Create()

Using client As New Smtp()
	client.ConnectSSL("smtp.example.org")
	client.UseBestLogin("user", "password")
	client.SendMessage(email)
	client.Close()
End Using

Things get a bit more complicated if you want to define recurring event in different timezone. You can not use UTC time zone in such case.

Send iCalendar recurring meeting requests

Friday, November 19th, 2010

First add all necessary namespaces:

// C# version

using System;
using Limilabs.Mail;
using Limilabs.Mail.Appointments;
using Limilabs.Mail.Headers;
using Fluent = Limilabs.Mail.Fluent;
using Limilabs.Client.SMTP;
' VB.NET version

Imports System;
Imports Limilabs.Mail;
Imports Limilabs.Mail.Appointments;
Imports Limilabs.Mail.Headers;
Imports Fluent = Limilabs.Mail.Fluent;
Imports Limilabs.Client.SMTP;

Now, we’ll create an appointment, specify it’s recurring options and send it:

// C# version

Appointment appointment = new Appointment();
Event newEvent = appointment.AddEvent();
newEvent.SetOrganizer(new Person("Alice", "alice@example.org"));
newEvent.AddParticipant(new Participant("Bob", "bob@gmail.com"));
newEvent.Description = "We need to talk about the daily report";
newEvent.Summary = "Daily report meeting";
newEvent.Start = new DateTime(2010, 11, 29, 16, 0, 0);
newEvent.End = new DateTime(2010, 11, 29, 17, 0, 0);
newEvent.Priority = 5;
newEvent.Class = EventClass.Public;

RecurringRule rule = newEvent.AddRecurringRule();
rule.Interval = 1;
rule.Until = new DateTime(2011, 12, 1); // -or- rule.Count = 10;

// Every week on  Mon, Tue, Wed, Thu Fri:
rule.Frequency = Frequency.Weekly;
rule.ByDay.AddRange(new[] {
    Weekday.Monday,
    Weekday.Tuesday,
    Weekday.Wednesday,
    Weekday.Thursday,
    Weekday.Friday });

Alarm alarm = newEvent.AddAlarm();
alarm.Description = "Reminder";
alarm.BeforeStart(TimeSpan.FromMinutes(5));

IMail email = Fluent.Mail.Text("Daily report meeting at 4PM")
    .Subject("Daily report meeting")
    .From(new MailBox("alice@example.org", "Alice"))
    .To(new MailBox("bob@example.org", "Bob"))
    .AddAppointment(appointment)
    .Create();

using (Smtp client = new Smtp())
{
    client.ConnectSSL("smtp.example.org");
    client.UseBestLogin("user", "password");
    client.SendMessage(email);
    client.Close();
}
' VB.NET version

Dim appointment As New Appointment()
Dim newEvent As [Event] = appointment.AddEvent()
newEvent.SetOrganizer(New Person("Alice", "alice@example.org"))
newEvent.AddParticipant(New Participant("Bob", "bob@gmail.com"))
newEvent.Description = "We need to talk about the daily report"
newEvent.Summary = "Daily report meeting"
newEvent.Start = New DateTime(2010, 11, 29, 16, 0, 0)
newEvent.[End] = New DateTime(2010, 11, 29, 17, 0, 0)
newEvent.Priority = 5
newEvent.[Class] = EventClass.[Public]

Dim rule As RecurringRule = newEvent.AddRecurringRule()
rule.Interval = 1
rule.Until = New DateTime(2011, 12, 1)
' -or- rule.Count = 10;
' Every week on  Mon, Tue, Wed, Thu Fri:
rule.Frequency = Frequency.Weekly
rule.ByDay.AddRange(New Weekday() {Weekday.Monday _
                                   , Weekday.Tuesday _
                                   , Weekday.Wednesday _
                                   , Weekday.Thursday _
                                   , Weekday.Friday})

Dim alarm As Alarm = newEvent.AddAlarm()
alarm.Description = "Reminder"
alarm.BeforeStart(TimeSpan.FromMinutes(5))

Dim email As IMail = Fluent.Mail.Text("Daily report meeting at 4PM") _
    .Subject("Daily report meeting") _
    .From(New MailBox("alice@example.org", "Alice")) _
    .[To](New MailBox("bob@example.org", "Bob")) _
    .AddAppointment(appointment).Create()

Using client As New Smtp()
    client.ConnectSSL("smtp.example.org")
    client.UseBestLogin("user", "password")
    client.SendMessage(email)
    client.Close()
End Using

This is how it looks like in Gmail:

Recurring appointment

Recurring meeting frequency

Here are the few samples of how you can set the frequency:

Daily:

// C# version

rule.Frequency = Frequency.Daily;
' VB.NET version

rule.Frequency = Frequency.Daily

Every month on last monday:

// C# version

rule.Frequency = Frequency.Monthly;
rule.ByDay.Add(Weekday.LastMonday);
' VB.NET version

rule.Frequency = Frequency.Monthly
rule.ByDay.Add(Weekday.LastMonday)

Every month on second but last monday:

// C# version

rule.Frequency = Frequency.Monthly;
rule.ByDay.Add(new Weekday(-2, Weekday.Monday));
' VB.NET version

rule.Frequency = Frequency.Monthly
rule.ByDay.Add(New Weekday(-2, Weekday.Monday))