515 lines
15 KiB
PHP
515 lines
15 KiB
PHP
<?php
|
|
/**
|
|
* Renders order edit page, works with both post and order object.
|
|
*/
|
|
|
|
namespace Automattic\WooCommerce\Internal\Admin\Orders;
|
|
|
|
use Automattic\WooCommerce\Internal\Admin\Orders\MetaBoxes\CustomerHistory;
|
|
use Automattic\WooCommerce\Internal\Admin\Orders\MetaBoxes\CustomMetaBox;
|
|
use Automattic\WooCommerce\Internal\Admin\Orders\MetaBoxes\OrderAttribution;
|
|
use Automattic\WooCommerce\Internal\Admin\Orders\MetaBoxes\TaxonomiesMetaBox;
|
|
use Automattic\WooCommerce\Internal\Features\FeaturesController;
|
|
use Automattic\WooCommerce\Utilities\OrderUtil;
|
|
use WC_Order;
|
|
|
|
/**
|
|
* Class Edit.
|
|
*/
|
|
class Edit {
|
|
|
|
/**
|
|
* Screen ID for the edit order screen.
|
|
*
|
|
* @var string
|
|
*/
|
|
private $screen_id;
|
|
|
|
/**
|
|
* Instance of the CustomMetaBox class. Used to render meta box for custom meta.
|
|
*
|
|
* @var CustomMetaBox
|
|
*/
|
|
private $custom_meta_box;
|
|
|
|
/**
|
|
* Instance of the TaxonomiesMetaBox class. Used to render meta box for taxonomies.
|
|
*
|
|
* @var TaxonomiesMetaBox
|
|
*/
|
|
private $taxonomies_meta_box;
|
|
|
|
/**
|
|
* Instance of WC_Order to be used in metaboxes.
|
|
*
|
|
* @var \WC_Order
|
|
*/
|
|
private $order;
|
|
|
|
/**
|
|
* Action name that the form is currently handling. Could be new_order or edit_order.
|
|
*
|
|
* @var string
|
|
*/
|
|
private $current_action;
|
|
|
|
/**
|
|
* Message to be displayed to the user. Index of message from the messages array registered when declaring shop_order post type.
|
|
*
|
|
* @var int
|
|
*/
|
|
private $message;
|
|
|
|
/**
|
|
* Controller for orders page. Used to determine redirection URLs.
|
|
*
|
|
* @var PageController
|
|
*/
|
|
private $orders_page_controller;
|
|
|
|
/**
|
|
* Hooks all meta-boxes for order edit page. This is static since this may be called by post edit form rendering.
|
|
*
|
|
* @param string $screen_id Screen ID.
|
|
* @param string $title Title of the page.
|
|
*/
|
|
public static function add_order_meta_boxes( string $screen_id, string $title ) {
|
|
/* Translators: %s order type name. */
|
|
add_meta_box( 'woocommerce-order-data', sprintf( __( '%s data', 'woocommerce' ), $title ), 'WC_Meta_Box_Order_Data::output', $screen_id, 'normal', 'high' );
|
|
add_meta_box( 'woocommerce-order-items', __( 'Items', 'woocommerce' ), 'WC_Meta_Box_Order_Items::output', $screen_id, 'normal', 'high' );
|
|
/* Translators: %s order type name. */
|
|
add_meta_box( 'woocommerce-order-notes', sprintf( __( '%s notes', 'woocommerce' ), $title ), 'WC_Meta_Box_Order_Notes::output', $screen_id, 'side', 'default' );
|
|
add_meta_box( 'woocommerce-order-downloads', __( 'Downloadable product permissions', 'woocommerce' ) . wc_help_tip( __( 'Note: Permissions for order items will automatically be granted when the order status changes to processing/completed.', 'woocommerce' ) ), 'WC_Meta_Box_Order_Downloads::output', $screen_id, 'normal', 'default' );
|
|
/* Translators: %s order type name. */
|
|
add_meta_box( 'woocommerce-order-actions', sprintf( __( '%s actions', 'woocommerce' ), $title ), 'WC_Meta_Box_Order_Actions::output', $screen_id, 'side', 'high' );
|
|
self::maybe_register_order_attribution( $screen_id, $title );
|
|
}
|
|
|
|
/**
|
|
* Hooks metabox save functions for order edit page.
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function add_save_meta_boxes() {
|
|
/**
|
|
* Save Order Meta Boxes.
|
|
*
|
|
* In order:
|
|
* Save the order items.
|
|
* Save the order totals.
|
|
* Save the order downloads.
|
|
* Save order data - also updates status and sends out admin emails if needed. Last to show latest data.
|
|
* Save actions - sends out other emails. Last to show latest data.
|
|
*/
|
|
add_action( 'woocommerce_process_shop_order_meta', 'WC_Meta_Box_Order_Items::save', 10 );
|
|
add_action( 'woocommerce_process_shop_order_meta', 'WC_Meta_Box_Order_Downloads::save', 30, 2 );
|
|
add_action( 'woocommerce_process_shop_order_meta', 'WC_Meta_Box_Order_Data::save', 40 );
|
|
add_action( 'woocommerce_process_shop_order_meta', 'WC_Meta_Box_Order_Actions::save', 50, 2 );
|
|
}
|
|
|
|
/**
|
|
* Enqueue necessary scripts for order edit page.
|
|
*/
|
|
private function enqueue_scripts() {
|
|
if ( wp_is_mobile() ) {
|
|
wp_enqueue_script( 'jquery-touch-punch' );
|
|
}
|
|
wp_enqueue_script( 'post' ); // Ensure existing JS libraries are still available for backward compat.
|
|
}
|
|
|
|
/**
|
|
* Returns the PageController for this edit form. This method is protected to allow child classes to overwrite the PageController object and return custom links.
|
|
*
|
|
* @since 8.0.0
|
|
*
|
|
* @return PageController PageController object.
|
|
*/
|
|
protected function get_page_controller() {
|
|
if ( ! isset( $this->orders_page_controller ) ) {
|
|
$this->orders_page_controller = wc_get_container()->get( PageController::class );
|
|
}
|
|
return $this->orders_page_controller;
|
|
}
|
|
|
|
/**
|
|
* Setup hooks, actions and variables needed to render order edit page.
|
|
*
|
|
* @param \WC_Order $order Order object.
|
|
*/
|
|
public function setup( \WC_Order $order ) {
|
|
$this->order = $order;
|
|
$current_screen = get_current_screen();
|
|
$current_screen->is_block_editor( false );
|
|
$this->screen_id = $current_screen->id;
|
|
if ( ! isset( $this->custom_meta_box ) ) {
|
|
$this->custom_meta_box = wc_get_container()->get( CustomMetaBox::class );
|
|
}
|
|
|
|
if ( ! isset( $this->taxonomies_meta_box ) ) {
|
|
$this->taxonomies_meta_box = wc_get_container()->get( TaxonomiesMetaBox::class );
|
|
}
|
|
|
|
$this->add_save_meta_boxes();
|
|
$this->handle_order_update();
|
|
$this->add_order_meta_boxes( $this->screen_id, __( 'Order', 'woocommerce' ) );
|
|
$this->add_order_specific_meta_box();
|
|
$this->add_order_taxonomies_meta_box();
|
|
|
|
/**
|
|
* From wp-admin/includes/meta-boxes.php.
|
|
*
|
|
* Fires after all built-in meta boxes have been added. Custom metaboxes may be enqueued here.
|
|
*
|
|
* @since 3.8.0.
|
|
*/
|
|
do_action( 'add_meta_boxes', $this->screen_id, $this->order );
|
|
|
|
/**
|
|
* Provides an opportunity to inject custom meta boxes into the order editor screen. This
|
|
* hook is an analog of `add_meta_boxes_<POST_TYPE>` as provided by WordPress core.
|
|
*
|
|
* @since 7.4.0
|
|
*
|
|
* @oaram WC_Order $order The order being edited.
|
|
*/
|
|
do_action( 'add_meta_boxes_' . $this->screen_id, $this->order );
|
|
|
|
$this->enqueue_scripts();
|
|
}
|
|
|
|
/**
|
|
* Set the current action for the form.
|
|
*
|
|
* @param string $action Action name.
|
|
*/
|
|
public function set_current_action( string $action ) {
|
|
$this->current_action = $action;
|
|
}
|
|
|
|
/**
|
|
* Hooks meta box for order specific meta.
|
|
*/
|
|
private function add_order_specific_meta_box() {
|
|
add_meta_box(
|
|
'order_custom',
|
|
__( 'Custom Fields', 'woocommerce' ),
|
|
array( $this, 'render_custom_meta_box' ),
|
|
$this->screen_id,
|
|
'normal'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Render custom meta box.
|
|
*
|
|
* @return void
|
|
*/
|
|
private function add_order_taxonomies_meta_box() {
|
|
$this->taxonomies_meta_box->add_taxonomies_meta_boxes( $this->screen_id, $this->order->get_type() );
|
|
}
|
|
|
|
/**
|
|
* Register order attribution meta boxes if the feature is enabled.
|
|
*
|
|
* @since 8.5.0
|
|
*
|
|
* @param string $screen_id Screen ID.
|
|
* @param string $title Title of the page.
|
|
*
|
|
* @return void
|
|
*/
|
|
private static function maybe_register_order_attribution( string $screen_id, string $title ) {
|
|
/**
|
|
* Features controller.
|
|
*
|
|
* @var FeaturesController $feature_controller
|
|
*/
|
|
$feature_controller = wc_get_container()->get( FeaturesController::class );
|
|
if ( ! $feature_controller->feature_is_enabled( 'order_attribution' ) ) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Order attribution meta box.
|
|
*
|
|
* @var OrderAttribution $order_attribution_meta_box
|
|
*/
|
|
$order_attribution_meta_box = wc_get_container()->get( OrderAttribution::class );
|
|
|
|
add_meta_box(
|
|
'woocommerce-order-source-data',
|
|
/* Translators: %s order type name. */
|
|
sprintf( __( '%s attribution', 'woocommerce' ), $title ),
|
|
function( $post_or_order ) use ( $order_attribution_meta_box ) {
|
|
$order = $post_or_order instanceof WC_Order ? $post_or_order : wc_get_order( $post_or_order );
|
|
if ( $order instanceof WC_Order ) {
|
|
$order_attribution_meta_box->output( $order );
|
|
}
|
|
},
|
|
$screen_id,
|
|
'side',
|
|
'high'
|
|
);
|
|
|
|
// Add customer history meta box if analytics is enabled.
|
|
if ( 'yes' !== get_option( 'woocommerce_analytics_enabled' ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! OrderUtil::is_order_edit_screen() ) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Customer history meta box.
|
|
*
|
|
* @var CustomerHistory $customer_history_meta_box
|
|
*/
|
|
$customer_history_meta_box = wc_get_container()->get( CustomerHistory::class );
|
|
|
|
add_meta_box(
|
|
'woocommerce-customer-history',
|
|
__( 'Customer history', 'woocommerce' ),
|
|
function ( $post_or_order ) use ( $customer_history_meta_box ) {
|
|
$order = $post_or_order instanceof WC_Order ? $post_or_order : wc_get_order( $post_or_order );
|
|
if ( $order instanceof WC_Order ) {
|
|
$customer_history_meta_box->output( $order );
|
|
}
|
|
},
|
|
$screen_id,
|
|
'side',
|
|
'high'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Takes care of updating order data. Fires action that metaboxes can hook to for order data updating.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function handle_order_update() {
|
|
if ( ! isset( $this->order ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( 'edit_order' !== sanitize_text_field( wp_unslash( $_POST['action'] ?? '' ) ) ) {
|
|
return;
|
|
}
|
|
|
|
check_admin_referer( $this->get_order_edit_nonce_action() );
|
|
|
|
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized later on by taxonomies_meta_box object.
|
|
$taxonomy_input = isset( $_POST['tax_input'] ) ? wp_unslash( $_POST['tax_input'] ) : null;
|
|
$this->taxonomies_meta_box->save_taxonomies( $this->order, $taxonomy_input );
|
|
|
|
/**
|
|
* Save meta for shop order.
|
|
*
|
|
* @param int Order ID.
|
|
* @param \WC_Order Post object.
|
|
*
|
|
* @since 2.1.0
|
|
*/
|
|
do_action( 'woocommerce_process_shop_order_meta', $this->order->get_id(), $this->order );
|
|
|
|
$this->custom_meta_box->handle_metadata_changes($this->order);
|
|
|
|
// Order updated message.
|
|
$this->message = 1;
|
|
|
|
// Claim lock.
|
|
$edit_lock = wc_get_container()->get( EditLock::class );
|
|
$edit_lock->lock( $this->order );
|
|
|
|
$this->redirect_order( $this->order );
|
|
}
|
|
|
|
/**
|
|
* Helper method to redirect to order edit page.
|
|
*
|
|
* @since 8.0.0
|
|
*
|
|
* @param \WC_Order $order Order object.
|
|
*/
|
|
private function redirect_order( \WC_Order $order ) {
|
|
$redirect_to = $this->get_page_controller()->get_edit_url( $order->get_id() );
|
|
if ( isset( $this->message ) ) {
|
|
$redirect_to = add_query_arg( 'message', $this->message, $redirect_to );
|
|
}
|
|
wp_safe_redirect(
|
|
/**
|
|
* Filter the URL used to redirect after an order is updated. Similar to the WP post's `redirect_post_location` filter.
|
|
*
|
|
* @param string $redirect_to The redirect destination URL.
|
|
* @param int $order_id The order ID.
|
|
* @param \WC_Order $order The order object.
|
|
*
|
|
* @since 8.0.0
|
|
*/
|
|
apply_filters(
|
|
'woocommerce_redirect_order_location',
|
|
$redirect_to,
|
|
$order->get_id(),
|
|
$order
|
|
)
|
|
);
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Helper method to get the name of order edit nonce.
|
|
*
|
|
* @return string Nonce action name.
|
|
*/
|
|
private function get_order_edit_nonce_action() {
|
|
return 'update-order_' . $this->order->get_id();
|
|
}
|
|
|
|
/**
|
|
* Render meta box for order specific meta.
|
|
*/
|
|
public function render_custom_meta_box() {
|
|
$this->custom_meta_box->output( $this->order );
|
|
}
|
|
|
|
/**
|
|
* Render order edit page.
|
|
*/
|
|
public function display() {
|
|
/**
|
|
* This is used by the order edit page to show messages in the notice fields.
|
|
* It should be similar to post_updated_messages filter, i.e.:
|
|
* array(
|
|
* {order_type} => array(
|
|
* 1 => 'Order updated.',
|
|
* 2 => 'Custom field updated.',
|
|
* ...
|
|
* ).
|
|
*
|
|
* The index to be displayed is computed from the $_GET['message'] variable.
|
|
*
|
|
* @since 7.4.0.
|
|
*/
|
|
$messages = apply_filters( 'woocommerce_order_updated_messages', array() );
|
|
|
|
$message = $this->message;
|
|
if ( isset( $_GET['message'] ) ) {
|
|
$message = absint( $_GET['message'] );
|
|
}
|
|
|
|
if ( isset( $message ) ) {
|
|
$message = $messages[ $this->order->get_type() ][ $message ] ?? false;
|
|
}
|
|
|
|
$this->render_wrapper_start( '', $message );
|
|
$this->render_meta_boxes();
|
|
$this->render_wrapper_end();
|
|
}
|
|
|
|
/**
|
|
* Helper function to render wrapper start.
|
|
*
|
|
* @param string $notice Notice to display, if any.
|
|
* @param string $message Message to display, if any.
|
|
*/
|
|
private function render_wrapper_start( $notice = '', $message = '' ) {
|
|
$post_type = get_post_type_object( $this->order->get_type() );
|
|
|
|
$edit_page_url = $this->get_page_controller()->get_edit_url( $this->order->get_id() );
|
|
$form_action = 'edit_order';
|
|
$referer = wp_get_referer();
|
|
$new_page_url = $this->get_page_controller()->get_new_page_url( $this->order->get_type() );
|
|
|
|
?>
|
|
<div class="wrap">
|
|
<h1 class="wp-heading-inline">
|
|
<?php
|
|
echo 'new_order' === $this->current_action ? esc_html( $post_type->labels->add_new_item ) : esc_html( $post_type->labels->edit_item );
|
|
?>
|
|
</h1>
|
|
<?php
|
|
if ( 'edit_order' === $this->current_action ) {
|
|
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">
|
|
|
|
<?php
|
|
if ( $notice ) :
|
|
?>
|
|
<div id="notice" class="notice notice-warning"><p
|
|
id="has-newer-autosave"><?php echo wp_kses_post( $notice ); ?></p></div>
|
|
<?php endif; ?>
|
|
<?php if ( $message ) : ?>
|
|
<div id="message" class="updated notice notice-success is-dismissible">
|
|
<p><?php echo wp_kses_post( $message ); ?></p></div>
|
|
<?php
|
|
endif;
|
|
?>
|
|
|
|
<form name="order" action="<?php echo esc_url( $edit_page_url ); ?>" method="post" id="order"
|
|
<?php
|
|
/**
|
|
* Fires inside the order edit form tag.
|
|
*
|
|
* @param \WC_Order $order Order object.
|
|
*
|
|
* @since 6.9.0
|
|
*/
|
|
do_action( 'order_edit_form_tag', $this->order );
|
|
?>
|
|
>
|
|
<?php wp_nonce_field( $this->get_order_edit_nonce_action() ); ?>
|
|
<?php
|
|
/**
|
|
* Fires at the top of the order edit form. Can be used as a replacement for edit_form_top hook for HPOS.
|
|
*
|
|
* @param \WC_Order $order Order object.
|
|
*
|
|
* @since 8.0.0
|
|
*/
|
|
do_action( 'order_edit_form_top', $this->order );
|
|
|
|
wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
|
|
wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
|
|
?>
|
|
<input type="hidden" id="hiddenaction" name="action" value="<?php echo esc_attr( $form_action ); ?>"/>
|
|
<input type="hidden" id="original_order_status" name="original_order_status" value="<?php echo esc_attr( $this->order->get_status() ); ?>"/>
|
|
<input type="hidden" id="referredby" name="referredby" value="<?php echo $referer ? esc_url( $referer ) : ''; ?>"/>
|
|
<input type="hidden" id="post_ID" name="post_ID" value="<?php echo esc_attr( $this->order->get_id() ); ?>"/>
|
|
<div id="poststuff">
|
|
<div id="post-body"
|
|
class="metabox-holder columns-<?php echo ( 1 === get_current_screen()->get_columns() ) ? '1' : '2'; ?>">
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Helper function to render meta boxes.
|
|
*/
|
|
private function render_meta_boxes() {
|
|
?>
|
|
<div id="postbox-container-1" class="postbox-container">
|
|
<?php do_meta_boxes( $this->screen_id, 'side', $this->order ); ?>
|
|
</div>
|
|
<div id="postbox-container-2" class="postbox-container">
|
|
<?php
|
|
do_meta_boxes( $this->screen_id, 'normal', $this->order );
|
|
do_meta_boxes( $this->screen_id, 'advanced', $this->order );
|
|
?>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Helper function to render wrapper end.
|
|
*/
|
|
private function render_wrapper_end() {
|
|
?>
|
|
</div> <!-- /post-body -->
|
|
</div> <!-- /poststuff -->
|
|
</form>
|
|
</div> <!-- /wrap -->
|
|
<?php
|
|
}
|
|
}
|