Enter Search Query:

  • 4783
  • Close

XML driven calendar

Recently I’ve been playing around with various Calendar scripts. There’s Adam Shaw’s Full-calendar and Ross Haney’s GCE plugin for WordPress. Either of these work work great with a Google Calendar events feed, but what if you’ve got multiple XML files, each of which represent a single event? Well to handle that scenario I had to write my own code. Here is a quick overview:

In essence will cover a PHP script I wrote to create a calendar where dates with matching events are linked to a page containing more information about that event. All I had to work with, was a directory of XML files each of which represent a single event.

The script below is proof of concept code and not a particularly polished application. Some issues that have come to mind in the 3-4 hours I spent writing it are:

  1. The script can only display one event link per day, so if you’ve got multiple events on a day…sorry that’s not implemented yet.
  2. The script simply attaches the event link from the XML file to a matching day in the calendar. Other event information (i.e. title) could be read from this file, but displaying it is not implemented.

Here’s how the script works:

Take a bunch of XML event files like the sample below and read them all into an array of events. Then generate an HTML calendar and using the day, month and link nodes from the XML file and add an anchor to those days that have a corresponding event. Pretty simple.

<?xml version="1.0"?>

[step 1] Create a function that can read in all of the XML files in a directory and create an array containing event days and links for a single month.

