Skip to content

Instantly share code, notes, and snippets.

@Maximilianos
Created February 12, 2016 14:51
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Maximilianos/b21020ecceaa67650d01 to your computer and use it in GitHub Desktop.
Save Maximilianos/b21020ecceaa67650d01 to your computer and use it in GitHub Desktop.
Register custom page templates in WordPress programmatically in a plugin or in the theme without even requiring real template files.
<?php
/**
*
*/
/**
* Class MCF_Templater
*
* Based on work by Harri Bell-Thomas <https://github.com/HarriBellThomas>
* @link http://www.wpexplorer.com/wordpress-page-templates-plugin/
*
* @author Max GJ Panas <https://github.com/Maximilianos>
*/
class MCF_Templater {
/**
* The array of templates that this plugin tracks.
*/
protected $templates;
/**
* The directory where the templates are located
*/
protected $template_dir;
/**
* MCF_Templates constructor.
*
* Initializes the plugin by setting filters and administration functions.
*
* @param array $templates
* @param string $template_dir
*/
public function __construct( $templates = array(), $template_dir = null ) {
$this->templates = $templates;
$this->template_dir = $template_dir;
add_filter( 'page_attributes_dropdown_pages_args', array( &$this, 'add_to_wp_cache' ) );
add_filter( 'wp_insert_post_data', array( &$this, 'add_to_wp_cache' ) );
// interop with Advanced Custom Fields
add_filter( 'acf/location/rule_values/page_template', array( &$this, 'add_to_acf_rules' ) );
if ( $this->template_dir ) {
// 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, 'get_template_path' ) );
}
}
/**
* Adds the templates to the pages cache in order to trick WordPress
* into thinking the template files exists where they do not really
* exist.
*
* @param $attributes
*
* @return mixed
*/
public function add_to_wp_cache( $attributes ) {
// 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 $attributes;
}
/**
* Adds the templates to the Advanced Custom Fields
* Page Template rule
*
* @param array $choices
*
* @return array
*/
public function add_to_acf_rules( $choices = array() ) {
return array_merge( (array) $choices, (array) $this->templates );
}
/**
* Checks if the template is assigned to the page
* and makes sure to render the correct template
*
* @param $template
*
* @return string
*/
public function get_path( $template ) {
global $post;
$page_template = get_post_meta( $post->ID, '_wp_page_template', true );
if ( ! isset( $this->templates[$page_template] ) ) {
return $template;
}
$file = $this->template_dir . $page_template;
return file_exists( $file ) ? $file : $template;
}
}
@Maximilianos
Copy link
Author

To use it just include it in a plugin or a theme and initialize it giving it your custom templates and optionally where those templates are located

A rough example:

// wp-content/plugins/templatz/templatz.php
/*
Plugin Name: Extra Templatz
*/
include 'class-templater.php'

add_action( 'plugins_loaded', function () {
  // wp-content/plugins/templatz/templates/
  $template_dir = path_join( plugin_dir_path( __FILE__ ), '/templates/' );

  new MCF_Templater( array(
    'template-one.php' => 'Template One',
    'template-two.php' => 'Template Two'
  ), $template_dir );
} );

@Maximilianos
Copy link
Author

The actual reason I wrote this is for a (headless) WordPress setup where the site is not rendered by the theme, but by a js based client somewhere else.

I wanted to leverage some of the builtin WordPress functionality surrounding Page Templates (like how they work well as location rules for Advanced Custom Fields), but I did not want to actually create them in the theme as I was creating them already in the js client. So, I use the above code simply to "trick" WordPress into believing my custom page templates exist and are valid.

The way I actually use the above is in a plugin where I simply call it something like so:

// Note that there is no $template_dir passed to the constructor.
// Here we only care about tricking WordPress into thinking our templates exist.
// We are going to do the actual rendering of the templates in the js app.
new MCF_Templater( array(
  'Home' => 'Homepage Template',
  'Contact' => 'Contact Template',
  ...
) );

Then in the js client which leverages the wp-json api, I simply check the page's template key and try to match it against one the defined templates in my js app.

@BRiera
Copy link

BRiera commented Nov 23, 2016

Won't run in Wordpress 4.6.1 custom plugin

@certainlyakey
Copy link

True, doesn't work on 4.7.x either.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment