How-to: Custom Post Type for Events Pt. 2

Now that we’ve handled the entire registration and back-end functionality (see Pt. 1) of our events custom post type, it’s time to move on to our design output (download the file for this tutorial here)! There’s no point in having fancy custom post types if you can’t display them properly. As you may remember, the entire reason for me requiring this functionality is for my Pub Theme (Pubforce), so I need to give my clients some flexibility with regards to its use. As you can see from the screenshot below, I have the following designs: 1) Featured (Shown with Big Thumbnail), 2) Full Listing (which groups by day) and 3) Widget Listing (again, grouping by day).

custom post types events pub

I don’t want to hinder my end-users in any way so I’ve chosen to use shortcodes to let them decide where and how they want to deploy events within the main area, so let’s get right to that! My tutorial below will revolve around design #2 above.

1) shortcode structure

Shortcodes remind me a lot of Excel macro’s; very detailed scripts serving a specific purpose, yet simple enough that they allow non-developers the ability to pass parameters and make use of them where and how they want. This is a massive plus over something like a page template which is a lot more rigid in its ways.

Incase you’re not all too familiar with shortcodes, I’ve laid out the basic structure below (but be sure to read the relevant codex article):

function shortcode_function ( $atts ) {
	// - define arguments -
	extract(shortcode_atts(array(
		'name' => '',
	 ), $atts));
	// - spit output -
	$output = 'My name is' . $name;
	return $output;
	}
add_shortcode('introduction', 'shortcode_function');
?>

Thus, if I were to now insert [introduction name=”Noel”] in a post, I’d get “My name is Noel“. Really hard right? As the code is going to get really long after, I’m just going to take it one step further to show you the shell of the shortcode we’ll create today. We’re going to use a nifty little function called Output Buffer (which just collects everything that is produced, starting with ob_start() and ending with ob_get_contents()). This will save us from having to piece everything together a different way (shortcodes require the use of ‘return‘, hence all this messing around):

function tf_events_full ( $atts ) {
	// - define arguments -
	extract(shortcode_atts(array(
		'limit' => '10', // Default amount of events to show
	 ), $atts))
	// - start grabbing output -
	ob_start();
	// - generate output -
	... next step of the tutorial ...
	// - spit output -
	$output = ob_get_contents();
	ob_end_clean();
	return $output;
	}
add_shortcode('tf-events-full', ' tf_events_full');
?>

Now that we have the shell of our shortcode, lets move on to more interesting things shall we.

2) query

Although tradition would have dictated I use a built-in function such as wp_query, I wanted to leave my options down the road without having to rewrite everything. This is why I’m using a custom select query which lets you do anything you want (more or less).

// - hide events that are older than 6am today -
$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 $limit
 ";
$events = $wpdb->get_results($querystr, OBJECT);

You’ll notice right at the top that I’m defining this variable called $today6am. Given my clients all run Pubs, I figured this would be a good time to make events disappear. I like the concept of removing dates on a daily basis as opposed to when they occur, and seeing as many events go into the early morning, I found 6am to be a happy medium to remove events that happened on the day before. You’ll also see that I’m conscious of the local timezone by multiplying the timezone difference in hours (gmt_offset) by how many seconds there are in an hour (3600). Adding this value to the Unix timestamp means it’s always 6am local time. Also note that I’m comparing against the event end time (as some events are full-day).

It’s also important that we call the postmeta twice (metastart & metaend), this way we can use the start & end time for their respective purposes; start for sorting the events and end for checking if they have expired.

Also, you’ll remember that $limit is a parameter passed through the shortcode (default ’10’).

3) loop

Now the loop itself, as mentioned above this is the final look we’re trying to achieve:

custom post types wordpress

In order to do that, this is the code we’ll require (starting off with the loop declaration itself and any variables we’d like to use in our final output):

// - declare fresh day -
$daycheck = null;
// - loop start -
if ($events):
global $post;
foreach ($events as $post):
setup_postdata($post);
// - custom variables -
$custom = get_post_custom(get_the_ID());
$sd = $custom["tf_events_startdate"][0];
$ed = $custom["tf_events_enddate"][0];
// - determine if it's a new day -
$longdate = date("l, F j, Y", $sd);
if ($daycheck == null) { echo '<h2 class="full-events">' . $longdate . '</h2>'; }
if ($daycheck != $longdate && $daycheck != null) { echo '<h2 class="full-events">' . $longdate . '</h2>'; }
// - local time format -
$time_format = get_option('time_format');
$stime = date($time_format, $sd);
$etime = date($time_format, $ed);
?>

… followed by the XHTML output …

<div class="full-events">
    <div class="text">
        <div class="title">
            <div class="time"><?php echo $stime . ' - ' . $etime; ?></div>
            <div class="eventtext"><?php the_title(); ?></div>
        </div>
    </div>
     <div class="desc"><?php if (strlen($post->post_content) > 150) { echo substr($post->post_content, 0, 150) . '...'; } else { echo $post->post_content; } ?></div>
</div>

… and finally the end of the loop …

<?php
// - fill daycheck with the current day -
$daycheck = $longdate;
// - go back to the top -
endforeach;
else :
endif;

You’re probably wondering what all those ‘day’ variables are? Well they’re there to help us group the events into days like the design above! Let me break it down so it makes more sense (see commenting):

// - declare fresh day -
$daycheck = null;
... loop starts ...
// - convert date -
$longdate = date("l, F j, Y", $sd);
// - if it's our first event, echo date as title -
if ($daycheck == null) { echo '<h2 class="full-events">' . $longdate . '</h2>'; }
// - if the date has changed since the last post, echo date as title -
if ($daycheck != $longdate && $daycheck != null) { echo '<h2 class="full-events">' . $longdate . '</h2>'; }
... output rest of event ...
// - fill daycheck with the current day -
$daycheck = $longdate;
... loop ends and repeats ...

4) bonus: featured loop

Now that we’ve got the regular event listing done, I figured I’d go an extra step to show you how a featured event is displayed. The big difference here is that we’ll be using the custom taxonomy that we created to help filter results. Specifically, the end user will be able to select ‘Featured’ to trigger the required visibility:

