Comment créer une page "virtuelle" dans WordPress


52

J'essaie de créer un point de terminaison d'API personnalisé dans WordPress et je dois rediriger les demandes vers une page virtuelle située dans la racine de WordPress vers une page fournie avec mon plug-in. Donc, fondamentalement, toutes les demandes d’une page sont en réalité acheminées vers une autre page.

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

Le but de ceci est de rendre l'URL du noeud final de l'API aussi courte que possible (comme pour http://mysite.com/xmlrpc.phpenvoyer le fichier de noeud final de l'API avec le plug-in plutôt que d'exiger que l'utilisateur déplace les fichiers dans son installation et / ou pirate son noyau. .

Mon premier essai consistait à ajouter une règle de réécriture personnalisée. Cependant, cela a eu deux problèmes.

  1. Le point final a toujours eu un slash final. C'est devenuhttp://mysite.com/my-api.php/
  2. Ma règle de réécriture n'était que partiellement appliquée. Il ne redirigerait pas vers wp-content/plugins..., il redirigerait vers index.php&wp-content/plugins.... Cela a amené WordPress à afficher une erreur de page introuvable ou tout simplement à afficher par défaut la page d'accueil.

Des idées? Suggestions?

Réponses:


55

Il existe deux types de règles de réécriture dans WordPress: les règles internes (stockées dans la base de données et analysées par WP :: parse_request () ) et les règles externes (stockées .htaccesset analysées par Apache). Vous pouvez choisir l’une ou l’autre solution, en fonction de la quantité de WordPress dont vous avez besoin dans votre fichier appelé.

Règles externes:

La règle externe est la plus facile à configurer et à suivre. Il s’exécutera my-api.phpdans votre répertoire de plugin, sans rien charger à partir 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 );
}

Règles internes:

La règle interne nécessite un peu plus de travail: nous ajoutons d'abord une règle de réécriture qui ajoute un vars de requête, puis nous rendons cette requête var publique, puis nous devons vérifier l'existence de cette requête var pour passer le contrôle à notre fichier de plug-in. Au moment où nous faisons cela, l’initialisation habituelle de WordPress aura déjà eu lieu (nous nous échappons juste avant la requête normale).

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
Je veux juste ajouter qu'il est important d'aller à la page Permalinks et de cliquer sur "Enregistrer les modifications" dans WP-Admin. Je jouais avec cela pendant une heure avant de penser que je devais rafraîchir les permaliens ... À moins que quelqu'un connaisse une fonction capable de le faire?
ethanpil

Pour la règle externe: le chemin d'accès à ma racine Web ayant un caractère d'espacement, cela a provoqué la chute d'apache. Les espaces blancs doivent être évités s’ils existent dans le chemin de votre installation wordpress.
Willster

1
Fonctionne, mais je ne semble pas pouvoir accéder aux variables de requête passées avec get_query_vars()dans my-api.php. J'ai vérifié quelles variables sont chargées. Et la seule variable définie est aa WP objectappelée $wp. Comment puis-je accéder ou transformer cela en un WP_Queryobjet pour pouvoir accéder aux variables transmises avec get_query_vars()?
Jules

1
@Jules: Lorsque vous êtes includeun fichier, il est exécuté dans la portée actuelle. Dans ce cas, c'est la wpse9870_parse_requestfonction qui n'a que le $wpparamètre. Il est possible que l' $wp_queryobjet global n'ait pas encore été défini. Cela get_query_var()ne fonctionnera donc pas. Cependant, vous avez de la chance: $wpla classe qui contient le query_varsmembre dont vous avez besoin - je l’utilise moi-même dans le code ci-dessus.
Jan Fabry

1
essayer de créer des règles de réécriture externes. ajouté votre premier morceau de code mais je reçois toujours 404. btw: règles de réécriture
flush

12

Cela a fonctionné pour moi. Je n'ai jamais touché l'API de réécriture, mais je suis toujours prêt à me pousser dans de nouvelles directions. Ce qui suit a fonctionné sur mon serveur de test pour 3.0 situé dans un sous-dossier de localhost. Je ne vois pas de problème si WordPress est installé dans la racine Web.

Il suffit de déposer ce code dans un plugin et de télécharger le fichier nommé "taco-kittens.php" directement dans le dossier du plugin. Vous aurez besoin d'écrire un flush dur pour vos permaliens. Je pense qu'ils disent que le meilleur moment pour le faire est l'activation du plugin.

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' );

Meilleurs voeux, -Mike


1
J'ai eu une erreur d'accès refusé en essayant ce code. Je soupçonne que mon serveur ou WP n'a pas aimé l'URL absolue. Ceci, en revanche, a bien fonctionné:add_rewrite_rule( 'taco-kittens', 'wp-content/plugins/taco-kittens.php', 'top' );
Jules

