Como você cria uma página "virtual" no WordPress

52

Estou tentando criar um terminal de API personalizado no WordPress e preciso redirecionar solicitações para uma página virtual na raiz do WordPress para uma página real que é fornecida com meu plug-in. Então, basicamente, todos os pedidos para uma página são realmente roteados para a outra.

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

O objetivo é tornar o URL do terminal da API o mais curto possível (semelhante a http://mysite.com/xmlrpc.phpenviar o arquivo real do terminal da API com o plug-in, em vez de exigir que o usuário mova arquivos na instalação e / ou hack core .

Minha primeira tentativa foi adicionar uma regra de reescrita personalizada. No entanto, isso teve dois problemas.

  1. O ponto final sempre teve uma barra final. Tornou-sehttp://mysite.com/my-api.php/
  2. Minha regra de reescrita foi aplicada apenas parcialmente. Não redirecionaria para wp-content/plugins..., redirecionaria para index.php&wp-content/plugins.... Isso levou o WordPress a exibir um erro de página não encontrada ou apenas padronizar a página inicial.

Idéias? Sugestões?

EAMann
fonte

Respostas:

55

Existem dois tipos de regras de reescrita no WordPress: regras internas (armazenadas no banco de dados e analisadas por WP :: parse_request () ) e regras externas (armazenadas .htaccesse analisadas pelo Apache). Você pode escolher de qualquer maneira, dependendo da quantidade de WordPress necessária no seu arquivo chamado.

Regras externas:

A regra externa é a mais fácil de configurar e seguir. Ele será executado my-api.phpno diretório do plugin, sem carregar nada do 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 );
}

Regras internas:

A regra interna requer mais trabalho: primeiro, adicionamos uma regra de reescrita que adiciona vários consultas, depois tornamos essa consulta pública e, em seguida, precisamos verificar a existência dessa consulta para passar o controle ao nosso arquivo de plug-in. Quando fizermos isso, a inicialização usual do WordPress terá acontecido (nos separamos logo antes da consulta de postagem regular).

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;
}
Jan Fabry
fonte
3
Eu só quero acrescentar que é importante ir para a página Permalinks e clicar em "Salvar alterações" no WP-Admin. Eu estava brincando com isso por uma hora antes de pensar que precisava atualizar os permalinks ... A menos que alguém conheça uma função que possa fazer isso?
Ethanpil 28/05
Para a regra externa: como o caminho para minha raiz da web tinha um caractere de espaço em branco, isso causou uma queda no apache. Espaços em branco precisam ser escapados se existirem no caminho para a instalação do wordpress.
Willster
11
Funciona, mas não consigo acessar nenhuma variável de consulta passada get_query_vars()no meu-api.php. Eu verifiquei quais variáveis ​​estão carregadas. E o único var definido é um WP objectchamado $wp. Como acessar ou transformar isso em um WP_Queryobjeto para que eu possa acessar as variáveis ​​passadas get_query_vars()?
Jules
11
@Jules: Quando você includeum arquivo, ele é executado no escopo atual. Nesse caso, é a wpse9870_parse_requestfunção que possui apenas o $wpparâmetro É possível que o $wp_queryobjeto global não tenha sido definido no momento, portanto get_query_var()não funcionará. No entanto, você tem sorte: $wpé a classe que contém o query_varsmembro que você precisa - eu mesmo o uso no código acima.
Jan Fabry
11
tentando criar regras de reescrita externas. acrescentou o seu pedaço punho de código, mas ainda estou recebendo 404. btw: reescrever as regras coradas
Sisir
12

Isso funcionou para mim. Eu nunca toco na API de reescrita, mas estou sempre pronto para me impulsionar em novas direções. O seguinte funcionou no meu servidor de teste para 3.0, localizado em uma subpasta localhost. Não vejo nenhum problema se o WordPress estiver instalado na raiz da web.

Basta soltar esse código em um plug-in e enviar o arquivo chamado "taco-kittens.php" diretamente na pasta do plug-in. Você precisará escrever um hard flush para seus permalinks. Eu acho que eles dizem que o melhor momento para fazer isso é na ativação do 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' );

Muitas felicidades, -Mike

