Dynamic template serving, change theme_root using add_filter from current theme

Some plugins use the 'template', 'option_template' and 'option_stylesheet' to dynamically serve (alternative) wordpress templates. For example, Nathan Rice’s ServeDefaultToIESix.
For Example –

add_filter('template', 'change_theme');
add_filter('option_template', 'change_theme');
add_filter('option_stylesheet', 'change_theme');

function change_theme() 
{
    // Alternate theme
    return 'AwesomeTheme';
}

Above code only works from a wordpress plugin. What i need, is to switch to alternative template, located in one of the subfolders of the current theme(template). Examples: display an alternative HTML5 theme, serve mobile users a minimal version of the site.. etc.

I tried to use ‘theme_root’ and ‘theme_root_uri’ as below. But that isn’t working.

// Extra lines to change the theme's root.
add_filter('theme_root', 'change_theme_root');
add_filter('theme_root', 'change_theme_root_uri');
//
add_filter('template', 'change_theme');
add_filter('option_template', 'change_theme');
add_filter('option_stylesheet', 'change_theme');

function change_theme() 
{
    // Display Alternate theme
    return 'AwesomeTheme';
}

function change_theme_root()
{
    // Return the new theme root
    return WP_CONTENT_DIR . 'themes/OrigTheme/lib/AltThemes';
}

function change_theme_root_uri()
{ 
     // Return the new theme root uri
     return get_bloginfo('wpurl') . '/wp-content/themes/OrigTheme/lib/AltThemes';
}

Is this the correct way of doing it? Or does anyone know of the possible way to do so?
Thanks in advance.

Solutions Collecting From Web of "Dynamic template serving, change theme_root using add_filter from current theme"

You could also write your own simple get_template_part alias function:
The following allows 3 subfolders for template parts that sit in a theme root folder named devices.

<?php
// STYLESHEETS
    function print_device_styles( $client = 'desktop' ) 
    {
        $client = apply_filters( 'set_theme_client', $client );
        wp_enqueue_style( $client.'-css' );
    }
    add_action( 'wp_head', 'print_device_styles', 11 );

// TEMPLATE PARTS
    function get_device_template_part( $args = array( 'file' => NULL, 'suffix' => 'default', 'client' => 'desktop', 'media' => 'screen' ) 
    {
        if ( ! $args['file'] )
            wp_die( sprintf( __('You have to specify a file name if you want to load a template part with the %1$s function.', 'textdomain', '<pre>get_device_template_part()</pre>' );

        $template_path = user_trailingslashit( get_stylesheet_directory().'/devices/templates-'.$args['client'] );
        $ui_path = user_trailingslashit( get_stylesheet_directory().'/ui/css-'.$args['client'] );
        $ui_suffix = '.css'; // could be switched between '.dev.css' & '.css'

        // add styles & template directory
        if ( is_condition_mobile() ) 
        {
            $args['client'] = 'mobile';
            $args['screen'] = 'handheld';
        }
        elseif ( is_condition_tablet() )
        {
            $args['client'] = 'tablet';
            $args['screen'] = 'handheld';
        }

        // register styles
        // wp_register_style( 'mobile-css', /theme_root/ui/css-mobile/mobile.css, false 'handheld' );
        wp_register_style( $args['client'].'-css', $ui_path.$args['client'].$ui_suffix, false, $args['screen'] );

            // Requires PHP 5.3+ (for lower versions, use a plain function).
        add_filter( 'set_theme_client', function('set_theme_client') { return $args['client'];} );

            // {$template}-{$suffix}.php
        if ( file_exists( $template_path.$args['file'].'-'.$args['suffix'].'.php' ) )
        {
            require( $template_path.$args['file'].'-'.$args['suffix'].'php' );
        }
            // {$template}.php
        elseif ( file_exists( $template_path.$args['file'].'.php' ) )
        {
            require( $template_path.$args['file'].'.php' );
        }
            // {$template}-default.php
        elseif ( file_exists( $template_path.$args['file'].'-default.php' ) )
        {
            require( $template_path.$args['file'].'-default.php' );
        }
    }

// CALL THEM in a template file
// This will require a file named {$template}-{$suffix}.php from your devices folder
// based on your conditional functions that detect your devices
// If not found, it will search for a file named {$template}.php
// and if it wasn't found it will search for a file named {$template}-default.php
    get_device_template_part( array( 'file' => 'nav', 'suffix' => 'main' ) );
?>


Feel free to add your conditional device detection to: https://gist.github.com/886501

I found out that add_filter('template', ...) and add_filter('stylesheet', ...) must be called at the same time for it to work… at least in my case.

Generally Plugins and Themes have the same possiblities. But at some points, there’s a big difference. Themes come later and therefore have less power. Plus: a lot of functions only work for plugins.

What you’re trying to do is modifying core-behavior. In some places core offers hooks to even completely exchange parts of it and in others it offers hooks to extend its functionality. But keep in mind that the core never really wants its behavior to be really changed when it comes to themes (see subfolders and get_template_part()). So i would step back from switching themes ‘inside’ a theme (it’s like modding the main query) and look for some other approach:

// inside one of your template files:
do_action( 'main_nav_hook' );

// inside your functions.php
define( 'THEME_DESKTOP_DIR', user_trailingslashit( get_stylesheet_directory().'/desktop' ) );
define( 'THEME_MOBILE_DIR',  user_trailingslashit( get_stylesheet_directory().'/mobile' ) );
define( 'THEME_TABLET_DIR', user_trailingslashit( get_stylesheet_directory().'/tablett' ) );

function add_main_nav_desktop()
{
    // do stuff
    require_once( THEME_DESKTOP_DIR.'template_nav.php' );
}
function add_main_nav_mobile()
{
    // do stuff
    require_once( THEME_MOBILE_DIR.'template_nav.php' );
}
function add_main_nav_tablett()
{
    // do stuff
    require_once( THEME_TABLET_DIR.'template_nav.php' );
}

// Hook the actions
if ( is_condition_mobile() )
{
    add_action( 'main_nav_hook', 'add_main_nav_mobile' );
}
elseif ( is_condition_tablet() )
{
    add_action( 'main_nav_hook', 'add_main_nav_tablet' );
}
else
{
    add_action( 'main_nav_hook', 'add_main_nav_desktop' );
}

This is a really simple and basic approach, but it’s more likely to work.

Have you tried hooking the template_directory filter? There are also hooks for each of the individual template elements and stylesheet_directory.