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;
    }
}

function format_date($unixtime) {
    return date("F", $unixtime)." ".date("d", $unixtime).", ".date("Y", $unixtime);
}

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:

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:

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) != 'events')
      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:

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:

Did you love / hate / were unmoved by this post?
Then show your support / disgust / indifference by following me on Twitter!

This post got 90 comments so far. Care to add yours?

  1. Jonathan says:

    Hello

    I can´t seem to get this to work.
    I was wondering where the code for “4. Displaying Events” should be?

    In functions.php or in the events.php where I’m listing my events?

    • Alex Tatiyants says:

      Hi Jonathan, all the php code I’ve listed should either be in functions.php or a separate file (like events.php) that’s referenced in functions.php. There is some information here on how to do that. Hope this helps!

      • Webtechideas says:

        Hi Alex,
        Thanks for this great post. Its really helpful. But this is going to help someone who is having access to theme files like functions.php. What if as a developer you are developing this events plugin and sharing the plugin with others. In that case you can’t have access to their theme files. So is there any way to have the events listings and details page generated with in the plugin? To be precise how can template files be added inside plugin? Thanks in advance.

  2. Caswell says:

    Great work!! 🙂

    But I keep getting the following error message at the bottom:

    Call to undefined function format_date() ….. from the line:

    …..$ret = ‘Date: ……

    • Alex Tatiyants says:

      Hi Caswell, sorry about that, I left out the format_date() function:

      function format_date($unixtime) {
      return date(“F”, $unixtime).” “.date(“d”, $unixtime).”, “.date(“Y”, $unixtime);
      }

    • Laurel says:

      So… I am somewhat new to building templates and I have been reading over how you’ve built this post type, but I don’t quite understand where to put the function (format_date)? Could you help me out with that?

      • Alex Tatiyants says:

        Hi Laurel, thank you very much for reading. format_date() is a function I defined to display a date in a nicer format (like “June 16, 2012”. Here’s the code:

        function format_date($unixtime) {
        return date(“F”, $unixtime).” “.date(“d”, $unixtime).”, “.date(“Y”, $unixtime);
        }

        You can put it anywhere in functions.php

  3. kevmatic says:

    Thank you! This is an excellent tutorial for us beginning wp developers. Is it possible to pass a parameter into the shortcode to only show the first two or three upcoming events?

    • Alex Tatiyants says:

      Hi Kevin, thank you very much, glad you found it useful. To answer your question, you can pass additional parameters (like max_items) to get_events_shortcode() via $atts:

      extract( shortcode_atts( array(
      ‘daterange’ => ‘current’,
      ‘max_items’ => ‘5’
      ), $atts ) );

      Then, when constructing the $args array for the call to query_posts() method, add a property called posts_per_page which lets you limit how many items come back from the query:

      $args = array(
      ‘post_type’ => ‘events’,
      ‘orderby’ => ‘event_date’,
      ‘meta_key’ => ‘event_date’,
      ‘order’ => ‘ASC’,
      ‘posts_per_page’ => $max_items
      );

      You can read more about post parameters to query_posts() here :http://codex.wordpress.org/Class_Reference/WP_Query#Parameters

      p.s. please note that I didn’t debug the code snippets above, but the gist should work.

      • kevmatic says:

        Wow, that’s amazing, thanks. I’ve got your site bookmarked & look forward to your future columns!

  4. Christopher says:

    This is wonderful, thank you kindly! I am currently putting together a site that features events all over the US and this will be quite helpful.
    Any idea how I would allow the location of the event to be searchable for the site visitor?

  5. Brightweb says:

    Nice tutorial! How did you used the introduction text in events page (…I’m passionate about what I do and love being able to help families…)?

    I saw that you use a template page…but how you don’t have URL conflict between page x CPT? Im trying to do this..

    Thanks!

    • Alex Tatiyants says:

      Thank you very much Brightweb, glad you enjoyed it. The answer to your question is shortcodes. Once you create a shortcode, you can simply include it in any HTML page like so:


      ... some text or html tags...

      [events daterange="current"]

      [events daterange="past"]

      ... some other text or html tags

      In my case, I simply created a separate page for events and included the small intro paragraph before using the short codes.

      Hope this helps!

  6. Josh says:

    Hi Alex, this tutorial looks like just what I’m after! Although when I copy all the code into my functions.php file and try to add an Event, the “Event Details” meta box will not expand. It seems like something strange is happening with step 3. I would really appreciate if you could help me out! 🙂 Thanks, Josh

    • Alex Tatiyants says:

      Hi Josh, thanks for reading. It’s really tough for me to help you figure out your issue without seeing the code, but I do have a recommendation. To debug this, I’d start by removing everything from functions.php except for the call to add_action() and functions to define the event details meta (events_admin_init(), event_details_data()). Also, make event_details_meta() really simple:

      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() {
      echo '

      hi

      ';
      }

      Hopefully, this will give you a hint as to what’s going on. Good luck!

  7. Josh says:

    Hi again Alex, I’ve tried the troubleshooting with the method you suggested and it will echo ‘hi’ just fine, although when I try even put the first line of “$ret = …” and then ‘echo $ret’ it doesn’t show display anything. Very confused! 🙁 I’ve tried it using a fresh install of wordpress using the twenty eleven theme and still no luck!

    Any help would be really appreciated! Thanks:)

    • Alex Tatiyants says:

      It’s very odd that setting a variable to a value and then echoing it out doesn’t work. The only other thing I can recommend since “echo” works is to use capturing buffered output:


      ob_start();
      echo "some text";
      echo "some more text";
      $output_string = ob_get_contents();
      ob_end_clean();
      return $output_string;

      Hope this helps, thanks

    • Issan says:

      Hi, I have the same problem. It works just fine until “$ret = …” and then ‘echo $ret’. Did you get it to work?

  8. Alex says:

    Hey there Great tutorial just a 2 quick question.

    1.How can I retrieve individual values(Like url, or date separately) instead of calling the entire function?

    2. For some odd reason the register url and location url are not showing on the single page but everything else does. Any tips for fixing this error?

    • Alex Tatiyants says:

      Hi Alex, to answer your questions:

      1. You could probably use get_post(). That returns a post object which you could use to get a specific value: $post->event_location_url
      2. This is probably because I’m not including Location URL in the output of get_event_details() function. You could add something like this:
        $ret = $ret .'<a href="' . $location_url . '">location</a>

  9. Jhon says:

    Hey Alex !

    Thank you for this amazing tutorial. I really needed it ! But it doesn’t work for me ! Do you have an easy way to contact you about my problem? Cuz it’s a bit complicated to explain… Hope you have skype or something like this cuz i really need to achieve this !

    Many Thanks Alex,

    Jhon

  10. Gwilym says:

    Hi – great tutorial. I’m keen to have a second custom post type “venues” which is referenced in “events” that way I can list events by venue and have a write up for the venue includng a map. Any idea on how to create a one to many relationship between two post types and have a select box on the “events” edit page?

    Thanks !

  11. Inao says:

    Gracias, buen tutorial!

  12. Jenny Lynne says:

    Thank you so much for this article. Last year when I was trying to understand custom post types, everyone just kept pointing me toward posts that made huge assumptions of what the readers understood. It’s refreshing to have someone explain everything step by step with examples. I am bookmarking this because maybe this time I will actually be able to accomplish what I was trying to do last year. Thank you!

    J.

    • Alex Tatiyants says:

      Thanks Jenny, very glad you found it useful. I had a very similar experience to yours and that motivated me to put together this tutorial.

  13. Amy says:

    I have the same issue as the users above with the meta box, it will echo any text but as soon as I reference your event date etc. input boxes, it just doesn’t display anything at all..

    Is there any solution to this at all?

    There is nothing else in my functions file except your code.

  14. Amy says:

    I actually realised the problem, it is because you didn’t give us the format date function in the post so the whole thing breaks from the first field trying to find the format date function which was not in the code. However when I use your format date function, it breaks the entire website, do I have to place it somewhere in particular. I am placing it at the top of my functions file.

    • Alex Tatiyants says:

      Hi Amy, the format function should be somewhere in functions.php file. When you say it breaks the entire website, what error are you seeing?

      • Benno says:

        Hi Alex,

        I’ve got the same problem. This:

        function format_date($unixtime) {
        return date(“F”, $unixtime).” “.date(“d”, $unixtime).”, “.date(“Y”, $unixtime);
        }

        gives a syntax error in the 2nd line. If I ignore this warning of my php program and upload it anyway then wordpress is broken, it only shows this: Parse error: syntax error, unexpected T_STRING in —mysite—/wp-content/themes/art-portfolio/expo-functions.php on line 98

        I’m no code-expert, but is it possible it can’t get $unixtime or so? Since this code seems to work for everybody exept Amy and myself I guess there’s no real syntax error but another problem. I’m guessing $unixtime gets the current time from the database or so? Maybe that’s not working?

        • Benno says:

          Found the problem, thanks to stackoverflow.com (I myself am not so smart ;))
          The problem is I copied your comment here. Only that makes the quotes curly quotes instead of block-quotes. Maybe you can just add this function in your tutorial to prevent other people asking for this function and running into the same problem?

          Kind regards,
          Benno

  15. Mick says:

    Hi Alex,
    First of all, I would like to thank you for writing such a great tutorial. I was looking for the sortable date functionality and this article was a great resource. The only issue I am encountering right now is with ‘taxonomy’. It’s surprising that you haven’t touched taxonomy at all. I have a ‘types’ taxonomy which basically groups events into ‘parties, concerts, birthdays etc’. I don’t know how to fetch them based on ‘current’ events and a particular type e.g. party. I think, I have to use ‘tax_query’ but not sure how it will work with the date query. Can you help somehow?

    Thanks
    Mick

    • Alex Tatiyants says:

      Hi Mick, thank you, glad you found it useful. I didn’t include any taxonomy related code because I didn’t really need it. To answer your question, I think you need to add a ‘cat’ parameter to the query that retrieves events (assuming you’re using built in categories:


      $args = array(
      'post_type' => 'events',
      'cat' => $category_id,
      'posts_per_page' => 5,
      'orderby' => 'event_date',
      'meta_key' => 'event_date',
      'order' => 'ASC'
      );

      Hope this helps.

  16. Mick says:

    Thanks a lot, I don’t have default categories but the same concept worked with taxonomies as well with few modifications. I also went ahead to include it as a shortcode functionality. Here’s some snippet code for reference –

    // get shortcode parameter for daterange (can be “current” or “past”)
    extract( shortcode_atts( array(
    ‘daterange’ => ‘current’,
    ‘eventtype’ => ‘parties’
    ), $atts ) );

    ob_start();

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

    Here’s the function to add the taxonomy –
    function event_taxonomy() {
    register_taxonomy(
    ‘event_type’,
    ‘events’,
    array(
    ‘hierarchical’ => true,
    ‘label’ => ‘Events Category’,
    ‘query_var’ => true,
    ‘rewrite’ => array(‘slug’ => ‘event-type’)
    )
    );
    }
    add_action( ‘init’, ‘event_taxonomy’ );

    Shortcode: [events daterange=”current” eventtype=”birthdays”]

  17. James says:

    Need to add description i’m typing on wordpress editor to below loop how can i to that

    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 ” . $day . ‘‘ . $month . ‘‘;
    }

    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 . ‘‘ . $post->post_title . ‘‘;
    }

    $ret = $ret . ”.get_post_meta($post->ID, ‘event_location’, true) . ”;
    $ret = $ret . format_possibly_missing_metadata(‘event_street’);
    $ret = $ret . format_possibly_missing_metadata(‘event_city’);
    $ret = $ret . ”;
    $ret = $ret . date(“F d, Y”, $unixtime) . ”;
    $ret = $ret . ‘‘ . get_post_meta($post->ID, ‘event_start_time’, true) . ‘ – ‘;
    $ret = $ret . get_post_meta($post->ID, ‘event_end_time’, true) . ‘
    ‘;

    if (!empty($register_url) && $include_register_button && $unixtime > time()) {
    $ret = $ret .’register‘;
    }

    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.”;
    }
    }

  18. Richard says:

    Great tutorial thanks, just wondering if you might know how I might add an additional sort column to the back end with a keys of ‘current’ and ‘past’ just so there is additional method of sorting through events I understand you can sort by date.

    However it would be great if possible that when you post an event it would show in the column as current and when the date passes it reads past. This function probably could be done via categories somehow.

    But it’s the logic to say when the date has passed this post is now as past event.

    Thanks

  19. Keisa says:

    Getting Fatal Error//
    Fatal error: Call to undefined function format_date() in /functions.php on line 134

    //

    echo format_date($custom[“event_date”][0]) . ‘‘ .

    Please Help 🙁

    • Alex Tatiyants says:

      Hi Keisa, this is due to a function I forgot to include:

      function format_date($unixtime) {
      return date(“F”, $unixtime).” “.date(“d”, $unixtime).”, “.date(“Y”, $unixtime);
      }

  20. Keisa says:

    Also, what about a featured image (flyer perhaps)?
    I currently have
    //
    add_theme_support( ‘post-thumbnails’ );

    inside of my functions.php file.

    And I added the
    //

    code into my page template named events.php

    What can I do in order to convert this into a shortcode if needed?
    Or can I just use the same code along with the shortcodes for the date/time ect?
    Thanks in advance 🙂

  21. Keisa says:

    Just a suggestion…
    Just like the “gigpress” plugin it would be fabulous to add a canceled or sold out section…
    is there any way to do that?

    Also,
    Can you please post a code to add custom fields such as “Ticket Price” ?
    I just need one example and I can figure the rest out on my own hopefully.

    Sorry for the multiple comments.
    -Keisa

  22. Keisa says:

    How can I remove the dash between the start and end times? What if I want just the start time for an event? Some events din’t have an

  23. Jake says:

    Awesome article, Alex!
    Very helpful and laconic.
    With such a simple words you’ve managed to cover rather difficult topic.

    Everything great, except that should not it be

    if ( get_post_type($post) != ‘event’)

    when we ensuring that saving won’t run for non-event post types?

    • Alex Tatiyants says:

      Excellent point Jake, that’s definitely wrong. The correct line should be this:

      if ( get_post_type($post) != ‘events’)
      return;

      I’ll update the post accordingly, thank you very much for your comment.

  24. David says:

    Hi Alex

    Thanks for posting this. I’m trying to use the date and city fields automatically for the title instead of having to type in a title. Any ideas?

    Cheers

    • David says:

      Update, i’ve managed to generate the title from the date and city using this:

      add_filter(‘title_save_pre’, ‘save_title’);
      function save_title($title_to_ignore) {
      global $post;
      $custom = get_post_custom($post->ID);
      $gig_date = $custom[“gig_date”][0];
      $gig_city = $custom[“gig_city”][0];
      $my_post_title = $gig_date . ” in ” . $gig_city;
      return $my_post_title;
      }

      add_filter(‘name_save_pre’, ‘save_name’);
      function save_name($my_post_name) {
      if ($_POST[‘post_type’] == ‘gigs’) :
      $gig_date = $_POST[‘gig_date’];
      $gig_city = $_POST[‘gig_city’];
      $my_post_name = $gig_date . “-” . $gig_city;
      endif;
      return $my_post_name;
      }

      I’m getting a 404 on the single events though, need to fix that now!

      Cheers

      • David says:

        Sorry, that code was slightly off. If anyone is interested in creating the title of the post from fields here’s how I got it working using the date and city

        add_filter(‘title_save_pre’, ‘save_title’);
        function save_title($my_post_title) {
        global $post;
        $custom = get_post_custom($post->ID);
        $gig_date = $_POST[“gig_date”];
        $gig_city = $_POST[“gig_city”];
        $my_post_title = $gig_date . ” in ” . $gig_city;
        return $my_post_title;
        }

        add_filter(‘name_save_pre’, ‘save_name’);
        function save_name($my_post_name) {
        if ($_POST[‘post_type’] == ‘gigs’) :
        $gig_date = $_POST[‘gig_date’];
        $gig_city = $_POST[‘gig_city’];
        $my_post_name = $gig_date . “-” . $gig_city;
        endif;
        return $my_post_name;
        }

        Cheers

  25. David says:

    Hi Alex

    I’ve added some extra fields and they’re working fine and i’m using this to output them

    $ret = $ret . ‘Venue Phone: ‘ . format_possibly_missing_metadata(‘gig_phone’, true) . ”;
    $ret = $ret . ‘Ticket Link: Buy Tickets‘;

    I want to only display them if the user has added something to the field. Any ideas on how to achieve this?

    Cheers

    • Alex Tatiyants says:

      Hi David, you could use the code in format_possibly_missing_metadata() to check. For example, to output a line only if ‘gig_phone’ was entered, you could do this:

      $gig_phone = get_post_meta($post->ID, 'gig_phone', true);
      if (!empty($gig_phone)) {
         $ret =.'Venue Phone: '.$gig_phone;
      }
      

      Hope this helps and thank you for reading the post.

  26. Keisa says:

    Hello Alex, Me again…

    I have a few question, if you don’t mind helping out…

    1. Is there any way to remove the dash between the start and end times, just in case there is so specific end time for an event?
    2. How can I add a date picker?
    3. How can I add styles to the custom meta box to make it prettier?
    4. How exactly can I add new fields to the Event Details?
    I tried this ` $ret = $ret . ‘Location: ‘;` but it didn’t work 🙁

    I’m a beginner, I have no degree in Web Design/Development so I don’t know how to accomplish what I’m trying to get onto the site…

    Please help!

    • Alex Tatiyants says:

      Hi Keisa, to answer your questions:

      1. I assume you want to display the end time only if it’s set. If that’s the goal, case for “event_date” in events_custom_columns() can be rewritten like this:

          case "event_date":
          	$date_html =. format_date($custom["event_date"][0]) . '<br /><em>' . $custom["event_start_time"][0];
          	if (!empty($custom["event_end_time"])) {
          		$date_html =. ' - ' . $custom["event_end_time"][0] . '</em>';
          	}  	
          	echo $date_html;
      
            break;
      

      2. Probably the most straightforward one to incorporate would be jquery UI datepicker. There’s a post here that explains how do it it: http://wordpress.org/support/topic/jquery-date-picker-in-admin

      3. You can always inline the style (though I’m not a big fan of that for maintainability reasons). For example, here’s how you add a red border to the input box for event city in function event_details_meta:

      $ret = $ret . '<p><label>City: </label><input style="border: 1px solid red;" type="text" size="50" name="event_city" value="' . get_event_field("event_city") . '" /></p>';
      

      4. To add a new field, you need to update multiple functions: save_event_details(), event_details_meta, get_event_details and possibly others. For example, to add a field for ticket price, you do this:

      
      // in save_event_details() add this at the end of the function:
      save_event_field("event_ticket_price");
      
      // in get_event_details() add this right before the return statement:
      $ret =. format_possibly_missing_metadata('event_ticket_price');
      
      // in event_details_meta() add this right before echo $ret:
      $ret = $ret . '<p><label>Ticket Price: </label><input type="text" size="10" name="event_ticket_price" value="' . get_event_field("event_ticket_price") . '" /></p>';
      
      

      Hope this helps. On a separate note, I’d recommend reading a book on PHP development, which should hopefully clear a few things up for you.

      • Keisa says:

        Thank you for the response 🙂 Is there anyway you can E-mail me please?
        I don’t want to hog up your comments :/
        I’d really appreciate it if you ever have the spare time…
        -Keisa

  27. Keisa says:

    Not sure if this code will go through :/

    $ret = $ret . `Tickets: `;

  28. Keisa says:

    I love this tutorial, it adds a single event page and more custom meta fields.
    However, without the knowledge needed, I can’t even tweak a few things.
    If you check out Noel’s tutorial, you are able to place a few things where ever desired for the short code.
    But limited to changing code with this tutorial…
    I managed to add a field and get that specific field on the front-end…
    But I can’t even add divs around certain elements if I wanted to.
    If I wanted to add the date picker I’m not even sure where I’d be able to add the code…
    Please Help.

  29. Ashraf says:

    Hi,
    Thanks a lot for the tutorial.I am getting the error on front page..
    Fatal error: Call to undefined function get_event_calendar_icon() in C:\xampp\htdocs\wordpress\wp-content\themes\murtaugh-HTML5-Reset-Wordpress-Theme-8aa6329\single-events.php on line 8

    Thanks in advance

  30. Ashraf says:

    Sorry for previous post ..read more is not working..

  31. Jeremiah Lewis says:

    Fantastic tutorial. I think you did a marvelous job of expanding on Noel’s original posts, and in a really intuitive way.

    I have a question about results paging–is there a way to implement paging with this? You’d show x results at a time, if there are more, you would move to the next page for the next set of results, etc. Something like this would open this up nicely.

    All in all, I love what you’ve done here. Quite useful!

    • Alex Tatiyants says:

      Thank you very much Jeremiah. As far as pagination, it should work very similarly to how you page for posts:

      <?php 
        $temp = $wp_query; 
        $wp_query = new WP_Query(); 
        $wp_query->query('showposts=10&post_type=events'.'&paged='.$paged); 
        while ($wp_query->have_posts()) : $wp_query->the_post(); 
      ?>
      
        <!-- display event here-->
      
      <?php endwhile; ?>
      
      <nav>
          <?php previous_posts_link('&laquo; newer') ?>
          <?php next_posts_link('older &raquo;') ?>
      </nav>
      
      <?php 
        $wp_query = null; 
        $wp_query = $temp;  // Reset
      ?>
      
      • Jeremiah Lewis says:

        Oh duh, this is so obvious it’s ridiculous. I was looking at how to integrate it into the shortcode, but this is much more straightforward. Well done!

      • Jeremiah Lewis says:

        So I was able to implement paging, and it works well. Thanks for your help.
        I do have a question you might be able to answer.

        On the paged entries (any /page/2/, /pages/3/, etc) when I try using the shortcodes for current and past events in the sidebar, it shows up as “no events found”. But if you’re on the main non-paged version, events show up perfectly.

        Any ideas on why this might be happening?

        • Alex Tatiyants says:

          Hi Jeremiah, I don’t know why this might be happening without debugging what parameters the query is using. Sorry

  32. Lianne says:

    First of all, thank you for this great tutorial. Exactly what I was looking for. I have however ran into one problem. Everything is working well, except that I cannot view the individual events posts. I have added single-events to my directory with the other templates, but when I click on the event title on the feed, I get a 404 – no posts found. Any ideas??

    • Alex Tatiyants says:

      Hi Lianne, glad you found it useful. I don’t know exactly what your issue is, but I’ve seen this before if permalinks aren’t set up correctly. I have mine set to post name. (you can modify this in Settings -> Permalinks). Hope this helps.

    • Jeremy says:

      Just a note when I encountered this issue, all I needed to do was refresh or update my permalink structure as Alex mentioned and I was good to go.

  33. Mircea says:

    How I can display the events by location? . eg. [events location=”London”] to display just events from London

    I want to use shortcodes with different locations in different pages.

    Thank you very much.

  34. Toure says:

    All this is great, but I have been trying to directly pull up the events on my sidebar, events archive page and single-event.php without big success. One of my hope is to have the events enter from frontend by users. It will be great if you can enlighten us on how to do those things. Thanks

  35. Toure says:

    Sorry, but I have been looking for a contact form on your blog without success. I assume this might be OK. Is it possible to get some help on a small project. Please let me know so I can send over the scoop so I could get a quote from you. Thanks.

    • Alex Tatiyants says:

      Hi Toure, I’m not sure if I can help, but feel free to email me at alex at tatiyants.com. thanks

  36. Christian Bell says:

    Great tutorial! Brings everything I’ve been looking for into one neat package. Thank you so much!

    • Alex Tatiyants says:

      You’re welcome Christian, glad it was helpful.

      • Christian Bell says:

        I’m running into an issue now…
        For some reason some of the fields aren’t saving. The date saves but the other fields reset on updating. When I check the custom fields there’s nothing that’s been added there either. I’ve made a few changes, mostly changing the word events to something else. Also I’m running it as a plugin rather than in functions.php. Any ideas on what might cause this?

        • Christian Bell says:

          Solved it! I had put $post instead of $POST at some point… Man that’s annoying…

  37. Debra says:

    When some one searches for his required thing, therefore he/she needs to be available that in detail,
    thus that thing is maintained over here.

  38. Julie says:

    Hi,
    I’ve followed a number of these tutorials, and yours is very well explained. Thank you!!

    How can I have one of the fields (in the meta box) be a text area? I tried ‘ input type=”textarea” ‘ but that doesn’t do it. Just curious if there’s a fix that’s not too complicated?

    Thanks again for the great tutorial!

    • Alex Tatiyants says:

      Hi Julie, glad you found the tutorial useful, thanks for reading.

      To answer your question, “textarea” is not a valid input type. Instead, you can just use the textarea tag. For example, here’s the code to convert location_url:

      $ret = $ret . '<p><label>Location URL: </label><textarea rows="10" cols="20" name="event_location_url">' . get_event_field("event_location_url") . '
      
  39. Dao says:

    Hi Alex,
    First of all, I would like to thank you for writing such a great tutorial. I’m having trouble ordering a list of “past events” next to a list of “current events” when I use these two shortcodes on the same php page:

    The goal is to have first a list with all current events … ordered as it is by default ‘ASC’ and then to have another list of all past events order by ‘DESC’
    No matter what I try it’s always ordered by ASC…

    I’m a beginner in all this and sometimes I don’t know how to accomplish what I’m trying to do…

    Thanks again for your tutorial !!

  40. koushik mondal says:

    Hi Alex
    Hi Alex,
    Thanks for this great post. Its really helpful.I want to add category in this custom post please help me.

    Thank you very much.

  41. Fredrik Lindgårdh says:

    Hi! First a big thanks for this eminent lesson in custom page types. Great job.

    If you have an event that starts one day and ends another day – how would you go about that?

    I have tried pair a custom “event_day_end” to “event_day”, but I’m not doing it right. Could you please point me in the right direction?

    Thanks a lot.

  42. Gaby says:

    Hi 🙂 So I’m having a bit of trouble. I was trying to adapt the theme files of another theme that does this exact thing (creates event list of upcoming and past events) and it would be great to get your help, if you can offer it. Here’s my wordpress for sum listing where I explain in-depth:

    http://wordpress.org/support/topic/php-query-for-custom-post-type-not-displaying-creating-theme-event-list?replies=1#post-5587886

  43. Mark says:

    Hi Alex

    Is there a way to add a calender widget that will display on the sidebar with the events?

    Thank you in advance.

    Mark

  44. Nick says:

    Hi! Excellent post, got it implemented and working well. One question, the events meta (time/date etc) does not appear to be searchable, have tried the ‘Search Everything’ plugin but still missing the event fields. Any ideas would be appreciated, thanks

    Nick

  45. Rodrigo says:

    Hi Alex,
    First of all, I would like to thank you for writing such a great tutorial. I’m having trouble ordering a list of “past events” next to a list of “current events” when I use these two shortcodes on the same php page:

    The goal is to have first a list with all current events … ordered as it is by default ‘ASC’ and then to have another list of all past events order by ‘DESC’
    No matter what I try it’s always ordered by ASC…

    I’m a beginner in all this and sometimes I don’t know how to accomplish what I’m trying to do…

    Thanks again for your tutorial !!

  46. Juliet says:

    Hello Alex,
    I think you did a fantastic job with this tutorial. This is what I needed, but I’m having a problem. I’m trying to have my posts sort by the event date and it seems as though it’s only sorting by last date modified. Could it be a pagination thing? And the default template is index.php, I tried using a different template but no posts show up.

    I’m a newbie to all this, please bare with me.

    Any idea why this is happening?

    Juliet