เขียน PHP application ร่วมกับ Google Calendar


หน้าแรก PHP MySQL เกร็ดความรู้ เขียน PHP application ร่วมกับ Google Calendar
Introduction
For a long time, the only personal scheduling tool I used on a regular basis was the one that shipped with my PalmPilot. Over the last couple of years, though, I've gradually shifted my loyalties to Google Calendar: not only is it more Web-aware, but it also makes it easy to share news about events, organize invitations and responses, and handle different types of events.
Frequently used acronyms
API: Application Programming Interface
HTTP: Hypertext Transfer Protocol
PHP: PHP: Hypertext Preprocessor
REST: Representational state transfer
RSS: Really Simple Syndication
URL: Uniform Resource Locator
XML: Extensible Markup Language

refer: http://www.ibm.com/developerworks/library/x-googleclndr/

As a developer too, I find Google Calendar makes for interesting water-cooler conversation: With its Data API, developers can easily build new applications around the data stored in public and private user calendars. This API, which follows the REST model, can be accessed through any XML-capable development toolkit, and already has client libraries for many common programming languages...including my favourite, PHP.
This article will introduce you to the Google Calendar Data API, showing you how to integrate and use calendar data with a custom PHP application. It includes examples of how to:
Retrieve events from a user's public notebooks.
Add new events
Modify and delete events
Search for events by keyword or date range
So come on in, and get started!
Back to top
Understanding the Calendar Data API
Before you dive into the PHP code, a few words about the Google Calendar Data API are in order. As with all REST-based services, the API works by accepting HTTP requests containing one or more XML-encoded input arguments and returning XML-encoded responses that can be parsed in any XML-aware client. With the Google Calendar Data API, the response always consists of an Atom or RSS feed containing the requested information.
A typical Google Calendar feed includes more than enough information to build a useful and relevant application. To see an example, log into your Google Calendar account, navigate to your calendar settings, and find the link for your calendar's private address URL. This URL, which you should hold secret at all times, provides read-only access to your calendar's feed without first requiring authorization, and will look something like http://www.google.com/calendar/feeds/userid/private-magicCookie/basic. Pop this URL into your Web browser (or send a GET request for the feed through an HTTP client), and you should see something like Listing 1:

Listing 1: An example Google Calendar feed
                
<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' 
 xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' 
 xmlns:batch='http://schemas.google.com/gdata/batch' 
 xmlns:gCal='http://schemas.google.com/gCal/2005' 
 xmlns:gd='http://schemas.google.com/g/2005'>
  <id>http://www.google.com/calendar/feeds/user@gmail.com/
   private-cookie/basic</id>
  <updated>2008-06-13T19:15:18.000Z</updated>
  <category scheme='http://schemas.google.com/g/2005#kind' 
  term='http://schemas.google.com/g/2005#event'/>
  <title type='text'>Joe User</title>
  <subtitle type='text'>Joe User</subtitle>
  <link rel='alternate' type='text/html' 
  href='http://www.google.com/calendar/embed?src=user@gmail.com'/>
  <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' 
   href='http://www.google.com/calendar/feeds/user@gmail.com/private-cookie/basic'/>
  <link rel='http://schemas.google.com/g/2005#batch' type='application/atom+xml' 
   href='http://www.google.com/calendar/feeds/user@gmail.com/private-cookie/basic/batch'
    />
  <link rel='self' type='application/atom+xml' 
  href='http://www.google.com/calendar/feeds/user@gmail.com/private-cookie/basic?
  max-results=25'/>
  <author>
    <name>Joe User</name>
    <email>user@gmail.com</email>
  </author>
  <generator version='1.0' uri='http://www.google.com/calendar'
  >Google Calendar</generator>
  <openSearch:totalResults>4</openSearch:totalResults>
  <openSearch:startIndex>1</openSearch:startIndex>
  <openSearch:itemsPerPage>25</openSearch:itemsPerPage>
  <gCal:timezone value='Asia/Calcutta'/>
  <entry>
    <id>http://www.google.com/calendar/feeds/user@gmail.com/
    private-cookie/basic/xxxxxxxx</id>
    <published>2008-06-12T08:49:38.000Z</published>
    <updated>2008-06-13T19:06:21.000Z</updated>
    <category scheme='http://schemas.google.com/g/2005#kind' 
    term='http://schemas.google.com/g/2005#event'/>
    <title type='html'>Swim party</title>
    <summary type='html'>When: Sat Jun 21, 2008 12pm to 3:30pm&nbsp;
