WP - Use file in plugin directory as custom Page Template?

Is it possible for a file in the plugin directory to be used as a custom Page Template?

Also, how do you make a plugin create a page?

I'm developing a plugin for a client based on a theme, he wants this plugin to make sales pages while being able to use his theme on the homepage. This is a product that I'm making for him to market so it needs to be automated all through the plugin.

Is this possible?

EDIT

I have the activation/deactivation hooks in my plugins main file, and it's not working. Here's the code:

$filename = __FILE__;

register_activation_hook($filename, 'superActivation');
register_deactivation_hook($filename, 'superDeactivation');

global $myFile; global $fh; global $stringData; global $filename;

$myFile = "testFile.txt";
$stringData = "Testing\n";
$fh = fopen($myFile, 'w') or die("can't open file");

function superActivation() {
    global $myFile; global $fh; global $stringData; global $filename;
    fwrite($fh, $stringData);
    fclose($fh);
}

function superDeactivation() {
    $myFile = "testFile.txt";
    unlink($myFile);
}

Solution 1:

You can do this with the template_redirect hook. Here's my code to manually replace the template for a custom post type with one in the theme if there isn't one in the template folder. Put this in your plugin file and then put a folder underneath your plugin called themefiles with your default theme files.

//Template fallback
add_action("template_redirect", 'my_theme_redirect');

function my_theme_redirect() {
    global $wp;
    $plugindir = dirname( __FILE__ );

    //A Specific Custom Post Type
    if ($wp->query_vars["post_type"] == 'product') {
        $templatefilename = 'single-product.php';
        if (file_exists(TEMPLATEPATH . '/' . $templatefilename)) {
            $return_template = TEMPLATEPATH . '/' . $templatefilename;
        } else {
            $return_template = $plugindir . '/themefiles/' . $templatefilename;
        }
        do_theme_redirect($return_template);

    //A Custom Taxonomy Page
    } elseif ($wp->query_vars["taxonomy"] == 'product_categories') {
        $templatefilename = 'taxonomy-product_categories.php';
        if (file_exists(TEMPLATEPATH . '/' . $templatefilename)) {
            $return_template = TEMPLATEPATH . '/' . $templatefilename;
        } else {
            $return_template = $plugindir . '/themefiles/' . $templatefilename;
        }
        do_theme_redirect($return_template);

    //A Simple Page
    } elseif ($wp->query_vars["pagename"] == 'somepagename') {
        $templatefilename = 'page-somepagename.php';
        if (file_exists(TEMPLATEPATH . '/' . $templatefilename)) {
            $return_template = TEMPLATEPATH . '/' . $templatefilename;
        } else {
            $return_template = $plugindir . '/themefiles/' . $templatefilename;
        }
        do_theme_redirect($return_template);
    }
}

function do_theme_redirect($url) {
    global $post, $wp_query;
    if (have_posts()) {
        include($url);
        die();
    } else {
        $wp_query->is_404 = true;
    }
}

Solution 2:

You CAN add page templates from a plugin very easily by manipulating the page cache.

To customise, simply edit the following code block within the __construct method;

   $this->templates = array(
       'goodtobebad-template.php'     => 'It\'s Good to Be Bad',
   );

This is designed for a plugin (the template files are searched for in the root directory of the plugin). This can be changed if desired - check out my full tutorial http://www.wpexplorer.com/wordpress-page-templates-plugin/ for greater detail on this solution. These files are also in exactly the same format as if they were to be included directly in a theme.

Full code;

class PageTemplater {

    /**
     * A Unique Identifier
     */
     protected $plugin_slug;

    /**
     * A reference to an instance of this class.
     */
    private static $instance;

    /**
     * The array of templates that this plugin tracks.
     */
    protected $templates;


    /**
     * Returns an instance of this class. 
     */
    public static function get_instance() {

            if( null == self::$instance ) {
                    self::$instance = new PageTemplater();
            } 

            return self::$instance;

    } 

