Como forçar o download de um arquivo no back-end do WordPress?

30

Gostaria de adicionar um botão "Clique para fazer o download" a um dos meus plugins do WordPress e não tenho certeza de qual gancho usar. Até agora, conectar 'admin_init' a este código parece funcionar:

 header("Content-type: application/x-msdownload");
 header("Content-Disposition: attachment; filename=data.csv");
 header("Pragma: no-cache");
 header("Expires: 0");
 echo 'data';
 exit();

Isso parece funcionar, mas eu só quero ver se há uma prática recomendada por aí.

Obrigado Dave

Dave Morris
fonte

Respostas:

39

Se eu entendi corretamente, você deseja ter um URL semelhante ao seguinte, cuja resposta ao navegador será o conteúdo que você gerar, ou seja, seu .CSVarquivo e nenhum conteúdo gerado no WordPress?

http://example.com/download/data.csv

Eu acho que você está procurando o 'template_redirect'gancho. Você pode encontrar 'template_redirect'em /wp-includes/template-loader.phpqual arquivo todos os desenvolvedores do WordPress devem se familiarizar; é curto e agradável e roteia cada carregamento de página que não seja de administrador. Verifique isso.

Basta adicionar o seguinte para o seu tema functions.phparquivo ou em outro arquivo que você includeem functions.php:

add_action('template_redirect','yoursite_template_redirect');
function yoursite_template_redirect() {
  if ($_SERVER['REQUEST_URI']=='/downloads/data.csv') {
    header("Content-type: application/x-msdownload",true,200);
    header("Content-Disposition: attachment; filename=data.csv");
    header("Pragma: no-cache");
    header("Expires: 0");
    echo 'data';
    exit();
  }
}

Anote o teste do '/downloads/data.csv'URL inspecionando $_SERVER['REQUEST_URI']. Observe também o valor adicionado ,true,200à sua header()chamada, onde você define Content-type; isso ocorre porque o WordPress definirá o código de status 404 "Não encontrado" porque não reconhece o URL. Não há problema, pois as trueinstruções header()para substituir o 404WordPress foram definidas e usar o código de status HTTP 200 "OK" .

E aqui está o que parece no FireFox ( observe que a captura de tela não possui um /downloads/diretório virtual porque, após capturar e anotar a captura de tela, parecia uma boa ideia adicionar um '/downloads/'diretório virtual):

Captura de tela de um URL de download para um arquivo CSV
(fonte: mikeschinkel.com )

ATUALIZAR

Se você deseja que o download seja manipulado a partir de um URL prefixado /wp-admin/para fornecer ao usuário a indicação visual de que ele está protegido por um login, você também pode fazer isso; a descrição de uma maneira segue.

I encapsulado em uma classe desta vez, chamou DownloadCSV, e criado um usuário "capacidade" chamado 'download_csv'para o 'administrator'papel (leia sobre as Funções e Capacidades aqui ) Você poderia apenas nas costas fora do predefinido 'export'papel se você gosta e se assim for basta procurar e substituir 'download_csv'com 'export'e remova a register_activation_hook()chamada e a activate()função. A propósito, a necessidade de um gancho de ativação é uma das razões pelas quais mudei isso para um plug-in em vez de mantê-lo no functions.phparquivo do tema . *

Eu também adicionei uma opção de menu "Download CSV" no menu "Ferramentas" usando add_submenu_page()e vinculando-a à 'download_csv'capacidade.

Por fim, escolhi o 'plugins_loaded'gancho porque era o gancho apropriado mais antigo que eu poderia usar. Você poderia usar, 'admin_init'mas esse gancho é executado muito mais tarde (1130º telefonema vs. 3º telefonema), então por que deixar o WordPress fazer mais trabalho descartável do que o necessário? (Eu usei meu plug - in Instrument Hooks para descobrir qual gancho usar.)

No gancho, verifico para garantir que meu URL comece /wp-admin/tools.phpinspecionando a $pagenowvariável, verifico que, current_user_can('download_csv')se isso passa, testo $_GET['download']para ver se ela contém data.csv; se sim, rodamos praticamente o mesmo código de antes. Também removo o ,true,200da chamada para header()no exemplo anterior, porque aqui o WordPress sabe que é um bom URL, por isso ainda não definiu o status 404. Então, aqui está o seu código:

<?php
/*
Plugin Name: Download CSV
Author: Mike Schinkel
Author URI: http://mikeschinkel.com
 */
if (!class_exists('DownloadCSV')) {
  class DownloadCSV {
    static function on_load() {
      add_action('plugins_loaded',array(__CLASS__,'plugins_loaded'));
      add_action('admin_menu',array(__CLASS__,'admin_menu'));
      register_activation_hook(__FILE__,array(__CLASS__,'activate'));
    }
    static function activate() {
      $role = get_role('administrator');
      $role->add_cap('download_csv');
    }
    static function admin_menu() {
      add_submenu_page('tools.php',    // Parent Menu
        'Download CSV',                // Page Title
        'Download CSV',                // Menu Option Label
        'download_csv',                // Capability
        'tools.php?download=data.csv');// Option URL relative to /wp-admin/
    }
    static function plugins_loaded() {
      global $pagenow;
      if ($pagenow=='tools.php' && 
          current_user_can('download_csv') && 
          isset($_GET['download'])  && 
          $_GET['download']=='data.csv') {
        header("Content-type: application/x-msdownload");
        header("Content-Disposition: attachment; filename=data.csv");
        header("Pragma: no-cache");
        header("Expires: 0");
        echo 'data';
        exit();
      }
    }
  }
  DownloadCSV::on_load();
}

