Lauri Liimatta

Blog Home | Portfolio

How to create a simple event calendar for KirbyCMS

In this tutorial I will show you how to implement a simple and practical event calendar for KirbyCMS. Most of the heavy lifting will be done by the FullCalendar JavaScript library and some PHP peppered into the mix. Let's begin!

1) Download FullCalendar assets

Go to FullCalendar download page and grab the latest version. Move the fullcalendar.min.js to assets/js and fullcalendar.min.css to assets/css.

2) Get the latest jQuery and moment.js

Download momentjs from here and jQuery from here. Place both of them in assets/js.

3) Create calendar.js

Inside of assets/js create a file called calendar.js and for now just leave it empty.

4) Load all the assets

Open the header.php snippet and load the assets by using the following code:

<?php
echo js(array(
    'assets/js/jquery-3.2.1.min.js',
    'assets/js/moment.min.js',
    'assets/js/fullcalendar.min.js',
    'assets/js/calendar.js'
));
echo css('assets/css/fullcalendar.min.css');
?>

It's likely that you will only need these files on the calendar page and therefore we can wrap them with an if-statement. This way we can keep rest of the pages lighter by not loading unneccessary resources.

<?php
if($page->uid() == 'calendar') {
    echo js(array(
      'assets/js/jquery-3.2.1.min.js',
      'assets/js/moment.min.js',
      'assets/js/fullcalendar.min.js',
      'assets/js/calendar.js'
    ));
    echo css('assets/css/fullcalendar.min.css');
} ?>

5) Create a calendar page

Inside of your content folder create a new folder called calendar and also within that folder add a txt file with the same name. If you give your calendar page a different name then make sure it matches the one used in the step 4 if-statement.

6) Add events

Inside of the calendar folder create a few events in the usual Kirby way by creating subfolders. Each event should have at least a title, date and text fields. The format of the date field should be DD-MM-YYYY.

7) Create the calendar template

Create a calendar.php file inside of site/templates. If you're working with an existing site you probably already want to have the header.php snippet included but in case you aren't make sure you at least include that one. After the header snippet add the following code:

<?php if($events->count() > 0): ?>
    <ul class="events">
    <?php foreach($events as $event): ?>
        <li class="single-event">
            <h3><?php echo $event->title() ?></h3>
            <time><?php echo $event->date('d.m.Y') ?></time>
            <?php echo $event->text()->kirbytext() ?>
        </li>
    <?php endforeach; ?>
    </ul>
<?php endif; ?>
    <p>No events found.</p>
<?php endif; ?>

The above snippet loads all the events in a list and shows their title, date and text description. However at this moment it will not show you any results yet because the events variable is still undefined.

8) Create the calendar controller

Add the controllers folder inside of the site folder in case it isn't there already and then create a calendar.php inside of it. The file should read as follows:

<?php 
return function($site, $pages, $page) {

    $events = $page->children()->visible()->sortBy('date', 'desc');

    return compact('events');
};

If you open the calendar page in your browser now it should show all of your visible events. However since we are making a calendar it would be nice to have a little more control over which events get shown so let's modify this controller a bit.

<?php 
return function($site, $pages, $page) {

    if(get('events') == 'past'):
        $events = $page->children()->visible()->sortBy('date', 'desc')->filterBy('date', '<', time());
    elseif(get('events') == 'upcoming'):
        $events = $page->children()->visible()->sortBy('date', 'desc')->filterBy('date', '>=', time());
    elseif(get('events') && strtotime(get('events'))):
        $events = $page->children()->visible()->sortBy('date', 'desc')->filterBy('date', '==', strtotime(get('events')));
    else:
        $events = $page->children()->visible()->sortBy('date', 'desc');
    endif;

    return compact('events');
};

What I've done here is that I've defined a few parameters to look for (past and upcoming) and then I modify the collection accordingly to show either just upcoming or past events. You can also use a specific date and only show events occuring on that given day.

Here's an example each parameter option:

