1

In Woocommerce, I have implemented the code below from this answer thread to one of my questions that allows the admin user to download standardized information of orders in bulk from the backend "shop orders list". To achieve that, the user either selects the period between two dates or selects the desired orders one by one; after which he runs a new 'download to file' action to process his request.

My code is divided in 4 hooked functions:

  • the first displays the html content, including the dates inputs and a new bulk action to allow the download action;

  • the second is where the main action takes place when the user clicks the apply action button: collects all the woocommerce info and writes it into a file in the server;

  • the third requests the user to download the file to his PC (I had to seggregate this one from the second part because the only way for my downloaded file not having a lot of HTML rubbish mixed was by adding the 'exit' function at the end; and

  • the fourth just displays a message to the user informing how many orders were processed and downloaded.

// 1st SUB-SNIPPET: Adding to admin order list bulk dropdown a custom action 'custom_downloads'
add_filter( 'bulk_actions-edit-shop_order', 'downloads_bulk_actions_edit_product', 100, 1 );
function downloads_bulk_actions_edit_product( $actions ) {

    ?><div class="alignleft actions custom">

        <span>From: </span>
        <input type="date" id="download_date_initial" name="download_date_initial"  value="<?php echo date('Y-m-d'); ?>" class="input-date" />
        <span> To: </span>
        <input type="date" id="download_date_final" name="download_date_final"  value="<?php echo date('Y-m-d'); ?>" class="input-date" />

    </div><?php

    $actions['write_downloads'] = __( 'Download orders', 'woocommerce' );
    return $actions;
}

// 2nd SUB-SNIPPET: Make the action from selected orders
add_filter( 'handle_bulk_actions-edit-shop_order', 'downloads_handle_bulk_action_edit_shop_order', 10, 3 );
function downloads_handle_bulk_action_edit_shop_order( $redirect_to, $action, $post_ids ) {
    global $attach_download_dir, $attach_download_file;
    global $countries_list, $countries_obj;

    if ( $action !== 'write_downloads' ) return $redirect_to; // Exit

    $processed_ids = array();

    // Opens file
    $myfile = fopen($attach_download_dir . '/' . $attach_download_file, "w") or die("Unable to open file!");

    $date_initial = $_REQUEST['download_date_initial'];
    $date_final = $_REQUEST['download_date_final'];
    if ( isset($date_initial) && isset($date_final) ) $args_ = array( 'date_created' => $date_initial . '...' . $date_final );
    if ( isset($date_initial) && empty($date_final) ) $args_ = array( 'date_created' => '>=' . $date_initial );
    if ( empty($date_initial) && isset($date_final) ) $args_ = array( 'date_created' => '<=' . $date_final );
    if ( empty($date_initial) && empty($date_final) ) $args_ = [];

    if ( isset($post_ids) && empty($args_)) $array = $post_ids; else {
        $orders = wc_get_orders( $args_ );
        $array=[]; if (isset($orders)) foreach ($orders as $order) array_push($array, $order->get_id());
    }

    // Goes through each selected order   
    foreach ( $array as $key => $post_id ) {        
        $order = wc_get_order( $post_id );
        $order_data = $order->get_data();

        // Fills in woocommerce orders info as required ...
        fwrite($myfile, XXX);

        $processed_ids[] = $post_id;
    }
    // Closes file
    fclose($myfile);

    // Returns info to be used elsewhere, namely displaying message
    return $redirect_to = add_query_arg( array( 'write_downloads' => '1', 'processed_count' => count( $processed_ids ), ), $redirect_to );

}

// 3rd SUB-SNIPPET: requests admin user to download file to his PC
add_filter( 'handle_bulk_actions-edit-shop_order', 'downloads_handle_bulk_action_edit_shop_order_2', 20, 3 );
function downloads_handle_bulk_action_edit_shop_order_2( $redirect_to, $action, $post_ids ) {
    global $attach_download_dir, $attach_download_file;
    global $root_page;

    // Saves in pc
    $file_url = $root_page . 'wp-admin/' . $attach_download_dir . '/' . $attach_download_file;
    header('Content-Description: File Transfer');
    header('Content-Type: text/plain');
    header('Content-Disposition: attachment; filename=' . basename($file_url)); 
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    ob_clean();
    flush();
    readfile($file_url);
    exit;
}

// 4th SUB-SNIPPET: The results notice from bulk action on orders
add_action( 'admin_notices', 'downloads_bulk_action_admin_notice', 10 );
function downloads_bulk_action_admin_notice() {

    if ( empty( $_REQUEST['write_downloads'] ) ) return; // Exit
    $count = intval( $_REQUEST['processed_count'] );
    printf( '<div id="message" class="updated fade"><p>' . _n( 'Processed %s order for downloads.', 'Processed %s orders for downloads.', $count, 'write_downloads' ) . '</p></div>', $count );

}

However I have 3 problems here:

  1. If I have the "download to PC part", ie the 3rd sub-snippet in place, the message ceases to be displayed in the back-end. If I take it out, I can see the message but the user is no longer requested to download the file to his PC. I tried several alternatives, all without success.

  2. If one wants to download orders selected one by one, there is no issue. But if one wants to select all orders between two dates, the admin user is forced to also select at least one order for the bulk action to run. Otherwise, it won't process the 'handle_bulk_actions-edit-shop_order' hook. This is annoying and serves no purpose for the user, as that order is only selected to make the system go through that hook, eventhough only the 2 dates are, in this case, relevant to determine which orders are to be downloaded.

  3. I would like for the 'download' action to be chosen by default in the action button, without the user having to select it. I tried doing this namely with Javascript but to no avail ...

Any ideas?

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
Lcapitao
  • 193
  • 1
  • 5
  • 14

1 Answers1

1

For problem point 2 (two), Bulk actions only work on selected orders and don't handle date range selection… so I have removed partially your code in your 1st hooked function (below)…

Your date range download feature, need to be added separately somewhere else.

For problem point 3 (three), it's solved in the first hooked function below (tested and works).

For problem point 1 (one) I have removed the hook from your 3rd function and instead I trigger it from your last hooked function that displays the custom notice… I hope it will work (untested).

I have inverted (reordered) your 3rd and last functions:

// 1. Adding to admin order list bulk dropdown a custom action 'custom_downloads'
add_filter( 'bulk_actions-edit-shop_order', 'downloads_bulk_actions_edit_product', 100, 1 );
function downloads_bulk_actions_edit_product( $actions ) {
    $reordered_actions = array( 'write_downloads' => __( 'Download orders', 'woocommerce' ) );
    foreach( $actions as $key => $action ){
        // Reinserting reordered actions
        $reordered_actions[$key] = $action;
    }
    ?>
    <script type="text/javascript">
    jQuery( function($){
        $('#bulk-action-selector-top').find('option').eq(0).remove(); // Remove defaut option
        $('#bulk-action-selector-top').val('write_downloads'); // To be sure select "downloads"…
    });
    </script>
    <?php
    return $reordered_actions;
}

// 2. Make the action from selected orders
add_filter( 'handle_bulk_actions-edit-shop_order', 'downloads_handle_bulk_action_edit_shop_order', 10, 3 );
function downloads_handle_bulk_action_edit_shop_order( $redirect_to, $action, $post_ids ) {
    global $attach_download_dir, $attach_download_file, $countries_list, $countries_obj;

    if ( $action !== 'write_downloads' ) return $redirect_to; // Exit

    $processed_ids = array();

    // Opens file
    $myfile = fopen($attach_download_dir . '/' . $attach_download_file, "w") or die("Unable to open file!");

    $date_initial = $_REQUEST['download_date_initial'];
    $date_final = $_REQUEST['download_date_final'];
    if ( isset($date_initial) && isset($date_final) ) $args_ = array( 'date_created' => $date_initial . '...' . $date_final );
    if ( isset($date_initial) && empty($date_final) ) $args_ = array( 'date_created' => '>=' . $date_initial );
    if ( empty($date_initial) && isset($date_final) ) $args_ = array( 'date_created' => '<=' . $date_final );
    if ( empty($date_initial) && empty($date_final) ) $args_ = [];

    if ( isset($post_ids) && empty($args_)) $array = $post_ids; else {
        $orders = wc_get_orders( $args_ );
        $array=[]; if (isset($orders)) foreach ($orders as $order) array_push($array, $order->get_id());
    }

    // Goes through each selected order   
    foreach ( $array as $key => $post_id ) {        
        $order = wc_get_order( $post_id );
        $order_data = $order->get_data();

        // Fills in woocommerce orders info as required ...
        fwrite($myfile, XXX);

        $processed_ids[] = $post_id;
    }
    // Closes file
    fclose($myfile);

    // Returns info to be used elsewhere, namely displaying message
    return $redirect_to = add_query_arg( array( 'write_downloads' => '1', 'processed_count' => count( $processed_ids ), ), $redirect_to );

}

// 3. Display a results notice from this bulk action on orders and trigger download to PC
add_action( 'admin_notices', 'downloads_bulk_action_admin_notice', 10 );
function downloads_bulk_action_admin_notice() {
    if ( empty( $_REQUEST['write_downloads'] ) ) return; // Exit

    $count = intval( $_REQUEST['processed_count'] );
    printf( '<div id="message" class="updated fade"><p>' . _n( 'Processed %s order for downloads.', 'Processed %s orders for downloads.', $count, 'write_downloads' ) . '</p></div>', $count );

    // Execute the request to download file to  PC
    downloads_to_pc();
}

// 4. Simple function that request user to download file to his PC
function downloads_to_pc() {
    global $attach_download_dir, $attach_download_file, $root_page;

    // Saves in pc
    $file_url = $root_page . 'wp-admin/' . $attach_download_dir . '/' . $attach_download_file;
    header('Content-Description: File Transfer');
    header('Content-Type: text/plain');
    header('Content-Disposition: attachment; filename=' . basename($file_url)); 
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    ob_clean();
    flush();
    readfile($file_url);
    exit;
}

Code goes in function.php file of your active child theme (or active theme). Untested, it could work.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399