2

I am developing a simple child theme based on twentyeleven. But i notice twentyeleven has a showcase and sidebar template when you add a new page. How can i disable these templates from functions.php? I want to show only the templates from the child theme

regards

3
  • 1
    You have to populate the global $wp_object_cache with your page templates before WordPress does it, because there are no filters. Tricky.
    – fuxia
    Commented Apr 19, 2013 at 9:10
  • @toscho so $wp_themes doesn't work?
    – alex
    Commented Apr 19, 2013 at 9:39
  • That’s a variable, not a filter. See the linked trac discussion for the current state. It is possible, but not simple and probably not forward-compatible.
    – fuxia
    Commented Apr 19, 2013 at 9:41

3 Answers 3

4

As @toscho mentioned it is tricky. But it is doable.

Basically you need to find action hooks called before and after the template dropdown is generated and call ob_start() to initiate output buffering and ob_get_clean() to capture the buffered HTML. The 'submitpage_box' and 'edit_page_form' hooks will work for this, respectively.

After you've captured the buffered HTML you need to get the list of child page templates which unfortunately requires you duplicate a lot of code from WordPress because core doesn't make make the code reusable to get the list of page templates for only the child template. You can find the code needed in the method get_child_page_templates() in the code below which is almost completely a composition of the needed code from WordPress core.

Once you have both the buffered HTML you need to get the list of child page templates required to remove the <options>s from the <select> that are not page templates from the child theme but being sure to include the 'default' option. You can do this through a complex pair of regular expressions, the preg_match() and preg_match_all() functions and a foreach() loop where you add only the HTML for the child theme's templates to your list and then merge them back into the HTML that came before and after the list of <option>s

Package all that up into a called I called Omit_Parent_Theme_Page_Templates which you can drop into your theme's functions.php file or include in a plugin your are building and viola, gone are the parent page templates.

Here's the source code for my Omit_Parent_Theme_Page_Templates class that you need:

<?php
class Omit_Parent_Theme_Page_Templates {
  function __construct() {
    add_action( 'submitpage_box', array( $this, '_submitpage_box' ) );
    add_action( 'edit_page_form', array( $this, '_edit_page_form' ) );
  }
  function _submitpage_box() {
    ob_start();
  }
  function _edit_page_form() {
    $html = ob_get_clean();
    $select_regex = '<select\s*name="page_template"\s*id="page_template".*?>';
    preg_match( "#^(.*{$select_regex})(.*?)(</select>.*)$#sm", $html, $outer_match );
    preg_match_all( "#(<option\s*value='([^']+)'.*?>(.*?)</option>)#sm", $outer_match[2], $inner_matches, PREG_SET_ORDER );
    $child_page_templates = $this->_get_child_page_templates();    
    foreach( $inner_matches as $index => $matches )
      if ( isset( $child_page_templates[$matches[2]] ) )
        $child_page_templates[$matches[2]] = $inner_matches[$index][0];

    $html = $outer_match[1] . implode( "\n", $child_page_templates ). $outer_match[3];
    echo $html;
  }
  private function _get_child_page_templates() {
    $child_page_templates = array( 'default' => true );
    $files = wp_get_theme()->get_files( 'php', 1 );
    foreach ( $files as $file => $full_path ) {
      if ( ! preg_match( '|[^]]Template Name:(.*)$|mi', file_get_contents( $full_path ), $header ) )
        continue;
      $child_page_templates[ $file ] = true;
    }
    return $child_page_templates;
  }

}
new Omit_Parent_Theme_Page_Templates();

Because WordPress doesn't provide hooks for what you want this code is much more complex than we'd like. It uses a technique – the modification of HTML captured in the output buffer – that is generally "a hack." But given the nature of what we're changing I don't think it's very likely to break unless WordPress rearranges the id or name attributes of the <select> or fundamentally changes how page templates work, neither of which are very likely.

Of course if some other plugin decides to modify page templates too using the same technique but in an incompatible way then that plugin might break this code or vice versa.

But I doubt any of that is too likely and if you are using for your own site I would not worry about it at all however I would be slightly more worried about releasing the code in a widely distributed plugin, but only slightly. :)

UPDATE:

Ah what the heck. Decided to turn this into a plugin and publish this in the WordPress plugin repository, click here to access it. I guess I wasn't worried enough not to publish it. :)

3

Since 3.9 you can do it easily, using the theme_page_templates filter.

You just need to use something like this:

add_filter( 'theme_page_templates', 'my_remove_page_template' );
function my_remove_page_template( $pages_templates ) {
    unset( $pages_templates['onecolumn-page.php'] );
    return $pages_templates;
}

Further reading:

1
  • Answers should be more than plain links. They should actually be an answer instead of a route where someone will maybe find an answer. Please help preventing link rot, edit your answer and provide the needed information that helps the OP as well as later visitors to solve their problem.
    – kaiser
    Commented Jun 18, 2014 at 0:08
0

To generate the dropdown, WordPress calls page_template_dropdown(), which calls get_page_templates(), which (since WP 3.4 at least) calls wp_get_theme()->get_page_templates() (i.e. WP:Theme::get_page_templates() for the active theme).

The latter method loops through the actual theme .php files to find any file with a Template Name: header and caches the result. No actions or filters are provided at any point so far.

As an alternative to Mike's answer, we could modify the cache. Less hackish than regex-ing the output, but still hackish. Lets hook it onto the admin_head-post.php action, so it only runs when the post editor is shown.

In the example below the template page-home.php is removed from the list.

add_action("admin_head-post.php", "my_admin_head_post");

function my_admin_head_post() {
    // Run template fetching once so the template list is cached.
    $templates = wp_get_theme()->get_page_templates();

    // Generate cache hash, just like WP_Theme::__construct() does.
    $cache_hash = md5(WP_CONTENT_DIR."/themes/".get_stylesheet());
    $cache_key  = "page_templates-".$cache_hash;

    // Remove unwanted templates from the list and save changes.
    unset($templates['page-home.php']);
    wp_cache_replace($cache_key, $templates, "themes", 1800);
}

For your specific problem, where you want to remove the parent theme's templates from the list, luckily there's a public method WP_Theme::parent() that fetches the theme's parent as another WP_Theme object. The following (untested) code should do the trick:

function my_admin_head_post() {
    $theme  = wp_get_theme();
    $parent = $theme->parent();

    if(!is_a($parent, "WP_Theme"))
        return;

    $templates        = $theme->get_page_templates();
    $parent_templates = $parent->get_page_templates();

    $cache_hash = md5(WP_CONTENT_DIR."/themes/".get_stylesheet());
    $cache_key  = "page_templates-".$cache_hash;

    foreach($templates as $id => $template) {
        if(isset($parent_templates[$id]))
            unset($templates[$id]);
    }

    wp_cache_replace($cache_key, $templates, "themes", 1800);
}

Note: since a removed template can no longer be chosen from the dropdown, updating/publishing an existing page that previously used the template will reset the page's template.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.