¿Cómo se crea una página "virtual" en WordPress


52

Estoy tratando de crear un punto final API personalizado en WordPress, y necesito redirigir las solicitudes a una página virtual en la raíz de WordPress a una página real que se envía con mi complemento. Básicamente, todas las solicitudes a una página se enrutan a la otra.

Ejemplo:
http://mysite.com/my-api.php=>http://mysite.com/wp-content/plugins/my-plugin/my-api.php

El objetivo de esto es hacer que la url para el punto final de la API sea lo más breve posible (similar a http://mysite.com/xmlrpc.phpenviar el archivo de punto final de la API real con el complemento en lugar de requerir que el usuario mueva los archivos en su instalación y / o piratee el núcleo .

Mi primer intento fue agregar una regla de reescritura personalizada. Sin embargo, esto tuvo dos problemas.

  1. El punto final siempre tuvo una barra diagonal final. Se convirtióhttp://mysite.com/my-api.php/
  2. Mi regla de reescritura solo se aplicó parcialmente. No redirigiría a wp-content/plugins..., redirigiría a index.php&wp-content/plugins.... Esto llevó a WordPress a mostrar un error de página no encontrada o simplemente a la página principal.

Ideas? Sugerencias?

Respuestas:


55

Hay dos tipos de reglas de reescritura en WordPress: reglas internas (almacenadas en la base de datos y analizadas por WP :: parse_request () ) y reglas externas (almacenadas .htaccessy analizadas por Apache). Puede elegir de cualquier manera, dependiendo de la cantidad de WordPress que necesite en su archivo llamado.

Reglas externas:

La regla externa es la más fácil de configurar y seguir. Se ejecutará my-api.phpen su directorio de complementos, sin cargar nada de WordPress.

add_action( 'init', 'wpse9870_init_external' );
function wpse9870_init_external()
{
    global $wp_rewrite;
    $plugin_url = plugins_url( 'my-api.php', __FILE__ );
    $plugin_url = substr( $plugin_url, strlen( home_url() ) + 1 );
    // The pattern is prefixed with '^'
    // The substitution is prefixed with the "home root", at least a '/'
    // This is equivalent to appending it to `non_wp_rules`
    $wp_rewrite->add_external_rule( 'my-api.php$', $plugin_url );
}

Reglas internas:

La regla interna requiere algo más de trabajo: primero agregamos una regla de reescritura que agrega una consulta vars, luego hacemos pública esta consulta var, y luego necesitamos verificar la existencia de esta consulta var para pasar el control a nuestro archivo de complemento. En el momento en que hagamos esto, la inicialización habitual de WordPress habrá sucedido (nos separamos justo antes de la consulta posterior normal).

add_action( 'init', 'wpse9870_init_internal' );
function wpse9870_init_internal()
{
    add_rewrite_rule( 'my-api.php$', 'index.php?wpse9870_api=1', 'top' );
}

add_filter( 'query_vars', 'wpse9870_query_vars' );
function wpse9870_query_vars( $query_vars )
{
    $query_vars[] = 'wpse9870_api';
    return $query_vars;
}

add_action( 'parse_request', 'wpse9870_parse_request' );
function wpse9870_parse_request( &$wp )
{
    if ( array_key_exists( 'wpse9870_api', $wp->query_vars ) ) {
        include 'my-api.php';
        exit();
    }
    return;
}

3
Solo quiero agregar que es importante ir a la página de enlaces permanentes y hacer clic en "Guardar cambios" en el WP-Admin. Estuve jugando con esto durante una hora antes de pensar que necesitaba actualizar los enlaces permanentes ... ¿A menos que alguien conozca una función que pueda hacer eso?
ethanpil

Para la regla externa: debido a que la ruta a mi raíz web tenía un carácter de espacio en blanco, esto provocó que Apache se cayera. Los espacios en blanco deben escaparse si existen en el camino hacia la instalación de WordPress.
Willster

1
Funciona, pero parece que no puedo acceder a ninguna variable de consulta aprobada get_query_vars()en my-api.php. Verifiqué qué variables están cargadas. Y la única var que se establece es aa WP objectllamada $wp. ¿Cómo puedo acceder o transformar esto en un WP_Queryobjeto para poder acceder a las variables pasadas get_query_vars()?
Jules

1
@Jules: cuando includeun archivo, se ejecuta en el ámbito actual. En este caso, es la wpse9870_parse_requestfunción, que solo tiene el $wpparámetro. Es posible que el $wp_queryobjeto global no se haya configurado en este momento, por get_query_var()lo que no funcionará. Sin embargo, tienes suerte: $wpes la clase que contiene el query_varsmiembro que necesitas; lo uso yo mismo en el código anterior.
Jan Fabry

1
tratando de crear una regla de reescritura externa. agregó su primer fragmento de código, pero aún obtengo
Sisir

12

Esto funcionó para mí. Nunca toco la API de reescritura, pero siempre estoy dispuesto a empujarme en nuevas direcciones. Lo siguiente funcionó en mi servidor de prueba para 3.0 ubicado en una subcarpeta de localhost. No veo ningún problema si WordPress está instalado en la raíz web.

Simplemente suelte este código en un complemento y cargue el archivo llamado "taco-kittens.php" directamente en la carpeta del complemento. Necesitará escribir un rubor duro para sus enlaces permanentes. Creo que dicen que el mejor momento para hacer esto es activar el complemento.

function taco_kitten_rewrite() {
    $url = str_replace( trailingslashit( site_url() ), '', plugins_url( '/taco-kittens.php', __FILE__ ) );
    add_rewrite_rule( 'taco-kittens\\.php$', $url, 'top' );
}
add_action( 'wp_loaded', 'taco_kitten_rewrite' );

Mis mejores deseos, -Mike


1
Recibí un error de acceso denegado al intentar este código. Sospecho que a mi servidor o WP no le gustó la URL absoluta. Esto, por otro lado, funcionó bien:add_rewrite_rule( 'taco-kittens', 'wp-content/plugins/taco-kittens.php', 'top' );
Julio

¿Puede decirme qué debo poner en taco-kittens.php? No tengo conocimiento de .htaccess o reescritura de URL.
Prafulla Kumar Sahu

9

¿Alguna razón para no hacer algo como esto?

http://mysite.com/?my-api=1

Luego solo conecta tu plugin a 'init' y verifica que esa variable sea variable. Si existe, haga lo que su complemento necesita hacer y muera ()


55
Eso funcionaría, pero estoy tratando de proporcionar una distinción muy clara entre las variables de consulta y el punto final real. Puede haber otros argumentos de consulta en el futuro, y no quiero que los usuarios mezclen las cosas.
EAMann

¿Qué pasa si mantuvo la reescritura, pero la reescribió en la variable GET? También puede ver cómo funciona la reescritura de robots.txt. Podría ayudarlo a descubrir cómo evitar la redirección a my-api.php /
Will Anderson

Es la solución perfecta si no te importan los robots como las llamadas API.
beytarovski

4

Es posible que no entienda sus preguntas por completo, pero ¿un simple shortcode resolvería su problema?

Pasos:

  1. Haga que el cliente cree una página, es decir, http://mysite.com/my-api
  2. Haga que el cliente agregue un shortcode en esa página, es decir, [my-api-shortcode]

La nueva página actúa como un punto final de API y su shortcode envía solicitudes a su código de complemento en http://mysite.com/wp-content/plugins/my-plugin/my-api.php

(por supuesto, esto significa que my-api.php tendría el shortcode definido)

Probablemente pueda automatizar los pasos 1 y 2 a través del complemento.


1

Todavía no me he ocupado tanto de la reescritura, por lo que probablemente sea un poco difícil, pero parece funcionar:

function api_rewrite($wp_rewrite) {
    $wp_rewrite->non_wp_rules['my-api\.php'] = 'wp-content/plugins/my-plugin/my-api.php';
    file_put_contents(ABSPATH.'.htaccess', $wp_rewrite->mod_rewrite_rules() );
}

Funciona si conecta esto a 'generate_rewrite_rules', pero debe haber una mejor manera, ya que no desea reescribir .htaccess en cada carga de página.
Parece que no puedo dejar de editar mis propias publicaciones ... probablemente debería entrar en activar la devolución de llamada y hacer referencia a $ wp_rewrite global. Y luego elimine la entrada de non_wp_rules y la salida a .htaccess nuevamente cuando desactive la devolución de llamada.

Y finalmente, la escritura en .htaccess debería ser un poco más sofisticada, solo desea reemplazar la sección de wordpress allí.


1

Tenía un requisito similar y quería crear varios puntos finales basados ​​en babosas únicas que apuntaran al contenido generado por el complemento.

Echa un vistazo a la fuente de mi complemento: https://wordpress.org/extend/plugins/picasa-album-uploader/

La técnica que utilicé comienza agregando un filtro para the_postsexaminar la solicitud entrante. Si el complemento lo maneja, se genera una publicación ficticia y se agrega una acción template_redirect.

Cuando template_redirectse llama a la acción, debe dar como resultado que se muestre todo el contenido de la página para que se muestre y salga, o debe regresar sin generar ningún resultado. Vea el código wp_include/template-loader.phpy verá por qué.



0

Estoy usando un enfoque similar al anterior de Xavi Esteve, que dejó de funcionar debido a una actualización de WordPress por lo que pude ver en la segunda mitad de 2013.

Está documentado con gran detalle aquí: https://stackoverflow.com/questions/17960649/wordpress-plugin-generating-virtual-pages-and-using-theme-template

La parte clave de mi enfoque es usar la plantilla existente para que la página resultante se vea como parte del sitio; Quería que fuera lo más compatible posible con todos los temas, con suerte en todos los lanzamientos de WordPress. ¡El tiempo dirá si tenía razón!


0

es un ejemplo de producción, primero cree una clase de página virtual:


class VirtualPage
{

    private $query;
    private $title;
    private $content;
    private $template;
    private $wp_post;

    function __construct($query = '/index2', $template = 'page', $title = 'Untitled')
    {
        $this->query = filter_var($query, FILTER_SANITIZE_URL);
        $this->setTemplate($template);
        $this->setTitle($title);
    }

    function getQuery()
    {
        return $this->query;
    }

    function getTemplate()
    {
        return $this->template;
    }

    function getTitle()
    {
        return $this->title;
    }

    function setTitle($title)
    {
        $this->title = filter_var($title, FILTER_SANITIZE_STRING);

        return $this;
    }

    function setContent($content)
    {
        $this->content = $content;

        return $this;
    }

    function setTemplate($template)
    {
        $this->template = $template;

        return $this;
    }

    public function updateWpQuery()
    {

        global $wp, $wp_query;

        // Update the main query
        $wp_query->current_post = $this->wp_post->ID;
        $wp_query->found_posts = 1;
        $wp_query->is_page = true;//important part
        $wp_query->is_singular = true;//important part
        $wp_query->is_single = false;
        $wp_query->is_attachment = false;
        $wp_query->is_archive = false;
        $wp_query->is_category = false;
        $wp_query->is_tag = false;
        $wp_query->is_tax = false;
        $wp_query->is_author = false;
        $wp_query->is_date = false;
        $wp_query->is_year = false;
        $wp_query->is_month = false;
        $wp_query->is_day = false;
        $wp_query->is_time = false;
        $wp_query->is_search = false;
        $wp_query->is_feed = false;
        $wp_query->is_comment_feed = false;
        $wp_query->is_trackback = false;
        $wp_query->is_home = false;
        $wp_query->is_embed = false;
        $wp_query->is_404 = false;
        $wp_query->is_paged = false;
        $wp_query->is_admin = false;
        $wp_query->is_preview = false;
        $wp_query->is_robots = false;
        $wp_query->is_posts_page = false;
        $wp_query->is_post_type_archive = false;
        $wp_query->max_num_pages = 1;
        $wp_query->post = $this->wp_post;
        $wp_query->posts = array($this->wp_post);
        $wp_query->post_count = 1;
        $wp_query->queried_object = $this->wp_post;
        $wp_query->queried_object_id = $this->wp_post->ID;
        $wp_query->query_vars['error'] = '';
        unset($wp_query->query['error']);

        $GLOBALS['wp_query'] = $wp_query;

        $wp->query = array();
        $wp->register_globals();

    }

    public function createPage()
    {
        if (is_null($this->wp_post)) {
            $post = new stdClass();
            $post->ID = -99;
            $post->ancestors = array(); // 3.6
            $post->comment_status = 'closed';
            $post->comment_count = 0;
            $post->filter = 'raw';
            $post->guid = home_url($this->query);
            $post->is_virtual = true;
            $post->menu_order = 0;
            $post->pinged = '';
            $post->ping_status = 'closed';
            $post->post_title = $this->title;
            $post->post_name = sanitize_title($this->template); // append random number to avoid clash
            $post->post_content = $this->content ?: '';
            $post->post_excerpt = '';
            $post->post_parent = 0;
            $post->post_type = 'page';
            $post->post_status = 'publish';
            $post->post_date = current_time('mysql');
            $post->post_date_gmt = current_time('mysql', 1);
            $post->modified = $post->post_date;
            $post->modified_gmt = $post->post_date_gmt;
            $post->post_password = '';
            $post->post_content_filtered = '';
            $post->post_author = is_user_logged_in() ? get_current_user_id() : 0;
            $post->post_content = '';
            $post->post_mime_type = '';
            $post->to_ping = '';

            $this->wp_post = new WP_Post($post);
            $this->updateWpQuery();

            @status_header(200);
            wp_cache_add(-99, $this->wp_post, 'posts');

        }


        return $this->wp_post;
    }
}

En el siguiente paso, engancha la template_redirectacción y maneja tu página virtual como a continuación

    add_action( 'template_redirect', function () {


                    switch ( get_query_var( 'name' ,'') ) {

                        case 'contact':
                            // http://yoursite/contact  ==> loads page-contact.php
                            $page = new VirtualPage( "/contact", 'contact',__('Contact Me') );
                            $page->createPage();
                            break;

                        case 'archive':
                            // http://yoursite/archive  ==> loads page-archive.php
                            $page = new VirtualPage( "/archive", 'archive' ,__('Archives'));
                            $page->createPage();
                            break;

                        case 'blog':
                            // http://yoursite/blog  ==> loads page-blog.php
                            $page = new VirtualPage( "/blog", 'blog' ,__('Blog'));
                            $page->createPage();
                            break;


                }


            } );
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.