Advertisement

WordPress Event Calendar Using Custom Post Types and Verbose Calendar

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →

We use WordPress Event Calendar to show the posts created on each day. Also visitors would love to know about the future posts. I have seen some sites which provide the Title of future tutorials so that visitors can stay updated on the topics they like. Likewise we can use WordPress as an event management system as well. Schedules of events are much more important than blog posts. We need to provide the option for users to view events on a calendar. Even a site like the Tuts+ network can have so many events like the launch of a new Tuts+ site, the launch of an e-book, a monthly newsletter and many more.

So in this tutorial I am going to use a jQuery plugin called Verbose Calendar in combination with Custom Post Types to create a simple event calendar. Verbose Calendar plugin is developed by John Patrick Given and available for download on GitHub.


What Are We Developing Today

Verbose Event Calendar plugin will allow site owners to create events on the admin dashboard and display to users using a calendar.

  • Creating a Custom Post Type for events and adding events.
  • Create a shortcode to display jQuery Verbose Calendar.
  • Add events to the verbose calendar using AJAX.

Step 1 Creating the Plugin File

Initially we have to create the plugin folder in the /wp-content/plugins directory. I am going to create a folder called verbose-event-calendar as the plugin folder and create the index.php file with the necessary information about the plugin. Once created you can go to the plugins menu of the admin panel and activate the plugin using the activate link.

<?php
/*
  Plugin Name:  Verbose Event Calendar
  Version: 1.0
  Plugin URI: http://wp.tutsplus.com/tutorials/wordpress-event-calendar-using-custom-post-types-and-verbose-calendar
  Description: Custom post type for creating events and display visually in a calendar control.
  Author URI: http://www.innovativephp.com
  Author: Rakhitha Nimesh
  License: GPL2
 */
?>

Step 2 Understanding the Folder Structure

Since I am going to use external jQuery plugins for this tutorial, it's important to have a good understanding about the folder and files structure. The following screen will show you a graphical view of the file structure of this plugin.

Folder Structure
  • images - contains all the images used for the WordPress plugin.
  • javascripts - JavaScript files for the jQuery Verbose Calendar plugin.
  • stylesheets - CSS files for the jQuery Verbose Calendar plugin.
  • themes - CSS files for jQuery Date Picker.
  • ui - JavaScript files for jQuery Date Picker.
  • verboseCalAdmin.js - custom JavaScript code related to the admin section.
  • verboseCalCustom.js - custom JavaScript code related to front end.
  • styles.css - custom styles for the plugin.
  • index.php - PHP code of the plugin.
  • README.md - README file for jQuery Verbose Calendar.

Step 3 Enqueue JavaScript and CSS Files

We need separate JavaScript and CSS files for the front end and admin sections. The following code will show you how to include them properly using the wp_enqueue_scripts action.

Include the JavaScript and CSS files to display the jQuery Verbose Calendar:

<?php
function verbose_calendar_scripts() {
	global $post;

	wp_enqueue_script('jquery');

	wp_register_style('verboseCalCustomStyles', plugins_url('styles.css', __FILE__));
	wp_enqueue_style('verboseCalCustomStyles');

	wp_register_script('verboseCal', plugins_url('javascripts/jquery.calendar/jquery.calendar.min.js', __FILE__));
	wp_enqueue_script('verboseCal');

	wp_register_style('verboseCalMainStyles', plugins_url('stylesheets/main.css', __FILE__));
	wp_enqueue_style('verboseCalMainStyles');

	wp_register_style('verboseCalStyles', plugins_url('javascripts/jquery.calendar/calendar.css', __FILE__));
	wp_enqueue_style('verboseCalStyles');

	wp_register_script('verboseCalCustom', plugins_url('verboseCalCustom.js', __FILE__));
	wp_enqueue_script('verboseCalCustom');

	$config_array = array(
		'ajaxUrl' => admin_url('admin-ajax.php'),
		'nonce' => wp_create_nonce('reward-nonce')
	);

	wp_localize_script('verboseCal', 'calendarData', $config_array);
}

add_action('wp_enqueue_scripts', 'verbose_calendar_scripts');
?>

Include the JavaScript and CSS files for the admin interface:

