PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 11.1.0
WooCommerce v11.1.0
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 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 1 month ago class-wc-admin-list-table-coupons.php 1 month ago class-wc-admin-list-table-orders.php 1 month ago class-wc-admin-list-table-products.php 1 week ago
class-wc-admin-list-table-orders.php
732 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 use Automattic\WooCommerce\Internal\Utilities\OrderItemMetaUtil;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 if ( class_exists( 'WC_Admin_List_Table_Orders', false ) ) {
18 return;
19 }
20
21 if ( ! class_exists( 'WC_Admin_List_Table', false ) ) {
22 include_once __DIR__ . '/abstract-class-wc-admin-list-table.php';
23 }
24
25 /**
26 * WC_Admin_List_Table_Orders Class.
27 */
28 class WC_Admin_List_Table_Orders extends WC_Admin_List_Table {
29
30 /**
31 * Post type.
32 *
33 * @var string
34 */
35 protected $list_table_type = 'shop_order';
36
37 /**
38 * The data store-agnostic list table implementation (introduced to support custom order tables),
39 * which we use here to render columns.
40 *
41 * @var ListTable $orders_list_table
42 */
43 private $orders_list_table;
44
45 /**
46 * Constructor.
47 */
48 public function __construct() {
49 parent::__construct();
50 $this->orders_list_table = wc_get_container()->get( ListTable::class );
51 add_action( 'admin_notices', array( $this, 'bulk_admin_notices' ) );
52 add_action( 'admin_footer', array( $this, 'order_preview_template' ) );
53 add_filter( 'get_search_query', array( $this, 'search_label' ) );
54 add_filter( 'query_vars', array( $this, 'add_custom_query_var' ) );
55 add_action( 'parse_query', array( $this, 'search_custom_fields' ) );
56 }
57
58 /**
59 * Render blank state.
60 */
61 protected function render_blank_state() {
62 $this->orders_list_table->render_blank_state();
63 }
64
65 /**
66 * Define primary column.
67 *
68 * @return string
69 */
70 protected function get_primary_column() {
71 return 'order_number';
72 }
73
74 /**
75 * Get row actions to show in the list table.
76 *
77 * @param array $actions Array of actions.
78 * @param WP_Post $post Current post object.
79 * @return array
80 */
81 protected function get_row_actions( $actions, $post ) {
82 return array();
83 }
84
85 /**
86 * Define hidden columns.
87 *
88 * @return array
89 */
90 protected function define_hidden_columns() {
91 return array(
92 'shipping_address',
93 'billing_address',
94 'wc_actions',
95 );
96 }
97
98 /**
99 * Define which columns are sortable.
100 *
101 * @param array $columns Existing columns.
102 * @return array
103 */
104 public function define_sortable_columns( $columns ) {
105 $custom = array(
106 'order_number' => 'ID',
107 'order_total' => 'order_total',
108 'order_date' => 'date',
109 );
110 unset( $columns['comments'] );
111
112 return wp_parse_args( $custom, $columns );
113 }
114
115 /**
116 * Define which columns to show on this screen.
117 *
118 * @param array $columns Existing columns.
119 * @return array
120 */
121 public function define_columns( $columns ) {
122 $show_columns = array();
123 $show_columns['cb'] = $columns['cb'];
124 $show_columns['order_number'] = __( 'Order', 'woocommerce' );
125 $show_columns['order_date'] = __( 'Date', 'woocommerce' );
126 $show_columns['order_status'] = __( 'Status', 'woocommerce' );
127 $show_columns['billing_address'] = __( 'Billing', 'woocommerce' );
128 $show_columns['shipping_address'] = __( 'Ship to', 'woocommerce' );
129 $show_columns['order_total'] = __( 'Total', 'woocommerce' );
130 $show_columns['wc_actions'] = __( 'Actions', 'woocommerce' );
131
132 wp_enqueue_script( 'wc-orders' );
133
134 return $show_columns;
135 }
136
137 /**
138 * Define bulk actions.
139 *
140 * @param array $actions Existing actions.
141 * @return array
142 */
143 public function define_bulk_actions( $actions ) {
144 if ( isset( $actions['edit'] ) ) {
145 unset( $actions['edit'] );
146 }
147
148 $actions['mark_processing'] = __( 'Change status to processing', 'woocommerce' );
149 $actions['mark_on-hold'] = __( 'Change status to on-hold', 'woocommerce' );
150 $actions['mark_completed'] = __( 'Change status to completed', 'woocommerce' );
151 $actions['mark_cancelled'] = __( 'Change status to cancelled', 'woocommerce' );
152
153 if ( wc_string_to_bool( get_option( 'woocommerce_allow_bulk_remove_personal_data', 'no' ) ) ) {
154 $actions['remove_personal_data'] = __( 'Remove personal data', 'woocommerce' );
155 }
156
157 return $actions;
158 }
159
160 /**
161 * Pre-fetch any data for the row each column has access to it. the_order global is there for bw compat.
162 *
163 * @param int $post_id Post ID being shown.
164 */
165 protected function prepare_row_data( $post_id ) {
166 global $the_order;
167
168 if ( empty( $this->object ) || $this->object->get_id() !== $post_id ) {
169 $this->object = wc_get_order( $post_id );
170 $the_order = $this->object;
171 }
172 }
173
174 /**
175 * Render column: order_number.
176 */
177 protected function render_order_number_column() {
178 $this->orders_list_table->render_order_number_column( $this->object );
179 }
180
181 /**
182 * Render column: order_status.
183 */
184 protected function render_order_status_column() {
185 $this->orders_list_table->render_order_status_column( $this->object );
186 }
187
188 /**
189 * Render column: order_date.
190 */
191 protected function render_order_date_column() {
192 $this->orders_list_table->render_order_date_column( $this->object );
193 }
194
195 /**
196 * Render column: order_total.
197 */
198 protected function render_order_total_column() {
199 $this->orders_list_table->render_order_total_column( $this->object );
200 }
201
202 /**
203 * Render column: wc_actions.
204 */
205 protected function render_wc_actions_column() {
206 $this->orders_list_table->render_wc_actions_column( $this->object );
207 }
208
209 /**
210 * Render column: billing_address.
211 */
212 protected function render_billing_address_column() {
213 $this->orders_list_table->render_billing_address_column( $this->object );
214 }
215
216 /**
217 * Render column: shipping_address.
218 */
219 protected function render_shipping_address_column() {
220 $this->orders_list_table->render_shipping_address_column( $this->object );
221 }
222
223 /**
224 * Template for order preview.
225 *
226 * @since 3.3.0
227 */
228 public function order_preview_template() {
229 echo $this->orders_list_table->get_order_preview_template();
230 }
231
232 /**
233 * Get items to display in the preview as HTML.
234 *
235 * @param WC_Order $order Order object.
236 * @return string
237 */
238 public static function get_order_preview_item_html( $order ) {
239 $hidden_order_itemmeta = OrderItemMetaUtil::get_hidden_keys();
240
241 $line_items = apply_filters( 'woocommerce_admin_order_preview_line_items', $order->get_items(), $order );
242 $columns = apply_filters(
243 'woocommerce_admin_order_preview_line_item_columns',
244 array(
245 'product' => __( 'Product', 'woocommerce' ),
246 'quantity' => __( 'Quantity', 'woocommerce' ),
247 'tax' => __( 'Tax', 'woocommerce' ),
248 'total' => __( 'Total', 'woocommerce' ),
249 ),
250 $order
251 );
252
253 if ( ! wc_tax_enabled() ) {
254 unset( $columns['tax'] );
255 }
256
257 $html = '
258 <div class="wc-order-preview-table-wrapper">
259 <table cellspacing="0" class="wc-order-preview-table">
260 <thead>
261 <tr>';
262
263 foreach ( $columns as $column => $label ) {
264 $html .= '<th class="wc-order-preview-table__column--' . esc_attr( $column ) . '">' . esc_html( $label ) . '</th>';
265 }
266
267 $html .= '
268 </tr>
269 </thead>
270 <tbody>';
271
272 $refunds = array();
273 foreach ( $order->get_refunds() as $refund ) {
274 foreach ( $refund->get_items() as $item ) {
275 $product_id = $item->get_product_id();
276 if ( array_key_exists( $product_id, $refunds ) ) {
277 $refunds[ $product_id ]['quantity'] += absint( $item->get_quantity() );
278 $refunds[ $product_id ]['total'] += abs( (float) $item->get_total() );
279 } else {
280 $refunds[ $product_id ] = array(
281 'quantity' => absint( $item->get_quantity() ),
282 'total' => abs( (float) $item->get_total() ),
283 );
284 }
285 }
286 }
287
288 $price_args = array( 'currency' => $order->get_currency() );
289 foreach ( $line_items as $item_id => $item ) {
290
291 $product_object = is_callable( array( $item, 'get_product' ) ) ? $item->get_product() : null;
292 $row_class = apply_filters( 'woocommerce_admin_html_order_preview_item_class', '', $item, $order );
293 $refund = $refunds[ $item->get_product_id() ] ?? null;
294
295 $html .= '<tr class="wc-order-preview-table__item wc-order-preview-table__item--' . esc_attr( $item_id ) . ( $row_class ? ' ' . esc_attr( $row_class ) : '' ) . '">';
296
297 foreach ( $columns as $column => $label ) {
298 $html .= '<td class="wc-order-preview-table__column--' . esc_attr( $column ) . '">';
299 switch ( $column ) {
300 case 'product':
301 $html .= wp_kses_post( $item->get_name() );
302
303 if ( $product_object ) {
304 $html .= '<div class="wc-order-item-sku">' . esc_html( $product_object->get_sku() ) . '</div>';
305 }
306
307 $meta_data = $item->get_all_formatted_meta_data( '' );
308
309 if ( $meta_data ) {
310 $html .= '<table cellspacing="0" class="wc-order-item-meta">';
311
312 foreach ( $meta_data as $meta_id => $meta ) {
313 if ( in_array( $meta->key, $hidden_order_itemmeta, true ) ) {
314 continue;
315 }
316 $html .= '<tr><th>' . wp_kses_post( $meta->display_key ) . ':</th><td>' . wp_kses_post( force_balance_tags( $meta->display_value ) ) . '</td></tr>';
317 }
318 $html .= '</table>';
319 }
320 break;
321 case 'quantity':
322 $html .= esc_html( $item->get_quantity() );
323 if ( $refund ) {
324 $html .= "<div><small class='refunded'>-" . $refund['quantity'] . '</small></div><br/>';
325 }
326 break;
327 case 'tax':
328 $html .= wc_price( $item->get_total_tax(), $price_args );
329 break;
330 case 'total':
331 $html .= wc_price( $item->get_total(), $price_args );
332 if ( $refund ) {
333 $html .= "<div><small class='refunded'>-" . wc_price( $refund['total'], $price_args ) . '</small></div><br/>';
334 }
335 break;
336 default:
337 $html .= apply_filters( 'woocommerce_admin_order_preview_line_item_column_' . sanitize_key( $column ), '', $item, $item_id, $order );
338 break;
339 }
340 $html .= '</td>';
341 }
342
343 $html .= '</tr>';
344 }
345
346 $html .= '
347 </tbody>
348 </table>
349 </div>';
350
351 return $html;
352 }
353
354 /**
355 * Get actions to display in the preview as HTML.
356 *
357 * @param WC_Order $order Order object.
358 * @return string
359 */
360 public static function get_order_preview_actions_html( $order ) {
361 $actions = array();
362 $status_actions = array();
363
364 $wp_post_type = get_post_type_object( $order->get_type() ) ?? get_post_type_object( 'shop_order' );
365 if ( ! current_user_can( $wp_post_type->cap->edit_post, $order->get_id() ) ) {
366 return '';
367 }
368
369 if ( $order->has_status( array( OrderStatus::PENDING ) ) ) {
370 $status_actions['on-hold'] = array(
371 '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' ),
372 'name' => __( 'On-hold', 'woocommerce' ),
373 'title' => __( 'Change order status to on-hold', 'woocommerce' ),
374 'action' => 'on-hold',
375 );
376 }
377
378 if ( $order->has_status( array( OrderStatus::PENDING, OrderStatus::ON_HOLD ) ) ) {
379 $status_actions['processing'] = array(
380 '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' ),
381 'name' => __( 'Processing', 'woocommerce' ),
382 'title' => __( 'Change order status to processing', 'woocommerce' ),
383 'action' => 'processing',
384 );
385 }
386
387 if ( $order->has_status( array( OrderStatus::PENDING, OrderStatus::ON_HOLD, OrderStatus::PROCESSING ) ) ) {
388 $status_actions['complete'] = array(
389 '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' ),
390 'name' => __( 'Completed', 'woocommerce' ),
391 'title' => __( 'Change order status to completed', 'woocommerce' ),
392 'action' => 'complete',
393 );
394 }
395
396 if ( $status_actions ) {
397 $actions['status'] = array(
398 'group' => __( 'Change status: ', 'woocommerce' ),
399 'actions' => $status_actions,
400 );
401 }
402
403 return wc_render_action_buttons( apply_filters( 'woocommerce_admin_order_preview_actions', $actions, $order ) );
404 }
405
406 /**
407 * Get order details to send to the ajax endpoint for previews.
408 *
409 * @param WC_Order $order Order object.
410 * @return array
411 */
412 public static function order_preview_get_order_details( $order ) {
413 if ( ! $order ) {
414 return array();
415 }
416
417 $payment_via = $order->get_payment_method_title();
418 $payment_method = $order->get_payment_method();
419 $payment_gateways = WC()->payment_gateways() ? WC()->payment_gateways->payment_gateways() : array();
420 $transaction_id = $order->get_transaction_id();
421
422 if ( $transaction_id ) {
423
424 $url = isset( $payment_gateways[ $payment_method ] ) ? $payment_gateways[ $payment_method ]->get_transaction_url( $order ) : false;
425
426 if ( $url ) {
427 $payment_via .= ' (<a href="' . esc_url( $url ) . '" target="_blank">' . esc_html( $transaction_id ) . '</a>)';
428 } else {
429 $payment_via .= ' (' . esc_html( $transaction_id ) . ')';
430 }
431 }
432
433 $billing_address = $order->get_formatted_billing_address();
434 $shipping_address = $order->get_formatted_shipping_address();
435
436 $wp_post_type = get_post_type_object( $order->get_type() ) ?? get_post_type_object( 'shop_order' );
437 $is_editable = current_user_can( $wp_post_type->cap->edit_post, $order->get_id() );
438
439 // phpcs:disable WooCommerce.Commenting.CommentHooks.MissingSinceComment
440 /**
441 * Filter to customize the order details data that the woocommerce_get_order_details action will send.
442 *
443 * @param array $order_details Order details.
444 */
445 $order_details = apply_filters(
446 'woocommerce_admin_order_preview_get_order_details',
447 array(
448 'data' => $order->get_data(),
449 'is_editable' => $is_editable,
450 'order_number' => $order->get_order_number(),
451 'item_html' => self::get_order_preview_item_html( $order ),
452 'actions_html' => self::get_order_preview_actions_html( $order ),
453 'ship_to_billing' => wc_ship_to_billing_address_only(),
454 'needs_shipping' => $order->needs_shipping_address(),
455 'formatted_billing_address' => $billing_address ? $billing_address : __( 'N/A', 'woocommerce' ),
456 'formatted_shipping_address' => $shipping_address ? $shipping_address : __( 'N/A', 'woocommerce' ),
457 'shipping_address_map_url' => $order->get_shipping_address_map_url(),
458 'payment_via' => $payment_via,
459 'shipping_via' => $order->get_shipping_method(),
460 'status' => $order->get_status(),
461 'status_name' => wc_get_order_status_name( $order->get_status() ),
462 ),
463 $order
464 );
465 // phpcs:enable WooCommerce.Commenting.CommentHooks.MissingSinceComment
466
467 $order_details['data'] = array_intersect_key( $order_details['data'], array_flip( array( 'id', 'billing', 'shipping', 'customer_note' ) ) );
468
469 return $order_details;
470 }
471
472 /**
473 * Handle bulk actions.
474 *
475 * @param string $redirect_to URL to redirect to.
476 * @param string $action Action name.
477 * @param array $ids List of ids.
478 * @return string
479 */
480 public function handle_bulk_actions( $redirect_to, $action, $ids ) {
481 $ids = apply_filters( 'woocommerce_bulk_action_ids', array_reverse( array_map( 'absint', $ids ) ), $action, 'order' );
482 $changed = 0;
483
484 if ( 'remove_personal_data' === $action ) {
485 $report_action = 'removed_personal_data';
486
487 foreach ( $ids as $id ) {
488 $order = wc_get_order( $id );
489
490 if ( $order ) {
491 do_action( 'woocommerce_remove_order_personal_data', $order );
492 ++$changed;
493 }
494 }
495 } elseif ( false !== strpos( $action, 'mark_' ) ) {
496 $order_statuses = wc_get_order_statuses();
497 $new_status = substr( $action, 5 ); // Get the status name from action.
498 $report_action = 'marked_' . $new_status;
499
500 // Sanity check: bail out if this is actually not a status, or is not a registered status.
501 if ( isset( $order_statuses[ 'wc-' . $new_status ] ) ) {
502 // Initialize payment gateways in case order has hooked status transition actions.
503 WC()->payment_gateways();
504
505 foreach ( $ids as $id ) {
506 $order = wc_get_order( $id );
507 $order->update_status( $new_status, __( 'Order status changed by bulk edit:', 'woocommerce' ), true );
508 do_action( 'woocommerce_order_edit_status', $id, $new_status );
509 ++$changed;
510 }
511 }
512 }
513
514 if ( $changed ) {
515 $redirect_to = add_query_arg(
516 array(
517 'post_type' => $this->list_table_type,
518 'bulk_action' => $report_action,
519 'changed' => $changed,
520 'ids' => join( ',', $ids ),
521 ),
522 $redirect_to
523 );
524 }
525
526 return esc_url_raw( $redirect_to );
527 }
528
529 /**
530 * Show confirmation message that order status changed for number of orders.
531 */
532 public function bulk_admin_notices() {
533 global $post_type, $pagenow;
534
535 // Bail out if not on shop order list page.
536 if ( 'edit.php' !== $pagenow || 'shop_order' !== $post_type || ! isset( $_REQUEST['bulk_action'] ) ) { // WPCS: input var ok, CSRF ok.
537 return;
538 }
539
540 $order_statuses = wc_get_order_statuses();
541 $number = isset( $_REQUEST['changed'] ) ? absint( $_REQUEST['changed'] ) : 0; // WPCS: input var ok, CSRF ok.
542 $bulk_action = wc_clean( wp_unslash( $_REQUEST['bulk_action'] ) ); // WPCS: input var ok, CSRF ok.
543
544 // Check if any status changes happened.
545 foreach ( $order_statuses as $slug => $name ) {
546 if ( 'marked_' . str_replace( 'wc-', '', $slug ) === $bulk_action ) { // WPCS: input var ok, CSRF ok.
547 /* translators: %d: orders count */
548 $message = sprintf( _n( '%s order status changed.', '%s order statuses changed.', $number, 'woocommerce' ), number_format_i18n( $number ) );
549 echo '<div class="updated"><p>' . esc_html( $message ) . '</p></div>';
550 break;
551 }
552 }
553
554 if ( 'removed_personal_data' === $bulk_action ) { // WPCS: input var ok, CSRF ok.
555 /* translators: %d: orders count */
556 $message = sprintf( _n( 'Removed personal data from %s order.', 'Removed personal data from %s orders.', $number, 'woocommerce' ), number_format_i18n( $number ) );
557 echo '<div class="updated"><p>' . esc_html( $message ) . '</p></div>';
558 }
559 }
560
561 /**
562 * See if we should render search filters or not.
563 */
564 public function restrict_manage_posts() {
565 global $typenow;
566
567 if ( in_array( $typenow, wc_get_order_types( 'order-meta-boxes' ), true ) ) {
568 $this->render_filters();
569 }
570 }
571
572 /**
573 * Render any custom filters and search inputs for the list table.
574 */
575 protected function render_filters() {
576 $this->orders_list_table->created_via_filter();
577 $this->orders_list_table->customers_filter();
578 }
579
580 /**
581 * Handle any filters.
582 *
583 * @param array $query_vars Query vars.
584 * @return array
585 */
586 public function request_query( $query_vars ) {
587 global $typenow;
588
589 if ( in_array( $typenow, wc_get_order_types( 'order-meta-boxes' ), true ) ) {
590 return $this->query_filters( $query_vars );
591 }
592
593 return $query_vars;
594 }
595
596 /**
597 * Handle any custom filters.
598 *
599 * @param array $query_vars Query vars.
600 * @return array
601 */
602 protected function query_filters( $query_vars ) {
603 global $wp_post_statuses;
604
605 // Filter the orders by the posted customer.
606 if ( ! empty( $_GET['_customer_user'] ) ) { // WPCS: input var ok.
607 // @codingStandardsIgnoreStart.
608 $query_vars['meta_query'] = array(
609 array(
610 'key' => '_customer_user',
611 'value' => (int) $_GET['_customer_user'], // WPCS: input var ok, sanitization ok.
612 'compare' => '=',
613 ),
614 );
615 // @codingStandardsIgnoreEnd
616 }
617
618 // Filter the orders by created via.
619 if ( ! empty( $_GET['_created_via'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
620 // @codingStandardsIgnoreStart
621 $created_via = explode(',', sanitize_text_field( wp_unslash( $_GET['_created_via'] ) ) );
622
623 $query_vars['meta_query'] = array(
624 array(
625 'key' => '_created_via',
626 'value' => $created_via,
627 'compare' => 'IN',
628 ),
629 );
630 // @codingStandardsIgnoreEnd
631 }
632
633 // Sorting.
634 if ( isset( $query_vars['orderby'] ) ) {
635 if ( 'order_total' === $query_vars['orderby'] ) {
636 // @codingStandardsIgnoreStart
637 $query_vars = array_merge( $query_vars, array(
638 'meta_key' => '_order_total',
639 'orderby' => 'meta_value_num',
640 ) );
641 // @codingStandardsIgnoreEnd
642 }
643 }
644
645 // Status.
646 if ( empty( $query_vars['post_status'] ) ) {
647 $post_statuses = wc_get_order_statuses();
648
649 foreach ( $post_statuses as $status => $value ) {
650 if ( isset( $wp_post_statuses[ $status ] ) && false === $wp_post_statuses[ $status ]->show_in_admin_all_list ) {
651 unset( $post_statuses[ $status ] );
652 }
653 }
654
655 $query_vars['post_status'] = array_keys( $post_statuses );
656 }
657
658 return $query_vars;
659 }
660
661 /**
662 * Change the label when searching orders.
663 *
664 * @param mixed $query Current search query.
665 * @return string
666 */
667 public function search_label( $query ) {
668 global $pagenow, $typenow;
669
670 if ( 'edit.php' !== $pagenow || 'shop_order' !== $typenow || ! get_query_var( 'shop_order_search' ) || ! isset( $_GET['s'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
671 return $query;
672 }
673
674 return wc_clean( wp_unslash( $_GET['s'] ) ); // WPCS: input var ok, sanitization ok.
675 }
676
677 /**
678 * Query vars for custom searches.
679 *
680 * @param mixed $public_query_vars Array of query vars.
681 * @return array
682 */
683 public function add_custom_query_var( $public_query_vars ) {
684 $public_query_vars[] = 'shop_order_search';
685 return $public_query_vars;
686 }
687
688 /**
689 * Search custom fields as well as content.
690 *
691 * @param WP_Query $wp Query object.
692 */
693 public function search_custom_fields( $wp ) {
694 global $pagenow;
695
696 if ( 'edit.php' !== $pagenow || ! isset( $wp->query_vars['post_type'] ) || 'shop_order' !== $wp->query_vars['post_type'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
697 return;
698 }
699
700 $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
701
702 if ( ! empty( $post_ids ) ) {
703 // Remove "s" - we don't want to search order name.
704 unset( $wp->query_vars['s'] );
705
706 // so we know we're doing this.
707 $wp->query_vars['shop_order_search'] = true;
708
709 // Search by found posts.
710 $wp->query_vars['post__in'] = array_merge( $post_ids, array( 0 ) );
711 }
712
713 if ( isset( $_GET['order_date_type'] ) && isset( $_GET['m'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
714 $date_type = wc_clean( wp_unslash( $_GET['order_date_type'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
715 $date_query = wc_clean( wp_unslash( $_GET['m'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
716 // date_paid and date_completed are stored in postmeta, so we need to do a meta query.
717 if ( 'date_paid' === $date_type || 'date_completed' === $date_type ) {
718 $date_start = \DateTime::createFromFormat( 'Ymd H:i:s', "$date_query 00:00:00" );
719 $date_end = \DateTime::createFromFormat( 'Ymd H:i:s', "$date_query 23:59:59" );
720
721 unset( $wp->query_vars['m'] );
722
723 if ( $date_start && $date_end ) {
724 $wp->query_vars['meta_key'] = "_$date_type"; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
725 $wp->query_vars['meta_value'] = array( strval( $date_start->getTimestamp() ), strval( $date_end->getTimestamp() ) ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
726 $wp->query_vars['meta_compare'] = 'BETWEEN';
727 }
728 }
729 }
730 }
731 }
732