/calendar?events=upcoming
/calendar?events=past
/calendar?events=01-10-2017

9) Add a calendar view above the list

In the site/templates/calendar.php add a new div with the id of calendar just above the unordered list and leave it empty.

<div id="calendar"></div>

10) Activate the calendar view

Open the empty calendar.js file in your assets/js folder and drop the following code into it.

$(function() {
    $('#calendar').fullCalendar({
        header: {
            left: 'prev',
            center: 'title',
            right: 'next'
        }
    });
});

If you check the page in your browser a nice classic calendar view should be appearing above your list. However at the moment the calendar doesn't have any data or really let you do anything useful so let's change that next.

11) Create XML feed of your events

One of the ways to feed data to the FullCalendar plugin is to use a XML feed as the data source. To accomplish that you need to create a subfolder called xml inside of content/calendar. Also create a txt file called xml inside of this folder.

Before you can move on to creating the xml template you need to download the Feed plugin and place its contents into site/plugins.

Once the feed plugin is installed go to your site/templates and create a new template called xml.php. This template should look as follows:

<?php
echo page('calendar')->children()->visible()->flip()->feed(array(
    'title'       => $page->title(),
    'description' => $page->text(),
    'link'        => 'calendar',
));
?>

Here I'm using the feed plugin to create a XML feed of the visible subpages (events) of the calendar page.

12) Connect the XML feed with the calendar view

Next you should go back to the calendar.js file and modify the existing code to load the events from your freshly created XML feed. You can do it by adding the events option:

events: function(start, end, timezone, callback) {
    $.ajax({
        url: 'calendar/xml',
        dataType: 'xml',
        data: {
            start: start.unix(),
            end: end.unix()
        },
        success: function(doc) {
            var events = [];
            $(doc).find('item').each(function() {
                events.push({
                    title: $(this).find('title').text(),
                    start: new Date($(this).find('pubDate').text()),
                    url: $(this).find('link').text()
                });
            });
            callback(events);
        }
    });
},

What's happening here is that I am creating an ajax call to the XML feed, taking each event individually and adding them to my array which then gets used in the callback. Events should now appear in the calendar view above your list.

13) Filter by the day using the calendar view

In an earlier step we added an option to filter the results by the day using the right parameter. However a random visitor may not be aware of such feature so let's make it that clicking on the days of the calender will do this for the visitor. You can do this by defining the dayClick option:

dayClick: function(date, jsEvent, view) {
    var calEventDate = moment(date).format('DD-MM-YYYY');

    url = window.location.href.split('?')[0];

    window.location.href = url + '?events=' + calEventDate;

    return false;
},

Here we're grabbing the date which the visitor clicked on, formatting it correctly and then sending the visitor to the right address using the day filter parameter.

Now if you go and click on any day of the calendar view the list view will adjust to only show the events of that specific day.

Just in case here's a full snippet of the calendar.js:

$(function() {
    $('#calendar').fullCalendar({
        events: function(start, end, timezone, callback) {
            $.ajax({
                url: 'calendar/xml',
                dataType: 'xml',
                data: {
                    start: start.unix(),
                    end: end.unix()
                },
                success: function(doc) {
                    var events = [];
                    $(doc).find('item').each(function() {
                        events.push({
                            title: $(this).find('title').text(),
                            start: new Date($(this).find('pubDate').text()),
                            url: $(this).find('link').text()
                        });
                    });
                    callback(events);
                }
            });
        },
        dayClick: function(date, jsEvent, view) {
            var calEventDate = moment(date).format('DD-MM-YYYY');

            url = window.location.href.split('?')[0];

            window.location.href = url + '?events=' + calEventDate;

            return false;
        },
        header: {
            left: 'prev',
            center: 'title',
            right: 'next'
        }
    });
});

There you have it, a simple but practical calendar for any Kirby website. I recommend looking into the docs of FullCalendar because it has plenty of other features that were not mentioned in this tutorial.

Are you looking for a web development help or developer for your project? Get in touch and let's see if I can help you out.