Pouvez-vous s'il vous plaît laissez-moi savoir, ce que je devrais mettre dans taco-kittens.php, je n'ai pas connaissance de .htaccess ou url rewrite.
Prafulla Kumar Sahu

9

Une raison de ne pas faire quelque chose comme ça à la place?

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

Ensuite, connectez simplement votre plugin à 'init' et vérifiez que la variable get. S'il existe, faites ce que votre plugin doit faire et mourrez ()


5
Cela fonctionnerait, mais j'essaie de faire une distinction très claire entre les variables de requête et le point final. Il pourrait y avoir d'autres arguments de requête à l'avenir, et je ne veux pas que les utilisateurs mélangent les choses.
EAMann

Et si vous gardiez la réécriture, mais la réécriviez dans la variable GET? Vous pouvez également voir comment fonctionne la réécriture de robots.txt. Cela pourrait vous aider à comprendre comment éviter la redirection vers mon-api.php /
Will Anderson

C'est la solution idéale si vous ne vous souciez pas des robots comme les appels d'API.
Beytarovski

4

Je ne comprends peut-être pas bien vos questions, mais un simple shortcode résoudrait-il votre problème?

Pas:

  1. Demandez au client de créer une page, par exemple http://mysite.com/my-api
  2. Demandez au client d'ajouter un shortcode dans cette page, c'est-à-dire [my-api-shortcode]

La nouvelle page agit comme un point de terminaison de l'API et votre shortcode envoie des demandes à votre code de plug-in à l' adresse http://mysite.com/wp-content/plugins/my-plugin/my-api.php.

(bien sûr, cela signifie que my-api.php aurait défini le shortcode)

Vous pouvez probablement automatiser les étapes 1 et 2 via le plugin.


1

Je n'ai pas encore beaucoup traité de réécriture, donc c'est probablement un peu difficile, mais cela semble fonctionner:

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() );
}

Cela fonctionne si vous accrochez ceci à 'generate_rewrite_rules', mais il doit y avoir un meilleur moyen, car vous ne voulez pas réécrire .htaccess à chaque chargement de page.
On dirait que je ne peux pas arrêter de modifier mes propres messages ... il devrait probablement plutôt aller dans vous activez callback et référence globale $ wp_rewrite à la place. Ensuite, supprimez l'entrée de non_wp_rules et envoyez-la à nouveau à .htaccess en désactivant le rappel.

Et enfin, l'écriture dans .htaccess devrait être un peu plus sophistiquée, vous voulez seulement remplacer la section wordpress qui s'y trouve.


1

J'avais une exigence similaire et je voulais créer plusieurs points de terminaison basés sur des slugs uniques pointant vers le contenu généré par le plugin.

Regardez la source de mon plugin: https://wordpress.org/extend/plugins/picasa-album-uploader/

La technique que j'ai utilisée commence par l'ajout d'un filtre permettant the_postsd'examiner la demande entrante. Si le plugin doit le gérer, un post factice est généré et une action est ajoutée pour template_redirect.

Lorsque l' template_redirectaction est appelée, il doit en résulter la sortie de l'intégralité du contenu de la page à afficher et à quitter, sinon le résultat doit être renvoyé sans qu'aucun résultat ne soit généré. Voir le code dans wp_include/template-loader.phpet vous verrez pourquoi.


1

J'utilise une autre approche qui consiste à forcer la page d'accueil à charger un titre, un contenu et un modèle de page personnalisés .

La solution est très soignée puisqu'elle peut être mise en œuvre lorsqu'un utilisateur suit un lien convivial tel que http://example.com/ ? Plugin_page = myfakepage

Il est très facile à mettre en œuvre et devrait permettre un nombre illimité de pages.

Code et instructions ici: Générez une page Wordpress personnalisée / fictive / virtuelle à la volée


0

J'utilise une approche similaire à celle décrite ci-dessus par Xavi Esteve, qui a cessé de fonctionner en raison d'une mise à niveau de WordPress, pour autant que je sache, au deuxième semestre de 2013.

C'est documenté en détail ici: https://stackoverflow.com/questions/17960649/wordpress-plugin-generating-virtual-pages-and-using-theme-template

La partie clé de mon approche consiste à utiliser le modèle existant afin que la page résultante ressemble à une partie du site; Je voulais que ce soit aussi compatible que possible avec tous les thèmes, espérons-le, dans les versions de WordPress. Le temps dira si j'avais raison!


0

c'est un exemple de lecture en production, créez d'abord une classe de page virtuelle:


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;
    }
}

Dans l'étape suivante, accrocher l' template_redirectaction et gérer votre page virtuelle comme ci-dessous

    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;


                }


            } );
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.