Skip to main content

Displaying Google Calendar Events on a Club Web Site

Well, it was good while it lasted… but now the v2.0 API this is based on has been partially deprecated.  I’ve started the job of migrating – but in the mean time I’ve done a quick fix described here.

In this article I will explain how I present a list of events from Google Calendar in a club web site events page. This follows on from my previous article, Adding Calendar Events to Google Calendar in C#.

I had a few attempts at making this work and encountered a few problems along the way so I’ll mention these as I go along.

Where to Find the Google Calendar Events

Having already loaded the events into the Google Calendar, the first step to displaying them is to find out where to get them from. Thankfully, this is a relatively easy task which you do from your Google Calendar by finding the Calendar Settings Page.

Finding the Google Calendar Settings Dialog

If you loaded the events programmatically as I did then you will already have this URL.

Once you are on the settings page, find the XML data feed icon and click it to see the URL for it. If your calendar is public (as is the one I used) you may as well use the public calendar feed. For private calendars there will be no public feed so you need to select the private feed. Also, if you are going to use it to write back to the calendar use the private one (see previous article).

Select the Public (or Private) XML URL

This URL can be used to retrieve a basic list of events in an XML format called ATOM and looks something like this:

https://www.google.com/calendar/feeds/calendar@thistlesociety.org.uk/public/basic

For my purposes I needed to change the URL to retrieve more details and limit the results to future dated events so I replaced /basic with /full?singleevents=true&futureevents=true&orderby=starttime&sortorder=ascending (expand recurring events to multiple single events, only include future dated events, order by event start time, ascending) to get:

https://www.google.com/calendar/feeds/calendar@thistlesociety.org.uk/public/full?singleevents=true&futureevents=true&orderby=starttime&sortorder=ascending

Notes:

  1. There is an excellent reference page to the types of feeds and the parameters you can apply to them at http://code.google.com/apis/calendar/data/2.0/reference.html.
  2. It is also possible to request a JSON feed instead of XML ATOM format by appending &alt=json to the URL. See http://code.google.com/apis/gdata/docs/json.html.

Fetch Calendar Events from Google Calendar

Now that I knew where to get the events from, I needed to actually fetch them so that I could add them to the page. Oh, what fun that was…

First, I took the existing page and converted it to a PHP page. Now, I’m no PHP expert, but since I’m slowly picking it up to work with this Blog (WordPress which is PHP) so it seemed a reasonable choice.

This worked on my local PC (I tested it all under XAMPP) but meant that the page was produced in a different way to all the others (which I generate using an XSLT transform on some XML data and pages). Since I also embed a summary of the next few events in the Home page (a much more messy problem to covert to PHP) I decided that it was not the right solution for this situation.

My next attempt was to use a simple AJAX request to retrieve the data from the URL and render it in the page using JavaScript. This had the advantage that the page framework was generated in the same way as the rest of the site, but the events would be generated in real time from the Google Calendar feed.

I had a look around and found that the XMLHttpRequest object would do the trick. However, this presented two problems in quick succession; limitations on cross-domain requests and the annoying differences of the XMLHttpRequest implementations between different browsers.