IST<br>
<br>Event Status: confirmed</summary>
    <content type='html'>When: Sat Jun 21, 2008 12pm to 3:30pm 
IST<br />
<br />Event Status: confirmed</content>
    <link rel='alternate' type='text/html' 
    href='http://www.google.com/calendar/event?eid=cGxwbHExOYHHDlOHQ4ZjA
    yMGMgdmlrcmFtLm1lbG9uZmlyZUBnb29nxmNvbQ' title='alternate'/>
    <link rel='self' type='application/atom+xml' 
    href='http://www.google.com/calendar/feeds/user@gmail.com/
    private-cookie/basic/ddddddddddddddd/>
    <author>
      <name>Joe User</name>
      <email>user@gmail.com</email>
    </author>
  </entry>
  <entry>
    <id>http://www.google.com/calendar/feeds/user@gmail.com/
    private-cookie/basic/yyyyyyyyyyyyyyyy</id>
    <published>2008-06-12T08:48:30.000Z</published>
    <updated>2008-06-12T08:48:46.000Z</updated>
    <category scheme='http://schemas.google.com/g/2005#kind' 
    term='http://schemas.google.com/g/2005#event'/>
    <title type='html'>Dinner with the gang</title>
    <summary type='html'>When: Wed Jun 11, 2008 7pm to 9:30pm&nbsp;
IST<br>
<br>Event Status: confirmed</summary>
    <content type='html'>When: Wed Jun 11, 2008 7pm to 9:30pm 
IST<br />
<br />Event Status: confirmed</content>
    <link rel='alternate' type='text/html' 
    href='http://www.google.com/calendar/event?eid=
    MmhpYmV2cmowMM2kam9lZDQgdcmFtLm1lbG9uZmlyZU4858Bnb29nbGVtYWlsLmNvbQ' 
    title='alternate'/>
    <link rel='self' type='application/atom+xml' 
    href='http://www.google.com/calendar/feeds/user@gmail.com/private-cookie/
    basic/hhhhhhhhhhhhhhhhh'/>
    <author>
      <name>Joe User</name>
      <email>user@gmail.com</email>
    </author>
  </entry>
  <entry>
  ...
  </entry>
</feed>
        

Every Calendar feed opens with a <feed> element as the root element. The <feed> element contains <link> elements, which contain URLs for different versions of the feed, and <openSearch:> elements, which contain summary statistics.
The outermost <feed> element encloses one or more <entry> elements, each representing a calendar event. Each <entry> contains further detail, including the title, description, publication date, last update date, event feed URL and author of each event. These are represented by <title>, <summary>, <published>, <updated>, <link> and <author> elements respectively.
Back to top
Retrieving event listings with SimpleXML
Now you'll see an example of how to process a Google Calendar feed using PHP. Listing 2 takes the feed from Listing 1 and uses SimpleXML to extract relevant fragments of data from it and format it into a Web page:

