Completed
Push — master ( 15aa29...17da96 )
by Claudio
18:39 queued 11s
created

list-tables/class-wc-admin-list-table-orders.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * List tables: orders.
4
 *
5
 * @package WooCommerce\admin
6
 * @version 3.3.0
7
 */
8
9
if ( ! defined( 'ABSPATH' ) ) {
10
	exit;
11
}
12
13
if ( class_exists( 'WC_Admin_List_Table_Orders', false ) ) {
14
	return;
15
}
16
17
if ( ! class_exists( 'WC_Admin_List_Table', false ) ) {
18
	include_once 'abstract-class-wc-admin-list-table.php';
19
}
20
21
/**
22
 * WC_Admin_List_Table_Orders Class.
23
 */
24
class WC_Admin_List_Table_Orders extends WC_Admin_List_Table {
25
26
	/**
27
	 * Post type.
28
	 *
29
	 * @var string
30
	 */
31
	protected $list_table_type = 'shop_order';
32
33
	/**
34
	 * Constructor.
35
	 */
36
	public function __construct() {
37
		parent::__construct();
38
		add_action( 'admin_notices', array( $this, 'bulk_admin_notices' ) );
39
		add_action( 'admin_footer', array( $this, 'order_preview_template' ) );
40
		add_filter( 'get_search_query', array( $this, 'search_label' ) );
41
		add_filter( 'query_vars', array( $this, 'add_custom_query_var' ) );
42
		add_action( 'parse_query', array( $this, 'search_custom_fields' ) );
43
	}
44
45
	/**
46
	 * Render blank state.
47
	 */
48
	protected function render_blank_state() {
49
		echo '<div class="woocommerce-BlankState">';
50
51
		echo '<h2 class="woocommerce-BlankState-message">' . esc_html__( 'When you receive a new order, it will appear here.', 'woocommerce' ) . '</h2>';
52
53
		echo '<div class="woocommerce-BlankState-buttons">';
54
		echo '<a class="woocommerce-BlankState-cta button-primary button" target="_blank" href="https://docs.woocommerce.com/document/managing-orders/?utm_source=blankslate&utm_medium=product&utm_content=ordersdoc&utm_campaign=woocommerceplugin">' . esc_html__( 'Learn more about orders', 'woocommerce' ) . '</a>';
55
		echo '</div>';
56
57
		do_action( 'wc_marketplace_suggestions_orders_empty_state' );
58
59
		echo '</div>';
60
	}
61
62
	/**
63
	 * Define primary column.
64
	 *
65
	 * @return string
66
	 */
67
	protected function get_primary_column() {
68
		return 'order_number';
69
	}
70
71
	/**
72
	 * Get row actions to show in the list table.
73
	 *
74
	 * @param array   $actions Array of actions.
75
	 * @param WP_Post $post Current post object.
76
	 * @return array
77
	 */
78
	protected function get_row_actions( $actions, $post ) {
79
		return array();
80
	}
81
82
	/**
83
	 * Define hidden columns.
84
	 *
85
	 * @return array
86
	 */
87
	protected function define_hidden_columns() {
88
		return array(
89
			'shipping_address',
90
			'billing_address',
91
			'wc_actions',
92
		);
93
	}
94
95
	/**
96
	 * Define which columns are sortable.
97
	 *
98
	 * @param array $columns Existing columns.
99
	 * @return array
100
	 */
101
	public function define_sortable_columns( $columns ) {
102
		$custom = array(
103
			'order_number' => 'ID',
104
			'order_total'  => 'order_total',
105
			'order_date'   => 'date',
106
		);
107
		unset( $columns['comments'] );
108
109
		return wp_parse_args( $custom, $columns );
110
	}
111
112
	/**
113
	 * Define which columns to show on this screen.
114
	 *
115
	 * @param array $columns Existing columns.
116
	 * @return array
117
	 */
118
	public function define_columns( $columns ) {
119
		$show_columns                     = array();
120
		$show_columns['cb']               = $columns['cb'];
121
		$show_columns['order_number']     = __( 'Order', 'woocommerce' );
122
		$show_columns['order_date']       = __( 'Date', 'woocommerce' );
123
		$show_columns['order_status']     = __( 'Status', 'woocommerce' );
124
		$show_columns['billing_address']  = __( 'Billing', 'woocommerce' );
125
		$show_columns['shipping_address'] = __( 'Ship to', 'woocommerce' );
126
		$show_columns['order_total']      = __( 'Total', 'woocommerce' );
127
		$show_columns['wc_actions']       = __( 'Actions', 'woocommerce' );
128
129
		wp_enqueue_script( 'wc-orders' );
130
131
		return $show_columns;
132
	}
133
134
	/**
135
	 * Define bulk actions.
136
	 *
137
	 * @param array $actions Existing actions.
138
	 * @return array
139
	 */
140
	public function define_bulk_actions( $actions ) {
141
		if ( isset( $actions['edit'] ) ) {
142
			unset( $actions['edit'] );
143
		}
144
145
		$actions['mark_processing'] = __( 'Change status to processing', 'woocommerce' );
146
		$actions['mark_on-hold']    = __( 'Change status to on-hold', 'woocommerce' );
147
		$actions['mark_completed']  = __( 'Change status to completed', 'woocommerce' );
148
149
		if ( wc_string_to_bool( get_option( 'woocommerce_allow_bulk_remove_personal_data', 'no' ) ) ) {
150
			$actions['remove_personal_data'] = __( 'Remove personal data', 'woocommerce' );
151
		}
152
153
		return $actions;
154
	}
155
156
	/**
157
	 * Pre-fetch any data for the row each column has access to it. the_order global is there for bw compat.
158
	 *
159
	 * @param int $post_id Post ID being shown.
160
	 */
161 View Code Duplication
	protected function prepare_row_data( $post_id ) {
162
		global $the_order;
163
164
		if ( empty( $this->object ) || $this->object->get_id() !== $post_id ) {
165
			$this->object = wc_get_order( $post_id );
166
			$the_order    = $this->object;
167
		}
168
	}
169
170
	/**
171
	 * Render columm: order_number.
172
	 */
173
	protected function render_order_number_column() {
174
		$buyer = '';
175
176
		if ( $this->object->get_billing_first_name() || $this->object->get_billing_last_name() ) {
177
			/* translators: 1: first name 2: last name */
178
			$buyer = trim( sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce' ), $this->object->get_billing_first_name(), $this->object->get_billing_last_name() ) );
179
		} elseif ( $this->object->get_billing_company() ) {
180
			$buyer = trim( $this->object->get_billing_company() );
181
		} elseif ( $this->object->get_customer_id() ) {
182
			$user  = get_user_by( 'id', $this->object->get_customer_id() );
183
			$buyer = ucwords( $user->display_name );
184
		}
185
186
		if ( $this->object->get_status() === 'trash' ) {
187
			echo '<strong>#' . esc_attr( $this->object->get_order_number() ) . ' ' . esc_html( $buyer ) . '</strong>';
188
		} else {
189
			echo '<a href="#" class="order-preview" data-order-id="' . absint( $this->object->get_id() ) . '" title="' . esc_attr( __( 'Preview', 'woocommerce' ) ) . '">' . esc_html( __( 'Preview', 'woocommerce' ) ) . '</a>';
190
			echo '<a href="' . esc_url( admin_url( 'post.php?post=' . absint( $this->object->get_id() ) ) . '&action=edit' ) . '" class="order-view"><strong>#' . esc_attr( $this->object->get_order_number() ) . ' ' . esc_html( $buyer ) . '</strong></a>';
191
		}
192
	}
193
194
	/**
195
	 * Render columm: order_status.
196
	 */
197
	protected function render_order_status_column() {
198
		$tooltip                 = '';
199
		$comment_count           = get_comment_count( $this->object->get_id() );
200
		$approved_comments_count = absint( $comment_count['approved'] );
201
202
		if ( $approved_comments_count ) {
203
			$latest_notes = wc_get_order_notes(
204
				array(
205
					'order_id' => $this->object->get_id(),
206
					'limit'    => 1,
207
					'orderby'  => 'date_created_gmt',
208
				)
209
			);
210
211
			$latest_note = current( $latest_notes );
212
213
			if ( isset( $latest_note->content ) && 1 === $approved_comments_count ) {
214
				$tooltip = wc_sanitize_tooltip( $latest_note->content );
215
			} elseif ( isset( $latest_note->content ) ) {
216
				/* translators: %d: notes count */
217
				$tooltip = wc_sanitize_tooltip( $latest_note->content . '<br/><small style="display:block">' . sprintf( _n( 'Plus %d other note', 'Plus %d other notes', ( $approved_comments_count - 1 ), 'woocommerce' ), $approved_comments_count - 1 ) . '</small>' );
218
			} else {
219
				/* translators: %d: notes count */
220
				$tooltip = wc_sanitize_tooltip( sprintf( _n( '%d note', '%d notes', $approved_comments_count, 'woocommerce' ), $approved_comments_count ) );
221
			}
222
		}
223
224
		if ( $tooltip ) {
225
			printf( '<mark class="order-status %s tips" data-tip="%s"><span>%s</span></mark>', esc_attr( sanitize_html_class( 'status-' . $this->object->get_status() ) ), wp_kses_post( $tooltip ), esc_html( wc_get_order_status_name( $this->object->get_status() ) ) );
226
		} else {
227
			printf( '<mark class="order-status %s"><span>%s</span></mark>', esc_attr( sanitize_html_class( 'status-' . $this->object->get_status() ) ), esc_html( wc_get_order_status_name( $this->object->get_status() ) ) );
228
		}
229
	}
230
231
	/**
232
	 * Render columm: order_date.
233
	 */
234
	protected function render_order_date_column() {
235
		$order_timestamp = $this->object->get_date_created() ? $this->object->get_date_created()->getTimestamp() : '';
236
237
		if ( ! $order_timestamp ) {
238
			echo '&ndash;';
239
			return;
240
		}
241
242
		// Check if the order was created within the last 24 hours, and not in the future.
243
		if ( $order_timestamp > strtotime( '-1 day', current_time( 'timestamp', true ) ) && $order_timestamp <= current_time( 'timestamp', true ) ) {
244
			$show_date = sprintf(
245
				/* translators: %s: human-readable time difference */
246
				_x( '%s ago', '%s = human-readable time difference', 'woocommerce' ),
247
				human_time_diff( $this->object->get_date_created()->getTimestamp(), current_time( 'timestamp', true ) )
248
			);
249
		} else {
250
			$show_date = $this->object->get_date_created()->date_i18n( apply_filters( 'woocommerce_admin_order_date_format', __( 'M j, Y', 'woocommerce' ) ) );
251
		}
252
		printf(
253
			'<time datetime="%1$s" title="%2$s">%3$s</time>',
254
			esc_attr( $this->object->get_date_created()->date( 'c' ) ),
255
			esc_html( $this->object->get_date_created()->date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ) ),
256
			esc_html( $show_date )
257
		);
258
	}
259
260
	/**
261
	 * Render columm: order_total.
262
	 */
263
	protected function render_order_total_column() {
264
		if ( $this->object->get_payment_method_title() ) {
265
			/* translators: %s: method */
266
			echo '<span class="tips" data-tip="' . esc_attr( sprintf( __( 'via %s', 'woocommerce' ), $this->object->get_payment_method_title() ) ) . '">' . wp_kses_post( $this->object->get_formatted_order_total() ) . '</span>';
267
		} else {
268
			echo wp_kses_post( $this->object->get_formatted_order_total() );
269
		}
270
	}
271
272
	/**
273
	 * Render columm: wc_actions.
274
	 */
275
	protected function render_wc_actions_column() {
276
		echo '<p>';
277
278
		do_action( 'woocommerce_admin_order_actions_start', $this->object );
279
280
		$actions = array();
281
282 View Code Duplication
		if ( $this->object->has_status( array( 'pending', 'on-hold' ) ) ) {
283
			$actions['processing'] = array(
284
				'url'    => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_mark_order_status&status=processing&order_id=' . $this->object->get_id() ), 'woocommerce-mark-order-status' ),
285
				'name'   => __( 'Processing', 'woocommerce' ),
286
				'action' => 'processing',
287
			);
288
		}
289
290 View Code Duplication
		if ( $this->object->has_status( array( 'pending', 'on-hold', 'processing' ) ) ) {
291
			$actions['complete'] = array(
292
				'url'    => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_mark_order_status&status=completed&order_id=' . $this->object->get_id() ), 'woocommerce-mark-order-status' ),
293
				'name'   => __( 'Complete', 'woocommerce' ),
294
				'action' => 'complete',
295
			);
296
		}
297
298
		$actions = apply_filters( 'woocommerce_admin_order_actions', $actions, $this->object );
299
300
		echo wc_render_action_buttons( $actions ); // WPCS: XSS ok.
301
302
		do_action( 'woocommerce_admin_order_actions_end', $this->object );
303
304
		echo '</p>';
305
	}
306
307
	/**
308
	 * Render columm: billing_address.
309
	 */
310
	protected function render_billing_address_column() {
311
		$address = $this->object->get_formatted_billing_address();
312
313
		if ( $address ) {
314
			echo esc_html( preg_replace( '#<br\s*/?>#i', ', ', $address ) );
315
316
			if ( $this->object->get_payment_method() ) {
317
				/* translators: %s: payment method */
318
				echo '<span class="description">' . sprintf( __( 'via %s', 'woocommerce' ), esc_html( $this->object->get_payment_method_title() ) ) . '</span>'; // WPCS: XSS ok.
319
			}
320
		} else {
321
			echo '&ndash;';
322
		}
323
	}
324
325
	/**
326
	 * Render columm: shipping_address.
327
	 */
328
	protected function render_shipping_address_column() {
329
		$address = $this->object->get_formatted_shipping_address();
330
331
		if ( $address ) {
332
			echo '<a target="_blank" href="' . esc_url( $this->object->get_shipping_address_map_url() ) . '">' . esc_html( preg_replace( '#<br\s*/?>#i', ', ', $address ) ) . '</a>';
333
			if ( $this->object->get_shipping_method() ) {
334
				/* translators: %s: shipping method */
335
				echo '<span class="description">' . sprintf( __( 'via %s', 'woocommerce' ), esc_html( $this->object->get_shipping_method() ) ) . '</span>'; // WPCS: XSS ok.
336
			}
337
		} else {
338
			echo '&ndash;';
339
		}
340
	}
341
342
	/**
343
	 * Template for order preview.
344
	 *
345
	 * @since 3.3.0
346
	 */
347
	public function order_preview_template() {
348
		?>
349
		<script type="text/template" id="tmpl-wc-modal-view-order">
350
			<div class="wc-backbone-modal wc-order-preview">
351
				<div class="wc-backbone-modal-content">
352
					<section class="wc-backbone-modal-main" role="main">
353
						<header class="wc-backbone-modal-header">
354
							<mark class="order-status status-{{ data.status }}"><span>{{ data.status_name }}</span></mark>
355
							<?php /* translators: %s: order ID */ ?>
356
							<h1><?php echo esc_html( sprintf( __( 'Order #%s', 'woocommerce' ), '{{ data.order_number }}' ) ); ?></h1>
357
							<button class="modal-close modal-close-link dashicons dashicons-no-alt">
358
								<span class="screen-reader-text"><?php esc_html_e( 'Close modal panel', 'woocommerce' ); ?></span>
359
							</button>
360
						</header>
361
						<article>
362
							<?php do_action( 'woocommerce_admin_order_preview_start' ); ?>
363
364
							<div class="wc-order-preview-addresses">
365
								<div class="wc-order-preview-address">
366
									<h2><?php esc_html_e( 'Billing details', 'woocommerce' ); ?></h2>
367
									{{{ data.formatted_billing_address }}}
368
369
									<# if ( data.data.billing.email ) { #>
370
										<strong><?php esc_html_e( 'Email', 'woocommerce' ); ?></strong>
371
										<a href="mailto:{{ data.data.billing.email }}">{{ data.data.billing.email }}</a>
372
									<# } #>
373
374
									<# if ( data.data.billing.phone ) { #>
375
										<strong><?php esc_html_e( 'Phone', 'woocommerce' ); ?></strong>
376
										<a href="tel:{{ data.data.billing.phone }}">{{ data.data.billing.phone }}</a>
377
									<# } #>
378
379
									<# if ( data.payment_via ) { #>
380
										<strong><?php esc_html_e( 'Payment via', 'woocommerce' ); ?></strong>
381
										{{{ data.payment_via }}}
382
									<# } #>
383
								</div>
384
								<# if ( data.needs_shipping ) { #>
385
									<div class="wc-order-preview-address">
386
										<h2><?php esc_html_e( 'Shipping details', 'woocommerce' ); ?></h2>
387
										<# if ( data.ship_to_billing ) { #>
388
											{{{ data.formatted_billing_address }}}
389
										<# } else { #>
390
											<a href="{{ data.shipping_address_map_url }}" target="_blank">{{{ data.formatted_shipping_address }}}</a>
391
										<# } #>
392
393
										<# if ( data.shipping_via ) { #>
394
											<strong><?php esc_html_e( 'Shipping method', 'woocommerce' ); ?></strong>
395
											{{ data.shipping_via }}
396
										<# } #>
397
									</div>
398
								<# } #>
399
400
								<# if ( data.data.customer_note ) { #>
401
									<div class="wc-order-preview-note">
402
										<strong><?php esc_html_e( 'Note', 'woocommerce' ); ?></strong>
403
										{{ data.data.customer_note }}
404
									</div>
405
								<# } #>
406
							</div>
407
408
							{{{ data.item_html }}}
409
410
							<?php do_action( 'woocommerce_admin_order_preview_end' ); ?>
411
						</article>
412
						<footer>
413
							<div class="inner">
414
								{{{ data.actions_html }}}
415
416
								<a class="button button-primary button-large" aria-label="<?php esc_attr_e( 'Edit this order', 'woocommerce' ); ?>" href="<?php echo esc_url( admin_url( 'post.php?action=edit' ) ); ?>&post={{ data.data.id }}"><?php esc_html_e( 'Edit', 'woocommerce' ); ?></a>
417
							</div>
418
						</footer>
419
					</section>
420
				</div>
421
			</div>
422
			<div class="wc-backbone-modal-backdrop modal-close"></div>
423
		</script>
424
		<?php
425
	}
426
427
	/**
428
	 * Get items to display in the preview as HTML.
429
	 *
430
	 * @param  WC_Order $order Order object.
431
	 * @return string
432
	 */
433
	public static function get_order_preview_item_html( $order ) {
434
		$hidden_order_itemmeta = apply_filters(
435
			'woocommerce_hidden_order_itemmeta',
436
			array(
437
				'_qty',
438
				'_tax_class',
439
				'_product_id',
440
				'_variation_id',
441
				'_line_subtotal',
442
				'_line_subtotal_tax',
443
				'_line_total',
444
				'_line_tax',
445
				'method_id',
446
				'cost',
447
				'_reduced_stock',
448
			)
449
		);
450
451
		$line_items = apply_filters( 'woocommerce_admin_order_preview_line_items', $order->get_items(), $order );
452
		$columns    = apply_filters(
453
			'woocommerce_admin_order_preview_line_item_columns',
454
			array(
455
				'product'  => __( 'Product', 'woocommerce' ),
456
				'quantity' => __( 'Quantity', 'woocommerce' ),
457
				'tax'      => __( 'Tax', 'woocommerce' ),
458
				'total'    => __( 'Total', 'woocommerce' ),
459
			),
460
			$order
461
		);
462
463
		if ( ! wc_tax_enabled() ) {
464
			unset( $columns['tax'] );
465
		}
466
467
		$html = '
468
		<div class="wc-order-preview-table-wrapper">
469
			<table cellspacing="0" class="wc-order-preview-table">
470
				<thead>
471
					<tr>';
472
473 View Code Duplication
		foreach ( $columns as $column => $label ) {
474
			$html .= '<th class="wc-order-preview-table__column--' . esc_attr( $column ) . '">' . esc_html( $label ) . '</th>';
475
		}
476
477
		$html .= '
478
					</tr>
479
				</thead>
480
				<tbody>';
481
482
		foreach ( $line_items as $item_id => $item ) {
483
484
			$product_object = is_callable( array( $item, 'get_product' ) ) ? $item->get_product() : null;
485
			$row_class      = apply_filters( 'woocommerce_admin_html_order_preview_item_class', '', $item, $order );
486
487
			$html .= '<tr class="wc-order-preview-table__item wc-order-preview-table__item--' . esc_attr( $item_id ) . ( $row_class ? ' ' . esc_attr( $row_class ) : '' ) . '">';
488
489
			foreach ( $columns as $column => $label ) {
490
				$html .= '<td class="wc-order-preview-table__column--' . esc_attr( $column ) . '">';
491
				switch ( $column ) {
492
					case 'product':
493
						$html .= wp_kses_post( $item->get_name() );
494
495
						if ( $product_object ) {
496
							$html .= '<div class="wc-order-item-sku">' . esc_html( $product_object->get_sku() ) . '</div>';
497
						}
498
499
						$meta_data = $item->get_formatted_meta_data( '' );
500
501
						if ( $meta_data ) {
502
							$html .= '<table cellspacing="0" class="wc-order-item-meta">';
503
504
							foreach ( $meta_data as $meta_id => $meta ) {
505
								if ( in_array( $meta->key, $hidden_order_itemmeta, true ) ) {
506
									continue;
507
								}
508
								$html .= '<tr><th>' . wp_kses_post( $meta->display_key ) . ':</th><td>' . wp_kses_post( force_balance_tags( $meta->display_value ) ) . '</td></tr>';
509
							}
510
							$html .= '</table>';
511
						}
512
						break;
513
					case 'quantity':
514
						$html .= esc_html( $item->get_quantity() );
515
						break;
516
					case 'tax':
517
						$html .= wc_price( $item->get_total_tax(), array( 'currency' => $order->get_currency() ) );
518
						break;
519
					case 'total':
520
						$html .= wc_price( $item->get_total(), array( 'currency' => $order->get_currency() ) );
521
						break;
522
					default:
523
						$html .= apply_filters( 'woocommerce_admin_order_preview_line_item_column_' . sanitize_key( $column ), '', $item, $item_id, $order );
524
						break;
525
				}
526
				$html .= '</td>';
527
			}
528
529
			$html .= '</tr>';
530
		}
531
532
		$html .= '
533
				</tbody>
534
			</table>
535
		</div>';
536
537
		return $html;
538
	}
539
540
	/**
541
	 * Get actions to display in the preview as HTML.
542
	 *
543
	 * @param  WC_Order $order Order object.
544
	 * @return string
545
	 */
546
	public static function get_order_preview_actions_html( $order ) {
547
		$actions        = array();
548
		$status_actions = array();
549
550 View Code Duplication
		if ( $order->has_status( array( 'pending' ) ) ) {
551
			$status_actions['on-hold'] = array(
552
				'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' ),
553
				'name'   => __( 'On-hold', 'woocommerce' ),
554
				'title'  => __( 'Change order status to on-hold', 'woocommerce' ),
555
				'action' => 'on-hold',
556
			);
557
		}
558
559 View Code Duplication
		if ( $order->has_status( array( 'pending', 'on-hold' ) ) ) {
560
			$status_actions['processing'] = array(
561
				'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' ),
562
				'name'   => __( 'Processing', 'woocommerce' ),
563
				'title'  => __( 'Change order status to processing', 'woocommerce' ),
564
				'action' => 'processing',
565
			);
566
		}
567
568 View Code Duplication
		if ( $order->has_status( array( 'pending', 'on-hold', 'processing' ) ) ) {
569
			$status_actions['complete'] = array(
570
				'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' ),
571
				'name'   => __( 'Completed', 'woocommerce' ),
572
				'title'  => __( 'Change order status to completed', 'woocommerce' ),
573
				'action' => 'complete',
574
			);
575
		}
576
577
		if ( $status_actions ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $status_actions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
578
			$actions['status'] = array(
579
				'group'   => __( 'Change status: ', 'woocommerce' ),
580
				'actions' => $status_actions,
581
			);
582
		}
583
584
		return wc_render_action_buttons( apply_filters( 'woocommerce_admin_order_preview_actions', $actions, $order ) );
585
	}
586
587
	/**
588
	 * Get order details to send to the ajax endpoint for previews.
589
	 *
590
	 * @param  WC_Order $order Order object.
591
	 * @return array
592
	 */
593
	public static function order_preview_get_order_details( $order ) {
594
		if ( ! $order ) {
595
			return array();
596
		}
597
598
		$payment_via      = $order->get_payment_method_title();
599
		$payment_method   = $order->get_payment_method();
600
		$payment_gateways = WC()->payment_gateways() ? WC()->payment_gateways->payment_gateways() : array();
601
		$transaction_id   = $order->get_transaction_id();
602
603 View Code Duplication
		if ( $transaction_id ) {
604
605
			$url = isset( $payment_gateways[ $payment_method ] ) ? $payment_gateways[ $payment_method ]->get_transaction_url( $order ) : false;
606
607
			if ( $url ) {
608
				$payment_via .= ' (<a href="' . esc_url( $url ) . '" target="_blank">' . esc_html( $transaction_id ) . '</a>)';
609
			} else {
610
				$payment_via .= ' (' . esc_html( $transaction_id ) . ')';
611
			}
612
		}
613
614
		$billing_address  = $order->get_formatted_billing_address();
615
		$shipping_address = $order->get_formatted_shipping_address();
616
617
		return apply_filters(
618
			'woocommerce_admin_order_preview_get_order_details',
619
			array(
620
				'data'                       => $order->get_data(),
621
				'order_number'               => $order->get_order_number(),
622
				'item_html'                  => self::get_order_preview_item_html( $order ),
623
				'actions_html'               => self::get_order_preview_actions_html( $order ),
624
				'ship_to_billing'            => wc_ship_to_billing_address_only(),
625
				'needs_shipping'             => $order->needs_shipping_address(),
626
				'formatted_billing_address'  => $billing_address ? $billing_address : __( 'N/A', 'woocommerce' ),
627
				'formatted_shipping_address' => $shipping_address ? $shipping_address : __( 'N/A', 'woocommerce' ),
628
				'shipping_address_map_url'   => $order->get_shipping_address_map_url(),
629
				'payment_via'                => $payment_via,
630
				'shipping_via'               => $order->get_shipping_method(),
631
				'status'                     => $order->get_status(),
632
				'status_name'                => wc_get_order_status_name( $order->get_status() ),
633
			),
634
			$order
635
		);
636
	}
