Crear una página de archivo personalizada para un tipo de entrada personalizado en un plugin

27 sept 2013, 22:04:49
Vistas: 18.3K
Votos: 16

Estoy desarrollando un plugin que crea un tipo de entrada personalizado llamado "my_plugin_lesson":

$args = array (
    'public' => true,
    'has_archive' => true,
    'rewrite' => array('slug' => 'lessons', 'with_front' => false)
);
register_post_type ('my_plugin_lesson', $args);

El tipo de entrada personalizado tiene un archivo, y la URL del archivo es:

http://example.com/lessons

Quiero personalizar el aspecto de este archivo; deseo listar las entradas en formato de tabla, en lugar del formato estándar de archivo de WordPress. Entiendo que podría crearse una plantilla de archivo personalizada en el tema mediante el archivo archive-my_plugin_lesson.php; sin embargo, me gustaría que el plugin funcione con cualquier tema.

¿Cómo puedo alterar el contenido de la página de archivo sin añadir o modificar archivos del tema?

Edición: Entiendo que podría usar el hook de filtro archive_template. Sin embargo, esto solo reemplaza la plantilla del tema, que aún necesita ser específica para el tema. Por ejemplo, casi todas las plantillas de temas necesitarán las funciones get_header, get_sidebar y get_footer, pero ¿cuál debería ser el id del <div> de contenido? Esto varía en cada tema.

Lo que me gustaría hacer es reemplazar el contenido en sí con mi propio contenido, y usarlo en lugar de la página de archivo para mi tipo de entrada personalizado.

0
Todas las respuestas a la pregunta 4
1
17

Lo que necesitas es enganchar el filtro template_include y cargar selectivamente tu plantilla dentro del plugin.

Como buena práctica, si planeas distribuir tu plugin, deberías verificar si archive-my_plugin_lesson.php (o quizás myplugin/archive-lesson.php) existe en el tema, y si no existe, usar la versión del plugin.

De esta manera, es fácil para los usuarios reemplazar la plantilla a través del tema (o tema hijo) sin editar el código del plugin.

Este es el método utilizado por plugins populares, como WooCommmerce, por mencionar un ejemplo.

add_filter('template_include', 'lessons_template');

function lessons_template( $template ) {
  if ( is_post_type_archive('my_plugin_lesson') ) {
    $theme_files = array('archive-my_plugin_lesson.php', 'myplugin/archive-lesson.php');
    $exists_in_theme = locate_template($theme_files, false);
    if ( $exists_in_theme != '' ) {
      return $exists_in_theme;
    } else {
      return plugin_dir_path(__FILE__) . 'archive-lesson.php';
    }
  }
  return $template;
}

Más información en el Codex sobre:

28 sept 2013 14:38:19
Comentarios

¿Esto todavía solo reemplaza el archivo de plantilla del tema, cierto? ¿Qué debo poner en el archivo archive-lesson.php de mi plugin? Necesitaría ser diferente para funcionar con cada tema. Incluso los temas predeterminados "Twenty" no coinciden en qué contenedores div/section rodean el contenido.

Ben Miller Ben Miller
29 sept 2013 07:35:37
0

Puedes usar el hook archive_template para procesar el contenido de una plantilla de archivo del tema, utilizando el esquema a continuación, pero obviamente solo podrás procesar una fracción de los temas disponibles, ya que una plantilla básicamente puede contener cualquier cosa.

El esquema consiste en cargar la plantilla en una cadena ($tpl_str) dentro del filtro archive_template, sustituir tu contenido, incluir la cadena (usando el truco eval( '?>' . $tpl_str );), y luego devolver un archivo en blanco para que el include en "wp-includes/template-loader.php" no haga nada.

A continuación se muestra una versión modificada del código que uso en un plugin, que está dirigido a plantillas "clásicas" que utilizan get_template_part y está más enfocado en procesar plantillas individuales que de archivo, pero debería ayudarte a comenzar. La configuración es que el plugin tiene un subdirectorio llamado "templates" que contiene un archivo en blanco ("null.php") y plantillas de contenido (por ejemplo, "content-single-posttype1.php", "content-archive-postype1.php"), así como una plantilla de respaldo "single.php" para el caso individual, y utiliza una versión personalizada de get_template_part que busca en este directorio.

define( 'MYPLUGIN_FOLDER', dirname( __FILE__ ) . '/' );
define( 'MYPLUGIN_BASENAME', basename( MYPLUGIN_FOLDER ) );

add_filter( 'single_template', 'myplugin_single_template' );
add_filter( 'archive_template', 'myplugin_archive_template' );

