0

I've developed a plugin and have an export function that exports data from wordpress to csv.. the issue is that the "Header" statement is causing the "Headers already sent error".

I've checked through my code and there's no html or anything being sent before hand.

the error is coming from script-loader.php (output started at /home/test1/public_html/wp-includes/script-loader.php:1403)

I've looked at the core php and the section shows the following -

function _print_styles() {
global $compress_css;

$wp_styles = wp_styles();

$zip = $compress_css ? 1 : 0;
if ( $zip && defined('ENFORCE_GZIP') && ENFORCE_GZIP )
    $zip = 'gzip';

if ( $concat = trim( $wp_styles->concat, ', ' ) ) {
    $dir = $wp_styles->text_direction;
    $ver = $wp_styles->default_version;

    $concat = str_split( $concat, 128 );
    $concat = 'load%5B%5D=' . implode( '&load%5B%5D=', $concat );

    $href = $wp_styles->base_url . "/wp-admin/load-styles.php?c={$zip}&dir={$dir}&" . $concat . '&ver=' . $ver;
    echo "<link rel='stylesheet' href='" . esc_attr($href) . "' type='text/css' media='all' />\n";

    if ( !empty($wp_styles->print_code) ) {
        echo "<style type='text/css'>\n";
        echo $wp_styles->print_code;
        echo "\n</style>\n";
    }
}

if ( !empty($wp_styles->print_html) )
    echo $wp_styles->print_html;

}

Specific line 1403 -

if ( !empty($wp_styles->print_html) )
    echo $wp_styles->print_html;

here is my function that specifically does the export.

function process_bulk_action()
{
    global $wpdb;
    $table_name = $wpdb->prefix . 'cj_raffle_tickets'; // do not forget about tables prefix

    if ('delete' === $this->current_action()) {
        $ids = isset($_REQUEST['ticketid']) ? $_REQUEST['ticketid'] : array();
        if (is_array($ids)) $ids = implode(',', $ids);
        if (!empty($ids)) {
            $wpdb->query("DELETE FROM $table_name WHERE ticketid IN($ids)");
        }
    }
    if ('export' === $this->current_action()) {
            //csv_export();
            //ob_start();

        $output = '';                                           //Assigning the variable to store all future CSV file's data
        $result = $wpdb->get_results("SHOW COLUMNS FROM " . $table_name . "");   //Displays all COLUMN NAMES under 'Field' column in records     returned

        if (count($result) > 0) {
            foreach($result as $row) {
                $output = $output . $row->Field . ",";
            }
            $output = substr($output, 0, -1);               //Removing the last separator, because thats how CSVs work
        }
        $output .= "\n";
        $ids = isset($_REQUEST['ticketid']) ? $_REQUEST['ticketid'] : array();
        if (is_array($ids)) $ids = implode(',', $ids);

        if (!empty($ids)) {
            $values = $wpdb->get_results("SELECT * FROM $table_name where ticketid IN ($ids)");       //This here
        }

        foreach ($values as $rowr) {
            $fields = array_values((array) $rowr);                  //Getting rid of the keys and using numeric array to get values
            $output .= implode(",", $fields);      //Generating string with field separator
            $output .= "\n";    //Yeah...
        }
        // Download the file        
        $upload_dir = wp_upload_dir();
        $filedir = $upload_dir['path'];
        $filename = 'ticketssold_' . time() . '.csv';
        if ( ! is_writable( $filedir ) ) {
            wp_die( "<p>Uploads directory is not writable, can't save the file </p>", 'Directory not writable' );
        }
        $handle = fopen( $filedir . '/' . $filename, 'w' );

        fwrite( $handle, $output );
        fclose( $handle );
        header( 'Content-Description: File Transfer' );
        header( 'Content-Type: application/octet-stream' );
        header( 'Content-Disposition: attachment; filename='.$filename );
        header( 'Content-Transfer-Encoding: binary' );
        header( 'Expires: 0' );
        header( 'Cache-Control: must-revalidate' );
        header( 'Pragma: public' );
        header( 'Content-Length: ' . filesize( $filedir . '/' . $filename ) );
        flush();
        readfile( $filedir . '/' . $filename );
        exit;
}

Can anyone help me with this.. I've searched forum after forum trying everything from ob_clean to ob_start, ob_flush etc.

thanks Craig.

  • WordPress Core is fine. Most probably you do some echo in your plugin code. – Mateusz Marchel Nov 30 '17 at 12:24
  • Can you include your source code when outputing csv? Probably you are sending csv file after some html have been already loaded. I would suggest to use admin_hook to send csv. – kierzniak Nov 30 '17 at 12:30
  • what is the use of this line in your code :- echo "<link rel='stylesheet' href='" . esc_attr($href) . "' type='text/css' media='all' />\n"; – DHRUV GUPTA Nov 30 '17 at 12:56
  • hi @motivast can you explain what you mean by using the admin_hook? – Craig Johnstone Nov 30 '17 at 13:17
  • @motivast plugin code added to original question – Craig Johnstone Nov 30 '17 at 13:22
0

You are probably executing your process_bulk_action function in template where some html and headers are already sent. You have to use handle_bulk_actions-edit-post hook to execute your code before any content is sent. This might look something like that:

function wpse_287406_register_export_option($bulk_actions) {

    $bulk_actions['export_csv'] = __( 'Export to CSV');

    return $bulk_actions;
}

add_filter( 'bulk_actions-edit-post', 'wpse_287406_register_export_option' );

function wpse_287406_export_csv( $redirect_to, $doaction, $post_ids ) {

    if ( $doaction !== 'export_csv' ) {

        return $redirect_to;
    }

    // Export CSV code

    $file = realpath( dirname( __FILE__ ) . '/file.csv' );

    header( 'Content-Description: File Transfer' );
    header( 'Content-Type: application/octet-stream' );
    header( 'Content-Disposition: attachment; filename=file.csv' );
    header( 'Content-Transfer-Encoding: binary' );
    header( 'Expires: 0' );
    header( 'Cache-Control: must-revalidate' );
    header( 'Pragma: public' );
    header( 'Content-Length: ' . filesize($file) );

    readfile($file);

    exit;
}

add_action( 'handle_bulk_actions-edit-post', 'wpse_287406_export_csv', 10, 3 );
  • Hi, I'm using the bulk_action hook so that the user can select items in bulk then click Export.. therefore I dont understand how I can use this bulk_action hook within and admin_init? – Craig Johnstone Nov 30 '17 at 13:33
  • Actually for bulk operation there are better hooks e.g. handle_bulk_actions-edit-post I provided you full example. – kierzniak Nov 30 '17 at 14:14
  • I cant get this to work at all.. when I use your code and remove the call to my old function (process_bulk_action()) nothing happens when I click "Export" on the bulk action button – Craig Johnstone Dec 5 '17 at 10:30
  • My code is adding "Export to CSV" option to "Bulk Actions" select in "All Posts" view. Choose this option and click "Apply". Remember to select some posts otherwise handle_bulk_actions-edit-post hook won't be executed. Also as you see I'm using fake file.csv file to make export. Create this file, fill with some data and place it in the same directory where you place script. – kierzniak Dec 5 '17 at 10:51
  • whats in the prepare_items() function.. there is where I call process_bulk_action.. should I remove this also? – Craig Johnstone Dec 6 '17 at 11:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.