637
638
	/**
639
	 * Handle bulk actions.
640
	 *
641
	 * @param  string $redirect_to URL to redirect to.
642
	 * @param  string $action      Action name.
643
	 * @param  array  $ids         List of ids.
644
	 * @return string
645
	 */
646
	public function handle_bulk_actions( $redirect_to, $action, $ids ) {
647
		$ids     = apply_filters( 'woocommerce_bulk_action_ids', array_reverse( array_map( 'absint', $ids ) ), $action, 'order' );
648
		$changed = 0;
649
650
		if ( 'remove_personal_data' === $action ) {
651
			$report_action = 'removed_personal_data';
652
653
			foreach ( $ids as $id ) {
654
				$order = wc_get_order( $id );
655
656
				if ( $order ) {
657
					do_action( 'woocommerce_remove_order_personal_data', $order );
658
					$changed++;
659
				}
660
			}
661
		} elseif ( false !== strpos( $action, 'mark_' ) ) {
662
			$order_statuses = wc_get_order_statuses();
663
			$new_status     = substr( $action, 5 ); // Get the status name from action.
664
			$report_action  = 'marked_' . $new_status;
665
666
			// Sanity check: bail out if this is actually not a status, or is not a registered status.
667
			if ( isset( $order_statuses[ 'wc-' . $new_status ] ) ) {
668
				// Initialize payment gateways in case order has hooked status transition actions.
669
				WC()->payment_gateways();
670
671
				foreach ( $ids as $id ) {
672
					$order = wc_get_order( $id );
673
					$order->update_status( $new_status, __( 'Order status changed by bulk edit:', 'woocommerce' ), true );
674
					do_action( 'woocommerce_order_edit_status', $id, $new_status );
675
					$changed++;
676
				}
677
			}
678
		}
679
680
		if ( $changed ) {
681
			$redirect_to = add_query_arg(
682
				array(
683
					'post_type'   => $this->list_table_type,
684
					'bulk_action' => $report_action,
685
					'changed'     => $changed,
686
					'ids'         => join( ',', $ids ),
687
				),
688
				$redirect_to
689
			);
690
		}
691
692
		return esc_url_raw( $redirect_to );
693
	}
