Custom Post Type – FullCalendar & JSON with WordPress

This tutorial builds on my previous how-to’s relating to creating & using Events with Custom Post Types (all within WordPress). With the first tutorial came a number of comments, a couple of which were looking to integrate the events custom post type with FullCalendar, an extremely well put together jQuery plugin created by Adam Shaw . Today’s guide will do just that (but this can obviously be applied to regular posts too).

1) requirements

As stated, our goal is to display a beautiful calendar within WordPress listing our events. FullCalendar accepts event data in a variety of ways (even Google calendars), but for our purpose we will use JSON (JavaScript Object Notation). That may sound foreign to some of you, but it’s actually all very easy. What we will do today, is create a simple JSON feed using our custom post type data that will then be read by FullCalendar. Once that is in place, we will need to make sure FullCalendar is integrated into WordPress and then finally display the calendar!

2) json-feed.php – header

It’s important to note that the file we’ll be creating is standalone, you don’t include it through your functions.php or elsewhere. As such, our header should come as no surprise. The paths listed below also assume that this document will be within a sub-folder of your theme (i.e. /includes/json-feed.php, /functions/json-feed.php).

// - standalone json feed -

header('Content-Type:application/json');

// - grab wp load, wherever it's hiding -
if(file_exists('../../../../wp-load.php')) :
	include '../../../../wp-load.php';
else:
	include '../../../../../wp-load.php';
endif;

global $wpdb;

We declare the document type and then include wp-load to be able to make use of WordPress functionality & data.

3) json-feed.php – query

Now that you’re document is correctly coded and is hooked into WordPress, you’ll run the query you’ve seen in previous tutorials.

// - grab date barrier -
$today6am = strtotime('today 6:00') + ( get_option( 'gmt_offset' ) * 3600 );

// - query -
global $wpdb;
$querystr = "
    SELECT *
    FROM $wpdb->posts wposts, $wpdb->postmeta metastart, $wpdb->postmeta metaend
    WHERE (wposts.ID = metastart.post_id AND wposts.ID = metaend.post_id)
    AND (metaend.meta_key = 'tf_events_enddate' AND metaend.meta_value > $today6am )
    AND metastart.meta_key = 'tf_events_enddate'
    AND wposts.post_type = 'tf_events'
    AND wposts.post_status = 'publish'
    ORDER BY metastart.meta_value ASC LIMIT 500
 ";

$events = $wpdb->get_results($querystr, OBJECT);
$jsonevents = array();

// - loop -
if ($events):
global $post;
foreach ($events as $post):
setup_postdata($post);

// - custom post type variables -
$custom = get_post_custom(get_the_ID());
$sd = $custom["tf_events_startdate"][0];
$ed = $custom["tf_events_enddate"][0];

// - grab gmt for start -
$gmts = date('Y-m-d H:i:s', $sd);
$gmts = get_gmt_from_date($gmts); // this function requires Y-m-d H:i:s
$gmts = strtotime($gmts);

// - grab gmt for end -
$gmte = date('Y-m-d H:i:s', $ed);
$gmte = get_gmt_from_date($gmte); // this function requires Y-m-d H:i:s
$gmte = strtotime($gmte);

// - set to ISO 8601 date format -
$stime = date('c', $gmts);
$etime = date('c', $gmte);

// - json items -
$jsonevents[]= array(
    'title' => $post->post_title,
    'allDay' => false, //  $stime,
    'end' => $etime,
    'url' => get_permalink($post->ID)
    );

endforeach;
else :
endif;

// - fire away -
echo json_encode($jsonevents);

Many of the items should be familiar from the previous tutorials, $today6am to only grab future events (in a human way, not just event >= now), various $gmt variables are to convert the time to GMT+0 (a good practice, and was also done with my iCal export tutorial). What is new here however, is that we’re outputting arrays within an array in order to produce the JSON output. Above the loop, we declare $jsonevents as an array, and then during the loop populate it with individual event arrays containing all our data. See the event object documentation for more information about these parameters.

Finally we grab all the data and encode it in the JSON format. The feed is now done, that was easier than you thought right?

4) json-feed.php – validation

Before, we take another step, lets make sure that our feed validates. To do that, just copy paste your output (by visiting the file’s URL) here JSON Formatter & Validator. Everything should come up green!

You’ll also notice that my event times in the screenshot above have the timezone +00:00, which is exactly what we were striving for.

5) functions.php

I don’t particularly like adding JavaScript files through header.php, and the codex also explicitly states that such scripts should be enqueued. Not only are we doing that below (similar to past tutorials), but also using some PHP to derive the values of one of the JS parameters, in this case the file path to the json feed (we’re using the localize function to do that, storing within $jsonevents). GCal is also optional, unless of course you’d like to use Google Calendars instead of our JSON feed.

function load_themeforce_js() {

// ... my other scripts...

// - fullcalendar -
wp_enqueue_script('fullcalendar', (get_bloginfo('template_url')) . '/js/fullcalendar.js', array('jquery'));
wp_enqueue_script('gcal', (get_bloginfo('template_url')) . '/js/gcal.js', array('jquery'));

// - set path to json feed -
$jsonevents = get_bloginfo('template_url') . '/functions/json-feed.php';

// - tell JS to use this variable instead of a static value -
wp_localize_script( 'fullcalendar', 'themeforce', array(
	'events' => $jsonevents,
	));
}

add_action('wp_print_scripts', 'load_themeforce_js');

Last but not least we add this action to the print scripts hook.

6) header.php

Here, you can choose what you want to do, but to stay in line with the above section, we’ll add the actual jQuery script right into the header file (below wp_head() ):

<script type="text/javascript">// <![CDATA[
    jQuery(document).ready(function() {
    jQuery('#calendar').fullCalendar({
        events: themeforce.events
        });
});
// ]]></script>

You’ll see here that the parameter events is referencing to themeforce.events, a variable that we provided through the localize script in our functions.php. This entire script will essentially tell your browser; load the calendar in the div with ID ‘calendar’ and pull the data from the json path (which is derived from themeforce.events variable).

Also within the header, you’ll want to pull in the CSS file from FullCalendar:


7) fullcalendar.js

Remember how we are converting times to GMT +0? Well FullCalendar doesn’t timezone conversion on by default, so we’ll need to make a small edit to the actual javascript file (as adding it to the URL didn’t seem to function for our scenario). Just go into fullcalendar.js and search ignoreTimezone, find the instance which gives it the value true and then change it to false, like so (line #39 of the full JS file):

ignoreTimezone: false,

8) making it all happen

Yep, you guessed it. To make this all show up now, all you need to do is add the container which is called out in section 6 above. Obviously don’t add this in the ‘Visual’ part of a page, but the ‘HTML‘ segment.

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

You are finally done, make sure you style your fullcalendar.css file to give it that final polish! If you’re getting stuck understanding certain elements, just google the terms (if it’s a WordPress concept, search it in the Codex). Copy paste will only take you so far, understanding the underlying concepts (even if only on a higher level) will take you much further.

your turn to speak

Would you like to share your FullCalendar WordPress combo, discuss customizations or add your insights to this article? I’d love to hear what you think on this subject.