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"?>
<event>
<day>5</day>
<month>5</month>
<year>2011</year>
<link>https://davidvielmetter.com</link>
<description>Description</description>
<title>Title</title>
</event>

[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

Comments

7 responses to “XML driven calendar”

  1. John Avatar
    John

    Hi David,
    Thank you for sharing this script with us. It’s really easy to work with xml files, but it’s a pain to write a xml file for every event. Here:http://www.flashxml.net/calendar.html I found a calendar that uses just one xml for events and there you can add more events for a day. Maybe this will be helpful for somebody.

    1. David Vielmetter Avatar

      Hey thanks for sharing John. I hope it helps someone too.

    2. David Vielmetter Avatar

      Hi John,

      The reason there is one XML file per event is that I used to work for a company (OmniUpdate) that dealt with XML files which were generated by a Content Management system called OUCampus. This CMS was able to easily generate XML files with any structure. That’s the reason my script utilizes many XML files (one for each event). If the FlashXML calendar you found suits your needs better, I’m glad for you and thank you for posting as you are correct that it may help someone else.

      Cheers

  2. João Calheiros Avatar
    João Calheiros

    Hi David,

    This script it´s awesome, and very easy to understand and implement, thank you for sharing it.
    As a php novice i’ve a simple question, if possible for you to answer;
    – How can i change the language of the calendar (week, month); example to Portuguese?

    Thanks in advance

    1. David Vielmetter Avatar

      HI Joao,

      Thank you for those kind words! The script has limited use and is more of a proof of concept than anything else. Unfortunately I have not localized the script (meaning I have not created a separate language file for it). This means you’ll need to change the code to change the language to Portugese or localize the script with language files.

      Cheers,
      David

  3. Karen Avatar

    Hi David, Thanks so much for the great script. I wanted to show you what I did with it. I turned it into a simple reservation/booking system (if you go to this website I’ve listed and click on “Check Availability” you can see it in action. it works really good except for some strange reason a few people have said they aren’t able to proceed to the next or previous months (I changed the next/prev to form inputs using GET) I myself haven’t been able to duplicate the problem in any browser so I have no idea why for a small minority the next/prev links don’t work, any ideas? ;o)

    1. David Vielmetter Avatar

      Oh man I haven’t looked at this in ages. For what it’s worth the prev next links work for me on ipad and Firefox. I’ll look at the code over the weekend …there is a good chance the code is buggy. I threw it together in an evening or two a few years ago.