define('DISALLOW_FILE_EDIT', true); Uncategorized – unFocus Projects – Kevin Newman and Ken Newman

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.

[cc lang=”php”]
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;
}
[/cc](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.
[cc lang=”php”]
add_filter( ‘taxonomy_template’, ‘get_custom_taxonomy_template’ );
add_filter( ‘single_template’, ‘get_custom_single_template’ );
[/cc]

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

[cc lang=”php”]
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;
}
[/cc](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:

[cc lang=”php”]
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;
}
[/cc](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.