Generating an iCalendar .ics file using ColdFusion
I've been working on a small project at work to allow a user to click a link from an event registration confirmation email, which will open a .ics file in the browser. This special file is in the iCalendar format, and is recognized by MS Outlook, Mozilla Sunbird, and other calendaring tools. It creates an entry in your calendar, with a 30 minute reminder alert, to help you remember to attend the webinar event you registered for. I learned from the vCal() function on cflib.org, and will be submitting my new iCalUS() UDF soon. But here is the code now, as it takes some time to get verified for inclusion on the CFLib.org site.
Download the code in a zip file.
I wrote the function to work in the U.S. and account for daylight savings time. Hopefully developers outside the U.S. can adapt this code to fit their timezones accordingly. Here is the code used for the test submission form and cfcontent/cfheader tags for the following demo.
<P>
<cfoutput>
<form method="post">
<table>
<tr>
<td align="right">Organizer name</td>
<td><input type="Text" name="on" value="#Form.on#" size="30"></td>
</tr>
<tr>
<td align="right">Organizer email</td>
<td><input type="Text" name="oe" value="#Form.oe#" size="30"></td>
</tr>
<tr>
<td align="right">Description</td>
<td><input type="Text" name="desc" value="#Form.desc#" size="60"> (use \n sequences for newlines)</td>
</tr>
<tr>
<td align="right">Subject</td>
<td><input type="Text" name="sub" value="#Form.sub#" size="30"></td>
</tr>
<tr>
<td align="right">Location</td>
<td><input type="Text" name="loc" value="#Form.loc#" size="30"></td>
</tr>
<tr>
<td align="right">Start Date/Time</td>
<td><input type="Text" name="st" value="#Form.st#" size="20"> (format: <b>m/d/yyyy HH:mm</b> OR <b>h:mm TT</b> -- this is Eastern time)</td>
</tr>
<tr>
<td align="right">End Date/Time</td>
<td><input type="Text" name="et" value="#Form.et#" size="20"> (format: <b>m/d/yyyy HH:mm</b> OR <b>h:mm TT</b> -- this is Eastern time)</td>
</tr>
</table>
<input type="Submit" name="Submit" value="Submit">
</form>
</cfoutput>
<cfif IsDefined("Form.Submit")>
<cfset eventStr = StructNew()>
<cfset eventStr.organizerName = Form.on>
<cfset eventStr.organizerEmail = Form.oe>
<cfset eventStr.startTime = ParseDateTime(Form.st)>
<cfset eventStr.endTime = ParseDateTime(Form.et)>
<cfset eventStr.subject = Form.sub>
<cfset eventStr.location = Form.loc>
<cfset eventStr.description = Form.desc>
<cfcontent type="text/calendar" reset="Yes">
<cfheader name="Content-Disposition" value="inline; filename=newAppointment.ics"><cfoutput>#iCalUS(eventStr)#</cfoutput>
</cfif>
Here is a demo of this in action.
-- Update 4/10/08: I submitted the UDF to cflib.org today. Hopefully Ray will post it soon. --