// function for generating arrays of events from XML files in a directory
function get_xml_events ($month, $year){

// current time
$time = time();

// array of events built by xml files in this directory
$events = array();

// open each xml file in this directory
foreach(glob("*.xml") as $filename) {
// get the contents of the the current file
$xml_file = file_get_contents($filename, FILE_TEXT);

// create a simplexml object from the contents of the current file
$xml = simplexml_load_string($xml_file);

// create a day, link, description, etc.
$eventDay = (int)$xml->day;
$eventMonth = (int)$xml->month;
$eventYear = (int)$xml->year;
$eventLink = (string)$xml->link;
$eventDesc = (string)$xml->description;

if($eventMonth == $month && $eventYear == $year)
$events[$eventDay] = array($eventLink,'linked-day');
return $events;

[step 2] Use a function to generate the calendar (no need to re-invent the wheel here, Keith Devens already did the hard stuff).

Generate Calendar Function
# PHP Calendar (version 2.3), written by Keith Devens
# http://keithdevens.com/software/php_calendar
# see example at http://keithdevens.com/weblog
# License: http://keithdevens.com/software/license

function generate_calendar($year, $month, $days = array(), $day_name_length = 3, $month_href = NULL, $first_day = 0, $pn = array()){
$first_of_month = gmmktime(0,0,0,$month,1,$year);
#remember that mktime will automatically correct if invalid dates are entered
# for instance, mktime(0,0,0,12,32,1997) will be the date for Jan 1, 1998
# this provides a built in "rounding" feature to generate_calendar()

$day_names = array(); #generate all the day names according to the current locale
for($n=0,$t=(3+$first_day)*86400; $n<7; $n++,$t+=86400) #January 4, 1970 was a Sunday
$day_names[$n] = ucfirst(gmstrftime('%A',$t)); #%A means full textual day name

list($month, $year, $month_name, $weekday) = explode(',',gmstrftime('%m,%Y,%B,%w',$first_of_month));
$weekday = ($weekday + 7 - $first_day) % 7; #adjust for $first_day
$title = htmlentities(ucfirst($month_name)).'&nbsp;'.$year; #note that some locales don't capitalize month and day names

#Begin calendar. Uses a real <caption>. See http://diveintomark.org/archives/2002/07/03
@list($p, $pl) = each($pn); @list($n, $nl) = each($pn); #previous and next links, if applicable
if($p) $p = '<span class="calendar-prev">'.($pl ? '<a href="'.htmlspecialchars($pl).'">'.$p.'</a>' : $p).'</span>&nbsp;';
if($n) $n = '&nbsp;<span class="calendar-next">'.($nl ? '<a href="'.htmlspecialchars($nl).'">'.$n.'</a>' : $n).'</span>';
$calendar = '<table class="calendar">'."n".
'<caption class="calendar-month">'.$p.($month_href ? '<a href="'.htmlspecialchars($month_href).'">'.$title.'</a>' : $title).$n."</caption>n<tr>";

if($day_name_length){ #if the day names should be shown ($day_name_length > 0)
#if day_name_length is >3, the full name of the day will be printed
foreach($day_names as $d)
$calendar .= '<th abbr="'.htmlentities($d).'">'.htmlentities($day_name_length < 4 ? substr($d,0,$day_name_length) : $d).'</th>';
$calendar .= "</tr>n<tr>";

if($weekday > 0) $calendar .= '<td colspan="'.$weekday.'">&nbsp;</td>'; #initial 'empty' days
for($day=1,$days_in_month=gmdate('t',$first_of_month); $day<=$days_in_month; $day++,$weekday++){
if($weekday == 7){
$weekday = 0; #start a new week
$calendar .= "</tr>n<tr>";
if(isset($days[$day]) and is_array($days[$day])){
@list($link, $classes, $content) = $days[$day];
if(is_null($content)) $content = $day;
$calendar .= '<td'.($classes ? ' class="'.htmlspecialchars($classes).'">' : '>').
($link ? '<a href="'.htmlspecialchars($link).'">'.$content.'</a>' : $content).'</td>';
else $calendar .= "<td>$day</td>";
if($weekday != 7) $calendar .= '<td colspan="'.(7-$weekday).'">&nbsp;</td>'; #remaining "empty" days

return $calendar."</tr>n</table>n";

[step 3] Finally in order view upcoming and past events we’ll have to create some code that allows navigating through the calendar one month at a time forward and reverse. The tricky part here is when to roll over to the next/previous year. Once the previous and next month links are sorted out, we generate the calendar voila!

// set current time
$time = time();
// set current date
$today = date('j',$time);

// if url parameters are set, we generate some appropriate previous/next month (pn) links
if (isset($_GET['month']) && isset($_GET['year']) && isset($_GET['pn'])) {
// set month, year & previous or next (pn)
$month = (int)$_GET['month'];
$year = (int)$_GET['year'];
$pn = $_GET['pn'];
// if the month is btw 1 and 12 we incr for next and decr for previous
if($month > 1 && $month < 12){
$nxt = 'parser.php?pn=next&month='.((int)$_GET['month']+1).'&year='.$year;
$prv = 'parser.php?pn=prev&month='.((int)$_GET['month']-1).'&year='.$year;
} // In Dec the next month is Jan and the year is upped, the prev month is Nov and the year stays the same
if($month == 12){
$nxt = 'parser.php?pn=next&month=1&year='.($year+1);
$prv = 'parser.php?pn=prev&month=11&year='.$year;
} // In Jan the next month is Feb and the year stays the same, the prev month is Dec and the year is decr by one
if($month == 1){
$nxt = 'parser.php?pn=next&month=2&year='.$year;
$prv = 'parser.php?pn=prev&month=12&year='.($year-1);
} else { // otherwise we generate a calendar for the current month based on the current date & some (pn) links
// set current month & year
$month = (int)date('n', $time);
$year = (int)date('Y', $time);
// same conditional crap as above
if($month > 1 && $month < 12){
$nxt = 'parser.php?pn=next&month='.($month+1).'&year='.$year;
$prv = 'parser.php?pn=prev&month='.($month-1).'&year='.$year;
if($month == 12){
$nxt = 'parser.php?pn=next&month=1&year='.($year+1);
$prv = 'parser.php?pn=prev&month=11&year='.$year;
if($month == 1){
$nxt = 'parser.php?pn=next&month=2&year='.$year;
$prv = 'parser.php?pn=prev&month=12&year='.($year-1);

// array for our previous and next links
$pn = array('&laquo;'=>$prv, '&raquo;'=>$nxt);

// actually generating the calendar
echo generate_calendar($year, $month, get_xml_events($month, $year), 3, NULL, 0, $pn);

…and there we have it…my working XML file driven events calendar.

Download the source commented source here: XML calendar.zip

May 11, 2011

Related Posts