Including Page Templates from a WordPress Plugin

Recently, It occurred to me while writing a plugin dealing with custom post types and taxonomies for WordPress that it’d be nice to have some custom templates to go along with it. I seemed onerous to ask the the user to add template tags to their theme to be able to display a better page then what WordPress displays by default. So I set out to make the plugin include the┬átemplate pages from the plugin directory itself. The code below is derived directly from the WordPress source code, which means it’s GPL, so feel free to use it in your GPL licensed projects.

So the first thing I needed to figured out was what functions needed to be filtered to let me add the plugin directory to the locations that WordPress checks for. I eventually found the appropriate ones by reading the source code and asking in the #wordpress IRC chat (not #wordpress-dev, that’d be the wrong chat to ask this kind of question). “get_single_template()” and “get_taxonomy_template()” are the functions of interest and they in turn call “locate_template()” which is the function we need to rewrite.

function locate_plugin_template($template_names, $load = false, $require_once = true )
{
    if ( !is_array($template_names) )
        return '';
   
    $located = '';
   
    $this_plugin_dir = WP_PLUGIN_DIR.'/'.str_replace( basename( __FILE__), "", plugin_basename(__FILE__) );
   
    foreach ( $template_names as $template_name ) {
        if ( !$template_name )
            continue;
        if ( file_exists(STYLESHEETPATH . '/' . $template_name)) {
            $located = STYLESHEETPATH . '/' . $template_name;
            break;
        } else if ( file_exists(TEMPLATEPATH . '/' . $template_name) ) {
            $located = TEMPLATEPATH . '/' . $template_name;
            break;
        } else if ( file_exists( $this_plugin_dir .  $template_name) ) {
            $located =  $this_plugin_dir . $template_name;
            break;
        }
    }
   
    if ( $load && '' != $located )
        load_template( $located, $require_once );
   
    return $located;
}

(reference: ‘function locate_template‘)

The “locate_plugin_template()” function is a direct copy of “locate_template” function but I added the $this_plugin_dir variable (using code I found on the wordpress.org forums) and added a 3rd check to the foreach loop. (Some of the code here can be removed, for example the $load check with the load_template() call, as our code won’t be invoking it but I left it to better reflect the source.)

Next we have to filter the appropriate functions to call our new function.

add_filter( 'taxonomy_template', 'get_custom_taxonomy_template' );
add_filter( 'single_template', 'get_custom_single_template' );

There’s not much in the original functions that need to be changed:

function get_custom_taxonomy_template($template)
{
    // Twenty Ten adds a 'pretty' link at the end of the excerpt. We don't need it for the taxonomy.
        remove_filter( 'get_the_excerpt', 'twentyten_custom_excerpt_more' );
    remove_filter( 'get_the_excerpt', 'twentyten_auto_excerpt_more' );
   
    $taxonomy = get_query_var('taxonomy');
   
    if ( 'custom_taxonomy_name' == $taxonomy ) {
        $term = get_query_var('term');
   
        $templates = array();
        if ( $taxonomy && $term )
                $templates[] = "taxonomy-$taxonomy-$term.php";
        if ( $taxonomy )
                $templates[] = "taxonomy-$taxonomy.php";
   
        $templates[] = "taxonomy.php";
        $template = locate_plugin_template($templates);
    }
    // return apply_filters('taxonomy_template', $template);
    return $template;
}

(reference ‘function get_taxonomy_template‘)

There’s some bonus remove_filter calls at the beginning and ‘locate_template’ is replace by ‘locate_plugin_template’. The only other thing is that we simply return the $template variable instead of using ‘apply_filters’ (I got errors when trying to apply the filter while running the filter filtering the filter :-/). There is also a check to see if we are working with our own taxonomy: The code in the if statement doesn’t really need to run again unless it’s one of our taxonomies, else it’ll just return the original $template.

The single template function filter is much the same:

function get_custom_single_template($template)
{
    global $wp_query;
    $object = $wp_query->get_queried_object();
   
    if ( 'custom_post_type_name' == $object->post_type ) {
        $templates = array('single-' . $object->post_type . '.php', 'single.php');
        $template = locate_plugin_template($templates);
    }
    // return apply_filters('single_template', $template);
    return $template;
}

(reference ‘function get_single_template‘)

Now, this isn’t a perfect solution. For example, themes have much varying structures, so building a template that’s compatible with Twenty Ten wouldn’t necessarily be compatible with any other theme. If the theme isn’t compatible, it could be bad since you’ve interrupted the fallback that the theme provides in favor of yours. You should probably include a check to see if the current theme is the theme you are targeting. I suggest you use this code in a plugin that is basically a companion plugin for your own theme or framework. Additionally, you could offer template tag functions, shortcodes, and widgets as a more robust solution.

5 thoughts on “Including Page Templates from a WordPress Plugin”

  1. Interesting. I am trying to customise the “subscriptions manager” page of “subscribe-to-comments” plugin to my website’s theme. Will this be helpful? Wasn’t sure where to add this code:
    To the functions.php of theme?
    Or, create a separate file in the plugin directory? Shall be thankful for answer.

  2. My code was intended for inclusion in a plugin, but I don’t intend anyone use my code directly… I’d have to be adjusted significantly to do whatever you need. I meant it only as an example of where to look for such info (each function has a reference link to the WordPress source code) primarily for intermediate plugin developers.

    Prof.S.Balachandran: If you have access to the theme (as a theme dev or blog owner) you need not modify anything in the manner that I am. This probably isn’t very good code for what you’re attempting :-)

  3. Hello
    Can you tell me exact way to include page template into a word press plugin.

    Thanks
    Rohit sharma

  4. Kenneth,

    Thanks for this. I’m trying to adapt it to add custom page templates for archive-cpt.php and page-cpt.php which are located in the templates folder of my plugin. I don’t think the modifications I made based on your examples are quite working. I’ve put my modifications to the core functions from http://core.trac.wordpress.org/browser/tags/3.4.1/wp-includes/template.php#L0 in a gist at https://gist.github.com/3648028 Maybe you can find the obvious mistake I’m making.

  5. Hi Jon, I didn’t see any obvious mistakes, but the gist doesn’t include the add_filter calls or your version of locate_plugin_template. I can say that my tutorial code is over 2 years old now (I’m sure you noticed that the function links are outdated at least). Core may have changed how it handles things since then. A quick look though, I see a new function `get_query_template` and some new filters there. There’s probably an opportunity for simplifying down what we are trying to do there.

    I originally intended this tutorial to be more about how to read through core’s code, and write filters against what you find. I didn’t really intend for anyone to use this particular code, so I haven’t kept it updated. Sorry I couldn’t be more help.. I’ll see about writing an updated version of this tutorial.

Leave a Reply

Your email address will not be published. Required fields are marked *

Connect with Facebook

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>