Comment puis-je forcer le téléchargement d'un fichier dans le backend WordPress?

30

Je voudrais ajouter un bouton "Cliquez pour télécharger" à l'un de mes plugins WordPress, et je ne sais pas quel crochet utiliser. Jusqu'à présent, le raccordement de «admin_init» à ce code semble fonctionner:

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

Cela semble fonctionner, mais je veux juste voir s'il existe une meilleure pratique.

Merci, Dave

Dave Morris
la source

Réponses:

39

Si je vous comprends bien, vous voulez avoir une URL quelque chose comme la suivante dont la réponse au navigateur sera le contenu que vous générez, c'est-à-dire votre .CSVfichier et aucun contenu généré à partir de WordPress?

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

Je pense que vous cherchez le 'template_redirect'crochet. Vous pouvez trouver 'template_redirect'dans /wp-includes/template-loader.phpquel fichier tous les développeurs WordPress devraient se familiariser; il est court et doux et achemine chaque chargement de page non administrateur, alors assurez-vous d'y jeter un œil.

Il suffit d' ajouter ce qui suit à votre thème functions.phpde fichier ou dans un autre fichier que vous 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();
  }
}

Notez le test de l' '/downloads/data.csv'URL en inspectant $_SERVER['REQUEST_URI']. Notez également l'ajout ,true,200à votre header()appel où vous définissez le Content-type; cela est dû au fait que WordPress aura défini le code d'état 404 "Introuvable" car il ne reconnaît pas l'URL. Ce n'est pas un problème cependant car le truedit header()de remplacer 404WordPress avait été défini et d'utiliser le code d'état HTTP 200 "Okay" à la place.

Et voici à quoi cela ressemble dans FireFox ( Notez que la capture d'écran n'a pas /downloads/de répertoire virtuel car après avoir pris et annoté la capture d'écran, il semblait juste une bonne idée d'ajouter un '/downloads/'répertoire virtuel):

Capture d'écran d'une URL de téléchargement d'un fichier CSV
(source: mikeschinkel.com )

MISE À JOUR

Si vous souhaitez que le téléchargement soit géré à partir d'une URL préfixée /wp-admin/pour donner à l'utilisateur l'indication visuelle qu'il est protégé par une connexion, vous pouvez également le faire; la description d'une façon suit.

J'encapsulées dans une classe cette fois -ci , appelé DownloadCSV, et créé un utilisateur « capacité » appelé 'download_csv'pour le 'administrator'rôle (lu sur les rôles et capacités ici ) Vous pouvez tout simplement ferroutage hors du prédéfini 'export'rôle si vous aimez et si oui vous suffit de rechercher et remplacer 'download_csv'par 'export'et supprimez l' register_activation_hook()appel et la activate()fonction. Soit dit en passant, la nécessité d'un crochet d'activation est l'une des raisons pour lesquelles je l'ai déplacé vers un plugin au lieu de le conserver dans le functions.phpfichier du thème . *

J'ai également ajouté une option de menu "Télécharger CSV" dans le menu "Outils" en l' utilisant add_submenu_page()et je l'ai liée à la 'download_csv'capacité.

Enfin, j'ai choisi le 'plugins_loaded'crochet parce que c'était le premier crochet approprié que je pouvais utiliser. Vous pouvez l'utiliser, 'admin_init'mais ce hook est exécuté beaucoup plus tard (1130e appel hook contre le 3e appel hook), alors pourquoi laisser WordPress faire plus de travail de mise au rebut qu'il n'en a besoin? (J'ai utilisé mon plugin Instrument Hooks pour savoir quel crochet utiliser.)

Dans le crochet, je vérifie que mon URL commence /wp-admin/tools.phppar inspecter la $pagenowvariable, je vérifie cela current_user_can('download_csv')et si cela passe, je teste $_GET['download']pour voir si elle contient data.csv; si oui, nous exécutons pratiquement le même code qu'avant. J'ai également supprimé le ,true,200de l'appel à header()dans l'exemple précédent, car ici WordPress sait que c'est une bonne URL, donc n'a pas encore défini le statut 404. Voici donc votre code:

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

Et voici une capture d'écran du plugin activé: (source: mikeschinkel.com )Capture d'écran de la page Plugin montrant un plugin activé

Et enfin, voici une capture d'écran du déclenchement du téléchargement: (source: mikeschinkel.com )Capture d'écran de Téléchargement d'un fichier par URL à partir d'une option du menu Outils de l'administrateur WordPress

Mike Schinkel
la source
Mike, merci pour votre aide. Le seul hic avec cette fonctionnalité est que je voudrais que le fichier soit téléchargé depuis le backend. Il semble que template_redirect ne fonctionne pas sur le backend, et si je ne suis pas censé utiliser admin_init, je me demande ce que je devrais utiliser à la place. admin_init semble fonctionner pour moi maintenant, je pourrais m'en tenir au moins à court terme. C'est une fonctionnalité mineure que seules quelques personnes vont utiliser.
Dave Morris
@Dave Morris - Pouvez-vous définir ce que vous entendez par «back-end» ? Voulez-vous dire sur le serveur? Si oui, 'template_redirect's'exécute certainement sur le serveur. Sinon, je serais totalement confus; pouvez-vous clarifier la préoccupation? Merci d'avance.
MikeSchinkel du
@Dave: Si vous entendez la zone d'administration par le "back-end", cela fonctionnera toujours. L'URL de téléchargement commence par /downloads/data.csv, qui est un fichier inexistant, donc le "front end" WordPress gérera cette demande et finira par atteindre template-redirect. Vous venez de créer un lien dans la zone d'administration qui pointe vers cette URL frontale. (Il faut dire que de cette façon, vous n'obtenez pas la protection de connexion administrateur gratuitement - toute personne connaissant l'URL peut télécharger le fichier, mais il existe peut-être un moyen facile de résoudre ce problème?)
Jan Fabry
@Jan Fabry - Ah, je comprends maintenant. Par «back-end», il voulait dire à partir de l'administrateur, non? Il peut utiliser la fonction current_user_can()avec le code ci-dessus ou adopter une autre approche. Après ce commentaire, je vais ajouter une mise à jour à ma réponse.
MikeSchinkel
Oui, je m'excuse, je ne reçois pas d'alertes par e-mail de ce site, ce qui explique mon retard à répondre. Je faisais effectivement référence à la zone d'administration de WordPress lorsque j'ai dit "backend". Désolé pour ça. Je vais essayer d'utiliser template_redirect et voir ce qui se passe. Merci! ~ Dave
Dave Morris
3

un autre plugin utile pour exporter vers CSV. peut être utile à quelqu'un

    <?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();
Développeur
la source
2

admin_init Hook or load- (page) Hook semble fonctionner, WordPress n'a pas été défini en-tête dans cet état. J'utilise load- (page) Hook car il s'exécute lorsqu'une page de menu d'administration est chargée. Vous pouvez charger votre script pour une page spécifique.

Vous pouvez vérifier la charge- (page) Accrocher sur Codex WordPress

Si vous utilisez admin_init Hook, assurez-vous de vérifier le nonce à l' aide de check_admin_referer ou d'un autre script, la condition obtiendra la sortie de votre fichier de téléchargement.

Joko Wandiro
la source