Calendar events and contact information with iCalendar and vCard

A common scenario in Enonic Vertical Site is listing events and contact information, and EVS provides a convenient interface for managing this across Intranets and public websites. Many users use desktop applications such as Microsoft Outlook to manage contacts and events, and may find it awkward to manually copy information from a website. In this article we will look at how we can use the iCalendar and vCard formats to enable users to automatically import information into their applications.

A brief overview of the formats

The iCalendar (based on the vCalendar format) and the vCard formats are for exchanging calendar events and contact information respectively, and supported by a large number of products. They are text based and fairly simple to work with.

Here is a sample iCalendar object that defines a project planning meeting on September 14th, from 09:00 to 10:30:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//foo/bar//NONSGML v1.0//EN
BEGIN:VEVENT
DTSTART:20081014T090000Z
DTEND:20081014T103000Z
SUMMARY:Project planning meeting
END:VEVENT
END:VCALENDAR

The field PRODID identifies the product (fictional in this case) that generated the data, and the date and times are in this case in UTC, as indicated by the Z at the end. To ensure that events appear at the right time in the calendar, a timezone should be specified. To specify a timezone other than UTC, DTSTART and DTEND should be on the following format:

DTSTART;TZID=Europe/Oslo:20081014T090000

Here is a sample vCard object that defines a contact:

BEGIN:VCARD
VERSION:3.0
N:Doe;John;;;
FN:John Doe
EMAIL;type=INTERNET;type=WORK;type=pref:test@example.com
TEL;type=WORK;type=pref:1234567
TEL;type=CELL:7654321
END:VCARD

This specifies a contact, John Doe, with an email address, work phone number and mobile phone number. The work phone number is in this case marked as preferred.

Content types for events and contacts

Before we show how to write XSLT for generating iCal and vCard objects, we will create two sample content types and create some content that we can use later. The first one we will call contact, the second event.

The first one will simply have first name, surname, email address and phone number:

<contenttype>
    <config version="1.0">
        <form>
            <title name="surname"/>
            <block name="Person">
                <input name="firstname" required="true" type="text">
                    <display>First name</display>
                    >xpath>contentdata/firstname</xpath>
                </input>
                <input name="surname" required="true" type="text">
                    <display>Surname</display>
                    <xpath>contentdata/surname</xpath>
                </input>
                <input name="phone" type="text">
                    <display>Phone</display>
                    <xpath>contentdata/phone</xpath>
                </input>
                <input name="email" type="text">
                    <display>E-mail</display>
                    <xpath>contentdata/email</xpath>
                </input>
            </block>
        </form>
    </config>
    <indexparameters>
        <index xpath="data/surname"/>
        <index xpath="data/firstname"/>
        <index xpath="data/mobile"/>
        <index xpath="data/email"/>
    </indexparameters>
</contenttype>

The latter will have a title, description, start date, start time, end date and end time:

<contenttype>
    <config version="1.0">
        <form>
            <title name="title"/>
            <block name="Event">
                <input name="title" required="true" type="text">
                    <display>Title</display>
                    <xpath>contentdata/title</xpath>
                </input>
                <input name="description" required="true" type="textarea">
                    <display>Description</display>
                    <xpath>contentdata/description</xpath>
                </input>
            </block>
            <block group="contentdata/times" name="Start time">
                <input name="starttime" type="text">
                    <display>Start time (hh:mm)</display>
                    <xpath>starttime</xpath>
                    <size value="5"/>
                </input>
                <input name="endtime" type="text">
                    <display>End time (hh:mm)</display>
                    <xpath>endtime</xpath>
                    <size value="5"/>
                </input>
                <input index="true" name="startdate" type="date">
                    <display>Start date</display>
                    <xpath>startdate</xpath>
                    <size value="10"/>
                    <maxlength value="10"/>
                </input>
                <input index="true" name="enddate" type="date">
                    <display>End date</display>
                    <xpath>enddate</xpath>
                    <size value="10"/>
                    <maxlength value="10"/>
                </input>
            </block>
        </form>
    </config>
    <indexparameters>
    </indexparameters>
</contenttype>

Next, create two categories, one for each content type, and add some events and contacts.

Generating vCard objects with XSLT

