James.Cridland.net

Google Calendar on your website

Google Calendar is marvellous. But you might want to use it on your own website, to show something handy like your own calendar for, say, speaking engagements or something. Sure beats editing HTML. Well, here's how I did it, since a friend called Rick wanted to know and was really impressed at my coding skills couldn't be arsed to write his own.

Configure your Google Calendar
- Find the Google Calendar you want to publish in the 'Calendars' tab (lower left of the Calendar homepage).
- Click the down-arrow next to it, and choose 'share this calendar'.
- Click 'Share all information on this calendar with everyone'.
- Then click 'calendar details' and scroll down to 'calendar address'.
- Copy the Calendar ID which is displayed here.

Now, for your website.
- Get Simplepie, an excellent RSS reader which works well with Google Calendar, unlike Magpie. Get it working on your website: the instructions are pretty simple. Ensure you're using v1.0 - older versions won't work.
- Then, use this magic code:

<?php

//v0.84: corrected an error only visible using E_ALL in PHP5. Thank you Dave!
//v0.83: correctly handle 'cancelled' events. Thank you Daniel!
//v0.82: using the new "futureevents" call
//v0.81: Bugfix: all day events no longer occur on the day before
//       Bugfix: this now actually sets your cache location correctly
//       Thank you Adrian Jones
//v0.80: Thanks to Michiel Gardner: rewrite
//       Bugfix: "only shows one event at same time"
//      Bugfix: "only shows 25 events"
//v0.74: No more phantom events if an event is marked private, after Michael asked
//v0.73: Added a comment if you only want "today", after Mugresh asked
//v0.72: Stacey wanted to correctly parse links and HTML within each field.
//       Added: these are the two lines with &gt; and &lt; in there.
//v0.71: using SimplePie's parse_date() to make things even easier, after a tip-off
//       from Ryan of... SimplePie. Cheers!
//v0.7:  Based on documentation on SimplePie's website; made PHP4 friendly and
//       much more configurable
//       Complete rewrite from v0.6.
//       Tested on, and requires, SimplePie v1.0 RC3 or higher
//       http://james.cridland.net/code


/////////
//Configuration
//

// The 'Calendar ID' code (as shown in your 'calendar settings' page)
if (!isset($gmail)) {$gmail = "g894tamjsovj5p4vff8cuk7evc%40group.calendar.google.com"; }

// Date format you want your details to appear
$dateformat="j F, Y"; // Thursday, 10 March - see http://www.php.net/date for details
$timeformat="g.ia"; // 12.15am

// How you want each thing to display.
// By default, this contains all the bits you can grab.
$event_display="<P><B>###TITLE###</b> - ###DATE### at ###FROM### (<a href='###LINK###'>add this</a>)<BR>###WHERE### (<a href='###MAPLINK###'>map</a>)<br>###DESCRIPTION###</p>";

//Where your simplepie.inc is (mine's in the root for some reason)
require_once($_SERVER['DOCUMENT_ROOT'].'/simplepie.inc');

// Cache location for your XML file
$cache_location=$_SERVER['DOCUMENT_ROOT'].'../cache';

// Change this to 'true' to see lots of fancy debug code
$debug_mode=false;

//
//End of configuration block
/////////



if ($debug_mode) {echo "<P>Debug mode is on.</p>";}

// Make sure that correct version of SimplePie is loaded
if (SIMPLEPIE_VERSION<1) { echo "<P><B>Fatal error</b><BR>You need to be running SimplePie v1.0 or above for this to work.</p>"; die; }

// Form the XML address.
$calendar_xml_address = "http://www.google.com/calendar/feeds/".$gmail."/public/full?max-results=2500&futureevents=true&orderby=starttime";

// If you only want a section of dates - today only, for example, then try the following, which sets a maximum date to return. I've set this for a day from now.
// $calendar_xml_address = "http://www.google.com/calendar/feeds/".$gmail."/public/full?start-min=".date("Y-m-d")."&start-max=".date("Y-m-d",strtotime("+1 day"));



// Let's create a new SimplePie object
$feed = new SimplePie();

// Set the cache location
$feed->set_cache_location($cache_location);

if (
$debug_mode) {
$feed->enable_cache(false);
echo
"<P>We're going to go and grab <a href='$calendar_xml_address'>this feed</a>.<P>";}