Listing 2: Retrieving event listings with SimpleXML
                
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Listing calendar contents</title>
    <style>
    body {
      font-family: Verdana;      
    }
    li {
      border-bottom: solid black 1px;      
      margin: 10px; 
      padding: 2px; 
      width: auto;
      padding-bottom: 20px;
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    </style>    
  </head>
  <body>
    <?php
    $userid = 'username%40googlemail.com';
    $magicCookie = 'cookie';
    
    // build feed URL
    $feedURL = "http://www.google.com/calendar/feeds/$userid/private-$magicCookie/basic";
    
    // read feed into SimpleXML object
    $sxml = simplexml_load_file($feedURL);
    
    // get number of events
    $counts = $sxml->children('http://a9.com/-/spec/opensearchrss/1.0/');
    $total = $counts->totalResults; 
    ?>
    <h1><?php echo $sxml->title; ?></h1>
    <?php echo $total; ?> event(s) found.
    <p/>
    <ol>
    <?php    
    // iterate over entries in category
    // print each entry's details
    foreach ($sxml->entry as $entry) {
      $title = stripslashes($entry->title);
      $summary = stripslashes($entry->summary);
      
      echo "<li>n";
      echo "<h2>$title</h2>n";
      echo "$summary <br/>n";
      echo "</li>n";
    }
    ?>
    </ol>
  </body>
</html>     
        

Figure 1 demonstrates the output you might see:

Figure 1. A Web page listing calendar events produced with SimpleXML
 
In the code in Listing 2, the simplexml_load_file() object sends a request to the feed URL and converts the response into a SimpleXML object. It then iterates over the <entry> elements in the response, processes each one using a foreach() loop and retrieves the information in Figure 1. Child nodes under each <entry> are represented as SimpleXML object properties—for example, the <title> node is represented by $entry->title, the <summary> node by $entry->summary, and so on.
The feed URL used here references the user's private calendar feed, and contains both the user's email address and a so-called magic cookie that it uses to obtain read-only access to a calendar's data without first requiring authorization. As discussed previously, obtaining this feed URL is a manual task: you will need to visit the corresponding Google Calendar page and manually copy the URL from the calendar's settings into the PHP script.
Back to top
Retrieving event listings with the Zend GData Client Library
While magic cookie authentication is certainly convenient, it isn't very practical for full-fledged PHP applications, for two reasons:
It permits read-only access to a calendar feed,
It isn't available for all calendar operations.
To do anything really substantial with the Calendar Data API, you need to add user authentication to your application using one of the two Google-approved authentication methods: AuthSub or ClientLogin.
Performing this type of authentication manually is a fairly messy task, and requires a fair bit of code to account for the various scenarios that might crop up during a typical authentication transaction. Fortunately, you don't have to worry too much about this: Zend's GData Client Library, designed specifically for developers who try to integrate PHP applications with the Google Data API, handles all the details for you. This library, which you can download separately (see Resources for a link), provides a convenient, object-oriented interface to the Google Data API, encapsulating most common tasks (including authentication)>. Thus, you are free to focus on the core functions of your application.
Listing 3 demonstrates, by replicating the functionality of Listing 2 using the Zend GData Client Library:

Listing 3: Retrieving event listings with the Zend library
                
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Listing calendar contents</title>
    <style>
    body {
      font-family: Verdana;      
    }
    li {
      border-bottom: solid black 1px;      
      margin: 10px; 
      padding: 2px; 
      width: auto;
      padding-bottom: 20px;
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    </style>    
  </head>
  <body>
    <?php
    // load library
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
    Zend_Loader::loadClass('Zend_Gdata_Calendar');
    Zend_Loader::loadClass('Zend_Http_Client');
    
    // create authenticated HTTP client for Calendar service
    $gcal = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
    $user = "username@gmail.com";
    $pass = "pass";
    $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $gcal);
    $gcal = new Zend_Gdata_Calendar($client);
    
    // generate query to get event list
    $query = $gcal->newEventQuery();
    $query->setUser('default');
    $query->setVisibility('private');
    $query->setProjection('basic');
    
    // get and parse calendar feed
    // print output
    try {
      $feed = $gcal->getCalendarEventFeed($query);
    } catch (Zend_Gdata_App_Exception $e) {
      echo "Error: " . $e->getResponse();
    }
    ?>
    <h1><?php echo $feed->title; ?></h1>
    <?php echo $feed->totalResults; ?> event(s) found.
    <p/>
    <ol>

    <?php        
    foreach ($feed as $event) {
      echo "<li>n";
      echo "<h2>" . stripslashes($event->title) . "</h2>n";
      echo stripslashes($event->summary) . " <br/>n";
      echo "</li>n";
    }
    echo "</ul>";
    ?>
    </ol>

  </body>
</html>     
        

Listing 3 first loads the Zend class libraries, and then initializes an instance of the Zend_Http_Client class. This client is provided with the necessary user authentication information, and opens an authenticated connection to the Calendar service. Once an authenticated connection opens, the getCalendarEventFeed() method retrieves the calendar feed. This method accepts an EventQuery object, which is configured with various parameters: the user name, the type of feed (public or private) and the level of detail required in the feed (full or basic). The response to the getCalendarEventFeed() API call is an XML document, which is then parsed and converted into a PHP object. It's now a simple matter to retrieve this data through object properties and use it to generate an HTML page.
Figure 2 demonstrates the output you might see:

Figure 2. A Web page listing calendar events produced with the Zend GData Client Library
 
Back to top
Adding new events
That takes care of listing events through a client application. Now, how do you add new events?
This is actually not as complicated as it might seem. The Calendar Data API makes it easy to add new events to a calendar, by creating a new XML-encoded event <entry> and POST-ing this XML to the calendar feed. Listing 4 has an example of one such entry:

Listing 4: An example entry for a new event
                
        <atom:entry xmlns:atom="http://www.w3.org/2005/Atom">
          <atom:title type="text">Dinner with the gang</atom:title>
          <gd:when xmlns:gd="http://schemas.google.com/g/2005" 
          startTime="2008-06-23T18:00:00+05:30" endTime="2008-06-23T20:00:00+05:30"/>
        </atom:entry>
        

If you use the Zend library, things are even simpler: You only need to call the insertEvent() method, which creates the XML in Listing 4 and then posts it to the calendar feed. Listing 5 demonstrates, by building a Web form that asks the user for various event-related details and then uses the Zend library to construct the corresponding XML and save the event to the calendar:

Listing 5: Adding a new event through a Web form
                
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Adding calendar events</title>
    <style>
    body {
      font-family: Verdana;      
    }
    li {
      border-bottom: solid black 1px;      
      margin: 10px; 
      padding: 2px; 
      width: auto;
      padding-bottom: 20px;
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    </style>    
  </head>
  <body>
    <h1>Add Event</h1>
    <?php if (!isset($_POST['submit'])) { ?>
    <form method="post" action="
     <?php echo htmlentities($_SERVER['PHP_SELF']); ?>">
      Event title: <br/>
      <input name="title" type="text" size="15" /><p/>
      Start date (dd/mm/yyyy): <br/>
      <input name="sdate_dd" type="text" size="2" />
      <input name="sdate_mm" type="text" size="2" />
      <input name="sdate_yy" type="text" size="4" /><p/>
      Start time (hh:mm): <br/>
      <input name="sdate_hh" type="text" size="2" /> 
      <input name="sdate_ii" type="text" size="2" /><br/>
      End  date (dd/mm/yyyy): <br/>
      <input name="edate_dd" type="text" size="2" />
      <input name="edate_mm" type="text" size="2" />
      <input name="edate_yy" type="text" size="4" /><p/>
      End  time (hh:mm): <br/>
      <input name="edate_hh" type="text" size="2" /> 
      <input name="edate_ii" type="text" size="2" /><br/>
      <input name="submit" type="submit" value="Save" />      
    </form>
    <?php
    } else {
      // load classes
      require_once 'Zend/Loader.php';
      Zend_Loader::loadClass('Zend_Gdata');
      Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
      Zend_Loader::loadClass('Zend_Gdata_Calendar');
      Zend_Loader::loadClass('Zend_Http_Client');
      
      // connect to service
      $gcal = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
      $user = "username@gmail.com";
      $pass = "pass";
      $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $gcal);
      $gcal = new Zend_Gdata_Calendar($client);
      
      // validate input
      if (empty($_POST['title'])) {
        die('ERROR: Missing title');
      } 
      
      if (!checkdate($_POST['sdate_mm'], $_POST['sdate_dd'], $_POST['sdate_yy'])) {
        die('ERROR: Invalid start date/time');        
      }
      
      if (!checkdate($_POST['edate_mm'], $_POST['edate_dd'], $_POST['edate_yy'])) {
        die('ERROR: Invalid end date/time');        
      }
      
      $title = htmlentities($_POST['title']);
      $start = date(DATE_ATOM, mktime($_POST['sdate_hh'], $_POST['sdate_ii'], 
       0, $_POST['sdate_mm'], $_POST['sdate_dd'], $_POST['sdate_yy']));
      $end = date(DATE_ATOM, mktime($_POST['edate_hh'], $_POST['edate_ii'], 
       0, $_POST['edate_mm'], $_POST['edate_dd'], $_POST['edate_yy']));

      // construct event object
      // save to server      
      try {
        $event = $gcal->newEventEntry();        
        $event->title = $gcal->newTitle($title);        
        $when = $gcal->newWhen();
        $when->startTime = $start;
        $when->endTime = $end;
        $event->when = array($when);        
        $gcal->insertEvent($event);   
      } catch (Zend_Gdata_App_Exception $e) {
        echo "Error: " . $e->getResponse();
      }
      echo 'Event successfully added!';      
    }
    ?>
  </body>
</html>    
        

Listing 5 actually consists of two parts: a Web form, and the PHP code that processes the input submitted through the form. Figure 3 illustrates what this form looks like:

Figure 3. A Web form to add a new event
 
Once the user enters event details into this form and submits it, the second half of the script swings into action. First, the script initializes an HTTP client that opens an authenticated connection to the Calendar Data API. Next, the script validates the input entered into the Web form, checks the event start and end dates, and converts the dates into RFC 3339 format.
Once the data entered into the form is validated, a new EventEntry object is created. This object represents the new event to be inserted into the calendar, and it exposes newTitle() and newWhen() methods, which set the event's title and start/end dates. Once these object properties are set, the insertEvent() method actually saves the event to the Google servers. Once added, the event should immediately become visible in the calendar.
Figure 4 illustrates the output after successfully adding a new event to a calendar:

Figure 4. The result of adding a new event
 
Back to top
Deleting and modifying existing events
Deletion of an event is even simpler: simply obtain the unique URL of the event and send a DELETE request to that URL. In the Zend library context, the getCalendarEventEntry() method obtains an event object and then calls that object's delete() method. Listing 6 illustrates the process:

Listing 6: Deleting an event
                
<?php
// load classes
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Calendar');
Zend_Loader::loadClass('Zend_Http_Client');

// connect to service    
$gcal = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
$user = "username@gmail.com";
$pass = "pass";
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $gcal);
$gcal = new Zend_Gdata_Calendar($client);    

