I’ve been looking around for the past couple hours for a way to create a custom page template from within a plugin but haven’t had any luck yet.

What I am specifically trying to do is add an option to the list of available page templates when editing a page, and not using some other method like if( is_page( 'page-slug' ) ) { /* etc */ }

Is there a global variable I can modify to do this?

Edit:

I’m using this code currently, based on the link @m0r7if3r gave me in a comment, the problem is it will run this function when viewing the page, but not when editing the page (to populate the dropdown list with the page templates):

class JW_SiteGrader {

    private static $instance;

    private function __construct() {


        add_action( 'template_redirect', array( &$this, 'sitegrader_template_redirect' ), 20 );

    }

    public static function getInstance() {

        // Return the class data in a Singleton fashion
        if (self::$instance == null)
            self::$instance = new JW_SiteGrader();
        return self::$instance;

    }

    public function sitegrader_template_redirect() {

        add_filter( 'page_template', array( &$this, 'sitegrader_page_template' ), 10, 1 );

    }

    public function locate_plugin_template( $template_names, $load = false, $require_once = true ) {

        if ( !is_array( $template_names ) )
            return '';

        $located = '';

        $this_plugin_dir = WP_PLUGIN_DIR . "https://wordpress.stackexchange.com/" . str_replace( basename( __FILE__ ), '', plugin_basename( __FILE__ ) );

        foreach ( $template_names as $template_name ) {

            if ( !$template_name )
                continue;

            if ( file_exists( STYLESHEETPATH . "https://wordpress.stackexchange.com/" . $template_name ) ) {

                $located = STYLESHEETPATH . "https://wordpress.stackexchange.com/" . $template_name;
                break;

            } else if ( file_exists( TEMPLATEPATH . "https://wordpress.stackexchange.com/" . $template_name ) ) {

                $located = TEMPLATEPATH . "https://wordpress.stackexchange.com/" . $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;
    }

    public function sitegrader_page_template( $template ) {

        $object = get_queried_object();

        if ( 'page' == $object->post_type ) {

            // New 
            $templates[] = "page-sitegrader.php";
            // Like in core
            $templates[] = "page-{$object->post_type}.php";
            $templates[] = "page.php";

            return locate_template( $templates );  

        }

        // return apply_filters('page_template', $template);
        return $template;
    }

}

Edit 2:

It seems that this functionality will be released in a future update, I read quite a few trac tickets about this and there has been some discussion but no real answer (hoping for 3.4). Will list trac tickets URLs here in a bit.

Edit 3:

The code above works, BUT, the only issue I’m having at this point is there’s no template being added to the dropdown list when editing/adding a new page. I’m trying a few things and will update my question soon.

6 s
6

Filters? Anyone?

There’s no filter there to help: page_template_dropdown($template); is used to build the drop down and it’s not filterable.

Sneaking into the Templates Array?

To build the drop downs contents, the core meta box uses get_page_templates(). From inside, the function looks like the following:

$themes = get_themes();
$theme = get_current_theme();
$templates = $themes[$theme]['Template Files'];

But when looking into get_themes(); then there seams to be no possibility to intercept the list of templates. Further more we have the problem, that there’s no chance to get a template from outside the themes directory…

…Faking a Theme!

The theory and it’s drawbacks…

You can use register_theme_directory() to add an additional theme directory where you can place templates. So the easiest thing would be to register your plugin as themes folder too:

// Register during init hook:
register_theme_directory( plugin_dir_path( __FILE__ ).'templates' );

Note: This is where I’m not sure if it will work.

During plugin activation:
Then you should place a style.css.php file inside your templates folder. This would allow you to add variables to your file. This variable would then be the parent theme. And the parent theme should simply be the currently active theme. Then update your active theme to your plugin.

Drawback #2: About the »Appearance« UI… Maybe add a note that this “Theme” is not meant to be used as actual Theme. I leave the rest of »Avoid activating this theme« stuff up to your imagination. Anyway: It should work.

Drawback #2: This trick will successfully avoid child themes. You’re allowed to have one parent theme. Nothing more.

Leave a Reply

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