The better, cleaner solution would be not to use custom page templates to define page layouts*, but rather to use custom post meta to define page layouts.
To implement:
- Create a Theme Option for Default static page layout, that includes all possible page layout options
- Create a
_page_layout
custom post meta for Page Layout, that includes a "Default Layout" option, as well as all possible layout options used in the above Theme Option
- Define a
body_class
filter to output a layout-$layout
class to the HTML <body>
tag, via the body_class()
template tag, where $layout
is defined as so:
- If
'default' == '_page_layout'
, use the Theme Option value
- If
'$layout' == '_page_layout'
, use $layout
Proof-of-concept code from Oenology follows (note: modified from original, since I apply custom post meta for layouts for static pages, single blog posts, and archive index pages):
Adding Meta Boxes
/**
* Add Layout Meta Box
*
* @uses __()
* @uses add_meta_box()
*/
function oenology_add_layout_meta_box( $post ) {
global $wp_meta_boxes;
$context = 'side'; // 'normal', 'side', 'advanced'
$priority = 'default'; // 'high', 'core', 'low', 'default'
add_meta_box(
'oenology_layout',
__( 'Static Page Layout', 'oenology' ),
'oenology_layout_meta_box',
'page',
$context,
$priority
);
}
// Hook meta boxes into 'add_meta_boxes'
add_action( 'add_meta_boxes-page', 'oenology_add_layout_meta_box' );
/**
* Define Layout Meta Box
*
* Define the markup for the meta box
* for the "layout" post custom meta
* data. The metabox will consist of
* radio selection options for "default"
* and each defined, valid layout
* option for single blog posts or
* static pages, depending on the
* context.
*
* @uses oenology_get_option_parameters() Defined in \functions\options.php
* @uses checked()
* @uses get_post_custom()
*/
function oenology_layout_meta_box() {
global $post;
$option_parameters = oenology_get_option_parameters();
$custom = ( get_post_custom( $post->ID ) ? get_post_custom( $post->ID ) : false );
$layout = ( isset( $custom['_oenology_layout'][0] ) ? $custom['_oenology_layout'][0] : 'default' );
$valid_layouts = $option_parameters['default_static_page_layout']['valid_options'];
?>
<p>
<input type="radio" name="_oenology_layout" <?php checked( 'default' == $layout ); ?> value="default" />
<label>Default</label><br />
<?php foreach ( $valid_layouts as $valid_layout ) { ?>
<input type="radio" name="_oenology_layout" <?php checked( $valid_layout['name'] == $layout ); ?> value="<?php echo $valid_layout['name']; ?>" />
<label><?php echo $valid_layout['title']; ?> <span style="padding-left:5px;"><em><?php echo $valid_layout['description']; ?></em></span></label><br />
<?php } ?>
</p>
<?php
}
/**
* Validate, sanitize, and save post metadata.
*
* Validates the user-submitted post custom
* meta data, ensuring that the selected layout
* option is in the array of valid layout
* options; otherwise, it returns 'default'.
*
* @uses oenology_get_option_parameters() Defined in \functions\options.php
* @uses array_key_exists()
* @uses update_post_meta()
*/
function oenology_save_layout_post_metadata(){
global $post;
$option_parameters = oenology_get_option_parameters();
$valid_layouts = array();
if ( 'post' == $post->post_type ) {
$valid_layouts = $option_parameters['default_single_post_layout']['valid_options'];
} else if ( 'page' == $post->post_type ) {
$valid_layouts = $option_parameters['default_static_page_layout']['valid_options'];
}
$layout = ( isset( $_POST['_oenology_layout'] ) && array_key_exists( $_POST['_oenology_layout'], $valid_layouts ) ? $_POST['_oenology_layout'] : 'default' );
update_post_meta( $post->ID, '_oenology_layout', $layout );
}
// Hook the save layout post custom meta data into
// publish_{post-type}, draft_{post-type}, and future_{post-type}
add_action( 'draft_page', 'oenology_save_layout_post_metadata' );
add_action( 'future_post', 'oenology_save_layout_post_metadata' );
add_action( 'future_page', 'oenology_save_layout_post_metadata' );
Determining Current Page Layout
/**
* Get Current Page Layout
*/
function oenology_get_current_page_layout() {
// Use default layout for 404 pages
if ( is_404() ) {
return 'default';
}
// Otherwise, determine appropriate layout
$layout = '';
global $post;
global $oenology_options;
$custom = ( get_post_custom( $post->ID ) ? get_post_custom( $post->ID ) : false );
$custom_layout = ( isset( $custom['_oenology_layout'][0] ) ? $custom['_oenology_layout'][0] : 'default' );
if ( ! is_admin() ) {
if ( is_attachment() ) {
$layout .= 'attachment';
}
else if ( is_page() ) {
if ( 'default' == $custom_layout ) {
$layout .= $oenology_options['default_static_page_layout'];
} else {
$layout .= $custom_layout;
}
}
}
else if ( is_admin() ) {
if ( 'attachment' == $post->post_type ) {
$layout .= 'attachment';
}
else if ( 'page' == $post->post_type ) {
if ( 'default' == $custom_layout ) {
$layout .= $oenology_options['default_static_page_layout'];
}
else {
$layout .= $custom_layout;
}
}
}
return $layout;
}
Filtering Body Class
/**
* Add layout CSS classes to the HTML body tag
*
* Filter Hook: body_class
*
* Filter 'body_class' to include
* classes for page layout.
*
* @uses oenology_get_current_page_layout() Defined in \functions\custom.php
*
* @since Oenology 2.0
*/
function oenology_filter_body_class( $classes ) {
$layout = 'layout-';
$layout .= oenology_get_current_page_layout();
$classes[] = $layout;
return $classes;
}
// Hook custom classes into 'body_class'
add_filter( 'body_class', 'oenology_filter_body_class' );
*Custom page templates really weren't intended for defining layouts, but rather for defining custom page content.