// retrieve event
// delete event
try {          
  $event = $gcal->getCalendarEventEntry('http://www.google.com/calendar/
   feeds/default/private/full/xxxxxxx');
  $event->delete();
} catch (Zend_Gdata_App_Exception $e) {
  echo "Error: " . $e->getResponse();
}        
echo 'Event successfully deleted!';  
?>
        

The update of an event follows a similar process: obtain the unique URL of the event and send a PUT request to that URL containing the updated event data. With the Zend library, to accomplish this, set new values for the event object's properties, and then save the revised event to the server through the object's save() method. Listing 7 illustrates:

Listing 7: Modifying an event
                
<?php
// load classes
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Calendar');
Zend_Loader::loadClass('Zend_Http_Client');

// connect to service    
$gcal = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
$user = "username@gmail.com";
$pass = "pass";
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $gcal);
$gcal = new Zend_Gdata_Calendar($client);    

// retrieve event
// set new event properties and update event
try {
  $event = $gcal->getCalendarEventEntry('http://www.google.com/calendar/feeds/
   default/private/full/xxxxxxxxxxx');
  $event->title = $gcal->newTitle($title); 
  $when = $gcal->newWhen();
  $when->startTime = $start;
  $when->endTime = $end;
  $event->when = array($when);         
  $event->save();   
} catch (Zend_Gdata_App_Exception $e) {
  die("Error: " . $e->getResponse());
}
echo 'Event successfully modified!';    
?>
        

Back to top
Integrating calendar operations
With all this knowledge at hand, look at it in a practical application context. Listing 8 updates Listing 3 and adds edit and delete links to each event. These links point to the edit.php and delete.php scripts respectively, and uses the GET method to pass the event identifier (extracted from the event entry) to these scripts.

Listing 8: Retrieving event listings
                
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Listing calendar contents</title>
    <style>
    body {
      font-family: Verdana;      
    }
    li {
      border-bottom: solid black 1px;      
      margin: 10px; 
      padding: 2px; 
      width: auto;
      padding-bottom: 20px;
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    </style>    
  </head>
  <body>
    <?php
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
    Zend_Loader::loadClass('Zend_Gdata_Calendar');
    Zend_Loader::loadClass('Zend_Http_Client');
    
    $gcal = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
    $user = "username@gmail.com";
    $pass = "pass";
    $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $gcal);
    $gcal = new Zend_Gdata_Calendar($client);
    
    $query = $gcal->newEventQuery();
    $query->setUser('default');
    $query->setVisibility('private');
    $query->setProjection('basic');

    try {
      $feed = $gcal->getCalendarEventFeed($query);
    } catch (Zend_Gdata_App_Exception $e) {
      echo "Error: " . $e->getResponse();
    }
    ?>
    <h1><?php echo $feed->title; ?></h1>
    <?php echo $feed->totalResults; ?> event(s) found.
    <p/>
    <ol>

    <?php        
    foreach ($feed as $event) {
      echo "<li>n";
      echo "<h2>" . stripslashes($event->title) . "</h2>n";
      echo stripslashes($event->summary) . " <br/>n";
      $id = substr($event->id, strrpos($event->id, '/')+1);
      echo "<a href="edit.php?id=$id">edit</a> | ";
      echo "<a href="delete.php?id=$id">delete</a> <br/>n";
      echo "</li>n";
    }
    echo "</ul>";
    ?>
    </ol>
    <p/>
    <a href="add.php">Add a new event</a><p/>
  </body>
</html>  
        

Figure 5 illustrates an example of the output of Listing 8:

Figure 5. A Web page containing event listings
 
Listing 9 contains the code for the delete.php script, which receives the event identifier through the GET method and uses it to delete the event, following the approach outlined in Listing 6:

Listing 9: Deleting events
                
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Deleting calendar events</title>
    <style>
    body {
      font-family: Verdana;      
    }
    li {
      border-bottom: solid black 1px;      
      margin: 10px; 
      padding: 2px; 
      width: auto;
      padding-bottom: 20px;
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    </style>    
  </head>
  <body>
    <h1>Delete Event</h1>
    <?php
    // load classes
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
    Zend_Loader::loadClass('Zend_Gdata_Calendar');
    Zend_Loader::loadClass('Zend_Http_Client');
    
    // connect to service
    $gcal = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
    $user = "username@gmail.com";
    $pass = "pass";
    $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $gcal);
    $gcal = new Zend_Gdata_Calendar($client);
      
    // if event ID is present
    // get event object from feed
    // delete event  
    if (isset($_GET['id'])) {
      try {          
          $event = $gcal->getCalendarEventEntry('http://www.google.com/calendar/
           feeds/default/private/full/' . $_GET['id']);
          $event->delete();
      } catch (Zend_Gdata_App_Exception $e) {
          echo "Error: " . $e->getResponse();
      }        
      echo 'Event successfully deleted!';  
    } else {
      echo 'No event ID available';  
    }
    ?>
  </body>
</html>
        

Listing 10 contains the code for the edit.php script.

Listing 10: Editing events
                
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Updating calendar events</title>
    <style>
    body {
      font-family: Verdana;      
    }
    li {
      border-bottom: solid black 1px;      
      margin: 10px; 
      padding: 2px; 
      width: auto;
      padding-bottom: 20px;
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    </style>    
  </head>
  <body>
    <h1>Edit Event</h1>
    <?php
    // load classes
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
    Zend_Loader::loadClass('Zend_Gdata_Calendar');
    Zend_Loader::loadClass('Zend_Http_Client');
    
    // connect to service
    $gcal = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
    $user = "username@gmail.com";
    $pass = "pass";
    $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $gcal);
    $gcal = new Zend_Gdata_Calendar($client);
     
    // get event details
    if (!isset($_POST['submit'])) {
      if (isset($_GET['id'])) {
        try {          
          $event = $gcal->getCalendarEventEntry('http://www.google.com/calendar/
           feeds/default/private/full/' . $_GET['id']);
        } catch (Zend_Gdata_App_Exception $e) {
          echo "Error: " . $e->getResponse();
        }
      } else {
          die('ERROR: No event ID available!');  
      }  
      
      // format data into human-readable form
      // populate a Web form with the record
      $title = $event->title;
      $when = $event->getWhen();
      $startTime = strtotime($when[0]->getStartTime());
      $sdate_dd = date('d', $startTime);
      $sdate_mm = date('m', $startTime);
      $sdate_yy = date('Y', $startTime);
      $sdate_hh = date('H', $startTime);
      $sdate_ii = date('i', $startTime);
      $endTime = strtotime($when[0]->getEndTime());
      $edate_dd = date('d', $endTime);
      $edate_mm = date('m', $endTime);
      $edate_yy = date('Y', $endTime);
      $edate_hh = date('H', $endTime);
      $edate_ii = date('i', $endTime);      
    ?>
    <form method="post" 
     action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>">
      <input type="hidden" name="id" value="<?php echo $_GET['id']; ?>">
      Event title: <br/>
      <input name="title" type="text" size="15" 
       value="<?php echo $title; ?>"/><p/>
      Start date (dd/mm/yyyy): <br/>
      <input name="sdate_dd" type="text" size="2" 
       value="<?php echo $sdate_dd; ?>" />
      <input name="sdate_mm" type="text" size="2" 
       value="<?php echo $sdate_mm; ?>"/>
      <input name="sdate_yy" type="text" size="4" 
       value="<?php echo $sdate_yy; ?>"/><p/>
      Start time (hh:mm): <br/>
      <input name="sdate_hh" type="text" size="2" 
       value="<?php echo $sdate_hh; ?>"/> 
      <input name="sdate_ii" type="text" size="2" 
       value="<?php echo $sdate_ii; ?>"/><br/>
      End  date (dd/mm/yyyy): <br/>
      <input name="edate_dd" type="text" size="2" 
       value="<?php echo $edate_dd; ?>" />
      <input name="edate_mm" type="text" size="2" 
       value="<?php echo $edate_mm; ?>" />
      <input name="edate_yy" type="text" size="4" 
       value="<?php echo $edate_yy; ?>" /><p/>
      End  time (hh:mm): <br/>
      <input name="edate_hh" type="text" size="2" 
       value="<?php echo $edate_hh; ?>"  /> 
      <input name="edate_ii" type="text" size="2" 
       value="<?php echo $edate_ii; ?>"  /><br/>
      <input name="submit" type="submit" value="Save" />      
    </form>    
    <?php              
    } else {
      // if form submitted
      // validate input
      if (empty($_POST['id'])) {
        die('ERROR: Missing event ID');
      } 
      
      if (empty($_POST['title'])) {
        die('ERROR: Missing title');
      } 
      
      if (!checkdate($_POST['sdate_mm'], $_POST['sdate_dd'], $_POST['sdate_yy'])) {
        die('ERROR: Invalid start date/time');        
      }
      
      if (!checkdate($_POST['edate_mm'], $_POST['edate_dd'], $_POST['edate_yy'])) {
        die('ERROR: Invalid end date/time');        
      }     
      
      $title = htmlentities($_POST['title']);
      $start = date(DATE_ATOM, mktime($_POST['sdate_hh'], $_POST['sdate_ii'], 
       0, $_POST['sdate_mm'], $_POST['sdate_dd'], $_POST['sdate_yy']));
      $end = date(DATE_ATOM, mktime($_POST['edate_hh'], $_POST['edate_ii'], 
       0, $_POST['edate_mm'], $_POST['edate_dd'], $_POST['edate_yy']));
      
      // get existing event record
      // update event attributes
      // save changes to server
      try {
        $event = $gcal->getCalendarEventEntry('http://www.google.com/calendar/
         feeds/default/private/full/' . $_POST['id']);
        $event->title = $gcal->newTitle($title); 
        $when = $gcal->newWhen();
        $when->startTime = $start;
        $when->endTime = $end;
        $event->when = array($when);        
        $event->save();   
      } catch (Zend_Gdata_App_Exception $e) {
        die("Error: " . $e->getResponse());
      }
      echo 'Event successfully modified!';    
    }    
    ?>
  </body>
</html>  
        

Like Listing 9, Listing 10 also receives the event identifier over the GET method. It then uses this identifier to retrieve the event details using the getCalendarEventEntry() method and pre-populates a Web form with these details. The user can then modify the event details in the form and submit it; on submission, the script again contacts the Calendar Data API, constructs a revised <entry> containing the new event details, and uses the save() method to save the changes to the server.
Back to top
Searching for events
Like with any Google Data feed, the Calendar API too allows developers to customize the output by adding some of the following parameters to the REST query:
The start-index parameter, which specifies the start offset for the entries
The max-results parameter, which specifies the number of entries to retrieve
The start-min and start-max parameters, which specify a date range for the returned entries
The orderby parameter, which specifies how to order entries
Listing 11 demonstrates some of these in action, restricting the default output of Listing 2 to entries for the next seven days only and ordering them by their start time:

Listing 11: Searching for events by date
                
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Listing calendar contents</title>
    <style>
    body {
      font-family: Verdana;      
    }
    li {
      border-bottom: solid black 1px;      
      margin: 10px; 
      padding: 2px; 
      width: auto;
      padding-bottom: 20px;
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    </style>    
  </head>
  <body>
    <?php
    // set configuration parameters
    $userid = 'username%40googlemail.com';
    $magicCookie = 'cookie';
    $start = urlencode(date(DATE_ATOM, strtotime('today 00:00')));
    $end = urlencode(date(DATE_ATOM, strtotime('+7 days 23:59')));
    
    // build feed URL
    $feedURL = "http://www.google.com/calendar/feeds/$userid/private-
     $magicCookie/basic?start-min=$start&start-max=$end&orderby=starttime";
    
    // read feed into SimpleXML object
    $sxml = simplexml_load_file($feedURL);
    
    // get number of events
    $counts = $sxml->children('http://a9.com/-/spec/opensearchrss/1.0/');
    $total = $counts->totalResults; 
    ?>
    <h1><?php echo $sxml->title; ?></h1>
    <?php echo $total; ?> event(s) found.
    <p/>
    <ol>
    <?php    
    // iterate over entries in category
    // print each entry's details
    foreach ($sxml->entry as $entry) {
      $title = stripslashes($entry->title);
      $summary = stripslashes($entry->summary);
      
      echo "<li>n";
      echo "<h2>$title</h2>n";
      echo "$summary <br/>n";
      echo "</li>n";
    }
    ?>
    </ol>
  </body>
</html>    
        

It's also possible to perform full-text search queries against the entries in a calendar, and only return those entries matching a particular query term. Consider Listing 12, which illustrates how this works:

Listing 12: Searching for events by query term
                
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Listing calendar contents</title>
    <style>
    body {
      font-family: Verdana;      
    }
    li {
      border-bottom: solid black 1px;      
      margin: 10px; 
      padding: 2px; 
      width: auto;
      padding-bottom: 20px;
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    </style>    
  </head>
  <body>
    <?php
    // set configuration parameters
    $userid = 'username%40googlemail.com';
    $magicCookie = 'cookie';
    $query = urlencode('party');
    
    // build feed URL
    $feedURL = "http://www.google.com/calendar/feeds/$userid/private-
     $magicCookie/basic?q=$query";
    
    // read feed into SimpleXML object
    $sxml = simplexml_load_file($feedURL);
    
    // get number of events
    $counts = $sxml->children('http://a9.com/-/spec/opensearchrss/1.0/');
    $total = $counts->totalResults; 
    ?>
    <h1><?php echo $sxml->title; ?></h1>
    <?php echo $total; ?> event(s) found.
    <p/>
    <ol>
    <?php    
    // iterate over entries in category
    // print each entry's details
    foreach ($sxml->entry as $entry) {
      $title = stripslashes($entry->title);
      $summary = stripslashes($entry->summary);
      
      echo "<li>n";
      echo "<h2>$title</h2>n";
      echo "$summary <br/>n";
      echo "</li>n";
    }
    ?>
    </ol>
  </body>
</html>     
        

The Zend client library also includes support for these parameters. To illustrate, update Listing 8 to include a search form and form processor, as in Listing 13 below:

Listing 13: Adding interactive search
                
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Listing calendar contents</title>
    <style>
    body {
      font-family: Verdana;      
    }
    li {
      border-bottom: solid black 1px;      
      margin: 10px; 
      padding: 2px; 
      width: auto;
      padding-bottom: 20px;
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    </style>    
  </head>
  <body>
    <?php
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
    Zend_Loader::loadClass('Zend_Gdata_Calendar');
    Zend_Loader::loadClass('Zend_Http_Client');
    
    $gcal = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
    $user = "username@gmail.com";
    $pass = "pass";
    $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $gcal);
    $gcal = new Zend_Gdata_Calendar($client);
    
    $query = $gcal->newEventQuery();
    $query->setUser('default');
    $query->setVisibility('private');
    $query->setProjection('basic');
    $query->setOrderby('starttime');
    if(isset($_GET['q'])) {
      $query->setQuery($_GET['q']);      
    }
    
    try {
      $feed = $gcal->getCalendarEventFeed($query);
    } catch (Zend_Gdata_App_Exception $e) {
      echo "Error: " . $e->getResponse();
    }
    ?>
    <h1><?php echo $feed->title; ?></h1>
    <?php echo $feed->totalResults; ?> event(s) found.
    <p/>
    <ol>

    <?php        
    foreach ($feed as $event) {
      echo "<li>n";
      echo "<h2>" . stripslashes($event->title) . "</h2>n";
      echo stripslashes($event->summary) . " <br/>n";
      $id = substr($event->id, strrpos($event->id, '/')+1);
      echo "<a href="edit.php?id=$id">edit</a> | ";
      echo "<a href="delete.php?id=$id">delete</a> <br/>n";
      echo "</li>n";
    }
    echo "</ul>";
    ?>
    </ol>
    <p/>
    <a href="add.php">Add a new event</a><p/>
    <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="get">
      Search for events containing:<br/>
      <input type="text" name="q" size="10"/><p/>
      <input type="submit" name="submit" value="Search"/>
    </form>
  </body>
</html>    
        

Figure 6 illustrates what the form looks like:

Figure 6.

ขึ้นไปด้านบน