E aqui está uma captura de tela do plug-in ativado: (fonte: mikeschinkel.com )Captura de tela da página de plug-in mostrando um plug-in ativado

E finalmente, aqui está uma captura de tela do acionamento do download: (fonte: mikeschinkel.com )Captura de tela de Download de um arquivo por URL a partir de uma opção do menu Ferramentas do administrador do WordPress

MikeSchinkel
fonte
Mike, obrigado pela sua ajuda. O único problema com esse recurso é que eu gostaria que o arquivo fosse baixado do back-end. Parece que template_redirect não funciona no back-end e, se eu não devo usar admin_init, me pergunto o que devo usar. admin_init parece funcionar para mim agora, eu posso ficar com ela pelo menos a curto prazo. É um recurso menor que apenas algumas pessoas vão usar.
Dave Morris
@ Dave Morris - Você pode definir o que você quer dizer com "back-end" ? Você quer dizer no servidor? Se sim, 'template_redirect'definitivamente é executado no servidor. Se não, eu estava totalmente confuso; você pode esclarecer a preocupação? Desde já, obrigado.
MikeSchinkel
@ Dave: Se você quer dizer a área administrativa pelo "back-end", isso ainda funcionará. O URL de download começa com /downloads/data.csv, que é um arquivo inexistente, para que o "front end" do WordPress lide com essa solicitação e, eventualmente, alcance template-redirect. Você acabou de criar um link na área de administração que aponta para esse URL frontal. (Deve ser dito que esta maneira, você não obter a proteção login de administrador de graça? - Qualquer um que conheça o URL pode baixar o arquivo, mas talvez haja uma maneira fácil de corrigir isso)
Jan Fabry
@ Jan Fabry - Ah, eu entendo agora. Por "back-end", ele quis dizer de dentro do administrador, certo? Ele pode usar a função current_user_can()com o código acima ou seguir outra abordagem. Após esse comentário, adicionarei uma atualização à minha resposta.
precisa saber é o seguinte
Sim, peço desculpas, não estou recebendo alertas por e-mail deste site, o que explica meu atraso em responder. Na verdade, eu estava me referindo à área administrativa do WordPress quando disse "back-end". Me desculpe por isso. Vou tentar usar o template_redirect e ver o que acontece. Obrigado! ~ Dave
Dave Morris
3

mais um plugin útil para exportar para CSV. pode ser útil para alguém

    <?php

class CSVExport
{
/**
* Constructor
*/
public function __construct()
{
if(isset($_GET['download_report']))
{
$csv = $this->generate_csv();

header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private", false);
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"report.csv\";" );
header("Content-Transfer-Encoding: binary");

echo $csv;
exit;
}

// Add extra menu items for admins
add_action('admin_menu', array($this, 'admin_menu'));

// Create end-points
add_filter('query_vars', array($this, 'query_vars'));
add_action('parse_request', array($this, 'parse_request'));
}

/**
* Add extra menu items for admins
*/
public function admin_menu()
{
add_menu_page('Download Report', 'Download Report', 'manage_options', 'download_report', array($this, 'download_report'));
}

/**
* Allow for custom query variables
*/
public function query_vars($query_vars)
{
$query_vars[] = 'download_report';
return $query_vars;
}

/**
* Parse the request
*/
public function parse_request(&$wp)
{
if(array_key_exists('download_report', $wp->query_vars))
{
$this->download_report();
exit;
}
}

/**
* Download report
*/
public function download_report()
{
echo '<div class="wrap">';
echo '<div id="icon-tools" class="icon32">
</div>';
echo '<h2>Download Report</h2>';
//$url = site_url();

echo '<p>Export the Users';
}

/**
* Converting data to CSV
*/
public function generate_csv()
{
$csv_output = '';
$table = 'users';

$result = mysql_query("SHOW COLUMNS FROM ".$table."");

$i = 0;
if (mysql_num_rows($result) > 0) {
while ($row = mysql_fetch_assoc($result)) {
$csv_output = $csv_output . $row['Field'].",";
$i++;
}
}
$csv_output .= "\n";

$values = mysql_query("SELECT * FROM ".$table."");
while ($rowr = mysql_fetch_row($values)) {
for ($j=0;$j<$i;$j++) {
$csv_output .= $rowr[$j].",";
}
$csv_output .= "\n";
}

return $csv_output;
}
}

// Instantiate a singleton of this plugin
$csvExport = new CSVExport();
Desenvolvedor
fonte
2

admin_init Hook ou load- (página) Hook parece funcionar, o WordPress não foi definido como cabeçalho nesse estado. Estou usando o gancho load- (página) porque é executado quando uma página do menu de administração é carregada. Você pode carregar seu script para uma página específica.

Você pode verificar o gancho de carga- (página) no WordPress Codex

Se você estiver usando o admin_init Hook, verifique o nonce usando o check_admin_referer ou outro script.

Joko Wandiro
fonte