Wordpress - Add custom template page programmatically

The article linked is on the right track, but i'll make it more simple for you.. ;)

add_filter( 'page_template', 'catch_plugin_template' );

function catch_plugin_template( $template ) {
    if( 'tp-file.php' == basename( $template ) )
        $template = WP_PLUGIN_DIR . '/yourpluginname/tp-file.php';
    return $template;
}

The filter basically looks to see if your special page template is set for the current page, if it is, updates the path to point at your plugins template instead.

Just be sure the path is right, else you'll be seeing include errors... :)

Follow-up #1

Ok first problem is that WordPress validates any template set as a page template, ie. it checks to see if the file is in the theme folder, and is a valid template file, if not it skips past it and includes a more generic template, like page.php ...

However, this doesn't change the fact that the meta field still holds the value of your custom template, and also is_page_template( 'tp-file.php' ) will correctly return true if used in place of my previous conditional statement, eg..

// Filter page template
add_filter('page_template', 'catch_plugin_template');

// Page template filter callback
function catch_plugin_template($template) {
    // If tp-file.php is the set template
    if( is_page_template('tp-file.php') )
        // Update path(must be path, use WP_PLUGIN_DIR and not WP_PLUGIN_URL) 
        $template = WP_PLUGIN_DIR . '/tp-test/tp-file.php';
    // Return
    return $template;
}

NOTE: I've switched the code to use WP_PLUGIN_DIR as the WP_PLUGIN_URL constant is not suitable for paths... (includes must use a path, not a URL).

One issue, and this really isn't something you can fix, is that when viewing the page from the admin area, when editting the page, the template won't be listed as the active template, and saving changes could of course change the active template. There's much we can do there, the page template dropdown is generated from a function that scans theme files, there's no hooks in place that i can see that would allows us to extend the list with plugin templates.

Personally what i'd suggest, as a work-around would be to save an additional meta field with every page that created using your special plugin page, then add a hook onto save_post or wp_insert_post_data and check if this meta field exists, if it does, also check the page template is set to tp-file.php, and if not, update it to tp-file.php. The additional meta field would just be a flag so to speak, to indicate which pages need to have your plugin template attached.

Here's your plugin working in it's most basic form(yes i tested)... :)

<?php
/*
  Plugin Name: TP Test Plugin
  Plugin URI: 
  Description: TP Test Plugin
  Version: 1.0.0
  Author: 
  Author URI: 
*/

global $wp_version;

if( version_compare( $wp_version, "2.9", "<" ) )
    exit( 'This plugin requires WordPress 2.9 or newer. <a href="http://codex.wordpress.org/Upgrading_WordPress">Please update!</a>' );

// Add callback to admin menu
add_action('admin_menu', 'create_tp_menu');

// Callback to add menu items
function create_tp_menu() {
    add_management_page('TP Test', 'TP Test', 'manage_options', 'tp-teste', 'wp_tp_test_fnc' );
}

function wp_tp_test_fnc() {

    //include('tp-form-file.php');

    if( !empty( $_POST['tp_name'] ) ) {
        $tp_name = $_POST['tp_name'];

        global $user_ID;
        $new_post = array(
            'post_title' => $tp_name,
            'post_content' => 'Some text',
            'post_status' => 'publish',
            'post_date' => date('Y-m-d H:i:s'),
            'post_author' => $user_ID,
            'post_type' => 'page',
        );
        $post_id = wp_insert_post($new_post);

        if( !$post_id )
            wp_die('Error creating template page');
        else
            update_post_meta( $post_id, '_wp_page_template', 'tp-file.php' );
        /*
        $pt = get_page_templates();

        $pt['TP file test'] = WP_PLUGIN_URL . '/tp-test/tp-file.php';

        echo "<pre>";
        print_r($pt);
        echo "</pre>";
        */
    }   
    ?>
    <fieldset style="margin: 50px 100px;background-color: #cccccc;padding: 30px;border: 1px solid #ccc">
         <legend style="background-color: #ccccff;padding: 20px;font-weight: bold;font-size: 18px">Create Template Page</legend>
         <form name="frm_main" action="" method="POSt">
              <input class="text" type="text" name="tp_name" size="50" />
              <br />
              <input class="button" type="submit" value="Create Template Page" name="btn_submit" />
         </form>
    </fieldset>
    <?php

}


// Filter page template
add_filter('page_template', 'catch_plugin_template');

// Page template filter callback
function catch_plugin_template($template) {
    // If tp-file.php is the set template
    if( is_page_template('tp-file.php') )
        // Update path(must be path, use WP_PLUGIN_DIR and not WP_PLUGIN_URL) 
        $template = WP_PLUGIN_DIR . '/tp-test/tp-file.php';
    // Return
    return $template;
}

Hope that helps clear things up.. :)


The page_template filter is deprecated now. (http://adambrown.info/p/wp_hooks/hook/page_template)

Try using single_template (or archive_template for archive templates) instead.

Based on @t31os's answer:

// Filter page template
add_filter('single_template', 'catch_plugin_template');

// Page template filter callback
function catch_plugin_template($template) {
    // If tp-file.php is the set template
    if( is_page_template('tp-file.php') )
        // Update path(must be path, use WP_PLUGIN_DIR and not WP_PLUGIN_URL) 
        $template = WP_PLUGIN_DIR . '/tp-test/tp-file.php';
    // Return
    return $template;
}