To generate iCal and vCard objects, we will be creating two page template XSLTs.

Generating vCards is fairly simple, our page template just needs to output the data as text on the form described earlier. Setting up XSLT to output text is not a problem, we just use xsl:output to tell the XSLT engine to output text. We will also set the correct media-type to make sure that the browser pass it on to the associated application.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet exclude-result-prefixes="saxon xs" version="2.0" xmlns:saxon="http://icl.com/saxon" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output encoding="utf-8" indent="no" method="text" omit-xml-declaration="yes" media-type="text/x-vcard"/>

  <xsl:template match="content">

<xsl:text>BEGIN:VCARD
VERSION:3.0
N:</xsl:text><xsl:value-of select="contentdata/surname"/>;<xsl:value-of select="contentdata/firstname"/><xsl:text>;;;
FN:</xsl:text><xsl:value-of select="contentdata/firstname"/><xsl:text> </xsl:text><xsl:value-of select="contentdata/surname"/><xsl:text>
EMAIL;type=INTERNET;type=WORK;type=pref:</xsl:text><xsl:value-of select="contentdata/email"/><xsl:text>
TEL;type=WORK;type=pref:</xsl:text><xsl:value-of select="contentdata/phone"/><xsl:text>
END:VCARD
</xsl:text>

  </xsl:template>
</xsl:stylesheet>

The reason xsl:text is used so much here, is to ensure that we get the right formatting in the resulting text. In HTML, white space is not so important and we can accept spaces and newlines in places where they are not necessarily meant to be, but with both vCard and iCal, we need to pay special attention to this.

After creating this XSL, we can create a page template based on it, with the following datasource.

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
  <datasource>
    <methodname>getContent</methodname>
    <parameters>
      <parameter name="key" override="url" type="int">-1</parameter>
      <parameter name="parentLevel" type="int">0</parameter>
      <parameter name="childrenLevel" type="int">2</parameter>
      <parameter name="parentChildrenLevel" type="int">0</parameter>
      <parameter name="updateStatistics" type="boolean">true</parameter>
      <parameter name="relatedTitlesOnly" type="boolean">false</parameter>
      <parameter name="userRights" type="boolean">false</parameter>
      <parameter name="categoryFilter" type="int[]"/>
      <parameter name="categoryRecursive" type="boolean"/>
      <parameter name="ctyFilter" type="int[]"/>
    </parameters>
  </datasource>
</datasources>

Finally we create a page that we can link to with a content key to get our vCard generated.

Generating iCal objects with XSLT

Generating iCal objects can be a little bit more complex, it will depend on the type of event - perhaps recurring or multi-day event). In this case we will keep it simple and only generate events occurring on a single day.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet exclude-result-prefixes="saxon xs" version="2.0" xmlns:saxon="http://icl.com/saxon" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output encoding="utf-8" indent="no" method="text" omit-xml-declaration="yes" media-type="text/calendar"/>

  <xsl:template match="content">
<xsl:text>BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//enonic/vertical site//NONSGML v1.0//EN
BEGIN:VEVENT
DTSTART:</xsl:text><xsl:value-of select="translate(contentdata/times/startdate, '-', '')"/>T<xsl:value-of select="translate(contentdata/times/starttime, ':', '')"/><xsl:text>00Z
DTEND:</xsl:text><xsl:value-of select="translate(contentdata/times/enddate, '-', '')"/>T<xsl:value-of select="translate(contentdata/times/endtime, ':', '')"/><xsl:text>00Z
SUMMARY:</xsl:text><xsl:value-of select="title"/><xsl:text>
END:VEVENT
END:VCALENDAR
</xsl:text>

  </xsl:template>
</xsl:stylesheet>

Next we create a page template and page, as we did for the vCard objects. It might also be necessary to give this page a name ending in ".ics", to make sure that the browser sends it to the right application, although it should be enough to set the media type (as we do in xsl:output).

Summary

As we have now shown, it is fairly simple to create vCard and iCal objects, and it has fairly high value for users who use calendar and address book applications. Although this article doesn't show all the intricacies of the formats, it should be a good starting point for extending websites with this kind of functionality.

Comments

If you want to comment on this article you need to be logged in.

Published in 2011

2010

2009

2008

2007