function myplugin_single_template( $template ) {
    static $using_null = array();

    // Ajusta con tus tipos de post personalizados.
    $post_types = array( 'posttype1', );

    if ( is_single() || is_archive() ) {
        $template_basename = basename( $template );
        // Esta verificación puede eliminarse.
        if ( $template == '' || substr( $template_basename, 0, 4 ) == 'sing' || substr( $template_basename, 0, 4 ) == 'arch' ) {
            $post_type = get_post_type();
            $slug = is_archive() ? 'archive' : 'single';
            if ( in_array( $post_type, $post_types ) ) {
                // Permite que el usuario lo sobrescriba.
                if ( $single_template = myplugin_get_template( $slug, $post_type ) ) {
                    $template = $single_template;
                } else {
                    // Si no hemos pasado por todo esto antes...
                    if ( empty( $using_null[$slug][$post_type] ) ) {
                        if ( $template && ( $content_template = myplugin_get_template( 'content-' . $slug, $post_type ) ) ) {
                            $tpl_str = file_get_contents( $template );
                            // Tendrás que ajustar estas expresiones regulares a tu propio caso - ¡buena suerte!
                            if ( preg_match( '/get_template_part\s*\(\s*\'content\'\s*,\s*\'' . $slug . '\'\s*\)/', $tpl_str, $matches, PREG_OFFSET_CAPTURE )
                            || preg_match( '/get_template_part\s*\(\s*\'content\'\s*,\s*get_post_format\s*\(\s*\)\s*\)/', $tpl_str, $matches, PREG_OFFSET_CAPTURE )
                            || preg_match( '/get_template_part\s*\(\s*\'content\'\s*\)/', $tpl_str, $matches, PREG_OFFSET_CAPTURE )
                            || preg_match( '/get_template_part\s*\(\s*\'[^\']+\'\s*,\s*\'' . $slug . '\'\s*\)/', $tpl_str, $matches, PREG_OFFSET_CAPTURE ) ) {
                                $using_null[$slug][$post_type] = true;
                                $tpl_str = substr( $tpl_str, 0, $matches[0][1] ) . 'include \'' . $content_template . '\'' . substr( $tpl_str, $matches[0][1] + strlen( $matches[0][0] ) );
                                // Este truco incluye el $tpl_str.
                                eval( '?>' . $tpl_str );
                            }
                        }
                    }
                    if ( empty( $using_null[$slug][$post_type] ) ) {
                        // Falló al analizar - busca una plantilla de respaldo.
                        if ( file_exists( MYPLUGIN_FOLDER . 'templates/' . $slug . '.php' ) ) {
                            $template = MYPLUGIN_FOLDER . 'templates/' . $slug . '.php';
                        }
                    } else {
                        // ¡Éxito! "null.php" es solo un archivo en blanco de cero bytes.
                        $template = MYPLUGIN_FOLDER . 'templates/null.php';
                    }
                }
            }
        }
    }
    return $template;
}

function myplugin_archive_template( $template ) {
    return myplugin_single_template( $template );
}

La versión personalizada de get_template_part:

/*
 * Versión de WP get_template_part() que busca en el tema, luego en el tema padre y finalmente en el directorio de plantillas del plugin (subdirectorio "templates").
 * También busca inicialmente en el subdirectorio "myplugin" si existe en los directorios del tema y del tema padre, para que las plantillas del plugin puedan mantenerse separadas.
 */
function myplugin_get_template( $slug, $part = '' ) {
    $template = $slug . ( $part ? '-' . $part : '' ) . '.php';

    $dirs = array();

    if ( is_child_theme() ) {
        $child_dir = get_stylesheet_directory() . '/';
        $dirs[] = $child_dir . MYPLUGIN_BASENAME . '/';
        $dirs[] = $child_dir;
    }

    $template_dir = get_template_directory() . '/';
    $dirs[] = $template_dir . MYPLUGIN_BASENAME . '/';
    $dirs[] = $template_dir;
    $dirs[] = MYPLUGIN_FOLDER . 'templates/';

    foreach ( $dirs as $dir ) {
        if ( file_exists( $dir . $template ) ) {
            return $dir . $template;
        }
    }
    return false;
}

Para completar, aquí está la plantilla de respaldo "single.php", que utiliza la versión personalizada de get_template_part:

<?php
get_header(); ?>

    <div id="primary" class="content-area">
        <div id="content" class="clearfix">
            <?php while ( have_posts() ) : the_post(); ?>

            <?php if ( $template = myplugin_get_template( 'content-single', get_post_type() ) ) include $template; else get_template_part( 'content', 'single' ); ?>

                <?php
                    // Si los comentarios están abiertos o tenemos al menos un comentario, cargar la plantilla de comentarios
                    if ( comments_open() || '0' != get_comments_number() ) :
                        comments_template();
                    endif;
                ?>

            <?php endwhile; ?>

        </div><!-- #content -->
    </div><!-- #primary -->

<?php get_sidebar(); ?>
<?php get_footer(); ?>
11 sept 2014 19:35:11
2

He estado reflexionando sobre la misma pregunta, y esta es la solución hipotética que he ideado:

  • Dentro del plugin, crear un shortcode que muestre tu bucle de archivo de la forma que desees.
  • Al crear el tipo de contenido personalizado, no habilitar la opción 'archivo'.
  • Añadir una hoja de estilos que controle todos los estilos del contenido de tu bucle.

Al activar el plugin, crear una página usando wp_insert_post con el nombre siendo el tipo de contenido y el contenido siendo el shortcode.

Puedes proporcionar opciones en el shortcode para consideraciones de estilo adicionales, o añadir clases al contenedor del contenido para que coincidan con estilos específicos del tema o personalizados. El usuario también puede añadir contenido adicional antes/después del bucle editando la página.

30 may 2015 22:32:43
Comentarios

Aunque no soy el autor original del post, estaba buscando una solución al mismo problema. He seguido tu solución hipotética y ahora puedo confirmar que también funciona en la práctica.

Lucio Crusca Lucio Crusca
6 ene 2016 16:21:01

¡Genial! Me alegra que esto haya sido útil para alguien. Lo había olvidado por completo.

SkyShab SkyShab
6 ene 2016 17:50:20
1

Puedes usar el filtro single_template. Un ejemplo básico tomado del Codex:

function get_custom_post_type_template($single_template) {
     global $post;

     if ($post->post_type == 'my_post_type') {
          $single_template = dirname( __FILE__ ) . '/post-type-template.php';
     }
     return $single_template;
}

add_filter( "single_template", "get_custom_post_type_template" );
27 sept 2013 22:18:41
Comentarios

Creo que el gancho de filtro para una plantilla de archivo es archive_template, pero no creo que esto funcione para lo que estoy intentando hacer. He editado mi pregunta con más información.

Ben Miller Ben Miller
28 sept 2013 07:49:09