1 <?php
  2 /**
  3  * WC_Shortcodes class.
  4  *
  5  * @class       WC_Shortcodes
  6  * @version     2.1.0
  7  * @package     WooCommerce/Classes
  8  * @category    Class
  9  * @author      WooThemes
 10  */
 11 class WC_Shortcodes {
 12 
 13     /**
 14      * Init shortcodes
 15      */
 16     public static function init() {
 17         // Define shortcodes
 18         $shortcodes = array(
 19             'product'                    => __CLASS__ . '::product',
 20             'product_page'               => __CLASS__ . '::product_page',
 21             'product_category'           => __CLASS__ . '::product_category',
 22             'product_categories'         => __CLASS__ . '::product_categories',
 23             'add_to_cart'                => __CLASS__ . '::product_add_to_cart',
 24             'add_to_cart_url'            => __CLASS__ . '::product_add_to_cart_url',
 25             'products'                   => __CLASS__ . '::products',
 26             'recent_products'            => __CLASS__ . '::recent_products',
 27             'sale_products'              => __CLASS__ . '::sale_products',
 28             'best_selling_products'      => __CLASS__ . '::best_selling_products',
 29             'top_rated_products'         => __CLASS__ . '::top_rated_products',
 30             'featured_products'          => __CLASS__ . '::featured_products',
 31             'product_attribute'          => __CLASS__ . '::product_attribute',
 32             'related_products'           => __CLASS__ . '::related_products',
 33             'shop_messages'              => __CLASS__ . '::shop_messages',
 34             'woocommerce_order_tracking' => __CLASS__ . '::order_tracking',
 35             'woocommerce_cart'           => __CLASS__ . '::cart',
 36             'woocommerce_checkout'       => __CLASS__ . '::checkout',
 37             'woocommerce_my_account'     => __CLASS__ . '::my_account',
 38         );
 39 
 40         foreach ( $shortcodes as $shortcode => $function ) {
 41             add_shortcode( apply_filters( "{$shortcode}_shortcode_tag", $shortcode ), $function );
 42         }
 43 
 44         // Alias for pre 2.1 compatibility
 45         add_shortcode( 'woocommerce_messages', __CLASS__ . '::shop_messages' );
 46     }
 47 
 48     /**
 49      * Shortcode Wrapper
 50      *
 51      * @param mixed $function
 52      * @param array $atts (default: array())
 53      * @return string
 54      */
 55     public static function shortcode_wrapper(
 56         $function,
 57         $atts    = array(),
 58         $wrapper = array(
 59             'class'  => 'woocommerce',
 60             'before' => null,
 61             'after'  => null
 62         )
 63     ) {
 64         ob_start();
 65 
 66         $before     = empty( $wrapper['before'] ) ? '<div class="' . esc_attr( $wrapper['class'] ) . '">' : $wrapper['before'];
 67         $after      = empty( $wrapper['after'] ) ? '</div>' : $wrapper['after'];
 68 
 69         echo $before;
 70         call_user_func( $function, $atts );
 71         echo $after;
 72 
 73         return ob_get_clean();
 74     }
 75 
 76     /**
 77      * Cart page shortcode.
 78      *
 79      * @access public
 80      * @param mixed $atts
 81      * @return string
 82      */
 83     public static function cart( $atts ) {
 84         return self::shortcode_wrapper( array( 'WC_Shortcode_Cart', 'output' ), $atts );
 85     }
 86 
 87     /**
 88      * Checkout page shortcode.
 89      *
 90      * @access public
 91      * @param mixed $atts
 92      * @return string
 93      */
 94     public static function checkout( $atts ) {
 95         return self::shortcode_wrapper( array( 'WC_Shortcode_Checkout', 'output' ), $atts );
 96     }
 97 
 98     /**
 99      * Order tracking page shortcode.
100      *
101      * @access public
102      * @param mixed $atts
103      * @return string
104      */
105     public static function order_tracking( $atts ) {
106         return self::shortcode_wrapper( array( 'WC_Shortcode_Order_Tracking', 'output' ), $atts );
107     }
108 
109     /**
110      * Cart shortcode.
111      *
112      * @access public
113      * @param mixed $atts
114      * @return string
115      */
116     public static function my_account( $atts ) {
117         return self::shortcode_wrapper( array( 'WC_Shortcode_My_Account', 'output' ), $atts );
118     }
119 
120     /**
121      * List products in a category shortcode
122      *
123      * @access public
124      * @param array $atts
125      * @return string
126      */
127     public static function product_category( $atts ) {
128         global $woocommerce_loop;
129 
130         if ( empty( $atts ) ) return '';
131 
132         extract( shortcode_atts( array(
133             'per_page'      => '12',
134             'columns'       => '4',
135             'orderby'       => 'title',
136             'order'         => 'desc',
137             'category'      => '',
138             'operator'      => 'IN' // Possible values are 'IN', 'NOT IN', 'AND'.
139             ), $atts ) );
140 
141         if ( ! $category ) return '';
142 
143         // Default ordering args
144         $ordering_args = WC()->query->get_catalog_ordering_args( $orderby, $order );
145 
146         $args = array(
147             'post_type'             => 'product',
148             'post_status'           => 'publish',
149             'ignore_sticky_posts'   => 1,
150             'orderby'               => $ordering_args['orderby'],
151             'order'                 => $ordering_args['order'],
152             'posts_per_page'        => $per_page,
153             'meta_query'            => array(
154                 array(
155                     'key'           => '_visibility',
156                     'value'         => array('catalog', 'visible'),
157                     'compare'       => 'IN'
158                 )
159             ),
160             'tax_query'             => array(
161                 array(
162                     'taxonomy'      => 'product_cat',
163                     'terms'         => array( esc_attr( $category ) ),
164                     'field'         => 'slug',
165                     'operator'      => $operator
166                 )
167             )
168         );
169 
170         if ( isset( $ordering_args['meta_key'] ) ) {
171             $args['meta_key'] = $ordering_args['meta_key'];
172         }
173 
174         ob_start();
175 
176         $products = new WP_Query( apply_filters( 'woocommerce_shortcode_products_query', $args, $atts ) );
177 
178         $woocommerce_loop['columns'] = $columns;
179 
180         if ( $products->have_posts() ) : ?>
181 
182             <?php woocommerce_product_loop_start(); ?>
183 
184                 <?php while ( $products->have_posts() ) : $products->the_post(); ?>
185 
186                     <?php wc_get_template_part( 'content', 'product' ); ?>
187 
188                 <?php endwhile; // end of the loop. ?>
189 
190             <?php woocommerce_product_loop_end(); ?>
191 
192         <?php endif;
193 
194         woocommerce_reset_loop();
195         wp_reset_postdata();
196 
197         return '<div class="woocommerce columns-' . $columns . '">' . ob_get_clean() . '</div>';
198     }
199 
200 
201     /**
202      * List all (or limited) product categories
203      *
204      * @access public
205      * @param array $atts
206      * @return string
207      */
208     public static function product_categories( $atts ) {
209         global $woocommerce_loop;
210 
211         extract( shortcode_atts( array(
212             'number'     => null,
213             'orderby'    => 'name',
214             'order'      => 'ASC',
215             'columns'    => '4',
216             'hide_empty' => 1,
217             'parent'     => ''
218         ), $atts ) );
219 
220         if ( isset( $atts[ 'ids' ] ) ) {
221             $ids = explode( ',', $atts[ 'ids' ] );
222             $ids = array_map( 'trim', $ids );
223         } else {
224             $ids = array();
225         }
226 
227         $hide_empty = ( $hide_empty == true || $hide_empty == 1 ) ? 1 : 0;
228 
229         // get terms and workaround WP bug with parents/pad counts
230         $args = array(
231             'orderby'    => $orderby,
232             'order'      => $order,
233             'hide_empty' => $hide_empty,
234             'include'    => $ids,
235             'pad_counts' => true,
236             'child_of'   => $parent
237         );
238 
239         $product_categories = get_terms( 'product_cat', $args );
240 
241         if ( $parent !== "" ) {
242             $product_categories = wp_list_filter( $product_categories, array( 'parent' => $parent ) );
243         }
244 
245         if ( $hide_empty ) {
246             foreach ( $product_categories as $key => $category ) {
247                 if ( $category->count == 0 ) {
248                     unset( $product_categories[ $key ] );
249                 }
250             }
251         }
252 
253         if ( $number ) {
254             $product_categories = array_slice( $product_categories, 0, $number );
255         }
256 
257         $woocommerce_loop['columns'] = $columns;
258 
259         ob_start();
260 
261         // Reset loop/columns globals when starting a new loop
262         $woocommerce_loop['loop'] = $woocommerce_loop['column'] = '';
263 
264         if ( $product_categories ) {
265 
266             woocommerce_product_loop_start();
267 
268             foreach ( $product_categories as $category ) {
269 
270                 wc_get_template( 'content-product_cat.php', array(
271                     'category' => $category
272                 ) );
273 
274             }
275 
276             woocommerce_product_loop_end();
277 
278         }
279 
280         woocommerce_reset_loop();
281 
282         return '<div class="woocommerce columns-' . $columns . '">' . ob_get_clean() . '</div>';
283     }
284 
285     /**
286      * Recent Products shortcode
287      *
288      * @access public
289      * @param array $atts
290      * @return string
291      */
292     public static function recent_products( $atts ) {
293         global $woocommerce_loop;
294 
295         extract( shortcode_atts( array(
296             'per_page'  => '12',
297             'columns'   => '4',
298             'orderby'   => 'date',
299             'order'     => 'desc'
300         ), $atts ) );
301 
302         $meta_query = WC()->query->get_meta_query();
303 
304         $args = array(
305             'post_type'             => 'product',
306             'post_status'           => 'publish',
307             'ignore_sticky_posts'   => 1,
308             'posts_per_page'        => $per_page,
309             'orderby'               => $orderby,
310             'order'                 => $order,
311             'meta_query'            => $meta_query
312         );
313 
314         ob_start();
315 
316         $products = new WP_Query( apply_filters( 'woocommerce_shortcode_products_query', $args, $atts ) );
317 
318         $woocommerce_loop['columns'] = $columns;
319 
320         if ( $products->have_posts() ) : ?>
321 
322             <?php woocommerce_product_loop_start(); ?>
323 
324                 <?php while ( $products->have_posts() ) : $products->the_post(); ?>
325 
326                     <?php wc_get_template_part( 'content', 'product' ); ?>
327 
328                 <?php endwhile; // end of the loop. ?>
329 
330             <?php woocommerce_product_loop_end(); ?>
331 
332         <?php endif;
333 
334         wp_reset_postdata();
335 
336         return '<div class="woocommerce columns-' . $columns . '">' . ob_get_clean() . '</div>';
337     }
338 
339 
340     /**
341      * List multiple products shortcode
342      *
343      * @access public
344      * @param array $atts
345      * @return string
346      */
347     public static function products( $atts ) {
348         global $woocommerce_loop;
349 
350         if ( empty( $atts ) ) return '';
351 
352         extract( shortcode_atts( array(
353             'columns'   => '4',
354             'orderby'   => 'title',
355             'order'     => 'asc'
356         ), $atts ) );
357 
358         $args = array(
359             'post_type'             => 'product',
360             'post_status'           => 'publish',
361             'ignore_sticky_posts'   => 1,
362             'orderby'               => $orderby,
363             'order'                 => $order,
364             'posts_per_page'        => -1,
365             'meta_query'            => array(
366                 array(
367                     'key'       => '_visibility',
368                     'value'     => array('catalog', 'visible'),
369                     'compare'   => 'IN'
370                 )
371             )
372         );
373 
374         if ( isset( $atts['skus'] ) ) {
375             $skus = explode( ',', $atts['skus'] );
376             $skus = array_map( 'trim', $skus );
377             $args['meta_query'][] = array(
378                 'key'       => '_sku',
379                 'value'     => $skus,
380                 'compare'   => 'IN'
381             );
382         }
383 
384         if ( isset( $atts['ids'] ) ) {
385             $ids = explode( ',', $atts['ids'] );
386             $ids = array_map( 'trim', $ids );
387             $args['post__in'] = $ids;
388         }
389 
390         ob_start();
391 
392         $products = new WP_Query( apply_filters( 'woocommerce_shortcode_products_query', $args, $atts ) );
393 
394         $woocommerce_loop['columns'] = $columns;
395 
396         if ( $products->have_posts() ) : ?>
397 
398             <?php woocommerce_product_loop_start(); ?>
399 
400                 <?php while ( $products->have_posts() ) : $products->the_post(); ?>
401 
402                     <?php wc_get_template_part( 'content', 'product' ); ?>
403 
404                 <?php endwhile; // end of the loop. ?>
405 
406             <?php woocommerce_product_loop_end(); ?>
407 
408         <?php endif;
409 
410         wp_reset_postdata();
411 
412         return '<div class="woocommerce columns-' . $columns . '">' . ob_get_clean() . '</div>';
413     }
414 
415 
416     /**
417      * Display a single product
418      *
419      * @access public
420      * @param array $atts
421      * @return string
422      */
423     public static function product( $atts ) {
424         if ( empty( $atts ) ) return '';
425 
426         $args = array(
427             'post_type'         => 'product',
428             'posts_per_page'    => 1,
429             'no_found_rows'     => 1,
430             'post_status'       => 'publish',
431             'meta_query'        => array(
432                 array(
433                     'key'       => '_visibility',
434                     'value'     => array('catalog', 'visible'),
435                     'compare'   => 'IN'
436                 )
437             )
438         );
439 
440         if ( isset( $atts['sku'] ) ) {
441             $args['meta_query'][] = array(
442                 'key'       => '_sku',
443                 'value'     => $atts['sku'],
444                 'compare'   => '='
445             );
446         }
447 
448         if ( isset( $atts['id'] ) ) {
449             $args['p'] = $atts['id'];
450         }
451 
452         ob_start();
453 
454         $products = new WP_Query( apply_filters( 'woocommerce_shortcode_products_query', $args, $atts ) );
455 
456         if ( $products->have_posts() ) : ?>
457 
458             <?php woocommerce_product_loop_start(); ?>
459 
460                 <?php while ( $products->have_posts() ) : $products->the_post(); ?>
461 
462                     <?php wc_get_template_part( 'content', 'product' ); ?>
463 
464                 <?php endwhile; // end of the loop. ?>
465 
466             <?php woocommerce_product_loop_end(); ?>
467 
468         <?php endif;
469 
470         wp_reset_postdata();
471 
472         return '<div class="woocommerce">' . ob_get_clean() . '</div>';
473     }
474 
475     /**
476      * Display a single product price + cart button
477      *
478      * @access public
479      * @param array $atts
480      * @return string
481      */
482     public static function product_add_to_cart( $atts ) {
483         global $wpdb, $post;
484 
485         if ( empty( $atts ) ) return '';
486 
487         extract( shortcode_atts( array(
488             'id'         => '',
489             'sku'        => '',
490             'style'      => 'border:4px solid #ccc; padding: 12px;',
491             'show_price' => 'true'
492         ), $atts ) );
493 
494         if ( ! empty( $id ) ) {
495             $product_data = get_post( $id );
496         } elseif ( ! empty( $sku ) ) {
497             $product_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key='_sku' AND meta_value='%s' LIMIT 1", $sku ) );
498             $product_data = get_post( $product_id );
499         } else {
500             return '';
501         }
502 
503         $product = wc_setup_product_data( $product_data );
504 
505         if ( ! $product ) {
506             return '';
507         }
508 
509         ob_start();
510         ?>
511         <p class="product woocommerce" style="<?php echo $style; ?>">
512 
513             <?php if ( $show_price == 'true' ) : ?>
514                 <?php echo $product->get_price_html(); ?>
515             <?php endif; ?>
516 
517             <?php woocommerce_template_loop_add_to_cart(); ?>
518 
519         </p><?php
520 
521         // Restore Product global in case this is shown inside a product post
522         wc_setup_product_data( $post );
523 
524         return ob_get_clean();
525     }
526 
527     /**
528      * Get the add to cart URL for a product
529      *
530      * @access public
531      * @param array $atts
532      * @return string
533      */
534     public static function product_add_to_cart_url( $atts ) {
535         global $wpdb;
536 
537         if ( empty( $atts ) ) return '';
538 
539         if ( isset( $atts['id'] ) ) {
540             $product_data = get_post( $atts['id'] );
541         } elseif ( isset( $atts['sku'] ) ) {
542             $product_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key='_sku' AND meta_value='%s' LIMIT 1", $atts['sku'] ) );
543             $product_data = get_post( $product_id );
544         } else {
545             return '';
546         }
547 
548         if ( 'product' !== $product_data->post_type ) {
549             return '';
550         }
551 
552         $_product = get_product( $product_data );
553 
554         return esc_url( $_product->add_to_cart_url() );
555     }
556 
557     /**
558      * List all products on sale
559      *
560      * @access public
561      * @param array $atts
562      * @return string
563      */
564     public static function sale_products( $atts ) {
565         global $woocommerce_loop;
566 
567         extract( shortcode_atts( array(
568             'per_page'      => '12',
569             'columns'       => '4',
570             'orderby'       => 'title',
571             'order'         => 'asc'
572         ), $atts ) );
573 
574         // Get products on sale
575         $product_ids_on_sale = wc_get_product_ids_on_sale();
576 
577         $meta_query   = array();
578         $meta_query[] = WC()->query->visibility_meta_query();
579         $meta_query[] = WC()->query->stock_status_meta_query();
580         $meta_query   = array_filter( $meta_query );
581 
582         $args = array(
583             'posts_per_page'    => $per_page,
584             'orderby'           => $orderby,
585             'order'             => $order,
586             'no_found_rows'     => 1,
587             'post_status'       => 'publish',
588             'post_type'         => 'product',
589             'meta_query'        => $meta_query,
590             'post__in'          => array_merge( array( 0 ), $product_ids_on_sale )
591         );
592 
593         ob_start();
594 
595         $products = new WP_Query( apply_filters( 'woocommerce_shortcode_products_query', $args, $atts ) );
596 
597         $woocommerce_loop['columns'] = $columns;
598 
599         if ( $products->have_posts() ) : ?>
600 
601             <?php woocommerce_product_loop_start(); ?>
602 
603                 <?php while ( $products->have_posts() ) : $products->the_post(); ?>
604 
605                     <?php wc_get_template_part( 'content', 'product' ); ?>
606 
607                 <?php endwhile; // end of the loop. ?>
608 
609             <?php woocommerce_product_loop_end(); ?>
610 
611         <?php endif;
612 
613         wp_reset_postdata();
614 
615         return '<div class="woocommerce columns-' . $columns . '">' . ob_get_clean() . '</div>';
616     }
617 
618     /**
619      * List best selling products on sale
620      *
621      * @access public
622      * @param array $atts
623      * @return string
624      */
625     public static function best_selling_products( $atts ) {
626         global $woocommerce_loop;
627 
628         extract( shortcode_atts( array(
629             'per_page'      => '12',
630             'columns'       => '4'
631         ), $atts ) );
632 
633         $args = array(
634             'post_type'             => 'product',
635             'post_status'           => 'publish',
636             'ignore_sticky_posts'   => 1,
637             'posts_per_page'        => $per_page,
638             'meta_key'              => 'total_sales',
639             'orderby'               => 'meta_value_num',
640             'meta_query'            => array(
641                 array(
642                     'key'       => '_visibility',
643                     'value'     => array( 'catalog', 'visible' ),
644                     'compare'   => 'IN'
645                 )
646             )
647         );
648 
649         ob_start();
650 
651         $products = new WP_Query( apply_filters( 'woocommerce_shortcode_products_query', $args, $atts ) );
652 
653         $woocommerce_loop['columns'] = $columns;
654 
655         if ( $products->have_posts() ) : ?>
656 
657             <?php woocommerce_product_loop_start(); ?>
658 
659                 <?php while ( $products->have_posts() ) : $products->the_post(); ?>
660 
661                     <?php wc_get_template_part( 'content', 'product' ); ?>
662 
663                 <?php endwhile; // end of the loop. ?>
664 
665             <?php woocommerce_product_loop_end(); ?>
666 
667         <?php endif;
668 
669         wp_reset_postdata();
670 
671         return '<div class="woocommerce columns-' . $columns . '">' . ob_get_clean() . '</div>';
672     }
673 
674     /**
675      * List top rated products on sale
676      *
677      * @access public
678      * @param array $atts
679      * @return string
680      */
681     public static function top_rated_products( $atts ) {
682         global $woocommerce_loop;
683 
684         extract( shortcode_atts( array(
685             'per_page'      => '12',
686             'columns'       => '4',
687             'orderby'       => 'title',
688             'order'         => 'asc'
689             ), $atts ) );
690 
691         $args = array(
692             'post_type'             => 'product',
693             'post_status'           => 'publish',
694             'ignore_sticky_posts'   => 1,
695             'orderby'               => $orderby,
696             'order'                 => $order,
697             'posts_per_page'        => $per_page,
698             'meta_query'            => array(
699                 array(
700                     'key'           => '_visibility',
701                     'value'         => array('catalog', 'visible'),
702                     'compare'       => 'IN'
703                 )
704             )
705         );
706 
707         ob_start();
708 
709         add_filter( 'posts_clauses', array( __CLASS__, 'order_by_rating_post_clauses' ) );
710 
711         $products = new WP_Query( apply_filters( 'woocommerce_shortcode_products_query', $args, $atts ) );
712 
713         remove_filter( 'posts_clauses', array( __CLASS__, 'order_by_rating_post_clauses' ) );
714 
715         $woocommerce_loop['columns'] = $columns;
716 
717         if ( $products->have_posts() ) : ?>
718 
719             <?php woocommerce_product_loop_start(); ?>
720 
721                 <?php while ( $products->have_posts() ) : $products->the_post(); ?>
722 
723                     <?php wc_get_template_part( 'content', 'product' ); ?>
724 
725                 <?php endwhile; // end of the loop. ?>
726 
727             <?php woocommerce_product_loop_end(); ?>
728 
729         <?php endif;
730 
731         wp_reset_postdata();
732 
733         return '<div class="woocommerce columns-' . $columns . '">' . ob_get_clean() . '</div>';
734     }
735 
736     /**
737      * Output featured products
738      *
739      * @access public
740      * @param array $atts
741      * @return string
742      */
743     public static function featured_products( $atts ) {
744         global $woocommerce_loop;
745 
746         extract( shortcode_atts( array(
747             'per_page'  => '12',
748             'columns'   => '4',
749             'orderby'   => 'date',
750             'order'     => 'desc'
751         ), $atts ) );
752 
753         $args = array(
754             'post_type'             => 'product',
755             'post_status'           => 'publish',
756             'ignore_sticky_posts'   => 1,
757             'posts_per_page'        => $per_page,
758             'orderby'               => $orderby,
759             'order'                 => $order,
760             'meta_query'            => array(
761                 array(
762                     'key'       => '_visibility',
763                     'value'     => array('catalog', 'visible'),
764                     'compare'   => 'IN'
765                 ),
766                 array(
767                     'key'       => '_featured',
768                     'value'     => 'yes'
769                 )
770             )
771         );
772 
773         ob_start();
774 
775         $products = new WP_Query( apply_filters( 'woocommerce_shortcode_products_query', $args, $atts ) );
776 
777         $woocommerce_loop['columns'] = $columns;
778 
779         if ( $products->have_posts() ) : ?>
780 
781             <?php woocommerce_product_loop_start(); ?>
782 
783                 <?php while ( $products->have_posts() ) : $products->the_post(); ?>
784 
785                     <?php wc_get_template_part( 'content', 'product' ); ?>
786 
787                 <?php endwhile; // end of the loop. ?>
788 
789             <?php woocommerce_product_loop_end(); ?>
790 
791         <?php endif;
792 
793         wp_reset_postdata();
794 
795         return '<div class="woocommerce columns-' . $columns . '">' . ob_get_clean() . '</div>';
796     }
797 
798 
799     /**
800      * Show a single product page
801      *
802      * @access public
803      * @param array $atts
804      * @return string
805      */
806     public static function product_page( $atts ) {
807         if ( empty( $atts ) ) return '';
808 
809         if ( ! isset( $atts['id'] ) && ! isset( $atts['sku'] ) ) return '';
810 
811         $args = array(
812             'posts_per_page'        => 1,
813             'post_type'             => 'product',
814             'post_status'           => 'publish',
815             'ignore_sticky_posts'   => 1,
816             'no_found_rows'         => 1
817         );
818 
819         if ( isset( $atts['sku'] ) ) {
820             $args['meta_query'][] = array(
821                 'key'     => '_sku',
822                 'value'   => $atts['sku'],
823                 'compare' => '='
824             );
825         }
826 
827         if ( isset( $atts['id'] ) ) {
828             $args['p'] = $atts['id'];
829         }
830 
831         $single_product = new WP_Query( $args );
832 
833         ob_start();
834 
835         while ( $single_product->have_posts() ) : $single_product->the_post(); wp_enqueue_script( 'wc-single-product' ); ?>
836 
837             <div class="single-product">
838 
839                 <?php wc_get_template_part( 'content', 'single-product' ); ?>
840 
841             </div>
842 
843         <?php endwhile; // end of the loop.
844 
845         wp_reset_postdata();
846 
847         return '<div class="woocommerce">' . ob_get_clean() . '</div>';
848     }
849 
850 
851     /**
852      * Show messages
853      *
854      * @access public
855      * @return string
856      */
857     public static function shop_messages() {
858         ob_start();
859 
860         wc_print_notices();
861 
862         return '<div class="woocommerce">' . ob_get_clean() . '</div>';
863     }
864 
865     /**
866      * woocommerce_order_by_rating_post_clauses function.
867      *
868      * @access public
869      * @param array $args
870      * @return array
871      */
872     public static function order_by_rating_post_clauses( $args ) {
873         global $wpdb;
874 
875         $args['where'] .= " AND $wpdb->commentmeta.meta_key = 'rating' ";
876 
877         $args['join'] .= "
878             LEFT JOIN $wpdb->comments ON($wpdb->posts.ID = $wpdb->comments.comment_post_ID)
879             LEFT JOIN $wpdb->commentmeta ON($wpdb->comments.comment_ID = $wpdb->commentmeta.comment_id)
880         ";
881 
882         $args['orderby'] = "$wpdb->commentmeta.meta_value DESC";
883 
884         $args['groupby'] = "$wpdb->posts.ID";
885 
886         return $args;
887     }
888 
889 
890     /**
891      * List products with an attribute shortcode
892      * Example [product_attribute attribute='color' filter='black']
893      *
894      * @access public
895      * @param array $atts
896      * @return string
897      */
898     public static function product_attribute( $atts ) {
899         global $woocommerce_loop;
900 
901         extract( shortcode_atts( array(
902             'per_page'  => '12',
903             'columns'   => '4',
904             'orderby'   => 'title',
905             'order'     => 'asc',
906             'attribute' => '',
907             'filter'    => ''
908         ), $atts ) );
909 
910         $attribute  = strstr( $attribute, 'pa_' ) ? sanitize_title( $attribute ) : 'pa_' . sanitize_title( $attribute );
911 
912         $args = array(
913             'post_type'           => 'product',
914             'post_status'         => 'publish',
915             'ignore_sticky_posts' => 1,
916             'posts_per_page'      => $per_page,
917             'orderby'             => $orderby,
918             'order'               => $order,
919             'meta_query'          => array(
920                 array(
921                     'key'               => '_visibility',
922                     'value'             => array('catalog', 'visible'),
923                     'compare'           => 'IN'
924                 )
925             ),
926             'tax_query'             => array(
927                 array(
928                     'taxonomy'  => $attribute,
929                     'terms'     => array_map( 'sanitize_title', explode( ",", $filter ) ),
930                     'field'     => 'slug'
931                 )
932             )
933         );
934 
935         ob_start();
936 
937         $products = new WP_Query( apply_filters( 'woocommerce_shortcode_products_query', $args, $atts ) );
938 
939         $woocommerce_loop['columns'] = $columns;
940 
941         if ( $products->have_posts() ) : ?>
942 
943             <?php woocommerce_product_loop_start(); ?>
944 
945                 <?php while ( $products->have_posts() ) : $products->the_post(); ?>
946 
947                     <?php wc_get_template_part( 'content', 'product' ); ?>
948 
949                 <?php endwhile; // end of the loop. ?>
950 
951             <?php woocommerce_product_loop_end(); ?>
952 
953         <?php endif;
954 
955         wp_reset_postdata();
956 
957         return '<div class="woocommerce">' . ob_get_clean() . '</div>';
958     }
959 
960     /**
961      * @param array $atts
962      * @return string
963      */
964     public static function related_products( $atts ) {
965 
966         $atts = shortcode_atts( array(
967             'posts_per_page' => '2',
968             'columns'        => '2',
969             'orderby'        => 'rand',
970         ), $atts );
971 
972         if ( isset( $atts['per_page'] ) ) {
973             _deprecated_argument( __CLASS__ . '->' . __FUNCTION__, '2.1', __( 'Use $args["posts_per_page"] instead. Deprecated argument will be removed in WC 2.2.', 'woocommerce' ) );
974             $atts['posts_per_page'] = $atts['per_page'];
975             unset( $atts['per_page'] );
976         }
977 
978         ob_start();
979 
980         woocommerce_related_products( $atts );
981 
982         return ob_get_clean();
983     }
984 }
985 
WooCommerce API documentation generated by ApiGen 2.8.0