CaldavCFC - Access and Update Caldav with ColdFusion

If you're looking to do any web dev work with Caldav Calendar Events, you could be running in circles for a while. Classes and components for working with Caldav are often buried inside larger projects and require many other dependencies. Come to find out they're not terribly difficult to work with once you figure out what's going on. I present to you CaldavCFC for querying, getting, updating, deleting Events and Todos on Caldav standard calendaring servers.

What is Caldav? Caldav is an Internet standard allowing a client to access scheduling information on a remote server. (Wikipedia)

This will serve as the documentation for CaldavCFC.

To understand how CaldavCFC works, a brief description of how Caldav in general works is necessary. On the remote server sits a bunch of iCalalendar formatted files. (.eml, .ics, etc..). Each file represents a calendar event or todo. Your goal is to create, update, delete, and query these files. The method to do this is with standard http requests GET, POST, PUT, DELETE, REPORT, etc. To make these requests we will use a socket connection. Don't worry, CaldavCFC handles the difficult socket stuff for you.

To use CaldavCFC you must first include and init the object to create an instance:

cdc = CreateObject('component','caldav').init(host=host,port=port,path=path,username=username,password=password)

Once you have the instance you may make calls to any of the following methods documented.

To use the Caldav you need to know what options are avaliable (GET, POST, PUT, etc.). To get these options use:

myEvent = cdc.options()

To get an iCal formatted file off the server:

myEvent = cdc.get("test-event-123456.ics")

To delete an iCal event from the server use:

myEvent = cdc.delete("test-event-123456.ics")

To create or update an event on the server you must pass an iCal string. If the name of the file does not exist, one will be created. If the filename does exist it will be updated. To accomplish either of the two use:

<cfsavecontent variable="myICal">BEGIN:VCALENDAR
DESCRIPTION:My event description details
SUMMARY:My Event Title 2.1

<cfset myEvent = cdc.edit("test-event-123456.ics", myICal)>

As you notice in the iCal format above there is a UID entry. This is important. You may have a method in place to create a unique id, but if not CreateUUID() may be a good start for you. With that unique id you can query for a single event or todo by that id:

myEvent = cdc.getEventByUid("my-test-uid-123456","","ical")
myTodo = cdc.getTodoByUid("my-test-uid-123456","","ical")
myEvent and myTodo looks like:
The third parameter passed can be 
"all" to get the entire response, 
"struct" to return a struct of the parsed ical string, or 
"ical" to return the raw ical string.


To query the server you can pass many options, here is the rundown:

- startDate String start date: 20120601T000000Z
- endDate String end date: 20120601T000000Z
- expand Boolean true to expand recurring events into separate events
- posFilters list of KEY:VALUE pairs to query against for a positive 
    match. Ex: "STATUS:NEEDS-ACTION,PRIORITY:1". Look at the raw ical 
    to see the pairs you can query against
- negFilters list of KEY:VALUE pairs to query against for a negative 
    match. Ex: "STATUS:COMPLETED,STATUS:CANCELLED" will get all todos 
    not cancelled and not completed
- urlAppend extend the host url to the ico file or folder
- mode how to return the results. "all" gives the http response, 
    "struct" parses out the ical, and "ical" is the text string.  
    struct and ical are  array of entries

The returned value is an array of entries similar to getEventByUid() or getTodoByUid()
myEvents = cdc.getEvents("20120701T000000Z", "20120801T000000Z", true, "", "", "", "struct")

myTodos = cdc.getTodos("20120601T000000Z","20120701T000000Z", false,"", "STATUS:COMPLETED,STATUS:CANCELLED", "", "struct")

The following methods are more advanced, but at your disposal if need be:

To create your own query use the following. It is highly suggested that you look at how getEvents and getEventByUid work.

- xmlFilter is a string of xml to specify the query
- urlAppend is a string path to append to the url requested
- mode is "all", "struct", or "ical"
myEvents = cdc.query(xmlFilter, urlAppend, mode)

To create a custom request all together use:

makeRequest will create a socket connection to the caldav server. You 
should use the getOptions method from this component to determine the 
request types (arguments.method) you may make.

- urlAppend extend the host url to the ico file (optional)
- method type of request to make (PUT, DELETE, GET, etc)
- data string of the data to send (ex: an iCalendar format string)
- contentType the content type of the data sent (ex: text/calendar)
- headers any extra headers to pass along [{name='', value=''}, ...]

returns a request object similar to 
{ headers={}, headersRaw="", body="", raw="" }
headers will be an object of each header value
body will be the content returned (xml, ical file, etc..)
raw and headersRaw is the unparsed response from the server
myRequest = cdc.makeRequest(urlAppend, method, data, contentType, headers)

Last but not least you may from time to time need to parse headers or an iCal formatted file. A utility function is provided to do so. Pass it a string and it gives you back a struct of values:

obj = cdc.parseResponse(myICalString)

The class overall is not difficult to use. It should also be noted that a related topic CardDav follows a very similar principle. With a few modifications this class could very well be transformed to access CardDav address books too. I have run this on ColdFusion and Open Blue Dragon successfully. As always enjoy!

Get CaldavCFC on Github