Back in September of last year, I authored a plugin and template for the excellent Expression Engine (EE) content management system to output iCal/vCal files. The template is rather short and I took a bit of time today to document how it works.
I had previously posted a fully working template on the EE forums. It does seem overly simple, but everything necessary is right there. It is a mix of PHP, EE and of course XML.
Here’s the breakdown:
In the first block of PHP, I’m getting the current date in a format I can use and storing as a variable. Specifically, last month in ‘d-m-Y’ format and next year in ‘d-m-Y’ format.
<?php
// last month in ‘d-m-Y’ format
$uts[‘lastmonth’] = strtotime( ‘-1 months’ ); // or, strtotime( ‘yesterday’ );
$last_month = date( ‘Y-m-d g:i A’, $uts[‘lastmonth’] );
// next year in ‘d-m-Y’ format
$uts[‘nextyear’] = strtotime( ‘+1 years’ ); // or, strtotime( ‘yesterday’ );
$next_year = date( ‘Y-m-d g:i A’, $uts[‘nextyear’] );
?>
Now, we start into XML as required by the vCal standard.
BEGIN:VCALENDAR
VERSION:2.0
X-WR-CALNAME:BMI Event Calendar
PRODID:-//BMI/ Events//NONSGML v1.0//EN
X-WR-TIMEZONE:US/Eastern
Now, some standard EE code to get weblog data. Here’s where I use those PHP date variables we grabbed at the beginning to limit the data returned by EE. My weblog has > 20,000 events listed, so I list one month back and one year forward.
{exp:weblog:entries weblog=“events” status=“not closed” show_future_entries=“yes” disable=“member_data|pagination|trackbacks|categories” show_expired=“no” orderby=“date” sort=“asc” start_on=”<?php echo $last_month; ?>” stop_before=”<?php echo $next_year; ?>” limit=“1000”}
This data is wrapped in the proper XML formatting. On my calendar, I don’t have proper times set in the weblog entries, so I leave them at midnight. (T120001)
BEGIN:VEVENT
UID:{entry_id}
DTSTAMP:{entry_date format="%Y%m%dT%H%i%sZ"}
DTSTART;VALUE=DATE:{entry_date format="%Y%m%d"}T120001
DTEND;VALUE=DATE:{entry_date format="%Y%m%d"}T120002
Here’s where I had to cleanup the data a bit to conform to the strict vCal spec with my own little vcal_encode plugin. (vCal doesn’t like extended characters, linebreaks, etc.)
SUMMARY:{exp:vcal_encode}{title}{/exp:vcal_encode}
DESCRIPTION:{exp:vcal_encode}{long}{/exp:vcal_encode}
Finally, I wrap up the template by closing the vCal file and the EE tags.
END:VEVENT
{/exp:weblog:entries}
END:VCALENDAR
Remember that this template must be served from EE as XML and PHP must be enabled for it to work properly. You may have named fields differently from mine, but you should be able to just plug those right in.
You can see all this in action here:
The vCal link: webcal://www.bmi.com/events/ical
The calendar: http://www.bmi.com/events/calendar/
Full template
<?php
// last month in ‘d-m-Y’ format
$uts[‘lastmonth’] = strtotime( ‘-1 months’ ); // or, strtotime( ‘yesterday’ );
$last_month = date( ‘Y-m-d g:i A’, $uts[‘lastmonth’] );
// next year in ‘d-m-Y’ format
$uts[‘nextyear’] = strtotime( ‘+1 years’ ); // or, strtotime( ‘yesterday’ );
$next_year = date( ‘Y-m-d g:i A’, $uts[‘nextyear’] );
?>BEGIN:VCALENDAR
VERSION:2.0
X-WR-CALNAME:BMI Event Calendar
PRODID:-//BMI/ Events//NONSGML v1.0//EN
X-WR-TIMEZONE:US/Eastern
{exp:weblog:entries weblog=“events” status=“not closed” show_future_entries=“yes” disable=“member_data|pagination|trackbacks|categories” show_expired=“no” orderby=“date” sort=“asc” start_on=”<?php echo $last_month; ?>” stop_before=”<?php echo $next_year; ?>” limit=“1000”}BEGIN:VEVENT
UID:{entry_id}
DTSTAMP:{entry_date format="%Y%m%dT%H%i%sZ"}
DTSTART;VALUE=DATE:{entry_date format="%Y%m%d"}T120001
DTEND;VALUE=DATE:{entry_date format="%Y%m%d"}T120002
SUMMARY:{exp:vcal_encode}{title}{/exp:vcal_encode}
DESCRIPTION:{exp:vcal_encode}{long}{/exp:vcal_encode}
END:VEVENT
{/exp:weblog:entries}
END:VCALENDAR
One final tip- I found it very helpful to debug the rendered code using curl at the command line. On my setup, I just pipe it into TextMate like this:
curl http://www.bmi.com/events/ical | mate