Funcționalitate de export CSV în plugin-ul meu

28 aug. 2012, 01:39:59
Vizualizări: 15.1K
Voturi: 2

Am nevoie să implementez o funcționalitate de "export ca .csv" în plugin-ul meu. Problema este: primesc avertizarea despre header-uri deja trimise, pentru că în momentul în care funcția mea de "export" este apelată, WordPress a început deja procesul de redare.

Am configurat asta prin intermediul unui mic formular în ecranul de Setări, al cărui atribut action indică către un fișier (download.csv.php) din folderul plugin-ului meu, care nu ar trebui să fie afișat, ci doar să deschidă un dialog de descărcare pentru dump-ul CSV.

Iată codul meu.

În ecranul de Setări al plugin-ului meu:

<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('Descarcă jurnalul (.csv)', $this->localizationDomain); ?>" />
        </form>

Și fișierul 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'] = "CÂND";
        $CSVHeader['who'] = "CINE";
        $CSVHeader['description'] = "DESCRIERE";
        $CSVHeader['type'] = "TIP";

        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;

    } 

Ce fac greșit?

0
Toate răspunsurile la întrebare 3
1

Începutul a ceea ce faci greșit poate fi explicat prin acest citat:

al cărui atribut action indică către un fișier (download.csv.php) în directorul meu de plugin, care nu ar trebui afișat, ci doar să deschidă un dialog de descărcare pentru dump-ul CSV.

și acest cod:

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

Ceea ce faci în esență este a) ieși din mediul WordPress și accesezi direct o parte din pluginul tău și b) apoi încerci să încarci mediul WordPress din interiorul acelui fișier (și o faci într-un mod extrem de nesigur, trebuie să menționez).

În loc să faci asta, este mai bine să rămâi în mediul WordPress de la bun început și să suprascrii output-ul să fie așa cum vrei tu.

O metodă mai bună ar fi să rămâi în admin, să te conectezi la admin_init și să detectezi când ai nevoie să obții output-ul CSV și să returnezi acel lucru în schimb.

Deci pentru formularul tău, fă ceva de genul:

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

Observă că nu este folosit niciun action aici. Asta înseamnă că se trimite înapoi la aceeași pagină de admin, fără modificări. Acum poți detecta asta în orice funcție ai conectat la action hook-ul admin_init, astfel:

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

Variabila globală $plugin_page va fi setată la page=whatever pe care îl ai în ecranul normal de setări al pluginului tău. În loc să afișezi "BUNĂ" cum am făcut eu aici, apelează o funcție din pluginul tău care să genereze și să afișeze corect acel CSV, cu header-e și toate cele, apoi die. Sau ceva de genul ăsta.

Reține că acest lucru este simplist și poate fi nesigur. Poți dori de asemenea să faci o verificare nonce și de capabilități aici, pentru a te asigura că utilizatorul are permisiunea să descărce acest CSV și a intenționat să facă acest lucru.

28 aug. 2012 01:59:39
Comentarii

Super. Știam că greșesc, dar explicațiile tale m-au pus pe drumul cel bun. Mulțumesc!!

pixeline pixeline
28 aug. 2012 02:31:08
1

Codul complet actualizat.

În actualizarea plugin-ului tău

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

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

</form>

Acum în fișierul tău 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_coloană";
        $csv_fields[] = 'a_doua_coloană';

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

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

        $first = true;
       // Parsează rezultatele în format csv
        foreach ($rows as $row) {

       // Adaugă antetele tabelului
            if ($first) {

               $titles = array();

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

                    $titles[] = $key;

                }

                fputcsv($output_handle, $titles);

                $first = false;
            }

            $leadArray = (array) $row; // Transformă Obiectul în array
            // Adaugă rândul în fișier
            fputcsv($output_handle, $leadArray);
        }

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

        // Închide fluxul de ieșire al fișierului
        fclose($output_handle);

        die();
    }
  }
}
2 mai 2017 16:46:51
Comentarii

Mulțumesc în special pentru partea cu add_action("admin_init", "download_csv"); :)

Jasom Dotnet Jasom Dotnet
1 iun. 2017 10:18:16
0

Am întâmpinat câteva probleme cu metodele menționate mai sus - add_action. Nu am dorit ca plugin-ul să execute această funcționalitate automat. În schimb, aveam nevoie de o soluție în administrare.

1. Am descoperit că trebuie să cureți cache-ul, altfel conținutul HTML va fi inclus în fișierul CSV.
2. Este necesar să oprești procesul cu "exit".

Iată soluția mea. Modificați tabelele și câmpurile conform nevoilor dumneavoastră:

 function Export()
 {
  global $wpdb; 

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

 // curăță buffer-ul de ieșire
 ob_end_clean();

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

 $header_row = array(
    0 => 'eticheta câmp 1',
    1 => 'eticheta câmp 2',
    2 => 'eticheta câmp 3',
     );


 fputcsv($fp, $header_row); 

 $Table_Name   = $wpdb->prefix.'TabelulTau'; 
 $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['camp1'],$Record['camp2'],$Record['camp3']);   
  fputcsv($fp, $OutputRecord);       
   }
  }

fclose( $fp );
exit;
}

ACTUALIZARE: De când am scris acest cod, am observat că datele din header-ul HTML sunt incluse în fișier dacă utilizați codul prin shortcode sau apel direct în front-end. Dacă îl folosiți în zona de administrare / backend, funcționează perfect.

2 apr. 2019 23:42:07