mfields
fonte
11
Recebi um erro de acesso negado ao tentar esse código. Suspeito que meu servidor ou WP não tenha gostado do URL absoluto. Por outro lado, isso funcionou bem:add_rewrite_rule( 'taco-kittens', 'wp-content/plugins/taco-kittens.php', 'top' );
Jules
Você pode me informar o que devo colocar no taco-kittens.php, não tenho conhecimento de .htaccess ou reescrita de URL.
Prafulla Kumar Sahu
9

Alguma razão para não fazer algo assim?

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

Em seguida, basta conectar seu plug-in em 'init' e verificar se essa variável é obtida. Se existir, faça o que seu plug-in precisa fazer e morra ()

Will Anderson
fonte
5
Isso funcionaria, mas estou tentando fornecer uma distinção muito clara entre as variáveis ​​de consulta e o ponto de extremidade real. Pode haver outros argumentos de consulta no futuro, e eu não quero que os usuários misturem as coisas.
EAMann
E se você mantivesse a reescrita, mas reescrevesse para a var GET? Você também pode ver como a reescrita do robots.txt funciona. Pode ajudar você a descobrir como evitar o redirecionamento para my-api.php /
Will Anderson
É a solução perfeita se você não se importa com robôs, como chamadas de API.
beytarovski
4

Talvez eu não esteja entendendo suas perguntas completamente, mas um código de acesso simples resolveria seu problema?

Passos:

  1. Peça ao cliente que crie uma página, por exemplo, http://mysite.com/my-api
  2. Peça ao cliente para adicionar um código curto nessa página, ou seja, [my-api-shortcode]

A nova página atua como um ponto final da API e seu código de acesso envia solicitações para o código do plug-in em http://mysite.com/wp-content/plugins/my-plugin/my-api.php

(é claro que isso significa que meu-api.php teria o código abreviado definido)

Provavelmente, você pode automatizar as etapas 1 e 2 através do plug-in.

rexposadas
fonte
1

Ainda não lidei com reescrever muito, então isso provavelmente é um pouco difícil, mas 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 se você conectar isso a 'generate_rewrite_rules', mas deve haver uma maneira melhor, pois você não deseja reescrever .htaccess em cada carregamento de página.
Parece que não consigo parar de editar minhas próprias postagens ... provavelmente deve entrar em você para ativar o retorno de chamada e referenciar global $ wp_rewrite. E, em seguida, remova a entrada de non_wp_rules e a saída para .htaccess novamente, ao desativar o retorno de chamada.

E, finalmente, a escrita em .htaccess deve ser um pouco mais sofisticada, você deseja substituir apenas a seção wordpress.

Wyrfel
fonte
1

Eu tinha um requisito semelhante e queria criar vários pontos de extremidade com base em slugs únicos que apontavam para o conteúdo gerado pelo plug-in.

Dê uma olhada na fonte do meu plugin: https://wordpress.org/extend/plugins/picasa-album-uploader/

A técnica que usei começa adicionando um filtro para the_postsexaminar a solicitação recebida. Se o plug-in deve lidar com isso, uma postagem fictícia é gerada e uma ação é adicionada template_redirect.

Quando a template_redirectação é chamada, ela deve resultar na saída de todo o conteúdo da página a ser exibido e sair ou deve retornar sem saída gerada. Veja o código wp_include/template-loader.phpe você verá o porquê.

Ken
fonte
0

Estou usando uma abordagem semelhante à do Xavi Esteve acima, que parou de funcionar devido a uma atualização do WordPress, tanto quanto pude perceber no segundo semestre de 2013.

Está documentado em grandes detalhes aqui: https://stackoverflow.com/questions/17960649/wordpress-plugin-generating-virtual-pages-and-using-theme-template

A parte principal da minha abordagem é usar o modelo existente para que a página resultante pareça fazer parte do site; Eu queria que fosse o mais compatível possível com todos os temas, espero que nas versões do WordPress. O tempo dirá se eu estava certa!

Brian C
fonte
0

é um exemplo real de produção, primeiro crie uma classe 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;
    }
}

Na próxima etapa, conecte a template_redirectação e lide com sua página virtual como abaixo

    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;


                }


            } );
Mr.Hosseini
fonte