user2813435
user2813435

Reputation: 47

How to correctly make a Wordpress plugin returns a downloadable excel file

I'm trying to make some reports for my wordpress plugin. This reports must be downloadable as excel. Basically what I did was creating a button and when that button were pressed, I will query the database and make an excel from the results.

Now I need to modify the header to return the results as excel file. This is how I did it:

header('Content-Disposition: attachment; filename='.$filename.'.xls');
header('Content-type: application/force-download');
header('Content-Transfer-Encoding: binary');
header('Pragma: public');
print "\xEF\xBB\xBF"; // UTF-8 BOM

The problem is it returns the following error:

Warning: Cannot modify header information - headers already sent by (output started at .....\wp-admin\menu-header.php:137) 

I probably need ob_start() for this, but isn't that ob_start() must be placed BEFORE the first header (which is from the core files of Wordpress). I prefer not to modify the core files if possible.

Or maybe Wordpress' own hook 'send_headers'? but I can't get it to work, it's still generate the same error.

So what do I need to solve this problem? Is there exist another way to grenerate an excel file from wordpress plugin?

Any help appreciated!

Upvotes: 3

Views: 2214

Answers (2)

Lucas Hernandez
Lucas Hernandez

Reputation: 3

This is a complete example i used following Danijel idea:

first the link to download:

<form action="" method="post">
<a class="btn btn-outline-success" href="<?php echo admin_url( '?download-excel' ); ?>">Descargar a Excel</a>
</form>

then the add action which leads to another file VerAliados.php:

add_action( 'plugins_loaded', function() {
if ( isset( $_GET['download-excel'] ) ) {
    // here you can create .xls file
    include ("Aliados/VerAliados.php");
    //include ("Aliados/prueba.php");
    global $Canal;
    if ($Canal=="WFU"){
        //echo "aca-aliado0";
        $resp=descarga_aliados();
        //echo $resp;
    }
    header('Content-Disposition: attachment; filename=resultados.xlsx');
    header('Content-type: application/force-download');
    header('Content-Transfer-Encoding: binary');
    header('Pragma: public');
    die();      
}
});

VerAliados.php contains this function using the plugin CBXPHPSPREADSHEET:

function descarga_aliados() {
    
    if ( defined('CBXPHPSPREADSHEET_PLUGIN_NAME') && file_exists( CBXPHPSPREADSHEET_ROOT_PATH . 'lib/vendor/autoload.php' ) ) {
        
        require_once( CBXPHPSPREADSHEET_ROOT_PATH . 'lib/vendor/autoload.php' );
        
        $objPHPExcel = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
        $sheet = $objPHPExcel->getActiveSheet();
        $args = array(
            'orderby' => 'id',
            'user_registered' => 'ASC',
            'meta_query' => array(
                'relation' => 'AND',
                    array(
                        'key'     => 'custom_field',
                        'value'   => 'aliado',
                    )
            )
        );
        $user_query = new WP_User_Query( $args );
        $sheet->setCellValue('A1', 'ID');
        $sheet->setCellValue('B1', 'Fecha_Registro_usuario');
        $sheet->setCellValue('C1', 'Estado');
        $sheet->setCellValue('D1', 'Email');
        $sheet->setCellValue('E1', 'Nombre');
        $row_num = 2;
        foreach ( $user_query->get_results() as $user ) {
            
            $sheet->setCellValue('A' . $row_num, $user->id);
            $sheet->setCellValue('B' . $row_num, $user->user_registered);
            $sheet->setCellValue('C' . $row_num, $user->afreg_new_user_status);
            $sheet->setCellValue('D' . $row_num, $user->user_email);
            $sheet->setCellValue('E' . $row_num, $user->afreg_additional_68083);
            $row_num++;
        }
        $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($objPHPExcel, "Xlsx");
        $writer->save('resultados.xlsx');
        $writer->save('php://output');
    }
}

Upvotes: 0

Danijel
Danijel

Reputation: 12709

You send a header too late since header() is already sent in menu-header.php. From the code provided i can't see at which point you are sending header, but a good place would be in plugins_loaded action since that action hook is called when all plugins have been loaded, and before any output is sent.

The link for download can look like this:

<a href="<?php echo admin_url( '?download' ); ?>">download</a>

And, plugins_loaded action:

add_action( 'plugins_loaded', function() {
    if ( isset( $_GET['download'] ) ) {
        // here you can create .xls file

        header('Content-Disposition: attachment; filename='.$filename.'.xls');
        header('Content-type: application/force-download');
        header('Content-Transfer-Encoding: binary');
        header('Pragma: public');
        die();      
    }
});

Upvotes: 4

Related Questions