Thursday 31 January 2013

Automatically Inserting events into Google Calendar using Zend Framework

It's been a while since I posted something on this blog so here is a little problem I sorted out and wanted to share the code to help anyone out there who was planning on doing something similar.

The issue: I had an XML file containing events that needed to be entered into a Google Calendar automatically. I needed my script to log into my Google account and insert the event onto my Calendar. I looked at the Google Calendar API but didn't like it and it required PHP5.3+ according to the instructions and the host I had to get my script working on was PHP5.2. So I went to find another way of solving the problem and found the solution available in the Zend Framework Gdata module. I'm using ZF 1.12 files in my examples. Here is the Calendar documentation.

So, I downloaded the ZF zip archive, but because it's soooo huge, I only took out the files and folders that I needed and stuck them into a 'Zend' folder at the root of my src directory:

/Zend/Feed/
/Zend/Gdata/
/Zend/Http/
/Zend/Loader/
/Zend/Uri/
/Zend/Validate/
/Zend/Exception.php
/Zend/Gdata.php
/Zend/Loader.php/
/Zend/Uri.php
/Zend/Vaildate.php
/Zend/Version.php

Then we need to think how to log into Google to use its services. Zend's Google library has three ways of doing the log in.

For my purpose I did not need anything special. I didn't need a login form or to have an authentication token cookie passed through successive pages. All I needed was to have the same login credentials hard-coded into my script and used for a single script run, so I used the ClientAuth direct login method.

I created a new script at the root of my src directory.

calendar.php

<?php

//Step 1:
//Turn on error reporting so we know what's going on if it doesn't work
//Set your time zone location so your local time is correct and not the server's time zon
//Register the autoloader to automatically load the classes from the Zend Framework when needed

ini_set('display_errors', 1);
ini_set('error_reporting', E_ALL);

date_default_timezone_set('Europe/London');

require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->suppressNotFoundWarnings(false);
$autoloader->setFallbackAutoloader(true);


//Step 2:
//Log into Google
//Create an instance of the Zend_Gdata_Calendar class that we use to send and receive responses from Google Calendar

$email = 'someguy@gmail.com';
$pass = 'monkey';

try {
   $client = Zend_Gdata_ClientLogin::getHttpClient($email, $pass, 'cl'); //cl is to signify the calendar app is what we are using

} catch (Zend_Gdata_App_CaptchaRequiredException $cre) {
    echo 'URL of CAPTCHA image: ' . $cre->getCaptchaUrl() . "\n";
    echo 'Token ID: ' . $cre->getCaptchaToken() . "\n";
} catch (Zend_Gdata_App_AuthException $ae) {
    echo 'Problem authenticating: ' . $ae->exception() . "\n";
}

$gcal = new Zend_Gdata_Calendar($client);



//Step 3:
//Create an event object and use the awesome PHP DateTime class to set the time and duration

$event = $gcal->newEventEntry();
$event->title = $gcal->newTitle('FNP Meeting @ 2030');

$when = $gcal->newWhen();
$startTime = new DateTime('2013-02-01 20:30:00'); //enter your start date and time here
$duration = '30 minutes'; //how long the event is
$when->startTime = $starttime->format(DateTime::RFC3339); //Only Atom-format time is allowed
$when->endTime = $starttime->modify('+' . $duration)->format(DateTime::RFC3339);
$event->when = array($when);

//insert event - the $newEvent variable contains details and the unique id of the inserted event
$newEvent = $gcal->insertEvent($event);


?>

That's it. Simple no?! I only used what I needed but the library offers more options if you care to read the documentation.

The DateTime class can be instantiated with a Unix timestamp as well. In my XML I have the time in a non-standard format and used some PHP code to split it up into individual parts and passed it to the mktime() function. By adding the @ symbol before the timestamp it makes it get parsed correctly by DateTime.

$startTime = new DateTime('@' . mktime((int)$hour, (int)$min, 0, $month, $daynum, $year));

My biggest criticism of this process of inserting events into a Google Calendar in this way is that it is SLOW! Each event insertion requires a http post to the Google server and waiting for the Google server to respond, and if it takes my server 200ms to push the event to Google servers, 100ms for Google to process my query, and 200ms to send the response back, that's 500ms, half a second to carry out a single event insertion. What if I have hundreds of things to insert... sorry, you have to do them one by one - there is no bulk insert API. The alternative is to use some kind of "HTTP multi-threading" approach since PHP doesn't do multi-threading.