Listado de categorías con miniatura y descripción en la página de inicio
Realmente necesito una solución completa para esto, así que estoy ofreciendo casi un cuarto de mi reputación en recompensa :)
Me gustaría tener un plugin que cree un listado personalizado de categorías en mi página de inicio. Para esto, me gustaría que la pantalla de "Editar Categoría" se complemente con algunas funciones adicionales como se describe a continuación...
- Agrega un manejador "Subir/Eliminar Imagen" a la pantalla de Edición de Categoría para una categoría dada (esto permite al usuario final subir una imagen que representará la categoría. Idealmente, debería redimensionarse automáticamente a 125 píxeles de ancho cuando se suba.
- Agrega un campo de entrada "Título de Categoría" a la pantalla de Edición de Categoría. Esto es adicional al campo "Nombre" existente.
- Agrega una casilla de verificación titulada "Mostrar en listado de página de inicio" para permitir la visualización selectiva de cada categoría en el listado de la página principal.
- Cuando se visualiza la página de inicio, el plugin añade al contenido (the_content) el listado personalizado de categorías, incluyendo la miniatura y título personalizados de cada categoría.
El marcado resultante debe ser una lista desordenada sin anidamiento, como esto...
<ul class="custom-categories">
<li>
<span><a href="link-to-category-1"><img src="the-category-1-image" /></a></span>
<a href="link-to-category-1">Category 1 Title</a> El texto de descripción de la categoría va aquí
</li>
<li>
<span><a href="link-to-category-2"><img src="the-category-2-image" /></a></span>
<a href="link-to-category-2">Category 2 Title</a> El texto de descripción de la categoría va aquí
</li>
etc...
</ul>
Aquí hay un archivo de inicio que he comenzado...
add_filter( 'the_content', 'cb_category_listing' );
function cb_category_listing( $content )
{
if ( is_home() ) {
$cat_args['title_li'] = '';
$cat_args['exclude_tree'] = 1;
$cat_args['exclude'] = 1;
$myContent = wp_list_categories(apply_filters('widget_categories_args', $cat_args));
$content .= $myContent;
}
return $content;
}

Nota: Esto es para versiones antiguas de WP < 3.9 antes de que se introdujera el nuevo sistema de carga de medios
Aquí se explica cómo añadir campos y guardar los valores en la pantalla de edición de categorías, así como un método para añadir un campo de carga de imágenes.
Añadir campos a la pantalla de edición de categorías
Para empezar, necesitamos que aparezca algún código en la pantalla de edición de categorías.
add_action( 'edit_category_form_fields', 'my_category_custom_fields' );
add_action( 'edit_category', 'save_my_category_custom_fields' );
function my_category_custom_fields( $tag ) {
// aquí irá tu HTML de campo personalizado
// la variable $tag es un objeto de término de taxonomía con propiedades como $tag->name, $tag->term_id, etc...
// necesitamos conocer los valores de nuestras entradas existentes, si las hay
$category_meta = get_option( 'category_meta' );
?>
<tr class="form-field">
<th scope="row" valign="top"><label for="category-title"><?php _e("Título"); ?></label></th>
<td>
<input id="category-title" name="category_meta[<?php echo $tag->term_id ?>][title]" value="<?php if ( isset( $category_meta[ $tag->term_id ] ) ) esc_attr_e( $category_meta[ $tag->term_id ]['title'] ); ?>" />
<span class="description"><?php _e('Ingresa un título alternativo para esta categoría.'); ?></span>
</td>
</tr>
<!-- repetir para otros campos que necesites -->
<?php
}
La forma más simple de almacenar nuestros valores personalizados es en la tabla de opciones (debería haber una tabla de meta-taxonomía, pero no importa). De esta manera solo necesitamos hacer una consulta para obtener los metadatos de todas nuestras categorías. ¡Si alguien tiene una mejor idea para el almacenamiento, que lo diga!
function save_my_category_custom_fields() {
if ( isset( $_POST['category_meta'] ) && !update_option('category_meta', $_POST['category_meta']) )
add_option('category_meta', $_POST['category_meta']);
}
Para un checkbox simplemente estás almacenando verdadero o falso, así que usarías category_extras[$tag->term_id][show_on_home]
para el atributo name y usarías el valor almacenado en $category_meta
para determinar si está marcado o no.
Puedes querer añadir algún procesamiento o saneamiento extra a la función de guardado - el mío es solo un ejemplo rápido y sencillo.
El campo de imagen
Esto es bastante código y bastante complicado, así que no lo explicaré todo aquí, pero los comentarios describen el propósito de cada función. Podemos discutirlo en los comentarios si lo deseas.
Las siguientes funciones añaden un enlace a la pantalla de edición de categorías que muestra la biblioteca de medios de WordPress/popup de carga de imágenes. Puedes entonces subir una imagen y hacer clic para usarla. Tendrás entonces el ID de la imagen y la URL del thumbnail disponibles con los otros meta anteriores.
// añade el tamaño de imagen que necesites
add_image_size( 'category_thumb', 125, 125, true );
// configura nuestro campo de imagen y métodos de manejo
function setup_category_image_handling() {
// añade el campo de imagen al resto
add_action( 'edit_category_form_fields', 'category_image' );
global $pagenow;
if ( is_admin() ) {
add_action( 'admin_init', 'fix_async_upload_image' );
if ( 'edit-tags.php' == $pagenow ) {
add_thickbox();
add_action('admin_print_footer_scripts', 'category_image_send_script', 1000);
} elseif ( 'media-upload.php' == $pagenow || 'async-upload.php' == $pagenow ) {
add_filter( 'media_send_to_editor', 'category_image_send_to_editor', 1, 8 );
add_filter( 'gettext', 'category_image_replace_text_in_thickbox', 1, 3 );
}
}
}
add_action( 'admin_init', 'setup_category_image_handling' );
// el campo de imagen en la pantalla de edición de taxonomía
function category_image( $tag ) {
// obtenemos nuestros metadatos de categoría
$category_meta = get_option('category_meta');
?>
<tr class="form-field hide-if-no-js">
<th scope="row" valign="top"><label for="taxonomy-image"><?php _e("Imagen"); ?></label></th>
<td>
<div id="taxonomy-image-holder">
<?php if( !empty($category_meta[$tag->term_id]['image']) ) { ?>
<img style="max-width:100%;display:block;" src="<?php echo esc_attr( $category_meta[ $tag->term_id ]['image']['thumb'] ); ?>" alt="Miniatura de categoría" title="Miniatura de categoría" />
<a id="taxonomy-image-select" class="thickbox" href="media-upload.php?is_term=true&type=image&TB_iframe=1"><?php _e('Cambiar imagen'); ?></a>
<a class="deletion" id="taxonomy-image-remove" href="#remove-image">Eliminar imagen</a>
<?php } else { ?>
<a id="taxonomy-image-select" class="thickbox" href="media-upload.php?is_term=true&type=image&TB_iframe=1"><?php _e('Seleccionar una imagen'); ?></a>
<?php } ?>
</div>
<input type="hidden" name="category_meta[<?php echo $tag->term_id ?>][image][id]" value="<?php if( isset($category_meta[ $tag->term_id ]['image']['id']) ) echo esc_attr($category_meta[ $tag->term_id ]['image']['id']); ?>" class="tax-image-id" />
<input type="hidden" name="category_meta[<?php echo $tag->term_id ?>][image][thumb]" value="<?php if( isset($category_meta[ $tag->term_id ]['image']['thumb']) ) echo esc_attr($category_meta[ $tag->term_id ]['image']['thumb']); ?>" class="tax-image-thumb" />
<span class="description"><?php _e('Una imagen para la categoría.'); ?></span></td>
</tr>
<?php
}
// necesario para cargar imágenes en pantallas que no son de entradas/páginas
function fix_async_upload_image() {
if(isset($_REQUEST['attachment_id'])) {
$GLOBALS['post'] = get_post($_REQUEST['attachment_id']);
}
}
// ¿estamos en la pantalla de edición de taxonomía?
function is_category_context() {
if ( isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'],'is_term') !== false ) {
return true;
} elseif ( isset($_REQUEST['_wp_http_referer']) && strpos($_REQUEST['_wp_http_referer'],'is_term') !== false ) {
return true;
} elseif ( isset($_REQUEST['is_term']) && $_REQUEST['is_term'] !== false ) {
return true;
}
return false;
}
// reemplaza el texto "Insertar en la entrada" con algo más apropiado
function category_image_replace_text_in_thickbox($translated_text, $source_text, $domain) {
if ( is_category_context() ) {
if ('Insert into Post' == $source_text) {
return __('Usar esta imagen', MB_DOM );
}
}
return $translated_text;
}
// envía un script que establece variables en el objeto window para que puedan ser accedidas desde otros lugares
function category_image_send_to_editor( $html, $id, $attachment ) {
// la verificación de contexto podría no ser necesaria, y podría no funcionar en todos los casos
if ( is_category_context() ) {
$item = get_post($id);
$src = wp_get_attachment_image_src($id,'thumbnail',true); // 0 = url, 1 = width, 2 = height, 3 = icon(bool)
?>
<script type="text/javascript">
// envía variables de imagen de vuelta al opener
var win = window.dialogArguments || opener || parent || top;
win.TI.id = <?php echo $id ?>;
win.TI.thumb = '<?php echo $src[0]; ?>';
</script>
<?php
}
return $html;
}
// escribe el javascript que maneja lo que ocurre cuando un usuario hace clic para usar una imagen
function category_image_send_script() { ?>
<script>
self.TI = {};
var tb_position;
function send_to_editor(h) {
// ignora el contenido devuelto del cargador de medios y usa las variables pasadas a window en su lugar
jQuery('.tax-image-id').val( self.TI.id );
jQuery('.tax-image-thumb').val( self.TI.thumb );
// muestra la imagen
jQuery('#taxonomy-image-holder img, #taxonomy-image-remove').remove();
jQuery('#taxonomy-image-holder')
.prepend('<img style="max-width:100%;display:block;" src="'+ self.TI.thumb +'" alt="Miniatura de categoría" title="Miniatura de categoría" />')
.append('<a class="deletion" id="taxonomy-image-remove" href="#remove-image">Eliminar imagen</a>');
jQuery('#taxonomy-image-select').html('Cambiar imagen');
// cierra el thickbox
tb_remove();
}
(function($){
$(document).ready(function() {
tb_position = function() {
var tbWindow = $('#TB_window'), width = $(window).width(), H = $(window).height(), W = ( 720 < width ) ? 720 : width;
if ( tbWindow.size() ) {
tbWindow.width( W - 50 ).height( H - 45 );
$('#TB_iframeContent').width( W - 50 ).height( H - 75 );
tbWindow.css({'margin-left': '-' + parseInt((( W - 50 ) / 2),10) + 'px'});
if ( typeof document.body.style.maxWidth != 'undefined' )
tbWindow.css({'top':'20px','margin-top':'0'});
};
return $('a.thickbox').each( function() {
var href = $(this).attr('href');
if ( ! href ) return;
href = href.replace(/&width=[0-9]+/g, '');
href = href.replace(/&height=[0-9]+/g, '');
$(this).attr( 'href', href + '&width=' + ( W - 80 ) + '&height=' + ( H - 85 ) );
});
};
$(window).resize(function(){ tb_position(); });
$('#taxonomy-image-select').click(function(event){
tb_show("Seleccionar una imagen:", $(this).attr("href"), false);
return false;
});
$('#taxonomy-image-remove').live('click',function(event){
$('#taxonomy-image-select').html('Seleccionar una imagen');
$('#taxonomy-image-holder img').remove();
$('input[class^="tax-image"]').val("");
$(this).remove();
return false;
});
});
})(jQuery);
</script>
<?php
}
Accediendo a la información
En tu función my_function
tomada de tu propia respuesta, accederías a los metadatos así:
function my_function(){
$args=array(
'orderby' => 'name',
'order' => 'ASC'
);
$categories=get_categories($args);
// obtenemos nuestros metadatos de categoría y salimos si no hay ninguno
$category_meta = get_option('category_meta');
if ( !$category_meta )
return;
echo '<ul style="list-style-type:none;margin:0;padding:0">';
foreach($categories as $category) {
// saltamos categorías no marcadas y categorías sin metadatos
if ( !isset( $category_meta[ $category->term_id ] ) || ( isset( $category_meta[ $category->term_id ] ) && !$category_meta[ $category->term_id ]['show_on_home'] ) )
continue;
echo '<li><a style="display:block;margin-top:20px;" href="' . get_category_link( $category->term_id ) . '" title="' . sprintf( __( "Ver todas las entradas en %s" ), $category->name ) . '" ' . '>' . $category_meta[ $category->term_id ]['title'].'</a>';
if ( "" != $category_meta[ $category->term_id ]['image']['id'] )
echo wp_get_attachment_image( $category_meta[ $category->term_id ]['image']['id'], 'category_thumbnail', false, array( 'alt' => $category->name, 'class' => '' ) );
echo $category->description . '</li>';
}
echo '</ul>';
}

Guau, gracias sanchothefat. Aprecio tu esfuerzo. He logrado armar la mayor parte por mí mismo, pero voy a aceptar tu respuesta e incorporarla a mi código.

Sanchothefat, mereces la recompensa que puse por esto, ¿recibiste alguna?

¿Qué tan difícil sería modificar tu código para guardar las imágenes en una carpeta dentro de uploads llamada "category-images"? Y si la carpeta no existe, crearla.

Honestamente no estoy seguro. Podría haber un filtro para la ruta del directorio de subida. A mí me gusta usar la biblioteca de medios integrada porque hace que las subidas y la generación de miniaturas sean pan comido. Echa un vistazo a http://adambrown.info/p/wp_hooks - es un gran recurso que describe cada hook y filtro en WordPress.
No conseguí la recompensa :( debe haber expirado.

La forma más fácil sería con algo como esto http://wordpress.org/extend/plugins/category-images/. O podrías escribir tu propio hook.

Gracias Wyck, he probado este plugin pero no hace exactamente TODO lo que necesito. Por ejemplo, no quiero reemplazar el texto del enlace con la imagen, quiero la imagen Y el texto del enlace.

Parece que la miniatura solo se muestra en la página single.php de la publicación. Necesito un listado personalizado de categorías (una especie de página índice) de todas las categorías. He actualizado mi pregunta con más especificidad y añadí una recompensa por una solución concluyente.

Aquí estaba mi primer intento...
add_filter( 'the_content', 'cb_category_listing' );
function cb_category_listing( $content )
{
if ( is_home() ) {
$cat_args['title_li'] = '';
$cat_args['exclude_tree'] = 1;
$cat_args['exclude'] = 1;
$myContent = wp_list_categories(apply_filters('widget_categories_args', $cat_args));
$content .= $myContent;
}
return $content;
}
Obviamente, esto simplemente crea una lista de nombres de categorías con un enlace a cada categoría. Así que me da un comienzo. Sin embargo, todavía necesito...
Una forma de asociar una imagen miniatura de categoría con cada categoría, y mostrarla en el listado de categorías en mi código anterior.
Una forma de incluir un título personalizado de categoría, aparte del nombre de la categoría, y mostrarlo en el listado de categorías (en lugar del "Nombre de la Categoría").
Una forma de mostrar/ocultar cada categoría del listado. Idealmente, con una casilla de verificación "Mostrar en el listado de la página de inicio" que pueda editarse en la pantalla de edición de la categoría.
Aquí está mi código actualizado. Voy avanzando, pero todavía no he abordado la imagen miniatura o añadir los campos personalizados (imagen miniatura, título personalizado, mostrar/ocultar) al editor de categorías en el administrador...
function _add_my_filter() {
if ( is_home() OR is_sticky() )
{
add_filter( 'the_content', 'my_function' );
}
}
add_action('template_redirect', '_add_my_filter');
function my_function(){
$args=array(
'orderby' => 'name',
'order' => 'ASC'
);
$categories=get_categories($args);
echo '<ul style="list-style-type:none;margin:0;padding:0">';
foreach($categories as $category) {
echo '<li><a style="display:block;margin-top:20px;" href="' . get_category_link( $category->term_id ) . '" title="' . sprintf( __( "Ver todas las publicaciones en %s" ), $category->name ) . '" ' . '>' . $category->name.'</a>';
echo $category->description . '</li>';
}
echo '</ul>';
}