694
695
	/**
696
	 * Show confirmation message that order status changed for number of orders.
697
	 */
698
	public function bulk_admin_notices() {
699
		global $post_type, $pagenow;
700
701
		// Bail out if not on shop order list page.
702
		if ( 'edit.php' !== $pagenow || 'shop_order' !== $post_type || ! isset( $_REQUEST['bulk_action'] ) ) { // WPCS: input var ok, CSRF ok.
703
			return;
704
		}
705
706
		$order_statuses = wc_get_order_statuses();
707
		$number         = isset( $_REQUEST['changed'] ) ? absint( $_REQUEST['changed'] ) : 0; // WPCS: input var ok, CSRF ok.
708
		$bulk_action    = wc_clean( wp_unslash( $_REQUEST['bulk_action'] ) ); // WPCS: input var ok, CSRF ok.
709
710
		// Check if any status changes happened.
711
		foreach ( $order_statuses as $slug => $name ) {
712 View Code Duplication
			if ( 'marked_' . str_replace( 'wc-', '', $slug ) === $bulk_action ) { // WPCS: input var ok, CSRF ok.
713
				/* translators: %d: orders count */
714
				$message = sprintf( _n( '%d order status changed.', '%d order statuses changed.', $number, 'woocommerce' ), number_format_i18n( $number ) );
715
				echo '<div class="updated"><p>' . esc_html( $message ) . '</p></div>';
716
				break;
717
			}
718
		}
719
720 View Code Duplication
		if ( 'removed_personal_data' === $bulk_action ) { // WPCS: input var ok, CSRF ok.
721
			/* translators: %d: orders count */
722
			$message = sprintf( _n( 'Removed personal data from %d order.', 'Removed personal data from %d orders.', $number, 'woocommerce' ), number_format_i18n( $number ) );
723
			echo '<div class="updated"><p>' . esc_html( $message ) . '</p></div>';
724
		}
725
	}
726
727
	/**
728
	 * See if we should render search filters or not.
729
	 */
730
	public function restrict_manage_posts() {
731
		global $typenow;
732
733
		if ( in_array( $typenow, wc_get_order_types( 'order-meta-boxes' ), true ) ) {
734
			$this->render_filters();
735
		}
736
	}
737
738
	/**
739
	 * Render any custom filters and search inputs for the list table.
740
	 */
741
	protected function render_filters() {
742
		$user_string = '';
743
		$user_id     = '';
744
745
		if ( ! empty( $_GET['_customer_user'] ) ) { // phpcs:disable  WordPress.Security.NonceVerification.NoNonceVerification
746
			$user_id = absint( $_GET['_customer_user'] ); // WPCS: input var ok, sanitization ok.
747
			$user    = get_user_by( 'id', $user_id );
748
749
			$user_string = sprintf(
750
				/* translators: 1: user display name 2: user ID 3: user email */
751
				esc_html__( '%1$s (#%2$s &ndash; %3$s)', 'woocommerce' ),
752
				$user->display_name,
753
				absint( $user->ID ),
754
				$user->user_email
755
			);
756
		}
757
		?>
758
		<select class="wc-customer-search" name="_customer_user" data-placeholder="<?php esc_attr_e( 'Filter by registered customer', 'woocommerce' ); ?>" data-allow_clear="true">
759
			<option value="<?php echo esc_attr( $user_id ); ?>" selected="selected"><?php echo htmlspecialchars( wp_kses_post( $user_string ) ); // htmlspecialchars to prevent XSS when rendered by selectWoo. ?><option>
760
		</select>
761
		<?php
762
	}
