1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* List tables: products. |
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_Products', 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_Products Class. |
23
|
|
|
*/ |
24
|
|
|
class WC_Admin_List_Table_Products extends WC_Admin_List_Table { |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Post type. |
28
|
|
|
* |
29
|
|
|
* @var string |
30
|
|
|
*/ |
31
|
|
|
protected $list_table_type = 'product'; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Constructor. |
35
|
|
|
*/ |
36
|
|
|
public function __construct() { |
37
|
|
|
parent::__construct(); |
38
|
|
|
add_filter( 'disable_months_dropdown', '__return_true' ); |
39
|
|
|
add_filter( 'query_vars', array( $this, 'add_custom_query_var' ) ); |
40
|
|
|
add_filter( 'views_edit-product', array( $this, 'product_views' ) ); |
41
|
|
|
add_filter( 'get_search_query', array( $this, 'search_label' ) ); |
42
|
|
|
add_filter( 'posts_clauses', array( $this, 'posts_clauses' ), 10, 2 ); |
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__( 'Ready to start selling something awesome?', 'woocommerce' ) . '</h2>'; |
52
|
|
|
|
53
|
|
|
echo '<div class="woocommerce-BlankState-buttons">'; |
54
|
|
|
|
55
|
|
|
echo '<a class="woocommerce-BlankState-cta button-primary button" href="' . esc_url( admin_url( 'post-new.php?post_type=product&tutorial=true' ) ) . '">' . esc_html__( 'Create Product', 'woocommerce' ) . '</a>'; |
56
|
|
|
echo '<a class="woocommerce-BlankState-cta button" href="' . esc_url( admin_url( 'edit.php?post_type=product&page=product_importer' ) ) . '">' . esc_html__( 'Start Import', 'woocommerce' ) . '</a>'; |
57
|
|
|
|
58
|
|
|
echo '</div>'; |
59
|
|
|
|
60
|
|
|
do_action( 'wc_marketplace_suggestions_products_empty_state' ); |
61
|
|
|
|
62
|
|
|
echo '</div>'; |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Define primary column. |
67
|
|
|
* |
68
|
|
|
* @return string |
69
|
|
|
*/ |
70
|
|
|
protected function get_primary_column() { |
71
|
|
|
return 'name'; |
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
|
|
|
/* translators: %d: product ID. */ |
83
|
|
|
return array_merge( array( 'id' => sprintf( __( 'ID: %d', 'woocommerce' ), $post->ID ) ), $actions ); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* Define which columns are sortable. |
88
|
|
|
* |
89
|
|
|
* @param array $columns Existing columns. |
90
|
|
|
* @return array |
91
|
|
|
*/ |
92
|
|
|
public function define_sortable_columns( $columns ) { |
93
|
|
|
$custom = array( |
94
|
|
|
'price' => 'price', |
95
|
|
|
'sku' => 'sku', |
96
|
|
|
'name' => 'title', |
97
|
|
|
); |
98
|
|
|
return wp_parse_args( $custom, $columns ); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Define which columns to show on this screen. |
103
|
|
|
* |
104
|
|
|
* @param array $columns Existing columns. |
105
|
|
|
* @return array |
106
|
|
|
*/ |
107
|
|
|
public function define_columns( $columns ) { |
108
|
|
|
if ( empty( $columns ) && ! is_array( $columns ) ) { |
109
|
|
|
$columns = array(); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
unset( $columns['title'], $columns['comments'], $columns['date'] ); |
113
|
|
|
|
114
|
|
|
$show_columns = array(); |
115
|
|
|
$show_columns['cb'] = '<input type="checkbox" />'; |
116
|
|
|
$show_columns['thumb'] = '<span class="wc-image tips" data-tip="' . esc_attr__( 'Image', 'woocommerce' ) . '">' . __( 'Image', 'woocommerce' ) . '</span>'; |
117
|
|
|
$show_columns['name'] = __( 'Name', 'woocommerce' ); |
118
|
|
|
|
119
|
|
|
if ( wc_product_sku_enabled() ) { |
120
|
|
|
$show_columns['sku'] = __( 'SKU', 'woocommerce' ); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) { |
124
|
|
|
$show_columns['is_in_stock'] = __( 'Stock', 'woocommerce' ); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
$show_columns['price'] = __( 'Price', 'woocommerce' ); |
128
|
|
|
$show_columns['product_cat'] = __( 'Categories', 'woocommerce' ); |
129
|
|
|
$show_columns['product_tag'] = __( 'Tags', 'woocommerce' ); |
130
|
|
|
$show_columns['featured'] = '<span class="wc-featured parent-tips" data-tip="' . esc_attr__( 'Featured', 'woocommerce' ) . '">' . __( 'Featured', 'woocommerce' ) . '</span>'; |
131
|
|
|
$show_columns['date'] = __( 'Date', 'woocommerce' ); |
132
|
|
|
|
133
|
|
|
return array_merge( $show_columns, $columns ); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* Pre-fetch any data for the row each column has access to it. the_product global is there for bw compat. |
138
|
|
|
* |
139
|
|
|
* @param int $post_id Post ID being shown. |
140
|
|
|
*/ |
141
|
|
View Code Duplication |
protected function prepare_row_data( $post_id ) { |
|
|
|
|
142
|
|
|
global $the_product; |
143
|
|
|
|
144
|
|
|
if ( empty( $this->object ) || $this->object->get_id() !== $post_id ) { |
145
|
|
|
$the_product = wc_get_product( $post_id ); |
146
|
|
|
$this->object = $the_product; |
|
|
|
|
147
|
|
|
} |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Render columm: thumb. |
152
|
|
|
*/ |
153
|
|
|
protected function render_thumb_column() { |
154
|
|
|
echo '<a href="' . esc_url( get_edit_post_link( $this->object->get_id() ) ) . '">' . $this->object->get_image( 'thumbnail' ) . '</a>'; // WPCS: XSS ok. |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Render column: name. |
159
|
|
|
*/ |
160
|
|
|
protected function render_name_column() { |
161
|
|
|
global $post; |
162
|
|
|
|
163
|
|
|
$edit_link = get_edit_post_link( $this->object->get_id() ); |
164
|
|
|
$title = _draft_or_post_title(); |
165
|
|
|
|
166
|
|
|
echo '<strong><a class="row-title" href="' . esc_url( $edit_link ) . '">' . esc_html( $title ) . '</a>'; |
167
|
|
|
|
168
|
|
|
_post_states( $post ); |
169
|
|
|
|
170
|
|
|
echo '</strong>'; |
171
|
|
|
|
172
|
|
|
if ( $this->object->get_parent_id() > 0 ) { |
173
|
|
|
echo ' ← <a href="' . esc_url( get_edit_post_link( $this->object->get_parent_id() ) ) . '">' . get_the_title( $this->object->get_parent_id() ) . '</a>'; // @codingStandardsIgnoreLine. |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
get_inline_data( $post ); |
177
|
|
|
|
178
|
|
|
/* Custom inline data for woocommerce. */ |
179
|
|
|
echo ' |
180
|
|
|
<div class="hidden" id="woocommerce_inline_' . absint( $this->object->get_id() ) . '"> |
181
|
|
|
<div class="menu_order">' . esc_html( $this->object->get_menu_order() ) . '</div> |
182
|
|
|
<div class="sku">' . esc_html( $this->object->get_sku() ) . '</div> |
183
|
|
|
<div class="regular_price">' . esc_html( $this->object->get_regular_price() ) . '</div> |
184
|
|
|
<div class="sale_price">' . esc_html( $this->object->get_sale_price() ) . '</div> |
185
|
|
|
<div class="weight">' . esc_html( $this->object->get_weight() ) . '</div> |
186
|
|
|
<div class="length">' . esc_html( $this->object->get_length() ) . '</div> |
187
|
|
|
<div class="width">' . esc_html( $this->object->get_width() ) . '</div> |
188
|
|
|
<div class="height">' . esc_html( $this->object->get_height() ) . '</div> |
189
|
|
|
<div class="shipping_class">' . esc_html( $this->object->get_shipping_class() ) . '</div> |
190
|
|
|
<div class="visibility">' . esc_html( $this->object->get_catalog_visibility() ) . '</div> |
191
|
|
|
<div class="stock_status">' . esc_html( $this->object->get_stock_status() ) . '</div> |
192
|
|
|
<div class="stock">' . esc_html( $this->object->get_stock_quantity() ) . '</div> |
193
|
|
|
<div class="manage_stock">' . esc_html( wc_bool_to_string( $this->object->get_manage_stock() ) ) . '</div> |
194
|
|
|
<div class="featured">' . esc_html( wc_bool_to_string( $this->object->get_featured() ) ) . '</div> |
195
|
|
|
<div class="product_type">' . esc_html( $this->object->get_type() ) . '</div> |
196
|
|
|
<div class="product_is_virtual">' . esc_html( wc_bool_to_string( $this->object->get_virtual() ) ) . '</div> |
197
|
|
|
<div class="tax_status">' . esc_html( $this->object->get_tax_status() ) . '</div> |
198
|
|
|
<div class="tax_class">' . esc_html( $this->object->get_tax_class() ) . '</div> |
199
|
|
|
<div class="backorders">' . esc_html( $this->object->get_backorders() ) . '</div> |
200
|
|
|
<div class="low_stock_amount">' . esc_html( $this->object->get_low_stock_amount() ) . '</div> |
201
|
|
|
</div> |
202
|
|
|
'; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* Render columm: sku. |
207
|
|
|
*/ |
208
|
|
|
protected function render_sku_column() { |
209
|
|
|
echo $this->object->get_sku() ? esc_html( $this->object->get_sku() ) : '<span class="na">–</span>'; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Render columm: price. |
214
|
|
|
*/ |
215
|
|
|
protected function render_price_column() { |
216
|
|
|
echo $this->object->get_price_html() ? wp_kses_post( $this->object->get_price_html() ) : '<span class="na">–</span>'; |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* Render columm: product_cat. |
221
|
|
|
*/ |
222
|
|
View Code Duplication |
protected function render_product_cat_column() { |
|
|
|
|
223
|
|
|
$terms = get_the_terms( $this->object->get_id(), 'product_cat' ); |
224
|
|
|
if ( ! $terms ) { |
225
|
|
|
echo '<span class="na">–</span>'; |
226
|
|
|
} else { |
227
|
|
|
$termlist = array(); |
228
|
|
|
foreach ( $terms as $term ) { |
229
|
|
|
$termlist[] = '<a href="' . esc_url( admin_url( 'edit.php?product_cat=' . $term->slug . '&post_type=product' ) ) . ' ">' . esc_html( $term->name ) . '</a>'; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
echo apply_filters( 'woocommerce_admin_product_term_list', implode( ', ', $termlist ), 'product_cat', $this->object->get_id(), $termlist, $terms ); // WPCS: XSS ok. |
233
|
|
|
} |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* Render columm: product_tag. |
238
|
|
|
*/ |
239
|
|
View Code Duplication |
protected function render_product_tag_column() { |
|
|
|
|
240
|
|
|
$terms = get_the_terms( $this->object->get_id(), 'product_tag' ); |
241
|
|
|
if ( ! $terms ) { |
242
|
|
|
echo '<span class="na">–</span>'; |
243
|
|
|
} else { |
244
|
|
|
$termlist = array(); |
245
|
|
|
foreach ( $terms as $term ) { |
246
|
|
|
$termlist[] = '<a href="' . esc_url( admin_url( 'edit.php?product_tag=' . $term->slug . '&post_type=product' ) ) . ' ">' . esc_html( $term->name ) . '</a>'; |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
echo apply_filters( 'woocommerce_admin_product_term_list', implode( ', ', $termlist ), 'product_tag', $this->object->get_id(), $termlist, $terms ); // WPCS: XSS ok. |
250
|
|
|
} |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* Render columm: featured. |
255
|
|
|
*/ |
256
|
|
|
protected function render_featured_column() { |
257
|
|
|
$url = wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_feature_product&product_id=' . $this->object->get_id() ), 'woocommerce-feature-product' ); |
258
|
|
|
echo '<a href="' . esc_url( $url ) . '" aria-label="' . esc_attr__( 'Toggle featured', 'woocommerce' ) . '">'; |
259
|
|
|
if ( $this->object->is_featured() ) { |
260
|
|
|
echo '<span class="wc-featured tips" data-tip="' . esc_attr__( 'Yes', 'woocommerce' ) . '">' . esc_html__( 'Yes', 'woocommerce' ) . '</span>'; |
261
|
|
|
} else { |
262
|
|
|
echo '<span class="wc-featured not-featured tips" data-tip="' . esc_attr__( 'No', 'woocommerce' ) . '">' . esc_html__( 'No', 'woocommerce' ) . '</span>'; |
263
|
|
|
} |
264
|
|
|
echo '</a>'; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
/** |
268
|
|
|
* Render columm: is_in_stock. |
269
|
|
|
*/ |
270
|
|
|
protected function render_is_in_stock_column() { |
271
|
|
|
if ( $this->object->is_on_backorder() ) { |
272
|
|
|
$stock_html = '<mark class="onbackorder">' . __( 'On backorder', 'woocommerce' ) . '</mark>'; |
273
|
|
|
} elseif ( $this->object->is_in_stock() ) { |
274
|
|
|
$stock_html = '<mark class="instock">' . __( 'In stock', 'woocommerce' ) . '</mark>'; |
275
|
|
|
} else { |
276
|
|
|
$stock_html = '<mark class="outofstock">' . __( 'Out of stock', 'woocommerce' ) . '</mark>'; |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
if ( $this->object->managing_stock() ) { |
280
|
|
|
$stock_html .= ' (' . wc_stock_amount( $this->object->get_stock_quantity() ) . ')'; |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
echo wp_kses_post( apply_filters( 'woocommerce_admin_stock_html', $stock_html, $this->object ) ); |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
/** |
287
|
|
|
* Query vars for custom searches. |
288
|
|
|
* |
289
|
|
|
* @param mixed $public_query_vars Array of query vars. |
290
|
|
|
* @return array |
291
|
|
|
*/ |
292
|
|
|
public function add_custom_query_var( $public_query_vars ) { |
293
|
|
|
$public_query_vars[] = 'sku'; |
294
|
|
|
return $public_query_vars; |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* Render any custom filters and search inputs for the list table. |
299
|
|
|
*/ |
300
|
|
|
protected function render_filters() { |
301
|
|
|
$filters = apply_filters( |
302
|
|
|
'woocommerce_products_admin_list_table_filters', |
303
|
|
|
array( |
304
|
|
|
'product_category' => array( $this, 'render_products_category_filter' ), |
305
|
|
|
'product_type' => array( $this, 'render_products_type_filter' ), |
306
|
|
|
'stock_status' => array( $this, 'render_products_stock_status_filter' ), |
307
|
|
|
) |
308
|
|
|
); |
309
|
|
|
|
310
|
|
|
ob_start(); |
311
|
|
|
foreach ( $filters as $filter_callback ) { |
312
|
|
|
call_user_func( $filter_callback ); |
313
|
|
|
} |
314
|
|
|
$output = ob_get_clean(); |
315
|
|
|
|
316
|
|
|
echo apply_filters( 'woocommerce_product_filters', $output ); // WPCS: XSS ok. |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* Render the product category filter for the list table. |
321
|
|
|
* |
322
|
|
|
* @since 3.5.0 |
323
|
|
|
*/ |
324
|
|
|
protected function render_products_category_filter() { |
325
|
|
|
$categories_count = (int) wp_count_terms( 'product_cat' ); |
326
|
|
|
|
327
|
|
|
if ( $categories_count <= apply_filters( 'woocommerce_product_category_filter_threshold', 100 ) ) { |
328
|
|
|
wc_product_dropdown_categories( |
329
|
|
|
array( |
330
|
|
|
'option_select_text' => __( 'Filter by category', 'woocommerce' ), |
331
|
|
|
'hide_empty' => 0, |
332
|
|
|
) |
333
|
|
|
); |
334
|
|
|
} else { |
335
|
|
|
$current_category_slug = isset( $_GET['product_cat'] ) ? wc_clean( wp_unslash( $_GET['product_cat'] ) ) : false; // WPCS: input var ok, CSRF ok. |
336
|
|
|
$current_category = $current_category_slug ? get_term_by( 'slug', $current_category_slug, 'product_cat' ) : false; |
337
|
|
|
?> |
338
|
|
|
<select class="wc-category-search" name="product_cat" data-placeholder="<?php esc_attr_e( 'Filter by category', 'woocommerce' ); ?>" data-allow_clear="true"> |
339
|
|
|
<?php if ( $current_category_slug && $current_category ) : ?> |
340
|
|
|
<option value="<?php echo esc_attr( $current_category_slug ); ?>" selected="selected"><?php echo esc_html( htmlspecialchars( wp_kses_post( $current_category->name ) ) ); ?><option> |
341
|
|
|
<?php endif; ?> |
342
|
|
|
</select> |
343
|
|
|
<?php |
344
|
|
|
} |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
/** |
348
|
|
|
* Render the product type filter for the list table. |
349
|
|
|
* |
350
|
|
|
* @since 3.5.0 |
351
|
|
|
*/ |
352
|
|
|
protected function render_products_type_filter() { |
353
|
|
|
$current_product_type = isset( $_REQUEST['product_type'] ) ? wc_clean( wp_unslash( $_REQUEST['product_type'] ) ) : false; // WPCS: input var ok, sanitization ok. |
354
|
|
|
$output = '<select name="product_type" id="dropdown_product_type"><option value="">' . __( 'Filter by product type', 'woocommerce' ) . '</option>'; |
355
|
|
|
|
356
|
|
|
foreach ( wc_get_product_types() as $value => $label ) { |
357
|
|
|
$output .= '<option value="' . esc_attr( $value ) . '" '; |
358
|
|
|
$output .= selected( $value, $current_product_type, false ); |
359
|
|
|
$output .= '>' . esc_html( $label ) . '</option>'; |
360
|
|
|
|
361
|
|
|
if ( 'simple' === $value ) { |
362
|
|
|
|
363
|
|
|
$output .= '<option value="downloadable" '; |
364
|
|
|
$output .= selected( 'downloadable', $current_product_type, false ); |
365
|
|
|
$output .= '> ' . ( is_rtl() ? '←' : '→' ) . ' ' . __( 'Downloadable', 'woocommerce' ) . '</option>'; |
366
|
|
|
|
367
|
|
|
$output .= '<option value="virtual" '; |
368
|
|
|
$output .= selected( 'virtual', $current_product_type, false ); |
369
|
|
|
$output .= '> ' . ( is_rtl() ? '←' : '→' ) . ' ' . __( 'Virtual', 'woocommerce' ) . '</option>'; |
370
|
|
|
} |
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
$output .= '</select>'; |
374
|
|
|
echo $output; // WPCS: XSS ok. |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
/** |
378
|
|
|
* Render the stock status filter for the list table. |
379
|
|
|
* |
380
|
|
|
* @since 3.5.0 |
381
|
|
|
*/ |
382
|
|
|
public function render_products_stock_status_filter() { |
383
|
|
|
$current_stock_status = isset( $_REQUEST['stock_status'] ) ? wc_clean( wp_unslash( $_REQUEST['stock_status'] ) ) : false; // WPCS: input var ok, sanitization ok. |
384
|
|
|
$stock_statuses = wc_get_product_stock_status_options(); |
385
|
|
|
$output = '<select name="stock_status"><option value="">' . esc_html__( 'Filter by stock status', 'woocommerce' ) . '</option>'; |
386
|
|
|
|
387
|
|
|
foreach ( $stock_statuses as $status => $label ) { |
388
|
|
|
$output .= '<option ' . selected( $status, $current_stock_status, false ) . ' value="' . esc_attr( $status ) . '">' . esc_html( $label ) . '</option>'; |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
$output .= '</select>'; |
392
|
|
|
echo $output; // WPCS: XSS ok. |
393
|
|
|
} |
394
|
|
|
|
395
|
|
|
/** |
396
|
|
|
* Search by SKU or ID for products. |
397
|
|
|
* |
398
|
|
|
* @deprecated Logic moved to query_filters. |
399
|
|
|
* @param string $where Where clause SQL. |
400
|
|
|
* @return string |
401
|
|
|
*/ |
402
|
|
|
public function sku_search( $where ) { |
403
|
|
|
return $where; |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
/** |
407
|
|
|
* Change views on the edit product screen. |
408
|
|
|
* |
409
|
|
|
* @param array $views Array of views. |
410
|
|
|
* @return array |
411
|
|
|
*/ |
412
|
|
|
public function product_views( $views ) { |
413
|
|
|
global $wp_query; |
414
|
|
|
|
415
|
|
|
// Products do not have authors. |
416
|
|
|
unset( $views['mine'] ); |
417
|
|
|
|
418
|
|
|
// Add sorting link. |
419
|
|
|
if ( current_user_can( 'edit_others_pages' ) ) { |
420
|
|
|
$class = ( isset( $wp_query->query['orderby'] ) && 'menu_order title' === $wp_query->query['orderby'] ) ? 'current' : ''; |
421
|
|
|
$query_string = remove_query_arg( array( 'orderby', 'order' ) ); |
422
|
|
|
$query_string = add_query_arg( 'orderby', rawurlencode( 'menu_order title' ), $query_string ); |
423
|
|
|
$query_string = add_query_arg( 'order', rawurlencode( 'ASC' ), $query_string ); |
424
|
|
|
$views['byorder'] = '<a href="' . esc_url( $query_string ) . '" class="' . esc_attr( $class ) . '">' . __( 'Sorting', 'woocommerce' ) . '</a>'; |
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
return $views; |
428
|
|
|
} |
429
|
|
|
|
430
|
|
|
/** |
431
|
|
|
* Change the label when searching products |
432
|
|
|
* |
433
|
|
|
* @param string $query Search Query. |
434
|
|
|
* @return string |
435
|
|
|
*/ |
436
|
|
View Code Duplication |
public function search_label( $query ) { |
|
|
|
|
437
|
|
|
global $pagenow, $typenow; |
438
|
|
|
|
439
|
|
|
if ( 'edit.php' !== $pagenow || 'product' !== $typenow || ! get_query_var( 'product_search' ) || ! isset( $_GET['s'] ) ) { // WPCS: input var ok. |
440
|
|
|
return $query; |
441
|
|
|
} |
442
|
|
|
|
443
|
|
|
return wc_clean( wp_unslash( $_GET['s'] ) ); // WPCS: input var ok, sanitization ok. |
444
|
|
|
} |
445
|
|
|
|
446
|
|
|
/** |
447
|
|
|
* Handle any custom filters. |
448
|
|
|
* |
449
|
|
|
* @param array $query_vars Query vars. |
450
|
|
|
* @return array |
451
|
|
|
*/ |
452
|
|
|
protected function query_filters( $query_vars ) { |
453
|
|
|
$this->remove_ordering_args(); |
454
|
|
|
// Custom order by arguments. |
455
|
|
|
if ( isset( $query_vars['orderby'] ) ) { |
456
|
|
|
$orderby = strtolower( $query_vars['orderby'] ); |
457
|
|
|
$order = isset( $query_vars['order'] ) ? strtoupper( $query_vars['order'] ) : 'DESC'; |
458
|
|
|
|
459
|
|
|
if ( 'price' === $orderby ) { |
460
|
|
|
$callback = 'DESC' === $order ? 'order_by_price_desc_post_clauses' : 'order_by_price_asc_post_clauses'; |
461
|
|
|
add_filter( 'posts_clauses', array( $this, $callback ) ); |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
if ( 'sku' === $orderby ) { |
465
|
|
|
$callback = 'DESC' === $order ? 'order_by_sku_desc_post_clauses' : 'order_by_sku_asc_post_clauses'; |
466
|
|
|
add_filter( 'posts_clauses', array( $this, $callback ) ); |
467
|
|
|
} |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
// Type filtering. |
471
|
|
|
if ( isset( $query_vars['product_type'] ) ) { |
472
|
|
|
if ( 'downloadable' === $query_vars['product_type'] ) { |
473
|
|
|
$query_vars['product_type'] = ''; |
474
|
|
|
add_filter( 'posts_clauses', array( $this, 'filter_downloadable_post_clauses' ) ); |
475
|
|
|
} elseif ( 'virtual' === $query_vars['product_type'] ) { |
476
|
|
|
$query_vars['product_type'] = ''; |
477
|
|
|
add_filter( 'posts_clauses', array( $this, 'filter_virtual_post_clauses' ) ); |
478
|
|
|
} |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
// Stock status filter. |
482
|
|
|
if ( ! empty( $_GET['stock_status'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification |
483
|
|
|
add_filter( 'posts_clauses', array( $this, 'filter_stock_status_post_clauses' ) ); |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
// Shipping class taxonomy. |
487
|
|
|
if ( ! empty( $_GET['product_shipping_class'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification |
488
|
|
|
$query_vars['tax_query'][] = array( |
489
|
|
|
'taxonomy' => 'product_shipping_class', |
490
|
|
|
'field' => 'slug', |
491
|
|
|
'terms' => sanitize_title( wp_unslash( $_GET['product_shipping_class'] ) ), |
492
|
|
|
'operator' => 'IN', |
493
|
|
|
); |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
// Search using CRUD. |
497
|
|
|
if ( ! empty( $query_vars['s'] ) ) { |
498
|
|
|
$data_store = WC_Data_Store::load( 'product' ); |
499
|
|
|
$ids = $data_store->search_products( wc_clean( wp_unslash( $query_vars['s'] ) ), '', true, true ); |
|
|
|
|
500
|
|
|
$query_vars['post__in'] = array_merge( $ids, array( 0 ) ); |
501
|
|
|
$query_vars['product_search'] = true; |
502
|
|
|
unset( $query_vars['s'] ); |
503
|
|
|
} |
504
|
|
|
|
505
|
|
|
return $query_vars; |
506
|
|
|
} |
507
|
|
|
|
508
|
|
|
/** |
509
|
|
|
* Undocumented function |
510
|
|
|
* |
511
|
|
|
* @param array $args Array of SELECT statement pieces (from, where, etc). |
512
|
|
|
* @param WP_Query $query WP_Query instance. |
513
|
|
|
* @return array |
514
|
|
|
*/ |
515
|
|
|
public function posts_clauses( $args, $query ) { |
|
|
|
|
516
|
|
|
|
517
|
|
|
return $args; |
518
|
|
|
} |
519
|
|
|
|
520
|
|
|
/** |
521
|
|
|
* Remove ordering queries. |
522
|
|
|
* |
523
|
|
|
* @param array $posts Posts array, keeping this for backwards compatibility defaulting to empty array. |
524
|
|
|
* @return array |
525
|
|
|
*/ |
526
|
|
|
public function remove_ordering_args( $posts = array() ) { |
527
|
|
|
remove_filter( 'posts_clauses', array( $this, 'order_by_price_asc_post_clauses' ) ); |
528
|
|
|
remove_filter( 'posts_clauses', array( $this, 'order_by_price_desc_post_clauses' ) ); |
529
|
|
|
remove_filter( 'posts_clauses', array( $this, 'order_by_sku_asc_post_clauses' ) ); |
530
|
|
|
remove_filter( 'posts_clauses', array( $this, 'order_by_sku_desc_post_clauses' ) ); |
531
|
|
|
remove_filter( 'posts_clauses', array( $this, 'filter_downloadable_post_clauses' ) ); |
532
|
|
|
remove_filter( 'posts_clauses', array( $this, 'filter_virtual_post_clauses' ) ); |
533
|
|
|
remove_filter( 'posts_clauses', array( $this, 'filter_stock_status_post_clauses' ) ); |
534
|
|
|
return $posts; // Keeping this here for backward compatibility. |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
/** |
538
|
|
|
* Handle numeric price sorting. |
539
|
|
|
* |
540
|
|
|
* @param array $args Query args. |
541
|
|
|
* @return array |
542
|
|
|
*/ |
543
|
|
|
public function order_by_price_asc_post_clauses( $args ) { |
544
|
|
|
$args['join'] = $this->append_product_sorting_table_join( $args['join'] ); |
545
|
|
|
$args['orderby'] = ' wc_product_meta_lookup.min_price ASC, wc_product_meta_lookup.product_id ASC '; |
546
|
|
|
return $args; |
547
|
|
|
} |
548
|
|
|
|
549
|
|
|
/** |
550
|
|
|
* Handle numeric price sorting. |
551
|
|
|
* |
552
|
|
|
* @param array $args Query args. |
553
|
|
|
* @return array |
554
|
|
|
*/ |
555
|
|
|
public function order_by_price_desc_post_clauses( $args ) { |
556
|
|
|
$args['join'] = $this->append_product_sorting_table_join( $args['join'] ); |
557
|
|
|
$args['orderby'] = ' wc_product_meta_lookup.max_price DESC, wc_product_meta_lookup.product_id DESC '; |
558
|
|
|
return $args; |
559
|
|
|
} |
560
|
|
|
|
561
|
|
|
/** |
562
|
|
|
* Handle sku sorting. |
563
|
|
|
* |
564
|
|
|
* @param array $args Query args. |
565
|
|
|
* @return array |
566
|
|
|
*/ |
567
|
|
|
public function order_by_sku_asc_post_clauses( $args ) { |
568
|
|
|
$args['join'] = $this->append_product_sorting_table_join( $args['join'] ); |
569
|
|
|
$args['orderby'] = ' wc_product_meta_lookup.sku ASC, wc_product_meta_lookup.product_id ASC '; |
570
|
|
|
return $args; |
571
|
|
|
} |
572
|
|
|
|
573
|
|
|
/** |
574
|
|
|
* Handle sku sorting. |
575
|
|
|
* |
576
|
|
|
* @param array $args Query args. |
577
|
|
|
* @return array |
578
|
|
|
*/ |
579
|
|
|
public function order_by_sku_desc_post_clauses( $args ) { |
580
|
|
|
$args['join'] = $this->append_product_sorting_table_join( $args['join'] ); |
581
|
|
|
$args['orderby'] = ' wc_product_meta_lookup.sku DESC, wc_product_meta_lookup.product_id DESC '; |
582
|
|
|
return $args; |
583
|
|
|
} |
584
|
|
|
|
585
|
|
|
/** |
586
|
|
|
* Filter by type. |
587
|
|
|
* |
588
|
|
|
* @param array $args Query args. |
589
|
|
|
* @return array |
590
|
|
|
*/ |
591
|
|
|
public function filter_downloadable_post_clauses( $args ) { |
592
|
|
|
$args['join'] = $this->append_product_sorting_table_join( $args['join'] ); |
593
|
|
|
$args['where'] .= ' AND wc_product_meta_lookup.downloadable=1 '; |
594
|
|
|
return $args; |
595
|
|
|
} |
596
|
|
|
|
597
|
|
|
/** |
598
|
|
|
* Filter by type. |
599
|
|
|
* |
600
|
|
|
* @param array $args Query args. |
601
|
|
|
* @return array |
602
|
|
|
*/ |
603
|
|
|
public function filter_virtual_post_clauses( $args ) { |
604
|
|
|
$args['join'] = $this->append_product_sorting_table_join( $args['join'] ); |
605
|
|
|
$args['where'] .= ' AND wc_product_meta_lookup.virtual=1 '; |
606
|
|
|
return $args; |
607
|
|
|
} |
608
|
|
|
|
609
|
|
|
/** |
610
|
|
|
* Filter by stock status. |
611
|
|
|
* |
612
|
|
|
* @param array $args Query args. |
613
|
|
|
* @return array |
614
|
|
|
*/ |
615
|
|
|
public function filter_stock_status_post_clauses( $args ) { |
616
|
|
|
global $wpdb; |
617
|
|
|
if ( ! empty( $_GET['stock_status'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification |
618
|
|
|
$args['join'] = $this->append_product_sorting_table_join( $args['join'] ); |
619
|
|
|
$args['where'] .= $wpdb->prepare( ' AND wc_product_meta_lookup.stock_status=%s ', wc_clean( wp_unslash( $_GET['stock_status'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification |
620
|
|
|
} |
621
|
|
|
return $args; |
622
|
|
|
} |
623
|
|
|
|
624
|
|
|
/** |
625
|
|
|
* Join wc_product_meta_lookup to posts if not already joined. |
626
|
|
|
* |
627
|
|
|
* @param string $sql SQL join. |
628
|
|
|
* @return string |
629
|
|
|
*/ |
630
|
|
View Code Duplication |
private function append_product_sorting_table_join( $sql ) { |
|
|
|
|
631
|
|
|
global $wpdb; |
632
|
|
|
|
633
|
|
|
if ( ! strstr( $sql, 'wc_product_meta_lookup' ) ) { |
634
|
|
|
$sql .= " LEFT JOIN {$wpdb->wc_product_meta_lookup} wc_product_meta_lookup ON $wpdb->posts.ID = wc_product_meta_lookup.product_id "; |
635
|
|
|
} |
636
|
|
|
return $sql; |
637
|
|
|
} |
638
|
|
|
|
639
|
|
|
/** |
640
|
|
|
* Modifies post query so that it includes parent products whose variations have particular shipping class assigned. |
641
|
|
|
* |
642
|
|
|
* @param array $pieces Array of SELECT statement pieces (from, where, etc). |
643
|
|
|
* @param WP_Query $wp_query WP_Query instance. |
644
|
|
|
* @return array Array of products, including parents of variations. |
645
|
|
|
*/ |
646
|
|
|
public function add_variation_parents_for_shipping_class( $pieces, $wp_query ) { |
|
|
|
|
647
|
|
|
global $wpdb; |
648
|
|
|
if ( isset( $_GET['product_shipping_class'] ) && '0' !== $_GET['product_shipping_class'] ) { // WPCS: input var ok. |
649
|
|
|
$replaced_where = str_replace( ".post_type = 'product'", ".post_type = 'product_variation'", $pieces['where'] ); |
650
|
|
|
$pieces['where'] .= " OR {$wpdb->posts}.ID in ( |
651
|
|
|
SELECT {$wpdb->posts}.post_parent FROM |
652
|
|
|
{$wpdb->posts} LEFT JOIN {$wpdb->term_relationships} ON ({$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id) |
653
|
|
|
WHERE 1=1 $replaced_where |
654
|
|
|
)"; |
655
|
|
|
return $pieces; |
656
|
|
|
} |
657
|
|
|
return $pieces; |
658
|
|
|
} |
659
|
|
|
|
660
|
|
|
} |
661
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.