<?php
function verbose_calendar_admin_scripts() {
	global $post;

	wp_enqueue_script('jquery');

	wp_register_style('verboseCalCustomStyles', plugins_url('styles.css', __FILE__));
	wp_enqueue_style('verboseCalCustomStyles');

	wp_register_style('jqueryUIALL', plugins_url('themes/base/jquery.ui.all.css', __FILE__));
	wp_enqueue_style('jqueryUIALL');

	wp_register_script('jqueryUICore', plugins_url('ui/jquery.ui.core.js', __FILE__));
	wp_enqueue_script('jqueryUICore');

	wp_register_script('jqueryUIWidget', plugins_url('ui/jquery.ui.widget.js', __FILE__));
	wp_enqueue_script('jqueryUIWidget');

	wp_register_script('jqueryUIDate', plugins_url('ui/jquery.ui.datepicker.js', __FILE__));
	wp_enqueue_script('jqueryUIDate');

	wp_register_script('verboseCalAdmin', plugins_url('verboseCalAdmin.js', __FILE__));
	wp_enqueue_script('verboseCalAdmin');
}

add_action('admin_enqueue_scripts', 'verbose_calendar_admin_scripts');
?>

Step 4 Register Custom Post Type for Events

Events can have many details like date, location, participants, etc. I am going to create a Custom Post Type called event considering the flexibility and extendability of the plugin. The first task would be to register a Custom Post Type with WordPress. Let's go through the code for registering Custom Post Types.

<?php
function register_custom_event_type() {
	$labels = array(
		'name' => _x('Events', 'event'),
		'singular_name' => _x('Event', 'event'),
		'add_new' => _x('Add New', 'event'),
		'add_new_item' => _x('Add New Event', 'event'),
		'edit_item' => _x('Edit Event', 'event'),
		'new_item' => _x('New Event', 'event'),
		'view_item' => _x('View Event', 'event'),
		'search_items' => _x('Search Events', 'event'),
		'not_found' => _x('No events found', 'event'),
		'not_found_in_trash' => _x('No events found in Trash', 'event'),
		'parent_item_colon' => _x('Parent Event:', 'event'),
		'menu_name' => _x('Events', 'event'),
	);
	$args = array(
		'labels' => $labels,
		'hierarchical' => false,
		'supports' => array('title', 'editor'),
		'public' => true,
		'show_ui' => true,
		'show_in_menu' => true,
		'show_in_nav_menus' => true,
		'publicly_queryable' => true,
		'exclude_from_search' => false,
		'has_archive' => true,
		'query_var' => true,
		'can_export' => true,
		'rewrite' => true,
		'capability_type' => 'post'
	);
	register_post_type('event', $args);
}
add_action('init', 'register_custom_event_type');
?>
  • First I am going to make a function call on the init action to register a Custom Post Type. register_custom_event_type will be called.
  • Inside the function we have to define labels for the Custom Post Type. These variables defined in the $labels array will be used as the labels for the Event creation form and saving post types in the database. You can find more information about Custom Post Type labels under register_post_type in the WordPress Codex
  • Then we have to define a list of arguments to be passed into the register_post_type function. I am going to use default values for most of the arguments as shown in the above code.
  • I have done modifications to the supports argument. This is used to define the available form fields and components inside the event creation screen. In this plugin we need only Event Title and Description. So I have removed the other values like thumbnail, custom-fields, excerpt, etc.
  • Finally we pass the arguments for the register_post_type function with the name of our Custom Post Type as event.

"It's important that you keep your Custom Post Type name under 20 characters and with no capital letters or spaces."

Now you will be able to see a menu item called Events in the left menu of the admin panel before the Appearance menu. Once you click on it you will be redirected to an event creation screen with Event Title and Description fields. Next I'll show you how to add event specific fields to the form.


Step 5 Create Custom Events Fields

Events can have much information as I mentioned earlier. You have the ability to create your own fields according to the type of events you are using in the website. So I am going to create fields for starting date and ending date, which are common to any event type and let you create your own fields by modifying code. We can create our own fields inside meta boxes as well as default custom fields to store additional information. Since we have already removed custom fields from the events screen, I am going to use fields inside meta boxes to create starting and ending dates.

Add Event Info Meta Box

First we have to add a meta box as the container of our custom fields using the code below.

<?php
add_action('add_meta_boxes', 'add_events_fields_box');

function add_events_fields_box() {
	add_meta_box('events_fields_box_id', 'Event Info', 'display_event_info_box', 'event');
}
?>

I have used the required parameters for the add_meta_box function in the above code. You have to pass a unique ID, meta box display title, function name for generating custom fields, and event type as the required parameters. You can find more details about the optional parameters under add_meta_box in the WordPress Codex.

Add Event Fields

We are going to add 2 fields for start date and end date of the event inside the meta box created earlier. HTML code for the form fields are generated inside the display_event_info_box function.