    /**
     * Initializes the plugin by setting filters and administration functions.
     */
    private function __construct() {

            $this->templates = array();


            // Add a filter to the attributes metabox to inject template into the cache.
            add_filter(
                'page_attributes_dropdown_pages_args',
                 array( $this, 'register_project_templates' ) 
            );


            // Add a filter to the save post to inject out template into the page cache
            add_filter(
                'wp_insert_post_data', 
                array( $this, 'register_project_templates' ) 
            );


            // Add a filter to the template include to determine if the page has our 
            // template assigned and return it's path
            add_filter(
                'template_include', 
                array( $this, 'view_project_template') 
            );


            // Add your templates to this array.
            $this->templates = array(
                    'goodtobebad-template.php'     => 'It\'s Good to Be Bad',
            );

    } 


    /**
     * Adds our template to the pages cache in order to trick WordPress
     * into thinking the template file exists where it doens't really exist.
     *
     */

    public function register_project_templates( $atts ) {

            // Create the key used for the themes cache
            $cache_key = 'page_templates-' . md5( get_theme_root() . '/' . get_stylesheet() );

            // Retrieve the cache list. 
            // If it doesn't exist, or it's empty prepare an array
            $templates = wp_get_theme()->get_page_templates();
            if ( empty( $templates ) ) {
                    $templates = array();
            } 

            // New cache, therefore remove the old one
            wp_cache_delete( $cache_key , 'themes');

            // Now add our template to the list of templates by merging our templates
            // with the existing templates array from the cache.
            $templates = array_merge( $templates, $this->templates );

            // Add the modified cache to allow WordPress to pick it up for listing
            // available templates
            wp_cache_add( $cache_key, $templates, 'themes', 1800 );

            return $atts;

    } 

    /**
     * Checks if the template is assigned to the page
     */
    public function view_project_template( $template ) {

            global $post;

            if (!isset($this->templates[get_post_meta( 
                $post->ID, '_wp_page_template', true 
            )] ) ) {

                    return $template;

            } 

            $file = plugin_dir_path(__FILE__). get_post_meta( 
                $post->ID, '_wp_page_template', true 
            );

            // Just to be safe, we check if the file exist first
            if( file_exists( $file ) ) {
                    return $file;
            } 
            else { echo $file; }

            return $template;

    } 


} 

add_action( 'plugins_loaded', array( 'PageTemplater', 'get_instance' ) );

Check out my tutorial on this for more info.

http://www.wpexplorer.com/wordpress-page-templates-plugin/

I hope this helps you in what you want to do :)

Solution 3:

the code david posted above almost works for me. but it seems to blanket over all posts and pages for me. This code below works great for adding a template to a single post type that is created by my main plugin file

function get_book_post_type_template($single_template) {
 global $post;

 if ($post->post_type == 'books') {
      $single_template = dirname( __FILE__ ) . '/themefiles/single-books.php';
 }
 return $single_template;
}

add_filter( "single_template", "get_book_post_type_template" ) ;

but I'm having trouble getting it to work with a custom page templates that don't have a post_type or has a post_type = page for instance lets say the custom page is an auxiliary member login page to see my custom posts. in my case this file is called myaccount.php and i've included it in a subfolder within my plugin folder named themefiles.

//Add Page and Post Template Files to Current Theme 
add_action("template_redirect", 'my_account_redirect');

function my_account_redirect() {
    global $wp;

    //Set myAccount Custom Page Template 
    if (isset($wp->query_vars['pagename'] ) == "myaccount") {
        $templatefilename = 'myAccount.php';
        if (file_exists(dirname( __FILE__ ) . '/themefiles/' . $templatefilename)) {
            $return_template = dirname( __FILE__ ) . '/themefiles/' . $templatefilename;
        }
        do_account_redirect($return_template);
    } 
} 

//Finishing setting templates 
function do_account_redirect($url) {
    global $post, $wp_query;

    if (have_posts()) {
        include($url);
        die();
    } else {
        $wp_query->is_404 = true;
    }
}

when i do the above code the myaccount template shows up on all pages except for home which i believe is because it is set to a blogroll instead of a static page