iCalendar event generator

This article is also available in the following languages:
By immersionit
I was looking for a way to export data in an iCalendar (RFC 2445) compatible format. To accomplish this I created a helper class that generates this code using the iCalcreator class.
There are two major iCalendar classes available for PHP, phpicalendar http://phpicalendar.net/ and iCalcreator http://www.kigkonsult.se/iCalcreator/index.php. I chose the later since it was a single class file and slightly better from a object oriented point of view.

The first step is to download a copy of iCalcreator and place the iCalcreator.class.php file in your /vendors folder. http://www.kigkonsult.se/downloads/index.php
Now place the following helper class into your /views/helpers folder.

Helper Class:

<?php 
require_once('vendors/iCalcreator.class.php');

class 
ICalHelper extends 

Helper 
{
    var 
$name 'ICalHelper';
    var 
$errorCode null;
    var 
$errorMessage null;
    
    var 
$calendar;
            
    function 
create($name$description=''$tz='US/Eastern')
    {
        
$v = new vcalendar();
        
$v->setConfig('unique_id'$name.'.'.'yourdomain.com');
        
$v->setProperty('method''PUBLISH');
        
$v->setProperty('x-wr-calname'$name.' Calendar');
        
$v->setProperty("X-WR-CALDESC"$description);
        
$v->setProperty("X-WR-TIMEZONE"$tz);
        
$this->calendar $v;
    }
    
    function 
addEvent($start$end=false$summary$description=''$extra=false)
    {
        
$start strtotime($start);
        
        
$vevent = new vevent();
        if(!
$end)
        {
            
$end $start 24*60*60;
            
$vevent->setProperty('dtstart'date('Ymd'$start), array('VALUE'=>'DATE'));
            
$vevent->setProperty('dtend'date('Ymd'$end), array('VALUE'=>'DATE'));
        }
        else
        {
            
$end strtotime($end);
            
$end getdate($end);
            
$end['sec'] = $end['second'];
            
$end['hour'] = $end['hours'];
            
$end['min'] = $end['minutes'];
            
$end['month'] = $end['mon'];
            
            
$start getdate($start);
            
$start['sec'] = $start['second'];
            
$start['hour'] = $start['hours'];
            
$start['min'] = $start['minutes'];
            
$start['month'] = $start['mon'];
            
            
$vevent->setProperty('dtstart'$start);
            
$vevent->setProperty('dtend'$end);            
        }
        
$vevent->setProperty('summary'$summary);
        
$vevent->setProperty('description'$description);
        if(
is_array($extra))
        {
            foreach(
$extra as $key=>$value)
            {
                
$vevent->setProperty($key$value);
            }
        }
        
$this->calendar->setComponent($vevent);
    }
    
    function 
getCalendar()
    {
        return 
$this->calendar;
    }
    
    function 
render()
    {
        
$this->calendar->returnCalendar();
    }
}
?>

Once you have completed this step you may want to support ics extensions in your application, which is required by some calendar applications.

To do this, add ics to your router extensions in config/routes.php


Router::parseExtensions('ics'); 

Now setup a function in your controller for your calendar view. It might look something like:

Controller Class:

<?php 
   
class Project extends AppController
   
{
        var 
$helpers = array('Html''Text''ICal');        
    function 
due() {
        
$this->Project->recursive 0;
        
$this->paginate = array(
          
'Project' => array(
            
'order' => 'due ASC',
            
'limit' => 5,
            
'scope' => array('complete = 0 AND due IS NOT NULL')
          )
        );
        
$projects_due $this->paginate();
        return 
$projects_due;
    }
    }
?>

Now create a view for your data. In my case to speed up the operation I used the caching instructions from http://bakery.cakephp.org/articles/view/optimizing-your-cakephp-elements-and-views-with-caching and created an element (elements/projects_due.ctp) to handle the conversion of to ics.


<?php
    $projects 
$this->requestAction('projects/due');
    
$iCal->create('Activeprojects''Active outstanding projects''US/Eastern');
    
    foreach(
$projects as $Project)
    {
        
$iCal->addEvent($Project['Project']['due'], false$Project['Project']['title'], $Project['Project']['description']."\n\n".$html->url('/Project/view/'.$Project['Project']['id'], true), array('UID'=>$Project['Project']['id'], 'attach'=>$html->url('/Project/view/'.$Project['Project']['id'], true), 'organizer'=>$Project['User']['username'], 'location'=>$Project['location']));
    }
    
$iCal->render();
?>

Now include this element in your view (views/projects/due.ctp).

View Template:


<?php 
echo $this->element('projects_due', array('cache'=>'+1 hour')); 
?>

Now go to http://yourdomain.com/projects/due.ics and you should get an ics file download that you can open in any icalendar compatible program or you can paste that URL in Google calendar.

Comments

  • Posted 05/06/11 04:36:46 PM
    Hello,
    the helper is still working with cake 1.3.6

    Just a note, if you want to export the GEO property of the VEVENT. While trying to export it, I had to treat the property individually:

    foreach($extra as $key=>$value)
    {
    if($key == 'geo') {
    $vevent->setProperty($key, $value['latitude'], $value['longitude']);
    }
    else {
    $vevent->setProperty($key, $value);
    }
    }

    (instead of
    foreach($extra as $key=>$value)
    {
    $vevent->setProperty($key, $value);
    }
    in the helper class)

    Didn't check other array based properties, perhaps it could be generalized
  • Posted 03/15/09 12:14:34 AM
    class ICalHelper does not works because mismatch of typesin helper and iCalcreator library.

    iCalcreator library checks the type of input by counting array's element. So, I changed helper class like this:

    function addEvent($start, $end=false, $summary, $description='', $extra=false)
    .
    .
    .
    else
    {
    $end = strtotime($end);

    $start = getdate($start);
    $a_start['year'] = $start['year'];
    $a_start['month'] = $start['mon'];
    $a_start['day'] = $start['mday'];
    $a_start['hour'] = $start['hours'];
    $a_start['min'] = $start['minutes'];
    $a_start['sec'] = $start['seconds'];

    $end = getdate($end);
    $a_end['year'] = $end['year'];
    $a_end['month'] = $end['mon'];
    $a_end['day'] = $end['mday'];
    $a_end['sec'] = $end['seconds'];
    $a_end['hour'] = $end['hours'];
    $a_end['min'] = $end['minutes'];

    $vevent->setProperty('dtstart', $a_start);
    $vevent->setProperty('dtend', $a_end);
    }

    .
    .
    .


  • Posted 01/28/09 10:20:53 PM
    Google Calendar doesn't seem to want to import it. The title of the calendar is something like "tuumr5se9auuumc9fs@import.google.com"

    Any ideas why this is happening?
  • Posted 01/13/09 10:14:38 AM
    Hi, thanks for sharing! Can you please remove the link to your website since only case studies are allowed to have them, as per the bakery guidelines?

Comments are closed for articles over a year old