¿Cómo agregar el metabox de Atributos de Página y Plantillas de Página al editor de Posts?
(Nota del moderador: El título original era "¿Cómo puedo agregar el selector 'Atributos de Página' y/o 'Atributos de Página > Plantilla' al editor de POSTS?")
WordPress actualmente solo permite asignar una "plantilla" a las Páginas (es decir, post_type=='page'
). Me gustaría extender esta funcionalidad a los Posts también (es decir, post_type=='post'
).
¿Cómo puedo agregar el metabox "Atributos de Página" y más específicamente, el selector de plantillas al editor de posts?
Asumo que este es código que colocaré en mi functions.php
para mi tema.
ACTUALIZACIÓN: He logrado agregar el menú desplegable de plantillas codificadas manualmente a mi editor de posts, simplemente agregando el HTML del select box a mi caja de opciones meta personalizadas existente. Este es el código que estoy usando para eso...
add_meta_box('categorydiv2', __('Opciones del Post'), 'post_categories_meta_box_modified', 'post', 'side', 'high');
Y aquí está la función que escribe las opciones y el cuadro de selección de plantilla...
//agrega la caja de categorías personalizada
function post_categories_meta_box_modified() {
global $post;
if( get_post_meta($post->ID, '_noindex', true) ) $noindexChecked = " checked='checked'";
if( get_post_meta($post->ID, '_nofollow', true) ) $nofollowChecked = " checked='checked'";
?>
<div id="categories-all" class="ui-tabs-panel">
<ul id="categorychecklist" class="list:category categorychecklist form-no-clear">
<li id='noIndex' class="popular-category"><label class="selectit"><input value="noIndex" type="checkbox" name="chk_noIndex" id="chk_noIndex"<?php echo $noindexChecked ?> /> noindex</label></li>
<li id='noFollow' class="popular-category"><label class="selectit"><input value="noFollow" type="checkbox" name="chk_noFollow" id="chk_noFollow"<?php echo $nofollowChecked ?> /> nofollow</label></li>
</ul>
<p><strong>Plantilla</strong></p>
<label class="screen-reader-text" for="page_template">Plantilla del Post</label><select name="page_template" id="page_template">
<option value='default'>Plantilla Predeterminada</option>
<option value='template-wide.php' >Sin Barra Lateral</option>
<option value='template-salespage.php' >Página de Ventas</option>
</select>
</div>
<?php
}
Y finalmente, el código para capturar los valores seleccionados al guardar...
function save_post_categories_meta($post_id) {
if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) return $post_id;
$noIndex = $_POST['chk_noIndex'];
$noFollow = $_POST['chk_noFollow'];
update_post_meta( $post_id, '_noindex', $noIndex );
update_post_meta( $post_id, '_nofollow', $noFollow );
return $post_id;
}
Ahora, creo que todo lo que queda es (1) capturar la plantilla seleccionada y agregarla al meta del post para este post y (2) modificar index.php y single.php para que use la plantilla elegida.

