PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 10.9.3
WooCommerce v10.9.3
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 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / includes / admin / list-tables / class-wc-admin-list-table-orders.php
woocommerce / includes / admin / list-tables Last commit date
abstract-class-wc-admin-list-table.php 5 years ago class-wc-admin-list-table-coupons.php 2 years ago class-wc-admin-list-table-orders.php 5 months ago class-wc-admin-list-table-products.php 1 month ago
class-wc-admin-list-table-orders.php
747 lines
1 <?php
2 /**
3 * List tables: orders.
4 *
5 * @package WooCommerce\Admin
6 * @version 3.3.0
7 */
8
9 use Automattic\WooCommerce\Enums\OrderStatus;
10 use Automattic\WooCommerce\Internal\Admin\Orders\ListTable;
11
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit;
14 }
15
16 if ( class_exists( 'WC_Admin_List_Table_Orders', false ) ) {
17 return;
18 }
19
20 if ( ! class_exists( 'WC_Admin_List_Table', false ) ) {
21 include_once __DIR__ . '/abstract-class-wc-admin-list-table.php';
22 }
23
24 /**
25 * WC_Admin_List_Table_Orders Class.
26 */
27 class WC_Admin_List_Table_Orders extends WC_Admin_List_Table {
28
29 /**
30 * Post type.
31 *
32 * @var string
33 */
34 protected $list_table_type = 'shop_order';
35
36 /**
37 * The data store-agnostic list table implementation (introduced to support custom order tables),
38 * which we use here to render columns.
39 *
40 * @var ListTable $orders_list_table
41 */
42 private $orders_list_table;
43
44 /**
45 * Constructor.
46 */
47 public function __construct() {
48 parent::__construct();
49 $this->orders_list_table = wc_get_container()->get( ListTable::class );
50 add_action( 'admin_notices', array( $this, 'bulk_admin_notices' ) );
51 add_action( 'admin_footer', array( $this, 'order_preview_template' ) );
52 add_filter( 'get_search_query', array( $this, 'search_label' ) );
53 add_filter( 'query_vars', array( $this, 'add_custom_query_var' ) );
54 add_action( 'parse_query', array( $this, 'search_custom_fields' ) );
55 }
56
57 /**
58 * Render blank state.
59 */
60 protected function render_blank_state() {
61 $this->orders_list_table->render_blank_state();
62 }
63
64 /**
65 * Define primary column.
66 *
67 * @return string
68 */
69 protected function get_primary_column() {
70 return 'order_number';
71 }
72
73 /**
74 * Get row actions to show in the list table.
75 *
76 * @param array $actions Array of actions.
77 * @param WP_Post $post Current post object.
78 * @return array
79 */
80 protected function get_row_actions( $actions, $post ) {
81 return array();
82 }
83
84 /**
85 * Define hidden columns.
86 *
87 * @return array
88 */
89 protected function define_hidden_columns() {
90 return array(
91 'shipping_address',
92 'billing_address',
93 'wc_actions',
94 );
95 }
96
97 /**
98 * Define which columns are sortable.
99 *
100 * @param array $columns Existing columns.
101 * @return array
102 */
103 public function define_sortable_columns( $columns ) {
104 $custom = array(
105 'order_number' => 'ID',
106 'order_total' => 'order_total',
107 'order_date' => 'date',
108 );
109 unset( $columns['comments'] );
110
111 return wp_parse_args( $custom, $columns );
112 }
113
114 /**
115 * Define which columns to show on this screen.
116 *
117 * @param array $columns Existing columns.
118 * @return array
119 */
120 public function define_columns( $columns ) {
121 $show_columns = array();
122 $show_columns['cb'] = $columns['cb'];
123 $show_columns['order_number'] = __( 'Order', 'woocommerce' );
124 $show_columns['order_date'] = __( 'Date', 'woocommerce' );
125 $show_columns['order_status'] = __( 'Status', 'woocommerce' );
126 $show_columns['billing_address'] = __( 'Billing', 'woocommerce' );
127 $show_columns['shipping_address'] = __( 'Ship to', 'woocommerce' );
128 $show_columns['order_total'] = __( 'Total', 'woocommerce' );
129 $show_columns['wc_actions'] = __( 'Actions', 'woocommerce' );
130
131 wp_enqueue_script( 'wc-orders' );
132
133 return $show_columns;
134 }
135
136 /**
137 * Define bulk actions.
138 *
139 * @param array $actions Existing actions.
140 * @return array
141 */
142 public function define_bulk_actions( $actions ) {
143 if ( isset( $actions['edit'] ) ) {
144 unset( $actions['edit'] );
145 }
146
147 $actions['mark_processing'] = __( 'Change status to processing', 'woocommerce' );
148 $actions['mark_on-hold'] = __( 'Change status to on-hold', 'woocommerce' );
149 $actions['mark_completed'] = __( 'Change status to completed', 'woocommerce' );
150 $actions['mark_cancelled'] = __( 'Change status to cancelled', 'woocommerce' );
151
152 if ( wc_string_to_bool( get_option( 'woocommerce_allow_bulk_remove_personal_data', 'no' ) ) ) {
153 $actions['remove_personal_data'] = __( 'Remove personal data', 'woocommerce' );
154 }
155
156 return $actions;
157 }
158
159 /**
160 * Pre-fetch any data for the row each column has access to it. the_order global is there for bw compat.
161 *
162 * @param int $post_id Post ID being shown.
163 */
164 protected function prepare_row_data( $post_id ) {
165 global $the_order;
166
167 if ( empty( $this->object ) || $this->object->get_id() !== $post_id ) {
168 $this->object = wc_get_order( $post_id );
169 $the_order = $this->object;
170 }
171 }
172
173 /**
174 * Render column: order_number.
175 */
176 protected function render_order_number_column() {
177 $this->orders_list_table->render_order_number_column( $this->object );
178 }
179
180 /**
181 * Render column: order_status.
182 */
183 protected function render_order_status_column() {
184 $this->orders_list_table->render_order_status_column( $this->object );
185 }
186
187 /**
188 * Render column: order_date.
189 */
190 protected function render_order_date_column() {
191 $this->orders_list_table->render_order_date_column( $this->object );
192 }
193
194 /**
195 * Render column: order_total.
196 */
197 protected function render_order_total_column() {
198 $this->orders_list_table->render_order_total_column( $this->object );
199 }
200
201 /**
202 * Render column: wc_actions.
203 */
204 protected function render_wc_actions_column() {
205 $this->orders_list_table->render_wc_actions_column( $this->object );
206 }
207
208 /**
209 * Render column: billing_address.
210 */
211 protected function render_billing_address_column() {
212 $this->orders_list_table->render_billing_address_column( $this->object );
213 }
214
215 /**
216 * Render column: shipping_address.
217 */
218 protected function render_shipping_address_column() {
219 $this->orders_list_table->render_shipping_address_column( $this->object );
220 }
221
222 /**
223 * Template for order preview.
224 *
225 * @since 3.3.0
226 */
227 public function order_preview_template() {
228 echo $this->orders_list_table->get_order_preview_template();
229 }
230
231 /**
232 * Get items to display in the preview as HTML.
233 *
234 * @param WC_Order $order Order object.
235 * @return string
236 */
237 public static function get_order_preview_item_html( $order ) {
238 $hidden_order_itemmeta = apply_filters(
239 'woocommerce_hidden_order_itemmeta',
240 array(
241 '_qty',
242 '_tax_class',
243 '_product_id',
244 '_variation_id',
245 '_line_subtotal',
246 '_line_subtotal_tax',
247 '_line_total',
248 '_line_tax',
249 'method_id',
250 'cost',
251 '_reduced_stock',
252 '_restock_refunded_items',
253 )
254 );
255
256 $line_items = apply_filters( 'woocommerce_admin_order_preview_line_items', $order->get_items(), $order );
257 $columns = apply_filters(
258 'woocommerce_admin_order_preview_line_item_columns',
259 array(
260 'product' => __( 'Product', 'woocommerce' ),
261 'quantity' => __( 'Quantity', 'woocommerce' ),
262 'tax' => __( 'Tax', 'woocommerce' ),
263 'total' => __( 'Total', 'woocommerce' ),
264 ),
265 $order
266 );
267
268 if ( ! wc_tax_enabled() ) {
269 unset( $columns['tax'] );
270 }
271
272 $html = '
273 <div class="wc-order-preview-table-wrapper">
274 <table cellspacing="0" class="wc-order-preview-table">
275 <thead>
276 <tr>';
277
278 foreach ( $columns as $column => $label ) {
279 $html .= '<th class="wc-order-preview-table__column--' . esc_attr( $column ) . '">' . esc_html( $label ) . '</th>';
280 }
281
282 $html .= '
283 </tr>
284 </thead>
285 <tbody>';
286
287 $refunds = array();
288 foreach ( $order->get_refunds() as $refund ) {
289 foreach ( $refund->get_items() as $item ) {
290 $product_id = $item->get_product_id();
291 if ( array_key_exists( $product_id, $refunds ) ) {
292 $refunds[ $product_id ]['quantity'] += absint( $item->get_quantity() );
293 $refunds[ $product_id ]['total'] += abs( (float) $item->get_total() );
294 } else {
295 $refunds[ $product_id ] = array(
296 'quantity' => absint( $item->get_quantity() ),
297 'total' => abs( (float) $item->get_total() ),
298 );
299 }
300 }
301 }
302
303 $price_args = array( 'currency' => $order->get_currency() );
304 foreach ( $line_items as $item_id => $item ) {
305
306 $product_object = is_callable( array( $item, 'get_product' ) ) ? $item->get_product() : null;
307 $row_class = apply_filters( 'woocommerce_admin_html_order_preview_item_class', '', $item, $order );
308 $refund = $refunds[ $item->get_product_id() ] ?? null;
309
310 $html .= '<tr class="wc-order-preview-table__item wc-order-preview-table__item--' . esc_attr( $item_id ) . ( $row_class ? ' ' . esc_attr( $row_class ) : '' ) . '">';
311
312 foreach ( $columns as $column => $label ) {
313 $html .= '<td class="wc-order-preview-table__column--' . esc_attr( $column ) . '">';
314 switch ( $column ) {
315 case 'product':
316 $html .= wp_kses_post( $item->get_name() );
317
318 if ( $product_object ) {
319 $html .= '<div class="wc-order-item-sku">' . esc_html( $product_object->get_sku() ) . '</div>';
320 }
321
322 $meta_data = $item->get_all_formatted_meta_data( '' );
323
324 if ( $meta_data ) {
325 $html .= '<table cellspacing="0" class="wc-order-item-meta">';
326
327 foreach ( $meta_data as $meta_id => $meta ) {
328 if ( in_array( $meta->key, $hidden_order_itemmeta, true ) ) {
329 continue;
330 }
331 $html .= '<tr><th>' . wp_kses_post( $meta->display_key ) . ':</th><td>' . wp_kses_post( force_balance_tags( $meta->display_value ) ) . '</td></tr>';
332 }
333 $html .= '</table>';
334 }
335 break;
336 case 'quantity':
337 $html .= esc_html( $item->get_quantity() );
338 if ( $refund ) {
339 $html .= "<div><small class='refunded'>-" . $refund['quantity'] . '</small></div><br/>';
340 }
341 break;
342 case 'tax':
343 $html .= wc_price( $item->get_total_tax(), $price_args );
344 break;
345 case 'total':
346 $html .= wc_price( $item->get_total(), $price_args );
347 if ( $refund ) {
348 $html .= "<div><small class='refunded'>-" . wc_price( $refund['total'], $price_args ) . '</small></div><br/>';
349 }
350 break;
351 default:
352 $html .= apply_filters( 'woocommerce_admin_order_preview_line_item_column_' . sanitize_key( $column ), '', $item, $item_id, $order );
353 break;
354 }
355 $html .= '</td>';
356 }
357
358 $html .= '</tr>';
359 }
360
361 $html .= '
362 </tbody>
363 </table>
364 </div>';
365
366 return $html;
367 }
368
369 /**
370 * Get actions to display in the preview as HTML.
371 *
372 * @param WC_Order $order Order object.
373 * @return string
374 */
375 public static function get_order_preview_actions_html( $order ) {
376 $actions = array();
377 $status_actions = array();
378
379 $wp_post_type = get_post_type_object( $order->get_type() ) ?? get_post_type_object( 'shop_order' );
380 if ( ! current_user_can( $wp_post_type->cap->edit_post, $order->get_id() ) ) {
381 return '';
382 }
383
384 if ( $order->has_status( array( OrderStatus::PENDING ) ) ) {
385 $status_actions['on-hold'] = array(
386 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_mark_order_status&status=on-hold&order_id=' . $order->get_id() ), 'woocommerce-mark-order-status' ),
387 'name' => __( 'On-hold', 'woocommerce' ),
388 'title' => __( 'Change order status to on-hold', 'woocommerce' ),
389 'action' => 'on-hold',
390 );
391 }
392
393 if ( $order->has_status( array( OrderStatus::PENDING, OrderStatus::ON_HOLD ) ) ) {
394 $status_actions['processing'] = array(
395 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_mark_order_status&status=processing&order_id=' . $order->get_id() ), 'woocommerce-mark-order-status' ),
396 'name' => __( 'Processing', 'woocommerce' ),
397 'title' => __( 'Change order status to processing', 'woocommerce' ),
398 'action' => 'processing',
399 );
400 }
401
402 if ( $order->has_status( array( OrderStatus::PENDING, OrderStatus::ON_HOLD, OrderStatus::PROCESSING ) ) ) {
403 $status_actions['complete'] = array(
404 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_mark_order_status&status=completed&order_id=' . $order->get_id() ), 'woocommerce-mark-order-status' ),
405 'name' => __( 'Completed', 'woocommerce' ),
406 'title' => __( 'Change order status to completed', 'woocommerce' ),
407 'action' => 'complete',
408 );
409 }
410
411 if ( $status_actions ) {
412 $actions['status'] = array(
413 'group' => __( 'Change status: ', 'woocommerce' ),
414 'actions' => $status_actions,
415 );
416 }
417
418 return wc_render_action_buttons( apply_filters( 'woocommerce_admin_order_preview_actions', $actions, $order ) );
419 }
420
421 /**
422 * Get order details to send to the ajax endpoint for previews.
423 *
424 * @param WC_Order $order Order object.
425 * @return array
426 */
427 public static function order_preview_get_order_details( $order ) {
428 if ( ! $order ) {
429 return array();
430 }
431
432 $payment_via = $order->get_payment_method_title();
433 $payment_method = $order->get_payment_method();
434 $payment_gateways = WC()->payment_gateways() ? WC()->payment_gateways->payment_gateways() : array();
435 $transaction_id = $order->get_transaction_id();
436
437 if ( $transaction_id ) {
438
439 $url = isset( $payment_gateways[ $payment_method ] ) ? $payment_gateways[ $payment_method ]->get_transaction_url( $order ) : false;
440
441 if ( $url ) {
442 $payment_via .= ' (<a href="' . esc_url( $url ) . '" target="_blank">' . esc_html( $transaction_id ) . '</a>)';
443 } else {
444 $payment_via .= ' (' . esc_html( $transaction_id ) . ')';
445 }
446 }
447
448 $billing_address = $order->get_formatted_billing_address();
449 $shipping_address = $order->get_formatted_shipping_address();
450
451 $wp_post_type = get_post_type_object( $order->get_type() ) ?? get_post_type_object( 'shop_order' );
452 $is_editable = current_user_can( $wp_post_type->cap->edit_post, $order->get_id() );
453
454 // phpcs:disable WooCommerce.Commenting.CommentHooks.MissingSinceComment
455 /**
456 * Filter to customize the order details data that the woocommerce_get_order_details action will send.
457 *
458 * @param array $order_details Order details.
459 */
460 $order_details = apply_filters(
461 'woocommerce_admin_order_preview_get_order_details',
462 array(
463 'data' => $order->get_data(),
464 'is_editable' => $is_editable,
465 'order_number' => $order->get_order_number(),
466 'item_html' => self::get_order_preview_item_html( $order ),
467 'actions_html' => self::get_order_preview_actions_html( $order ),
468 'ship_to_billing' => wc_ship_to_billing_address_only(),
469 'needs_shipping' => $order->needs_shipping_address(),
470 'formatted_billing_address' => $billing_address ? $billing_address : __( 'N/A', 'woocommerce' ),
471 'formatted_shipping_address' => $shipping_address ? $shipping_address : __( 'N/A', 'woocommerce' ),
472 'shipping_address_map_url' => $order->get_shipping_address_map_url(),
473 'payment_via' => $payment_via,
474 'shipping_via' => $order->get_shipping_method(),
475 'status' => $order->get_status(),
476 'status_name' => wc_get_order_status_name( $order->get_status() ),
477 ),
478 $order
479 );
480 // phpcs:enable WooCommerce.Commenting.CommentHooks.MissingSinceComment
481
482 $order_details['data'] = array_intersect_key( $order_details['data'], array_flip( array( 'id', 'billing', 'shipping', 'customer_note' ) ) );
483
484 return $order_details;
485 }
486
487 /**
488 * Handle bulk actions.
489 *
490 * @param string $redirect_to URL to redirect to.
491 * @param string $action Action name.
492 * @param array $ids List of ids.
493 * @return string
494 */
495 public function handle_bulk_actions( $redirect_to, $action, $ids ) {
496 $ids = apply_filters( 'woocommerce_bulk_action_ids', array_reverse( array_map( 'absint', $ids ) ), $action, 'order' );
497 $changed = 0;
498
499 if ( 'remove_personal_data' === $action ) {
500 $report_action = 'removed_personal_data';
501
502 foreach ( $ids as $id ) {
503 $order = wc_get_order( $id );
504
505 if ( $order ) {
506 do_action( 'woocommerce_remove_order_personal_data', $order );
507 ++$changed;
508 }
509 }
510 } elseif ( false !== strpos( $action, 'mark_' ) ) {
511 $order_statuses = wc_get_order_statuses();
512 $new_status = substr( $action, 5 ); // Get the status name from action.
513 $report_action = 'marked_' . $new_status;
514
515 // Sanity check: bail out if this is actually not a status, or is not a registered status.
516 if ( isset( $order_statuses[ 'wc-' . $new_status ] ) ) {
517 // Initialize payment gateways in case order has hooked status transition actions.
518 WC()->payment_gateways();
519
520 foreach ( $ids as $id ) {
521 $order = wc_get_order( $id );
522 $order->update_status( $new_status, __( 'Order status changed by bulk edit:', 'woocommerce' ), true );
523 do_action( 'woocommerce_order_edit_status', $id, $new_status );
524 ++$changed;
525 }
526 }
527 }
528
529 if ( $changed ) {
530 $redirect_to = add_query_arg(
531 array(
532 'post_type' => $this->list_table_type,
533 'bulk_action' => $report_action,
534 'changed' => $changed,
535 'ids' => join( ',', $ids ),
536 ),
537 $redirect_to
538 );
539 }
540
541 return esc_url_raw( $redirect_to );
542 }
543
544 /**
545 * Show confirmation message that order status changed for number of orders.
546 */
547 public function bulk_admin_notices() {
548 global $post_type, $pagenow;
549
550 // Bail out if not on shop order list page.
551 if ( 'edit.php' !== $pagenow || 'shop_order' !== $post_type || ! isset( $_REQUEST['bulk_action'] ) ) { // WPCS: input var ok, CSRF ok.
552 return;
553 }
554
555 $order_statuses = wc_get_order_statuses();
556 $number = isset( $_REQUEST['changed'] ) ? absint( $_REQUEST['changed'] ) : 0; // WPCS: input var ok, CSRF ok.
557 $bulk_action = wc_clean( wp_unslash( $_REQUEST['bulk_action'] ) ); // WPCS: input var ok, CSRF ok.
558
559 // Check if any status changes happened.
560 foreach ( $order_statuses as $slug => $name ) {
561 if ( 'marked_' . str_replace( 'wc-', '', $slug ) === $bulk_action ) { // WPCS: input var ok, CSRF ok.
562 /* translators: %d: orders count */
563 $message = sprintf( _n( '%s order status changed.', '%s order statuses changed.', $number, 'woocommerce' ), number_format_i18n( $number ) );
564 echo '<div class="updated"><p>' . esc_html( $message ) . '</p></div>';
565 break;
566 }
567 }
568
569 if ( 'removed_personal_data' === $bulk_action ) { // WPCS: input var ok, CSRF ok.
570 /* translators: %d: orders count */
571 $message = sprintf( _n( 'Removed personal data from %s order.', 'Removed personal data from %s orders.', $number, 'woocommerce' ), number_format_i18n( $number ) );
572 echo '<div class="updated"><p>' . esc_html( $message ) . '</p></div>';
573 }
574 }
575
576 /**
577 * See if we should render search filters or not.
578 */
579 public function restrict_manage_posts() {
580 global $typenow;
581
582 if ( in_array( $typenow, wc_get_order_types( 'order-meta-boxes' ), true ) ) {
583 $this->render_filters();
584 }
585 }
586
587 /**
588 * Render any custom filters and search inputs for the list table.
589 */
590 protected function render_filters() {
591 $this->orders_list_table->created_via_filter();
592 $this->orders_list_table->customers_filter();
593 }
594
595 /**
596 * Handle any filters.
597 *
598 * @param array $query_vars Query vars.
599 * @return array
600 */
601 public function request_query( $query_vars ) {
602 global $typenow;
603
604 if ( in_array( $typenow, wc_get_order_types( 'order-meta-boxes' ), true ) ) {
605 return $this->query_filters( $query_vars );
606 }
607
608 return $query_vars;
609 }
610
611 /**
612 * Handle any custom filters.
613 *
614 * @param array $query_vars Query vars.
615 * @return array
616 */
617 protected function query_filters( $query_vars ) {
618 global $wp_post_statuses;
619
620 // Filter the orders by the posted customer.
621 if ( ! empty( $_GET['_customer_user'] ) ) { // WPCS: input var ok.
622 // @codingStandardsIgnoreStart.
623 $query_vars['meta_query'] = array(
624 array(
625 'key' => '_customer_user',
626 'value' => (int) $_GET['_customer_user'], // WPCS: input var ok, sanitization ok.
627 'compare' => '=',
628 ),
629 );
630 // @codingStandardsIgnoreEnd
631 }
632
633 // Filter the orders by created via.
634 if ( ! empty( $_GET['_created_via'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
635 // @codingStandardsIgnoreStart
636 $created_via = explode(',', sanitize_text_field( wp_unslash( $_GET['_created_via'] ) ) );
637
638 $query_vars['meta_query'] = array(
639 array(
640 'key' => '_created_via',
641 'value' => $created_via,
642 'compare' => 'IN',
643 ),
644 );
645 // @codingStandardsIgnoreEnd
646 }
647
648 // Sorting.
649 if ( isset( $query_vars['orderby'] ) ) {
650 if ( 'order_total' === $query_vars['orderby'] ) {
651 // @codingStandardsIgnoreStart
652 $query_vars = array_merge( $query_vars, array(
653 'meta_key' => '_order_total',
654 'orderby' => 'meta_value_num',
655 ) );
656 // @codingStandardsIgnoreEnd
657 }
658 }
659
660 // Status.
661 if ( empty( $query_vars['post_status'] ) ) {
662 $post_statuses = wc_get_order_statuses();
663
664 foreach ( $post_statuses as $status => $value ) {
665 if ( isset( $wp_post_statuses[ $status ] ) && false === $wp_post_statuses[ $status ]->show_in_admin_all_list ) {
666 unset( $post_statuses[ $status ] );
667 }
668 }
669
670 $query_vars['post_status'] = array_keys( $post_statuses );
671 }
672
673 return $query_vars;
674 }
675
676 /**
677 * Change the label when searching orders.
678 *
679 * @param mixed $query Current search query.
680 * @return string
681 */
682 public function search_label( $query ) {
683 global $pagenow, $typenow;
684
685 if ( 'edit.php' !== $pagenow || 'shop_order' !== $typenow || ! get_query_var( 'shop_order_search' ) || ! isset( $_GET['s'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
686 return $query;
687 }
688
689 return wc_clean( wp_unslash( $_GET['s'] ) ); // WPCS: input var ok, sanitization ok.
690 }
691
692 /**
693 * Query vars for custom searches.
694 *
695 * @param mixed $public_query_vars Array of query vars.
696 * @return array
697 */
698 public function add_custom_query_var( $public_query_vars ) {
699 $public_query_vars[] = 'shop_order_search';
700 return $public_query_vars;
701 }
702
703 /**
704 * Search custom fields as well as content.
705 *
706 * @param WP_Query $wp Query object.
707 */
708 public function search_custom_fields( $wp ) {
709 global $pagenow;
710
711 if ( 'edit.php' !== $pagenow || ! isset( $wp->query_vars['post_type'] ) || 'shop_order' !== $wp->query_vars['post_type'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
712 return;
713 }
714
715 $post_ids = isset( $_GET['s'] ) && ! empty( $wp->query_vars['s'] ) ? wc_order_search( wc_clean( wp_unslash( $_GET['s'] ) ) ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
716
717 if ( ! empty( $post_ids ) ) {
718 // Remove "s" - we don't want to search order name.
719 unset( $wp->query_vars['s'] );
720
721 // so we know we're doing this.
722 $wp->query_vars['shop_order_search'] = true;
723
724 // Search by found posts.
725 $wp->query_vars['post__in'] = array_merge( $post_ids, array( 0 ) );
726 }
727
728 if ( isset( $_GET['order_date_type'] ) && isset( $_GET['m'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
729 $date_type = wc_clean( wp_unslash( $_GET['order_date_type'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
730 $date_query = wc_clean( wp_unslash( $_GET['m'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
731 // date_paid and date_completed are stored in postmeta, so we need to do a meta query.
732 if ( 'date_paid' === $date_type || 'date_completed' === $date_type ) {
733 $date_start = \DateTime::createFromFormat( 'Ymd H:i:s', "$date_query 00:00:00" );
734 $date_end = \DateTime::createFromFormat( 'Ymd H:i:s', "$date_query 23:59:59" );
735
736 unset( $wp->query_vars['m'] );
737
738 if ( $date_start && $date_end ) {
739 $wp->query_vars['meta_key'] = "_$date_type"; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
740 $wp->query_vars['meta_value'] = array( strval( $date_start->getTimestamp() ), strval( $date_end->getTimestamp() ) ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
741 $wp->query_vars['meta_compare'] = 'BETWEEN';
742 }
743 }
744 }
745 }
746 }
747