¿Cómo puedo forzar la descarga de un archivo en el backend de WordPress?

30

Me gustaría agregar un botón "Haga clic para descargar" a uno de mis complementos de WordPress, y no estoy seguro de qué gancho usar. Hasta ahora, 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();

Esto parece funcionar, pero solo quiero ver si hay una mejor práctica disponible.

Gracias Dave

Dave Morris
fuente

Respuestas:

39

Si te entiendo correctamente, ¿quieres tener una URL como la siguiente cuya respuesta al navegador será el contenido que generes, es decir, tu .CSVarchivo y ningún contenido generado de WordPress?

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

Creo que estás buscando el 'template_redirect'gancho. Puede encontrar 'template_redirect'en /wp-includes/template-loader.phpqué archivo se encuentran todos los desarrolladores de WordPress; es breve y agradable y enruta cada carga de página no administrativa, así que asegúrese de echarle un vistazo.

Sólo tiene que añadir lo siguiente al de su tema de functions.phparchivo o en otro archivo que includeen 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();
  }
}

Tenga en cuenta la prueba de la '/downloads/data.csv'URL mediante la inspección $_SERVER['REQUEST_URI']. También tenga en cuenta lo agregado ,true,200a su header()llamada donde configura el Content-type; Esto se debe a que WordPress habrá configurado 404 "No encontrado" código de estado porque no reconoce la URL. Sin embargo, no es un problema, ya que truele dice header()que reemplace el 404WordPress establecido y que use el código de estado HTTP 200 "Okay" .

Y así es como se ve en Firefox ( Nota tenga la captura de pantalla no tiene un /downloads/directorio virtual porque después de tomar y anotar la captura de pantalla, me pareció una buena idea agregar un '/downloads/'directorio virtual):

Captura de pantalla de una URL de descarga para un archivo CSV
(fuente: mikeschinkel.com )

ACTUALIZAR

Si desea que la descarga se maneje desde una URL con el prefijo /wp-admin/para dar al usuario la indicación visual de que está protegida por un inicio de sesión, también puede hacerlo; La descripción de una manera sigue.

Esta vez encapsulé en una clase, llamé DownloadCSV, y para crear una "capacidad" de usuario llamada 'download_csv'para el 'administrator'rol (lea sobre Roles y Capacidades aquí ). Si lo desea, puede aprovechar el 'export'rol predefinido y, de ser así, simplemente busque y reemplace 'download_csv'con 'export'y eliminar la register_activation_hook()llamada y elactivate() función. Por cierto, la necesidad de un enlace de activación es una de las razones por las que moví esto a un complemento en lugar de guardarlo en el functions.phparchivo del tema . *

También agregué una opción de menú "Descargar CSV" del menú "Herramientas" usando add_submenu_page()y lo vinculé a la 'download_csv'capacidad.

Por último, elegí el 'plugins_loaded'gancho porque era el gancho apropiado más antiguo que podía usar. Podría usar, 'admin_init'pero ese enlace se ejecuta mucho más tarde (1130a llamada de gancho frente a la 3ra llamada de gancho), entonces, ¿por qué dejar que WordPress haga más trabajo desechable del que necesita? (Usé mi complemento Instrument Hooks para averiguar qué gancho usar).

En el enlace que verifico para asegurarme de que mi URL comience /wp-admin/tools.phpinspeccionando la $pagenowvariable, verifico eso current_user_can('download_csv')y si eso pasa, lo pruebo $_GET['download']para ver si contiene data.csv; en caso afirmativo, ejecutamos prácticamente el mismo código que antes. También eliminé la ,true,200llamada de header()en el ejemplo anterior porque aquí WordPress sabe que es una buena URL, por lo que aún no configuré el estado 404. Así que aquí está tu 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();
}

Y aquí hay una captura de pantalla del complemento activado: (fuente: mikeschinkel.comCaptura de pantalla de la página de complementos que muestra un complemento activado
)

Y finalmente aquí hay una captura de pantalla de la activación de la descarga: (fuente: mikeschinkel.com )Captura de pantalla de Descargar un archivo por URL desde una opción del menú Herramientas del administrador de WordPress

MikeSchinkel
fuente
Mike, gracias por tu ayuda. El único inconveniente con esta característica es que me gustaría que el archivo se descargue desde el back-end. Parece que template_redirect no funciona en el backend, y si se supone que no debo usar admin_init, me pregunto qué debería usar en su lugar. admin_init parece funcionar para mí ahora, podría mantenerlo al menos a corto plazo. Es una característica menor que solo unas pocas personas van a usar.
Dave Morris el
@Dave Morris - ¿Puedes definir lo que quieres decir con "back end" ? ¿Te refieres al servidor? En caso afirmativo, 'template_redirect'definitivamente se ejecuta en el servidor. Si no, estaría totalmente confundido; ¿Puedes aclarar la preocupación? Gracias por adelantado.
MikeSchinkel
@Dave: Si te refieres al área de administración por el "back end", esto seguirá funcionando. La URL de descarga comienza con /downloads/data.csv, que es un archivo no existente, por lo que el "front end" de WordPress se encargará de esta solicitud y eventualmente llegará template-redirect. Simplemente cree un enlace en el área de administración que apunte a esta URL frontal. (Debe decirse que de esta manera, no obtienes la protección de inicio de sesión de administrador de forma gratuita; cualquiera que conozca la URL puede descargar el archivo, pero ¿tal vez hay una manera fácil de solucionarlo?)
Jan Fabry
@ Jan Fabry - Ah, ahora entiendo. Por "back end" quiso decir desde dentro del administrador, ¿verdad? Puede usar la función current_user_can()con el código anterior o tomar otro enfoque. Después de este comentario, agregaré una actualización a mi respuesta.
MikeSchinkel
Sí, pido disculpas, no estoy recibiendo alertas por correo electrónico de este sitio, por lo que eso explica mi demora en responder. De hecho, me refería al área de administración de WordPress cuando dije "backend". Lo siento por eso. Intentaré usar template_redirect y veré qué sucede. ¡Gracias! ~ Dave
Dave Morris
3

Un complemento más útil para exportar a CSV. puede ser útil para alguien

    <?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();
Desarrollador
fuente
2

admin_init Hook o load- (page) Hook parece funcionar, WordPress no se ha configurado como encabezado en este estado. Estoy usando load- (page) Hook porque se ejecuta cuando se carga una página de menú de administración. Puede cargar su script para una página específica.

Puede verificar cargar- (página) Hook en WordPress Codex

Si está utilizando admin_init Hook, asegúrese de verificar nonce utilizando check_admin_referer u otro script, tal vez pase la condición y obtendrá el archivo de descarga.

Joko Wandiro
fuente