Cum pot forța descărcarea unui fișier în interfața de administrare WordPress?
Aș dori să adaug un buton "Click pentru descărcare" într-unul dintre plugin-urile mele WordPress și nu sunt sigur ce hook să folosesc. Până acum, folosirea hook-ului 'admin_init' cu acest cod pare să funcționeze:
header("Content-type: application/x-msdownload");
header("Content-Disposition: attachment; filename=data.csv");
header("Pragma: no-cache");
header("Expires: 0");
echo 'data';
exit();
Acest lucru pare să funcționeze, dar vreau să văd dacă există o practică recomandată pentru această situație.
Mulțumesc, Dave

Dacă te înțeleg corect, vrei să ai un URL similar cu următorul al cărui răspuns către browser va fi conținutul pe care îl generezi, adică fișierul tău .CSV
și nu conținut generat de WordPress?
http://example.com/download/data.csv
Cred că cauți hook-ul 'template_redirect'
. Poți găsi 'template_redirect'
în /wp-includes/template-loader.php
, un fișier pe care toți dezvoltatorii WordPress ar trebui să-l cunoască; este scurt și la obiect și direcționează fiecare încărcare de pagină care nu este în admin, așa că asigură-te să arunci o privire.
Doar adaugă următoarele în fișierul functions.php
al temei tale sau într-un alt fișier pe care îl include
în 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();
}
}
Observă testul pentru URL-ul '/downloads/data.csv'
prin inspectarea $_SERVER['REQUEST_URI']
. De asemenea, observă adăugarea ,true,200
la apelul tău header()
unde setezi Content-type
; acest lucru se datorează faptului că WordPress va fi setat codul de stare 404
"Not Found" pentru că nu recunoaște URL-ul. Nu este o problemă, deoarece true
îi spune lui header()
să înlocuiască 404
setat de WordPress și să folosească în schimb codul de stare HTTP 200
"Okay".
Și iată cum arată în FireFox (Notă captura de ecran nu are un director virtual /downloads/
pentru că după ce am făcut și adnotat captura de ecran, a părut o idee bună să adaug un director virtual '/downloads/'
):
(sursa: mikeschinkel.com)
ACTUALIZARE
Dacă dorești ca descărcarea să fie gestionată dintr-un URL prefixat cu /wp-admin/
pentru a oferi utilizatorului o indicație vizuală că este protejată de un login, poți face și asta; descrierea unei modalități urmează.
Am încapsulat de data aceasta într-o clasă, numită DownloadCSV
, și am creat o "capabilitate" de utilizator numită 'download_csv'
pentru rolul 'administrator'
(citește despre Roluri și Capabilități aici) Ai putea pur și simplu să te bazezi pe rolul predefinit 'export'
dacă dorești și, în acest caz, doar caută și înlocuiește 'download_csv'
cu 'export'
și elimină apelul register_activation_hook()
și funcția activate()
. Apropo, nevoia unui hook de activare este un motiv pentru care am mutat acest lucru într-un plugin în loc să-l păstrez în fișierul functions.php
al temei.*
Am adăugat și o opțiune de meniu "Descarcă CSV" sub meniul "Unelte" folosind add_submenu_page()
și am legat-o de capabilitatea 'download_csv'
.
În final, am ales hook-ul 'plugins_loaded'
pentru că a fost cel mai timpuriu hook adecvat pe care l-am putut folosi. Ai putea folosi 'admin_init'
, dar acel hook este rulat mult mai târziu (al 1130-lea apel de hook față de al 3-lea apel de hook), așa că de ce să lași WordPress să facă mai multă muncă inutilă decât este necesar? (Am folosit plugin-ul meu Instrument Hooks pentru a afla ce hook să folosesc.)
În hook, verific să mă asigur că URL-ul meu începe cu /wp-admin/tools.php
prin inspectarea variabilei $pagenow
, verific dacă current_user_can('download_csv')
și, dacă acest lucru trece, atunci testez $_GET['download']
pentru a vedea dacă conține data.csv
; dacă da, rulăm practic același cod ca înainte. Am eliminat și ,true,200
din apelul către header()
din exemplul anterior pentru că aici WordPress știe că este un URL valid, așa că nu a setat încă statusul 404. Deci, iată codul tău:
<?php
/*
Plugin Name: Descarcă 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', // Meniul Părinte
'Descarcă CSV', // Titlul Paginii
'Descarcă CSV', // Eticheta Opțiunii de Meniu
'download_csv', // Capabilitate
'tools.php?download=data.csv');// URL-ul Opțiunii relativ la /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();
}
Și iată o captură de ecran a plugin-ului activat:
(sursa: mikeschinkel.com)
Și în final, iată o captură de ecran a declanșării descărcării:
(sursa: mikeschinkel.com)

Mike, mulțumesc pentru ajutor. Singura problemă cu această funcționalitate este că aș dori ca fișierul să fie descărcat din backend. Se pare că template_redirect nu funcționează pe backend, iar dacă nu ar trebui să folosesc admin_init, mă întreb ce ar trebui să folosesc în schimb. admin_init pare să funcționeze pentru mine acum, așa că s-ar putea să rămân la el cel puțin pe termen scurt. Este o funcționalitate minoră pe care doar câțiva oameni o vor folosi.

@Dave Morris - Poți să definești ce înțelegi prin "back end"? Te referi la server? Dacă da, 'template_redirect'
cu siguranță rulează pe server. Dacă nu, sunt total confuz; poți să clarifici problema? Mulțumesc anticipat.

@Dave: Dacă prin "back end" te referi la zona de administrare, acest lucru va funcționa în continuare. URL-ul de descărcare începe cu /downloads/data.csv
, care este un fișier inexistent, așa că "front end"-ul WordPress va gestiona această solicitare și va ajunge în cele din urmă la template-redirect
. Pur și simplu creezi un link în zona de administrare care trimite către acest URL din partea frontală. (Trebuie menționat că în acest fel, nu beneficiezi de protecția de login a administratorului gratuit - oricine cunoaște URL-ul poate descărca fișierul, dar poate există o modalitate ușoară de a remedia acest lucru?)

@Jan Fabry - Ah, acum am înțeles. Prin "back end" se referea la zona de administrare, corect? Poate folosi funcția current_user_can()
cu codul de mai sus sau poate încerca o altă abordare. După acest comentariu voi actualiza răspunsul meu.

Da, îmi cer scuze, nu primesc notificări prin email de pe acest site, așa că asta explică întârzierea mea în a răspunde. Mă refeream într-adevăr la zona de administrare WordPress când am spus "backend". Îmi pare rău pentru asta. Voi încerca să folosesc template_redirect și voi vedea ce se întâmplă. Mulțumesc! ~Dave

@Dave Morris - template_redirect
este mai mult pentru accesul extern. Încearcă cel de-al doilea exemplu pe care l-am postat.

Acest lucru este excelent. Mi-ai economisit mult timp. Vreau doar să clarific pentru alții care s-ar putea să nu aibă nevoie de pagina de submeniu că tot ce trebuie să faci este să conectezi metoda clasei DownloadCSV
admin_menu
la cârligul de acțiune cu apelul către add_action('admin_menu',array(__CLASS__,'admin_menu'));
și să adaugi codul tău pentru a forța descărcarea în codul metodei.

încă un plugin util pentru exportarea în format CSV. poate fi util pentru cineva
<?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;
}
// Adaugă elemente extra în meniu pentru administratori
add_action('admin_menu', array($this, 'admin_menu'));
// Creează end-point-uri
add_filter('query_vars', array($this, 'query_vars'));
add_action('parse_request', array($this, 'parse_request'));
}
/**
* Adaugă elemente extra în meniu pentru administratori
*/
public function admin_menu()
{
add_menu_page('Descarcă Raport', 'Descarcă Raport', 'manage_options', 'download_report', array($this, 'download_report'));
}
/**
* Permite variabile personalizate în interogări
*/
public function query_vars($query_vars)
{
$query_vars[] = 'download_report';
return $query_vars;
}
/**
* Procesează cererea
*/
public function parse_request(&$wp)
{
if(array_key_exists('download_report', $wp->query_vars))
{
$this->download_report();
exit;
}
}
/**
* Descarcă raportul
*/
public function download_report()
{
echo '<div class="wrap">';
echo '<div id="icon-tools" class="icon32">
</div>';
echo '<h2>Descarcă Raport</h2>';
//$url = site_url();
echo '<p>Exportă Utilizatorii';
}
/**
* Convertirea datelor în 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;
}
}
// Instanțiază un singleton al acestui plugin
$csvExport = new CSVExport();

Cârligul admin_init sau cârligul load-(page) pare să funcționeze, WordPress nu a setat header-ul în această stare. Folosesc cârligul load-(page) pentru că rulează atunci când o pagină din meniul de administrare este încărcată. Poți încărca scriptul tău pentru o anumită pagină.
Poți verifica cârligul load-(page) în WordPress Codex
Dacă folosești cârligul admin_init, asigură-te că verifici nonce-ul folosind check_admin_referer sau alt script care poate trece condiția va obține fișierul tău de descărcare ca output.