763
764
	/**
765
	 * Handle any filters.
766
	 *
767
	 * @param array $query_vars Query vars.
768
	 * @return array
769
	 */
770
	public function request_query( $query_vars ) {
771
		global $typenow;
772
773
		if ( in_array( $typenow, wc_get_order_types( 'order-meta-boxes' ), true ) ) {
774
			return $this->query_filters( $query_vars );
775
		}
776
777
		return $query_vars;
778
	}
779
780
	/**
781
	 * Handle any custom filters.
782
	 *
783
	 * @param array $query_vars Query vars.
784
	 * @return array
785
	 */
786
	protected function query_filters( $query_vars ) {
787
		global $wp_post_statuses;
788
789
		// Filter the orders by the posted customer.
790 View Code Duplication
		if ( ! empty( $_GET['_customer_user'] ) ) { // WPCS: input var ok.
791
			// @codingStandardsIgnoreStart.
792
			$query_vars['meta_query'] = array(
793
				array(
794
					'key'     => '_customer_user',
795
					'value'   => (int) $_GET['_customer_user'], // WPCS: input var ok, sanitization ok.
796
					'compare' => '=',
797
				),
798
			);
799
			// @codingStandardsIgnoreEnd
800
		}
801
802
		// Sorting.
803
		if ( isset( $query_vars['orderby'] ) ) {
804
			if ( 'order_total' === $query_vars['orderby'] ) {
805
				// @codingStandardsIgnoreStart
806
				$query_vars = array_merge( $query_vars, array(
807
					'meta_key'  => '_order_total',
808
					'orderby'   => 'meta_value_num',
809
				) );
810
				// @codingStandardsIgnoreEnd
811
			}
812
		}
813
814
		// Status.
815
		if ( empty( $query_vars['post_status'] ) ) {
816
			$post_statuses = wc_get_order_statuses();
817
818
			foreach ( $post_statuses as $status => $value ) {
819
				if ( isset( $wp_post_statuses[ $status ] ) && false === $wp_post_statuses[ $status ]->show_in_admin_all_list ) {
820
					unset( $post_statuses[ $status ] );
821
				}
822
			}
823
824
			$query_vars['post_status'] = array_keys( $post_statuses );
825
		}
826
		return $query_vars;
827
	}
