Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[COT] Implement order bulk actions #33687

Merged
merged 7 commits into from Jul 4, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,4 @@
Significance: patch
Type: update

Implement bulk actions in the new orders admin list table.
34 changes: 3 additions & 31 deletions plugins/woocommerce/includes/admin/class-wc-admin-menus.php
Expand Up @@ -7,6 +7,7 @@
*/

use Automattic\WooCommerce\Internal\Admin\Orders\ListTable as Custom_Orders_List_Table;
use Automattic\WooCommerce\Internal\Admin\Orders\PageController as Custom_Orders_PageController;
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;

defined( 'ABSPATH' ) || exit;
Expand Down Expand Up @@ -316,40 +317,11 @@ public function addons_page() {
*/
public function orders_menu(): void {
if ( wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled() ) {
add_submenu_page( 'woocommerce', __( 'Orders', 'woocommerce' ), __( 'Orders', 'woocommerce' ), 'edit_others_shop_orders', 'wc-orders', array( $this, 'orders_page' ) );
add_filter( 'manage_woocommerce_page_wc-orders_columns', array( $this, 'orders_table' ) );

// In some cases (such as if the authoritative order store was changed earlier in the current request) we
// need an extra step to remove the menu entry for the menu post type.
add_action(
'admin_init',
function () {
remove_submenu_page( 'woocommerce', 'edit.php?post_type=shop_order' );
}
);
$this->orders_page_controller = new Custom_Orders_PageController();
jorgeatorres marked this conversation as resolved.
Show resolved Hide resolved
$this->orders_page_controller->setup();
}
}

/**
* Set-up the orders admin list table.
*
* @return void
*/
public function orders_table(): void {
$this->orders_list_table = new Custom_Orders_List_Table();
$this->orders_list_table->setup();
}

/**
* Render the orders admin list table.
*
* @return void
*/
public function orders_page(): void {
$this->orders_list_table->prepare_items();
$this->orders_list_table->display();
}

/**
* Add custom nav meta box.
*
Expand Down
148 changes: 145 additions & 3 deletions plugins/woocommerce/src/Internal/Admin/Orders/ListTable.php
Expand Up @@ -4,7 +4,6 @@

use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
use WC_Order;
use WC_Order_Data_Store_Interface;
use WP_List_Table;
use WP_Screen;

Expand Down Expand Up @@ -33,9 +32,12 @@ public function __construct() {
* @return void
*/
public function setup(): void {
add_action( 'admin_notices', array( $this, 'bulk_action_notices' ) );

add_filter( 'manage_woocommerce_page_wc-orders_columns', array( $this, 'get_columns' ) );
add_filter( 'set_screen_option_edit_orders_per_page', array( $this, 'set_items_per_page' ), 10, 3 );
add_filter( 'default_hidden_columns', array( $this, 'default_hidden_columns' ), 10, 2 );

$this->items_per_page();
set_screen_options();
}
Expand Down Expand Up @@ -118,7 +120,7 @@ public function render_blank_state(): void {
/**
* Renders after the 'blank state' message for the order list table has rendered.
*/
do_action( 'wc_marketplace_suggestions_orders_empty_state' );
do_action( 'wc_marketplace_suggestions_orders_empty_state' ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingSinceComment
?>

</div>
Expand Down Expand Up @@ -458,7 +460,7 @@ public function column_order_date( WC_Order $order ): void {
human_time_diff( $order->get_date_created()->getTimestamp(), time() )
);
} else {
$show_date = $order->get_date_created()->date_i18n( apply_filters( 'woocommerce_admin_order_date_format', __( 'M j, Y', 'woocommerce' ) ) );
$show_date = $order->get_date_created()->date_i18n( apply_filters( 'woocommerce_admin_order_date_format', __( 'M j, Y', 'woocommerce' ) ) ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
}
printf(
'<time datetime="%1$s" title="%2$s">%3$s</time>',
Expand Down Expand Up @@ -583,6 +585,7 @@ public function column_wc_actions( WC_Order $order ): void {
* are registered.
*
* @param WC_Order $order Current order object.
* @since 6.7.0
*/
do_action( 'woocommerce_admin_order_actions_start', $order );

Expand All @@ -609,6 +612,7 @@ public function column_wc_actions( WC_Order $order ): void {
*
* @param array $action Order actions.
* @param WC_Order $order Current order object.
* @since 6.7.0
*/
$actions = apply_filters( 'woocommerce_admin_order_actions', $actions, $order );

Expand All @@ -620,6 +624,7 @@ public function column_wc_actions( WC_Order $order ): void {
* are rendered.
*
* @param WC_Order $order Current order object.
* @since 6.7.0
*/
do_action( 'woocommerce_admin_order_actions_end', $order );

Expand Down Expand Up @@ -649,4 +654,141 @@ private function print_hidden_form_fields(): void {
echo '<input type="hidden" name="status" value="' . esc_attr( sanitize_text_field( wp_unslash( $_GET[ $param ] ) ) ) . '" >';
}
}

/**
* Handle bulk actions.
*/
public function handle_bulk_actions() {
$action = $this->current_action();

if ( ! $action ) {
return;
}

check_admin_referer( 'bulk-orders' );

$redirect_to = remove_query_arg( array( 'deleted', 'ids' ), wp_get_referer() );
$redirect_to = add_query_arg( 'paged', $this->get_pagenum(), $redirect_to );

/**
* Allows 3rd parties to modify order IDs about to be affected by a bulk action.
*
* @param array Array of order IDs.
*/
$ids = apply_filters( // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingSinceComment
'woocommerce_bulk_action_ids',
isset( $_REQUEST['order'] ) ? array_reverse( array_map( 'absint', $_REQUEST['order'] ) ) : array(),
$action,
'order'
);

if ( ! $ids ) {
wp_safe_redirect( $redirect_to );
exit;
}

$report_action = '';
$changed = 0;

if ( 'remove_personal_data' === $action ) {
$report_action = 'removed_personal_data';
$changed = $this->do_bulk_action_remove_personal_data( $ids );
} elseif ( false !== strpos( $action, 'mark_' ) ) {
$order_statuses = wc_get_order_statuses();
$new_status = substr( $action, 5 );
$report_action = 'marked_' . $new_status;

if ( isset( $order_statuses[ 'wc-' . $new_status ] ) ) {
$changed = $this->do_bulk_action_mark_orders( $ids, $new_status );
}
}

if ( $changed ) {
$redirect_to = add_query_arg(
array(
'bulk_action' => $report_action,
'changed' => $changed,
'ids' => implode( ',', $ids ),
),
$redirect_to
);
}

wp_safe_redirect( $redirect_to );
exit;
}

/**
* Implements the "remove personal data" bulk action.
*
* @param array $order_ids The Order IDs.
* @return int Number of orders modified.
*/
private function do_bulk_action_remove_personal_data( $order_ids ): int {
$changed = 0;

foreach ( $order_ids as $id ) {
$order = wc_get_order( $id );

if ( $order ) {
do_action( 'woocommerce_remove_order_personal_data', $order ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
$changed++;
}
}

return $changed;
}

/**
* Implements the "mark <status>" bulk action.
*
* @param array $order_ids The order IDs to change.
* @param string $new_status The new order status.
* @return int Number of orders modified.
*/
private function do_bulk_action_mark_orders( $order_ids, $new_status ): int {
$changed = 0;

// Initialize payment gateways in case order has hooked status transition actions.
WC()->payment_gateways();

foreach ( $order_ids as $id ) {
$order = wc_get_order( $id );
jorgeatorres marked this conversation as resolved.
Show resolved Hide resolved
$order->update_status( $new_status, __( 'Order status changed by bulk edit:', 'woocommerce' ), true );
jorgeatorres marked this conversation as resolved.
Show resolved Hide resolved
do_action( 'woocommerce_order_edit_status', $id, $new_status ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
$changed++;
}

return $changed;
}

/**
* Show confirmation message that order status changed for number of orders.
*/
public function bulk_action_notices() {
if ( empty( $_REQUEST['bulk_action'] ) ) {
return;
}

$order_statuses = wc_get_order_statuses();
$number = isset( $_REQUEST['changed'] ) ? absint( $_REQUEST['changed'] ) : 0;
jorgeatorres marked this conversation as resolved.
Show resolved Hide resolved
$bulk_action = wc_clean( wp_unslash( $_REQUEST['bulk_action'] ) );

// Check if any status changes happened.
foreach ( $order_statuses as $slug => $name ) {
if ( 'marked_' . str_replace( 'wc-', '', $slug ) === $bulk_action ) { // WPCS: input var ok, CSRF ok.
/* translators: %d: orders count */
$message = sprintf( _n( '%d order status changed.', '%d order statuses changed.', $number, 'woocommerce' ), number_format_i18n( $number ) );
jorgeatorres marked this conversation as resolved.
Show resolved Hide resolved
echo '<div class="updated"><p>' . esc_html( $message ) . '</p></div>';
break;
}
}

if ( 'removed_personal_data' === $bulk_action ) { // WPCS: input var ok, CSRF ok.
/* translators: %d: orders count */
$message = sprintf( _n( 'Removed personal data from %d order.', 'Removed personal data from %d orders.', $number, 'woocommerce' ), number_format_i18n( $number ) );
echo '<div class="updated"><p>' . esc_html( $message ) . '</p></div>';
}
}

}
116 changes: 116 additions & 0 deletions plugins/woocommerce/src/Internal/Admin/Orders/PageController.php
@@ -0,0 +1,116 @@
<?php
namespace Automattic\WooCommerce\Internal\Admin\Orders;

/**
* Controls the different pages/screens associated to the "Orders" menu page.
*/
class PageController {

/**
* Instance of the orders list table.
*
* @var Automattic\WooCommerce\Internal\Admin\Orders\ListTable
*/
private $orders_table;

/**
* Current action.
*
* @var string
*/
private $current_action = '';

/**
* Sets up the page controller, including registering the menu item.
*
* @return void
*/
public function setup(): void {
// Register menu.
if ( 'admin_menu' === current_action() ) {
$this->register_menu();
} else {
add_action( 'admin_menu', 'register_menu', 9 );
}

$this->set_action();

// Perform initialization for the current action.
add_action(
'load-woocommerce_page_wc-orders',
function() {
if ( method_exists( $this, 'setup_action_' . $this->current_action ) ) {
$this->{"setup_action_{$this->current_action}"}();
}
}
);
}

/**
* Sets the current action based on querystring arguments. Defaults to 'list_orders'.
*
* @return void
*/
private function set_action(): void {
$this->current_action = 'list_orders';

if ( ! empty( $_GET['action'] ) && 'edit' === $_GET['action'] ) {
$this->current_action = 'edit_order';
}
}

/**
* Registers the "Orders" menu.
*
* @return void
*/
public function register_menu(): void {
add_submenu_page(
'woocommerce',
__( 'Orders', 'woocommerce' ),
__( 'Orders', 'woocommerce' ),
'edit_others_shop_orders',
'wc-orders',
array( $this, 'output' )
);

// In some cases (such as if the authoritative order store was changed earlier in the current request) we
// need an extra step to remove the menu entry for the menu post type.
add_action(
'admin_init',
function() {
remove_submenu_page( 'woocommerce', 'edit.php?post_type=shop_order' );
}
);
}

/**
* Outputs content for the current orders screen.
*
* @return void
*/
public function output(): void {
switch ( $this->current_action ) {
case 'list_orders':
default:
$this->orders_table->prepare_items();
$this->orders_table->display();
break;
}
}

/**
* Handles initialization of the orders list table.
*
* @return void
*/
private function setup_action_list_orders(): void {
$this->orders_table = new ListTable();
$this->orders_table->setup();

if ( $this->orders_table->current_action() ) {
$this->orders_table->handle_bulk_actions();
}
}

}