funzionalità di esportazione CSV nel mio plugin

28 ago 2012, 01:39:59
Visualizzazioni: 15.1K
Voti: 2

Devo implementare una funzionalità di "esporta come .csv" nel mio plugin. Il problema è che ottengo l'avviso "header already sent", perché quando viene chiamata la mia funzione "export", WordPress ha già iniziato il rendering.

L'ho impostato tramite un piccolo modulo nella schermata delle Impostazioni, il cui attributo action punta a un file (download.csv.php) nella cartella del mio plugin, che non dovrebbe essere visualizzato ma solo mostrare una finestra di dialogo per il download del dump CSV.

Ecco il mio codice.

Nella schermata delle Impostazioni del mio plugin:

<form method="post" id="download_form" action="<?php echo plugins_url('download.csv.php', __FILE__); ?>">
    <input type="hidden" name="download" value="<?php echo get_home_path(); ?>" />
    <input type="submit" name="download_csv" class="button-primary" value="<?php _e('Scarica il log (.csv)', $this->localizationDomain); ?>" />
</form>

E il file download.csv.php:

$core = $_POST['download'].'wp-load.php';

if(isset($_POST['download']) && is_file($core)){
    
    require_once($core);
    
    global $wpdb;
    
    $rows = $wpdb->get_results('SELECT * FROM `'.$this->dbName.'`;', ARRAY_A);
    
    if($rows){
        
        require_once('classes/csvmaker.class.php');
        $CSV = new CSVMaker();
        
        $CSVHeader = array();
        $CSVHeader['id'] = "ID";
        $CSVHeader['when'] = "QUANDO";
        $CSVHeader['who'] = "CHI";
        $CSVHeader['description'] = "DESCRIZIONE";
        $CSVHeader['type'] = "TIPO";
        
        foreach($rows as $r){
            $CSV->addEntry($r);
        }
        $file_name = plugin_dir_path(__FILE__).'data.csv';
        file_put_contents($file_name, $CSV->buildDoc);
        ob_start();
        header("Expires: Mon, 1 Apr 1974 05:00:00 GMT");
        header("Last-Modified: " . gmdate("D,d M YH:i:s") . " GMT");
        header("Cache-Control: no-cache, must-revalidate");
        header("Pragma: no-cache");
        header("Content-type: application/CSV");
        header("Content-Disposition: attachment; filename=$file_name.csv");
        echo $CSV->buildDoc;
        return ob_get_clean();
        exit;
        
    }

Cosa sto sbagliando?

0
Tutte le risposte alla domanda 3
1

L'inizio di ciò che stai facendo male può essere spiegato da questa citazione:

l'attributo action punta a un file (download.csv.php) nella mia cartella del plugin, che non dovrebbe essere visualizzato, ma solo mostrare una finestra di dialogo per il download del dump CSV.

e questo codice:

$core = $_POST['download'].'wp-load.php';
if(isset($_POST['download']) && is_file($core)){
    require_once( $core );

Quello che stai fondamentalmente facendo è a) uscire dall'ambiente WordPress e accedere direttamente a una parte del tuo plugin e b) poi cercare di caricare l'ambiente WordPress dall'interno di quel file (e farlo in un modo estremamente insicuro, aggiungerei).

Invece di fare così, è meglio rimanere dall'inizio nell'ambiente WordPress e sovrascrivere l'output nel modo desiderato.

Un modo migliore sarebbe rimanere nell'admin, agganciarsi a admin_init, e rilevare quando hai bisogno di ottenere il tuo output CSV e restituire quello invece.

Quindi per il tuo form, fai qualcosa del genere:

<form method="post" id="download_form" action="">
            <input type="submit" name="download_csv" class="button-primary" value="<?php _e('Scarica il log (.csv)', $this->localizationDomain); ?>" />
    </form>

Nota che qui non viene usato alcun action. Questo significa che sta inviando nuovamente alla stessa pagina admin, senza modifiche. Ora, puoi rilevarlo in qualsiasi funzione hai collegato all'hook admin_init, così:

global $plugin_page;
if ( isset($_POST['download_csv']) && $plugin_page == 'whatever' ) {
    echo "CIAO"; die;
}

La variabile globale $plugin_page sarà impostata su page=whatever che hai nella schermata delle impostazioni normale del tuo plugin. Invece di stampare "CIAO" come ho fatto qui, chiama una funzione nel tuo plugin per generare e restituire quel CSV correttamente, intestazioni e tutto, e poi die. O qualcosa del genere.

Nota che questo è semplificato e potrebbe essere insicuro. Potresti anche voler aggiungere un nonce e un controllo delle capacità qui, per assicurarti che l'utente sia autorizzato a scaricare questo CSV e abbia intenzione di farlo.

28 ago 2012 01:59:39
Commenti

Fantastico. Sapevo di sbagliare, ma le tue spiegazioni mi hanno messo sulla strada giusta. Grazie!!

pixeline pixeline
28 ago 2012 02:31:08
1

Ecco il codice completo aggiornato.

Nel tuo plugin aggiornato

<form method="post" id="download_form" action="">

<input type="submit" name="download_csv" class="button-primary" value="<?php _e('Scarica il log (.csv)', $this->localizationDomain); ?>" />

</form>

Ora nella tua function.php

add_action("admin_init", "download_csv");

function download_csv() {

  if (isset($_POST['download_csv'])) {

    global $wpdb;

    $sql = "SELECT * FROM {$wpdb->prefix}table_name";

    $rows = $wpdb->get_results($sql, 'ARRAY_A');

    if ($rows) {

        $csv_fields = array();
        $csv_fields[] = "prima_colonna";
        $csv_fields[] = 'seconda_colonna';

        $output_filename = 'nome_file' .'.csv';
        $output_handle = @fopen('php://output', 'w');

        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Content-Description: File Transfer');
        header('Content-type: text/csv');
        header('Content-Disposition: attachment; filename=' . 
        $output_filename);
        header('Expires: 0');
        header('Pragma: public');

        $first = true;
       // Converti i risultati in formato csv
        foreach ($rows as $row) {

       // Aggiungi intestazioni della tabella
            if ($first) {

               $titles = array();

                foreach ($row as $key => $val) {

                    $titles[] = $key;

                }

                fputcsv($output_handle, $titles);

                $first = false;
            }

            $leadArray = (array) $row; // Converti l'oggetto in array
            // Aggiungi riga al file
            fputcsv($output_handle, $leadArray);
        }

        //echo '<a href="'.$output_handle.'">test</a>';

        // Chiudi lo stream del file di output
        fclose($output_handle);

        die();
    }
  }
}
2 mag 2017 16:46:51
Commenti

Grazie soprattutto per la parte add_action("admin_init", "download_csv"); :)