<?php
function display_event_info_box() {
	global $post;

	$values = get_post_custom($post->ID);
	$eve_start_date = isset($values['_eve_sta_date']) ? esc_attr($values['_eve_sta_date'][0]) : '';
	$eve_end_date = isset($values['_eve_end_date']) ? esc_attr($values['_eve_end_date'][0]) : '';

	wp_nonce_field('event_frm_nonce', 'event_frm_nonce');

	$html = "<label>Event Start Date</label><input id='datepickerStart' type='text' name='datepickerStart' value='$eve_start_date' />
		<label>Event End Date</label><input id='datepickerEnd' type='text' name='datepickerEnd' value='$eve_end_date' />";
	echo $html;
}
?>
  • This HTML code will be used in both Add Event and Edit Event screens. So initially we have to get the current values of the fields using the get_post_custom function.
  • Next we create the nonce value to be checked in the event saving process.
  • Then we output the HTML code for the 2 text boxes for start and end date. If a value already exists, we assign it to the value attribute of input fields.

"I have named my meta keys for start and end event as _eve_sta_date and _eve_end_date. When you use '_' in front of meta keys, it will not be shown as a custom field. Otherwise we will get duplicate fields one inside the meta box and one with custom fields. Always make sure to prefix your meta keys with '_' in case you don't want it to be shown as a custom field."

Add Date Picker to Event Fields

Even though we created 2 text fields for start and end dates, it will be used to insert actual dates. So I am going to assign the jQuery Date Picker to text fields in order to allow users to select dates from a calendar without entering manually. We need to add some JavaScript code to the verboseCalAdmin.js file to assign date pickers to input fields.

<script>
$jq =jQuery.noConflict();

$jq(document).ready(function() {
	$jq("#datepickerStart").datepicker();
	$jq("#datepickerEnd").datepicker();
});
</script>

Step 6 Validating Event Creation

We need to do some validation before saving events to database. So I am going to use jQuery validation on the event creation form as shown below. The below code is included inside the verboseCalAdmin.js file.

<script>
	$jq('#post').submit(function() {

		$jq('.ver_cal_err').remove();

		if($jq("#post_type").val() =='event') {
			var err = 0;
			if($jq("#title").val() == '') {
				$jq("#title").after("<div class='ver_cal_err'>Event Title cannot be empty</div>");
				err++;
			}
			if($jq("#datepickerStart").val() == '' || $jq("#datepickerEnd").val() == '') {
				$jq("#datepickerEnd").after("<div class='ver_cal_err'>Start Date and End Date is required</div>");
				err++;
			}

			var start = $jq('#datepickerStart').datepicker('getDate');
			var end = $jq('#datepickerEnd').datepicker('getDate');
			var days = (end - start)/1000/60/60/24;
			if(days<0) {
				$jq("#datepickerEnd").after("<div class='ver_cal_err'>End Date should be greater than Start Date.</div>");
				err++;
			}

			if(err>0) {
				return false;
			}
			else {
				return true;
			}
		}
	});
</script>
  • The jQuery function will be called on form submission using the form ID post.
  • There are a set of hidden fields in the post creation screen. I am using the value of the post_type hidden field to check the post type.
  • Title and Dates are required. So those fields are validated using jQuery.
  • Then we add custom error boxes to be displayed under each field.
  • If the form does not validate properly, errors will be displayed and event creation will be stopped until all the validation errors are fixed.

Step 7 Saving Events to Database

Once all the validation errors are corrected and the form is submitted successfully, we call the save_event_information function on the save_post action as shown in the following code.

<?php
add_action('save_post', 'save_event_information');

function save_event_information($post_id) {
	// Bail if we're doing an auto save
	if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
		return;

	// if our nonce isn't there, or we can't verify it, bail
	if (!isset($_POST['event_frm_nonce']) || !wp_verify_nonce($_POST['event_frm_nonce'], 'event_frm_nonce'))
		return;

	// if our current user can't edit this post, bail
	if (!current_user_can('edit_post'))
		return;

	if (isset($_POST['datepickerStart']))
		update_post_meta($post_id, '_eve_sta_date', esc_attr($_POST['datepickerStart']));
	if (isset($_POST['datepickerEnd']))
		update_post_meta($post_id, '_eve_end_date', esc_attr($_POST['datepickerEnd']));
}
?>
  • This is the standard method of saving custom post meta to database. First we check if the function is called within WordPress autosave and return from the script.
  • Next we validate the nonce value generated in the form and nonce value submitted through $_POST.
  • Then we have to check whether the user has necessary permission to create events using the current_user_can('edit_post') function.
  • Finally we save both start date and end date to the wp_postmeta table in the database.

