Tag Archives: widget

Adding another Custom Post Type

A new requirement came in for the site I manage. There was a need to add about 3 years worth of question and answer PDF files to the site. The nature of this was similar to the newsletters. Each post would contain a link to a PDF file. The title would hold the question, but the writing area would not be deactivated so the question could be split if it is too long.

It is very likely that there would be similar files in the future, so I decided to make this a custom post type so a non techie administrator can handle future uploads. I considered doing this as a plugin, but I’m still not sure how to include page templates as part of plugins, so I decided to just incorporate it into my custom theme, like the other post types.

The first thing to do was to register the post type using the register_post_type() function. Straightforward stuff.

In the following paragraphs, {cpt} should be replaced with the name of the post type. If the custom post type is named “Books”, loop-{cpt}.php means loop-books.php.

The loop-{cpt}.php template file is where I customize the look of each custom post type. This is where I choose what to display and how to show it. In this case, I printed the words “Download the answer: “, followed by a link to the PDF file with the answer. As this is handled by the template, the look of every post will be consistent and this will not depend on the uploader entering the proper wording into the content area.

Next came the {cpt}-template.php file. This modifies the query so it only selects posts of the custom post type I’m interested in displaying. It then calls the loop template file using get_template_part( 'loop', {cpt} ); A blank page is then created in the admin area and assigned this page template. A link is added to the menu so this page shows up in the menu system.

A single-{cpt}.php file is also created. It calls the loop template using get_template_part() as well.

I also had to edit the sidebar.php file. My sidebar shows date archives for each custom post type. Which one to show depends on which post type is currently displayed. I registered a new widget area in functions.php, then modified the if-elseif statements in the sidebar.php file so it could support new post type. To see which post type is currently displayed, the get_post_type() WordPress function is used. I can’t remember what I was previously using, but it wasn’t good enough.

Another change was to style.css. Instead of targeting each widget area by ID to make them align right and set the sidebar width, I realized I could just use the widget-area class instead.

On the admin side, a meta box had to be added. I had already done this before so it was a simple matter of copying and editing the existing code. The meta_box class described in earlier posts is flexible enough to handle this with no problems. I did make sure to look through the save() function to make sure it could support the new file name format without any unpleasant surprises.

After all these code additions, I now have a new custom post type I can use to add question and answer PDF files through the admin interface. I added the existing content manually, and will teach the administrator to upload future ones.

The new custom post type gave 404 errors when first used. I found that it was necessary to place flush_rewrite_rules() into the functions.php file as the usual method of resaving the permalinks didn’t work. However, after refreshing the page, the 404 errors went away and I could take out the flush_rewrite_rules() line. It is important to remove this line once the site works, or the site will be severely slowed by the constant regeneration of rewrite rules.

Custom Post Type Archives Part 4

Phew, custom post type archives are causing lots of problems. Three separate posts about them already, and the plugin was the one which broke WordPress 3.1. Well the updated version doesn’t. However, this led to a thought. Since it’s supposed to be built in, could I do it without using the plugin? After all, I was just after some very basic functionality. The answer turns out to be yes, well, almost.

Enabling the Feature

There is an attribute has_archive in the register_post_type() function which activates the archives feature, but it defaults to false. I added the code in to set this to true. This information was from Mark McWilliams, the same post also gave some hints on how the permalinks would look like.

Disabling the Plugin

The next step was to disable the plugin and see what stops working. Looks like the sidebar has some problems, which also prevents the admin bar from loading properly. The month listing from the custom widget wasn’t appearing at all.

Fixing the Widget

The widget depends on the custom get_post_type_archives() function, explained in Part 2, to do its magic.

Using a debug echo statement and commenting out suspect lines, I found the problem in one of the custom functions provided by the plugin. It returns the permalink of the post type. This can actually be generalized as

get_bloginfo('url') . '/' . $post_type . '/';

Now I get output with a link to the archives by date, but it wasn’t showing the correct number of months. Why??

Filter getarchives_where

Even the plugin just let the built in WordPress function wp_get_archives() do the hard work, so why did it work with the plugin activated? A closer look at the function itself, and the way it was used by the plugin, revealed that wp_get_archives() was insufficient to get archives by post type. It applies a filter to get the correct SQL Where clause. This was used by the plugin.

Since I only want that function, I copied the entire thing over to my own functions.php file.

function pta_wp_get_archives_filter($where, $options) {
	if(!isset($options['post_type'])) return $where; // OK - this is regular wp_get_archives call - don't do anything
	
	global $wpdb; // get the DB engine
	
	$post_type = $wpdb->escape($options['post_type']); // escape the passed value to be SQL safe
	if($post_type == 'all') $post_type = ''; // if we want to have archives for all post types
	else $post_type = "post_type = '$post_type' AND"; // otherwise just for specific one
	
	$where = str_replace('post_type = \'post\' AND', $post_type, $where);
	
	return $where;
}
add_filter('getarchives_where', 'pta_wp_get_archives_filter', 10, 2);

Now the archive links work and the correct number of links are displayed, depending on what the post type is!

Archive Templates

This approach led to my custom post types being displayed incorrectly as they now use the archive.php template file. It only displays the excerpts for archives, so the shortcodes and meta data no longer show up. As each post will be very short in length, I want the full content and meta data to show up, just like it does on the main page listings.

The solution to this was simply to copy archive.php to the child theme, rename it archive-{post-type}.php, and change the function call to the loop so that it calls my previously written custom loops.

Page Templates

{post-type}-template.php were previously used for both the display of the full listing as well as the archive pages. With WordPress 3.1, archives are handled by the archive template pages. Thus, the if-else conditions which handled query_posts() can be removed. The page templates will be used just for display of all the custom posts.

Conclusion

With all these changes, I can now use one less plugin and use more of the functionality available in WordPress Core.

Custom Post Type Archives Part 5