Go Whitesox!!
thanks for this great post. just curious, how can we add additional fields so that we can add some event details? we want to integrate into outlook
I was trying to put your code to our website. when I run it, I can create the iCal file, but when I open it, I got a error to import ti the outlook.
The error message: This error can appear if you have attempted to save a recurring Lunar appointment in iCalendar format.
To avoid this error, set the appointment option to Gregorian instead of Lunar.
here is my test website:
http://1bosdev1.conferon.com/demo/demo2007/Agenda/...
Please help me. We would like to use your code in our application.
Thanks
Jason
http://blog.webdh.com/demos/iCalUS.zip
http://severinghaus.org/projects/icv/
Thanks,
Robert
Thanks,
Robert
Any suggestions? I really want to use this code but I seem to be tripping at the finish line when it goes to save to Outlook.
The problem seems to be in the "direct" delivery via cfcontent.
I've tried the exact same setup with IIS/CF7 (successful) and Apache2/Railo (no success)
Verified the mime definition of ics in both servers,
IIS: ics=applicatin/octet-stream
Ap: ics=text/calendar
modified the Mimetypes in Apache to match that of IIS -> Still no success
Wondered if you could explain to me how to adjust the timezone of the iCalUS() script. Specifically to default to pacific time. I also had a hard time decyphering how the time offset for daylight savings works. Thanks in advance, very useful function.
On 3/30, I replied back with:
In the code, these values are setup for Eastern time
vCal = vCal & "TZOFFSETFROM:-0500" & CRLF;
vCal = vCal & "TZOFFSETTO:-0400" & CRLF;
so to modify to Pacific, I think you'd change to this (2 spots in the code)
vCal = vCal & "TZOFFSETFROM:-0800" & CRLF;
vCal = vCal & "TZOFFSETTO:-0700" & CRLF;
The DST should just work, it uses the logic (copied from Wikipedia) which started in 2007, most of the United States and Canada observe DST from the second Sunday in March to the first Sunday in November. This corresponds to these 2 lines of code.
vCal = vCal & "RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SU;BYMONTH=3" & CRLF;
vCal = vCal & "RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=11" & CRLF;
I have added my query to the top of the page, but keep getting "Element ON is undefined in FORM."
Here is my code:
----------------------
<cfquery name="rsEmailCallDataOL" datasource="salesPipeline">
SELECT tblCallTracker.wkClsCallRem, tblCallTracker.wkClsCallBack, tblWeeklyRpt.wkRptID, tblCallTracker.wkClsEntDate, tblCallTracker.wkClsNum, tblWeeklyRpt.wkRptCustName, tblCallTracker.id_ClsWkRpt, tblCallTracker.wkClsID, user_usr.name_usr, user_usr.email_usr, tblWeeklyRpt.wkRptCustCity, tblWeeklyRpt.wkRptContact
FROM ((tblCallTracker
LEFT JOIN tblWeeklyRpt ON tblWeeklyRpt.wkRptID=tblCallTracker.id_ClsWkRpt)
INNER JOIN user_usr ON user_usr.id_usr=tblWeeklyRpt.wkRptUser)
WHERE tblCallTracker.wkClsID = <cfqueryparam value="#URL.id_com#" cfsqltype="cf_sql_numeric">
</cfquery>
<b>newAppointment.ics</b>
<cfoutput>
<cfform method="post" name="formOL" id="formOL">
<table>
<tr>
<td align="right">Account Exec</td>
<td><cfinput type="Text" name="on" value="#rsEmailCallDataOL.name_usr#" size="30"></td>
</tr>
<tr>
<td align="right">Email</td>
<td><cfinput type="Text" name="oe" value="#rsEmailCallDataOL.email_usr#" size="30"></td>
</tr>
<tr>
<td align="right">Contact Name</td>
<td><cfinput type="Text" name="desc" value="#rsEmailCallDataOL.wkRptContact#" size="60">
(use \n sequences for newlines)</td>
</tr>
<tr>
<td align="right">Customer Name</td>
<td><cfinput type="Text" name="sub" value="#rsEmailCallDataOL.wkRptCustName#" size="30"></td>
</tr>
<tr>
<td align="right">Phone Number</td>
<td><cfinput type="Text" name="loc" value="#rsEmailCallDataOL.wkClsNum#" size="30"></td>
</tr>
<tr>
<td align="right">Called Date/Time</td>
<td><cfinput type="Text" name="st" value="#rsEmailCallDataOL.wkClsEntDate#" size="20">
(format: <b>m/d/yyyy HH:mm</b> OR <b>h:mm TT</b> -- this is Eastern time)</td>
</tr>
<tr>
<td align="right">Call Back Date/Time</td>
<td><cfinput type="Text" name="et" value="#rsEmailCallDataOL.wkClsCallBack#" size="20">
(format: <b>m/d/yyyy HH:mm</b> OR <b>h:mm TT</b> -- this is Eastern time)</td>
</tr>
</table>
<cfinput type="Submit" name="Submit" value="Submit">
</cfform>
</cfoutput>
<cfif IsDefined("Form.Submit")>
<cfset eventStr = StructNew()>
<cfset eventStr.organizerName = Form.on>
<cfset eventStr.organizerEmail = Form.oe>
<cfset eventStr.startTime = ParseDateTime(Form.st)>
<cfset eventStr.endTime = ParseDateTime(Form.et)>
<cfset eventStr.subject = Form.sub>
<cfset eventStr.location = Form.loc>
<cfset eventStr.description = Form.desc>
<cfcontent type="text/calendar" reset="Yes">
<cfparam name="URL.id_com" default="-1">
<cfheader name="Content-Disposition" value="inline; filename=newAppointment.ics"><cfoutput>#iCalUS(eventStr)#</cfoutput>
</cfif>
----------------------------------
Basically, I just want the Form to open up, (similiar to your Demo), but with the Form fields already pre-filled already with my data from query?
Thanks,
ja
thanks in advance,
Simon.
I need it to not open in the browser, but be emailed...
What do I need to change in the following block of code to automatically email the generated .ics file??
<cfcontent type="text/calendar" reset="Yes">
<cfheader name="Content-Disposition" value="filename=newAppointment.ics" >
<cfoutput>#iCalUS(eventStr)#</cfoutput>
Thanks!
<!--- build to list --->
<cfset toEmail = "">
<cfloop query="users">
<cfset toEmail = toEmail & users.email_address & ",">
</cfloop>
<!--- create message body --->
<cfset myMessage = "BEGIN:VCALENDAR"&chr(13)&chr(10)>
<cfset myMessage = myMessage & "VERSION:2.0"&chr(13)&chr(10)>
<cfset myMessage = myMessage & "METHOD:REQUEST"&chr(13)&chr(10)>
<cfset myMessage = myMessage & "BEGIN:VEVENT"&chr(13)&chr(10)>
<!--- use a sequence number to let notes know if this is the first occurence of this event --->
<cfset myMessage = myMessage & "SEQUENCE:"&#event.sequence#&chr(13)&chr(10)>
<cfset myMessage = myMessage & "ORGANIZER;CN=Common Name for Organizer:MAILTO:email address for organizer"&chr(13)&chr(10)>
<cfloop query="users">
<cfset myMessage = myMessage & "ATTENDEE;RSVP=YES;TYPE=INDIVIDUAL;MAILTO:"&#users.email_address#&chr(13)&chr(10)>
</cfloop>
<cfset myMessage = myMessage & "DTSTART:"&#DateFormat(startDate,"YYYYMMDD")#&"T"&#FORM.startHours#&#FORM.startMinutes#&"00Z"&chr(13)&chr(10)>
<cfset myMessage = myMessage & "DTEND:"&#DateFormat(endDate,"YYYYMMDD")#&"T"&#FORM.endHours#&#FORM.endMinutes#&"00Z"&chr(13)&chr(10)>
<!--- in notes each event must have a unique id --->
<cfset myMessage = myMessage & "UID:"&#event.uniqueEventId#&chr(13)&chr(10)>
<cfset myMessage = myMessage & "LOCATION:"&#FORM.location#&chr(13)&chr(10)>
<cfset myMessage = myMessage & "DESCRIPTION:"&#FORM.description#&chr(13)&chr(10)>
<cfset myMessage = myMessage & "SUMMARY:"&#FORM.calendarName#&chr(13)&chr(10)>
<cfset myMessage = myMessage & "PRIORITY:3"&chr(13)&chr(10)>
<cfset myMessage = myMessage & "STATUS:CONFIRMED"&chr(13)&chr(10)>
<cfset myMessage = myMessage & "END:VEVENT"&chr(13)&chr(10)>
<cfset myMessage = myMessage & "END:VCALENDAR"&chr(13)&chr(10)>
<!--- trim the last comma off the end --->
<cfset toEmail = #Left(toEmail,Len(toEmail)-1)#>
<!--- send out invites --->
<cfmail to="#toEmail#" from="generic account <generic email>" subject="Calendar Entry" type="multipart">
<cfmailparam name="content-class" value="urn:content-classes:calendarmessage">
<cfmailparam name="content-type" value="text">
<cfmailparam name="charset" value="utf-8">
<cfmailparam name="content-transfer-encoding" value="7bit">
<cfmailpart type="text/calendar">
<cfoutput>#myMessage#</cfoutput>
</cfmailpart>
</cfmail>