Now we have completed the process of creating events through the admin panel. Next we have to work on including the jQuery Verbose Calendar in the front end and display the events and posts to the users.


Step 8 Creating Shortcode for Verbose Calendar

First we need to create a shortcode which displays the Verbose Calendar on the page. Once you include the following code you can create a new page in the admin panel add the shortcode as [verbose_calendar/] to the editor to display the calendar on post.

<?php
function verbose_calendar() {
	global $post;

	return "<div id='main-container'></div><div id='popup_events'>
		<div class='pop_cls'></div>
		<div id='popup_events_list'>
			<div id='popup_events_head'></div>
			<div id='popup_events_bar'></div>
			<div id='event_row_panel' class='event_row_panel'></div>
			<div id='popup_events_bottom'></div>
		</div>
	</div>";
}

add_shortcode("verbose_calendar", "verbose_calendar");
?>

The above shortcode code inserts HTML elements needed for the calendar. We have to load the calendar using jQuery as shown below. The following code is included inside the verboseCalCustom.js file.

<script>
$jq =jQuery.noConflict();

$jq(document).ready(function() {

	$jq("#main-container").calendar({
		tipsy_gravity: 's', // How do you want to anchor the tipsy notification? (n / s / e / w)
		post_dates : ["1","2"],
		click_callback: calendar_callback, // Callback to return the clicked date
		year: "2012", // Optional, defaults to current year - pass in a year - Integer or String
		scroll_to_date: false // Scroll to the current date?
	});

	$jq(".pop_cls").on("click",function() {
		$jq("#popup_events").fadeOut("slow");
	});
});
</script>

If everything is done properly, you should have a calendar like the following image in the page you created.


Step 9 Assigning Events to Calendar

Next task of this tutorial is to query the events and posts from the database and display on the calendar. Default Verbose Calendar provides only the calendar layout. We have to customize the plugin in order to assign events to the calendar. Verbose Calendar uses a function called g.prototype.print to display the calendar. So we are going to customize this function to retrieve events and posts from the database as shown below. Code is located in the jquery.calendar.min.js file inside the javascripts folder.

<script>
g.prototype.print=function(c) {

	postDetailsArr = [];

	var verboseElement = this.element;
	$jq.post(calendarData.ajaxUrl, {
		action:"get_posts_for_year",
		nonce:calendarData.nonce,
		currentYear:e.options.year
	}, function(result, textStatus) {

		$jq.each(result, function(index, data) {

			if(data.type == 'event') {
				if(postDetailsArr[data.startDate]) {
					postDetailsArr[data.startDate].push(data);
				}
				else {
					postDetailsArr[data.startDate] = [];
					postDetailsArr[data.startDate].push(data);
				}
				postArr.push(data.startDate);
			}
			else {
				if(postDetailsArr[data.post_date]) {
					postDetailsArr[data.post_date].push(data);
				}
				else {
					postDetailsArr[data.post_date] = [];
					postDetailsArr[data.post_date].push(data);
				}
				postArr.push(data.post_date);
			}

		});

	}, "json");

};
</script>
  • I'll explain the codes we have changed and important from the plugin's perspective. Plugin specific code is omitted here.
  • First I have placed the whole code inside a AJAX request. This request will be made each time you change the year or load the calendar.
  • AJAX request will be made to the get_posts_for_year action. It will return list of posts and events for the current year displayed on the top of calendar.
  • Then we loop through each result using $jq.each method. We pass the details of result to the postDetailsArr array.
  • We use the start date for events and published date for posts and assigns to postArr array.
<script>
	var mon = ((parseInt(b)+1) < 9) ? "0"+(parseInt(b)+1) : (parseInt(b)+1);
	daynum = (j<9)?"0"+j:j;

	var searchedIndex = ($jq.inArray((mon+"-"+daynum+"-"+d), postArr));
	if(searchedIndex != -1) {
		g.append("<div data-date='"+(parseInt(b)+1)+"/"+j+"/"+d+"' class='label dat-"+(parseInt(b)+1)+"-"+j+"-"+d+" day "+h+" event_highlight'>"+j+"</div>");
	}
	else {
		g.append("<div data-date='"+(parseInt(b)+1)+"/"+j+"/"+d+"' class='label dat-"+(parseInt(b)+1)+"-"+j+"-"+d+" day "+h+"'>"+j+"</div>");
	}