Lamento ser el portador de malas noticias, pero WordPress codifica la funcionalidad de Plantilla de Página exclusivamente para el tipo de contenido "page", al menos en la v3.0 (esto podría cambiar en versiones futuras, pero no hay una iniciativa específica que yo conozca para modificarlo todavía. Así que esta es una de las pocas veces que me cuesta encontrar una solución sin modificar el núcleo.)
La solución que he encontrado es básicamente copiar el código relevante del núcleo de WordPress y modificarlo según nuestras necesidades. Aquí están los pasos (los números de línea corresponden a la v3.0.1):
Copiar la función
page_attributes_meta_box()
de la línea 535 de/wp-admin/includes/meta-boxes.php
y modificarla según convenga.Programar un hook
add_meta_boxes
para añadir el metabox creado en el paso #1.Copiar la función
get_page_templates()
de la línea 166 de/wp-admin/includes/theme.php
y modificarla según convenga.Copiar la función
page_template_dropdown()
de la línea 2550 de/wp-admin/includes/template.php
y modificarla según convenga.Añadir una Plantilla de Entrada a tu tema.
Programar un hook
save_post
para guardar el nombre del archivo de plantilla al publicar.Programar un hook
single_template
para cargar la plantilla correspondiente a las entradas asociadas.
¡Manos a la obra!
1. Copiar la función page_attributes_meta_box()
Como primer paso, necesitas copiar la función page_attributes_meta_box()
de la línea 535 de /wp-admin/includes/meta-boxes.php
. Yo la he renombrado como post_template_meta_box()
. Como solo pediste plantillas para entradas, omití el código para especificar una entrada padre y el orden, lo que simplifica mucho el código. También decidí usar postmeta en lugar de reutilizar la propiedad page_template
para evitar incompatibilidades. Aquí está el código:
function post_template_meta_box($post) {
if ( 'post' == $post->post_type && 0 != count( get_post_templates() ) ) {
$template = get_post_meta($post->ID,'_post_template',true);
?>
<label class="screen-reader-text" for="post_template"><?php _e('Plantilla de Entrada') ?></label><select name="post_template" id="post_template">
<option value='default'><?php _e('Plantilla Predeterminada'); ?></option>
<?php post_template_dropdown($template); ?>
</select>
<?php
} ?>
<?php
}
2. Programar un hook add_meta_boxes
El siguiente paso es añadir el metabox usando el hook add_meta_boxes
:
add_action('add_meta_boxes','add_post_template_metabox');
function add_post_template_metabox() {
add_meta_box('postparentdiv', __('Plantilla de Entrada'), 'post_template_meta_box', 'post', 'side', 'core');
}
3. Copiar la función get_page_templates()
Asumí que solo tendría sentido diferenciar entre plantillas de página y plantillas de entrada, de ahí la necesidad de una función get_post_templates()
basada en get_page_templates()
de la línea 166 de /wp-admin/includes/theme.php
. Pero en lugar de usar el marcador Template Name:
, esta función usa Post Template:
, como se muestra abajo.
También excluí la inspección de functions.php
y cambié las referencias de page
a post
para mayor claridad:
function get_post_templates() {
$themes = get_themes();
$theme = get_current_theme();
$templates = $themes[$theme]['Template Files'];
$post_templates = array();
if ( is_array( $templates ) ) {
$base = array( trailingslashit(get_template_directory()), trailingslashit(get_stylesheet_directory()) );
foreach ( $templates as $template ) {
$basename = str_replace($base, '', $template);
if ($basename != 'functions.php') {
// no permitir archivos de plantilla en subdirectorios
if ( false !== strpos($basename, '/') )
continue;
$template_data = implode( '', file( $template ));
$name = '';
if ( preg_match( '|Post Template:(.*)$|mi', $template_data, $name ) )
$name = _cleanup_header_comment($name[1]);
if ( !empty( $name ) ) {
$post_templates[trim( $name )] = $basename;
}
}
}
}
return $post_templates;
}
4. Copiar la función page_template_dropdown()
De manera similar, copia page_template_dropdown()
de la línea 2550 de /wp-admin/includes/template.php
para crear post_template_dropdown()
, cambiando la llamada a get_post_templates()
:
function post_template_dropdown( $default = '' ) {
$templates = get_post_templates();
ksort( $templates );
foreach (array_keys( $templates ) as $template )
: if ( $default == $templates[$template] )
$selected = " selected='selected'";
else
$selected = '';
echo "\n\t<option value='".$templates[$template]."' $selected>$template</option>";
endforeach;
}
5. Añadir una Plantilla de Entrada
El siguiente paso es añadir una plantilla de entrada para probar. Usando el marcador Post Template:
, copia single.php
de tu tema a single-test.php
y añade este encabezado (modifica algo en single-test.php
para distinguirlo de single.php
):
/**
* Post Template: Mi Plantilla de Prueba
*/
Después de los pasos #1 al #5, verás el metabox "Plantillas de Entrada" en el editor de entradas:
(fuente: mikeschinkel.com)
6. Programar un hook save_post
Ahora necesitas guardar el nombre del archivo de plantilla en postmeta al publicar. Aquí está el código:
add_action('save_post','save_post_template',10,2);
function save_post_template($post_id,$post) {
if ($post->post_type=='post' && !empty($_POST['post_template']))
update_post_meta($post->ID,'_post_template',$_POST['post_template']);
}
7. Programar un hook single_template
Finalmente, necesitas que WordPress use tus nuevas plantillas. Esto se hace con el hook single_template
:
add_filter('single_template','get_post_template_for_template_loader');
function get_post_template_for_template_loader($template) {
global $wp_query;
$post = $wp_query->get_queried_object();
if ($post) {
$post_template = get_post_meta($post->ID,'_post_template',true);
if (!empty($post_template) && $post_template!='default')
$template = get_stylesheet_directory() . "/{$post_template}";
}
return $template;
}
¡Y eso es todo!
NOTA que no he considerado Tipos de Contenido Personalizados, solo post_type=='post'
. En mi opinión, manejar tipos personalizados requeriría diferenciar entre ellos, y aunque no es complicado, no lo he implementado aquí.

¡Genial! Me fui a dormir con un código casi completo en mi editor con el mismo enfoque de copiar las funciones predeterminadas de WordPress (estaba completo, pero no lo publicaría ya que no lo probé). :)

@sorich87 - Ya conoces el viejo dicho, "El que se duerme, pierde!" En serio, estoy SOLO bromeando. Realmente solo hay una forma razonable de hacer que funcione, así que no es de extrañar que tu código sea el mismo.

Mike, sigues sorprendiéndome. Muchas gracias por tomarte el tiempo para resolver esto.

@sorich87 - Gracias por trabajar en ello. Realmente aprecio el esfuerzo.

@Scott B: No hay problema, me alegra poder ayudar. Busco preguntas razonablemente genéricas que potencialmente podrían ayudar a mucha gente e intento responderlas no solo para la persona que hace la pregunta, sino también para aquellos que puedan venir después.

WordPress te permite añadir Meta a las Categorías usando un plugin:
Para hacer esto necesitas añadir una de las diversas extensiones que agrega meta a las categorías (imitando lo que las páginas obtienen por defecto), Simple Term Meta hace el trabajo muy bien.
Nota: Se necesita WordPress 3.x para extender las Categorías.
Después de eso puedes usar:
- add_term_meta
- update_term_meta
- get_term_meta
Usa Functions.php para añadir métodos que hagan lo que quieras, por ejemplo:
add_action('category_add_form_fields', 'category_metabox_add', 10, 1);
function category_metabox_add($tag) { ?>
<div class="form-field">
<label for="image-url"><?php _e('URL de la imagen') ?></label>
<input name="image-url" id="image-url" type="text" value="" size="40" aria-required="true" />
<p class="description"><?php _e('Esta imagen será la miniatura mostrada en la página de la categoría.'); ?></p>
</div>
<?php }
add_action('created_category', 'save_category_metadata', 10, 1);
function save_category_metadata($term_id)
{
if (isset($_POST['image-url']))
update_term_meta( $term_id, 'image-url', $_POST['image-url']);
}
Llamar a los nuevos campos en los temas es fácil:
<?php echo get_term_meta(get_query_var('cat'), 'image-url', true); ?>
Más detalles y ejemplos: http://www.wphub.com/adding-metadata-taxonomy-terms/
