| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- <?php
- // Handles custom bulk actions for orders.
- defined('ABSPATH') || exit;
- class Bulk_Actions_Manager {
- public function __construct() {
- add_filter('bulk_actions-woocommerce_page_wc-orders', array($this, 'register_bulk_actions'));
- add_filter('handle_bulk_actions-woocommerce_page_wc-orders', array($this, 'handle_bulk_actions'), 10, 3);
- }
- public function register_bulk_actions($bulk_actions) {
- $bulk_actions['prepare_to_printing_export'] = __('Prepare to Printing Export', 'studiou-wc-ord-print-statuses');
- $bulk_actions['set_status_to_prepare_to_printing'] = __('Set Status to Prepare to Printing', 'studiou-wc-ord-print-statuses');
- $bulk_actions['set_status_to_in_printing'] = __('Set Status to In Printing', 'studiou-wc-ord-print-statuses');
- return $bulk_actions;
- }
- public function handle_bulk_actions($redirect_to, $action, $post_ids) {
- $known = array(
- 'prepare_to_printing_export',
- 'set_status_to_prepare_to_printing',
- 'set_status_to_in_printing',
- );
- if (!in_array($action, $known, true)) {
- return $redirect_to;
- }
- if (!current_user_can('edit_shop_orders')) {
- wp_die(esc_html__('You do not have sufficient permissions to perform this action.', 'studiou-wc-ord-print-statuses'));
- }
- $post_ids = array_filter(array_map('intval', (array) $post_ids));
- if (empty($post_ids)) {
- return $redirect_to;
- }
- switch ($action) {
- case 'prepare_to_printing_export':
- return $this->prepare_to_printing_export($post_ids, $redirect_to);
- case 'set_status_to_prepare_to_printing':
- return $this->set_status_to_prepare_to_printing($post_ids, $redirect_to);
- case 'set_status_to_in_printing':
- return $this->set_status_to_in_printing($post_ids, $redirect_to);
- }
- return $redirect_to;
- }
- /**
- * Atomic order: pull rows, build the CSV in memory, then flip statuses, then
- * stream the file. If the export query returns nothing, statuses are still
- * advanced (the spec requires it) but the user is told no CSV was produced.
- */
- private function prepare_to_printing_export($post_ids, $redirect_to) {
- $orders_data = Studiou_DB_Manager::get_orders_for_printing($post_ids);
- $csv_data = !empty($orders_data) ? $this->generate_csv($orders_data) : '';
- $updated = Studiou_DB_Manager::set_orders_to_prepare_printing($post_ids);
- $this->notify_moved_count($updated, 'to-print');
- if ($csv_data === '') {
- UtilsLog::message(
- __('No exportable line items were found for the selected orders. CSV was not generated.', 'studiou-wc-ord-print-statuses'),
- 'warning'
- );
- return $redirect_to;
- }
- $this->output_csv($csv_data, 'prepare_to_printing_export.csv'); // exits
- }
- private function set_status_to_prepare_to_printing($post_ids, $redirect_to) {
- $updated = Studiou_DB_Manager::set_orders_to_prepare_printing($post_ids);
- $this->notify_moved_count($updated, 'to-print');
- return add_query_arg('bulk_action', 'marked_prepare_to_printing', $redirect_to);
- }
- private function set_status_to_in_printing($post_ids, $redirect_to) {
- $updated = Studiou_DB_Manager::set_orders_to_in_printing($post_ids);
- $this->notify_moved_count($updated, 'in-print');
- return add_query_arg('bulk_action', 'marked_in_printing', $redirect_to);
- }
- /**
- * Emit a "%d orders moved to …" notice unless zero orders were actually
- * changed — in that case the "skipped because terminal status" warning
- * from Studiou_DB_Manager is more informative on its own.
- */
- private function notify_moved_count($count, $slug) {
- if ($count <= 0) {
- return;
- }
- if ($slug === 'to-print') {
- $message = _n(
- '%d order moved to "Prepare to Printing".',
- '%d orders moved to "Prepare to Printing".',
- $count,
- 'studiou-wc-ord-print-statuses'
- );
- } else {
- $message = _n(
- '%d order moved to "In Printing".',
- '%d orders moved to "In Printing".',
- $count,
- 'studiou-wc-ord-print-statuses'
- );
- }
- UtilsLog::message(sprintf($message, $count), 'success');
- }
- private function generate_csv($data) {
- $output = fopen('php://temp', 'w+');
- // Explicit empty-string escape suppresses the PHP 8.4 deprecation
- // notice for the implicit '\\' default.
- fputcsv($output, array_keys((array) $data[0]), ',', '"', '');
- foreach ($data as $row) {
- fputcsv($output, (array) $row, ',', '"', '');
- }
- rewind($output);
- $csv = stream_get_contents($output);
- fclose($output);
- return $csv;
- }
- /**
- * Stream CSV with UTF-8 BOM so Excel on Windows reads diacritics correctly.
- * If output has already started (some upstream code printed something) we
- * cannot reliably stream a CSV download — bail with a clear error rather
- * than echoing CSV bytes into an HTML page.
- */
- private function output_csv($csv_data, $filename) {
- if (headers_sent($file, $line)) {
- UtilsLog::log(sprintf(
- 'output_csv: headers already sent from %s:%d — aborting CSV stream.',
- $file,
- $line
- ));
- wp_die(esc_html__(
- 'The CSV export could not be streamed because output had already started. Check for plugin/theme code that prints before headers are sent.',
- 'studiou-wc-ord-print-statuses'
- ));
- }
- nocache_headers();
- header('Content-Type: text/csv; charset=utf-8');
- header('Content-Disposition: attachment; filename="' . $filename . '"');
- echo "\xEF\xBB\xBF" . $csv_data; // UTF-8 BOM
- exit;
- }
- }
|