1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* WooCommerce Product Functions |
4
|
|
|
* |
5
|
|
|
* Functions for product specific things. |
6
|
|
|
* |
7
|
|
|
* @author WooThemes |
8
|
|
|
* @category Core |
9
|
|
|
* @package WooCommerce/Functions |
10
|
|
|
* @version 2.3.0 |
11
|
|
|
*/ |
12
|
|
|
|
13
|
|
|
if ( ! defined( 'ABSPATH' ) ) { |
14
|
|
|
exit; // Exit if accessed directly |
15
|
|
|
} |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Main function for returning products, uses the WC_Product_Factory class. |
19
|
|
|
* |
20
|
|
|
* @param mixed $the_product Post object or post ID of the product. |
21
|
|
|
* @param array $args (default: array()) Contains all arguments to be used to get this product. |
22
|
|
|
* @return WC_Product |
23
|
|
|
*/ |
24
|
|
View Code Duplication |
function wc_get_product( $the_product = false, $args = array() ) { |
|
|
|
|
25
|
|
|
if ( ! did_action( 'woocommerce_init' ) ) { |
26
|
|
|
_doing_it_wrong( __FUNCTION__, __( 'wc_get_product should not be called before the woocommerce_init action.', 'woocommerce' ), '2.5' ); |
27
|
|
|
return false; |
28
|
|
|
} |
29
|
|
|
return WC()->product_factory->get_product( $the_product, $args ); |
30
|
|
|
} |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Update a product's stock amount. |
34
|
|
|
* |
35
|
|
|
* @param int $product_id |
36
|
|
|
* @param int $new_stock_level |
37
|
|
|
*/ |
38
|
|
|
function wc_update_product_stock( $product_id, $new_stock_level ) { |
39
|
|
|
$product = wc_get_product( $product_id ); |
40
|
|
|
|
41
|
|
|
if ( ! metadata_exists( 'post', $product_id, '_stock' ) || $product->get_stock_quantity() !== $new_stock_level ) { |
42
|
|
|
$product->set_stock( $new_stock_level ); |
43
|
|
|
} |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Update a product's stock status. |
48
|
|
|
* |
49
|
|
|
* @param int $product_id |
50
|
|
|
* @param int $status |
51
|
|
|
*/ |
52
|
|
|
function wc_update_product_stock_status( $product_id, $status ) { |
53
|
|
|
$product = wc_get_product( $product_id ); |
54
|
|
|
if ( $product ) { |
55
|
|
|
$product->set_stock_status( $status ); |
56
|
|
|
} |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Returns whether or not SKUS are enabled. |
61
|
|
|
* @return bool |
62
|
|
|
*/ |
63
|
|
|
function wc_product_sku_enabled() { |
64
|
|
|
return apply_filters( 'wc_product_sku_enabled', true ); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Returns whether or not product weights are enabled. |
69
|
|
|
* @return bool |
70
|
|
|
*/ |
71
|
|
|
function wc_product_weight_enabled() { |
72
|
|
|
return apply_filters( 'wc_product_weight_enabled', true ); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Returns whether or not product dimensions (HxWxD) are enabled. |
77
|
|
|
* @return bool |
78
|
|
|
*/ |
79
|
|
|
function wc_product_dimensions_enabled() { |
80
|
|
|
return apply_filters( 'wc_product_dimensions_enabled', true ); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Clear all transients cache for product data. |
85
|
|
|
* |
86
|
|
|
* @param int $post_id (default: 0) |
87
|
|
|
*/ |
88
|
|
|
function wc_delete_product_transients( $post_id = 0 ) { |
89
|
|
|
// Core transients |
90
|
|
|
$transients_to_clear = array( |
91
|
|
|
'wc_products_onsale', |
92
|
|
|
'wc_featured_products', |
93
|
|
|
'wc_outofstock_count', |
94
|
|
|
'wc_low_stock_count' |
95
|
|
|
); |
96
|
|
|
|
97
|
|
|
// Transient names that include an ID |
98
|
|
|
$post_transient_names = array( |
99
|
|
|
'wc_product_children_', |
100
|
|
|
'wc_product_total_stock_', |
101
|
|
|
'wc_var_prices_', |
102
|
|
|
'wc_related_' |
103
|
|
|
); |
104
|
|
|
|
105
|
|
|
if ( $post_id > 0 ) { |
106
|
|
|
foreach( $post_transient_names as $transient ) { |
107
|
|
|
$transients_to_clear[] = $transient . $post_id; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
// Does this product have a parent? |
111
|
|
|
if ( $parent_id = wp_get_post_parent_id( $post_id ) ) { |
112
|
|
|
wc_delete_product_transients( $parent_id ); |
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
// Delete transients |
117
|
|
|
foreach( $transients_to_clear as $transient ) { |
118
|
|
|
delete_transient( $transient ); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
// Increments the transient version to invalidate cache |
122
|
|
|
WC_Cache_Helper::get_transient_version( 'product', true ); |
123
|
|
|
|
124
|
|
|
do_action( 'woocommerce_delete_product_transients', $post_id ); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Function that returns an array containing the IDs of the products that are on sale. |
129
|
|
|
* |
130
|
|
|
* @since 2.0 |
131
|
|
|
* @access public |
132
|
|
|
* @return array |
133
|
|
|
*/ |
134
|
|
|
function wc_get_product_ids_on_sale() { |
135
|
|
|
global $wpdb; |
136
|
|
|
|
137
|
|
|
// Load from cache |
138
|
|
|
$product_ids_on_sale = get_transient( 'wc_products_onsale' ); |
139
|
|
|
|
140
|
|
|
// Valid cache found |
141
|
|
|
if ( false !== $product_ids_on_sale ) { |
142
|
|
|
return $product_ids_on_sale; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
$on_sale_posts = $wpdb->get_results( " |
146
|
|
|
SELECT post.ID, post.post_parent FROM `$wpdb->posts` AS post |
147
|
|
|
LEFT JOIN `$wpdb->postmeta` AS meta ON post.ID = meta.post_id |
148
|
|
|
LEFT JOIN `$wpdb->postmeta` AS meta2 ON post.ID = meta2.post_id |
149
|
|
|
WHERE post.post_type IN ( 'product', 'product_variation' ) |
150
|
|
|
AND post.post_status = 'publish' |
151
|
|
|
AND meta.meta_key = '_sale_price' |
152
|
|
|
AND meta2.meta_key = '_price' |
153
|
|
|
AND CAST( meta.meta_value AS DECIMAL ) >= 0 |
154
|
|
|
AND CAST( meta.meta_value AS CHAR ) != '' |
155
|
|
|
AND CAST( meta.meta_value AS DECIMAL ) = CAST( meta2.meta_value AS DECIMAL ) |
156
|
|
|
GROUP BY post.ID; |
157
|
|
|
" ); |
158
|
|
|
|
159
|
|
|
$product_ids_on_sale = array_unique( array_map( 'absint', array_merge( wp_list_pluck( $on_sale_posts, 'ID' ), array_diff( wp_list_pluck( $on_sale_posts, 'post_parent' ), array( 0 ) ) ) ) ); |
160
|
|
|
|
161
|
|
|
set_transient( 'wc_products_onsale', $product_ids_on_sale, DAY_IN_SECONDS * 30 ); |
162
|
|
|
|
163
|
|
|
return $product_ids_on_sale; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* Function that returns an array containing the IDs of the featured products. |
168
|
|
|
* |
169
|
|
|
* @since 2.1 |
170
|
|
|
* @access public |
171
|
|
|
* @return array |
172
|
|
|
*/ |
173
|
|
|
function wc_get_featured_product_ids() { |
174
|
|
|
|
175
|
|
|
// Load from cache |
176
|
|
|
$featured_product_ids = get_transient( 'wc_featured_products' ); |
177
|
|
|
|
178
|
|
|
// Valid cache found |
179
|
|
|
if ( false !== $featured_product_ids ) |
180
|
|
|
return $featured_product_ids; |
181
|
|
|
|
182
|
|
|
$featured = get_posts( array( |
183
|
|
|
'post_type' => array( 'product', 'product_variation' ), |
184
|
|
|
'posts_per_page' => -1, |
185
|
|
|
'post_status' => 'publish', |
186
|
|
|
'meta_query' => array( |
187
|
|
|
array( |
188
|
|
|
'key' => '_visibility', |
189
|
|
|
'value' => array('catalog', 'visible'), |
190
|
|
|
'compare' => 'IN' |
191
|
|
|
), |
192
|
|
|
array( |
193
|
|
|
'key' => '_featured', |
194
|
|
|
'value' => 'yes' |
195
|
|
|
) |
196
|
|
|
), |
197
|
|
|
'fields' => 'id=>parent' |
198
|
|
|
) ); |
199
|
|
|
|
200
|
|
|
$product_ids = array_keys( $featured ); |
201
|
|
|
$parent_ids = array_values( array_filter( $featured ) ); |
202
|
|
|
$featured_product_ids = array_unique( array_merge( $product_ids, $parent_ids ) ); |
203
|
|
|
|
204
|
|
|
set_transient( 'wc_featured_products', $featured_product_ids, DAY_IN_SECONDS * 30 ); |
205
|
|
|
|
206
|
|
|
return $featured_product_ids; |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
/** |
210
|
|
|
* Filter to allow product_cat in the permalinks for products. |
211
|
|
|
* |
212
|
|
|
* @param string $permalink The existing permalink URL. |
213
|
|
|
* @param WP_Post $post |
214
|
|
|
* @return string |
215
|
|
|
*/ |
216
|
|
|
function wc_product_post_type_link( $permalink, $post ) { |
217
|
|
|
// Abort if post is not a product. |
218
|
|
|
if ( $post->post_type !== 'product' ) { |
219
|
|
|
return $permalink; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
// Abort early if the placeholder rewrite tag isn't in the generated URL. |
223
|
|
|
if ( false === strpos( $permalink, '%' ) ) { |
224
|
|
|
return $permalink; |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
// Get the custom taxonomy terms in use by this post. |
228
|
|
|
$terms = get_the_terms( $post->ID, 'product_cat' ); |
229
|
|
|
|
230
|
|
|
if ( ! empty( $terms ) ) { |
231
|
|
|
usort( $terms, '_usort_terms_by_ID' ); // order by ID |
232
|
|
|
|
233
|
|
|
$category_object = apply_filters( 'wc_product_post_type_link_product_cat', $terms[0], $terms, $post ); |
234
|
|
|
$category_object = get_term( $category_object, 'product_cat' ); |
235
|
|
|
$product_cat = $category_object->slug; |
236
|
|
|
|
237
|
|
|
if ( $category_object->parent ) { |
238
|
|
|
$ancestors = get_ancestors( $category_object->term_id, 'product_cat' ); |
239
|
|
|
foreach ( $ancestors as $ancestor ) { |
240
|
|
|
$ancestor_object = get_term( $ancestor, 'product_cat' ); |
241
|
|
|
$product_cat = $ancestor_object->slug . '/' . $product_cat; |
242
|
|
|
} |
243
|
|
|
} |
244
|
|
|
} else { |
245
|
|
|
// If no terms are assigned to this post, use a string instead (can't leave the placeholder there) |
246
|
|
|
$product_cat = _x( 'uncategorized', 'slug', 'woocommerce' ); |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
$find = array( |
250
|
|
|
'%year%', |
251
|
|
|
'%monthnum%', |
252
|
|
|
'%day%', |
253
|
|
|
'%hour%', |
254
|
|
|
'%minute%', |
255
|
|
|
'%second%', |
256
|
|
|
'%post_id%', |
257
|
|
|
'%category%', |
258
|
|
|
'%product_cat%' |
259
|
|
|
); |
260
|
|
|
|
261
|
|
|
$replace = array( |
262
|
|
|
date_i18n( 'Y', strtotime( $post->post_date ) ), |
263
|
|
|
date_i18n( 'm', strtotime( $post->post_date ) ), |
264
|
|
|
date_i18n( 'd', strtotime( $post->post_date ) ), |
265
|
|
|
date_i18n( 'H', strtotime( $post->post_date ) ), |
266
|
|
|
date_i18n( 'i', strtotime( $post->post_date ) ), |
267
|
|
|
date_i18n( 's', strtotime( $post->post_date ) ), |
268
|
|
|
$post->ID, |
269
|
|
|
$product_cat, |
270
|
|
|
$product_cat |
271
|
|
|
); |
272
|
|
|
|
273
|
|
|
$permalink = str_replace( $find, $replace, $permalink ); |
274
|
|
|
|
275
|
|
|
return $permalink; |
276
|
|
|
} |
277
|
|
|
add_filter( 'post_type_link', 'wc_product_post_type_link', 10, 2 ); |
278
|
|
|
|
279
|
|
|
|
280
|
|
|
/** |
281
|
|
|
* Get the placeholder image URL for products etc. |
282
|
|
|
* |
283
|
|
|
* @access public |
284
|
|
|
* @return string |
285
|
|
|
*/ |
286
|
|
|
function wc_placeholder_img_src() { |
287
|
|
|
return apply_filters( 'woocommerce_placeholder_img_src', WC()->plugin_url() . '/assets/images/placeholder.png' ); |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
/** |
291
|
|
|
* Get the placeholder image. |
292
|
|
|
* |
293
|
|
|
* @access public |
294
|
|
|
* @return string |
295
|
|
|
*/ |
296
|
|
|
function wc_placeholder_img( $size = 'shop_thumbnail' ) { |
297
|
|
|
$dimensions = wc_get_image_size( $size ); |
298
|
|
|
|
299
|
|
|
return apply_filters('woocommerce_placeholder_img', '<img src="' . wc_placeholder_img_src() . '" alt="' . esc_attr__( 'Placeholder', 'woocommerce' ) . '" width="' . esc_attr( $dimensions['width'] ) . '" class="woocommerce-placeholder wp-post-image" height="' . esc_attr( $dimensions['height'] ) . '" />', $size, $dimensions ); |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* Variation Formatting. |
304
|
|
|
* |
305
|
|
|
* Gets a formatted version of variation data or item meta. |
306
|
|
|
* |
307
|
|
|
* @access public |
308
|
|
|
* @param string $variation |
309
|
|
|
* @param bool $flat (default: false) |
310
|
|
|
* @return string |
311
|
|
|
*/ |
312
|
|
|
function wc_get_formatted_variation( $variation, $flat = false ) { |
313
|
|
|
$return = ''; |
314
|
|
|
if ( is_array( $variation ) ) { |
315
|
|
|
|
316
|
|
|
if ( ! $flat ) { |
317
|
|
|
$return = '<dl class="variation">'; |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
$variation_list = array(); |
321
|
|
|
|
322
|
|
|
foreach ( $variation as $name => $value ) { |
323
|
|
|
if ( ! $value ) { |
324
|
|
|
continue; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
// If this is a term slug, get the term's nice name |
328
|
|
|
if ( taxonomy_exists( esc_attr( str_replace( 'attribute_', '', $name ) ) ) ) { |
329
|
|
|
$term = get_term_by( 'slug', $value, esc_attr( str_replace( 'attribute_', '', $name ) ) ); |
330
|
|
|
if ( ! is_wp_error( $term ) && ! empty( $term->name ) ) { |
331
|
|
|
$value = $term->name; |
332
|
|
|
} |
333
|
|
|
} else { |
334
|
|
|
$value = ucwords( str_replace( '-', ' ', $value ) ); |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
if ( $flat ) { |
338
|
|
|
$variation_list[] = wc_attribute_label( str_replace( 'attribute_', '', $name ) ) . ': ' . rawurldecode( $value ); |
339
|
|
|
} else { |
340
|
|
|
$variation_list[] = '<dt>' . wc_attribute_label( str_replace( 'attribute_', '', $name ) ) . ':</dt><dd>' . rawurldecode( $value ) . '</dd>'; |
341
|
|
|
} |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
if ( $flat ) { |
345
|
|
|
$return .= implode( ', ', $variation_list ); |
346
|
|
|
} else { |
347
|
|
|
$return .= implode( '', $variation_list ); |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
if ( ! $flat ) { |
351
|
|
|
$return .= '</dl>'; |
352
|
|
|
} |
353
|
|
|
} |
354
|
|
|
return $return; |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
/** |
358
|
|
|
* Function which handles the start and end of scheduled sales via cron. |
359
|
|
|
* |
360
|
|
|
* @access public |
361
|
|
|
*/ |
362
|
|
|
function wc_scheduled_sales() { |
363
|
|
|
global $wpdb; |
364
|
|
|
|
365
|
|
|
// Sales which are due to start |
366
|
|
|
$product_ids = $wpdb->get_col( $wpdb->prepare( " |
367
|
|
|
SELECT postmeta.post_id FROM {$wpdb->postmeta} as postmeta |
368
|
|
|
LEFT JOIN {$wpdb->postmeta} as postmeta_2 ON postmeta.post_id = postmeta_2.post_id |
369
|
|
|
LEFT JOIN {$wpdb->postmeta} as postmeta_3 ON postmeta.post_id = postmeta_3.post_id |
370
|
|
|
WHERE postmeta.meta_key = '_sale_price_dates_from' |
371
|
|
|
AND postmeta_2.meta_key = '_price' |
372
|
|
|
AND postmeta_3.meta_key = '_sale_price' |
373
|
|
|
AND postmeta.meta_value > 0 |
374
|
|
|
AND postmeta.meta_value < %s |
375
|
|
|
AND postmeta_2.meta_value != postmeta_3.meta_value |
376
|
|
|
", current_time( 'timestamp' ) ) ); |
377
|
|
|
|
378
|
|
|
if ( $product_ids ) { |
379
|
|
View Code Duplication |
foreach ( $product_ids as $product_id ) { |
|
|
|
|
380
|
|
|
$sale_price = get_post_meta( $product_id, '_sale_price', true ); |
381
|
|
|
|
382
|
|
|
if ( $sale_price ) { |
383
|
|
|
update_post_meta( $product_id, '_price', $sale_price ); |
384
|
|
|
} else { |
385
|
|
|
// No sale price! |
386
|
|
|
update_post_meta( $product_id, '_sale_price_dates_from', '' ); |
387
|
|
|
update_post_meta( $product_id, '_sale_price_dates_to', '' ); |
388
|
|
|
} |
389
|
|
|
|
390
|
|
|
$parent = wp_get_post_parent_id( $product_id ); |
391
|
|
|
|
392
|
|
|
// Sync parent |
393
|
|
|
if ( $parent ) { |
394
|
|
|
// Clear prices transient for variable products. |
395
|
|
|
delete_transient( 'wc_var_prices_' . $parent ); |
396
|
|
|
|
397
|
|
|
// Grouped products need syncing via a function |
398
|
|
|
$this_product = wc_get_product( $product_id ); |
399
|
|
|
|
400
|
|
|
if ( $this_product->is_type( 'simple' ) ) { |
401
|
|
|
$this_product->grouped_product_sync(); |
402
|
|
|
} |
403
|
|
|
} |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
delete_transient( 'wc_products_onsale' ); |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
// Sales which are due to end |
410
|
|
|
$product_ids = $wpdb->get_col( $wpdb->prepare( " |
411
|
|
|
SELECT postmeta.post_id FROM {$wpdb->postmeta} as postmeta |
412
|
|
|
LEFT JOIN {$wpdb->postmeta} as postmeta_2 ON postmeta.post_id = postmeta_2.post_id |
413
|
|
|
LEFT JOIN {$wpdb->postmeta} as postmeta_3 ON postmeta.post_id = postmeta_3.post_id |
414
|
|
|
WHERE postmeta.meta_key = '_sale_price_dates_to' |
415
|
|
|
AND postmeta_2.meta_key = '_price' |
416
|
|
|
AND postmeta_3.meta_key = '_regular_price' |
417
|
|
|
AND postmeta.meta_value > 0 |
418
|
|
|
AND postmeta.meta_value < %s |
419
|
|
|
AND postmeta_2.meta_value != postmeta_3.meta_value |
420
|
|
|
", current_time( 'timestamp' ) ) ); |
421
|
|
|
|
422
|
|
|
if ( $product_ids ) { |
423
|
|
View Code Duplication |
foreach ( $product_ids as $product_id ) { |
|
|
|
|
424
|
|
|
$regular_price = get_post_meta( $product_id, '_regular_price', true ); |
425
|
|
|
|
426
|
|
|
update_post_meta( $product_id, '_price', $regular_price ); |
427
|
|
|
update_post_meta( $product_id, '_sale_price', '' ); |
428
|
|
|
update_post_meta( $product_id, '_sale_price_dates_from', '' ); |
429
|
|
|
update_post_meta( $product_id, '_sale_price_dates_to', '' ); |
430
|
|
|
|
431
|
|
|
$parent = wp_get_post_parent_id( $product_id ); |
432
|
|
|
|
433
|
|
|
// Sync parent |
434
|
|
|
if ( $parent ) { |
435
|
|
|
// Grouped products need syncing via a function |
436
|
|
|
$this_product = wc_get_product( $product_id ); |
437
|
|
|
if ( $this_product->is_type( 'simple' ) ) { |
438
|
|
|
$this_product->grouped_product_sync(); |
439
|
|
|
} |
440
|
|
|
} |
441
|
|
|
} |
442
|
|
|
|
443
|
|
|
WC_Cache_Helper::get_transient_version( 'product', true ); |
444
|
|
|
delete_transient( 'wc_products_onsale' ); |
445
|
|
|
} |
446
|
|
|
} |
447
|
|
|
add_action( 'woocommerce_scheduled_sales', 'wc_scheduled_sales' ); |
448
|
|
|
|
449
|
|
|
/** |
450
|
|
|
* Get attachment image attributes. |
451
|
|
|
* |
452
|
|
|
* @access public |
453
|
|
|
* @param array $attr |
454
|
|
|
* @return array |
455
|
|
|
*/ |
456
|
|
|
function wc_get_attachment_image_attributes( $attr ) { |
457
|
|
|
if ( strstr( $attr['src'], 'woocommerce_uploads/' ) ) { |
458
|
|
|
$attr['src'] = wc_placeholder_img_src(); |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
return $attr; |
462
|
|
|
} |
463
|
|
|
add_filter( 'wp_get_attachment_image_attributes', 'wc_get_attachment_image_attributes' ); |
464
|
|
|
|
465
|
|
|
|
466
|
|
|
/** |
467
|
|
|
* Prepare attachment for JavaScript. |
468
|
|
|
* |
469
|
|
|
* @access public |
470
|
|
|
* @param array $response |
471
|
|
|
* @return array |
472
|
|
|
*/ |
473
|
|
|
function wc_prepare_attachment_for_js( $response ) { |
474
|
|
|
|
475
|
|
|
if ( isset( $response['url'] ) && strstr( $response['url'], 'woocommerce_uploads/' ) ) { |
476
|
|
|
$response['full']['url'] = wc_placeholder_img_src(); |
477
|
|
|
if ( isset( $response['sizes'] ) ) { |
478
|
|
|
foreach( $response['sizes'] as $size => $value ) { |
479
|
|
|
$response['sizes'][ $size ]['url'] = wc_placeholder_img_src(); |
480
|
|
|
} |
481
|
|
|
} |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
return $response; |
485
|
|
|
} |
486
|
|
|
add_filter( 'wp_prepare_attachment_for_js', 'wc_prepare_attachment_for_js' ); |
487
|
|
|
|
488
|
|
|
/** |
489
|
|
|
* Track product views. |
490
|
|
|
*/ |
491
|
|
|
function wc_track_product_view() { |
492
|
|
|
if ( ! is_singular( 'product' ) || ! is_active_widget( false, false, 'woocommerce_recently_viewed_products', true ) ) { |
493
|
|
|
return; |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
global $post; |
497
|
|
|
|
498
|
|
|
if ( empty( $_COOKIE['woocommerce_recently_viewed'] ) ) |
499
|
|
|
$viewed_products = array(); |
500
|
|
|
else |
501
|
|
|
$viewed_products = (array) explode( '|', $_COOKIE['woocommerce_recently_viewed'] ); |
502
|
|
|
|
503
|
|
|
if ( ! in_array( $post->ID, $viewed_products ) ) { |
504
|
|
|
$viewed_products[] = $post->ID; |
505
|
|
|
} |
506
|
|
|
|
507
|
|
|
if ( sizeof( $viewed_products ) > 15 ) { |
508
|
|
|
array_shift( $viewed_products ); |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
// Store for session only |
512
|
|
|
wc_setcookie( 'woocommerce_recently_viewed', implode( '|', $viewed_products ) ); |
513
|
|
|
} |
514
|
|
|
|
515
|
|
|
add_action( 'template_redirect', 'wc_track_product_view', 20 ); |
516
|
|
|
|
517
|
|
|
/** |
518
|
|
|
* Get product types. |
519
|
|
|
* |
520
|
|
|
* @since 2.2 |
521
|
|
|
* @return array |
522
|
|
|
*/ |
523
|
|
|
function wc_get_product_types() { |
524
|
|
|
return (array) apply_filters( 'product_type_selector', array( |
525
|
|
|
'simple' => __( 'Simple product', 'woocommerce' ), |
526
|
|
|
'grouped' => __( 'Grouped product', 'woocommerce' ), |
527
|
|
|
'external' => __( 'External/Affiliate product', 'woocommerce' ), |
528
|
|
|
'variable' => __( 'Variable product', 'woocommerce' ) |
529
|
|
|
) ); |
530
|
|
|
} |
531
|
|
|
|
532
|
|
|
/** |
533
|
|
|
* Check if product sku is unique. |
534
|
|
|
* |
535
|
|
|
* @since 2.2 |
536
|
|
|
* @param int $product_id |
537
|
|
|
* @param string $sku Will be slashed to work around https://core.trac.wordpress.org/ticket/27421 |
538
|
|
|
* @return bool |
539
|
|
|
*/ |
540
|
|
|
function wc_product_has_unique_sku( $product_id, $sku ) { |
541
|
|
|
global $wpdb; |
542
|
|
|
|
543
|
|
|
$sku_found = $wpdb->get_var( $wpdb->prepare( " |
544
|
|
|
SELECT $wpdb->posts.ID |
545
|
|
|
FROM $wpdb->posts |
546
|
|
|
LEFT JOIN $wpdb->postmeta ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id ) |
547
|
|
|
WHERE $wpdb->posts.post_type IN ( 'product', 'product_variation' ) |
548
|
|
|
AND $wpdb->posts.post_status = 'publish' |
549
|
|
|
AND $wpdb->postmeta.meta_key = '_sku' AND $wpdb->postmeta.meta_value = '%s' |
550
|
|
|
AND $wpdb->postmeta.post_id <> %d LIMIT 1 |
551
|
|
|
", wp_slash( $sku ), $product_id ) ); |
552
|
|
|
|
553
|
|
|
if ( apply_filters( 'wc_product_has_unique_sku', $sku_found, $product_id, $sku ) ) { |
554
|
|
|
return false; |
555
|
|
|
} else { |
556
|
|
|
return true; |
557
|
|
|
} |
558
|
|
|
} |
559
|
|
|
|
560
|
|
|
/** |
561
|
|
|
* Get product ID by SKU. |
562
|
|
|
* |
563
|
|
|
* @since 2.3.0 |
564
|
|
|
* @param string $sku |
565
|
|
|
* @return int |
566
|
|
|
*/ |
567
|
|
|
function wc_get_product_id_by_sku( $sku ) { |
568
|
|
|
global $wpdb; |
569
|
|
|
|
570
|
|
|
$product_id = $wpdb->get_var( $wpdb->prepare( " |
571
|
|
|
SELECT posts.ID |
572
|
|
|
FROM $wpdb->posts AS posts |
573
|
|
|
LEFT JOIN $wpdb->postmeta AS postmeta ON ( posts.ID = postmeta.post_id ) |
574
|
|
|
WHERE posts.post_type IN ( 'product', 'product_variation' ) |
575
|
|
|
AND postmeta.meta_key = '_sku' AND postmeta.meta_value = '%s' |
576
|
|
|
LIMIT 1 |
577
|
|
|
", $sku ) ); |
578
|
|
|
|
579
|
|
|
return ( $product_id ) ? intval( $product_id ) : 0; |
580
|
|
|
} |
581
|
|
|
|
582
|
|
|
/** |
583
|
|
|
* Save product price. |
584
|
|
|
* |
585
|
|
|
* This is a private function (internal use ONLY) used until a data manipulation api is built. |
586
|
|
|
* |
587
|
|
|
* @since 2.4.0 |
588
|
|
|
* @todo look into Data manipulation API |
589
|
|
|
* |
590
|
|
|
* @param int $product_id |
591
|
|
|
* @param float $regular_price |
592
|
|
|
* @param float $sale_price |
593
|
|
|
* @param string $date_from |
594
|
|
|
* @param string $date_to |
595
|
|
|
*/ |
596
|
|
|
function _wc_save_product_price( $product_id, $regular_price, $sale_price = '', $date_from = '', $date_to = '' ) { |
597
|
|
|
$product_id = absint( $product_id ); |
598
|
|
|
$regular_price = wc_format_decimal( $regular_price ); |
599
|
|
|
$sale_price = $sale_price === '' ? '' : wc_format_decimal( $sale_price ); |
600
|
|
|
$date_from = wc_clean( $date_from ); |
601
|
|
|
$date_to = wc_clean( $date_to ); |
602
|
|
|
|
603
|
|
|
update_post_meta( $product_id, '_regular_price', $regular_price ); |
604
|
|
|
update_post_meta( $product_id, '_sale_price', $sale_price ); |
605
|
|
|
|
606
|
|
|
// Save Dates |
607
|
|
|
update_post_meta( $product_id, '_sale_price_dates_from', $date_from ? strtotime( $date_from ) : '' ); |
608
|
|
|
update_post_meta( $product_id, '_sale_price_dates_to', $date_to ? strtotime( $date_to ) : '' ); |
609
|
|
|
|
610
|
|
|
if ( $date_to && ! $date_from ) { |
611
|
|
|
update_post_meta( $product_id, '_sale_price_dates_from', strtotime( 'NOW', current_time( 'timestamp' ) ) ); |
612
|
|
|
} |
613
|
|
|
|
614
|
|
|
// Update price if on sale |
615
|
|
View Code Duplication |
if ( '' !== $sale_price && '' === $date_to && '' === $date_from ) { |
|
|
|
|
616
|
|
|
update_post_meta( $product_id, '_price', $sale_price ); |
617
|
|
|
} else { |
618
|
|
|
update_post_meta( $product_id, '_price', $regular_price ); |
619
|
|
|
} |
620
|
|
|
|
621
|
|
View Code Duplication |
if ( '' !== $sale_price && $date_from && strtotime( $date_from ) < strtotime( 'NOW', current_time( 'timestamp' ) ) ) { |
|
|
|
|
622
|
|
|
update_post_meta( $product_id, '_price', $sale_price ); |
623
|
|
|
} |
624
|
|
|
|
625
|
|
View Code Duplication |
if ( $date_to && strtotime( $date_to ) < strtotime( 'NOW', current_time( 'timestamp' ) ) ) { |
|
|
|
|
626
|
|
|
update_post_meta( $product_id, '_price', $regular_price ); |
627
|
|
|
update_post_meta( $product_id, '_sale_price_dates_from', '' ); |
628
|
|
|
update_post_meta( $product_id, '_sale_price_dates_to', '' ); |
629
|
|
|
} |
630
|
|
|
} |
631
|
|
|
|
632
|
|
|
/** |
633
|
|
|
* Get attibutes/data for an individual variation from the database and maintain it's integrity. |
634
|
|
|
* @since 2.4.0 |
635
|
|
|
* @param int $variation_id |
636
|
|
|
* @return array |
637
|
|
|
*/ |
638
|
|
|
function wc_get_product_variation_attributes( $variation_id ) { |
639
|
|
|
// Build variation data from meta |
640
|
|
|
$all_meta = get_post_meta( $variation_id ); |
641
|
|
|
$parent_id = wp_get_post_parent_id( $variation_id ); |
642
|
|
|
$parent_attributes = array_filter( (array) get_post_meta( $parent_id, '_product_attributes', true ) ); |
643
|
|
|
$found_parent_attributes = array(); |
644
|
|
|
$variation_attributes = array(); |
645
|
|
|
|
646
|
|
|
// Compare to parent variable product attributes and ensure they match |
647
|
|
|
foreach ( $parent_attributes as $attribute_name => $options ) { |
648
|
|
|
if ( ! empty( $options['is_variation'] ) ) { |
649
|
|
|
$attribute = 'attribute_' . sanitize_title( $attribute_name ); |
650
|
|
|
$found_parent_attributes[] = $attribute; |
651
|
|
|
if ( ! array_key_exists( $attribute, $variation_attributes ) ) { |
652
|
|
|
$variation_attributes[ $attribute ] = ''; // Add it - 'any' will be asumed |
653
|
|
|
} |
654
|
|
|
} |
655
|
|
|
} |
656
|
|
|
|
657
|
|
|
// Get the variation attributes from meta |
658
|
|
|
foreach ( $all_meta as $name => $value ) { |
659
|
|
|
// Only look at valid attribute meta, and also compare variation level attributes and remove any which do not exist at parent level |
660
|
|
|
if ( 0 !== strpos( $name, 'attribute_' ) || ! in_array( $name, $found_parent_attributes ) ) { |
661
|
|
|
unset( $variation_attributes[ $name ] ); |
662
|
|
|
continue; |
663
|
|
|
} |
664
|
|
|
/** |
665
|
|
|
* Pre 2.4 handling where 'slugs' were saved instead of the full text attribute. |
666
|
|
|
* Attempt to get full version of the text attribute from the parent. |
667
|
|
|
*/ |
668
|
|
|
if ( sanitize_title( $value[0] ) === $value[0] && version_compare( get_post_meta( $parent_id, '_product_version', true ), '2.4.0', '<' ) ) { |
669
|
|
View Code Duplication |
foreach ( $parent_attributes as $attribute ) { |
|
|
|
|
670
|
|
|
if ( $name !== 'attribute_' . sanitize_title( $attribute['name'] ) ) { |
671
|
|
|
continue; |
672
|
|
|
} |
673
|
|
|
$text_attributes = wc_get_text_attributes( $attribute['value'] ); |
674
|
|
|
|
675
|
|
|
foreach ( $text_attributes as $text_attribute ) { |
676
|
|
|
if ( sanitize_title( $text_attribute ) === $value[0] ) { |
677
|
|
|
$value[0] = $text_attribute; |
678
|
|
|
break; |
679
|
|
|
} |
680
|
|
|
} |
681
|
|
|
} |
682
|
|
|
} |
683
|
|
|
|
684
|
|
|
$variation_attributes[ $name ] = $value[0]; |
685
|
|
|
} |
686
|
|
|
|
687
|
|
|
return $variation_attributes; |
688
|
|
|
} |
689
|
|
|
|
690
|
|
|
/** |
691
|
|
|
* Get all product cats for a product by ID, including hierarchy |
692
|
|
|
* @since 2.5.0 |
693
|
|
|
* @param int $product_id |
694
|
|
|
* @return array |
695
|
|
|
*/ |
696
|
|
|
function wc_get_product_cat_ids( $product_id ) { |
697
|
|
|
$product_cats = wp_get_post_terms( $product_id, 'product_cat', array( "fields" => "ids" ) ); |
698
|
|
|
|
699
|
|
|
foreach ( $product_cats as $product_cat ) { |
700
|
|
|
$product_cats = array_merge( $product_cats, get_ancestors( $product_cat, 'product_cat' ) ); |
701
|
|
|
} |
702
|
|
|
|
703
|
|
|
return $product_cats; |
704
|
|
|
} |
705
|
|
|
|
706
|
|
|
/** |
707
|
|
|
* Gets data about an attachment, such as alt text and captions. |
708
|
|
|
* @since 2.6.0 |
709
|
|
|
* @param object|bool $product |
710
|
|
|
* @return array |
711
|
|
|
*/ |
712
|
|
|
function wc_get_product_attachment_props( $attachment_id, $product = false ) { |
713
|
|
|
$props = array( |
714
|
|
|
'title' => '', |
715
|
|
|
'caption' => '', |
716
|
|
|
'url' => '', |
717
|
|
|
'alt' => '', |
718
|
|
|
); |
719
|
|
|
if ( $attachment_id ) { |
720
|
|
|
$attachment = get_post( $attachment_id ); |
721
|
|
|
$props['title'] = trim( strip_tags( $attachment->post_title ) ); |
722
|
|
|
$props['caption'] = trim( strip_tags( $attachment->post_excerpt ) ); |
723
|
|
|
$props['url'] = wp_get_attachment_url( $attachment_id ); |
724
|
|
|
$props['alt'] = trim( strip_tags( get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ) ); |
725
|
|
|
|
726
|
|
|
// Alt text fallbacks |
727
|
|
|
$props['alt'] = empty( $props['alt'] ) ? $props['caption'] : $props['alt']; |
728
|
|
|
$props['alt'] = empty( $props['alt'] ) ? trim( strip_tags( $attachment->post_title ) ) : $props['alt']; |
729
|
|
|
$props['alt'] = empty( $props['alt'] ) && $product ? trim( strip_tags( get_the_title( $product->ID ) ) ) : $props['alt']; |
730
|
|
|
} |
731
|
|
|
return $props; |
732
|
|
|
} |
733
|
|
|
|
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.