Custom Post Types: FullCalendar & JSON

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, // <- true by default with FullCalendar
    'start' => $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'>
    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:

<link rel="stylesheet" href="<?php bloginfo('template_url'); ?>/css/fullcalendar.css" type="text/css" media="screen"  />

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.

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

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.

  • Pingback: wpmag.com - WordPress News, Themes, Tutorials, Plugins, Questions, ...

  • http://10sexyapples.com 10SexyApples

    Really excellent walkthrough series Noel.
    I’ve had many event related headaches over the past few years trying to research/decide what code to use or modify for my clients’ event needs and finally, thanks to your hospitality in sharing this, I dove in and incorporated the whole shebang into my framework so I can better provide what they need on demand … and learned some new stuff along the way.
    It’s a perfect foundation for being able to provide basic functionality or for building upon to create more customized or advanced functionality.
    Again … enormous thanks … seriously ;-)

  • http://axwax.de Axel

    Thanks so much for your tutorials on this subject – very useful indeed.
    One small suggestion: wouldn’t it be better to replace the code in step 2 using one of the techniques in http://ottopress.com/2010/dont-include-wp-load-please/ ? It does look a bit clunky…
    Other than that great stuff!
    Cheers,
    Axel

  • http://10sexyapples.com 10SexyApples

    Hi Noel, me again …
    I came across this method for passing the event post type into fullcalendar and wondered if you had any insight as to whether there were any advantages or disadvantages between the two methods?
    http://stackoverflow.com/questions/5074242/minimizing-event-data-shown-on-the-jquery-fullcalendar

  • Noel

    Hi SexyApples (or all 10 of you :) ),

    Thank you for the kind comments, they’re well appreciated! The tutorial you posted right above is actually a method to display the content different (not the actual process of displaying it). The guy just wants to remove time from the event blocks within the FullCalendar.

    Hi Axel,

    Totally valid point, I’ve previously read Otto’s article. However, I found that his points 2 & 3 didn’t really apply so I went with a pragmatic approach. That’s no excuse though for not using best practices, I’ll try to change that once I start understanding more of this stuff ;)

    Cheers

    Noel

  • http://mroomphilly.com/ Michael

    Some great ideas here.. I took a slightly different approach here (post template instead of an external page to generate the JSON). Still working out a few kinks but I’m so much happier implementing events on a WP install using custom post types than anything else I tried in the past by far. Will keep checking back for more inspirations :)

  • http://10sexyapples.com 10SexyApples

    Ah, just one of me, although I tend to imagine there are more ;-)

    I realize he want to remove the time, as do I, but, I was referring to the fact that he is inserting php directly into the full calendar javascript function as opposed to including it as a json feed.

    I’m new to working with calendar data ( these time formats are a p.i.t.a. and have never used a json feed before so … and I’m extremely anal about trying to make the right decisions about my implementations before committing them to the page, so, I feel like I need to understand everything inside and out to form an intelligent opinion.

    … and I’m pretty confused … heh.

    I was struggling with figuring out why one method might be better/worse than the other.

    Also, in the fullcalendar json.php example, they don’t use a json header … made me question why of course … and my times are showing up incorrectly on fullcalendar althought they are correct in my widgets, so, more questions … working on that one right now.

    I actually implemented the timepicker addon to datepicker to ensure that the times get entered in the right format, and am using their hh:mm format to ensure that only the time as you specified it is being recorded. There will be many people entering the data, so, necessary evil.

    I need to implement the allday option as well ( couldn’t use the code that was in the comments for that ) and figure out how to take it all the way to fullcalendar, as well as separate by my event type taxonomies … heh … got my work cut out for me tonight ;-)

    Oh, and I meant to ask if you had any issues with that ical file … I get it downloaded, but, when ical tries to open it I get a “this file isn’t readable by ical” error.

    Any insight into any of the many questions I have roaming around in my head would of course be greatly appreciated, although obviously not expected. I’m mostly just rambling out loud.

    All of the info and examples you’ve provided have been so truly helpful … so, just want to say thanks for that again Noel!

    cheers!

  • Noel

    Hi Michael,

    Awesome implementation, looks good!

    SexyApples,

    All valid questions & apologies for the delay, busy as all hell!

    – I chose the ‘feed’ route so that external parties could also hook up to it if they wanted too, just a little future-proofing I guess. Otherwise, there’s not that big of a difference (to my knowledge)

    – JSON header isn’t required because you’re dumping everything in arrays (not in a separate file).

    – Timepicker was also something I thought about, but in the end, my 100+ pubforce users (for my WP theme) never once ran into that issue, so somewhere down the line I give my clients a bit of leeway (as most like the tab-type-tab-type solution, rather then moving sliders around).

    – Thanks for the iCal info, I checked it on mine and had no issue (MS Outlook). I’ll test again at the next opportunity.

    Looks like you’ve got your work cut out for you though ;)

    Cheers

    Noel

  • Angelia

    No worries Noel, believe me, I understand busy … heh … and thanks for the feedback.

    I know what you mean on the sliders, and didn’t realize you were implementing with tab-type, I think that would be preferable as well. I just didn’t want it to be just one open text field, like in the example. I don’t feel confident enough yet with all the time formatting to mess too much with it, but, I’m going to be getting into that later on today, so …

    Also makes sense on the feed, making it accessible for future proofing.
    I have been moving things around and renaming quite a bit with all the files and code you provided to make them work and get enqueued etc. from within my framework, so, chances could be that I’ve made something go askew with the ical template. I have to put it off to the side for now, but, will be back on that after launch.

    And yes, I’ve got my work cut out for me … although … it looks like Michael has implemented things almost identically to how I need to implement, so, perhaps he would be so kind as to share some enlightenment … hey comment neighbor … any chance?

    cheers back! ;-)

  • Pingback: WordPress Community Links: Gold star cookie edition | WPCandy

  • Pingback: Alex Barber | Digital Artist | WordPress calendar with custom post types

  • http://fantasticvisions.net Leo Plaw

    Here is another implementation of FullCalendar.

    http://fantasticvisions.net/calendar/

    There are still some things to attend to such as some of the filters and little of the styling.

    It integrates with my custom post type, Evens.

  • Tobi

    Hey Noel.
    I was looking for this for a long time. Thanks. Is there any chance you could add a function I can call in a template page to get a list of search result. Something like …
    get_tf_list(fromDate, toDate, categories, custom-fields, limit);
    Or do you have a suggestion how to get a date based search option to filter the events?
    Take care, Tobi

  • Noel

    Thanks Angelia & Leo!

    Hi Tobi, haven’t looked into that yet, but it’s a good idea (although it would only really apply in extremely busy calendars I assume?).

    Cheers

    Noel

  • Tobi

    Thanks for the fast reply, Noel.

    I think about managing my events in a list on a template page. I found the following code that helps creating a query to get the events and custom fields. If you have any idea how to order the events by their startdate from today on, please let me know.
    Keep doing your tutorials and themes, your work is awesome.


    // Create a new instance
    $query = new WP_Query( array ( 'post_type' => 'tf_events', 'orderby' => 'meta_value', 'meta_key' => 'tf_events_startdate' ) );
    $second_query = new WP_Query('post_type=tf_events');

    // The Loop
    while( $second_query->have_posts() ) : $second_query->the_post();
    echo '';
    echo get_post_meta(18 , 'tf_events_startdate', true);
    $starttime = get_post_custom_values("tf_events_startdate");
    echo date("l, j. F Y", $starttime[0]);
    get_post_custom_values('tf_events_startdate', 17);
    echo '';
    the_title();
    the_meta();
    echo '';
    endwhile;

    wp_reset_postdata();
    ?>

  • Todd

    Thanks so much for sharing this, Noel! Trying to wrap my head around it all!

  • Bassscape

    Great work on the combination of events using cpts and displaying nicely in a calendar! Creating and managing events is one of the most relevant WordPress customizations I have undertaken for various projects.

    I have been trying to remove the time from in front of the event title when displayed on the calendar.

    Could anyone provide some advice on where I should be looking to make this change?

  • Ghe

    Hi Noel,

    Great tutorial.
    I have a question. How i can set the json-feed to pull data from regular posts instead custom posts?

    // – custom post type variables –
    $custom = get_post_custom(get_the_ID());
    $sd = $custom[“tf_events_startdate”][0];
    $ed = $custom[“tf_events_enddate”][0];

    Thanks for your time,

    regards

  • Ejub

    I love your tutorials and the calendar is great really! I would like to ask if there is someway I can make the calendar load the events faster? Now it´s loading the calendar first, then, 5 seconds later the events appears.

    Keep up the amazing work! Thanks =)

  • Zakir Sajib

    Hello Michael,
    I seen your site where you implemented the calendar, I want exactly same, could you please let me know how did you do that? I followed this tutorial but some reason nothing is showing. Thanks in advance.

  • Zakir Sajib

    my json output following but still does not show in calendar:

    [{“title”:”Session three”,”start”:”2011-11-01 22:00:00″,”end”:”2011-11-01 23:35:00″,”url”:”http://localhost/StageEvent/?sessions=session-three”},{“title”:”Another time pass”,”start”:”2011-11-03 09:00:00″,”end”:”2011-11-03 16:00:00″,”url”:”http://localhost/StageEvent/?sessions=another-time-pass”},{“title”:”Test”,”start”:”2011-11-04 10:00:00″,”end”:”2011-11-04 20:00:00″,”url”:”http://localhost/StageEvent/?sessions=test-2″},{“title”:”Session four”,”start”:”2011-11-29 03:00:00″,”end”:”2011-11-30 13:00:00″,”url”:”http://localhost/StageEvent/?sessions=session-four”}]

    My code as follows:

    // – grab date barrier –
    $today6am = strtotime(‘today 6:00′) + ( get_option( ‘gmt_offset’ ) * 3600 );
    echo $today6am;

    // – query –
    global $wpdb;
    $querystr = ”
    SELECT *
    FROM $wpdb->posts, $wpdb->postmeta
    WHERE ($wpdb->posts.ID = $wpdb->postmeta.post_id)
    AND $wpdb->postmeta.meta_key = ‘sessions_event_end’
    AND $wpdb->posts.post_type = ‘sessions’
    AND $wpdb->posts.post_status = ‘publish’
    ORDER BY $wpdb->postmeta.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();
    $sd = strtotime($custom[“sessions_event_start”][0]);
    $ed = strtotime($custom[“sessions_event_end”][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);

    $year = date(‘Y’);
    $month = date(‘m’);

    // – json items –
    $jsonevents[]= array(
    ‘title’ => $post->post_title,
    ‘start’ => $gmts,
    ‘end’ => $gmte,
    ‘url’ => get_permalink($post->ID)
    );

    endforeach;
    else :
    endif;

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

  • Zakir Sajib

    I found better code which avoids your

    3) json-feed.php – query part.

    posts as $post) {
    $events[] = array(
    ‘title’ => $post->post_title,
    ‘start’ => get_post_meta($post->ID,’sessions_event_start’,true),
    ‘end’ => get_post_meta($post->ID,’sessions_event_end’,true),
    ‘allDay’ => (get_post_meta($post->ID,’_all_day’,true) ? ‘true’ : ‘false’),
    );
    }
    echo json_encode($events);
    ?>

    That’s it.

  • Zakir Sajib

    I found better code which avoids your

    3) json-feed.php – query part.

    posts as $post) {
    $events[] = array(
    ‘title’ => $post->post_title,
    ‘start’ => get_post_meta($post->ID,’sessions_event_start’,true),
    ‘end’ => get_post_meta($post->ID,’sessions_event_end’,true),
    ‘allDay’ => (get_post_meta($post->ID,’_all_day’,true) ? ‘true’ : ‘false’),
    );
    }
    echo json_encode($events);
    ?>

    That’s it.

  • http://twitter.com/sanchothefat Robert O ‘Rourke

    I’m using fullcalendar as an interface for creating events in the backend in my events plugin. Lovely stuff.

    I’m just curious why you don’t use the wordpress ajax methodology though? eg. add_action( ‘wp_ajax_nopriv_events_json_feed’, ‘events_json_feed’ );

  • http://www.noeltock.com Noel

    @ robert , no clue tbh. Not familiar with that action or ajax much (just a silly front-end dev :) ). I’ll look into it though.. definitely makes sense. thanks!

    @ zakir, yep definitely, from 3.1 there are many ways to now execute queries differently using meta. thanks!

  • Kate

    I’d be interested in seeing how it would work (if it’s even possible) to pass the info back to WordPress after an event is dragged to a new date, etc. Might take a stab at it myself…

  • http://www.svenvanbrabant.be/ Sven_Vanbrabant

    This is really a great tutorial. Very useful. Thanks!

  • Cody S.

    When I create an event that is happening in the future (say August 25, 2012) and I post it the calendar view shows it as an event for the day I posted it. So the WP FullCalendar isn’t displaying the event on the day of the event, but rather the day it was posted. How do I make it so it shows the event on the day the event actually is and not when it was posted?

  • zakir

    is it possible to pass image. i mean my variable holds image location. it seems its possible using title key in full calendar.

    the images is one extra custom field of my custom taxonomy.

    still no luck.

  • Pat

    is there a mini or widget version of this fullcalendar

  • http://BandonRandon.com BandonRandon

    Is there a reason why you are only getting future events? Is this just a speed thing?

  • Ben Danyi

    Is there anything wrong with using this to create a JSON linkup for a iOS app using phonegap + jquery mobile?