Jasom Dotnet Jasom Dotnet
1 giu 2017 10:18:16
0

Ho riscontrato alcuni problemi con i metodi sopra citati - add_action. Non volevo che il plugin eseguisse questa funzionalità automaticamente. Invece avevo bisogno di una soluzione per l'area amministrativa.

1. Ho scoperto che è necessario pulire la cache altrimenti l'output HTML finisce nel file csv.
2. Bisogna "terminare" il processo con exit.

Ecco la mia soluzione. Modifica le tabelle e i campi in base alle tue esigenze:

function Export()
{
  global $wpdb; 

  header('Content-Type: text/csv');
  header('Content-Disposition: attachment; filename="export.csv"');

 // pulisce il buffer di output
 ob_end_clean();

 $fp = fopen('php://output', 'w');

 $header_row = array(
    0 => 'etichetta campo 1',
    1 => 'etichetta campo 2',
    2 => 'etichetta campo 3',
     );


 fputcsv($fp, $header_row); 

 $Table_Name   = $wpdb->prefix.'YourTable'; 
 $sql_query    = $wpdb->prepare("SELECT * FROM $Table_Name", 1) ;
 $rows         = $wpdb->get_results($sql_query, ARRAY_A);
 if(!empty($rows)) 
   {
    foreach($rows as $Record)
      {  

 $OutputRecord = array($Record['field1'],$Record['field2'],$Record['field3']);   
  fputcsv($fp, $OutputRecord);       
   }
  }

fclose( $fp );
exit;
}

AGGIORNAMENTO: Da quando ho scritto questo codice, i dati dell'header HTML finiscono nel file se usi il codice tramite shortcode o chiamata diretta sul frontend. Usandolo nella sezione backend/amministrativa, funziona perfettamente.

2 apr 2019 23:42:07