Simple Custom Post Type Archives adds friendly permalink support, template files, and a new conditional for public, non-hierarchical custom post type archives in WordPress 3.0!

Version 0.9.3, with obscure bug fixes, released August 18, 2010! Download now from the official plug-in repository!

WordPress 3.0 opens the door for entirely new content management possibilities with custom post types. As awesome as it is, the first version to implement custom post types is missing a few things. This plug-in patches one of those holes: templates and friendly permalinks that enable “archives” for custom post types, much the like the “blog home” is an archive for all “post” post types.

This plug-in will only add custom post type archives for non-hierarchical (post-like), public custom post types with a “slug” or permalink.

For example, suppose you have a custom post type Movies. WordPress 3.0 takes care of permalinks for individual movie content, for example, http://yourblog.com/movies/casa-blanca/. You might think you could get an archive of all recently added movies (just like the blog home is an archive of all recently added posts) by going to http://yourblog.com/movies/, but this isn’t supported out of the gate. You can do it without permalinks by going to http://yourblog.com/?post_type=movies, but even then, you’re forced to use the index.php template file in your theme. This plug-in enables archive permalinks (yourblog.com/movies), adds two new template files in the hierarchy (i.e. type-movies.php and type.php), and adds a new is_custom_post_type_archive conditional you can use in your theme!

  • Adds support for custom post type archive permalinks, i.e. yourblog.com/custom-post-type-name – including paging (/page/2/) and feeds (/feed/)
  • Adds two new template files to the hierarchy, type-(custom-post-type-name).php and type.php
  • Adds new body classes to custom post type archives, custom-post-type-archive and custom-post-type-(post-type-name)-archive
  • Fixes the is_home conditional check on custom post type archives (incorrectly reports true by default)
  • Adds a new conditional, is_custom_post_type_archive for use in your themes: can optionally check against a specific post type by passing name of post type
  • Fixes the wp_title output on custom post type archives to show the custom type’s label
  • Automatically adds feed links for custom post type archives when on a custom archive or singular custom post type if automatic feed links is enabled in your theme

What didn’t make it in this pre-1.0 release:

  • New navigation menu widget for easily adding custom post type archives to navigation

Please keep in mind that robust custom post type support is new to 3.0, and although this plug-in was aggressively tested, it’s hard to test it with all possible custom post type setups. For this reason, along with the bullets listed above, I’m considering this a “beta” – in the Google sense – or pre-1.0 release.

You’re welcome to use the code in your own custom post type plug-ins or themes (the code is GPL), but please include attribution to the author.

The changelog is available here.

As always, feedback and suggestions are welcome!