828
829
	/**
830
	 * Change the label when searching orders.
831
	 *
832
	 * @param mixed $query Current search query.
833
	 * @return string
834
	 */
835 View Code Duplication
	public function search_label( $query ) {
836
		global $pagenow, $typenow;
837
838
		if ( 'edit.php' !== $pagenow || 'shop_order' !== $typenow || ! get_query_var( 'shop_order_search' ) || ! isset( $_GET['s'] ) ) { // phpcs:disable  WordPress.Security.NonceVerification.NoNonceVerification
839
			return $query;
840
		}
841
842
		return wc_clean( wp_unslash( $_GET['s'] ) ); // WPCS: input var ok, sanitization ok.
843
	}
844
845
	/**
846
	 * Query vars for custom searches.
847
	 *
848
	 * @param mixed $public_query_vars Array of query vars.
849
	 * @return array
850
	 */
851
	public function add_custom_query_var( $public_query_vars ) {
852
		$public_query_vars[] = 'shop_order_search';
853
		return $public_query_vars;
854
	}
855
856
	/**
857
	 * Search custom fields as well as content.
858
	 *
859
	 * @param WP_Query $wp Query object.
860
	 */
861
	public function search_custom_fields( $wp ) {
862
		global $pagenow;
863
864
		if ( 'edit.php' !== $pagenow || empty( $wp->query_vars['s'] ) || 'shop_order' !== $wp->query_vars['post_type'] || ! isset( $_GET['s'] ) ) { // phpcs:disable  WordPress.Security.NonceVerification.NoNonceVerification
865
			return;
866
		}
867
868
		$post_ids = wc_order_search( wc_clean( wp_unslash( $_GET['s'] ) ) ); // WPCS: input var ok, sanitization ok.
869
870
		if ( ! empty( $post_ids ) ) {
871
			// Remove "s" - we don't want to search order name.
872
			unset( $wp->query_vars['s'] );
873
874
			// so we know we're doing this.
875
			$wp->query_vars['shop_order_search'] = true;
876
877
			// Search by found posts.
878
			$wp->query_vars['post__in'] = array_merge( $post_ids, array( 0 ) );
879
		}
880
	}
881
}
882