Completed
Push — master ( 86a0ff...73fdfe )
by Mike
07:41
created

WC_Shortcodes::order_tracking()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 16 and the first side effect is on line 4.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
if ( ! defined( 'ABSPATH' ) ) {
4
	exit; // Exit if accessed directly
5
}
6
7
/**
8
 * WC_Shortcodes class
9
 *
10
 * @class       WC_Shortcodes
11
 * @version     2.1.0
12
 * @package     WooCommerce/Classes
13
 * @category    Class
14
 * @author      WooThemes
15
 */
16
class WC_Shortcodes {
17
18
	/**
19
	 * Init shortcodes.
20
	 */
21
	public static function init() {
22
		$shortcodes = array(
23
			'product'                    => __CLASS__ . '::product',
24
			'product_page'               => __CLASS__ . '::product_page',
25
			'product_category'           => __CLASS__ . '::product_category',
26
			'product_categories'         => __CLASS__ . '::product_categories',
27
			'add_to_cart'                => __CLASS__ . '::product_add_to_cart',
28
			'add_to_cart_url'            => __CLASS__ . '::product_add_to_cart_url',
29
			'products'                   => __CLASS__ . '::products',
30
			'recent_products'            => __CLASS__ . '::recent_products',
31
			'sale_products'              => __CLASS__ . '::sale_products',
32
			'best_selling_products'      => __CLASS__ . '::best_selling_products',
33
			'top_rated_products'         => __CLASS__ . '::top_rated_products',
34
			'featured_products'          => __CLASS__ . '::featured_products',
35
			'product_attribute'          => __CLASS__ . '::product_attribute',
36
			'related_products'           => __CLASS__ . '::related_products',
37
			'shop_messages'              => __CLASS__ . '::shop_messages',
38
			'woocommerce_order_tracking' => __CLASS__ . '::order_tracking',
39
			'woocommerce_cart'           => __CLASS__ . '::cart',
40
			'woocommerce_checkout'       => __CLASS__ . '::checkout',
41
			'woocommerce_my_account'     => __CLASS__ . '::my_account',
42
		);
43
44
		foreach ( $shortcodes as $shortcode => $function ) {
45
			add_shortcode( apply_filters( "{$shortcode}_shortcode_tag", $shortcode ), $function );
46
		}
47
48
		// Alias for pre 2.1 compatibility
49
		add_shortcode( 'woocommerce_messages', __CLASS__ . '::shop_messages' );
50
	}
51
52
	/**
53
	 * Shortcode Wrapper.
54
	 *
55
	 * @param string[] $function
56
	 * @param array $atts (default: array())
57
	 * @return string
58
	 */
59
	public static function shortcode_wrapper(
60
		$function,
61
		$atts    = array(),
62
		$wrapper = array(
63
			'class'  => 'woocommerce',
64
			'before' => null,
65
			'after'  => null
66
		)
67
	) {
68
		ob_start();
69
70
		echo empty( $wrapper['before'] ) ? '<div class="' . esc_attr( $wrapper['class'] ) . '">' : $wrapper['before'];
71
		call_user_func( $function, $atts );
72
		echo empty( $wrapper['after'] ) ? '</div>' : $wrapper['after'];
73
74
		return ob_get_clean();
75
	}
76
77
	/**
78
	 * Loop over found products.
79
	 * @param  array $query_args
80
	 * @param  array $atts
81
	 * @param  string $loop_name
82
	 * @return string
83
	 */
84
	private static function product_loop( $query_args, $atts, $loop_name ) {
85
		global $woocommerce_loop;
86
87
		$products                    = new WP_Query( apply_filters( 'woocommerce_shortcode_products_query', $query_args, $atts, $loop_name ) );
88
		$columns                     = absint( $atts['columns'] );
89
		$woocommerce_loop['columns'] = $columns;
90
		$woocommerce_loop['name']    = $loop_name;
91
92
		ob_start();
93
94
		if ( $products->have_posts() ) : ?>
95
96
			<?php do_action( "woocommerce_shortcode_before_{$loop_name}_loop" ); ?>
97
98
			<?php woocommerce_product_loop_start(); ?>
99
100
				<?php while ( $products->have_posts() ) : $products->the_post(); ?>
101
102
					<?php wc_get_template_part( 'content', 'product' ); ?>
103
104
				<?php endwhile; // end of the loop. ?>
105
106
			<?php woocommerce_product_loop_end(); ?>
107
108
			<?php do_action( "woocommerce_shortcode_after_{$loop_name}_loop" ); ?>
109
110
		<?php endif;
111
112
		woocommerce_reset_loop();
113
		wp_reset_postdata();
114
115
		return '<div class="woocommerce columns-' . $columns . '">' . ob_get_clean() . '</div>';
116
	}
117
118
	/**
119
	 * Cart page shortcode.
120
	 *
121
	 * @return string
122
	 */
123
	public static function cart() {
124
		return is_null( WC()->cart ) ? '' : self::shortcode_wrapper( array( 'WC_Shortcode_Cart', 'output' ) );
125
	}
126
127
	/**
128
	 * Checkout page shortcode.
129
	 *
130
	 * @param mixed $atts
131
	 * @return string
132
	 */
133
	public static function checkout( $atts ) {
134
		return self::shortcode_wrapper( array( 'WC_Shortcode_Checkout', 'output' ), $atts );
135
	}
136
137
	/**
138
	 * Order tracking page shortcode.
139
	 *
140
	 * @param mixed $atts
141
	 * @return string
142
	 */
143
	public static function order_tracking( $atts ) {
144
		return self::shortcode_wrapper( array( 'WC_Shortcode_Order_Tracking', 'output' ), $atts );
145
	}
146
147
	/**
148
	 * Cart shortcode.
149
	 *
150
	 * @param mixed $atts
151
	 * @return string
152
	 */
153
	public static function my_account( $atts ) {
154
		return self::shortcode_wrapper( array( 'WC_Shortcode_My_Account', 'output' ), $atts );
155
	}
156
157
	/**
158
	 * List products in a category shortcode.
159
	 *
160
	 * @param array $atts
161
	 * @return string
162
	 */
163
	public static function product_category( $atts ) {
164
		$atts = shortcode_atts( array(
165
			'per_page' => '12',
166
			'columns'  => '4',
167
			'orderby'  => 'title',
168
			'order'    => 'desc',
169
			'category' => '',  // Slugs
170
			'operator' => 'IN' // Possible values are 'IN', 'NOT IN', 'AND'.
171
		), $atts );
172
173
		if ( ! $atts['category'] ) {
174
			return '';
175
		}
176
177
		// Default ordering args
178
		$ordering_args = WC()->query->get_catalog_ordering_args( $atts['orderby'], $atts['order'] );
179
		$meta_query    = WC()->query->get_meta_query();
180
		$query_args    = array(
181
			'post_type'           => 'product',
182
			'post_status'         => 'publish',
183
			'ignore_sticky_posts' => 1,
184
			'orderby'             => $ordering_args['orderby'],
185
			'order'               => $ordering_args['order'],
186
			'posts_per_page'      => $atts['per_page'],
187
			'meta_query'          => $meta_query
188
		);
189
190
		$query_args = self::_maybe_add_category_args( $query_args, $atts['category'], $atts['operator'] );
191
192
		if ( isset( $ordering_args['meta_key'] ) ) {
193
			$query_args['meta_key'] = $ordering_args['meta_key'];
194
		}
195
196
		$return = self::product_loop( $query_args, $atts, 'product_cat' );
197
198
		// Remove ordering query arguments
199
		WC()->query->remove_ordering_args();
200
201
		return $return;
202
	}
203
204
205
	/**
206
	 * List all (or limited) product categories.
207
	 *
208
	 * @param array $atts
209
	 * @return string
210
	 */
211
	public static function product_categories( $atts ) {
212
		global $woocommerce_loop;
213
214
		$atts = shortcode_atts( array(
215
			'number'     => null,
216
			'orderby'    => 'name',
217
			'order'      => 'ASC',
218
			'columns'    => '4',
219
			'hide_empty' => 1,
220
			'parent'     => '',
221
			'ids'        => ''
222
		), $atts );
223
224
		if ( isset( $atts['ids'] ) ) {
225
			$ids = explode( ',', $atts['ids'] );
226
			$ids = array_map( 'trim', $ids );
227
		} else {
228
			$ids = array();
229
		}
230
231
		$hide_empty = ( $atts['hide_empty'] == true || $atts['hide_empty'] == 1 ) ? 1 : 0;
232
233
		// get terms and workaround WP bug with parents/pad counts
234
		$args = array(
235
			'orderby'    => $atts['orderby'],
236
			'order'      => $atts['order'],
237
			'hide_empty' => $hide_empty,
238
			'include'    => $ids,
239
			'pad_counts' => true,
240
			'child_of'   => $atts['parent']
241
		);
242
243
		$product_categories = get_terms( 'product_cat', $args );
244
245
		if ( '' !== $atts['parent'] ) {
246
			$product_categories = wp_list_filter( $product_categories, array( 'parent' => $atts['parent'] ) );
247
		}
248
249
		if ( $hide_empty ) {
250
			foreach ( $product_categories as $key => $category ) {
251
				if ( $category->count == 0 ) {
252
					unset( $product_categories[ $key ] );
253
				}
254
			}
255
		}
256
257
		if ( $atts['number'] ) {
258
			$product_categories = array_slice( $product_categories, 0, $atts['number'] );
259
		}
260
261
		$columns = absint( $atts['columns'] );
262
		$woocommerce_loop['columns'] = $columns;
263
264
		ob_start();
265
266
		// Reset loop/columns globals when starting a new loop
267
		$woocommerce_loop['loop'] = $woocommerce_loop['column'] = '';
268
269
		if ( $product_categories ) {
270
			woocommerce_product_loop_start();
271
272
			foreach ( $product_categories as $category ) {
273
				wc_get_template( 'content-product_cat.php', array(
274
					'category' => $category
275
				) );
276
			}
277
278
			woocommerce_product_loop_end();
279
		}
280
281
		woocommerce_reset_loop();
282
283
		return '<div class="woocommerce columns-' . $columns . '">' . ob_get_clean() . '</div>';
284
	}
285
286
	/**
287
	 * Recent Products shortcode.
288
	 *
289
	 * @param array $atts
290
	 * @return string
291
	 */
292
	public static function recent_products( $atts ) {
293
		$atts = shortcode_atts( array(
294
			'per_page' => '12',
295
			'columns'  => '4',
296
			'orderby'  => 'date',
297
			'order'    => 'desc',
298
			'category' => '',  // Slugs
299
			'operator' => 'IN' // Possible values are 'IN', 'NOT IN', 'AND'.
300
		), $atts );
301
302
		$query_args = array(
303
			'post_type'           => 'product',
304
			'post_status'         => 'publish',
305
			'ignore_sticky_posts' => 1,
306
			'posts_per_page'      => $atts['per_page'],
307
			'orderby'             => $atts['orderby'],
308
			'order'               => $atts['order'],
309
			'meta_query'          => WC()->query->get_meta_query()
310
		);
311
312
		$query_args = self::_maybe_add_category_args( $query_args, $atts['category'], $atts['operator'] );
313
314
		return self::product_loop( $query_args, $atts, 'recent_products' );
315
	}
316
317
	/**
318
	 * List multiple products shortcode.
319
	 *
320
	 * @param array $atts
321
	 * @return string
322
	 */
323
	public static function products( $atts ) {
324
		$atts = shortcode_atts( array(
325
			'columns' => '4',
326
			'orderby' => 'title',
327
			'order'   => 'asc',
328
			'ids'     => '',
329
			'skus'    => ''
330
		), $atts );
331
332
		$query_args = array(
333
			'post_type'           => 'product',
334
			'post_status'         => 'publish',
335
			'ignore_sticky_posts' => 1,
336
			'orderby'             => $atts['orderby'],
337
			'order'               => $atts['order'],
338
			'posts_per_page'      => -1,
339
			'meta_query'          => WC()->query->get_meta_query()
340
		);
341
342
		if ( ! empty( $atts['skus'] ) ) {
343
			$query_args['meta_query'][] = array(
344
				'key'     => '_sku',
345
				'value'   => array_map( 'trim', explode( ',', $atts['skus'] ) ),
346
				'compare' => 'IN'
347
			);
348
		}
349
350
		if ( ! empty( $atts['ids'] ) ) {
351
			$query_args['post__in'] = array_map( 'trim', explode( ',', $atts['ids'] ) );
352
		}
353
354
		return self::product_loop( $query_args, $atts, 'products' );
355
	}
356
357
	/**
358
	 * Display a single product.
359
	 *
360
	 * @param array $atts
361
	 * @return string
362
	 */
363
	public static function product( $atts ) {
364
		if ( empty( $atts ) ) {
365
			return '';
366
		}
367
368
		$meta_query = WC()->query->get_meta_query();
369
370
		$args = array(
371
			'post_type'      => 'product',
372
			'posts_per_page' => 1,
373
			'no_found_rows'  => 1,
374
			'post_status'    => 'publish',
375
			'meta_query'     => $meta_query
376
		);
377
378
		if ( isset( $atts['sku'] ) ) {
379
			$args['meta_query'][] = array(
380
				'key'     => '_sku',
381
				'value'   => $atts['sku'],
382
				'compare' => '='
383
			);
384
		}
385
386
		if ( isset( $atts['id'] ) ) {
387
			$args['p'] = $atts['id'];
388
		}
389
390
		ob_start();
391
392
		$products = new WP_Query( apply_filters( 'woocommerce_shortcode_products_query', $args, $atts ) );
393
394 View Code Duplication
		if ( $products->have_posts() ) : ?>
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
395
396
			<?php woocommerce_product_loop_start(); ?>
397
398
				<?php while ( $products->have_posts() ) : $products->the_post(); ?>
399
400
					<?php wc_get_template_part( 'content', 'product' ); ?>
401
402
				<?php endwhile; // end of the loop. ?>
403
404
			<?php woocommerce_product_loop_end(); ?>
405
406
		<?php endif;
407
408
		wp_reset_postdata();
409
410
		$css_class = 'woocommerce';
411
412
		if ( isset( $atts['class'] ) ) {
413
			$css_class .= ' ' . $atts['class'];
414
		}
415
416
		return '<div class="' . esc_attr( $css_class ) . '">' . ob_get_clean() . '</div>';
417
	}
418
419
	/**
420
	 * Display a single product price + cart button.
421
	 *
422
	 * @param array $atts
423
	 * @return string
424
	 */
425
	public static function product_add_to_cart( $atts ) {
426
		global $wpdb, $post;
427
428
		if ( empty( $atts ) ) {
429
			return '';
430
		}
431
432
		$atts = shortcode_atts( array(
433
			'id'         => '',
434
			'class'      => '',
435
			'quantity'   => '1',
436
			'sku'        => '',
437
			'style'      => 'border:4px solid #ccc; padding: 12px;',
438
			'show_price' => 'true'
439
		), $atts );
440
441
		if ( ! empty( $atts['id'] ) ) {
442
			$product_data = get_post( $atts['id'] );
443 View Code Duplication
		} elseif ( ! empty( $atts['sku'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
444
			$product_id   = wc_get_product_id_by_sku( $atts['sku'] );
445
			$product_data = get_post( $product_id );
446
		} else {
447
			return '';
448
		}
449
450
		$product = is_object( $product_data ) && in_array( $product_data->post_type, array( 'product', 'product_variation' ) ) ? wc_setup_product_data( $product_data ) : false;
451
452
		if ( ! $product ) {
453
			return '';
454
		}
455
456
		$styles = empty( $atts['style'] ) ? '' : ' style="' . esc_attr( $atts['style'] ) . '"';
457
458
		ob_start();
459
		?>
460
		<p class="product woocommerce add_to_cart_inline <?php echo esc_attr( $atts['class'] ); ?>"<?php echo $styles; ?>>
461
462
			<?php if ( 'true' == $atts['show_price'] ) : ?>
463
				<?php echo $product->get_price_html(); ?>
464
			<?php endif; ?>
465
466
			<?php woocommerce_template_loop_add_to_cart( array( 'quantity' => $atts['quantity'] ) ); ?>
467
468
		</p><?php
469
470
		// Restore Product global in case this is shown inside a product post
471
		wc_setup_product_data( $post );
472
473
		return ob_get_clean();
474
	}
475
476
	/**
477
	 * Get the add to cart URL for a product.
478
	 *
479
	 * @param array $atts
480
	 * @return string
481
	 */
482
	public static function product_add_to_cart_url( $atts ) {
483
		global $wpdb;
484
485
		if ( empty( $atts ) ) {
486
			return '';
487
		}
488
489
		if ( isset( $atts['id'] ) ) {
490
			$product_data = get_post( $atts['id'] );
491 View Code Duplication
		} elseif ( isset( $atts['sku'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
492
			$product_id   = wc_get_product_id_by_sku( $atts['sku'] );
493
			$product_data = get_post( $product_id );
494
		} else {
495
			return '';
496
		}
497
498
		$product = is_object( $product_data ) && in_array( $product_data->post_type, array( 'product', 'product_variation' ) ) ? wc_setup_product_data( $product_data ) : false;
499
500
		if ( ! $product ) {
501
			return '';
502
		}
503
504
		$_product = wc_get_product( $product_data );
505
506
		return esc_url( $_product->add_to_cart_url() );
507
	}
508
509
	/**
510
	 * List all products on sale.
511
	 *
512
	 * @param array $atts
513
	 * @return string
514
	 */
515
	public static function sale_products( $atts ) {
516
		$atts = shortcode_atts( array(
517
			'per_page' => '12',
518
			'columns'  => '4',
519
			'orderby'  => 'title',
520
			'order'    => 'asc'
521
		), $atts );
522
523
		$query_args = array(
524
			'posts_per_page' => $atts['per_page'],
525
			'orderby'        => $atts['orderby'],
526
			'order'          => $atts['order'],
527
			'no_found_rows'  => 1,
528
			'post_status'    => 'publish',
529
			'post_type'      => 'product',
530
			'meta_query'     => WC()->query->get_meta_query(),
531
			'post__in'       => array_merge( array( 0 ), wc_get_product_ids_on_sale() )
532
		);
533
534
		return self::product_loop( $query_args, $atts, 'sale_products' );
535
	}
536
537
	/**
538
	 * List best selling products on sale.
539
	 *
540
	 * @param array $atts
541
	 * @return string
542
	 */
543
	public static function best_selling_products( $atts ) {
544
		$atts = shortcode_atts( array(
545
			'per_page' => '12',
546
			'columns'  => '4',
547
			'category' => '',  // Slugs
548
			'operator' => 'IN' // Possible values are 'IN', 'NOT IN', 'AND'.
549
		), $atts );
550
551
		$query_args = array(
552
			'post_type'           => 'product',
553
			'post_status'         => 'publish',
554
			'ignore_sticky_posts' => 1,
555
			'posts_per_page'      => $atts['per_page'],
556
			'meta_key'            => 'total_sales',
557
			'orderby'             => 'meta_value_num',
558
			'meta_query'          => WC()->query->get_meta_query()
559
		);
560
561
		$query_args = self::_maybe_add_category_args( $query_args, $atts['category'], $atts['operator'] );
562
563
		return self::product_loop( $query_args, $atts, 'best_selling_products' );
564
	}
565
566
	/**
567
	 * List top rated products on sale.
568
	 *
569
	 * @param array $atts
570
	 * @return string
571
	 */
572
	public static function top_rated_products( $atts ) {
573
		$atts = shortcode_atts( array(
574
			'per_page' => '12',
575
			'columns'  => '4',
576
			'orderby'  => 'title',
577
			'order'    => 'asc',
578
			'category' => '',  // Slugs
579
			'operator' => 'IN' // Possible values are 'IN', 'NOT IN', 'AND'.
580
		), $atts );
581
582
		$query_args = array(
583
			'post_type'           => 'product',
584
			'post_status'         => 'publish',
585
			'ignore_sticky_posts' => 1,
586
			'orderby'             => $atts['orderby'],
587
			'order'               => $atts['order'],
588
			'posts_per_page'      => $atts['per_page'],
589
			'meta_query'          => WC()->query->get_meta_query()
590
		);
591
592
		$query_args = self::_maybe_add_category_args( $query_args, $atts['category'], $atts['operator'] );
593
594
		add_filter( 'posts_clauses', array( __CLASS__, 'order_by_rating_post_clauses' ) );
595
596
		$return = self::product_loop( $query_args, $atts, 'top_rated_products' );
597
598
		remove_filter( 'posts_clauses', array( __CLASS__, 'order_by_rating_post_clauses' ) );
599
600
		return $return;
601
	}
602
603
	/**
604
	 * Output featured products.
605
	 *
606
	 * @param array $atts
607
	 * @return string
608
	 */
609
	public static function featured_products( $atts ) {
610
		$atts = shortcode_atts( array(
611
			'per_page' => '12',
612
			'columns'  => '4',
613
			'orderby'  => 'date',
614
			'order'    => 'desc',
615
			'category' => '',  // Slugs
616
			'operator' => 'IN' // Possible values are 'IN', 'NOT IN', 'AND'.
617
		), $atts );
618
619
		$meta_query   = WC()->query->get_meta_query();
620
		$meta_query[] = array(
621
			'key'   => '_featured',
622
			'value' => 'yes'
623
		);
624
625
		$query_args = array(
626
			'post_type'           => 'product',
627
			'post_status'         => 'publish',
628
			'ignore_sticky_posts' => 1,
629
			'posts_per_page'      => $atts['per_page'],
630
			'orderby'             => $atts['orderby'],
631
			'order'               => $atts['order'],
632
			'meta_query'          => $meta_query
633
		);
634
635
		$query_args = self::_maybe_add_category_args( $query_args, $atts['category'], $atts['operator'] );
636
637
		return self::product_loop( $query_args, $atts, 'featured_products' );
638
	}
639
640
	/**
641
	 * Show a single product page.
642
	 *
643
	 * @param array $atts
644
	 * @return string
645
	 */
646
	public static function product_page( $atts ) {
647
		if ( empty( $atts ) ) {
648
			return '';
649
		}
650
651
		if ( ! isset( $atts['id'] ) && ! isset( $atts['sku'] ) ) {
652
			return '';
653
		}
654
655
		$args = array(
656
			'posts_per_page'      => 1,
657
			'post_type'           => 'product',
658
			'post_status'         => 'publish',
659
			'ignore_sticky_posts' => 1,
660
			'no_found_rows'       => 1
661
		);
662
663 View Code Duplication
		if ( isset( $atts['sku'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
664
			$args['meta_query'][] = array(
665
				'key'     => '_sku',
666
				'value'   => sanitize_text_field( $atts['sku'] ),
667
				'compare' => '='
668
			);
669
670
			$args['post_type'] = array( 'product', 'product_variation' );
671
		}
672
673
		if ( isset( $atts['id'] ) ) {
674
			$args['p'] = absint( $atts['id'] );
675
		}
676
677
		$single_product = new WP_Query( $args );
678
679
		$preselected_id = '0';
680
681
		// check if sku is a variation
682
		if ( isset( $atts['sku'] ) && $single_product->have_posts() && $single_product->post->post_type === 'product_variation' ) {
683
684
			$variation = new WC_Product_Variation( $single_product->post->ID );
685
			$attributes = $variation->get_variation_attributes();
686
687
			// set preselected id to be used by JS to provide context
688
			$preselected_id = $single_product->post->ID;
689
690
			// get the parent product object
691
			$args = array(
692
				'posts_per_page'      => 1,
693
				'post_type'           => 'product',
694
				'post_status'         => 'publish',
695
				'ignore_sticky_posts' => 1,
696
				'no_found_rows'       => 1,
697
				'p'                   => $single_product->post->post_parent
698
			);
699
700
			$single_product = new WP_Query( $args );
701
		?>
702
			<script type="text/javascript">
703
				jQuery( document ).ready( function( $ ) {
704
					var $variations_form = $( '[data-product-page-preselected-id="<?php echo esc_attr( $preselected_id ); ?>"]' ).find( 'form.variations_form' );
705
706
					<?php foreach( $attributes as $attr => $value ) { ?>
707
						$variations_form.find( 'select[name="<?php echo esc_attr( $attr ); ?>"]' ).val( '<?php echo $value; ?>' );
708
					<?php } ?>
709
				});
710
			</script>
711
		<?php
712
		}
713
714
		ob_start();
715
716
		while ( $single_product->have_posts() ) : $single_product->the_post(); wp_enqueue_script( 'wc-single-product' ); ?>
717
718
			<div class="single-product" data-product-page-preselected-id="<?php echo esc_attr( $preselected_id ); ?>">
719
720
				<?php wc_get_template_part( 'content', 'single-product' ); ?>
721
722
			</div>
723
724
		<?php endwhile; // end of the loop.
725
726
		wp_reset_postdata();
727
728
		return '<div class="woocommerce">' . ob_get_clean() . '</div>';
729
	}
730
731
	/**
732
	 * Show messages.
733
	 *
734
	 * @return string
735
	 */
736
	public static function shop_messages() {
737
		ob_start();
738
		wc_print_notices();
739
		return '<div class="woocommerce">' . ob_get_clean() . '</div>';
740
	}
741
742
	/**
743
	 * woocommerce_order_by_rating_post_clauses function.
744
	 *
745
	 * @param array $args
746
	 * @return array
747
	 */
748
	public static function order_by_rating_post_clauses( $args ) {
749
		global $wpdb;
750
751
		$args['where']   .= " AND $wpdb->commentmeta.meta_key = 'rating' ";
752
		$args['join']    .= "LEFT JOIN $wpdb->comments ON($wpdb->posts.ID               = $wpdb->comments.comment_post_ID) LEFT JOIN $wpdb->commentmeta ON($wpdb->comments.comment_ID = $wpdb->commentmeta.comment_id)";
753
		$args['orderby'] = "$wpdb->commentmeta.meta_value DESC";
754
		$args['groupby'] = "$wpdb->posts.ID";
755
756
		return $args;
757
	}
758
759
	/**
760
	 * List products with an attribute shortcode.
761
	 * Example [product_attribute attribute='color' filter='black'].
762
	 *
763
	 * @param array $atts
764
	 * @return string
765
	 */
766
	public static function product_attribute( $atts ) {
767
		$atts = shortcode_atts( array(
768
			'per_page'  => '12',
769
			'columns'   => '4',
770
			'orderby'   => 'title',
771
			'order'     => 'asc',
772
			'attribute' => '',
773
			'filter'    => ''
774
		), $atts );
775
776
		$query_args = array(
777
			'post_type'           => 'product',
778
			'post_status'         => 'publish',
779
			'ignore_sticky_posts' => 1,
780
			'posts_per_page'      => $atts['per_page'],
781
			'orderby'             => $atts['orderby'],
782
			'order'               => $atts['order'],
783
			'meta_query'          => WC()->query->get_meta_query(),
784
			'tax_query'           => array(
785
				array(
786
					'taxonomy' => strstr( $atts['attribute'], 'pa_' ) ? sanitize_title( $atts['attribute'] ) : 'pa_' . sanitize_title( $atts['attribute'] ),
787
					'terms'    => array_map( 'sanitize_title', explode( ',', $atts['filter'] ) ),
788
					'field'    => 'slug'
789
				)
790
			)
791
		);
792
793
		return self::product_loop( $query_args, $atts, 'product_attribute' );
794
	}
795
796
	/**
797
	 * List related products.
798
	 * @param array $atts
799
	 * @return string
800
	 */
801
	public static function related_products( $atts ) {
802
		$atts = shortcode_atts( array(
803
			'per_page' => '4',
804
			'columns'  => '4',
805
			'orderby'  => 'rand'
806
		), $atts );
807
808
		ob_start();
809
810
		woocommerce_related_products( $atts );
811
812
		return ob_get_clean();
813
	}
814
815
	/**
816
	 * Adds a tax_query index to the query to filter by category.
817
	 *
818
	 * @param array $args
819
	 * @param string $category
820
	 * @param string $operator
821
	 * @return array;
0 ignored issues
show
Documentation introduced by
The doc-type array; could not be parsed: Expected "|" or "end of type", but got ";" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
822
	 * @access private
823
	 */
824
	private static function _maybe_add_category_args( $args, $category, $operator ) {
825 View Code Duplication
		if ( ! empty( $category ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
826
			$args['tax_query'] = array(
827
				array(
828
					'taxonomy' => 'product_cat',
829
					'terms'    => array_map( 'sanitize_title', explode( ',', $category ) ),
830
					'field'    => 'slug',
831
					'operator' => $operator
832
				)
833
			);
834
		}
835
836
		return $args;
837
	}
838
}
839