The first problem was solved by introducing a PHP file on the server called events-feed.php which fetches the feed from the Google URL and passes it up to the browser on request. My first attempt used file_get_contents() (http://php.net/manual/en/function.file-get-contents.php) to read the entire file into a string. This worked on my XAMPP installation but failed miserably when I uploaded it to the server. Much searching later, I realised that this is considered a security hole and that I should use the “cURL” utility instead.

<?php 
// This is the Public calendar feed address with the extra parameters.
$calendarURL = "http://www.google.com/calendar/feeds/calendar@thistlesociety.org.uk/public" .
	"/full?singleevents=true&futureevents=true&orderby=starttime&sortorder=ascending";   

// Initialise a cURL instance with the calendar URL. 
$ch = curl_init(); 
curl_setopt($ch, CURLOPT_URL, $calendarURL); 

//return the transfer as a string 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 

// Fetch the Calendar Events into $feed.
$feed = curl_exec($ch); 

//	Tidy up afterwards to release resources. 
curl_close($ch);

//	Make sure it is recognised as XML.
header( "Content-type: text/xml" );

//  and send it to the browser.
echo $feed;
?>

The feed is returned to the browser with a content mime type of "text/xml".

After struggling with various suggestions to make the XMLHttpRequest object perform AJAX calls in more than one browser at a time, I finally gave up and switched to using jQuery instead. The end result was the following JavaScript which can be added to the <head> element of your web page – but I’ve put into a separate JavaScript file, events.js:

$(document).ready(
		function() {
			$.ajax( {
				type : "GET",
				url : "events-feed.php",
				dataType : "xml",
				error : handleError,
				success : handleCalendar
			});
		});

This code makes an AJAX call to retrieve the output of events-feed.php which will be the XML format ATOM feed of future dated events.  It calls the handleError function if anything goes wrong – or the handleCalendar method will be called with the results.

Parse and Extract Event Data

The handleError function is quite simple – here it just displays an alert box – but you could make it do anything you like.

/**
 * Handle any errors returned by the AJAX call.
 */
function handleError() {
	alert( "Unable to fetch event details." );
}


Having fetched the event data the XML needs to be parsed and the events extracted to create the list of events. Note that the Calendar Events are referred to as "entries" in the ATOM feed.

//	The HTML for the entries is collected into this string.
var htmlEntries = '';
//	Entries are grouped by Month in the display.
var lastMonth = '';

/**
 * Handle the ready event when the calendar feed is returned.
 * 
 * @param xml -
 *            the XML content of the calendar feed.
 */
function handleCalendar(xml) {
	//	Parse the feed into a tree of JS objects.
	var feed = $("feed", xml);

	//	Iterate through all of the entries.
	$(feed).children("entry").each( handleEvent );

	//	Insert the entries into the page.
	$("#entries").html( htmlEntries );
}

Here, jQuery comes to the rescue again making it easy to extract a collection of event entry objects from the XML feed and iterate through them. Each event is passed into handleEvent function one at a time. This handleEvent function extracts each piece of data about the event and uses them to build the HTML for the page which it appends to the htmlEntries variable.

First, the event title. Notice that the "event" element from the XML object tree is passed in both as a parameter (not used) and as the function context (i.e. "this"). It is then wrapped by jQuery to provide the find() method:

/**
 * Handle an individual event.
 */
function handleEvent() {
	var eventTitle = $(this).find("title").text();

That was nice and easy. Shame it gets a bit harder. To find the start and end times of the event the gd:when element is needed – this has attributes startTime and endTime which are standard XML Dates with optional time (e.g. "2011-10-17T22:15:00.000+01:00").

What’s so hard about that – apart from parsing dates in JavaScript, that is? Well, notice the gd: prefix in the when element. This indicates the XML namespace of the “when” element – but why is it different from the title element above?

A look at the source XML from the events feed gives a clue – the following namespaces are declared in the root element:

Namespace Prefix Definition
Default http://www.w3.org/2005/Atom
openSearch http://a9.com/-/spec/opensearchrss/1.0/
gCal http://schemas.google.com/gCal/2005
gd http://schemas.google.com/g/2005

The elements I needed to extract include both default Atom elements and some of the gd Google Calendar elements.

It should have been easy, especially with the jQuery library, but instead we have to handle prefixes differently in different browsers. There appear to be two different syntaxes in use and the following code works out which to use as it extract the when element.

	// Need to work out how to handle the namespaces on some elements.
	var nsgd = "gd\:";
	var startDateElement = $(this).find( nsgd + "when" );
	if( startDateElement.length == 0 )
	{
		// Some broswers ignore the namespace.
		nsgd = ""; 
		startDateElement = $(this).find( nsgd + "when" );
	}

See my earlier posting on this for more details.

One final problem with the dates: all day events run from 00:00hrs on the first day to 00:00hrs on the last. This means that the event appears to have an extra day if you present it as received. An all-day event doesn’t have a time associated with it – just the dates.

	var startDateStr = startDateElement.attr( "startTime" );
	var allDay = (startDateStr && startDateStr.length == 10);
	var startDate = ts_parseDate( startDateStr );
	var endDate = ts_parseDate( $(this).find( nsgd + "when" ).attr( "endTime" ) );

	// Fix extra day issue.
	if( allDay && endDate != null )
	{
		endDate.setDate( endDate.getDate() - 1 );
	}


After that it gets relatively easy so I’ll let the code speak for itself.

	var eventLocation = '';
	var eventWhere = $(this).find(nsgd + "where");
	if( eventWhere !== undefined )
	{
		eventLocation = eventWhere.attr("valueString");
	}

	var eventDescription = $(this).find( "content" ).text();

	var thisMonth = ts_formatMonth( startDate );
	if( thisMonth != lastMonth )
	{
		if( lastMonth != '' )
		{
			htmlEntries += '</table>';
		}

		htmlEntries += '<h2 class="event-month">' + thisMonth + '</h2>';
		htmlEntries += '<table class="events" align="center" width="85%" cellspacing="5">';
		htmlEntries += '<tr><td colspan="2"><hr color="silver"></td></tr>';

		lastMonth = thisMonth;
	}					

	var sDay = ts_formatMonthDay( startDate );
	htmlEntries += '<tr><td class="events" width="30%"><strong>' + sDay;
	var eDay = ts_formatMonthDay( endDate );
	if( endDate != null && sDay != eDay )
	{
		htmlEntries += ' - ' + eDay;
	}
	htmlEntries += '</strong>';

	if( !allDay )
	{
		htmlEntries += '<br>' + ts_formatTime( startDate );

		if( ts_formatTime( startDate ) != ts_formatTime( endDate ) )
		{
			htmlEntries += ' - ' + ts_formatTime( endDate );
		}
	}

	htmlEntries += '</td>';
	htmlEntries += '<td class="events">';
	htmlEntries += '<strong>' + eventTitle + '</strong><br/>';

	if( eventLocation != null && eventLocation.length > 0 )
	{
		htmlEntries += '<em><a href="http://maps.google.com/?q=' + eventLocation + '" target="_blank">' + eventLocation + '</a></em><br>';
	}

Notice that the Location is used to create an automatic Google Maps link when it is rendered. There are also a few extra functions referenced to parse and format the dates – these are all available in the JavaScript from the Thistle Society page.

One final detail worth noting: the content (eventDescription) is returned as plain text – Google Calendar doesn’t support anything else. Some solutions I have seen store escaped HTML in the description – but that is unreadable within the Google Calendar itself and messy to maintain.

I have a more elegant solution (well I think so) using the Wiki Text plugin. So here is the last part of the JavaScript:

	htmlEntries += '<p>' + $.wikiText( eventDescription ) + '</p>';
	htmlEntries += '</td></tr><tr><td colspan="2"><hr color="silver"></td></tr>';
}

Build the Page

The last line of the handleCalendar function (repeated below) inserts the list of events into the body of the page.

	$("#entries").html( htmlEntries );

A couple more things are required to put it all together in the page.

First a few extra script elements in the page head:

      <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
      <script type="text/javascript" src="scripts/jquery-wikitext-0.3.js"></script>
      <script type="text/javascript" src="scripts/events.js"></script>

This links in jQuery, the Wiki Text jQuery plugin and finally the code described above.

Finally, somewhere in the page at the point where the events should appear, the following is required:

    <div id="entries">Loading...</div>

Sample Code

Update, 3rd June 2014: I have extracted the relevant code from the Thistle Society web site and uploaded it to a GitHub repository at:

https://github.com/Kajabity/sample-code/tree/master/displaying-google-calendar-events-on-a-club-web-site

A quick way to try this out is to:

  • install XAMPP (https://www.apachefriends.org/index.html an open source combined Apache HTTP server with PHP and MySQL which I find ideal for developing WordPress web sites),
  • copy the “displaying-google-calendar-events-on-a-club-web-site” folder into the …htdocs folder in your XAMPP installation,
  • open a browser and navigate to http://localhost/displaying-google-calendar-events-on-a-club-web-site (assuming you left the default HTTP port as 80).
  • and then, if you are in Wigan, why not come along to one of the Thistle Society events and try some Scottish Country Dancing?

Conclusion

Well that’s how I went about it – this was one of my first forays into JavaScript and it’s all a bit of a mess. But it works – as you will see if you go to the page http://www.thistlesociety.org.uk/events.html. I plan to improve the page in the future and I’ve made a few small improvements while I have been writing this.

I hope this helps you add Google Calendar entries into your own pages. Don’t forget, you can also embed the calendar view directly into your page – have a look at some of the other options on the Calendar Settings page.

8 comments on “Displaying Google Calendar Events on a Club Web Site”

  1. Laura Y. says:

    Hi,

    The times are displayed on the web page according to which of the following timezones: a) the timezone of the google calendar or b) the timezone according to how its set on the client computer?

    Thanks,
    Laura

    1. simon says:

      I would imagine the code will display the Google Calendar time – but since the calendar is GMT and everyone I know that uses it is in the UK I haven’t had a chance to confirm that. However, the calendar can be configured in the settings to be in any timezone, and the timezone is included at the end of the date entries if they include a time (for GMT they look like “2012-02-11T17:46:27.000Z”).

      Ah, just found it – have a look at https://developers.google.com/google-apps/calendar/v2/reference and search for “timezone”. You need to set the current timezone parameter in the query, “ctz”. This will return the results adjusted to the timezone you require.

  2. Pink Frankenkstein says:

    Hi

    I was looking at your Google Calendar page and was wondering if you have a finished script available for download?

    Thanks

  3. simon says:

    Well, it’s not exactly a “Finished Script” but I’ve extracted the scruffy code I used for the Thistle web site and created a mini sample.

    Get the code from https://github.com/Kajabity/sample-code/tree/master/displaying-google-calendar-events-on-a-club-web-site

    If you download XAMPP (https://www.apachefriends.org/index.html) and copy the code into a subdirectory under the XAMPP htdocs directory then start XAMPP and navigate to http://localhost/displaying-google-calendar-events-on-a-club-web-site/ (or whatever URL you set it at) then you will see the results.

  4. John says:

    Thanks so much for posting details on your script. I wanted to let you know I found a problem you might want to be aware of. For your Google calendar, the first start date entry is coming in as:

    “2014-08-11T19:30:00.000+01:00”

    But for my calendar, the first entry is coming in as:

    “2014-08-11T09:00:00.000-07:00”

    The minus sign instead of a plus sign is making it so the page-events.js file fails on line 74. That is, m is null at that point since the regular expression match didn’t work. If you add a “-” to the regular expression on line 72, next to the “+”, is then works fine.

    1. simon says:

      Thanks – I’ve added it to the code, but not actually tested it with a negative date.

  5. John says:

    Another bug I found… for entries that are part of a series, the end time is not displaying correctly. I fixed it in the page-events.js file on line 191 by adding a last() to the call:

    var endDate = ts_parseDate(jQuery(this).find(nsgd + “when”).last().attr(“endTime”));

    1. simon says:

      Thanks for that: I had just spotted the same bug a week before – the solution I came up with was to identify the last “when” also using:

      var whenElement = $( nsgd + “when:last”, this );

      I just needed to commit the changes to GitHub…

Leave a Reply

Your email address will not be published. Required fields are marked *

*