43 Responses to “Simple Custom Post Type Archives (WordPress Plug-in)”

  1. Devin says:

    I was surprised that a multiple template for custom types didn’t get included in 3.0. It seems like it will be necessary for most cases. Up until now I’ve been using some code from Michael Fields (http://wordpress.mfields.org/2010/custom-content-type-add-theme-redirect/) or just using a page template to display those custom post types. I’ll check out your code tomorrow- it seems like it takes it a step further.

    Also curious, is type-(custom-post-type-name).php just duplicating the functionality of single-customposttype.php? If so, that seems like it could be confusing for people down the road.

  2. Jake Goldman says:

    Devin – I was surprised too, which is why I wrote this. I hope this functionality makes it into core at some point.

    To answer your question, the type-(name).php template is for the custom post type *archives* for that name, not single view. So, extending my example, single-movies.php would still be the template file for individual posts in the “movies” type, while type-movies.php is for the archive of movies posts.

    I did struggle with a naming convention for these template files. I could have used a clearer “custom-post-type-archive-(name).php” but that seemed too long. I could have just used (name).php but that seemed disorganized and made the generic “type.php” fallback less clear. So I just settled on the “type-” prefix.

    Does this make sense?

  3. akella says:

    i just wanted to say THANK YOU for this plugin, its hard for me to donate, but i certainly owe you a beer if you happen to be in Kyiv!

    Also, before i found your plugin biggest problem were: i could not manage to get to work even simple urls like
    http://yourblog.com/movies/casa-blanca/
    i always got 404s. Your plugin fixed it.
    But, i investigated, and it turned out that my default url structure for the posts made a conflict with custom post types urls.
    It was set to /%year%/%monthnum%/%day%/%postname%/. Custom posts permalinks started to work when i changed it to /%postname%/.
    Thought it might help someone who will find your post via google..

  4. litemotiv says:

    Any idea how to also add a category to custom post archives? For instance the post type ‘movies’, using a category ‘top stuff’. The plugin gives back a 404 for /movies/top-stuff/ right now.

    I’ve tried adding a separate rewrite rule like this:

    $type_info->rewrite['slug'].’/([^/]+)/?$’ => ‘index.php?post_type=’.$type_info->name.’&category_name=’.$wp_rewrite->preg_index(1)

    buty the category_name parameter seems to always force to the type ‘post’, so it produces a page with regular posts in that category.

  5. Adam says:

    I installed the plugin and created a new file called type-articles.php. Though when I go to myaddress.com/articles I am still getting a 404 Not Found.

    Any idea?

  6. Jake Goldman says:

    Adam – that’s not really enough background information to gauge the problem. I assume you registered a custom post type with public set to true, non-hierarchical, and with a slug of “articles” (or if slug wasn’t defined, “articles” at the name of the custom post type)? If you did all of that, and actually created at least 1 new post in “articles”, try go the Permalink settings panel and clicking “Save” again.

  7. Jake Goldman says:

    litemotiv – so you added the generic post “category” taxonomy to your custom “movies” post type? I can’t dig into the WordPress code base at the moment, but I’m betting the “category” parameters in the post query override the taxonomy option (since category is by default only assigned to “post” type content). Can you accomplish what you want with a new taxonomy?

  8. Adam says:

    Jake: Yes, I did all of the following. Name: articles, Public: true, Hierarchical: false, rewrite: true, rewrite slug: articles. There are five published posts in that post type also. I just now went to permalinks and saved again (/%category%/%postname%/) and tried the address.com (.com/articles) and am still getting a 404 Not Found.

    If there is anything else you can think of I would definitely appreciate the assistance.

  9. shawn says:

    Just like litemotiv, I am also trying to figure out how to get a url like
    ’site/post-type/custom-taxonomy/term/’
    example: /movies/genre/classics/

    to work. So far I have had no luck with this.

    any ideas?

  10. litemotiv says:

    Jake: yes, using custom taxonomies seems to retrieve the right (custom) post types.

  11. Tik says:

    : In the file simple-custom-post-type-archives.php, replace this line :

    if ( $post_type->_builtin === false && $post_type->public === true && !empty($post_type->rewrite['slug']) && $post_type->hierarchical === false ) return true;

    by this one :

    if ( $post_type->_builtin == false && $post_type->public == true && !empty($post_type->rewrite['slug']) && $post_type->hierarchical == false ) return true;

    I don’t know why use “===” instead of “==”, but when I remove it, it works.

    What does the developer thinks about that ?

  12. ejikas says:

    One question. How you deal with the post type taxonomy archive pages?

    What I mean there… You created custom post type “movies”. So /movies/ now works as movie archive and /movies/blabla/ works as a single custom type post. Is it possible to make so that custom taxonomy “Genre” would have working url /movies/humor/? I created the taxonomy with the slug “movies” but looks like it doesn’t work well.

  13. Ash says:

    Hi,

    I’m having a bit of an issue with this. I have just started on custom post types & quickly ran in to the problem which your plugin has set out to fix.

    But when I activate your plugin it does list out the posts, but I can’t seem to override the template being used.

    Now it might not be anything to do with your plugin, as I’m using the new Twenty Ten theme as a test bed for my play with custom post types, but I would be grateful for any help you could offer.

    I have a url structure of http://test.com/product/a-bag-of-crisps/ & when I go to http://test.com/product/ it lists the posts, but ignores my type-product.php file completely.

    Ash

  14. Robert says:

    Can not get it to work either. Getting a 404, just like Adam.

    Everything was done according to the protocol.

  15. Jake Goldman says:

    Ejikas – WordPress 2.9 and newer automatically add permalinks to new taxonomies. Off the top of my head, it would just be “/humor” without the custom post type prefix.

    Allowing access to custom post type specific taxonomies as permalinks “beneath” the custom post type slug is an interesting idea that I’ll explore. My concern is not conflicting with the native permalink handling, and how to deal with taxonomies assigned to several post types.

  16. Jake Goldman says:

    Ash – two questions.

    1 – are you using a child theme? It’s come to my attention the .8 doesn’t work with child themes.

    2 – are you certain that “product” is the *name* of the custom post type? The URL uses the slug, but I think the template uses the registered name / ID of the post type.

  17. I too am trying this. I’m trying with the fallback type.php and have no luck. I’ve tried with both hierarchical and non-hierarchical taxonomies to no avail.

  18. Oscar says:

    Jake – I couldn’t get it working with a parent OR a child theme until I made the change that @Tik suggested. Then both worked fine.

    I also adjusted the wp_title filter to separate the blog title from the post type name. I changed it from this:

    add_filter( ‘wp_title’, create_function( ”, ‘return “‘.$post_type->label.’”;’ ) );
    // correct wp_title

    to this:

    add_filter( ‘wp_title’, create_function( ”, ‘return ” – ‘.$post_type->label.’”;’ ) );
    // correct wp_title

  19. Jake Goldman says:

    Version 0.8.5 should solve many of the “just doesn’t work” issues – I believe this problem comes from “imprecisely” registered post types. I’ve loosened the conditional checks, which should address those issues – kudos Tik for helping discover that connection.

    I’ve also fixed the wp_title filter to behave as expected (kudos to Oscar for discovering), and added full child theme support (kudos to GravityForms’ Carl Hancock for pointing out).

  20. Oscar says:

    Awesome!

    Any chance that the conditional is_custom_post_type_archive could allow for for specific post types (i.e. is_custom_post_type_archive(‘movies’) in a future release?

  21. Jake Goldman says:

    Oscar – yep, that’s definitely in the 1.0 road map, and will probably be in the next update.

  22. pescadito says:

    hi,
    i tried you plugin without succefull and i have some questions to do:

    1) i use wp30 and more field and/or magic field plugins to create custom post types, are this plugins compatible with yours?

    2) suppose i have movie as custom post type, when you create type-movie.php and single-movie.php, do you need to add

    before get_header() ?

    3) after that how do you address the custom post type collection (archive) for the new templates?
    - http://myblog.com/?post_type=movie
    - http://myblog.com/movie
    - or something else??

    best regard, pescadito

  23. Ash says:

    Your update, seems to fix it. As yes I was using it in a child theme.

    Thanks

  24. Jake Goldman says:

    Pescadito – it’s designed for WordPress 3.0’s built in custom post type functions, not a third party implementation. You can fairly easily create post types using the “register_post_type” function, or by using any number of plugins that wrap that feature, like Custom Post Type UI.

  25. Mark Egli says:

    Jake, Thank you immensely for this plugin.

    Just a note to say I was having some issues using `type-(custom-post-type-name).php` and had to remove a leading slash from the first array element on line 82.

    Again, Thanks!

  26. [...] Ian Stewart's Toolbox. I used Jake Goldman's plugin as a drop-in to enable post type functionality:https://cmurrayconsulting.com/sof…type-archives/ The remaining issues I see are: 1) I'm not sure if I included Jake's plugin properly. Should [...]

  27. Steven says:

    “What didn’t make it in this pre-1.0 release:
    New navigation menu widget for easily adding custom post type archives to navigation”

    This is the only thing sorely missing for me. The proposed solution to use a “Custom Link” from the menu setup doesn’t work because that link is not recognized as current. All category and page menu items get assigned the class “current-menu-item” when the user clicks and goes to that page. Custom links don’t, which screws up my menu design. :-(

    Thanks for the plug-in though. I hope this gets all added/fixed in WP core soon, but not getting my hopes up too high now that they said they want to work on documentation first.

  28. Steven says:

    BTW to all those that say it 404s, double check you’re using the registered *name* of the post type in your template filename, not the slug. I had

    register_post_type( ‘cw_news’, array(
    ‘rewrite’ => array(’slug’ => ‘news’, ‘with_front’ => false),

    ));

    and pulled my hair out why my type-news.php didn’t work since “news” is my URL slug. Changed the file to type-cw_news.php and it works.

  29. Jake Goldman says:

    Steven – I’m finding it extremely tricky to add new menu sets. The code for this particularly feature is very bleeding edge, and its tricky hacking it to add new things.

    In the mean time, there are nav menu hooks you can use to do your own “checks” and force current classes into the appropriate nav item. You can also use CSS to accomplish this in a slightly hacky way using the page body class as pre-selector.

  30. Jake Goldman says:

    Steven – right. Template use the “real” identifier of the post type, not the slug. I believe it’s the same for the “single” post type template, isn’t it? Or does that support slugs as well?

  31. Steven says:

    “You can also use CSS to accomplish this in a slightly hacky way using the page body class as pre-selector.”
    I like that idea. Thanks!

    One more issue I found with your type.php files:

    I can pass a variable from a regular page or category template file to sidebar.php which I call with get_sidebar(); In the sidebar, I can access the variable by declaring it as global. This doesn’t work from type.php. When I call the side bar from here, the variable is undefined in the sidebar, even as global.

  32. [...] at C. Murray Consulting we found a part of the [...]

  33. Roy says:

    When I try to activate your plugin, I get a fatal error:

    Parse error: syntax error, unexpected T_OBJECT_OPERATOR in /home/vhosts/gangleri.nl/httpdocs/wp-content/plugins/simple-custom-post-type-archives/simple-custom-post-type-archives.php on line 108

    I don’t know if this could be because I have already tried working with CPT’s by means of another plugin (deactivation does not prevent the error) and manually.

  34. Roy says:

    Previous problem solved, I activated PHP5. Now I’m struggling with your plugin which doesn’t seem to do anything.
    “Adds two new template files to the hierarchy”
    Well, it doesn’t, and the installation instructions say: “Create new type-etc.php”. It might be helpfull to say how. I already created a file called “blog.php” in which I know at least two queries to make it show the custom post types as an index (this already worked without your plugin). Is the trick of your plugin a certain query or just the fact that a template is called type-blog.php? I tried that, but the result is the same as without your plugin: the pagination refuses to work. The link is correct (/blog/page2), the browser goes to page 2 and the navigation link changes from “previous” to “next” posts, but the same 10 posts are loaded. I just use the previous and next links from the index.php of my theme, your instructions don’t specify what kind of previous and next codes your plugin uses.
    All in all I can’t see what it is that your plugin adds and/or the instructions to set things up correctly are unclear to me. Please be more specific for dummies.

  35. Chris says:

    Great plugin — solves a great deal of problems on my end but I am running into a problem.

    From the structure of my site I am using the default “pages” post type to create regular pages on the site like

    Now… the issue I am running into is that I want to create a custom post type for “services” and I want this to be hierarchical so that I can create sub services under it.

    My objective is to have this going on:

    and then




    So my questions is… how can I achieve this exactly as I am running into problems.

    I have tried to add a regular “page” called “services” and entered content into this area. Then adding adding all the actual services under the custom post type of “services” with a slug of “services”… this does not work.

    I also tried to just create the post type of “services” (eliminating the page for services) and adding everything under that but then I run into the problem that the public site put out the urls like this:

    In other words… In this situation I can’t get things to so up correctly.

    Please help

  36. Ernie Leseberg says:

    This works great. A nice generic solution for using multiple custom post types.

  37. Jake Goldman says:

    – you could use the plug-in as a baseline and add hierarchical post type support. You’ll need to make some changes to the code so it orders by menu_order and only spits out posts with a proper parent.

    That said, I think your approach is sort of fundamentally flawed. What you’re trying to do is better suited to a hierarchical custom taxonomy. You can assign the “services” custom taxonomy to your custom post “service” (eg) post type, and than use the custom taxonomy archives (built right into WordPress) to list out services appropriately tagged services (even if there’s only 1).

  38. Angelia says:

    Hi there Jake. Thanks for your work on this. I’ve been struggling along working with one solution or another for a site I’ve been developing for some months now, and it was quite a relief to come across your code.

    That said ;-)

    I’ve just discovered a bug that I’m hoping you can help me get rid of.

    When I have your code implemented, and I try to run a request filter to get a couple of my custom post types into author.php, I get an illegal offset error. I’ve tested inside and out, and something in there is definitely the offending code, but, I’ve yet to discover what.

    I have the site open right now for testing, but, still somewhat obfuscated, so, if you would like to see the error for yourself, and perhaps troubleshoot, please do email me. I’d love to get this sorted … obviously … heh.

  39. Angelia says:

    It seems to be triggered by the is_scpta_post_type function.

  40. Jake Goldman says:

    Angelia – Can you tell me more about the error and the setup of that author template? I have a theory about the problem.

  41. Angelia says:

    Hi Jake,
    The error is this:
    PHP Warning: Illegal offset type in isset or empty in /xxxx/xxxx/xxxx/xxxx/wp-includes/post.php on line 736
    I marked out the url for obvious reasons ;-)
    I am using a post rewind so that I can first determine the author and grab the name etc., but, I also tested without, and using just the standard author.php from twenty-ten with the same results.

    This is the filter:
    // Get custom post types into author.php
    function my_request ( $request ) {
    // Check for proper indexes.
    if ( isset ( $request['author_name'] ) )
    $request['post_type'] = array ( ‘lesson’, ‘howto’ ); //Modify the $request
    //print_r ( $request );
    return $request;
    }
    add_filter ( ‘request’, ‘my_request’ )’

    I didn’t want to overcomplicate things by bringing another issue into play, but, it also conflicts with my custom post type archive redirecting to the taxonomy.php template ( on first “add term” filter only – second “add term” redirects properly ) when using Scribu’s QMT plugin, just to keep that in mind.

    It would probably be fairly easy to determine what’s happening by looking at the scenario face to face, so, if you want to do that just shoot me an email and I’ll show you where to go okay?

  42. Chris says:

    I love this plugin and am shocked this was not available in the default 3.0 install… One key thing which I noticed though which I am hoping you can add is the ability to have the main post archive available in the default wordpress menu options.

  43. Angelia says:

    I’m assuming comments are closed? At first, I thought I was losing my mind, as I was sure that I had submitted a specific comment, and never saw it show up here, but, fearing it was true, I submitted it again … and still it is not here, so, I’m happy to know that I’m not crazy, but, perhaps you can let us know if we should move on from posting here?