Instead of repeating the entire code, I’m just going to show you the actual query, which is the most important change (as it now involves pulling in taxonomy data):

global $wpdb;
$querystr = "
SELECT *
FROM $wpdb->postmeta metastart, $wpdb->postmeta metaend, $wpdb->posts
LEFT JOIN $wpdb->term_relationships ON($wpdb->posts.ID = $wpdb->term_relationships.object_id)
LEFT JOIN $wpdb->term_taxonomy ON($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)
LEFT JOIN $wpdb->terms ON($wpdb->terms.term_id = $wpdb->term_taxonomy.term_id)
WHERE ($wpdb->posts.ID = metastart.post_id AND $wpdb->posts.ID = metaend.post_id)
AND $wpdb->term_taxonomy.taxonomy = 'tf_eventcategory'
AND $wpdb->terms.name = '$group'
AND (metaend.meta_key = 'tf_events_enddate' AND metaend.meta_value > $today6am )
AND metastart.meta_key = 'tf_events_enddate'
AND $wpdb->posts.post_type = 'tf_events'
AND $wpdb->posts.post_status = 'publish'
ORDER BY metastart.meta_value ASC LIMIT $limit
";
$events = $wpdb->get_results($querystr, OBJECT);

You’ll notice I’ve also got a new variable in the mix called $group, this is by default ‘Featured’, however can be overridden by the user through the shortcode (maybe they only want to show Soccer matches). This is the sort of flexibility you want to give to your clients.

It would have also been possible to just create a checkbox within the custom post type, but again we’d be limiting user options and not making use of well-functioning WP code.

5) your turn to speak!

Your options are endless with this setup (you can downloaded the entire file here), but you’ll always need to beware of the amount of queries it runs as it can really be taxing on the server (check out my post on my development environment for more information on debugging).

If you have any suggestions, comments or ways you’d do things differently, please let me know by commenting below! I’d love to hear your take on this as well as your experiences. Feel free to ping me on Twitter @noeltock or retweet this message, always appreciated!

Fast & Furious WordPress Theme Development

I’m somewhat of a productivity nut; I automatically track my time through rescuetime, try to automate my workflows and absolutely despise repetitive work. After all, life is short, why spend it doing the same thing over and over again (I’m also fortunate enough to be able to make career choices)?

As a designer, coding has always had more of this “have to get it done” feel to it. Although I respect it (which is why I’m learning), I can’t relate much to the phrase “code is poetry”. I also don’t check into the WordPress Trac all too often or even know a single feature planned for PHP 6.  However, I love developing themes for WordPress and designing functional end-user solutions. This is also why I had taken it upon myself to delve into PHP, some jQuery and plenty of WordPress functionality. My first steps could probably be characterized as the typical NotePad & FTP Client setup, the equivalent of swimming laps in quicksand. The guide below is based on my experiences & needs as a designer,  I hope that you will join in and share yours.

All of the solutions I offer below are free, except for Beanstalk (where I can offer 10% off through this link). So in other words, this setup runs at about $15/month (or $0 if you’re willing to do away with automatic deployments). Before describing my time-savers, let me show you them!

It may seem like a lot of stuff, but I find it very manageable as a one man show. As WordPress is a core part of my daily business (Individual Clients + Theme Development for Theme Force), there’s no more room for inefficient workflows. I work on more than one theme everyday and certainly make more than a single change. It’s important that this process is safe, fast and efficient. Lets discuss the actual applications that help me save time:

1) netbeans (advanced editor)

What Photoshop is to MS Paint, Netbeans is to Notepad. It’s a pimped out code editor (or “integrated development environment”) that provides a project view, supports all the  common languages and even tells you off when you’re typing something funky (“This is your final warning Noel, stop using Excel functions for PHP!“). I had originally transitioned from Notepad to Notepad++, but even that was holding me back when I started getting serious about PHP. It’s really well done and easy to use for the non-developer types like myself, definitely give it a shot:

Time-Saving Features

  • Project View – When you close a project (or the application) it remembers which files you were working on. So when you re-open a project or start Netbeans the next day, you’ve got the same files open again.
  • Code-Assist (Error Checking) – It’s very smart at detecting mistakes or typo’s which can otherwise take you plenty of “investigating”. You can also easily comment lines or assign yourself To-Do’s.
  • SVN Support – You can commit changes directly from Netbeans.
  • Out of the Box Functionality – As soon as the install was complete, I didn’t have to touch anything more; PHP, XHTML, CSS, JS, etc. all recognized perfectly.

Overall and as a “non-developer”, I really enjoy working with this tool and feel like I have a much clearer overview of the items I’m working on.

2) xampp (local testing)

Confession: I actually used to upload my files via FTP for every single change (“overwrite existing file?”), it brings a whole new meaning to pissing in the wind. It gets old quick, especially once this WordPress stuff doesn’t become a hobby anymore. As a result I found XAMPP, and haven’t looked back since. The official website states “XAMPP is an easy to install Apache distribution containing MySQL, PHP and Perl. XAMPP is really very easy to install and to use – just download, extract and start.”. The statement is bang on and WordPress runs flawlessly within the environment:

Time-Saving Features

  • No more uploading / committing for testing – Instead of having  to go through your whole upload process, you just save the file and refresh your browser, voilà! Easily the biggest time-saver on this page (along with the auto-deployment from beanstalk, essentially the same thing).
  • Easy Test Environment Setup – It’s very easy to set up a new site or instance of WordPress (no upload, cpanel or general connection lag)
  • phpMyAdmin – Also included in the package and easily accessible through localhost/phpmyadmin . This sort of easy access is great when you’re troubleshooting database related matters.
  • Out of the Box Functionality – No different from Netbeans here; plug & play.

3) tortoisesvn (version control)

(Optional: As Netbeans can commit changes too, but has fewer features) Tortoise is a straightforward tool, enabling you to have easy version control right within Windows Explorer (given that you’re using a repository). For those who don’t know, the purpose of subversion is what could be summarized as a controlled collaborative environment where every change to the code is tagged as a revision, a full audit trail if you will. I like to keep my repositories on managed servers elsewhere (in my case beanstalk). Although I back up all my files through another provider, version control and collaboration is a massive plus here:

Time-Saving Features

  • Feature Rich – Be it grabbing a newer/older revision, exporting/relocating the repository or just committing your latest goods; its dead simple and integrated within Windows Explorer (i.e. right click access)
  • Windows Integration – Tortoisesvn is not a separate application visually, it integrates into your “right-click” menu within Windows Explorer. That’s a huge plus as it’s always accessible.

4) beanstalk app (subversion hosting)

I really love beanstalk; Have a look at the screenshot below, I did a commit by right clicking a folder within Windows and 16 seconds later it had been saved to my repository as well as deployed to my 2 production environments (as you can see deployment is set to “automatic” once the repository receives an update):

Although I’ve been self-sufficient for a while now, it’s also very helpful for easily verifying changes from other contributors:

Time-Saving Features

  • Automatic Deployment – Really just what I was looking for. Once everything looks good locally, I send it off to the repository which in turn automatically gets deployed (and I only pay $15 a month for this service).
  • Manual Deployments – You can rollback a deployment to any revision or do a full deployment (i.e. all files) at any time.
  • Collaboration – Easy user setup and great overview of what changes or contributions were made.

With a money back guarantee, you have nothing to lose by testing it out (and here’s 10% off).

5) wordpress time-savers

Last but not least, when you’re actually working within WordPress, there are some great tools out there to help you out:

  • WP-Devel (link) – If you want to see what’s going on within the back-end, this is the tool for you. I love using it to call me out on issues (non-existent variables, etc.) or reducing queries.
  • WP Dummy Content (link or a static pack here) – You don’t want to create dummy posts, pages, comments every time you’re testing a new theme. Get smart and use a tool to pre-populate your installation right off the bat, be it for testing design (i.e. unordered lists) or functionality (i.e. protected posts).
  • WP3 Cheat Sheet (link) – This actually has to do more with your actual WordPress knowledge, but the fact that there are consolidated lists out there, that are print-ready was worthy enough for me to mention (and I’m a big fan).

what does your development environment look like?

My method is by no means perfect or the only way of doing things (certainly not), but it fits my work style very nicely. I’d be interested in hearing how you work or develop on WordPress (be it for themes or plug-ins), so comment below and share with everyone else!

How-to: Custom Post Type for Events

Chances are you found this page looking for a tutorial on how to create Events with Custom Post Types within WordPress. I will outline the entire process I’ve used for one of my themes that I’m re-coding (in order to make use of this great feature). You can also download the entire custom post type file that includes the code mentioned here. This is the visual output as is in testing today (please see the second tutorial for the code on outputting the design):

Whilst this tutorial shows all the code, I’ll go over the “default” parts quite quickly and then focus on any code relating to the actual Events component. As a designer, I’ve found it very manageable to put together something that 5 years ago would have been so much more work (and who likes manual work?). So if I can do it, anyone can (really).

1) event requirements

Before hitting the code, it’s important to understand what you really need so that you can strive towards the simplest end solution. For the events custom post type of my theme,  I need to be able to show the following items (beyond the default fields):

  • Start Date
  • Start Time
  • End Date
  • End Time

I debated if I even required an end date (2 more fields to fill out for the user), but figured I’d probably be limiting my options for some of the other ideas I have. The great thing about storing dates is that they’re done so using Unix Time (i.e. seconds elapsed since 1/1/1970 as a single integer). This means that we have tons of flexibility when it comes to extracting and showing the dates with default PHP functions. As such, whilst the end user will see 4 fields, we’re only storing 2 fields (thus my custom post type will only require those 2 extra custom fields).

One thing I wanted to avoid was also feature bloat. There are plenty of event plug-ins out there with advanced functionality (recurrence, rsvp, paypal, etc.). I’ll leave those awesome things to the plug-in designers, there’s absolutely no need to reinvent the wheel there. My primary goal is to design themes, and the out of the box functionality I provide is a simple foundation that works with the design (more on that in my next tutorial). So, onto the good stuff:

2) registering our custom post type

I’ll assume you’re already somewhat familiar with this aspect of the custom post type process (it’s actually very well documented within the WordPress Codex). I’m not doing anything special either, so feel free to have a look or just skip right onto the next section. All we’re doing is providing a set of labels or phrases to be used by the Custom Post Type and then defining the characteristics or behavior of the post type itself. Also, if you want to save yourself an afternoon, make sure the actual name of the custom post type (not label) is 20 characters or less.

// 1. Custom Post Type Registration (Events)
add_action( 'init', 'create_event_postype' );
function create_event_postype() {
$labels = array(
    'name' => _x('Events', 'post type general name'),
    'singular_name' => _x('Event', 'post type singular name'),
    'add_new' => _x('Add New', 'events'),
    'add_new_item' => __('Add New Event'),
    'edit_item' => __('Edit Event'),
    'new_item' => __('New Event'),
    'view_item' => __('View Event'),
    'search_items' => __('Search Events'),
    'not_found' =>  __('No events found'),
    'not_found_in_trash' => __('No events found in Trash'),
    'parent_item_colon' => '',
);
$args = array(
    'label' => __('Events'),
    'labels' => $labels,
    'public' => true,
    'can_export' => true,
    'show_ui' => true,
    '_builtin' => false,
    'capability_type' => 'post',
    'menu_icon' => get_bloginfo('template_url').'/functions/images/event_16.png',
    'hierarchical' => false,
    'rewrite' => array( "slug" => "events" ),
    'supports'=> array('title', 'thumbnail', 'excerpt', 'editor') ,
    'show_in_nav_menus' => true,
    'taxonomies' => array( 'tf_eventcategory', 'post_tag')
);
register_post_type( 'tf_events', $args);
}

3) custom taxonomy

This step is optional, but always a safe play for anyone who’d like the ability to categorize their events. Again, I’m not doing anything out of the ordinary (except maybe making it hierarchical). Don’t forget to attach the taxonomy to the post type:

function create_eventcategory_taxonomy() {
$labels = array(
    'name' => _x( 'Categories', 'taxonomy general name' ),
    'singular_name' => _x( 'Category', 'taxonomy singular name' ),
    'search_items' =>  __( 'Search Categories' ),
    'popular_items' => __( 'Popular Categories' ),
    'all_items' => __( 'All Categories' ),
    'parent_item' => null,
    'parent_item_colon' => null,
    'edit_item' => __( 'Edit Category' ),
    'update_item' => __( 'Update Category' ),
    'add_new_item' => __( 'Add New Category' ),
    'new_item_name' => __( 'New Category Name' ),
    'separate_items_with_commas' => __( 'Separate categories with commas' ),
    'add_or_remove_items' => __( 'Add or remove categories' ),
    'choose_from_most_used' => __( 'Choose from the most used categories' ),
);
register_taxonomy('tf_eventcategory','tf_events', array(
    'label' => __('Event Category'),
    'labels' => $labels,
    'hierarchical' => true,
    'show_ui' => true,
    'query_var' => true,
    'rewrite' => array( 'slug' => 'event-category' ),
));
}
add_action( 'init', 'create_eventcategory_taxonomy', 0 );

4) creating & showing columns

This is where it starts to get more interesting. Within the dashboard, custom post types are listed the same way as regular posts & pages are, however we’re the ones who need to make it all happen. This is what our final output will look like:

As you’ll see from the code below, we need to create a function to define our headers (tf_events_edit_columns), and then another function to define with which content we’re going to populate it with (tf_events_custom_columns). Beyond the standard fields (title, description, etc.), I’m also going to show my times here, and this is thought process behind the event-related fields:

  • Dates – Here I’m grabbing the dates and hardcoding which format they should be output in. Dates are more or less universal, whereby times aren’t (i.e. 24hr vs AM/PM). Seeing as (in my case) the end date will almost always be the same as the start, I’ve subdued its visual effect to reduce the clutter a bit.
  • Times – As I mentioned above, 24hr vs AM/PM is an important factor. So to make this easy on the end-user we’ll grab the local format using get_option(‘time_format’) which we can then feed right into the date() function.
// 3. Show Columns
add_filter ("manage_edit-tf_events_columns", "tf_events_edit_columns");
add_action ("manage_posts_custom_column", "tf_events_custom_columns");
function tf_events_edit_columns($columns) {
$columns = array(
    "cb" => "<input type=\"checkbox\" />",
    "tf_col_ev_cat" => "Category",
    "tf_col_ev_date" => "Dates",
    "tf_col_ev_times" => "Times",
    "tf_col_ev_thumb" => "Thumbnail",
    "title" => "Event",
    "tf_col_ev_desc" => "Description",
    );
return $columns;
}
function tf_events_custom_columns($column)
{
global $post;
$custom = get_post_custom();
switch ($column)
{
case "tf_col_ev_cat":
    // - show taxonomy terms -
    $eventcats = get_the_terms($post->ID, "tf_eventcategory");
    $eventcats_html = array();
    if ($eventcats) {
    foreach ($eventcats as $eventcat)
    array_push($eventcats_html, $eventcat->name);
    echo implode($eventcats_html, ", ");
    } else {
    _e('None', 'themeforce');;
    }
break;
case "tf_col_ev_date":
    // - show dates -
    $startd = $custom["tf_events_startdate"][0];
    $endd = $custom["tf_events_enddate"][0];
    $startdate = date("F j, Y", $startd);
    $enddate = date("F j, Y", $endd);
    echo $startdate . '<br /><em>' . $enddate . '</em>';
break;
case "tf_col_ev_times":
    // - show times -
    $startt = $custom["tf_events_startdate"][0];
    $endt = $custom["tf_events_enddate"][0];
    $time_format = get_option('time_format');
    $starttime = date($time_format, $startt);
    $endtime = date($time_format, $endt);
    echo $starttime . ' - ' .$endtime;
break;
case "tf_col_ev_thumb":
    // - show thumb -
    $post_image_id = get_post_thumbnail_id(get_the_ID());
    if ($post_image_id) {
    $thumbnail = wp_get_attachment_image_src( $post_image_id, 'post-thumbnail', false);
    if ($thumbnail) (string)$thumbnail = $thumbnail[0];
    echo '<img src="';
    echo bloginfo('template_url');
    echo '/timthumb/timthumb.php?src=';
    echo $thumbnail;
    echo '&h=60&w=60&zc=1" alt="" />';
}
break;
case "tf_col_ev_desc";
    the_excerpt();
break;
}
}

In order to save space, I’ve combined my start & end dates into one column, as well as my start and end times. Again, this makes it all a little easier on the user. Here is also the CSS used which I’m only calling within the admin area (it’s a great help as it can really reduce those wide columns on larger monitors):

/* Columns CPT Events */
th#tf_col_ev_date, th#tf_col_ev_cat {width:150px}
td.tf_col_ev_date em {color:gray;}
th#tf_col_ev_times {width:150px}
th#tf_col_ev_thumb {width:100px}

5) show meta box

We then need to provide our users with fields they can fill out, for which a custom meta box is required. It’d be nice if WordPress would automatically do that for a predefined set of field types (text, date, checkbox, dropdown, etc.), however this hasn’t made its way into the system yet. So with the current state of things, you’ll need to beautify the look of it yourself. On a positive note, WordPress 3.1 will use jQuery 1.4.4 and jQuery UI 1.8.6., at least we’ll be somewhat up-to-date there. For the purpose of this tutorial, I’ve left out any fancy formatting or branding, this being the final look:

In the code below, we’ll register the meta-box, link it to the custom post type and then create the form. Again, here is the thought process behind the event-related fields:

  • Split the Database Date Value – As you may recall, I’m using 2 custom fields in the database to output 4 visible data-entry fields. I do this by using the date() function for both, but only outputting the *date* within the first variable, and only the *time* within the second.
  • If Empty, show Today – Zero in Unix Time is 1970. Clearly, it’d be tough to scroll through 40 years worth of months for the end-user (talk about the quickest refund in the history of WordPress). As such, if we have no value in the database (i.e. when we’re creating a new event), we’ll populate it with todays date. If you don’t want the current time to be shown (remember how we split 1 database field into 2 meta fields), then we’ll also need to change the new time field to 0.
  • Strong Date Format – You’ll notice here that I predefined the date format to “D, M d, Y“. When we’re going to use the strtotime() function later on to save our data, it needs to function for all sorts of dates. When experimenting with shorter date formats I noticed that often the day & month would get switched (US vs Euro date styles). By using “D, M d, Y” (i.e. “Fri, Feb 11, 2011” I not only have something that is visually functional but also feeds back into the database without any issues.

When looking at the code, you could argue that we need more controls & validation. I understand that to a degree, but people aren’t THAT stupid. If I’m asking them to input “14:00” for 2pm, 99% will do it on the first try and the remaining 1% would have learned it on their second attempt. Label me pragmatic, but don’t call my clients stupid :)

// 4. Show Meta-Box
add_action( 'admin_init', 'tf_events_create' );
function tf_events_create() {
    add_meta_box('tf_events_meta', 'Events', 'tf_events_meta', 'tf_events');
}
function tf_events_meta () {
// - grab data -
global $post;
$custom = get_post_custom($post->ID);
$meta_sd = $custom["tf_events_startdate"][0];
$meta_ed = $custom["tf_events_enddate"][0];
$meta_st = $meta_sd;
$meta_et = $meta_ed;
// - grab wp time format -
$date_format = get_option('date_format'); // Not required in my code
$time_format = get_option('time_format');
// - populate today if empty, 00:00 for time -
if ($meta_sd == null) { $meta_sd = time(); $meta_ed = $meta_sd; $meta_st = 0; $meta_et = 0;}
// - convert to pretty formats -
$clean_sd = date("D, M d, Y", $meta_sd);
$clean_ed = date("D, M d, Y", $meta_ed);
$clean_st = date($time_format, $meta_st);
$clean_et = date($time_format, $meta_et);
// - security -
echo '<input type="hidden" name="tf-events-nonce" id="tf-events-nonce" value="' .
wp_create_nonce( 'tf-events-nonce' ) . '" />';
// - output -
?>
<div class="tf-meta">
<ul>
    <li><label>Start Date</label><input name="tf_events_startdate" class="tfdate" value="<?php echo $clean_sd; ?>" /></li>
    <li><label>Start Time</label><input name="tf_events_starttime" value="<?php echo $clean_st; ?>" /><em>Use 24h format (7pm = 19:00)</em></li>
    <li><label>End Date</label><input name="tf_events_enddate" class="tfdate" value="<?php echo $clean_ed; ?>" /></li>
    <li><label>End Time</label><input name="tf_events_endtime" value="<?php echo $clean_et; ?>" /><em>Use 24h format (7pm = 19:00)</em></li>
</ul>
</div>
<?php
}

Also, some very basic CSS to structure it a little:

/* Metabox */
.tf-meta {  }
.tf-meta ul li { height: 20px; clear:both; margin: 0 0 15px 0;}
.tf-meta ul li label { width: 100px; display:block; float:left; padding-top:4px; }
.tf-meta ul li input { width:125px; display:block; float:left; }
.tf-meta ul li em { width: 200px; display:block; float:left; color:gray; margin-left:10px; padding-top: 4px}

Furthermore, I tweaked the jQuery a little to show 3 months right off the bat as well as the calendar icon. I think this helps the end-user (and especially my clients). Keep in mind that the following code is in a separate .js file, not in the document directly (as you’d need to switch out the $ with jQuery). The last section of the article will also show you how to implement the datepicker:

jQuery(document).ready(function($)
{
$(".tfdate").datepicker({
    dateFormat: 'D, M d, yy',
    showOn: 'button',
    buttonImage: '/yourpath/icon-datepicker.png',
    buttonImageOnly: true,
    numberOfMonths: 3
    });
});

6) save meta box

Saving the custom data deserves a little section of it’s own, but the code is all quite standard. Check Nonce, ok? Check if Current User is allowed, ok? Save the data. The only special part is where we take the 4 events-related input fields and merge them back to the 2 database fields. As we’ve kept everything nice and simple so far, it’s no issue, we’ll just merge the date & time fields and wrap it in a strtotime() function, like so: $fulldate = strtotime ( $date . $time ). As we’ve been using “safe” date & time formats, there’ll be no issue here. Obviously the actual code requires all the other WordPress parameters to make it work, but nothing out of the ordinary:

// 5. Save Data
add_action ('save_post', 'save_tf_events');
function save_tf_events(){
global $post;
// - still require nonce
if ( !wp_verify_nonce( $_POST['tf-events-nonce'], 'tf-events-nonce' )) {
    return $post->ID;
}
if ( !current_user_can( 'edit_post', $post->ID ))
    return $post->ID;
// - convert back to unix & update post
if(!isset($_POST["tf_events_startdate"])):
return $post;
endif;
$updatestartd = strtotime ( $_POST["tf_events_startdate"] . $_POST["tf_events_starttime"] );
update_post_meta($post->ID, "tf_events_startdate", $updatestartd );
if(!isset($_POST["tf_events_enddate"])):
return $post;
endif;
$updateendd = strtotime ( $_POST["tf_events_enddate"] . $_POST["tf_events_endtime"]);
update_post_meta($post->ID, "tf_events_enddate", $updateendd );
}

7) customize update messages

I think this part is often overlooked in custom post types tutorials but makes the usability aspect so much cleaner. Whenever a user updates something, we’d like it to use events or a similar phrase to update the user instead of “post”. My final code is below, but it’s almost an exact match to what’s in the Codex under the register_post_type page.

// 6. Customize Update Messages
add_filter('post_updated_messages', 'events_updated_messages');
function events_updated_messages( $messages ) {
  global $post, $post_ID;
  $messages['tf_events'] = array(
    0 => '', // Unused. Messages start at index 1.
    1 => sprintf( __('Event updated. <a href="%s">View item</a>'), esc_url( get_permalink($post_ID) ) ),
    2 => __('Custom field updated.'),
    3 => __('Custom field deleted.'),
    4 => __('Event updated.'),
    /* translators: %s: date and time of the revision */
    5 => isset($_GET['revision']) ? sprintf( __('Event restored to revision from %s'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
    6 => sprintf( __('Event published. <a href="%s">View event</a>'), esc_url( get_permalink($post_ID) ) ),
    7 => __('Event saved.'),
    8 => sprintf( __('Event submitted. <a target="_blank" href="%s">Preview event</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
    9 => sprintf( __('Event scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview event</a>'),
      // translators: Publish box date format, see http://php.net/date
      date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
    10 => sprintf( __('Event draft updated. <a target="_blank" href="%s">Preview event</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
  );
  return $messages;
}

8) Custom jQuery UI for Events only

Last but not least, in order to make the jQuery Datepicker work in the WordPress admin area, we’ll want do some configuring. We’ll de-register the default WP jQuery UI file and then register our custom files downloaded from the official jQuery site (or linked to from the Google jQuery CDN). You’ll notice the pubforce-admin.js below, that will contain our datepicker settings mentioned above in the meta box section of this tutorial.

// 7. JS Datepicker UI
function events_styles() {
    global $post_type;
    if( 'tf_events' != $post_type )
        return;
    wp_enqueue_style('ui-datepicker', get_bloginfo('template_url') . '/css/jquery-ui-1.8.9.custom.css');
}
function events_scripts() {
    global $post_type;
    if( 'tf_events' != $post_type )
        return;
    wp_enqueue_script('jquery-ui', get_bloginfo('template_url') . '/js/jquery-ui-1.8.9.custom.min.js', array('jquery'));
    wp_enqueue_script('ui-datepicker', get_bloginfo('template_url') . '/js/jquery.ui.datepicker.min.js');
    wp_enqueue_script('custom_script', get_bloginfo('template_url').'/js/pubforce-admin.js', array('jquery'));
}
add_action( 'admin_print_styles-post.php', 'events_styles', 1000 );
add_action( 'admin_print_styles-post-new.php', 'events_styles', 1000 );
add_action( 'admin_print_scripts-post.php', 'events_scripts', 1000 );
add_action( 'admin_print_scripts-post-new.php', 'events_scripts', 1000 );
}
?>

9) your turn to speak !

It may look like a lot of code (feel free to download the entire code here), but for the functionality and flexibility provided it’s really automated. If you have any questions, feedback or suggestions, feel free to comment below. Like said, I’m not a developer so I’m more then open to ideas and would also update the code above (& provide credit). Re-tweet if you feel like it too ;) Thank you!

I have already released Part 2: Query, Loop & Design Output , check it out!

Here are also the next tutorials planned in connection with this event post type:

other articles in this series

  1. Front-end: Design & Shortcodes
  2. Front-end: iCal Export
  3. Front-end: Implementing FullCalender

related links

Tutorial – Search Cloud (Tagxedo & Google Analytics)

Over the past few months, my WordPress Theme shop has been getting a good deal of organic traffic. After having seen a cool render of one these word clouds for a political issue (using Tagxedo , requires Silverlight), I decided to give it a shot with some of my Google Analytics organic search data, this tutorial will show you how to use both these applications to achieve this final result:

The really cool thing is that there’s a bunch of opportunities to customize the output of such charts, and I’ll show you just how:

google analytics data

The first thing you’ll want to do is login to your GA account, navigate right to your “Keywords” traffic section and export the data like so (choosing “CSV for Excel”, assuming you have Microsoft Excel, otherwise a regular CSV will do):

excel manipulations

In order for Tagxedo to be able to know how much weight to place on each word, you’ll need to provide it with the numbers too. The final format will be a single column in which each cell will have the keyword/keyphrase followed by the total search count for that phrase. The format will also need to be structured exactly like this “wordpress:500“. To achieve this, all we need to do is create a third column whereby we use the “Concatenate” function to bring these values together, like this (A & B are supplied by Google Analytics, we created C):

Once that is complete, you’ll just want to copy the content of the final column. I had tried a bunch of combinations till I saw what actually worked best, but to make a long story short, I found that taking the 100 most frequent keywords was the best approach towards a well balanced word cloud (as opposed to taking the entire column, i.e. 500-1000 keywords/key phrases).

tagxedo customizations

Just before we jump into Tagxedo, I’m first going to create my color palette (and there’s no better tool then Color Scheme Designer 3).

This is where it gets fun (assuming your clipboard is also holding the 100 top phrases including search counts) . Go into Tagxedo and start off by loading up your word list. This will generate a word cloud that usually isn’t that pretty (matter of taste I guess). This is where you’ll want to start customizing heavily, and in the following areas:

  • Shape (although complex shapes will lose the message quite quickly)
  • Fonts (upload them directly from your PC)
  • Color Themes (create one like I did above)

Once you’re happy with the result, go ahead and export a high-res version of the image (otherwise you’ll spend a bunch of time recreating it). At this point, you’re done, quite easy right? Feel free to share your word or search clouds.

Tutorial – WordPress and bxSlider

There are a bunch of jQuery content & image sliders out there. Although I initially gravitated towards Nivo Slider due to the fancy presentation and website, I found that bxSlider was the jQuery content slider I required (as you can see from my example here, work in progress). bxSlider allows you to put anything between the list item tags, hassle-free. A number of other sliders are limited to images & captions, yet still call themselves content sliders. I also noticed that a number of people were wondering how to integrate this within WordPress. So this post will serve as a guide for those interested in merging the two (and it is the only tutorial on the subject out there at the time of being published)!

Once you’ve downloaded the bxSlider package from the website, upload the files to your theme.

1) Functions.php

Instead of loading the files directly into the header, I’m going to load it within the functions file. The codex describes it as “…a safe way of adding javascripts to a WordPress generated page“. Although the CSS could be in the header, it makes more sense to keep everything packaged together.

// LOAD BX SLIDER
// *********************************************************
function loadbxslider()
{
    wp_enqueue_style('bxstyle', '/wp-content/themes/yourtheme/bx_styles/bx_styles.css');
    wp_enqueue_script('bxscript', '/wp-content/themes/yourtheme/js/jquery.bxSlider.min.js', array('jquery'));
}
add_action('init', 'loadbxslider');

2) Header.php

Notice that I haven’t called jQuery itself yet. Obviously you won’t need to reference to jquery-1.x.x.js directly as WordPress has this feature built-in. So the first thing you want to do is use the enqueue function to not only load the jQuery files, but also the bxSlider simultaneously (recall the array(‘jquery’) code above, that brings bxslider into the mix). You’ll want to place this right above the wp_head function to keep everything in good order.

<!--?php wp_enqueue_script('jquery'); ?-->
<!--?php wp_head(); ?-->

Right below wp_head, you’ll want to add the JavaScript code that will control your final output (specifying the unordered list class that will also be used as the container of rotating content). For a full list of options, please see the documentation on Steven’s website. It’s very important here that you replace the $’s with jQuery as it’s not assumed:

<script type="text/javascript">// <![CDATA[
jQuery(document).ready(function(){
  jQuery('#slidebx').bxSlider({
    mode: 'horizontal',
    infiniteLoop: true,
    speed: 2000,
    pause: 8000,
    auto: true,
    pager: false,
    controls: true
  });
});
// ]]></script>

3) BxSlider inside your Content

Now that you have everything in place, all you need to do is add the content to display your slides. In the case above, I defined #slidebx as my class containing all the slides (or more importantly the list structure). Here is a simple example of what this would look like:

</pre>
<ul id="slidebx">
	<li>Slide 1 Content</li>
	<li>Slide 2 Content</li>
	<li>Slide 3 Content</li>
</ul>
<pre>

At this point, you’re all done! And you should have some slides rotating on your website (here is an example of mine). Let me know if you have any questions, I’ll be glad to help where I can.

Tutorial – Custom Post Types for WordPress

With WordPress 3, came a slew of new features, the best one by far being Custom Post Types. I had fun using this to generate a “Dogs” page for the charity “Stiggy’s Dogs” that I manage. Stiggy’s Dogs rehabilitates dogs to pair them with returning veterans. I realize there is an abundance of tutorials out there, but they are all slightly different. Hopefully this will help someone else or give them some ideas: Here is an image of the final product as it is today:

WordPress Custom Post Type

There is a plan to develop it further with links that direct you to a chronological archive of posts related to the individual dog (almost like a diary), however this custom post type has already been very beneficial so far, as this static page gets quite a bit of traffic. For the charity, it’s a great way of showing their “portfolio”. Depending on the choices made when posting, the following various images will be displayed:

WordPress Custom Port Type Images

Now that you’ve seen where we’re going, let me show you how we get there. Custom Post Types only really consist of two files, the back-end which I’ve contained in the functions.php, and the front-end which we’ll use a loop for. As for functions.php, this is what it looks like (please take note that I’ve had to add additional php openers in order for the code to display below, if you want the final code, please download below).

Backend: Functions.php

In first line, we’ll fire up the custom post type fundamentals, defining the basics of our new tool. There are quite a number of parameters, so definitely reference the WordPress Codex if working with this for the first time:

// ************ DOGS START **************
// 1. Initialize
add_action( 'init', 'noeltockdotcom_dogs_register' );
// 2. Register Custom Post Type
function noeltockdotcom_dogs_register() {
	
		$labels = array(
			'name' => __( 'Dogs' ),
			'singular_name' => __( 'Dog' ),
			'add_new' => __( 'Add New' ),
			'add_new_item' => __( 'Add New Dog' ),
			'edit' => __( 'Edit' ),
			'edit_item' => __( 'Edit Dog' ),
			'new_item' => __( 'New Dog' ),
			'view' => __( 'View Dog' ),
			'view_item' => __( 'View Dog' ),
			'search_items' => __( 'Search Dogs' ),
			'not_found' => __( 'No dogs found' ),
			'not_found_in_trash' => __( 'No dogs found in deleted items' ),
			'parent' => __( 'Parent Dog' ),
		);
		
		$args = array( 
		'labels' => $labels,
		'description' => __( 'Your Dogs' ), 
		'public' => true,
		'supports' => array('title','editor','author','thumbnail','excerpt','comments'),
                'rewrite' => true,
                'capability_type' => 'post',
		);
		
	register_post_type( 'noeltockdotcom_dogs', $args);
}

Next up we’ll need to register our custom fields. This is what will make our custom post type so special. In the first instance we’ll tell WordPress which fields we want:

<!--?php
// **** 3. Register Custom Fields **** 
// Initialize
	add_action( 'admin_init', 'noeltockdotcom_dogs_admin' );
	
	// Add Meta Boxes
	
	function noeltockdotcom_dogs_admin(){
		add_meta_box("noeltockdotcom_dog_meta", "Dog Details", "noeltockdotcom_dog_meta", "noeltockdotcom_dogs", "normal", "low");
	}
	
	// Display Dog
	
	function noeltockdotcom_dog_meta (){
		global $post;
		$custom = get_post_custom($post->ID);
		$name = $custom["dog_name"][0];
		$status = $custom["dog_status"][0];	
		$gender = $custom["dog_gender"][0];
		$owner = $custom["dog_owner"][0];
		$branch = $custom["dog_branch"][0];
		$placed = $custom["dog_placed"][0];
	?>
	
	<label>Name of Dog:</label>
	<input name="dog_name" value="<?php echo $name; ?>" />
	<label>Status:</label>
	<select name="dog_status">
		<option value="deployed" <?php if (($status == '') || ($status == 'deployed')) { ?>selected="selected"<?php } ?>>Deployed</option>
		<option value="intraining" <?php if ($status == 'intraining') { ?>selected="selected"<?php } ?>>In Training</option>
		<option value="prospective" <?php if ($status == 'prospective') { ?>selected="selected"<?php } ?>>Prospective</option>	
	</select>	
	<label>Gender:</label>
	<select name="dog_gender">
		<option value="male" <?php if (($gender == '') || ($gender == 'male')) { ?>selected="selected"<?php } ?>>Male</option>
		<option value="female" <?php if ($gender == 'female') { ?>selected="selected"<?php } ?>>Female</option>
	</select>	
	<label>New Owner:</label>
	<input name="dog_owner" value="<?php echo $dog_owner; ?>" />
	<label>From Military Branch:</label>
	<select name="dog_branch">
		<option value="army" <?php if (($branch == '') || ($branch == 'army')) { ?>selected="selected"<?php } ?>>Army</option>
		<option value="marines" <?php if ($branch == 'marines') { ?>selected="selected"<?php } ?>>Marine Corps</option>
		<option value="navy" <?php if ($branch == 'navy') { ?>selected="selected"<?php } ?>>Navy</option>
		<option value="airforce" <?php if ($branch == 'airforce') { ?>selected="selected"<?php } ?>>Air Force</option>
		<option value="coastguard" <?php if ($branch == 'coastguard') { ?>selected="selected"<?php } ?>>Coast Guard</option>
		<option value="none" <?php if ($branch == 'none') { ?>selected="selected"<?php } ?>>None</option>
	</select>
	<label>Date Placed:</label>
	<input name="dog_placed" value="<?php echo $dog_placed; ?>" />
	<?php
	}
	
	// Save Fields
	
	add_action ('save_post', 'save_dogs');
	
	function save_dogs(){
		global $post;
		update_post_meta($post->ID, "dog_name", $_POST["dog_name"]);
		update_post_meta($post->ID, "dog_status", $_POST["dog_status"]);
		update_post_meta($post->ID, "dog_gender", $_POST["dog_gender"]);
		update_post_meta($post->ID, "dog_owner", $_POST["dog_owner"]);
		update_post_meta($post->ID, "dog_branch", $_POST["dog_branch"]);
		update_post_meta($post->ID, "dog_placed", $_POST["dog_placed"]);
	}

Last but not least; The great thing about Custom Post Types is that they also have their own dashboard for viewing all posts at once (which you can customize to show any or all of your custom fields). To display these various columns showing your custom content, whip up the following code:

<!--?php
// **** 4. Show Columns in Admin ****
// Initialize
add_action ("manage_posts_custom_column", "dogs_custom_columns");
add_filter ("manage_edit-noeltockdotcom_dogs_columns", "dogs_edit_columns");
// Display Columns
function dogs_edit_columns($columns){
	$columns = array (
		"cb" => "<input type=\"checkbox\" />",
		"title" => "Dog Name",
		"status" => "Status",
		"gender" => "Gender",
		"owner" => "Owner",
		"branch" => "Military Branch",
		"placed" => "Date Placed",
		"description" => "Description",
	);
	
	return $columns;
}
	
// Define Column Content
	
function dogs_custom_columns($column){
	global $post;
	switch ($column){
		case "status":
			$custom = get_post_custom();
			echo $custom["dog_status"][0];
			break;					
		case "gender":
			$custom = get_post_custom();
			echo $custom["dog_gender"][0];
			break;
		case "owner":
			$custom = get_post_custom();
			echo $custom["dog_owner"][0];
			break;	
		case "branch":
			$custom = get_post_custom();
			echo $custom["dog_branch"][0];
			break;	
		case "placed":
			$custom = get_post_custom();
			echo $custom["dog_placed"][0];
			break;	
		case "description":
			the_excerpt();
			break;		
	}
}

At this point, you’ll have successfully created everything needed in the back-end to make this functional. Good job!

Frontend: The Loop

This is actually the easy part, and I’ve tried to throw in some inline CSS (although not best practice) to show you how it all works.

<?php
//The Query
query_posts('post_type=noeltockdotcom_dogs&meta_key=dog_status&orderby=meta_value&order=ASC');
//The Loop
if (have_posts()) : ?>
<?php while (have_posts()) : the_post(); ?>
<?php
	$custom = get_post_custom($post->ID);
	$name = $custom["dog_name"][0];
	$status = $custom["dog_status"][0];
	$gender = $custom["dog_gender"][0];
	$owner = $custom["dog_owner"][0];
	$branch = $custom["dog_branch"][0];
	$placed = $custom["dog_placed"][0];
	$post_image_id = get_post_thumbnail_id($post_to_use->ID);
		if ($post_image_id) {
			$thumbnail = wp_get_attachment_image_src( $post_image_id, 'post-thumbnail', false);
			if ($thumbnail) (string)$thumbnail = $thumbnail[0];
		}
?>
<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<div class="dogblock">
			<div style="float:left;background: url('<?php echo $thumbnail; ?>') no-repeat; width:300px; height:250px;">
				<?php if ($status == "deployed") {
					echo "<img src='http://www.stiggysdogs.org/images/deployed.png' />";
					} elseif ($status == "intraining") {
					echo "<img src='http://www.stiggysdogs.org/images/intraining.png' />";
					} else {
					echo "";
				} ?>
			</div>
			<div class="customdogcontent">
				<h3><?=$name?></h3>
				<?php if ($status == "deployed") {
					echo "<div style=\"background: url('http://www.stiggysdogs.org/images/$branch.png') no-repeat; min-height:60px;;width:320px;padding:20px 0 0 70px;font-size:20px;\">$owner</div>"; } ?>
				<p><?php the_content(); ?></p>
			</div>
</div>
</div>
<div style="padding:20px 0 0 0;width:660px;border-bottom:1px dotted #b49579;clear:both;"></div>
<?php endwhile; ?>
<!-- Stop the post loop and process any code included here only once. This text is generated only if there are posts. Any code included in this part of the document is processed only once. -->
<?php else : ?>
<p>No Dogs :( </p>
<!-- If you are seeing this message, there are no posts to process/display. Any code included in this part of the document is processed only once. -->
<?php endif; ?>

Summary

Custom Post Types for WordPress are incredibly useful as they allow you to create and display content exactly how you want it, making your life as a web designer that much easier. More importantly, they are an excellent way to manage your client’s needs within WordPress (as opposed to custom PHP/MySQL development which may not always be future-proof when considering future versions of WordPress). If you have any questions, please let me know, I’d be glad to help!

Download

Download the two PHP files here (functions.php & loop.php)

Related Links

Derelict – Free Grunge Social Media Icons

This a set of grunge social media icons (blank PSD also available now) that I’m giving away free for commerical purposes (with attribution). The design is inspired by the free wordpress theme I am working on (see screenshots below).  I’ll release the same style for other social media sites if people want, just comment and let me know!

Grunge Social Media Icons Set

Continue reading “Derelict – Free Grunge Social Media Icons”