Allow HPOS screens to work for custom order types (#35658)

* Allow `wc_get_order_types()` to return list of order types that have UI

* Make `PageController` register menu items for all order types required

* Make `PageController` URL generation aware of order types

* Make `ListTable` order type aware

* Make `Edit` order type aware

* Add support to `wc_get_page_screen_id()` for order types

* Make `PageController` order type aware

* Include custom order type screens as WC screens

* Make `get_base_page_url()` in `PageController` public

* Make sure Trash links use the correct URL

* Gather request vars in `$request` in list table

* Add some order type specific filters to ListTable

* Fix wrong reference to orders per page in ListTable

* Make ListTable columns work in all screens

* Make PHPCS happy

* Minor PHPCS fix

* Add changelog

* Set page hidden form field based on the order_type (#35751)

* Add ‘woocommerce_order_list_table_extra_tablenav’ hook

* Fix PHPCS warnings

* Make `wc_get_page_screen_id()` datastore aware

* Make `WC_Admin_Assets::is_order_meta_box_screen()` aware of HPOS screen IDs

* Remove unnecessary `$context` arg from `wc_get_page_screen_id()`

* Add support for custom order types to `PostsRedirectionController`

* Make PHPCS happy

* Only connect COT admin page when COT is enabled

* Properly filter out empty status in ListTable

* Fix warning due to uninitialized var

* Do not rely on `wc_orders_count()` for counting orders in `ListTable`

`wc_orders_count()` assumes that order types correspond to a datastore’s object type, which isn’t necessarily the case, producing incorrect results.
Until this is addressed, it’d be best not to rely on it for types other than orders.

* Improve performance for status counts in ListTable

* Fire both order-type specific and generic hook for default column in ListTable

Co-authored-by: bruce aldridge <bruce.aldridge@automattic.com>
This commit is contained in:
Jorge A. Torres 2022-12-14 12:17:05 -03:00 committed by GitHub
parent c7132ec8f0
commit 778cb130f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 379 additions and 98 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Add support for custom order types in HPOS admin UI.

View File

@ -540,8 +540,16 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
* @return bool Whether the current screen is an order edit screen.
*/
private function is_order_meta_box_screen( $screen_id ) {
return in_array( str_replace( 'edit-', '', $screen_id ), wc_get_order_types( 'order-meta-boxes' ), true ) ||
wc_get_page_screen_id( 'shop-order' ) === $screen_id;
$screen_id = str_replace( 'edit-', '', $screen_id );
$types_with_metaboxes_screen_ids = array_filter(
array_map(
'wc_get_page_screen_id',
wc_get_order_types( 'order-meta-boxes' )
)
);
return in_array( $screen_id, $types_with_metaboxes_screen_ids, true );
}
}

View File

@ -8,6 +8,7 @@
* @version 2.1.0
*/
use Automattic\WooCommerce\Internal\Admin\Orders\PageController;
use Automattic\WooCommerce\Utilities\OrderUtil;
if ( ! defined( 'ABSPATH' ) ) {
@ -97,7 +98,8 @@ class WC_Meta_Box_Order_Actions {
*/
private static function get_trash_or_delete_order_link( int $order_id ): string {
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
$order_list_url = admin_url( 'admin.php?page=wc-orders' );
$order_type = wc_get_order( $order_id )->get_type();
$order_list_url = wc_get_container()->get( PageController::class )->get_base_page_url( $order_type );
$trash_order_url = add_query_arg(
array(
'action' => 'trash',

View File

@ -6,6 +6,8 @@
* @version 2.4.0
*/
use Automattic\WooCommerce\Utilities\OrderUtil;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
@ -39,12 +41,12 @@ function wc_get_screen_ids() {
'edit-product_tag',
'profile',
'user-edit',
wc_get_page_screen_id( 'shop-order' ),
);
foreach ( wc_get_order_types() as $type ) {
$screen_ids[] = $type;
$screen_ids[] = 'edit-' . $type;
$screen_ids[] = wc_get_page_screen_id( $type );
}
$attributes = wc_get_attribute_taxonomies();
@ -68,11 +70,18 @@ function wc_get_screen_ids() {
* @return string Page ID. Empty string if resource not found.
*/
function wc_get_page_screen_id( $for ) {
switch ( $for ) {
case 'shop-order':
return 'woocommerce_page_wc-orders';
$screen_id = '';
$for = str_replace( '-', '_', $for );
if ( in_array( $for, wc_get_order_types( 'admin-menu' ), true ) ) {
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
$screen_id = 'woocommerce_page_wc-orders' . ( 'shop_order' === $for ? '' : '--' . $for );
} else {
$screen_id = $for;
}
}
return '';
return $screen_id;
}
/**

View File

@ -7,6 +7,7 @@
use Automattic\WooCommerce\Admin\PageController;
use Automattic\WooCommerce\Admin\Features\Features;
use Automattic\WooCommerce\Utilities\OrderUtil;
/**
* Returns core WC pages to connect to WC-Admin.
@ -135,16 +136,6 @@ wc_admin_connect_page(
)
);
// WooCommerce > Orders (COT).
wc_admin_connect_page(
array(
'id' => 'woocommerce-custom-orders',
'screen_id' => wc_get_page_screen_id( 'shop-order' ),
'title' => __( 'Orders', 'woocommerce' ),
'path' => 'admin.php?page=wc-orders',
)
);
// WooCommerce > Orders > Add New.
wc_admin_connect_page(
array(
@ -165,6 +156,18 @@ wc_admin_connect_page(
)
);
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
// WooCommerce > Orders (COT).
wc_admin_connect_page(
array(
'id' => 'woocommerce-custom-orders',
'screen_id' => wc_get_page_screen_id( 'shop-order' ),
'title' => __( 'Orders', 'woocommerce' ),
'path' => 'admin.php?page=wc-orders',
)
);
}
// WooCommerce > Coupons.
wc_admin_connect_page(
array(

View File

@ -244,6 +244,17 @@ function wc_get_order_types( $for = '' ) {
}
}
break;
case 'admin-menu':
$order_types = array_intersect(
array_keys( $wc_order_types ),
get_post_types(
array(
'show_ui' => true,
'show_in_menu' => 'woocommerce',
)
)
);
break;
default:
$order_types = array_keys( $wc_order_types );
break;

View File

@ -188,18 +188,20 @@ class Edit {
* @param string $message Message to display, if any.
*/
private function render_wrapper_start( $notice = '', $message = '' ) {
$edit_page_url = admin_url( 'admin.php?page=wc-orders&action=edit&id=' . $this->order->get_id() );
$post_type = get_post_type_object( $this->order->get_type() );
$edit_page_url = wc_get_container()->get( PageController::class )->get_edit_url( $this->order->get_id() );
$form_action = 'edit_order';
$referer = wp_get_referer();
$new_page_url = wc_get_container()->get( PageController::class )->get_new_page_url();
$new_page_url = wc_get_container()->get( PageController::class )->get_new_page_url( $this->order->get_type() );
?>
<div class="wrap">
<h1 class="wp-heading-inline">
<?php echo esc_html( 'Edit order' ); ?>
<?php echo esc_html( $post_type->labels->edit_item ); ?>
</h1>
<?php
echo ' <a href="' . esc_url( $new_page_url ) . '" class="page-title-action"> Add order </a>';
echo ' <a href="' . esc_url( $new_page_url ) . '" class="page-title-action">' . esc_html( $post_type->labels->add_new ) . '</a>';
?>
<hr class="wp-header-end">

View File

@ -12,6 +12,21 @@ use WP_Screen;
* Admin list table for orders as managed by the OrdersTableDataStore.
*/
class ListTable extends WP_List_Table {
/**
* Order type.
*
* @var string
*/
private $order_type;
/**
* Request vars.
*
* @var array
*/
private $request = array();
/**
* Contains the arguments to be used in the order query.
*
@ -40,6 +55,13 @@ class ListTable extends WP_List_Table {
*/
private $is_trash = false;
/**
* Caches order counts by status.
*
* @var array
*/
private $status_count_cache = null;
/**
* Sets up the admin list table for orders (specifically, for orders managed by the OrdersTableDataStore).
*
@ -68,18 +90,23 @@ class ListTable extends WP_List_Table {
/**
* Performs setup work required before rendering the table.
*
* @param array $args Args to initialize this list table.
*
* @return void
*/
public function setup(): void {
public function setup( $args = array() ): void {
$this->order_type = $args['order_type'] ?? 'shop_order';
add_action( 'admin_notices', array( $this, 'bulk_action_notices' ) );
add_filter( 'manage_woocommerce_page_wc-orders_columns', array( $this, 'get_columns' ), 0 );
add_filter( 'set_screen_option_edit_orders_per_page', array( $this, 'set_items_per_page' ), 10, 3 );
add_filter( "manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0 );
add_filter( 'set_screen_option_edit_' . $this->order_type . '_per_page', array( $this, 'set_items_per_page' ), 10, 3 );
add_filter( 'default_hidden_columns', array( $this, 'default_hidden_columns' ), 10, 2 );
add_action( 'admin_footer', array( $this, 'enqueue_scripts' ) );
$this->items_per_page();
set_screen_options();
add_action( 'manage_' . wc_get_page_screen_id( 'shop-order' ) . '_custom_column', array( $this, 'render_column' ), 10, 2 );
add_action( 'manage_' . wc_get_page_screen_id( $this->order_type ) . '_custom_column', array( $this, 'render_column' ), 10, 2 );
}
/**
@ -105,6 +132,17 @@ class ListTable extends WP_List_Table {
* @param string $column_name Identifier for the custom column.
*/
public function column_default( $order, $column_name ) {
/**
* Fires for each custom column for a specific order type. This hook takes precedence over the generic
* action `manage_{$this->screen->id}_custom_column`.
*
* @param string $column_name Identifier for the custom column.
* @param \WC_Order $order Current WooCommerce order object.
*
* @since 7.3.0
*/
do_action( 'woocommerce_' . $this->order_type . '_list_table_custom_column', $column_name, $order );
/**
* Fires for each custom column in the Custom Order Table in the administrative screen.
*
@ -124,7 +162,7 @@ class ListTable extends WP_List_Table {
'per_page',
array(
'default' => 20,
'option' => 'edit_orders_per_page',
'option' => 'edit_' . $this->order_type . '_per_page',
)
);
}
@ -139,7 +177,7 @@ class ListTable extends WP_List_Table {
* @return mixed
*/
public function set_items_per_page( $default, string $option, int $value ) {
return 'edit_orders_per_page' === $option ? absint( $value ) : $default;
return 'edit_' . $this->order_type . '_per_page' === $option ? absint( $value ) : $default;
}
/**
@ -148,9 +186,11 @@ class ListTable extends WP_List_Table {
* @return void
*/
public function display() {
$title = esc_html__( 'Orders', 'woocommerce' );
$add_new = esc_html__( 'Add Order', 'woocommerce' );
$new_page_link = $this->page_controller->get_new_page_url();
$post_type = get_post_type_object( $this->order_type );
$title = esc_html( $post_type->labels->name );
$add_new = esc_html( $post_type->labels->add_new );
$new_page_link = $this->page_controller->get_new_page_url( $this->order_type );
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo wp_kses_post(
@ -240,15 +280,28 @@ class ListTable extends WP_List_Table {
* Prepares the list of items for displaying.
*/
public function prepare_items() {
$limit = $this->get_items_per_page( 'edit_orders_per_page' );
$limit = $this->get_items_per_page( 'edit_' . $this->order_type . '_per_page' );
$this->order_query_args = array(
'limit' => $limit,
'page' => $this->get_pagenum(),
'paginate' => true,
'type' => 'shop_order',
'type' => $this->order_type,
);
foreach ( array( 'status', 's', 'm', '_customer_user' ) as $query_var ) {
$this->request[ $query_var ] = sanitize_text_field( wp_unslash( $_REQUEST[ $query_var ] ?? '' ) );
}
/**
* Allows 3rd parties to filter the initial request vars before defaults and other logic is applied.
*
* @param array $request Request to be passed to `wc_get_orders()`.
*
* @since 7.3.0
*/
$this->request = apply_filters( 'woocommerce_' . $this->order_type . '_list_table_request', $this->request );
$this->set_status_args();
$this->set_order_args();
$this->set_date_args();
@ -265,6 +318,15 @@ class ListTable extends WP_List_Table {
*/
$order_query_args = (array) apply_filters( 'woocommerce_order_list_table_prepare_items_query_args', $this->order_query_args );
/**
* Same as `woocommerce_order_list_table_prepare_items_query_args` but for a specific order type.
*
* @param array $query_args Arguments to be passed to `wc_get_orders()`.
*
* @since 7.3.0
*/
$order_query_args = apply_filters( 'woocommerce_' . $this->order_type . '_list_table_prepare_items_query_args', $this->order_query_args );
// We must ensure the 'paginate' argument is set.
$order_query_args['paginate'] = true;
@ -291,7 +353,7 @@ class ListTable extends WP_List_Table {
);
// Are we inside the trash?
$this->is_trash = isset( $_REQUEST['status'] ) && 'trash' === $_REQUEST['status'];
$this->is_trash = 'trash' === $this->request['status'];
}
/**
@ -351,27 +413,35 @@ class ListTable extends WP_List_Table {
* Implements filtering of orders by status.
*/
private function set_status_args() {
$status = trim( sanitize_text_field( wp_unslash( $_REQUEST['status'] ?? '' ) ) );
$query_statuses = array();
$status = array_filter( array_map( 'trim', (array) $this->request['status'] ) );
if ( empty( $status ) || 'all' === $status ) {
$query_statuses = array_intersect(
array_keys( wc_get_order_statuses() ),
get_post_stati( array( 'show_in_admin_all_list' => true ), 'names' )
if ( empty( $status ) || in_array( 'all', $status, true ) ) {
/**
* Allows 3rd parties to set the default list of statuses for a given order type.
*
* @param string[] $statuses Statuses.
*
* @since 7.3.0
*/
$status = apply_filters(
'woocommerce_' . $this->order_type . '_list_table_default_statuses',
array_intersect(
array_keys( wc_get_order_statuses() ),
get_post_stati( array( 'show_in_admin_all_list' => true ), 'names' )
)
);
} else {
$query_statuses[] = $status;
$this->has_filter = true;
}
$this->order_query_args['status'] = $query_statuses;
$this->order_query_args['status'] = $status;
}
/**
* Implements order search.
*/
private function set_search_args(): void {
$search_term = trim( sanitize_text_field( wp_unslash( $_REQUEST['s'] ?? '' ) ) );
$search_term = trim( sanitize_text_field( $this->request['s'] ) );
if ( ! empty( $search_term ) ) {
$this->order_query_args['s'] = $search_term;
@ -389,7 +459,7 @@ class ListTable extends WP_List_Table {
$view_counts = array();
$view_links = array();
$statuses = $this->get_visible_statuses();
$current = isset( $_GET['status'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['status'] ?? '' ) ) : 'all';
$current = ! empty( $this->request['status'] ) ? sanitize_text_field( $this->request['status'] ) : 'all';
$all_count = 0;
foreach ( array_keys( $statuses ) as $slug ) {
@ -421,13 +491,40 @@ class ListTable extends WP_List_Table {
* @return int
*/
private function count_orders_by_status( $status ): int {
return array_sum(
array_map(
function( $order_status ) {
return wc_orders_count( $order_status, 'shop_order' );
},
(array) $status
)
global $wpdb;
// Compute all counts and cache if necessary.
if ( is_null( $this->status_count_cache ) ) {
$orders_table = OrdersTableDataStore::get_orders_table_name();
$res = $wpdb->get_results(
$wpdb->prepare(
"SELECT status, COUNT(*) AS cnt FROM {$orders_table} WHERE type = %s GROUP BY status", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$this->order_type
),
ARRAY_A
);
$this->status_count_cache =
$res
? array_combine( array_column( $res, 'status' ), array_map( 'absint', array_column( $res, 'cnt' ) ) )
: array();
}
$status = (array) $status;
$count = array_sum( array_intersect_key( $this->status_count_cache, array_flip( $status ) ) );
/**
* Allows 3rd parties to modify the count of orders by status.
*
* @param int $count Number of orders for the given status.
* @param string[] $status List of order statuses in the count.
* @since 7.3.0
*/
return apply_filters(
'woocommerce_' . $this->order_type . '_list_table_order_count',
$count,
$status
);
}
@ -470,10 +567,11 @@ class ListTable extends WP_List_Table {
* @return string
*/
private function get_view_link( string $slug, string $name, int $count, bool $current ): string {
$url = esc_url( add_query_arg( 'status', $slug, get_admin_url( null, 'admin.php?page=wc-orders' ) ) );
$name = esc_html( $name );
$count = absint( $count );
$class = $current ? 'class="current"' : '';
$base_url = get_admin_url( null, 'admin.php?page=wc-orders' . ( 'shop_order' === $this->order_type ? '' : '--' . $this->order_type ) );
$url = esc_url( add_query_arg( 'status', $slug, $base_url ) );
$name = esc_html( $name );
$count = absint( $count );
$class = $current ? 'class="current"' : '';
return "<a href='$url' $class>$name <span class='count'>($count)</span></a>";
}
@ -487,10 +585,26 @@ class ListTable extends WP_List_Table {
echo '<div class="alignleft actions">';
if ( 'top' === $which ) {
ob_start();
$this->months_filter();
$this->customers_filter();
submit_button( __( 'Filter', 'woocommerce' ), '', 'filter_action', false, array( 'id' => 'order-query-submit' ) );
/**
* Fires before the "Filter" button on the list table for orders and other order types.
*
* @since x.x.x
* @param string $order_type The order type.
* @param string $which The location of the extra table nav: 'top' or 'bottom'.
*/
do_action( 'woocommerce_order_list_table_extra_tablenav', $this->order_type, $which );
$output = ob_get_clean();
if ( ! empty( $output ) ) {
echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
submit_button( __( 'Filter', 'woocommerce' ), '', 'filter_action', false, array( 'id' => 'order-query-submit' ) );
}
}
if ( $this->is_trash && $this->has_items() && current_user_can( 'edit_others_shop_orders' ) ) {
@ -590,15 +704,25 @@ class ListTable extends WP_List_Table {
* @return array
*/
public function get_columns() {
return array(
'cb' => '<input type="checkbox" />',
'order_number' => esc_html__( 'Order', 'woocommerce' ),
'order_date' => esc_html__( 'Date', 'woocommerce' ),
'order_status' => esc_html__( 'Status', 'woocommerce' ),
'billing_address' => esc_html__( 'Billing', 'woocommerce' ),
'shipping_address' => esc_html__( 'Ship to', 'woocommerce' ),
'order_total' => esc_html__( 'Total', 'woocommerce' ),
'wc_actions' => esc_html__( 'Actions', 'woocommerce' ),
/**
* Filters the list of columns.
*
* @param array $columns List of sortable columns.
*
* @since 7.3.0
*/
return apply_filters(
'woocommerce_' . $this->order_type . '_list_table_columns',
array(
'cb' => '<input type="checkbox" />',
'order_number' => esc_html__( 'Order', 'woocommerce' ),
'order_date' => esc_html__( 'Date', 'woocommerce' ),
'order_status' => esc_html__( 'Status', 'woocommerce' ),
'billing_address' => esc_html__( 'Billing', 'woocommerce' ),
'shipping_address' => esc_html__( 'Ship to', 'woocommerce' ),
'order_total' => esc_html__( 'Total', 'woocommerce' ),
'wc_actions' => esc_html__( 'Actions', 'woocommerce' ),
)
);
}
@ -608,10 +732,20 @@ class ListTable extends WP_List_Table {
* @return string[]
*/
public function get_sortable_columns() {
return array(
'order_number' => 'ID',
'order_date' => 'date',
'order_total' => 'order_total',
/**
* Filters the list of sortable columns.
*
* @param array $sortable_columns List of sortable columns.
*
* @since 7.3.0
*/
return apply_filters(
'woocommerce_' . $this->order_type . '_list_table_sortable_columns',
array(
'order_number' => 'ID',
'order_date' => 'date',
'order_total' => 'order_total',
)
);
}
@ -907,7 +1041,7 @@ class ListTable extends WP_List_Table {
* @return void
*/
private function print_hidden_form_fields(): void {
echo '<input type="hidden" name="page" value="wc-orders" >';
echo '<input type="hidden" name="page" value="wc-orders' . ( 'shop_order' === $this->order_type ? '' : '--' . $this->order_type ) . '" >'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$state_params = array(
'paged',
@ -955,7 +1089,7 @@ class ListTable extends WP_List_Table {
// Get all trashed orders.
$ids = wc_get_orders(
array(
'type' => 'shop_order',
'type' => $this->order_type,
'status' => 'trash',
'limit' => -1,
'return' => 'ids',

View File

@ -11,6 +11,13 @@ class PageController {
use AccessiblePrivateMethods;
/**
* The order type.
*
* @var string
*/
private $order_type = '';
/**
* Instance of the posts redirection controller.
*
@ -55,9 +62,15 @@ class PageController {
if ( 'edit_order' === $this->current_action && ( ! isset( $this->order ) || ! $this->order ) ) {
wp_die( esc_html__( 'You attempted to edit an order that does not exist. Perhaps it was deleted?', 'woocommerce' ) );
}
if ( ! current_user_can( 'edit_others_shop_orders' ) && ! current_user_can( 'manage_woocommerce' ) ) {
if ( $this->order->get_type() !== $this->order_type ) {
wp_die( esc_html__( 'Order type mismatch.', 'woocommerce' ) );
}
if ( ! current_user_can( get_post_type_object( $this->order_type )->cap->edit_post, $this->order->get_id() ) && ! current_user_can( 'manage_woocommerce' ) ) {
wp_die( esc_html__( 'You do not have permission to edit this order', 'woocommerce' ) );
}
if ( 'trash' === $this->order->get_status() ) {
wp_die( esc_html__( 'You cannot edit this item because it is in the Trash. Please restore it and try again.', 'woocommerce' ) );
}
@ -69,9 +82,10 @@ class PageController {
* @return void
*/
private function verify_create_permission() {
if ( ! current_user_can( 'publish_shop_orders' ) && ! current_user_can( 'manage_woocommerce' ) ) {
if ( ! current_user_can( get_post_type_object( $this->order_type )->cap->publish_posts ) && ! current_user_can( 'manage_woocommerce' ) ) {
wp_die( esc_html__( 'You don\'t have permission to create a new order', 'woocommerce' ) );
}
if ( isset( $this->order ) ) {
$this->verify_edit_permission();
}
@ -92,9 +106,12 @@ class PageController {
add_action( 'admin_menu', 'register_menu', 9 );
}
$this->set_order_type();
$this->set_action();
self::add_action( 'load-woocommerce_page_wc-orders', array( $this, 'handle_load_page_action' ) );
$page_suffix = ( 'shop_order' === $this->order_type ? '' : '--' . $this->order_type );
self::add_action( 'load-woocommerce_page_wc-orders' . $page_suffix, array( $this, 'handle_load_page_action' ) );
}
/**
@ -106,6 +123,29 @@ class PageController {
}
}
/**
* Determines the order type for the current screen.
*
* @return void
*/
private function set_order_type() {
global $plugin_page, $pagenow;
if ( 'admin.php' !== $pagenow || 0 !== strpos( $plugin_page, 'wc-orders' ) ) {
return;
}
$this->order_type = str_replace( array( 'wc-orders--', 'wc-orders' ), '', $plugin_page );
$this->order_type = empty( $this->order_type ) ? 'shop_order' : $this->order_type;
$wc_order_type = wc_get_order_type( $this->order_type );
$wp_order_type = get_post_type_object( $this->order_type );
if ( ! $wc_order_type || ! $wp_order_type || ! $wp_order_type->show_ui || ! current_user_can( $wp_order_type->cap->edit_posts ) ) {
wp_die();
}
}
/**
* Sets the current action based on querystring arguments. Defaults to 'list_orders'.
*
@ -131,21 +171,29 @@ class PageController {
* @return void
*/
public function register_menu(): void {
add_submenu_page(
'woocommerce',
__( 'Orders', 'woocommerce' ),
__( 'Orders', 'woocommerce' ),
'edit_others_shop_orders',
'wc-orders',
array( $this, 'output' )
);
$order_types = wc_get_order_types( 'admin-menu' );
foreach ( $order_types as $order_type ) {
$post_type = get_post_type_object( $order_type );
add_submenu_page(
'woocommerce',
$post_type->labels->name,
$post_type->labels->menu_name,
$post_type->cap->edit_posts,
'wc-orders' . ( 'shop_order' === $order_type ? '' : '--' . $order_type ),
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' );
function() use ( $order_types ) {
foreach ( $order_types as $order_type ) {
remove_submenu_page( 'woocommerce', 'edit.php?post_type=' . $order_type );
}
}
);
}
@ -180,7 +228,12 @@ class PageController {
*/
private function setup_action_list_orders(): void {
$this->orders_table = wc_get_container()->get( ListTable::class );
$this->orders_table->setup();
$this->orders_table->setup(
array(
'order_type' => $this->order_type,
)
);
if ( $this->orders_table->current_action() ) {
$this->orders_table->handle_bulk_actions();
}
@ -222,14 +275,31 @@ class PageController {
*/
private function setup_action_new_order(): void {
global $theorder;
$this->verify_create_permission();
$this->order = new \WC_Order();
$order_class_name = wc_get_order_type( $this->order_type )['class_name'];
if ( ! $order_class_name || ! class_exists( $order_class_name ) ) {
wp_die();
}
$this->order = new $order_class_name();
$this->order->set_object_read( false );
$this->order->set_status( 'auto-draft' );
$this->order->save();
$theorder = $this->order;
}
/**
* Returns the current order type.
*
* @return string
*/
public function get_order_type() {
return $this->order_type;
}
/**
* Helper method to generate a link to the main orders screen.
*
@ -249,20 +319,51 @@ class PageController {
* @return string Edit link.
*/
public function get_edit_url( int $order_id ) : string {
return wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled() ?
admin_url( 'admin.php?page=wc-orders&id=' . absint( $order_id ) ) . '&action=edit' :
admin_url( 'post.php?post=' . absint( $order_id ) ) . '&action=edit';
if ( ! wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled() ) {
return admin_url( 'post.php?post=' . absint( $order_id ) ) . '&action=edit';
}
return add_query_arg(
array(
'action' => 'edit',
'id' => absint( $order_id ),
),
$this->get_base_page_url( wc_get_order( $order_id )->get_type() )
);
}
/**
* Helper method to generate a link for creating order.
*
* @param string $order_type The order type. Defaults to 'shop_order'.
* @return string
*/
public function get_new_page_url() : string {
return wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled() ?
admin_url( 'admin.php?page=wc-orders&action=new' ) :
admin_url( 'post-new.php?post_type=shop_order' );
public function get_new_page_url( $order_type = 'shop_order' ) : string {
$url = wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled() ?
add_query_arg( 'action', 'new', $this->get_base_page_url( $order_type ) ) :
admin_url( 'post-new.php?post_type=' . $order_type );
return $url;
}
/**
* Helper method to generate a link to the main screen for a custom order type.
*
* @param string $order_type The order type.
*
* @return string
*
* @throws \Exception When an invalid order type is passed.
*/
public function get_base_page_url( $order_type ): string {
$order_types_with_ui = wc_get_order_types( 'admin-menu' );
if ( ! in_array( $order_type, $order_types_with_ui, true ) ) {
// translators: %s is a custom order type.
throw new \Exception( sprintf( __( 'Invalid order type: %s.', 'woocommerce' ), esc_html( $order_type ) ) );
}
return admin_url( 'admin.php?page=wc-orders' . ( 'shop_order' === $order_type ? '' : '--' . $order_type ) );
}
}

View File

@ -56,7 +56,9 @@ class PostsRedirectionController {
* @return void
*/
private function maybe_redirect_to_orders_page(): void {
if ( ! isset( $_GET['post_type'] ) || 'shop_order' !== $_GET['post_type'] ) {
$post_type = $_GET['post_type'] ?? ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if ( ! $post_type || ! in_array( $post_type, wc_get_order_types( 'admin-menu' ), true ) ) {
return;
}
@ -72,7 +74,7 @@ class PostsRedirectionController {
unset( $query_args['post_status'] );
}
$new_url = $this->page_controller->get_orders_url();
$new_url = $this->page_controller->get_base_page_url( $post_type );
$new_url = add_query_arg( $query_args, $new_url );
// Handle bulk actions.
@ -100,7 +102,9 @@ class PostsRedirectionController {
* @return void
*/
private function maybe_redirect_to_new_order_page(): void {
if ( ! isset( $_GET['post_type'] ) || 'shop_order' !== $_GET['post_type'] ) {
$post_type = $_GET['post_type'] ?? ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if ( ! $post_type || ! in_array( $post_type, wc_get_order_types( 'admin-menu' ), true ) ) {
return;
}
@ -108,7 +112,7 @@ class PostsRedirectionController {
$query_args = wp_unslash( $_GET );
unset( $query_args['post_type'] );
$new_url = $this->page_controller->get_new_page_url();
$new_url = $this->page_controller->get_new_page_url( $post_type );
$new_url = add_query_arg( $query_args, $new_url );
wp_safe_redirect( $new_url, 301 );
@ -123,7 +127,10 @@ class PostsRedirectionController {
private function maybe_redirect_to_edit_order_page(): void {
$post_id = absint( $_GET['post'] ?? 0 );
if ( ! $post_id || ! in_array( get_post_type( $post_id ), array( 'shop_order_placehold', 'shop_order' ), true ) || ! isset( $_GET['action'] ) ) {
$redirect_from_types = wc_get_order_types( 'admin-menu' );
$redirect_from_types[] = 'shop_order_placehold';
if ( ! $post_id || ! in_array( get_post_type( $post_id ), $redirect_from_types, true ) || ! isset( $_GET['action'] ) ) {
return;
}