Click here to Skip to main content
12,396,047 members (66,322 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

12.9K views
2 bookmarked
Posted

How to Use WordPress Custom Post Types to Add Events to Your Site

, 16 Apr 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
One of the more interesting (and useful) features in WordPress is custom post types. As the name suggests, WordPress allows you to define your own post types which are treated just like existing ones (i.e. Posts and Pages).

One of the more interesting (and useful) features in WordPress is custom post types. As the name suggests, WordPress allows you to define your own post types which are treated just like existing ones (i.e. Posts and Pages).

For instance, I was recently developing a site which needed to create and publish events. After digging around, I found an excellent article by Noel Tock on how to use custom post types to create events.

Even though Noel did a great job explaining the concept and laying out the approach, he did leave a couple of things out. So, I decided to write my own slightly more comprehensive guide to using custom post types to create events.

How Should Events Work?

Before diving into implementation details, let’s first talk about requirements. Let’s imagine that you’re building a site for someone who’d like to create and post events. What might that look like?

Well, first of all, there would be a new category of things in WordPress called Events:

Second, there would be an easy way to see what Events have been created (like Posts or Pages):

And of course there would be an easy way to add a new event and specify event details like date and location:

It would also be nice to be able to embed a list of events within a page or a post, perhaps using code that looks like this:

<h2>upcoming events</h2>
[events daterange="current"]

<h2>past events</h2>
[events daterange="past"]

Finally, it would be great to have a separate template to display individual events.

Ok, But How Do We Do It?

In order to build comprehensive, user-friendly functionality for managing events, we’ll need to implement the following:

  1. Register a new custom post type called Events
  2. Add support for displaying Event lists
  3. Add support for adding and editing individual Events
  4. Add a shortcode to include Event lists in posts and pages
  5. Add a special page which can display an individual Event

Let’s discuss each of these in detail.

1. Registering Events Post Type

The first thing we’ll need to do is to register a new post type of Event with WordPress. To do that, we’ll need to modify functions.php file and add the following code:

add_action('init', 'event_register');

function event_register() {

	$labels = array(
		'name' => _x('Events', 'post type general name'),
		'singular_name' => _x('Event', 'post type singular name'),
		'add_new' => _x('Add New', 'event'),
		'add_new_item' => __('Add New Event'),
		'edit_item' => __('Edit Event'),
		'new_item' => __('New Event'),
		'view_item' => __('View Event'),
		'search_items' => __('Search Events'),
		'not_found' =>  __('Nothing found'),
		'not_found_in_trash' => __('Nothing found in Trash'),
		'parent_item_colon' => ''
	);

	$args = array(
		'labels' => $labels,
		'public' => true,
		'publicly_queryable' => true,
		'show_ui' => true,
		'query_var' => true,
		'rewrite' => true,
		'capability_type' => 'post',
		'hierarchical' => false,
		'menu_position' => null,
		'supports' => array('title','editor','thumbnail')
	  );

	register_post_type( 'events' , $args );
}

The $labels array tells WordPress what various new actions surrounding Events should be called, while the $args array specifies how Events should be handled. For the sake of brevity, I won’t to go into details about the parameters you need to specify (you can find that here).

2. Displaying Event Lists

To display a list of events to the user, we’ll need to do the following:

For my events list, I wanted to show the event date along with start and end times, location, and city. Here’s the code to do this:

add_action("manage_posts_custom_column",  "events_custom_columns");
add_filter("manage_events_posts_columns", "events_edit_columns");

function events_edit_columns($columns){
    $columns = array(
        "cb" => "<input type=\"checkbox\" />",
        "title" => "Event",
        "event_date" => "Event Date",
        "event_location" => "Location",
        "event_city" => "City",
  );
  return $columns;
}

function events_custom_columns($column){
    global $post;
    $custom = get_post_custom();

    switch ($column) {
    case "event_date":
            echo format_date($custom["event_date"][0]) . '<br /><em>' .
            $custom["event_start_time"][0] . ' - ' .
            $custom["event_end_time"][0] . '</em>';
            break;

    case "event_location":
            echo $custom["event_location"][0];
            break;

	case "event_city":
            echo $custom["event_city"][0];
            break;
    }
}

The code is fairly straight forward. The first function assigns labels and names to each of the columns I want to display, while the second function formats the content of each column.

Adding Sortable Columns

To make the list even nicer, we could make columns sortable. In this case, being able to sort on the event date is a very helpful feature. To do that, we need to:

  • add a filter to indicate which column is sortable
  • define how the sort should happen
Here’s the code:
add_filter("manage_edit-events_sortable_columns", "event_date_column_register_sortable");
add_filter("request", "event_date_column_orderby" );

function event_date_column_register_sortable( $columns ) {
        $columns['event_date'] = 'event_date';
        return $columns;
}

function event_date_column_orderby( $vars ) {
    if ( isset( $vars['orderby'] ) && 'event_date' == $vars['orderby'] ) {
        $vars = array_merge( $vars, array(
            'meta_key' => 'event_date',
            'orderby' => 'meta_value_num'
        ) );
    }
    return $vars;
}

3. Editing Events

Ok, the next thing we need to add is the ability to edit events. More specifically, we need to add custom properties to the normal post edit screen so that the user can input additional event details (like date and location). Here’s what we’ll need:

  • add Event Details meta box to the Post edit screen
  • add action to save event details

The code to add the Event Details meta box looks like this:

add_action("admin_init", "events_admin_init");

function events_admin_init(){
  add_meta_box("event_meta", "Event Details", "event_details_meta", "events", "normal", "default");
}

function event_details_meta() {

	$ret = '<p><label>Date: </label><input type="text" name="event_date" value="' . format_date(get_event_field("event_date")) . '" /><em>(mm/dd/yyy)</em>';
	$ret = $ret . '</p><p><label>Start Time: </label><input type="text" name="event_start_time" value="' . get_event_field("event_start_time") . '" /><em>(hh:mm pm)</em></p>';
	$ret = $ret . '<p><label>End Time: </label><input type="text" name="event_end_time" value="' . get_event_field("event_end_time") . '" />	<em>(hh:mm pm)</em> </p>';
	$ret = $ret . '<p><label>Location: </label><input type="text" size="70" name="event_location" value="' . get_event_field("event_location") . '" /></p>';
	$ret = $ret . '<p><label>Street: </label><input type="text" size="50" name="event_street" value="' . get_event_field("event_street") . '" /></p>';
	$ret = $ret . '<p><label>City: </label><input type="text" size="50" name="event_city" value="' . get_event_field("event_city") . '" /></p>';
	$ret = $ret . '<p><label>Location URL: </label><input type="text" size="70" name="event_location_url" value="' . get_event_field("event_location_url") . '" /></p>';
	$ret = $ret . '<p><label>Register URL: </label><input type="text" size="70" name="event_register_url" value="' . get_event_field("event_register_url") . '" /></p>';

	echo $ret;
}

The admin_init action tells WordPress to also call events_admin_init() whenever an event is added or edited. That, in turn, adds a meta box for Event Details via the add_meta_box() call. Finally, the actual Event Details box is displayed using event_details_meta() function.

To make the code a bit more readable, I’ve defined a get_event_field() function that returns the value of an event field if one exists:

function get_event_field($event_field) {
    global $post;

    $custom = get_post_custom($post->ID);

    if (isset($custom[$event_field])) {
        return $custom[$event_field][0];
    }
}

Saving Event Details

We also need to add code to save event details entered by the user. This is accomplished by adding an action for save_post:

add_action('save_post', 'save_event_details');

function save_event_details(){
   global $post;

   if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
      return;

   if ( get_post_type($post) == 'event')
      return;

   if(isset($_POST["event_date"])) {
      update_post_meta($post->ID, "event_date", strtotime($_POST["event_date"]));
   }

   save_event_field("event_start_time");
   save_event_field("event_end_time");
   save_event_field("event_location");
   save_event_field("event_street");
   save_event_field("event_city");
   save_event_field("event_location_url");
   save_event_field("event_register_url");
}

The save function is not complicated, but it does a couple of very important things. First, it makes sure that WordPress’ autosave feature doesn’t lose custom fields. Second, it ensures that this save won’t run for non-event post types (i.e. normal posts and pages).

By the way, note that I convert the event_date field into a Unix timestamp before saving it (this was one of the great tips from Noel’s post). Doing this makes it much easier to work with dates later, especially for things like sorting.

Finally, I wrote a small helper function to make saving fields easier:

function save_event_field($event_field) {
    global $post;

    if(isset($_POST[$event_field])) {
        update_post_meta($post->ID, $event_field, $_POST[$event_field]);
    }
}

4. Displaying Events

The simplest way to allow lists of events to be easily included in pages or posts is by defining a shortcode. And because shortcodes can be parametrized, you can use the same one to return both upcoming events as well as past ones. Here’s the code to do it:

add_shortcode( 'events', 'get_events_shortcode' );

function get_events_shortcode($atts){
    global $post;

    // get shortcode parameter for daterange (can be "current" or "past")
    extract( shortcode_atts( array(
        'daterange' => 'current',
    ), $atts ) );

    ob_start();

    // prepare to get a list of events sorted by the event date
    $args = array(
        'post_type'	=> 'events',
        'orderby'  	=> 'event_date',
        'meta_key'	=> 'event_date',
        'order'    	=> 'ASC'
    );

    query_posts( $args );

    $events_found = false;

    // build up the HTML from the retrieved list of events
    if ( have_posts() ) {
        while ( have_posts() ) {
            the_post();
            $event_date = get_post_meta($post->ID, 'event_date', true);

            switch ($daterange) {
                case "current":
                    if ($event_date >= time() ) {
                        echo get_event_container();
                        $events_found = true;
                    }
                    break;

                case "past":
                    if ($event_date < time() ) {
                        echo get_past_event_summary();
                        $events_found = true;
                    }
                    break;
            }
        }
    }

    wp_reset_query();

    if (!$events_found) {
        echo "<p>no events found.</p>";
    }

    $output_string = ob_get_contents();
    ob_end_clean();

    return $output_string;
}

There’s a whole bunch of stuff going on in this function and most of it is pretty ugly. A couple of things worth pointing out:

  • I’m using output buffering with ob_start() to capture the echoed output and display it in one shot (another great tip from Noel)
  • Because I store event_date as a unix timestamp, sorting events using the date field is as simple as specifying it as the order_by value

By the way, here are the helper functions get_event_container() and get_past_event_summary():

function get_event_container() {
    global $post;
    $ret = '<section class="event_container">';
    $ret =  $ret . get_event_calendar_icon() . '<section class="event_desc">';
    $ret = $ret .  get_event_details(false, true);
    $ret =  $ret . '</section></section>';

    return $ret;
}

function get_past_event_summary() {
    global $post;
    $ret = '<section class="event_container past">';
    $ret =  $ret . '<h3>' . $post->post_title . '</h3><p>';
    $ret = $ret . '<h4>' . get_post_meta($post->ID, 'event_city', true) .'</h4>';
    $ret = $ret .  '<em>' . format_date(get_post_meta($post->ID, 'event_date', true)) . '</em>';
    $ret =  $ret . '</p></section>';

    return $ret;
}

5. Displaying Individual Events

Finally, to display an individual events you can define a special template called single-events.php. Here’s what it might look like:

<?php get_header(); ?>

<?php if (have_posts()) : while (have_posts()) : the_post(); ?>

    <article id="event-<?php the_ID(); ?>">
        <h1><?php the_title(); ?></h1>
        <section class="event_container">
            <?php echo get_event_calendar_icon(); ?>
            <section class="event_desc">
                <?php the_content('Read more on "'.the_title('', '', false).'" &raquo;'); ?>
                <?php echo get_event_details(true, false); ?>
             </section>
          </section>
     </article>
<?php endwhile; endif; ?>

<?php get_footer(); ?>

After going into the standard WordPress loop, I construct an article which contains the name of the event as the header, a small chunk of HTML to display the date, and some additional information about the event.

Just to complete the picture, here are the helper functions get_event_calendar_icon() and get_event_details():

function get_event_calendar_icon() {
    global $post;
    $unixtime = get_post_meta($post->ID, 'event_date', true);
    $month = date("M", $unixtime);
    $day = date("d", $unixtime);
    $year = date("y", $unixtime);
    return 	'<div class="calendar">' . $day . '<em>' . $month . '</em></div>';
}

function get_event_details($include_register_button, $include_title) {
    global $post;
    $unixtime = get_post_meta($post->ID, 'event_date', true);
    $location_url = get_post_meta($post->ID, 'event_location_url', true);
    $register_url = get_post_meta($post->ID, 'event_register_url', true);

    $ret = '';
    if ($include_title) {
        $ret =  $ret . '<h3><a href="' . get_permalink() . '">' . $post->post_title . '</a></h3>';
    }

    $ret = $ret . '<p><h4>'.get_post_meta($post->ID, 'event_location', true) . '</h4>';
    $ret = $ret . format_possibly_missing_metadata('event_street');
    $ret = $ret . format_possibly_missing_metadata('event_city');
    $ret = $ret . '</p><p>';
    $ret = $ret . date("F d, Y", $unixtime) . '<br/>';
    $ret = $ret . '<em>' . get_post_meta($post->ID, 'event_start_time', true) . ' - ';
    $ret = $ret . get_post_meta($post->ID, 'event_end_time', true) . '</em>';

    if (!empty($register_url) && $include_register_button && $unixtime > time()) {
        $ret = $ret .'<a class="event_register" href="' . $register_url . '">register</a>';
    }

    return $ret;
}

function format_possibly_missing_metadata($field_name) {
    global $post;
    $field_value = get_post_meta($post->ID, $field_name, true);

    if (!empty($field_value)) {
        return $field_value.'<br/>';
    }
}

Without going into too much detail, each function simply returns a chunk of HTML containing metadata about the events. In case you’re wondering how to style HTML with CSS3 to look like a calendar icon, here’s a great tutorial from CSS Globe.

Final Thought

Although it’s quite a bit of work to get this done, the end result works well. To see the finished product in action, you can check out this page.

You may also like:


License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

AlexTatiyants
United States United States
No Biography provided

You may also be interested in...

Comments and Discussions

 
QuestionNot working Pin
Delano Myers15-Sep-15 4:20
memberDelano Myers15-Sep-15 4:20 
QuestionCan you help? Pin
marc f22-Apr-12 4:54
membermarc f22-Apr-12 4:54 
AnswerRe: Can you help? Pin
AlexTatiyants22-Apr-12 8:24
memberAlexTatiyants22-Apr-12 8:24 
GeneralHow to sync events with default Calendar on wordpress? Pin
ryezack7-Feb-13 5:32
memberryezack7-Feb-13 5:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160721.1 | Last Updated 16 Apr 2012
Article Copyright 2012 by AlexTatiyants
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid