sábado, 31 de agosto de 2013

9. Las plantillas Entrada Individual, Entrada de Adjuntos y error 404 del Tema de WordPress

(El artículo original y en inglés, fue publicado el 2 de noviembre de 2012)

ThemeShaper es el sitio del Equipo de Temas de Automattic, que, en junio de 2009, publicó una muy popular serie de 11 artículos de Ian Stewart, bajo el título: How To Create a WordPress Theme: The Ultimate WordPress Theme Tutorial (Como crear un Tema de WordPress: el tutorial definitivo).
El año pasado, reflotó y actualizó ese tutorial. Esta vez con 16 artículos a cargo de Michelle LangstonThe ThemeShaper WordPress Theme Tutorial: 2nd Edition (Tutorial de Temas de WordPress de ThemeShaper. Segunda edición).
Me propongo traducir libremente esas 16 entradas (además de la introducción) e ir publicándolas a medida que las tenga listas.
Al final de cada entrada va el listado con todas las entradas de este tutorial.


Ya ha creado el index de todas sus entradas, ahora necesita crear una plantilla para enmarcar el contenido (o contenido perdido) de cada entrada individual. En esta lección, creará plantillas para las entradas individuales, las entradas de adjuntos y la página del error 404.

Una plantilla para las plantillas

La estructura de single.php (y de prácticamente todas las demás plantillas que estaremos creando) es casi la misma que en index.php. De hecho, puede pensar en ella como nuestra plantilla de plantillas. Siga adelante y agregue el siguiente código a single.php.



<?php
/**
 * The Template for displaying all single posts.
 *
 * @package Shape
 * @since Shape 1.0
 */
 
get_header(); ?>
 
        <div id="primary" class="content-area">
            <div id="content" class="site-content" role="main">
 
            <?php while ( have_posts() ) : the_post(); ?>
 
                <?php shape_content_nav( 'nav-above' ); ?>
 
                <?php get_template_part( 'content', 'single' ); ?>
 
                <?php shape_content_nav( 'nav-below' ); ?>
 
                <?php
                    // If comments are open or we have at least one comment, load up the comment template
                    if ( comments_open() || '0' != get_comments_number() )
                        comments_template( '', true );
                ?>
 
            <?php endwhile; // end of the loop. ?>
 
            </div><!-- #content .site-content -->
        </div><!-- #primary .content-area -->
 
<?php get_sidebar(); ?>
<?php get_footer(); ?>

Note como estamos llamando de nuevo a get_template_part():

<?php get_template_part( 'content', 'single' ); ?>

Esta línea quiere decir que su Tema buscará un archivo llamado content-single.php para rellenar el Bucle, en caso de no encontrarlo usará content.php. En breve completaremos nuestro archivo content-single.php, pero, primero, vamos a repasar algunas cosas en single.php.

Primero, dado que esto es una entrada individual, necesitaremos de la función  comments_template(). De un vistazo a este código en single.php.

<?php
// If comments are open or we have at least one comment, load up the comment template
if ( comments_open() || '0' != get_comments_number() )
     comments_template( '', true );
?>

Cargaremos los comentarios si los mismos están abiertos y si hay, por lo menos, un comentario. Y, debido a que estaremos separando nuestros comentarios y trackbacks, cuando nos toque codificar comments.php, necesitamos llamar a comments_template() de esta manera: comments_template( '', true );.

Contenido de la Entrada Individual

Ya que habrán algunas diferencias notorias en cómo formatearemos las entradas individuales (en comparación con las entradas en la página index), es que vamos a crear un archivo de plantilla aparte para el bucle de las entradas individuales. Abra content-single.php, y agregue el siguiente código.

<?php
/**
 * @package Shape
 * @since Shape 1.0
 */
?>
 
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
    <header class="entry-header">
        <h1 class="entry-title"><?php the_title(); ?></h1>
 
        <div class="entry-meta">
            <?php shape_posted_on(); ?>
        </div><!-- .entry-meta -->
    </header><!-- .entry-header -->
 
    <div class="entry-content">
        <?php the_content(); ?>
        <?php wp_link_pages( array( 'before' => '<div class="page-links">' . __( 'Pages:', 'shape' ), 'after' => '</div>' ) ); ?>
    </div><!-- .entry-content -->
 
    <footer class="entry-meta">
        <?php
            /* translators: used between list items, there is a space after the comma */
            $category_list = get_the_category_list( __( ', ', 'shape' ) );
 
            /* translators: used between list items, there is a space after the comma */
            $tag_list = get_the_tag_list( '', ', ' );
 
            if ( ! shape_categorized_blog() ) {
                // This blog only has 1 category so we just need to worry about tags in the meta text
                if ( '' != $tag_list ) {
                    $meta_text = __( 'This entry was tagged %2$s. Bookmark the <a href="%3$s" title="Permalink to %4$s" rel="bookmark">permalink</a>.', 'shape' );
                } else {
                    $meta_text = __( 'Bookmark the <a href="%3$s" title="Permalink to %4$s" rel="bookmark">permalink</a>.', 'shape' );
                }
 
            } else {
                // But this blog has loads of categories so we should probably display them here
                if ( '' != $tag_list ) {
                    $meta_text = __( 'This entry was posted in %1$s and tagged %2$s. Bookmark the <a href="%3$s" title="Permalink to %4$s" rel="bookmark">permalink</a>.', 'shape' );
                } else {
                    $meta_text = __( 'This entry was posted in %1$s. Bookmark the <a href="%3$s" title="Permalink to %4$s" rel="bookmark">permalink</a>.', 'shape' );
                }
 
            } // end check for categories on this blog
 
            printf(
                $meta_text,
                $category_list,
                $tag_list,
                get_permalink(),
                the_title_attribute( 'echo=0' )
            );
        ?>
 
        <?php edit_post_link( __( 'Edit', 'shape' ), '<span class="edit-link">', '</span>' ); ?>
    </footer><!-- .entry-meta -->
</article><!-- #post-<?php the_ID(); ?> -->

Se ve muy parecido a content.php, con algunas diferencias, de las que escribiremos en las secciones más próximas.

Contenido de la Entrada Individual

A diferencia de content.php, en content-single.php, no tenemos ningunos condicionales que nos digan que mostremos extractos en páginas de resultados de búsquedas -- porque ya estamos en la vista de entrada individual.

Metadatos en la Entrada Individual

Los metadatos en la Entrada Individual son casi idénticos a los de content.php. La principal diferencia es que no necesitamos revisar si esta es o no una página.

Note también que no estamos incluyendo el enlace a comentarios, porque ya hemos llamado a comments_template() en single.php.

Navegación en la Entrada Individual

Ahora, regresemos a single.php. Note como, del mismo modo que hicimos para index.php, estamos usando la función shape_content_nav() para manejar la navegación para las entradas individuales. Habíamos agregado esta función a inc/template-tags.php en nuestra lección sobre "la plantilla index de su Tema de WordPress".

Aquí está el contenido de esa función (por las dudas que se haya perdido esa lección, o no tenga ganas de abrir inc/template-tags.php).

function shape_content_nav( $nav_id ) {
    global $wp_query, $post;
 
    // Don't print empty markup on single pages if there's nowhere to navigate.
    if ( is_single() ) {
        $previous = ( is_attachment() ) ? get_post( $post->post_parent ) : get_adjacent_post( false, '', true );
        $next = get_adjacent_post( false, '', false );
 
        if ( ! $next && ! $previous )
            return;
    }
 
    // Don't print empty markup in archives if there's only one page.
    if ( $wp_query->max_num_pages < 2 && ( is_home() || is_archive() || is_search() ) )
        return;
 
    $nav_class = 'site-navigation paging-navigation';
    if ( is_single() )
        $nav_class = 'site-navigation post-navigation';
 
    ?>
    <nav role="navigation" id="<?php echo $nav_id; ?>" class="<?php echo $nav_class; ?>">
        <h1 class="assistive-text"><?php _e( 'Post navigation', 'shape' ); ?></h1>
 
    <?php if ( is_single() ) : // navigation links for single posts ?>
 
        <?php previous_post_link( '<div class="nav-previous">%link</div>', '<span class="meta-nav">' . _x( '←', 'Previous post link', 'shape' ) . '</span> %title' ); ?>
       <?php next_post_link( '<div class="nav-next">%link</div>', '%title <span class="meta-nav">' . _x( '→', 'Next post link', 'shape' ) . '</span>' ); ?>
 
    <?php elseif ( $wp_query->max_num_pages > 1 && ( is_home() || is_archive() || is_search() ) ) : // navigation links for home, archive, and search pages ?>
 
        <?php if ( get_next_posts_link() ) : ?>
        <div class="nav-previous"><?php next_posts_link( __( '<span class="meta-nav">←</span> Older posts', 'shape' ) ); ?></div>
        <?php endif; ?>
 
        <?php if ( get_previous_posts_link() ) : ?>
        <div class="nav-next"><?php previous_posts_link( __( 'Newer posts <span class="meta-nav">→</span>', 'shape' ) ); ?></div>
        <?php endif; ?>
 
    <?php endif; ?>
 
    </nav><!-- #<?php echo $nav_id; ?> -->
    <?php
}

Y aquí está la parte específica de la función que maneja la navegación de la entrada individual.

<?php if ( is_single() ) : // navigation links for single posts ?>
 
    <?php previous_post_link( '<div class="nav-previous">%link</div>', '<span class="meta-nav">' . _x( '←', 'Previous post link', 'shape' ) . '</span> %title' ); ?>
    <?php next_post_link( '<div class="nav-next">%link</div>', '%title <span class="meta-nav">' . _x( '→', 'Next post link', 'shape' ) . '</span>' ); ?>

En lugar de usar los pobremente llamados next_posts_link() y previous_posts_link() estaremos usando los más acertadamente llamados previous_post_link() y next_post_link(). Ellos hacen, justamente, lo que usted piensa que hacen.

Entradas de adjuntos

No mucha gente utiliza entradas de adjuntos pero son bastante interesantes. Cuando agrega una imagen a su entrada, en realidad, está adjuntándola a la entrada. Y, por supuesto, puede adjuntar más que solo imágenes. Vamos a hacer una plantilla image.php, pero, si quiere, puede adaptarlo para abarcar otros tipos de adjuntos como video, audio y aplicaciones, creando las plantillas video.php, audio.php, y application.php. Hay muchas maneras de ser creativos con las plantillas de adjuntos y WordPress.

Entonces, sin más preámbulos, siga adelante y cree un archivo image.php en el directorio raíz de su Tema. Una vez hecho eso, rellene el archivo con el siguiente código.

<?php
/**
 * The template for displaying image attachments.
 *
 * @package Shape
 * @since Shape 1.0
 */
 
get_header();
?>
 
        <div id="primary" class="content-area image-attachment">
            <div id="content" class="site-content" role="main">
 
            <?php while ( have_posts() ) : the_post(); ?>
 
                <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
                    <header class="entry-header">
                        <h1 class="entry-title"><?php the_title(); ?></h1>
 
                        <div class="entry-meta">
                            <?php
                                $metadata = wp_get_attachment_metadata();
                                printf( __( 'Published <span class="entry-date"><time class="entry-date" datetime="%1$s" pubdate>%2$s</time></span> at <a href="%3$s" title="Link to full-size image">%4$s × %5$s</a> in <a href="%6$s" title="Return to %7$s" rel="gallery">%7$s</a>', 'shape' ),
                                    esc_attr( get_the_date( 'c' ) ),
                                    esc_html( get_the_date() ),
                                    wp_get_attachment_url(),
                                    $metadata['width'],
                                    $metadata['height'],
                                    get_permalink( $post->post_parent ),
                                    get_the_title( $post->post_parent )
                                );
                            ?>
                            <?php edit_post_link( __( 'Edit', 'shape' ), '<span class="sep"> | </span> <span class="edit-link">', '</span>' ); ?>
                        </div><!-- .entry-meta -->
 
                        <nav id="image-navigation" class="site-navigation">
                            <span class="previous-image"><?php previous_image_link( false, __( '← Previous', 'shape' ) ); ?></span>
                            <span class="next-image"><?php next_image_link( false, __( 'Next →', 'shape' ) ); ?></span>
                        </nav><!-- #image-navigation -->
                    </header><!-- .entry-header -->
 
                    <div class="entry-content">
 
                        <div class="entry-attachment">
                            <div class="attachment">
                                <?php
                                    /**
                                     * Grab the IDs of all the image attachments in a gallery so we can get the URL of the next adjacent image in a gallery,
                                     * or the first image (if we're looking at the last image in a gallery), or, in a gallery of one, just the link to that image file
                                     */
                                    $attachments = array_values( get_children( array( 'post_parent' => $post->post_parent, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => 'ASC', 'orderby' => 'menu_order ID' ) ) );
                                    foreach ( $attachments as $k => $attachment ) {
                                        if ( $attachment->ID == $post->ID )
                                            break;
                                    }
                                    $k++;
                                    // If there is more than 1 attachment in a gallery
                                    if ( count( $attachments ) > 1 ) {
                                        if ( isset( $attachments[ $k ] ) )
                                            // get the URL of the next image attachment
                                            $next_attachment_url = get_attachment_link( $attachments[ $k ]->ID );
                                        else
                                            // or get the URL of the first image attachment
                                            $next_attachment_url = get_attachment_link( $attachments[ 0 ]->ID );
                                    } else {
                                        // or, if there's only 1 image, get the URL of the image
                                        $next_attachment_url = wp_get_attachment_url();
                                    }
                                ?>
 
                                <a href="<?php echo $next_attachment_url; ?>" title="<?php echo esc_attr( get_the_title() ); ?>" rel="attachment"><?php
                                    $attachment_size = apply_filters( 'shape_attachment_size', array( 1200, 1200 ) ); // Filterable image size.
                                    echo wp_get_attachment_image( $post->ID, $attachment_size );
                                ?></a>
                            </div><!-- .attachment -->
 
                            <?php if ( ! empty( $post->post_excerpt ) ) : ?>
                            <div class="entry-caption">
                                <?php the_excerpt(); ?>
                            </div><!-- .entry-caption -->
                            <?php endif; ?>
                        </div><!-- .entry-attachment -->
 
                        <?php the_content(); ?>
                        <?php wp_link_pages( array( 'before' => '<div class="page-links">' . __( 'Pages:', 'shape' ), 'after' => '</div>' ) ); ?>
 
                    </div><!-- .entry-content -->
 
                    <footer class="entry-meta">
                        <?php if ( comments_open() && pings_open() ) : // Comments and trackbacks open ?>
                            <?php printf( __( '<a class="comment-link" href="#respond" title="Post a comment">Post a comment</a> or leave a trackback: <a class="trackback-link" href="%s" title="Trackback URL for your post" rel="trackback">Trackback URL</a>.', 'shape' ), get_trackback_url() ); ?>
                        <?php elseif ( ! comments_open() && pings_open() ) : // Only trackbacks open ?>
                            <?php printf( __( 'Comments are closed, but you can leave a trackback: <a class="trackback-link" href="%s" title="Trackback URL for your post" rel="trackback">Trackback URL</a>.', 'shape' ), get_trackback_url() ); ?>
                        <?php elseif ( comments_open() && ! pings_open() ) : // Only comments open ?>
                            <?php _e( 'Trackbacks are closed, but you can <a class="comment-link" href="#respond" title="Post a comment">post a comment</a>.', 'shape' ); ?>
                        <?php elseif ( ! comments_open() && ! pings_open() ) : // Comments and trackbacks closed ?>
                            <?php _e( 'Both comments and trackbacks are currently closed.', 'shape' ); ?>
                        <?php endif; ?>
                        <?php edit_post_link( __( 'Edit', 'shape' ), ' <span class="edit-link">', '</span>' ); ?>
                    </footer><!-- .entry-meta -->
                </article><!-- #post-<?php the_ID(); ?> -->
 
                <?php comments_template(); ?>
 
            <?php endwhile; // end of the loop. ?>
 
            </div><!-- #content .site-content -->
        </div><!-- #primary .content-area .image-attachment -->
 
<?php get_footer(); ?>

La plantilla de adjuntos es, en estructura, muy similar a single.php. El archivo se ve más complejo que single.php porque no estamos usando get_template_part() para llamar a una plantilla aparte que incluya los contenidos de nuestro bucle. ¿Por qué colocamos el contenido del Bucle directamente en este archivo? Porque no utilizamos este código específico en ningún otro lugar del Tema. Si fuéramos a reutilizarlo para otras plantillas, tendría sentido ubicarla en un archivo separado y llamarlo utilizando get_template_part().

Verá que es muy similar a mostrar una entrada normal, solo que, en vez de texto, estaremos mostrando la imagen adjunta. Repasemos algunas cosas. Dele un vistazo a este código de image.php.

<header class="entry-header">
    <h1 class="entry-title"><?php the_title(); ?></h1>
 
    <div class="entry-meta">
        <?php
            $metadata = wp_get_attachment_metadata();
            printf( __( 'Published <span class="entry-date"><time class="entry-date" datetime="%1$s" pubdate>%2$s</time></span> at <a href="%3$s" title="Link to full-size image">%4$s × %5$s</a> in <a href="%6$s" title="Return to %7$s" rel="gallery">%7$s</a>', 'shape' ),
                esc_attr( get_the_date( 'c' ) ),
                esc_html( get_the_date() ),
                wp_get_attachment_url(),
                $metadata['width'],
                $metadata['height'],
                get_permalink( $post->post_parent ),
                get_the_title( $post->post_parent )
            );
        ?>
        <?php edit_post_link( __( 'Edit', 'shape' ), '<span class="sep"> | </span> <span class="edit-link">', '</span>' ); ?>
    </div><!-- .entry-meta -->
    <nav id="image-navigation" class="site-navigation">
        <span class="previous-image"><?php previous_image_link( false, __( '← Previous', 'shape' ) ); ?></span>
        <span class="next-image"><?php next_image_link( false, __( 'Next →', 'shape' ) ); ?></span>
    </nav><!-- #image-navigation -->
</header><!-- .entry-header -->

En esta sección, mostramos el título de la imagen (el texto que se ingresó en el campo "Título" al editar y subir la imagen) y los metadatos de la imagen (la fecha de publicación, sus dimensiones en píxeles y el título de la entrada a la que está adjuntada), seguidos por un enlace para editar la imagen.

Los enlaces de navegación de imágenes nos permite navegar a través de las imágenes adjuntadas a una entrada particular. En Configuración de las funciones de su Tema, agregamos la función shape_enhanced_image_navigation() a inc/tweaks.php, que le agrega un ancla (#main) al final del enlace previo/posterior de navegación de imagen. Esto le permite a la ventana del navegador quedarse "anclada" a cierta altura mientras los usuarios van haciendo clic a través de las imágenes. En la "lección de la plantilla cabecera (header)", también agregamos algo de  JavaScript que le permite a los usuarios navegar sus imágenes usando las flechas derecha e izquierda del teclado.

El resto del código en image.php está bien comentado, de forma que pueda tener una idea general de qué es lo que está haciendo. Note que no hay llamada a get_sidebar(). Esto es para permitirle a la imagen adjuntada que use la cantidad de espacio óptima para que se vea impresionante. Sin embargo, si quisiera tener una barra lateral en su página de imagen adjunta, sin falta, siéntase libre de agregarlo.

La plantilla 404

El error 404 es el código del servidor para “No puedo encontrar esta página” y es un evento que hay que tener en cuenta en su Tema de WordPress. ¿Qué sucede cuando un enlace a su blog tiene la url de la entrada mal escrita? ¿O si usted borra una entrada de su blog? Su servidor escupe un error 404.

Afortunadamente, WordPress tiene una plantilla para esto. Se llama 404.php. La técnica que siempre utilizo para las plantillas 404 es bastante sencilla, pero funciona. Pedir disculpas e incluir un formulario de búsqueda. Podrían haber soluciones  más creativas, pero ninguna que lo saque más rápido del camino de su visitante.

Abra el archivo 404.php y agregue algo como esto.

<?php
/**
 * The template for displaying 404 pages (Not Found).
 *
 * @package Shape
 * @since Shape 1.0
 */
 
get_header(); ?>
 
    <div id="primary" class="content-area">
        <div id="content" class="site-content" role="main">
 
            <article id="post-0" class="post error404 not-found">
                <header class="entry-header">
                    <h1 class="entry-title"><?php _e( 'Oops! That page can’t be found.', 'shape' ); ?></h1>
                </header><!-- .entry-header -->
 
                <div class="entry-content">
                    <p><?php _e( 'It looks like nothing was found at this location. Maybe try one of the links below or a search?', 'shape' ); ?></p>
 
                    <?php get_search_form(); ?>
 
                    <?php the_widget( 'WP_Widget_Recent_Posts' ); ?>
 
                    <div class="widget">
                        <h2 class="widgettitle"><?php _e( 'Most Used Categories', 'shape' ); ?></h2>
                        <ul>
                        <?php wp_list_categories( array( 'orderby' => 'count', 'order' => 'DESC', 'show_count' => 1, 'title_li' => '', 'number' => 10 ) ); ?>
                        </ul>
                    </div><!-- .widget -->
 
                    <?php
                    /* translators: %1$s: smilie */
                    $archive_content = '<p>' . sprintf( __( 'Try looking in the monthly archives. %1$s', 'shape' ), convert_smilies( ':)' ) ) . '</p>';
                    the_widget( 'WP_Widget_Archives', 'dropdown=1', "after_title=</h2>$archive_content" );
                    ?>
 
                    <?php the_widget( 'WP_Widget_Tag_Cloud' ); ?>
 
                </div><!-- .entry-content -->
            </article><!-- #post-0 .post .error404 .not-found -->
 
        </div><!-- #content .site-content -->
    </div><!-- #primary .content-area -->
 
<?php get_footer(); ?>

También hemos omitido la barra lateral en esta página, pero siempre puede optar por agregarla colocando un <get_sidebar> justo sobre el llamado <get_footer>.


Sumario de entradas

  1. Introducción.
  2. El desarrollo de su "sentido temático".
  3. Herramientas para el desarrollo de Temas de WordPress.
  4. Creación de una estructura html para su Tema de Wordpress.
  5. Plantillas y estructura de directorios de un Tema de WordPress.
  6. Configuración de las funciones de su Tema.
  7. Asegure su Tema de WordPress.
  8. La plantilla cabecera del tema de WordPress: header.php.
  9. La plantilla Index de su Tema de WordPress.
  10. Las plantillas Entrada Individual, Entrada de Adjuntos y error 404 del Tema de WordPress.
  11. La plantilla de comentarios de un Tema de WordPress.
  12. La plantilla de búsqueda (search) y la plantilla página (page) del Tema de WordPress.
  13. La plantilla Archivo .
  14. Las plantillas "sidebar" (barra lateral) y "footer" (pie de página) del Tema de WordPress.
  15. Como resetear y reconstruir los CSS de su Tema de WordPress y definir su diseño.
  16. Fondo y cabecera personalizados.
  17. Distribuir su Tema de WordPress.

No hay comentarios:

Publicar un comentario

Lo que escriba a continuación será revisado antes de publicarse.
Gracias por tus comentarios.