Creating a default Custom Post Template that a Theme can override

I’m in the midst of building a WordPress Plugin that adds a custom post type, for which I’d like to include a default template to display. Essentially, this is an event management plugin, and the custom post type is for the Events. There’s a handful of custom meta fields, as well as a child post type (Performances), so without a default template to display them, using it would be pretty unfriendly. But I would like theme designers to be able to create their own templates for these post types if desired.

Is there a way to use the the template provided with my plugin unless the theme provides its own template? What’s the best practice for doing so?

Read More

Edit:

Following the advice of Peter Rowell, I caught the template_redirect action and, if the post type was one of mine and a template for it did not exist in the current theme, defaulted to the plugin’s template:

class FestivityTemplates {

  public static function determineTemplate(){
    global $post;
    $standard_type = strpos($post->post_type, 'fest_');

    if(is_single() && $standard_type !== false) {
      FestivityTemplates::loadSingleTemplate($post);
    }
  }

  private static function loadSingleTemplate($post) {
    $template_name = 'single-'.$post->post_type.'.php';
    $template = locate_template(array($template_name), true);
    if(empty($template)) {
      include(WP_PLUGIN_DIR . '/Festivity/lib/templates/' . $template_name);
      exit();
    }
  }
}

add_action('template_redirect', array('FestivityTemplates', 'determineTemplate'));

Related posts

Leave a Reply

3 comments

  1. You might want to look at the routine that WP uses for this: locate_template(). It is in wp-includes/theme.php and is called from a number of functions in that file. Those functions are used by wp-includes/template-loader.php to select the correct type of template based on the current page and then walk up the theme hierarchy looking for a match.

  2. There’s also a bunch of template related filters (scroll down) you can use to ‘hijack’ the templates requests and apply your own logic to them.

    Here’s an example how to hijack the calls for single-saalon_events.php and archive-saalon_events.php and use the files from /your-plugin/templates/ folder instead:

    # Template for displaying a single event
    add_filter( 'single_template', 'saalon_events_single_template') ) ;
    function saalon_events_single_template($single_template) {
        global $post;           
        if ($post->post_type == 'saalon_events')
            $single_template = dirname( __FILE__ ) . '/templates/saalon_events_single.php';
        return $single_template;
    }
    
    # Template for displaying the events archive
    add_filter( 'archive_template', 'saalon_events_archive_template') ) ;
    function saalon_events_archive_template($archive_template) {
        if ( is_post_type_archive('saalon_events') ) // since 3.1
            $archive_template = dirname( __FILE__ ) . '/templates/saalon_events_archive.php';
        return $archive_template;
    }
    

    Resources:
    http://codex.wordpress.org/Plugin_API/Filter_Reference#Template_Filters
    http://codex.wordpress.org/Plugin_API/Filter_Reference/_single_template

    Oh and template_redirect Action looks good too!
    http://codex.wordpress.org/Plugin_API/Action_Reference#Template_Actions

    Hope this helps!

  3. I don’t know if its best practice but when i faced a similar issue i used the_content hook checked the post type to see if its my custom type and if so i returned just what i wanted.
    for example:

    add_filter('the_content','events_conetnt_display');
    function events_conetnt_display($content){
        global $post;
        if (!$post_type == "events"){
            return $content;
        }else{
            remove_filter( 'the_content', 'events_conetnt_display' );
            $events_meta = get_post_custom($post->ID);
            $new_content = '<div class="event_container">';
            $new_content .= '<div class="event_title">'.get_the_title($post->ID).'</div>';
            $new_content .= '<div class="event_description">'.apply_filters('the_content',get_the_content()).'</div>';
            $new_content .= '<div class="event_start_date">'.$events_meta['start_date'].'</div>';
            $new_content .= '<div class="event_end_date">'.$events_meta['start_end'].'</div>';
            //...
            //whatever
            //...
            add_filter('the_content','events_conetnt_display');
            return $new_content;
        }
    }
    

    and my plugin had an option to let the user decide if he whats to use the_content Hook or if he has a custom template for that something like:

    if (get_option('use_content_template')){
      add_filter('the_content','events_conetnt_display');
    }