PluginProbe
WooCommerce / 11.1.0-beta.2
WooCommerce v11.1.0-beta.2
11.1.0 11.1.0-rc.2 11.1.0-rc.1 11.1.0-beta.2 11.1.0-beta.1 11.0.1 11.0.0 11.0.0-rc.3 11.0.0-rc.2 11.0.0-rc.1 11.0.0-beta.2 11.0.0-beta.1 10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 All 648 releases
woocommerce / src / Internal / Admin / Orders / Edit.php
Edit.php
525 lines 15.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Renders order edit page, works with both post and order object.
4 */
5
6 namespace Automattic\WooCommerce\Internal\Admin\Orders;
7
8 use Automattic\WooCommerce\Internal\Admin\Orders\MetaBoxes\CustomerHistory;
9 use Automattic\WooCommerce\Internal\Admin\Orders\MetaBoxes\CustomMetaBox;
10 use Automattic\WooCommerce\Internal\Admin\Orders\MetaBoxes\OrderAttribution;
11 use Automattic\WooCommerce\Internal\Admin\Orders\MetaBoxes\TaxonomiesMetaBox;
12 use Automattic\WooCommerce\Internal\Features\FeaturesController;
13 use Automattic\WooCommerce\Utilities\OrderUtil;
14 use WC_Order;
15
16 /**
17 * Class Edit.
18 */
19 class Edit {
20
21 /**
22 * Screen ID for the edit order screen.
23 *
24 * @var string
25 */
26 private $screen_id;
27
28 /**
29 * Instance of the CustomMetaBox class. Used to render meta box for custom meta.
30 *
31 * @var CustomMetaBox
32 */
33 private $custom_meta_box;
34
35 /**
36 * Instance of the TaxonomiesMetaBox class. Used to render meta box for taxonomies.
37 *
38 * @var TaxonomiesMetaBox
39 */
40 private $taxonomies_meta_box;
41
42 /**
43 * Instance of WC_Order to be used in metaboxes.
44 *
45 * @var \WC_Order
46 */
47 private $order;
48
49 /**
50 * Action name that the form is currently handling. Could be new_order or edit_order.
51 *
52 * @var string
53 */
54 private $current_action;
55
56 /**
57 * Message to be displayed to the user. Index of message from the messages array registered when declaring shop_order post type.
58 *
59 * @var int
60 */
61 private $message;
62
63 /**
64 * Controller for orders page. Used to determine redirection URLs.
65 *
66 * @var PageController
67 */
68 private $orders_page_controller;
69
70 /**
71 * Hooks all meta-boxes for order edit page. This is static since this may be called by post edit form rendering.
72 *
73 * @param string $screen_id Screen ID.
74 * @param string $title Title of the page.
75 */
76 public static function add_order_meta_boxes( string $screen_id, string $title ) {
77 /* Translators: %s order type name. */
78 add_meta_box( 'woocommerce-order-data', sprintf( __( '%s data', 'woocommerce' ), $title ), 'WC_Meta_Box_Order_Data::output', $screen_id, 'normal', 'high' );
79 add_meta_box( 'woocommerce-order-items', __( 'Items', 'woocommerce' ), 'WC_Meta_Box_Order_Items::output', $screen_id, 'normal', 'high' );
80 /* Translators: %s order type name. */
81 add_meta_box( 'woocommerce-order-notes', sprintf( __( '%s notes', 'woocommerce' ), $title ), 'WC_Meta_Box_Order_Notes::output', $screen_id, 'side', 'default' );
82 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' );
83 /* Translators: %s order type name. */
84 add_meta_box( 'woocommerce-order-actions', sprintf( __( '%s actions', 'woocommerce' ), $title ), 'WC_Meta_Box_Order_Actions::output', $screen_id, 'side', 'high' );
85 self::maybe_register_order_attribution( $screen_id, $title );
86 }
87
88 /**
89 * Hooks metabox save functions for order edit page.
90 *
91 * @return void
92 */
93 public static function add_save_meta_boxes() {
94 /**
95 * Save Order Meta Boxes.
96 *
97 * In order:
98 * Save the order items.
99 * Save the order totals.
100 * Save the order downloads.
101 * Save order data - also updates status and sends out admin emails if needed. Last to show latest data.
102 * Save actions - sends out other emails. Last to show latest data.
103 */
104 add_action( 'woocommerce_process_shop_order_meta', 'WC_Meta_Box_Order_Items::save', 10 );
105 add_action( 'woocommerce_process_shop_order_meta', 'WC_Meta_Box_Order_Downloads::save', 30, 2 );
106 add_action( 'woocommerce_process_shop_order_meta', 'WC_Meta_Box_Order_Data::save', 40 );
107 add_action( 'woocommerce_process_shop_order_meta', 'WC_Meta_Box_Order_Actions::save', 50, 2 );
108 }
109
110 /**
111 * Enqueue necessary scripts for order edit page.
112 */
113 private function enqueue_scripts() {
114 if ( wp_is_mobile() ) {
115 wp_enqueue_script( 'jquery-touch-punch' );
116 }
117 wp_enqueue_script( 'post' ); // Ensure existing JS libraries are still available for backward compat.
118 }
119
120 /**
121 * Returns the PageController for this edit form. This method is protected to allow child classes to overwrite the PageController object and return custom links.
122 *
123 * @since 8.0.0
124 *
125 * @return PageController PageController object.
126 */
127 protected function get_page_controller() {
128 if ( ! isset( $this->orders_page_controller ) ) {
129 $this->orders_page_controller = wc_get_container()->get( PageController::class );
130 }
131 return $this->orders_page_controller;
132 }
133
134 /**
135 * Setup hooks, actions and variables needed to render order edit page.
136 *
137 * @param \WC_Order $order Order object.
138 */
139 public function setup( \WC_Order $order ) {
140 $this->order = $order;
141 $current_screen = get_current_screen();
142 $current_screen->is_block_editor( false );
143 $this->screen_id = $current_screen->id;
144 if ( ! isset( $this->custom_meta_box ) ) {
145 $this->custom_meta_box = wc_get_container()->get( CustomMetaBox::class );
146 }
147
148 if ( ! isset( $this->taxonomies_meta_box ) ) {
149 $this->taxonomies_meta_box = wc_get_container()->get( TaxonomiesMetaBox::class );
150 }
151
152 $this->add_save_meta_boxes();
153 $this->handle_order_update();
154 $this->add_order_meta_boxes( $this->screen_id, __( 'Order', 'woocommerce' ) );
155 $this->add_order_specific_meta_box();
156 $this->add_order_taxonomies_meta_box();
157
158 /**
159 * From wp-admin/includes/meta-boxes.php.
160 *
161 * Fires after all built-in meta boxes have been added. Custom metaboxes may be enqueued here.
162 *
163 * Note that the documentation for this hook (and for the corresponding 'add_meta_boxes_<SCREEN_ID>' hook)
164 * suggest that a post type will be supplied for the first parameter, and and an instance of WP_Post will be
165 * supplied as the second parameter. We are not doing that here, however WordPress itself also deviates from
166 * this in respect of comments and (though now less relevant) links.
167 *
168 * @since 3.8.0.
169 */
170 do_action( 'add_meta_boxes', $this->screen_id, $this->order );
171
172 /**
173 * Provides an opportunity to inject custom meta boxes into the order editor screen. This
174 * hook is an analog of `add_meta_boxes_<POST_TYPE>` as provided by WordPress core.
175 *
176 * @since 7.4.0
177 *
178 * @param WC_Order $order The order being edited.
179 */
180 do_action( 'add_meta_boxes_' . $this->screen_id, $this->order );
181
182 $this->enqueue_scripts();
183 }
184
185 /**
186 * Set the current action for the form.
187 *
188 * @param string $action Action name.
189 */
190 public function set_current_action( string $action ) {
191 $this->current_action = $action;
192 }
193
194 /**
195 * Hooks meta box for order specific meta.
196 */
197 private function add_order_specific_meta_box() {
198 add_meta_box(
199 'order_custom',
200 __( 'Custom Fields', 'woocommerce' ),
201 array( $this, 'render_custom_meta_box' ),
202 $this->screen_id,
203 'normal'
204 );
205 }
206
207 /**
208 * Render custom meta box.
209 *
210 * @return void
211 */
212 private function add_order_taxonomies_meta_box() {
213 $this->taxonomies_meta_box->add_taxonomies_meta_boxes( $this->screen_id, $this->order->get_type() );
214 }
215
216 /**
217 * Register order attribution meta boxes if the feature is enabled.
218 *
219 * @since 8.5.0
220 *
221 * @param string $screen_id Screen ID.
222 * @param string $title Title of the page.
223 *
224 * @return void
225 */
226 private static function maybe_register_order_attribution( string $screen_id, string $title ) {
227 /**
228 * Features controller.
229 *
230 * @var FeaturesController $feature_controller
231 */
232 $feature_controller = wc_get_container()->get( FeaturesController::class );
233 if ( ! $feature_controller->feature_is_enabled( 'order_attribution' ) ) {
234 return;
235 }
236
237 /**
238 * Order attribution meta box.
239 *
240 * @var OrderAttribution $order_attribution_meta_box
241 */
242 $order_attribution_meta_box = wc_get_container()->get( OrderAttribution::class );
243
244 add_meta_box(
245 'woocommerce-order-source-data',
246 /* Translators: %s order type name. */
247 sprintf( __( '%s attribution', 'woocommerce' ), $title ),
248 function( $post_or_order ) use ( $order_attribution_meta_box ) {
249 $order = $post_or_order instanceof WC_Order ? $post_or_order : wc_get_order( $post_or_order );
250 if ( $order instanceof WC_Order ) {
251 $order_attribution_meta_box->output( $order );
252 }
253 },
254 $screen_id,
255 'side',
256 'high'
257 );
258
259 // Add customer history meta box if analytics is enabled.
260 if ( 'yes' !== get_option( 'woocommerce_analytics_enabled' ) ) {
261 return;
262 }
263
264 if ( ! OrderUtil::is_order_edit_screen() ) {
265 return;
266 }
267
268 /**
269 * Customer history meta box.
270 *
271 * @var CustomerHistory $customer_history_meta_box
272 */
273 $customer_history_meta_box = wc_get_container()->get( CustomerHistory::class );
274
275 add_meta_box(
276 'woocommerce-customer-history',
277 __( 'Customer history', 'woocommerce' ),
278 function ( $post_or_order ) use ( $customer_history_meta_box ) {
279 $order = $post_or_order instanceof WC_Order ? $post_or_order : wc_get_order( $post_or_order );
280 if ( $order instanceof WC_Order ) {
281 $customer_history_meta_box->output( $order );
282 }
283 },
284 $screen_id,
285 'side',
286 'high'
287 );
288 }
289
290 /**
291 * Takes care of updating order data. Fires action that metaboxes can hook to for order data updating.
292 *
293 * @return void
294 */
295 public function handle_order_update() {
296 if ( ! isset( $this->order ) ) {
297 return;
298 }
299
300 if ( 'edit_order' !== sanitize_text_field( wp_unslash( $_POST['action'] ?? '' ) ) ) {
301 return;
302 }
303
304 check_admin_referer( $this->get_order_edit_nonce_action() );
305
306 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized later on by taxonomies_meta_box object.
307 $taxonomy_input = isset( $_POST['tax_input'] ) ? wp_unslash( $_POST['tax_input'] ) : null;
308 $this->taxonomies_meta_box->save_taxonomies( $this->order, $taxonomy_input );
309
310 /**
311 * Save meta for shop order.
312 *
313 * @param int Order ID.
314 * @param \WC_Order Post object.
315 *
316 * @since 2.1.0
317 */
318 do_action( 'woocommerce_process_shop_order_meta', $this->order->get_id(), $this->order );
319
320 $this->custom_meta_box->handle_metadata_changes($this->order);
321
322 // Order updated message.
323 $this->message = 1;
324
325 // Claim lock.
326 $edit_lock = wc_get_container()->get( EditLock::class );
327 $edit_lock->lock( $this->order );
328
329 $this->redirect_order( $this->order );
330 }
331
332 /**
333 * Helper method to redirect to order edit page.
334 *
335 * @since 8.0.0
336 *
337 * @param \WC_Order $order Order object.
338 */
339 private function redirect_order( \WC_Order $order ) {
340 $redirect_to = $this->get_page_controller()->get_edit_url( $order->get_id() );
341 if ( isset( $this->message ) ) {
342 $redirect_to = add_query_arg( 'message', $this->message, $redirect_to );
343 }
344 wp_safe_redirect(
345 /**
346 * Filter the URL used to redirect after an order is updated. Similar to the WP post's `redirect_post_location` filter.
347 *
348 * @param string $redirect_to The redirect destination URL.
349 * @param int $order_id The order ID.
350 * @param \WC_Order $order The order object.
351 *
352 * @since 8.0.0
353 */
354 apply_filters(
355 'woocommerce_redirect_order_location',
356 $redirect_to,
357 $order->get_id(),
358 $order
359 )
360 );
361 exit;
362 }
363
364 /**
365 * Helper method to get the name of order edit nonce.
366 *
367 * @return string Nonce action name.
368 */
369 private function get_order_edit_nonce_action() {
370 return 'update-order_' . $this->order->get_id();
371 }
372
373 /**
374 * Render meta box for order specific meta.
375 */
376 public function render_custom_meta_box() {
377 $this->custom_meta_box->output( $this->order );
378 }
379
380 /**
381 * Render order edit page.
382 */
383 public function display() {
384 /**
385 * This is used by the order edit page to show messages in the notice fields.
386 * It should be similar to post_updated_messages filter, i.e.:
387 * array(
388 * {order_type} => array(
389 * 1 => 'Order updated.',
390 * 2 => 'Custom field updated.',
391 * ...
392 * ).
393 *
394 * The index to be displayed is computed from the $_GET['message'] variable.
395 *
396 * @since 7.4.0.
397 */
398 $messages = apply_filters( 'woocommerce_order_updated_messages', array() );
399
400 $message = $this->message;
401 if ( isset( $_GET['message'] ) ) {
402 $message = absint( $_GET['message'] );
403 }
404
405 if ( isset( $message ) ) {
406 $message = $messages[ $this->order->get_type() ][ $message ] ?? false;
407 }
408
409 $this->render_wrapper_start( '', $message );
410 $this->render_meta_boxes();
411 $this->render_wrapper_end();
412 }
413
414 /**
415 * Helper function to render wrapper start.
416 *
417 * @param string $notice Notice to display, if any.
418 * @param string $message Message to display, if any.
419 */
420 private function render_wrapper_start( $notice = '', $message = '' ) {
421 $post_type = get_post_type_object( $this->order->get_type() );
422
423 $edit_page_url = $this->get_page_controller()->get_edit_url( $this->order->get_id() );
424 $form_action = 'edit_order';
425 $referer = wp_get_referer();
426 $new_page_url = $this->get_page_controller()->get_new_page_url( $this->order->get_type() );
427
428 ?>
429 <div class="wrap">
430 <h1 class="wp-heading-inline">
431 <?php
432 echo 'new_order' === $this->current_action ? esc_html( $post_type->labels->add_new_item ) : esc_html( $post_type->labels->edit_item );
433 ?>
434 </h1>
435 <?php
436 if ( 'edit_order' === $this->current_action ) {
437 echo ' <a href="' . esc_url( $new_page_url ) . '" class="page-title-action">' . esc_html( $post_type->labels->add_new ) . '</a>';
438 }
439 ?>
440 <hr class="wp-header-end">
441
442 <?php
443 if ( $notice ) :
444 ?>
445 <div id="notice" class="notice notice-warning"><p
446 id="has-newer-autosave"><?php echo wp_kses_post( $notice ); ?></p></div>
447 <?php endif; ?>
448 <?php if ( $message ) : ?>
449 <div id="message" class="updated notice notice-success is-dismissible">
450 <p><?php echo wp_kses_post( $message ); ?></p></div>
451 <?php
452 endif;
453 ?>
454
455 <form name="order" action="<?php echo esc_url( $edit_page_url ); ?>" method="post" id="order"
456 <?php
457 /**
458 * Fires inside the order edit form tag.
459 *
460 * @param \WC_Order $order Order object.
461 *
462 * @since 6.9.0
463 */
464 do_action( 'order_edit_form_tag', $this->order );
465 ?>
466 >
467 <?php wp_nonce_field( $this->get_order_edit_nonce_action() ); ?>
468 <?php
469 /**
470 * Fires at the top of the order edit form. Can be used as a replacement for edit_form_top hook for HPOS.
471 *
472 * @param \WC_Order $order Order object.
473 *
474 * @since 8.0.0
475 */
476 do_action( 'order_edit_form_top', $this->order );
477
478 wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
479 wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
480 ?>
481 <input type="hidden" id="hiddenaction" name="action" value="<?php echo esc_attr( $form_action ); ?>"/>
482
483 <?php
484 $order_status = $this->order->get_status( 'edit' );
485 ?>
486 <input type="hidden" id="original_order_status" name="original_order_status" value="<?php echo esc_attr( $order_status ); ?>"/>
487 <input type="hidden" id="original_post_status" name="original_post_status" value="<?php echo esc_attr( wc_is_order_status( 'wc-' . $order_status ) ? 'wc-' . $order_status : $order_status ); ?>"/>
488 <input type="hidden" id="referredby" name="referredby" value="<?php echo $referer ? esc_url( $referer ) : ''; ?>"/>
489 <input type="hidden" id="post_ID" name="post_ID" value="<?php echo esc_attr( $this->order->get_id() ); ?>"/>
490 <div id="poststuff">
491 <div id="post-body"
492 class="metabox-holder columns-<?php echo ( 1 === get_current_screen()->get_columns() ) ? '1' : '2'; ?>">
493 <?php
494 }
495
496 /**
497 * Helper function to render meta boxes.
498 */
499 private function render_meta_boxes() {
500 ?>
501 <div id="postbox-container-1" class="postbox-container">
502 <?php do_meta_boxes( $this->screen_id, 'side', $this->order ); ?>
503 </div>
504 <div id="postbox-container-2" class="postbox-container">
505 <?php
506 do_meta_boxes( $this->screen_id, 'normal', $this->order );
507 do_meta_boxes( $this->screen_id, 'advanced', $this->order );
508 ?>
509 </div>
510 <?php
511 }
512
513 /**
514 * Helper function to render wrapper end.
515 */
516 private function render_wrapper_end() {
517 ?>
518 </div> <!-- /post-body -->
519 </div> <!-- /poststuff -->
520 </form>
521 </div> <!-- /wrap -->
522 <?php
523 }
524 }
525