// This is the feed we'll use
$feed->set_feed_url($calendar_xml_address);

// Let's turn this off because we're just going to re-sort anyways, and there's no reason to waste CPU doing it twice.
$feed->enable_order_by_date(false);

// Initialize the feed so that we can use it.
$feed->init();

// Make sure the content is being served out to the browser properly.
$feed->handle_content_type();

// We'll use this for re-sorting the items based on the new date.
$temp = array();

foreach (
$feed->get_items() as $item) {

    
// We want to grab the Google-namespaced <gd:when> tag.
    
$when = $item->get_item_tags('http://schemas.google.com/g/2005', 'when');

    
// Now, let's grab the Google-namespaced <gd:where> tag.
    
$gd_where = $item->get_item_tags('http://schemas.google.com/g/2005', 'where');
    
$location = $gd_where[0]['attribs']['']['valueString'];
    
//and the status tag too, come to that
    
$gd_status = $item->get_item_tags('http://schemas.google.com/g/2005', 'eventStatus');
    
$status = substr( $gd_status[0]['attribs']['']['value'], -8);

    
$when = $item->get_item_tags('http://schemas.google.com/g/2005', 'when');
    
$date = $when[0]['attribs']['']['startTime'];
    
$unixdate = SimplePie_Misc::parse_date($date);
    
$where = $item->get_item_tags('http://schemas.google.com/g/2005', 'where');
    
$location = $where[0]['attribs']['']['valueString'];

    
// If there's actually a title here (private events don't have titles) and it's not cancelled...
if (strlen(trim($item->get_title()))>1 && $status != "canceled" && strlen(trim($date)) > 0) {
        
$temp[] = array('date'=>$unixdate, 'where'=>$location, 'title'=>$item->get_title(), 'description'=>$item->get_description(), 'link'=>$item->get_link());
    }

}

//Sort this
sort($temp);

// Loop through the (now sorted) array, and display what we wanted.
foreach ($temp as $item) {
    
// These are the dates we'll display
    
$gCalDate = gmdate($dateformat, $item['date']);
    
$gCalTime = gmdate($timeformat, $item['date']);

    
// Now, let's run it through some str_replaces, and store it with the date for easy sorting later
    
$temp_event=$event_display;
    
$temp_event=str_replace("###TITLE###",$item['title'],$temp_event);
    
$temp_event=str_replace("###DESCRIPTION###",$item['description'],$temp_event);
    
$temp_event=str_replace("###DATE###",$gCalDate,$temp_event);
    
$temp_event=str_replace("###FROM###",$gCalTime,$temp_event);
    
$temp_event=str_replace("###WHERE###",$item['where'],$temp_event);
    
$temp_event=str_replace("###LINK###",$item['link'],$temp_event);
    
$temp_event=str_replace("###MAPLINK###","http://maps.google.com/?q=".urlencode($item['where']),$temp_event);
    
// Accept and translate HTML
    
$temp_event=str_replace("&lt;","<",$temp_event);
    
$temp_event=str_replace("&gt;",">",$temp_event);
    
$temp_event=str_replace("&quot;","\"",$temp_event);

    echo
$temp_event;
}

if (
$debug_mode) { echo "<PRE>";
echo
wordwrap(highlight_string(file_get_contents($calendar_xml_address),true),80);
echo
"</pre>"; }

?>

Download this code

Current known bugs (which you might want to fix)
- Recurring events will only show the first time, and not any recurrences. A workaround would be to manually add the recurrences. (Thanks, Patrick).
- If you place two different events at exactly the same date/time, you'll lose one of them at random. This is due to the way the script sorts the events. A workaround is to change the time slightly: you might even be able to do it with the seconds, which won't show if you configure the script correctly. (Thanks, Michiel). Fixed, thanks, Michiel
- All-day events appear to sometimes slip. I suspect this is a timezone issue. The workaround for this is not to use all-day events! (Thanks, Jason). Fixed, thanks, Adrian

Other bits of code based on this
Antii came up with something that looks like this; the nifty code is here.

FAQ

I just changed something in my Calendar but this script hasn't picked it up
It won't. Not instantly.
But it will within the hour. (Simplepie's default cache value is one hour: which keeps Google happy, as well as your site visitors).