</script>
  • The above code snippet is also located inside the print function.
  • We prepare the date and month by adding 0 as the prefix in case it is less than 10.
  • Then we check each date exists inside the postArr using the inArray function.
  • If date contains an event or post, we add a special CSS class called event_highlight.

Generating Post and Event Lists

Now we have to query the database and generate results for the AJAX request. Consider the following code for the request generation process.

<?php
add_action('wp_ajax_nopriv_get_posts_for_year', 'get_posts_for_year');
add_action('wp_ajax_get_posts_for_year', 'get_posts_for_year');

function get_posts_for_year() {
	global $post, $wpdb;

	$allEvents = array();

	$sql = "SELECT $wpdb->posts.guid,$wpdb->posts.post_title,DATE_FORMAT(post_date, '%m-%d-%Y') as post_date  FROM $wpdb->posts WHERE Year($wpdb->posts.post_date)='" . $_POST['currentYear'] . "' and post_status='publish' and post_type='post' ";

	$allPosts = array();
	$yearlyPosts = $wpdb->get_results($sql, ARRAY_A);
	foreach ($yearlyPosts as $key => $singlePost) {
		$singlePost['type'] = 'post';

		array_push($allEvents, $singlePost);
	}

	$sql = "SELECT $wpdb->posts.ID,$wpdb->posts.guid,$wpdb->posts.post_title,DATE_FORMAT(post_date, '%m-%d-%Y') as post_date  FROM $wpdb->posts inner join $wpdb->postmeta on $wpdb->posts.ID=$wpdb->postmeta.post_id WHERE $wpdb->postmeta.meta_key='_eve_sta_date' and Year(STR_TO_DATE($wpdb->postmeta.meta_value, '%m/%d/%Y'))='" . $_POST['currentYear'] . "' and post_status='publish' and post_type='event'";

	$yearlyEvents = $wpdb->get_results($sql, ARRAY_A);
	foreach ($yearlyEvents as $key => $singleEvent) {

		$startDate = str_replace("/", "-", get_post_meta($singleEvent['ID'], '_eve_sta_date'));
		$endDate = str_replace("/", "-", get_post_meta($singleEvent['ID'], '_eve_end_date'));

		$singleEvent['startDate'] = $startDate[0];
		$singleEvent['endDate'] = $endDate[0];
		$singleEvent['type'] = 'event';

		array_push($allEvents, $singleEvent);
	}
	echo json_encode($allEvents);
	exit;
}
?>
  • First we assign the action to both logged in users and normal users.
  • Then we get all the published posts for the selected year and assign it to the $allEvents array.
  • We assign the events for the selected year using same procedure as above and output in JSON format.

Step 10 Displaying Events List

Dates with events or posts will be highlighted in a blue color. Once you click on such a date, the events list should be displayed. Verbose Calendar has a function called calendar_callback which we are going to use in order to display the events list. The following JavaScript code inside the verboseCalCustom.js file will be used for this functionality.

<script>
var calendar_callback = function(date) {

	$jq("#event_row_panel").html("");

	date.month = (date.month < 10) ? "0"+date.month : date.month;
	date.day = (date.day < 10) ? "0"+date.day : date.day;
	var activeDate = date.month+"-"+date.day+"-"+date.year;

	$jq("#popup_events_head").html("Events for "+activeDate);

	var dailyEvents = postDetailsArr[activeDate];

	var eventHTML = "";

	$jq.each(dailyEvents, function(index, data) {

		if(data.type=='event') {
			eventHTML += "<div class='event_row'><div class='event_title'><a href='"+data.guid+"' >"+data.post_title+"</a></div><div class='event_dates'>Start Date : <span>"+data.startDate+"</span>  End Date : <span>"+data.endDate+"</div></div>";
		}
		else {
			eventHTML += "<div class='post_row'><div class='post_title'><a href='"+data.guid+"' >"+data.post_title+"</a></div><div class='post_dates'>Published Date : <span>"+data.post_date+"</span></div></div>";
		}

	});

	$jq("#event_row_panel").html(eventHTML);
	$jq("#popup_events").fadeIn("slow");
}
</script>
  • The clicked date will be passed automatically to this function. First we assign the date to a variable using the passed date object.
  • Next we get the events and posts for the selected date using postDetailsArr generated in the print function.
  • Then we assign event and post details by considering the type variable.
  • Finally we assign all the HTML to the event_row_panel container and display the events list.

Conclusion

Once the tutorial is completed you will have a cool event calendar plugin. This contains only basic fields of events. You can create more information about events by adding more custom fields. Feel free to extend the functionality of this plugin and share your suggestions.

Advertisement