/includes/wc-template-functions.php

  1. <?php 
  2. /** 
  3. * WooCommerce Template 
  4. * 
  5. * Functions for the templating system. 
  6. * 
  7. * @author WooThemes 
  8. * @category Core 
  9. * @package WooCommerce/Functions 
  10. * @version 2.5.0 
  11. */ 
  12.  
  13. if ( ! defined( 'ABSPATH' ) ) { 
  14. exit; // Exit if accessed directly 
  15.  
  16. /** 
  17. * Handle redirects before content is output - hooked into template_redirect so is_page works. 
  18. */ 
  19. function wc_template_redirect() { 
  20. global $wp_query, $wp; 
  21.  
  22. if ( ! empty( $_GET['page_id'] ) && '' === get_option( 'permalink_structure' ) && wc_get_page_id( 'shop' ) == $_GET['page_id'] ) { 
  23.  
  24. // When default permalinks are enabled, redirect shop page to post type archive url 
  25. wp_safe_redirect( get_post_type_archive_link( 'product' ) ); 
  26. exit; 
  27.  
  28. } elseif ( is_page( wc_get_page_id( 'checkout' ) ) && wc_get_page_id( 'checkout' ) !== wc_get_page_id( 'cart' ) && WC()->cart->is_empty() && empty( $wp->query_vars['order-pay'] ) && ! isset( $wp->query_vars['order-received'] ) ) { 
  29.  
  30. // When on the checkout with an empty cart, redirect to cart page 
  31. wc_add_notice( __( 'Checkout is not available whilst your cart is empty.', 'woocommerce' ), 'notice' ); 
  32. wp_redirect( wc_get_page_permalink( 'cart' ) ); 
  33. exit; 
  34.  
  35. } elseif ( isset( $wp->query_vars['customer-logout'] ) ) { 
  36.  
  37. // Logout 
  38. wp_redirect( str_replace( '&', '&', wp_logout_url( wc_get_page_permalink( 'myaccount' ) ) ) ); 
  39. exit; 
  40.  
  41. } elseif ( is_search() && is_post_type_archive( 'product' ) && apply_filters( 'woocommerce_redirect_single_search_result', true ) && 1 === absint( $wp_query->found_posts ) ) { 
  42.  
  43. // Redirect to the product page if we have a single product 
  44. $product = wc_get_product( $wp_query->post ); 
  45.  
  46. if ( $product && $product->is_visible() ) { 
  47. wp_safe_redirect( get_permalink( $product->get_id() ), 302 ); 
  48. exit; 
  49. } elseif ( is_add_payment_method_page() ) { 
  50.  
  51. // Ensure payment gateways are loaded early 
  52. WC()->payment_gateways(); 
  53.  
  54. } elseif ( is_checkout() ) { 
  55.  
  56. // Checkout pages handling 
  57. // Buffer the checkout page 
  58. ob_start(); 
  59.  
  60. // Ensure gateways and shipping methods are loaded early 
  61. WC()->payment_gateways(); 
  62. WC()->shipping(); 
  63.  
  64. add_action( 'template_redirect', 'wc_template_redirect' ); 
  65.  
  66. /** 
  67. * When loading sensitive checkout or account pages, send a HTTP header to limit rendering of pages to same origin iframes for security reasons. 
  68. * 
  69. * Can be disabled with: remove_action( 'template_redirect', 'wc_send_frame_options_header' ); 
  70. * 
  71. * @since 2.3.10 
  72. */ 
  73. function wc_send_frame_options_header() { 
  74. if ( is_checkout() || is_account_page() ) { 
  75. send_frame_options_header(); 
  76. add_action( 'template_redirect', 'wc_send_frame_options_header' ); 
  77.  
  78. /** 
  79. * No index our endpoints. 
  80. * Prevent indexing pages like order-received. 
  81. * 
  82. * @since 2.5.3 
  83. */ 
  84. function wc_prevent_endpoint_indexing() { 
  85. if ( is_wc_endpoint_url() || isset( $_GET['download_file'] ) ) { 
  86. @header( 'X-Robots-Tag: noindex' ); 
  87. add_action( 'template_redirect', 'wc_prevent_endpoint_indexing' ); 
  88.  
  89. /** 
  90. * Remove adjacent_posts_rel_link_wp_head - pointless for products. 
  91. * 
  92. * @since 3.0.0 
  93. */ 
  94. function wc_prevent_adjacent_posts_rel_link_wp_head() { 
  95. if ( is_singular( 'product' ) ) { 
  96. remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0 ); 
  97. add_action( 'template_redirect', 'wc_prevent_adjacent_posts_rel_link_wp_head' ); 
  98.  
  99. /** 
  100. * Show the gallery if JS is disabled. 
  101. * 
  102. * @since 3.0.6 
  103. */ 
  104. function wc_gallery_noscript() { 
  105. ?> 
  106. <noscript><style>.woocommerce-product-gallery{ opacity: 1 !important; }</style></noscript> 
  107. <?php 
  108. add_action( 'wp_head', 'wc_gallery_noscript' ); 
  109.  
  110. /** 
  111. * When the_post is called, put product data into a global. 
  112. * 
  113. * @param mixed $post 
  114. * @return WC_Product 
  115. */ 
  116. function wc_setup_product_data( $post ) { 
  117. unset( $GLOBALS['product'] ); 
  118.  
  119. if ( is_int( $post ) ) 
  120. $post = get_post( $post ); 
  121.  
  122. if ( empty( $post->post_type ) || ! in_array( $post->post_type, array( 'product', 'product_variation' ) ) ) 
  123. return; 
  124.  
  125. $GLOBALS['product'] = wc_get_product( $post ); 
  126.  
  127. return $GLOBALS['product']; 
  128. add_action( 'the_post', 'wc_setup_product_data' ); 
  129.  
  130. if ( ! function_exists( 'woocommerce_reset_loop' ) ) { 
  131.  
  132. /** 
  133. * Reset the loop's index and columns when we're done outputting a product loop. 
  134. * @subpackage Loop 
  135. */ 
  136. function woocommerce_reset_loop() { 
  137. $GLOBALS['woocommerce_loop'] = array( 
  138. 'loop' => '',  
  139. 'columns' => '',  
  140. 'name' => '',  
  141. ); 
  142. add_filter( 'loop_end', 'woocommerce_reset_loop' ); 
  143.  
  144. /** 
  145. * Products RSS Feed. 
  146. * @deprecated 2.6 
  147. * @access public 
  148. */ 
  149. function wc_products_rss_feed() { 
  150. // Product RSS 
  151. if ( is_post_type_archive( 'product' ) || is_singular( 'product' ) ) { 
  152.  
  153. $feed = get_post_type_archive_feed_link( 'product' ); 
  154.  
  155. echo '<link rel="alternate" type="application/rss+xml" title="' . esc_attr__( 'New products', 'woocommerce' ) . '" href="' . esc_url( $feed ) . '" />'; 
  156.  
  157. } elseif ( is_tax( 'product_cat' ) ) { 
  158.  
  159. $term = get_term_by( 'slug', esc_attr( get_query_var( 'product_cat' ) ), 'product_cat' ); 
  160.  
  161. if ( $term ) { 
  162. $feed = add_query_arg( 'product_cat', $term->slug, get_post_type_archive_feed_link( 'product' ) ); 
  163. echo '<link rel="alternate" type="application/rss+xml" title="' . sprintf( esc_attr__( 'New products added to %s', 'woocommerce' ), $term->name ) . '" href="' . esc_url( $feed ) . '" />'; 
  164. } elseif ( is_tax( 'product_tag' ) ) { 
  165.  
  166. $term = get_term_by( 'slug', esc_attr( get_query_var( 'product_tag' ) ), 'product_tag' ); 
  167.  
  168. if ( $term ) { 
  169. $feed = add_query_arg( 'product_tag', $term->slug, get_post_type_archive_feed_link( 'product' ) ); 
  170. echo '<link rel="alternate" type="application/rss+xml" title="' . sprintf( esc_attr__( 'New products tagged %s', 'woocommerce' ), urlencode( $term->name ) ) . '" href="' . esc_url( $feed ) . '" />'; 
  171.  
  172. /** 
  173. * Output generator tag to aid debugging. 
  174. * 
  175. * @access public 
  176. */ 
  177. function wc_generator_tag( $gen, $type ) { 
  178. switch ( $type ) { 
  179. case 'html': 
  180. $gen .= "\n" . '<meta name="generator" content="WooCommerce ' . esc_attr( WC_VERSION ) . '">'; 
  181. break; 
  182. case 'xhtml': 
  183. $gen .= "\n" . '<meta name="generator" content="WooCommerce ' . esc_attr( WC_VERSION ) . '" />'; 
  184. break; 
  185. return $gen; 
  186.  
  187. /** 
  188. * Add body classes for WC pages. 
  189. * 
  190. * @param array $classes 
  191. * @return array 
  192. */ 
  193. function wc_body_class( $classes ) { 
  194. $classes = (array) $classes; 
  195.  
  196. if ( is_woocommerce() ) { 
  197.  
  198. $classes[] = 'woocommerce'; 
  199. $classes[] = 'woocommerce-page'; 
  200.  
  201. } elseif ( is_checkout() ) { 
  202.  
  203. $classes[] = 'woocommerce-checkout'; 
  204. $classes[] = 'woocommerce-page'; 
  205.  
  206. } elseif ( is_cart() ) { 
  207.  
  208. $classes[] = 'woocommerce-cart'; 
  209. $classes[] = 'woocommerce-page'; 
  210.  
  211. } elseif ( is_account_page() ) { 
  212.  
  213. $classes[] = 'woocommerce-account'; 
  214. $classes[] = 'woocommerce-page'; 
  215.  
  216.  
  217. if ( is_store_notice_showing() ) { 
  218. $classes[] = 'woocommerce-demo-store'; 
  219.  
  220. foreach ( WC()->query->query_vars as $key => $value ) { 
  221. if ( is_wc_endpoint_url( $key ) ) { 
  222. $classes[] = 'woocommerce-' . sanitize_html_class( $key ); 
  223.  
  224. return array_unique( $classes ); 
  225.  
  226. /** 
  227. * Display the classes for the product cat div. 
  228. * 
  229. * @since 2.4.0 
  230. * @param string|array $class One or more classes to add to the class list. 
  231. * @param object $category object Optional. 
  232. */ 
  233. function wc_product_cat_class( $class = '', $category = null ) { 
  234. // Separates classes with a single space, collates classes for post DIV 
  235. echo 'class="' . esc_attr( join( ' ', wc_get_product_cat_class( $class, $category ) ) ) . '"'; 
  236.  
  237. /** 
  238. * Get classname for loops based on $woocommerce_loop global. 
  239. * @since 2.6.0 
  240. * @return string 
  241. */ 
  242. function wc_get_loop_class() { 
  243. global $woocommerce_loop; 
  244.  
  245. $woocommerce_loop['loop'] = ! empty( $woocommerce_loop['loop'] ) ? $woocommerce_loop['loop'] + 1 : 1; 
  246. $woocommerce_loop['columns'] = max( 1, ! empty( $woocommerce_loop['columns'] ) ? $woocommerce_loop['columns'] : apply_filters( 'loop_shop_columns', 4 ) ); 
  247.  
  248. if ( 0 === ( $woocommerce_loop['loop'] - 1 ) % $woocommerce_loop['columns'] || 1 === $woocommerce_loop['columns'] ) { 
  249. return 'first'; 
  250. } elseif ( 0 === $woocommerce_loop['loop'] % $woocommerce_loop['columns'] ) { 
  251. return 'last'; 
  252. } else { 
  253. return ''; 
  254.  
  255. /** 
  256. * Get the classes for the product cat div. 
  257. * 
  258. * @since 2.4.0 
  259. * @param string|array $class One or more classes to add to the class list. 
  260. * @param object $category object Optional. 
  261. */ 
  262. function wc_get_product_cat_class( $class = '', $category = null ) { 
  263. $classes = is_array( $class ) ? $class : array_map( 'trim', explode( ' ', $class ) ); 
  264. $classes[] = 'product-category'; 
  265. $classes[] = 'product'; 
  266. $classes[] = wc_get_loop_class(); 
  267. $classes = apply_filters( 'product_cat_class', $classes, $class, $category ); 
  268.  
  269. return array_unique( array_filter( $classes ) ); 
  270.  
  271. /** 
  272. * Adds extra post classes for products. 
  273. * 
  274. * @since 2.1.0 
  275. * @param array $classes 
  276. * @param string|array $class 
  277. * @param int $post_id 
  278. * @return array 
  279. */ 
  280. function wc_product_post_class( $classes, $class = '', $post_id = '' ) { 
  281. if ( ! $post_id || ! in_array( get_post_type( $post_id ), array( 'product', 'product_variation' ) ) ) { 
  282. return $classes; 
  283.  
  284. $product = wc_get_product( $post_id ); 
  285.  
  286. if ( $product ) { 
  287. $classes[] = 'product'; 
  288. $classes[] = wc_get_loop_class(); 
  289. $classes[] = $product->get_stock_status(); 
  290.  
  291. if ( $product->is_on_sale() ) { 
  292. $classes[] = 'sale'; 
  293. if ( $product->is_featured() ) { 
  294. $classes[] = 'featured'; 
  295. if ( $product->is_downloadable() ) { 
  296. $classes[] = 'downloadable'; 
  297. if ( $product->is_virtual() ) { 
  298. $classes[] = 'virtual'; 
  299. if ( $product->is_sold_individually() ) { 
  300. $classes[] = 'sold-individually'; 
  301. if ( $product->is_taxable() ) { 
  302. $classes[] = 'taxable'; 
  303. if ( $product->is_shipping_taxable() ) { 
  304. $classes[] = 'shipping-taxable'; 
  305. if ( $product->is_purchasable() ) { 
  306. $classes[] = 'purchasable'; 
  307. if ( $product->get_type() ) { 
  308. $classes[] = "product-type-" . $product->get_type(); 
  309. if ( $product->is_type( 'variable' ) ) { 
  310. if ( ! $product->get_default_attributes() ) { 
  311. $classes[] = 'has-default-attributes'; 
  312. if ( $product->has_child() ) { 
  313. $classes[] = 'has-children'; 
  314.  
  315. if ( false !== ( $key = array_search( 'hentry', $classes ) ) ) { 
  316. unset( $classes[ $key ] ); 
  317.  
  318. return $classes; 
  319.  
  320. /** 
  321. * Outputs hidden form inputs for each query string variable. 
  322. * @since 3.0.0 
  323. * @param array $values Name value pairs. 
  324. * @param array $exclude Keys to exclude. 
  325. * @param string $current_key Current key we are outputting. 
  326. */ 
  327. function wc_query_string_form_fields( $values = null, $exclude = array(), $current_key = '', $return = false ) { 
  328. if ( is_null( $values ) ) { 
  329. $values = $_GET; 
  330. $html = ''; 
  331.  
  332. foreach ( $values as $key => $value ) { 
  333. if ( in_array( $key, $exclude, true ) ) { 
  334. continue; 
  335. if ( $current_key ) { 
  336. $key = $current_key . '[' . $key . ']'; 
  337. if ( is_array( $value ) ) { 
  338. $html .= wc_query_string_form_fields( $value, $exclude, $key, true ); 
  339. } else { 
  340. $html .= '<input type="hidden" name="' . esc_attr( $key ) . '" value="' . esc_attr( $value ) . '" />'; 
  341.  
  342. if ( $return ) { 
  343. return $html; 
  344. } else { 
  345. echo $html; 
  346.  
  347. /** Template pages ********************************************************/ 
  348.  
  349. if ( ! function_exists( 'woocommerce_content' ) ) { 
  350.  
  351. /** 
  352. * Output WooCommerce content. 
  353. * 
  354. * This function is only used in the optional 'woocommerce.php' template. 
  355. * which people can add to their themes to add basic woocommerce support. 
  356. * without hooks or modifying core templates. 
  357. * 
  358. */ 
  359. function woocommerce_content() { 
  360.  
  361. if ( is_singular( 'product' ) ) { 
  362.  
  363. while ( have_posts() ) : the_post(); 
  364.  
  365. wc_get_template_part( 'content', 'single-product' ); 
  366.  
  367. endwhile; 
  368.  
  369. } else { ?> 
  370.  
  371. <?php if ( apply_filters( 'woocommerce_show_page_title', true ) ) : ?> 
  372.  
  373. <h1 class="page-title"><?php woocommerce_page_title(); ?></h1> 
  374.  
  375. <?php endif; ?> 
  376.  
  377. <?php do_action( 'woocommerce_archive_description' ); ?> 
  378.  
  379. <?php if ( have_posts() ) : ?> 
  380.  
  381. <?php do_action( 'woocommerce_before_shop_loop' ); ?> 
  382.  
  383. <?php woocommerce_product_loop_start(); ?> 
  384.  
  385. <?php woocommerce_product_subcategories(); ?> 
  386.  
  387. <?php while ( have_posts() ) : the_post(); ?> 
  388.  
  389. <?php wc_get_template_part( 'content', 'product' ); ?> 
  390.  
  391. <?php endwhile; // end of the loop. ?> 
  392.  
  393. <?php woocommerce_product_loop_end(); ?> 
  394.  
  395. <?php do_action( 'woocommerce_after_shop_loop' ); ?> 
  396.  
  397. <?php elseif ( ! woocommerce_product_subcategories( array( 'before' => woocommerce_product_loop_start( false ), 'after' => woocommerce_product_loop_end( false ) ) ) ) : ?> 
  398.  
  399. <?php do_action( 'woocommerce_no_products_found' ); ?> 
  400.  
  401. <?php endif; 
  402.  
  403.  
  404. /** Global ****************************************************************/ 
  405.  
  406. if ( ! function_exists( 'woocommerce_output_content_wrapper' ) ) { 
  407.  
  408. /** 
  409. * Output the start of the page wrapper. 
  410. * 
  411. */ 
  412. function woocommerce_output_content_wrapper() { 
  413. wc_get_template( 'global/wrapper-start.php' ); 
  414. if ( ! function_exists( 'woocommerce_output_content_wrapper_end' ) ) { 
  415.  
  416. /** 
  417. * Output the end of the page wrapper. 
  418. * 
  419. */ 
  420. function woocommerce_output_content_wrapper_end() { 
  421. wc_get_template( 'global/wrapper-end.php' ); 
  422.  
  423. if ( ! function_exists( 'woocommerce_get_sidebar' ) ) { 
  424.  
  425. /** 
  426. * Get the shop sidebar template. 
  427. * 
  428. */ 
  429. function woocommerce_get_sidebar() { 
  430. wc_get_template( 'global/sidebar.php' ); 
  431.  
  432. if ( ! function_exists( 'woocommerce_demo_store' ) ) { 
  433.  
  434. /** 
  435. * Adds a demo store banner to the site if enabled. 
  436. * 
  437. */ 
  438. function woocommerce_demo_store() { 
  439. if ( ! is_store_notice_showing() ) { 
  440. return; 
  441.  
  442. $notice = get_option( 'woocommerce_demo_store_notice' ); 
  443.  
  444. if ( empty( $notice ) ) { 
  445. $notice = __( 'This is a demo store for testing purposes — no orders shall be fulfilled.', 'woocommerce' ); 
  446.  
  447. echo apply_filters( 'woocommerce_demo_store', '<p class="woocommerce-store-notice demo_store">' . wp_kses_post( $notice ) . ' <a href="#" class="woocommerce-store-notice__dismiss-link">' . esc_html__( 'Dismiss', 'woocommerce' ) . '</a></p>', $notice ); 
  448.  
  449. /** Loop ******************************************************************/ 
  450.  
  451. if ( ! function_exists( 'woocommerce_page_title' ) ) { 
  452.  
  453. /** 
  454. * woocommerce_page_title function. 
  455. * 
  456. * @param bool $echo 
  457. * @return string 
  458. */ 
  459. function woocommerce_page_title( $echo = true ) { 
  460.  
  461. if ( is_search() ) { 
  462. $page_title = sprintf( __( 'Search results: “%s”', 'woocommerce' ), get_search_query() ); 
  463.  
  464. if ( get_query_var( 'paged' ) ) 
  465. $page_title .= sprintf( __( ' – Page %s', 'woocommerce' ), get_query_var( 'paged' ) ); 
  466.  
  467. } elseif ( is_tax() ) { 
  468.  
  469. $page_title = single_term_title( "", false ); 
  470.  
  471. } else { 
  472.  
  473. $shop_page_id = wc_get_page_id( 'shop' ); 
  474. $page_title = get_the_title( $shop_page_id ); 
  475.  
  476.  
  477. $page_title = apply_filters( 'woocommerce_page_title', $page_title ); 
  478.  
  479. if ( $echo ) 
  480. echo $page_title; 
  481. else 
  482. return $page_title; 
  483.  
  484. if ( ! function_exists( 'woocommerce_product_loop_start' ) ) { 
  485.  
  486. /** 
  487. * Output the start of a product loop. By default this is a UL. 
  488. * 
  489. * @param bool $echo 
  490. * @return string 
  491. */ 
  492. function woocommerce_product_loop_start( $echo = true ) { 
  493. ob_start(); 
  494. $GLOBALS['woocommerce_loop']['loop'] = 0; 
  495. wc_get_template( 'loop/loop-start.php' ); 
  496. if ( $echo ) 
  497. echo ob_get_clean(); 
  498. else 
  499. return ob_get_clean(); 
  500. if ( ! function_exists( 'woocommerce_product_loop_end' ) ) { 
  501.  
  502. /** 
  503. * Output the end of a product loop. By default this is a UL. 
  504. * 
  505. * @param bool $echo 
  506. * @return string 
  507. */ 
  508. function woocommerce_product_loop_end( $echo = true ) { 
  509. ob_start(); 
  510.  
  511. wc_get_template( 'loop/loop-end.php' ); 
  512.  
  513. if ( $echo ) 
  514. echo ob_get_clean(); 
  515. else 
  516. return ob_get_clean(); 
  517. if ( ! function_exists( 'woocommerce_template_loop_product_title' ) ) { 
  518.  
  519. /** 
  520. * Show the product title in the product loop. By default this is an H2. 
  521. */ 
  522. function woocommerce_template_loop_product_title() { 
  523. echo '<h2 class="woocommerce-loop-product__title">' . get_the_title() . '</h2>'; 
  524. if ( ! function_exists( 'woocommerce_template_loop_category_title' ) ) { 
  525.  
  526. /** 
  527. * Show the subcategory title in the product loop. 
  528. */ 
  529. function woocommerce_template_loop_category_title( $category ) { 
  530. ?> 
  531. <h2 class="woocommerce-loop-category__title"> 
  532. <?php 
  533. echo $category->name; 
  534.  
  535. if ( $category->count > 0 ) 
  536. echo apply_filters( 'woocommerce_subcategory_count_html', ' <mark class="count">(' . $category->count . ')</mark>', $category ); 
  537. ?> 
  538. </h2> 
  539. <?php 
  540. /** 
  541. * Insert the opening anchor tag for products in the loop. 
  542. */ 
  543. function woocommerce_template_loop_product_link_open() { 
  544. echo '<a href="' . get_the_permalink() . '" class="woocommerce-LoopProduct-link">'; 
  545. /** 
  546. * Insert the opening anchor tag for products in the loop. 
  547. */ 
  548. function woocommerce_template_loop_product_link_close() { 
  549. echo '</a>'; 
  550. /** 
  551. * Insert the opening anchor tag for categories in the loop. 
  552. */ 
  553. function woocommerce_template_loop_category_link_open( $category ) { 
  554. echo '<a href="' . get_term_link( $category, 'product_cat' ) . '">'; 
  555. /** 
  556. * Insert the closing anchor tag for categories in the loop. 
  557. */ 
  558. function woocommerce_template_loop_category_link_close() { 
  559. echo '</a>'; 
  560. if ( ! function_exists( 'woocommerce_taxonomy_archive_description' ) ) { 
  561.  
  562. /** 
  563. * Show an archive description on taxonomy archives. 
  564. * 
  565. * @subpackage Archives 
  566. */ 
  567. function woocommerce_taxonomy_archive_description() { 
  568. if ( is_product_taxonomy() && 0 === absint( get_query_var( 'paged' ) ) ) { 
  569. $description = wc_format_content( term_description() ); 
  570. if ( $description ) { 
  571. echo '<div class="term-description">' . $description . '</div>'; 
  572. if ( ! function_exists( 'woocommerce_product_archive_description' ) ) { 
  573.  
  574. /** 
  575. * Show a shop page description on product archives. 
  576. * 
  577. * @subpackage Archives 
  578. */ 
  579. function woocommerce_product_archive_description() { 
  580. // Don't display the description on search results page 
  581. if ( is_search() ) { 
  582. return; 
  583.  
  584. if ( is_post_type_archive( 'product' ) && 0 === absint( get_query_var( 'paged' ) ) ) { 
  585. $shop_page = get_post( wc_get_page_id( 'shop' ) ); 
  586. if ( $shop_page ) { 
  587. $description = wc_format_content( $shop_page->post_content ); 
  588. if ( $description ) { 
  589. echo '<div class="page-description">' . $description . '</div>'; 
  590.  
  591. if ( ! function_exists( 'woocommerce_template_loop_add_to_cart' ) ) { 
  592.  
  593. /** 
  594. * Get the add to cart template for the loop. 
  595. * 
  596. * @subpackage Loop 
  597. */ 
  598. function woocommerce_template_loop_add_to_cart( $args = array() ) { 
  599. global $product; 
  600.  
  601. if ( $product ) { 
  602. $defaults = array( 
  603. 'quantity' => 1,  
  604. 'class' => implode( ' ', array_filter( array( 
  605. 'button',  
  606. 'product_type_' . $product->get_type(),  
  607. $product->is_purchasable() && $product->is_in_stock() ? 'add_to_cart_button' : '',  
  608. $product->supports( 'ajax_add_to_cart' ) ? 'ajax_add_to_cart' : '',  
  609. ) ) ),  
  610. ); 
  611.  
  612. $args = apply_filters( 'woocommerce_loop_add_to_cart_args', wp_parse_args( $args, $defaults ), $product ); 
  613.  
  614. wc_get_template( 'loop/add-to-cart.php', $args ); 
  615. if ( ! function_exists( 'woocommerce_template_loop_product_thumbnail' ) ) { 
  616.  
  617. /** 
  618. * Get the product thumbnail for the loop. 
  619. * 
  620. * @subpackage Loop 
  621. */ 
  622. function woocommerce_template_loop_product_thumbnail() { 
  623. echo woocommerce_get_product_thumbnail(); 
  624. if ( ! function_exists( 'woocommerce_template_loop_price' ) ) { 
  625.  
  626. /** 
  627. * Get the product price for the loop. 
  628. * 
  629. * @subpackage Loop 
  630. */ 
  631. function woocommerce_template_loop_price() { 
  632. wc_get_template( 'loop/price.php' ); 
  633. if ( ! function_exists( 'woocommerce_template_loop_rating' ) ) { 
  634.  
  635. /** 
  636. * Display the average rating in the loop. 
  637. * 
  638. * @subpackage Loop 
  639. */ 
  640. function woocommerce_template_loop_rating() { 
  641. wc_get_template( 'loop/rating.php' ); 
  642. if ( ! function_exists( 'woocommerce_show_product_loop_sale_flash' ) ) { 
  643.  
  644. /** 
  645. * Get the sale flash for the loop. 
  646. * 
  647. * @subpackage Loop 
  648. */ 
  649. function woocommerce_show_product_loop_sale_flash() { 
  650. wc_get_template( 'loop/sale-flash.php' ); 
  651.  
  652. if ( ! function_exists( 'woocommerce_get_product_thumbnail' ) ) { 
  653.  
  654. /** 
  655. * Get the product thumbnail, or the placeholder if not set. 
  656. * 
  657. * @subpackage Loop 
  658. * @param string $size (default: 'shop_catalog') 
  659. * @param int $deprecated1 Deprecated since WooCommerce 2.0 (default: 0) 
  660. * @param int $deprecated2 Deprecated since WooCommerce 2.0 (default: 0) 
  661. * @return string 
  662. */ 
  663. function woocommerce_get_product_thumbnail( $size = 'shop_catalog', $deprecated1 = 0, $deprecated2 = 0 ) { 
  664. global $post; 
  665. $image_size = apply_filters( 'single_product_archive_thumbnail_size', $size ); 
  666.  
  667. if ( has_post_thumbnail() ) { 
  668. $props = wc_get_product_attachment_props( get_post_thumbnail_id(), $post ); 
  669. return get_the_post_thumbnail( $post->ID, $image_size, array( 
  670. 'title' => $props['title'],  
  671. 'alt' => $props['alt'],  
  672. ) ); 
  673. } elseif ( wc_placeholder_img_src() ) { 
  674. return wc_placeholder_img( $image_size ); 
  675.  
  676. if ( ! function_exists( 'woocommerce_result_count' ) ) { 
  677.  
  678. /** 
  679. * Output the result count text (Showing x - x of x results). 
  680. * 
  681. * @subpackage Loop 
  682. */ 
  683. function woocommerce_result_count() { 
  684. wc_get_template( 'loop/result-count.php' ); 
  685.  
  686. if ( ! function_exists( 'woocommerce_catalog_ordering' ) ) { 
  687.  
  688. /** 
  689. * Output the product sorting options. 
  690. * 
  691. * @subpackage Loop 
  692. */ 
  693. function woocommerce_catalog_ordering() { 
  694. global $wp_query; 
  695.  
  696. if ( 1 === (int) $wp_query->found_posts || ! woocommerce_products_will_display() ) { 
  697. return; 
  698.  
  699. $orderby = isset( $_GET['orderby'] ) ? wc_clean( $_GET['orderby'] ) : apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) ); 
  700. $show_default_orderby = 'menu_order' === apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) ); 
  701. $catalog_orderby_options = apply_filters( 'woocommerce_catalog_orderby', array( 
  702. 'menu_order' => __( 'Default sorting', 'woocommerce' ),  
  703. 'popularity' => __( 'Sort by popularity', 'woocommerce' ),  
  704. 'rating' => __( 'Sort by average rating', 'woocommerce' ),  
  705. 'date' => __( 'Sort by newness', 'woocommerce' ),  
  706. 'price' => __( 'Sort by price: low to high', 'woocommerce' ),  
  707. 'price-desc' => __( 'Sort by price: high to low', 'woocommerce' ),  
  708. ) ); 
  709.  
  710. if ( ! $show_default_orderby ) { 
  711. unset( $catalog_orderby_options['menu_order'] ); 
  712.  
  713. if ( 'no' === get_option( 'woocommerce_enable_review_rating' ) ) { 
  714. unset( $catalog_orderby_options['rating'] ); 
  715.  
  716. wc_get_template( 'loop/orderby.php', array( 'catalog_orderby_options' => $catalog_orderby_options, 'orderby' => $orderby, 'show_default_orderby' => $show_default_orderby ) ); 
  717.  
  718. if ( ! function_exists( 'woocommerce_pagination' ) ) { 
  719.  
  720. /** 
  721. * Output the pagination. 
  722. * 
  723. * @subpackage Loop 
  724. */ 
  725. function woocommerce_pagination() { 
  726. wc_get_template( 'loop/pagination.php' ); 
  727.  
  728. /** Single Product ********************************************************/ 
  729.  
  730. if ( ! function_exists( 'woocommerce_show_product_images' ) ) { 
  731.  
  732. /** 
  733. * Output the product image before the single product summary. 
  734. * 
  735. * @subpackage Product 
  736. */ 
  737. function woocommerce_show_product_images() { 
  738. wc_get_template( 'single-product/product-image.php' ); 
  739. if ( ! function_exists( 'woocommerce_show_product_thumbnails' ) ) { 
  740.  
  741. /** 
  742. * Output the product thumbnails. 
  743. * 
  744. * @subpackage Product 
  745. */ 
  746. function woocommerce_show_product_thumbnails() { 
  747. wc_get_template( 'single-product/product-thumbnails.php' ); 
  748. if ( ! function_exists( 'woocommerce_output_product_data_tabs' ) ) { 
  749.  
  750. /** 
  751. * Output the product tabs. 
  752. * 
  753. * @subpackage Product/Tabs 
  754. */ 
  755. function woocommerce_output_product_data_tabs() { 
  756. wc_get_template( 'single-product/tabs/tabs.php' ); 
  757. if ( ! function_exists( 'woocommerce_template_single_title' ) ) { 
  758.  
  759. /** 
  760. * Output the product title. 
  761. * 
  762. * @subpackage Product 
  763. */ 
  764. function woocommerce_template_single_title() { 
  765. wc_get_template( 'single-product/title.php' ); 
  766. if ( ! function_exists( 'woocommerce_template_single_rating' ) ) { 
  767.  
  768. /** 
  769. * Output the product rating. 
  770. * 
  771. * @subpackage Product 
  772. */ 
  773. function woocommerce_template_single_rating() { 
  774. wc_get_template( 'single-product/rating.php' ); 
  775. if ( ! function_exists( 'woocommerce_template_single_price' ) ) { 
  776.  
  777. /** 
  778. * Output the product price. 
  779. * 
  780. * @subpackage Product 
  781. */ 
  782. function woocommerce_template_single_price() { 
  783. wc_get_template( 'single-product/price.php' ); 
  784. if ( ! function_exists( 'woocommerce_template_single_excerpt' ) ) { 
  785.  
  786. /** 
  787. * Output the product short description (excerpt). 
  788. * 
  789. * @subpackage Product 
  790. */ 
  791. function woocommerce_template_single_excerpt() { 
  792. wc_get_template( 'single-product/short-description.php' ); 
  793. if ( ! function_exists( 'woocommerce_template_single_meta' ) ) { 
  794.  
  795. /** 
  796. * Output the product meta. 
  797. * 
  798. * @subpackage Product 
  799. */ 
  800. function woocommerce_template_single_meta() { 
  801. wc_get_template( 'single-product/meta.php' ); 
  802. if ( ! function_exists( 'woocommerce_template_single_sharing' ) ) { 
  803.  
  804. /** 
  805. * Output the product sharing. 
  806. * 
  807. * @subpackage Product 
  808. */ 
  809. function woocommerce_template_single_sharing() { 
  810. wc_get_template( 'single-product/share.php' ); 
  811. if ( ! function_exists( 'woocommerce_show_product_sale_flash' ) ) { 
  812.  
  813. /** 
  814. * Output the product sale flash. 
  815. * 
  816. * @subpackage Product 
  817. */ 
  818. function woocommerce_show_product_sale_flash() { 
  819. wc_get_template( 'single-product/sale-flash.php' ); 
  820.  
  821. if ( ! function_exists( 'woocommerce_template_single_add_to_cart' ) ) { 
  822.  
  823. /** 
  824. * Trigger the single product add to cart action. 
  825. * 
  826. * @subpackage Product 
  827. */ 
  828. function woocommerce_template_single_add_to_cart() { 
  829. global $product; 
  830. do_action( 'woocommerce_' . $product->get_type() . '_add_to_cart' ); 
  831. if ( ! function_exists( 'woocommerce_simple_add_to_cart' ) ) { 
  832.  
  833. /** 
  834. * Output the simple product add to cart area. 
  835. * 
  836. * @subpackage Product 
  837. */ 
  838. function woocommerce_simple_add_to_cart() { 
  839. wc_get_template( 'single-product/add-to-cart/simple.php' ); 
  840. if ( ! function_exists( 'woocommerce_grouped_add_to_cart' ) ) { 
  841.  
  842. /** 
  843. * Output the grouped product add to cart area. 
  844. * 
  845. * @subpackage Product 
  846. */ 
  847. function woocommerce_grouped_add_to_cart() { 
  848. global $product; 
  849.  
  850. $products = array_filter( array_map( 'wc_get_product', $product->get_children() ) ); 
  851.  
  852. if ( $products ) { 
  853. usort( $products, 'wc_products_array_orderby_menu_order' ); 
  854.  
  855. wc_get_template( 'single-product/add-to-cart/grouped.php', array( 
  856. 'grouped_product' => $product,  
  857. 'grouped_products' => $products,  
  858. 'quantites_required' => false,  
  859. ) ); 
  860. if ( ! function_exists( 'woocommerce_variable_add_to_cart' ) ) { 
  861.  
  862. /** 
  863. * Output the variable product add to cart area. 
  864. * 
  865. * @subpackage Product 
  866. */ 
  867. function woocommerce_variable_add_to_cart() { 
  868. global $product; 
  869.  
  870. // Enqueue variation scripts 
  871. wp_enqueue_script( 'wc-add-to-cart-variation' ); 
  872.  
  873. // Get Available variations? 
  874. $get_variations = sizeof( $product->get_children() ) <= apply_filters( 'woocommerce_ajax_variation_threshold', 30, $product ); 
  875.  
  876. // Load the template 
  877. wc_get_template( 'single-product/add-to-cart/variable.php', array( 
  878. 'available_variations' => $get_variations ? $product->get_available_variations() : false,  
  879. 'attributes' => $product->get_variation_attributes(),  
  880. 'selected_attributes' => $product->get_default_attributes(),  
  881. ) ); 
  882. if ( ! function_exists( 'woocommerce_external_add_to_cart' ) ) { 
  883.  
  884. /** 
  885. * Output the external product add to cart area. 
  886. * 
  887. * @subpackage Product 
  888. */ 
  889. function woocommerce_external_add_to_cart() { 
  890. global $product; 
  891.  
  892. if ( ! $product->add_to_cart_url() ) { 
  893. return; 
  894.  
  895. wc_get_template( 'single-product/add-to-cart/external.php', array( 
  896. 'product_url' => $product->add_to_cart_url(),  
  897. 'button_text' => $product->single_add_to_cart_text(),  
  898. ) ); 
  899.  
  900. if ( ! function_exists( 'woocommerce_quantity_input' ) ) { 
  901.  
  902. /** 
  903. * Output the quantity input for add to cart forms. 
  904. * 
  905. * @param array $args Args for the input 
  906. * @param WC_Product|null $product 
  907. * @param boolean $echo Whether to return or echo|string 
  908. */ 
  909. function woocommerce_quantity_input( $args = array(), $product = null, $echo = true ) { 
  910. if ( is_null( $product ) ) { 
  911. $product = $GLOBALS['product']; 
  912.  
  913. $defaults = array( 
  914. 'input_name' => 'quantity',  
  915. 'input_value' => '1',  
  916. 'max_value' => apply_filters( 'woocommerce_quantity_input_max', -1, $product ),  
  917. 'min_value' => apply_filters( 'woocommerce_quantity_input_min', 0, $product ),  
  918. 'step' => apply_filters( 'woocommerce_quantity_input_step', 1, $product ),  
  919. 'pattern' => apply_filters( 'woocommerce_quantity_input_pattern', has_filter( 'woocommerce_stock_amount', 'intval' ) ? '[0-9]*' : '' ),  
  920. 'inputmode' => apply_filters( 'woocommerce_quantity_input_inputmode', has_filter( 'woocommerce_stock_amount', 'intval' ) ? 'numeric' : '' ),  
  921. ); 
  922.  
  923. $args = apply_filters( 'woocommerce_quantity_input_args', wp_parse_args( $args, $defaults ), $product ); 
  924.  
  925. // Apply sanity to min/max args - min cannot be lower than 0. 
  926. $args['min_value'] = max( $args['min_value'], 0 ); 
  927. $args['max_value'] = 0 < $args['max_value'] ? $args['max_value'] : ''; 
  928.  
  929. // Max cannot be lower than min if defined. 
  930. if ( '' !== $args['max_value'] && $args['max_value'] < $args['min_value'] ) { 
  931. $args['max_value'] = $args['min_value']; 
  932.  
  933. ob_start(); 
  934.  
  935. wc_get_template( 'global/quantity-input.php', $args ); 
  936.  
  937. if ( $echo ) { 
  938. echo ob_get_clean(); 
  939. } else { 
  940. return ob_get_clean(); 
  941.  
  942. if ( ! function_exists( 'woocommerce_product_description_tab' ) ) { 
  943.  
  944. /** 
  945. * Output the description tab content. 
  946. * 
  947. * @subpackage Product/Tabs 
  948. */ 
  949. function woocommerce_product_description_tab() { 
  950. wc_get_template( 'single-product/tabs/description.php' ); 
  951. if ( ! function_exists( 'woocommerce_product_additional_information_tab' ) ) { 
  952.  
  953. /** 
  954. * Output the attributes tab content. 
  955. * 
  956. * @subpackage Product/Tabs 
  957. */ 
  958. function woocommerce_product_additional_information_tab() { 
  959. wc_get_template( 'single-product/tabs/additional-information.php' ); 
  960. if ( ! function_exists( 'woocommerce_product_reviews_tab' ) ) { 
  961.  
  962. /** 
  963. * Output the reviews tab content. 
  964. * @deprecated 2.4.0 Unused 
  965. * @subpackage Product/Tabs 
  966. */ 
  967. function woocommerce_product_reviews_tab() { 
  968. wc_deprecated_function( 'woocommerce_product_reviews_tab', '2.4' ); 
  969.  
  970. if ( ! function_exists( 'woocommerce_default_product_tabs' ) ) { 
  971.  
  972. /** 
  973. * Add default product tabs to product pages. 
  974. * 
  975. * @param array $tabs 
  976. * @return array 
  977. */ 
  978. function woocommerce_default_product_tabs( $tabs = array() ) { 
  979. global $product, $post; 
  980.  
  981. // Description tab - shows product content 
  982. if ( $post->post_content ) { 
  983. $tabs['description'] = array( 
  984. 'title' => __( 'Description', 'woocommerce' ),  
  985. 'priority' => 10,  
  986. 'callback' => 'woocommerce_product_description_tab',  
  987. ); 
  988.  
  989. // Additional information tab - shows attributes 
  990. if ( $product && ( $product->has_attributes() || apply_filters( 'wc_product_enable_dimensions_display', $product->has_weight() || $product->has_dimensions() ) ) ) { 
  991. $tabs['additional_information'] = array( 
  992. 'title' => __( 'Additional information', 'woocommerce' ),  
  993. 'priority' => 20,  
  994. 'callback' => 'woocommerce_product_additional_information_tab',  
  995. ); 
  996.  
  997. // Reviews tab - shows comments 
  998. if ( comments_open() ) { 
  999. $tabs['reviews'] = array( 
  1000. 'title' => sprintf( __( 'Reviews (%d)', 'woocommerce' ), $product->get_review_count() ),  
  1001. 'priority' => 30,  
  1002. 'callback' => 'comments_template',  
  1003. ); 
  1004.  
  1005. return $tabs; 
  1006.  
  1007. if ( ! function_exists( 'woocommerce_sort_product_tabs' ) ) { 
  1008.  
  1009. /** 
  1010. * Sort tabs by priority. 
  1011. * 
  1012. * @param array $tabs 
  1013. * @return array 
  1014. */ 
  1015. function woocommerce_sort_product_tabs( $tabs = array() ) { 
  1016.  
  1017. // Make sure the $tabs parameter is an array 
  1018. if ( ! is_array( $tabs ) ) { 
  1019. trigger_error( "Function woocommerce_sort_product_tabs() expects an array as the first parameter. Defaulting to empty array." ); 
  1020. $tabs = array(); 
  1021.  
  1022. // Re-order tabs by priority 
  1023. if ( ! function_exists( '_sort_priority_callback' ) ) { 
  1024. function _sort_priority_callback( $a, $b ) { 
  1025. if ( $a['priority'] === $b['priority'] ) 
  1026. return 0; 
  1027. return ( $a['priority'] < $b['priority'] ) ? -1 : 1; 
  1028.  
  1029. uasort( $tabs, '_sort_priority_callback' ); 
  1030.  
  1031. return $tabs; 
  1032.  
  1033. if ( ! function_exists( 'woocommerce_comments' ) ) { 
  1034.  
  1035. /** 
  1036. * Output the Review comments template. 
  1037. * 
  1038. * @subpackage Product 
  1039. * @param WP_Comment $comment 
  1040. * @param array $args 
  1041. * @param int $depth 
  1042. */ 
  1043. function woocommerce_comments( $comment, $args, $depth ) { 
  1044. $GLOBALS['comment'] = $comment; 
  1045. wc_get_template( 'single-product/review.php', array( 'comment' => $comment, 'args' => $args, 'depth' => $depth ) ); 
  1046.  
  1047. if ( ! function_exists( 'woocommerce_review_display_gravatar' ) ) { 
  1048. /** 
  1049. * Display the review authors gravatar 
  1050. * 
  1051. * @param array $comment WP_Comment. 
  1052. * @return void 
  1053. */ 
  1054. function woocommerce_review_display_gravatar( $comment ) { 
  1055. echo get_avatar( $comment, apply_filters( 'woocommerce_review_gravatar_size', '60' ), '' ); 
  1056.  
  1057. if ( ! function_exists( 'woocommerce_review_display_rating' ) ) { 
  1058. /** 
  1059. * Display the reviewers star rating 
  1060. * 
  1061. * @return void 
  1062. */ 
  1063. function woocommerce_review_display_rating() { 
  1064. wc_get_template( 'single-product/review-rating.php' ); 
  1065.  
  1066. if ( ! function_exists( 'woocommerce_review_display_meta' ) ) { 
  1067. /** 
  1068. * Display the review authors meta (name, verified owner, review date) 
  1069. * 
  1070. * @return void 
  1071. */ 
  1072. function woocommerce_review_display_meta() { 
  1073. wc_get_template( 'single-product/review-meta.php' ); 
  1074.  
  1075. if ( ! function_exists( 'woocommerce_review_display_comment_text' ) ) { 
  1076.  
  1077. /** 
  1078. * Display the review content. 
  1079. */ 
  1080. function woocommerce_review_display_comment_text() { 
  1081. echo '<div class="description">'; 
  1082. comment_text(); 
  1083. echo '</div>'; 
  1084.  
  1085. if ( ! function_exists( 'woocommerce_output_related_products' ) ) { 
  1086.  
  1087. /** 
  1088. * Output the related products. 
  1089. * 
  1090. * @subpackage Product 
  1091. */ 
  1092. function woocommerce_output_related_products() { 
  1093.  
  1094. $args = array( 
  1095. 'posts_per_page' => 4,  
  1096. 'columns' => 4,  
  1097. 'orderby' => 'rand',  
  1098. ); 
  1099.  
  1100. woocommerce_related_products( apply_filters( 'woocommerce_output_related_products_args', $args ) ); 
  1101.  
  1102. if ( ! function_exists( 'woocommerce_related_products' ) ) { 
  1103.  
  1104. /** 
  1105. * Output the related products. 
  1106. * 
  1107. * @param array Provided arguments 
  1108. */ 
  1109. function woocommerce_related_products( $args = array() ) { 
  1110. global $product, $woocommerce_loop; 
  1111.  
  1112. if ( ! $product ) { 
  1113. return; 
  1114.  
  1115. $defaults = array( 
  1116. 'posts_per_page' => 2,  
  1117. 'columns' => 2,  
  1118. 'orderby' => 'rand',  
  1119. 'order' => 'desc',  
  1120. ); 
  1121.  
  1122. $args = wp_parse_args( $args, $defaults ); 
  1123.  
  1124. // Get visble related products then sort them at random. 
  1125. $args['related_products'] = array_filter( array_map( 'wc_get_product', wc_get_related_products( $product->get_id(), $args['posts_per_page'], $product->get_upsell_ids() ) ), 'wc_products_array_filter_visible' ); 
  1126.  
  1127. // Handle orderby. 
  1128. $args['related_products'] = wc_products_array_orderby( $args['related_products'], $args['orderby'], $args['order'] ); 
  1129.  
  1130. // Set global loop values. 
  1131. $woocommerce_loop['name'] = 'related'; 
  1132. $woocommerce_loop['columns'] = apply_filters( 'woocommerce_related_products_columns', $args['columns'] ); 
  1133.  
  1134. wc_get_template( 'single-product/related.php', $args ); 
  1135.  
  1136. if ( ! function_exists( 'woocommerce_upsell_display' ) ) { 
  1137.  
  1138. /** 
  1139. * Output product up sells. 
  1140. * 
  1141. * @param int $limit (default: -1) 
  1142. * @param int $columns (default: 4) 
  1143. * @param string $orderby Supported values - rand, title, ID, date, modified, menu_order, price. 
  1144. * @param string $order Sort direction. 
  1145. */ 
  1146. function woocommerce_upsell_display( $limit = '-1', $columns = 4, $orderby = 'rand', $order = 'desc' ) { 
  1147. global $product, $woocommerce_loop; 
  1148.  
  1149. if ( ! $product ) { 
  1150. return; 
  1151.  
  1152. // Handle the legacy filter which controlled posts per page etc. 
  1153. $args = apply_filters( 'woocommerce_upsell_display_args', array( 
  1154. 'posts_per_page' => $limit,  
  1155. 'orderby' => $orderby,  
  1156. 'columns' => $columns,  
  1157. ) ); 
  1158. $woocommerce_loop['name'] = 'up-sells'; 
  1159. $woocommerce_loop['columns'] = apply_filters( 'woocommerce_upsells_columns', isset( $args['columns'] ) ? $args['columns'] : $columns ); 
  1160. $orderby = apply_filters( 'woocommerce_upsells_orderby', isset( $args['orderby'] ) ? $args['orderby'] : $orderby ); 
  1161. $limit = apply_filters( 'woocommerce_upsells_total', isset( $args['posts_per_page'] ) ? $args['posts_per_page'] : $limit ); 
  1162.  
  1163. // Get visble upsells then sort them at random, then limit result set. 
  1164. $upsells = wc_products_array_orderby( array_filter( array_map( 'wc_get_product', $product->get_upsell_ids() ), 'wc_products_array_filter_visible' ), $orderby, $order ); 
  1165. $upsells = $limit > 0 ? array_slice( $upsells, 0, $limit ) : $upsells; 
  1166.  
  1167. wc_get_template( 'single-product/up-sells.php', array( 
  1168. 'upsells' => $upsells,  
  1169.  
  1170. // Not used now, but used in previous version of up-sells.php. 
  1171. 'posts_per_page' => $limit,  
  1172. 'orderby' => $orderby,  
  1173. 'columns' => $columns,  
  1174. ) ); 
  1175.  
  1176. /** Cart ******************************************************************/ 
  1177.  
  1178. if ( ! function_exists( 'woocommerce_shipping_calculator' ) ) { 
  1179.  
  1180. /** 
  1181. * Output the cart shipping calculator. 
  1182. * 
  1183. * @subpackage Cart 
  1184. */ 
  1185. function woocommerce_shipping_calculator() { 
  1186. wc_get_template( 'cart/shipping-calculator.php' ); 
  1187.  
  1188. if ( ! function_exists( 'woocommerce_cart_totals' ) ) { 
  1189.  
  1190. /** 
  1191. * Output the cart totals. 
  1192. * 
  1193. * @subpackage Cart 
  1194. */ 
  1195. function woocommerce_cart_totals() { 
  1196. if ( is_checkout() ) { 
  1197. return; 
  1198. wc_get_template( 'cart/cart-totals.php' ); 
  1199.  
  1200. if ( ! function_exists( 'woocommerce_cross_sell_display' ) ) { 
  1201.  
  1202. /** 
  1203. * Output the cart cross-sells. 
  1204. * 
  1205. * @param int $limit (default: 2) 
  1206. * @param int $columns (default: 2) 
  1207. * @param string $orderby (default: 'rand') 
  1208. * @param string $order (default: 'desc') 
  1209. */ 
  1210. function woocommerce_cross_sell_display( $limit = 2, $columns = 2, $orderby = 'rand', $order = 'desc' ) { 
  1211. global $woocommerce_loop; 
  1212.  
  1213. if ( is_checkout() ) { 
  1214. return; 
  1215. // Get visble cross sells then sort them at random. 
  1216. $cross_sells = array_filter( array_map( 'wc_get_product', WC()->cart->get_cross_sells() ), 'wc_products_array_filter_visible' ); 
  1217. $woocommerce_loop['name'] = 'cross-sells'; 
  1218. $woocommerce_loop['columns'] = apply_filters( 'woocommerce_cross_sells_columns', $columns ); 
  1219.  
  1220. // Handle orderby and limit results. 
  1221. $orderby = apply_filters( 'woocommerce_cross_sells_orderby', $orderby ); 
  1222. $cross_sells = wc_products_array_orderby( $cross_sells, $orderby, $order ); 
  1223. $limit = apply_filters( 'woocommerce_cross_sells_total', $limit ); 
  1224. $cross_sells = $limit > 0 ? array_slice( $cross_sells, 0, $limit ) : $cross_sells; 
  1225.  
  1226. wc_get_template( 'cart/cross-sells.php', array( 
  1227. 'cross_sells' => $cross_sells,  
  1228.  
  1229. // Not used now, but used in previous version of up-sells.php. 
  1230. 'posts_per_page' => $limit,  
  1231. 'orderby' => $orderby,  
  1232. 'columns' => $columns,  
  1233. ) ); 
  1234.  
  1235. if ( ! function_exists( 'woocommerce_button_proceed_to_checkout' ) ) { 
  1236.  
  1237. /** 
  1238. * Output the proceed to checkout button. 
  1239. * 
  1240. * @subpackage Cart 
  1241. */ 
  1242. function woocommerce_button_proceed_to_checkout() { 
  1243. wc_get_template( 'cart/proceed-to-checkout-button.php' ); 
  1244.  
  1245. if ( ! function_exists( 'woocommerce_widget_shopping_cart_button_view_cart' ) ) { 
  1246.  
  1247. /** 
  1248. * Output the proceed to checkout button. 
  1249. * 
  1250. * @subpackage Cart 
  1251. */ 
  1252. function woocommerce_widget_shopping_cart_button_view_cart() { 
  1253. echo '<a href="' . esc_url( wc_get_cart_url() ) . '" class="button wc-forward">' . esc_html__( 'View cart', 'woocommerce' ) . '</a>'; 
  1254.  
  1255. if ( ! function_exists( 'woocommerce_widget_shopping_cart_proceed_to_checkout' ) ) { 
  1256.  
  1257. /** 
  1258. * Output the proceed to checkout button. 
  1259. * 
  1260. * @subpackage Cart 
  1261. */ 
  1262. function woocommerce_widget_shopping_cart_proceed_to_checkout() { 
  1263. echo '<a href="' . esc_url( wc_get_checkout_url() ) . '" class="button checkout wc-forward">' . esc_html__( 'Checkout', 'woocommerce' ) . '</a>'; 
  1264.  
  1265. /** Mini-Cart *************************************************************/ 
  1266.  
  1267. if ( ! function_exists( 'woocommerce_mini_cart' ) ) { 
  1268.  
  1269. /** 
  1270. * Output the Mini-cart - used by cart widget. 
  1271. * 
  1272. * @param array $args 
  1273. */ 
  1274. function woocommerce_mini_cart( $args = array() ) { 
  1275.  
  1276. $defaults = array( 
  1277. 'list_class' => '',  
  1278. ); 
  1279.  
  1280. $args = wp_parse_args( $args, $defaults ); 
  1281.  
  1282. wc_get_template( 'cart/mini-cart.php', $args ); 
  1283.  
  1284. /** Login *****************************************************************/ 
  1285.  
  1286. if ( ! function_exists( 'woocommerce_login_form' ) ) { 
  1287.  
  1288. /** 
  1289. * Output the WooCommerce Login Form. 
  1290. * 
  1291. * @subpackage Forms 
  1292. * @param array $args 
  1293. */ 
  1294. function woocommerce_login_form( $args = array() ) { 
  1295.  
  1296. $defaults = array( 
  1297. 'message' => '',  
  1298. 'redirect' => '',  
  1299. 'hidden' => false,  
  1300. ); 
  1301.  
  1302. $args = wp_parse_args( $args, $defaults ); 
  1303.  
  1304. wc_get_template( 'global/form-login.php', $args ); 
  1305.  
  1306. if ( ! function_exists( 'woocommerce_checkout_login_form' ) ) { 
  1307.  
  1308. /** 
  1309. * Output the WooCommerce Checkout Login Form. 
  1310. * 
  1311. * @subpackage Checkout 
  1312. */ 
  1313. function woocommerce_checkout_login_form() { 
  1314. wc_get_template( 'checkout/form-login.php', array( 'checkout' => WC()->checkout() ) ); 
  1315.  
  1316. if ( ! function_exists( 'woocommerce_breadcrumb' ) ) { 
  1317.  
  1318. /** 
  1319. * Output the WooCommerce Breadcrumb. 
  1320. * 
  1321. * @param array $args 
  1322. */ 
  1323. function woocommerce_breadcrumb( $args = array() ) { 
  1324. $args = wp_parse_args( $args, apply_filters( 'woocommerce_breadcrumb_defaults', array( 
  1325. 'delimiter' => ' / ',  
  1326. 'wrap_before' => '<nav class="woocommerce-breadcrumb">',  
  1327. 'wrap_after' => '</nav>',  
  1328. 'before' => '',  
  1329. 'after' => '',  
  1330. 'home' => _x( 'Home', 'breadcrumb', 'woocommerce' ),  
  1331. ) ) ); 
  1332.  
  1333. $breadcrumbs = new WC_Breadcrumb(); 
  1334.  
  1335. if ( ! empty( $args['home'] ) ) { 
  1336. $breadcrumbs->add_crumb( $args['home'], apply_filters( 'woocommerce_breadcrumb_home_url', home_url() ) ); 
  1337.  
  1338. $args['breadcrumb'] = $breadcrumbs->generate(); 
  1339.  
  1340. /** 
  1341. * @hooked WC_Structured_Data::generate_breadcrumblist_data() - 10 
  1342. */ 
  1343. do_action( 'woocommerce_breadcrumb', $breadcrumbs, $args ); 
  1344.  
  1345. wc_get_template( 'global/breadcrumb.php', $args ); 
  1346.  
  1347. if ( ! function_exists( 'woocommerce_order_review' ) ) { 
  1348.  
  1349. /** 
  1350. * Output the Order review table for the checkout. 
  1351. * 
  1352. * @subpackage Checkout 
  1353. */ 
  1354. function woocommerce_order_review( $deprecated = false ) { 
  1355. wc_get_template( 'checkout/review-order.php', array( 'checkout' => WC()->checkout() ) ); 
  1356.  
  1357. if ( ! function_exists( 'woocommerce_checkout_payment' ) ) { 
  1358.  
  1359. /** 
  1360. * Output the Payment Methods on the checkout. 
  1361. * 
  1362. * @subpackage Checkout 
  1363. */ 
  1364. function woocommerce_checkout_payment() { 
  1365. if ( WC()->cart->needs_payment() ) { 
  1366. $available_gateways = WC()->payment_gateways()->get_available_payment_gateways(); 
  1367. WC()->payment_gateways()->set_current_gateway( $available_gateways ); 
  1368. } else { 
  1369. $available_gateways = array(); 
  1370.  
  1371. wc_get_template( 'checkout/payment.php', array( 
  1372. 'checkout' => WC()->checkout(),  
  1373. 'available_gateways' => $available_gateways,  
  1374. 'order_button_text' => apply_filters( 'woocommerce_order_button_text', __( 'Place order', 'woocommerce' ) ),  
  1375. ) ); 
  1376.  
  1377. if ( ! function_exists( 'woocommerce_checkout_coupon_form' ) ) { 
  1378.  
  1379. /** 
  1380. * Output the Coupon form for the checkout. 
  1381. * 
  1382. * @subpackage Checkout 
  1383. */ 
  1384. function woocommerce_checkout_coupon_form() { 
  1385. wc_get_template( 'checkout/form-coupon.php', array( 'checkout' => WC()->checkout() ) ); 
  1386.  
  1387. if ( ! function_exists( 'woocommerce_products_will_display' ) ) { 
  1388.  
  1389. /** 
  1390. * Check if we will be showing products or not (and not sub-categories only). 
  1391. * @subpackage Loop 
  1392. * @return bool 
  1393. */ 
  1394. function woocommerce_products_will_display() { 
  1395. global $wpdb; 
  1396.  
  1397. if ( is_shop() ) { 
  1398. return 'subcategories' !== get_option( 'woocommerce_shop_page_display' ) || is_search(); 
  1399.  
  1400. if ( ! is_product_taxonomy() ) { 
  1401. return false; 
  1402.  
  1403. if ( is_search() || is_filtered() || is_paged() ) { 
  1404. return true; 
  1405.  
  1406. $term = get_queried_object(); 
  1407.  
  1408. if ( is_product_category() ) { 
  1409. switch ( get_woocommerce_term_meta( $term->term_id, 'display_type', true ) ) { 
  1410. case 'subcategories' : 
  1411. // Nothing - we want to continue to see if there are products/subcats 
  1412. break; 
  1413. case 'products' : 
  1414. case 'both' : 
  1415. return true; 
  1416. break; 
  1417. default : 
  1418. // Default - no setting 
  1419. if ( get_option( 'woocommerce_category_archive_display' ) != 'subcategories' ) { 
  1420. return true; 
  1421. break; 
  1422.  
  1423. // Begin subcategory logic 
  1424. if ( empty( $term->term_id ) || empty( $term->taxonomy ) ) { 
  1425. return true; 
  1426.  
  1427. $transient_name = 'wc_products_will_display_' . $term->term_id . '_' . WC_Cache_Helper::get_transient_version( 'product_query' ); 
  1428.  
  1429. if ( false === ( $products_will_display = get_transient( $transient_name ) ) ) { 
  1430. $has_children = $wpdb->get_col( $wpdb->prepare( "SELECT term_id FROM {$wpdb->term_taxonomy} WHERE parent = %d AND taxonomy = %s", $term->term_id, $term->taxonomy ) ); 
  1431.  
  1432. if ( $has_children ) { 
  1433. // Check terms have products inside - parents first. If products are found inside, subcats will be shown instead of products so we can return false. 
  1434. if ( sizeof( get_objects_in_term( $has_children, $term->taxonomy ) ) > 0 ) { 
  1435. $products_will_display = false; 
  1436. } else { 
  1437. // If we get here, the parents were empty so we're forced to check children 
  1438. foreach ( $has_children as $term_id ) { 
  1439. $children = get_term_children( $term_id, $term->taxonomy ); 
  1440.  
  1441. if ( sizeof( get_objects_in_term( $children, $term->taxonomy ) ) > 0 ) { 
  1442. $products_will_display = false; 
  1443. break; 
  1444. } else { 
  1445. $products_will_display = true; 
  1446.  
  1447. set_transient( $transient_name, $products_will_display, DAY_IN_SECONDS * 30 ); 
  1448.  
  1449. return $products_will_display; 
  1450.  
  1451. if ( ! function_exists( 'woocommerce_product_subcategories' ) ) { 
  1452.  
  1453. /** 
  1454. * Display product sub categories as thumbnails. 
  1455. * 
  1456. * @subpackage Loop 
  1457. * @param array $args 
  1458. * @return null|boolean 
  1459. */ 
  1460. function woocommerce_product_subcategories( $args = array() ) { 
  1461. global $wp_query; 
  1462.  
  1463. $defaults = array( 
  1464. 'before' => '',  
  1465. 'after' => '',  
  1466. 'force_display' => false,  
  1467. ); 
  1468.  
  1469. $args = wp_parse_args( $args, $defaults ); 
  1470.  
  1471. extract( $args ); 
  1472.  
  1473. // Main query only 
  1474. if ( ! is_main_query() && ! $force_display ) { 
  1475. return; 
  1476.  
  1477. // Don't show when filtering, searching or when on page > 1 and ensure we're on a product archive 
  1478. if ( is_search() || is_filtered() || is_paged() || ( ! is_product_category() && ! is_shop() ) ) { 
  1479. return; 
  1480.  
  1481. // Check categories are enabled 
  1482. if ( is_shop() && '' === get_option( 'woocommerce_shop_page_display' ) ) { 
  1483. return; 
  1484.  
  1485. // Find the category + category parent, if applicable 
  1486. $term = get_queried_object(); 
  1487. $parent_id = empty( $term->term_id ) ? 0 : $term->term_id; 
  1488.  
  1489. if ( is_product_category() ) { 
  1490. $display_type = get_woocommerce_term_meta( $term->term_id, 'display_type', true ); 
  1491.  
  1492. switch ( $display_type ) { 
  1493. case 'products' : 
  1494. return; 
  1495. break; 
  1496. case '' : 
  1497. if ( '' === get_option( 'woocommerce_category_archive_display' ) ) { 
  1498. return; 
  1499. break; 
  1500.  
  1501. // NOTE: using child_of instead of parent - this is not ideal but due to a WP bug ( https://core.trac.wordpress.org/ticket/15626 ) pad_counts won't work 
  1502. $product_categories = get_categories( apply_filters( 'woocommerce_product_subcategories_args', array( 
  1503. 'parent' => $parent_id,  
  1504. 'menu_order' => 'ASC',  
  1505. 'hide_empty' => 0,  
  1506. 'hierarchical' => 1,  
  1507. 'taxonomy' => 'product_cat',  
  1508. 'pad_counts' => 1,  
  1509. ) ) ); 
  1510.  
  1511. if ( apply_filters( 'woocommerce_product_subcategories_hide_empty', true ) ) { 
  1512. $product_categories = wp_list_filter( $product_categories, array( 'count' => 0 ), 'NOT' ); 
  1513.  
  1514. if ( $product_categories ) { 
  1515. echo $before; 
  1516.  
  1517. foreach ( $product_categories as $category ) { 
  1518. wc_get_template( 'content-product_cat.php', array( 
  1519. 'category' => $category,  
  1520. ) ); 
  1521.  
  1522. // If we are hiding products disable the loop and pagination 
  1523. if ( is_product_category() ) { 
  1524. $display_type = get_woocommerce_term_meta( $term->term_id, 'display_type', true ); 
  1525.  
  1526. switch ( $display_type ) { 
  1527. case 'subcategories' : 
  1528. $wp_query->post_count = 0; 
  1529. $wp_query->max_num_pages = 0; 
  1530. break; 
  1531. case '' : 
  1532. if ( 'subcategories' === get_option( 'woocommerce_category_archive_display' ) ) { 
  1533. $wp_query->post_count = 0; 
  1534. $wp_query->max_num_pages = 0; 
  1535. break; 
  1536.  
  1537. if ( is_shop() && 'subcategories' === get_option( 'woocommerce_shop_page_display' ) ) { 
  1538. $wp_query->post_count = 0; 
  1539. $wp_query->max_num_pages = 0; 
  1540.  
  1541. echo $after; 
  1542.  
  1543. return true; 
  1544.  
  1545. if ( ! function_exists( 'woocommerce_subcategory_thumbnail' ) ) { 
  1546.  
  1547. /** 
  1548. * Show subcategory thumbnails. 
  1549. * 
  1550. * @param mixed $category 
  1551. * @subpackage Loop 
  1552. */ 
  1553. function woocommerce_subcategory_thumbnail( $category ) { 
  1554. $small_thumbnail_size = apply_filters( 'subcategory_archive_thumbnail_size', 'shop_catalog' ); 
  1555. $dimensions = wc_get_image_size( $small_thumbnail_size ); 
  1556. $thumbnail_id = get_woocommerce_term_meta( $category->term_id, 'thumbnail_id', true ); 
  1557.  
  1558. if ( $thumbnail_id ) { 
  1559. $image = wp_get_attachment_image_src( $thumbnail_id, $small_thumbnail_size ); 
  1560. $image = $image[0]; 
  1561. $image_srcset = function_exists( 'wp_get_attachment_image_srcset' ) ? wp_get_attachment_image_srcset( $thumbnail_id, $small_thumbnail_size ) : false; 
  1562. $image_sizes = function_exists( 'wp_get_attachment_image_sizes' ) ? wp_get_attachment_image_sizes( $thumbnail_id, $small_thumbnail_size ) : false; 
  1563. } else { 
  1564. $image = wc_placeholder_img_src(); 
  1565. $image_srcset = $image_sizes = false; 
  1566.  
  1567. if ( $image ) { 
  1568. // Prevent esc_url from breaking spaces in urls for image embeds 
  1569. // Ref: https://core.trac.wordpress.org/ticket/23605 
  1570. $image = str_replace( ' ', '%20', $image ); 
  1571.  
  1572. // Add responsive image markup if available 
  1573. if ( $image_srcset && $image_sizes ) { 
  1574. echo '<img src="' . esc_url( $image ) . '" alt="' . esc_attr( $category->name ) . '" width="' . esc_attr( $dimensions['width'] ) . '" height="' . esc_attr( $dimensions['height'] ) . '" srcset="' . esc_attr( $image_srcset ) . '" sizes="' . esc_attr( $image_sizes ) . '" />'; 
  1575. } else { 
  1576. echo '<img src="' . esc_url( $image ) . '" alt="' . esc_attr( $category->name ) . '" width="' . esc_attr( $dimensions['width'] ) . '" height="' . esc_attr( $dimensions['height'] ) . '" />'; 
  1577.  
  1578. if ( ! function_exists( 'woocommerce_order_details_table' ) ) { 
  1579.  
  1580. /** 
  1581. * Displays order details in a table. 
  1582. * 
  1583. * @param mixed $order_id 
  1584. * @subpackage Orders 
  1585. */ 
  1586. function woocommerce_order_details_table( $order_id ) { 
  1587. if ( ! $order_id ) return; 
  1588.  
  1589. wc_get_template( 'order/order-details.php', array( 
  1590. 'order_id' => $order_id,  
  1591. ) ); 
  1592.  
  1593.  
  1594. if ( ! function_exists( 'woocommerce_order_again_button' ) ) { 
  1595.  
  1596. /** 
  1597. * Display an 'order again' button on the view order page. 
  1598. * 
  1599. * @param object $order 
  1600. * @subpackage Orders 
  1601. */ 
  1602. function woocommerce_order_again_button( $order ) { 
  1603. if ( ! $order || ! $order->has_status( 'completed' ) || ! is_user_logged_in() ) { 
  1604. return; 
  1605.  
  1606. wc_get_template( 'order/order-again.php', array( 
  1607. 'order' => $order,  
  1608. ) ); 
  1609.  
  1610. /** Forms ****************************************************************/ 
  1611.  
  1612. if ( ! function_exists( 'woocommerce_form_field' ) ) { 
  1613.  
  1614. /** 
  1615. * Outputs a checkout/address form field. 
  1616. * 
  1617. * @subpackage Forms 
  1618. * @param string $key 
  1619. * @param mixed $args 
  1620. * @param string $value (default: null) 
  1621. */ 
  1622. function woocommerce_form_field( $key, $args, $value = null ) { 
  1623. $defaults = array( 
  1624. 'type' => 'text',  
  1625. 'label' => '',  
  1626. 'description' => '',  
  1627. 'placeholder' => '',  
  1628. 'maxlength' => false,  
  1629. 'required' => false,  
  1630. 'autocomplete' => false,  
  1631. 'id' => $key,  
  1632. 'class' => array(),  
  1633. 'label_class' => array(),  
  1634. 'input_class' => array(),  
  1635. 'return' => false,  
  1636. 'options' => array(),  
  1637. 'custom_attributes' => array(),  
  1638. 'validate' => array(),  
  1639. 'default' => '',  
  1640. 'autofocus' => '',  
  1641. 'priority' => '',  
  1642. ); 
  1643.  
  1644. $args = wp_parse_args( $args, $defaults ); 
  1645. $args = apply_filters( 'woocommerce_form_field_args', $args, $key, $value ); 
  1646.  
  1647. if ( $args['required'] ) { 
  1648. $args['class'][] = 'validate-required'; 
  1649. $required = ' <abbr class="required" title="' . esc_attr__( 'required', 'woocommerce' ) . '">*</abbr>'; 
  1650. } else { 
  1651. $required = ''; 
  1652.  
  1653. if ( is_string( $args['label_class'] ) ) { 
  1654. $args['label_class'] = array( $args['label_class'] ); 
  1655.  
  1656. if ( is_null( $value ) ) { 
  1657. $value = $args['default']; 
  1658.  
  1659. // Custom attribute handling 
  1660. $custom_attributes = array(); 
  1661. $args['custom_attributes'] = array_filter( (array) $args['custom_attributes'] ); 
  1662.  
  1663. if ( $args['maxlength'] ) { 
  1664. $args['custom_attributes']['maxlength'] = absint( $args['maxlength'] ); 
  1665.  
  1666. if ( ! empty( $args['autocomplete'] ) ) { 
  1667. $args['custom_attributes']['autocomplete'] = $args['autocomplete']; 
  1668.  
  1669. if ( true === $args['autofocus'] ) { 
  1670. $args['custom_attributes']['autofocus'] = 'autofocus'; 
  1671.  
  1672. if ( ! empty( $args['custom_attributes'] ) && is_array( $args['custom_attributes'] ) ) { 
  1673. foreach ( $args['custom_attributes'] as $attribute => $attribute_value ) { 
  1674. $custom_attributes[] = esc_attr( $attribute ) . '="' . esc_attr( $attribute_value ) . '"'; 
  1675.  
  1676. if ( ! empty( $args['validate'] ) ) { 
  1677. foreach ( $args['validate'] as $validate ) { 
  1678. $args['class'][] = 'validate-' . $validate; 
  1679.  
  1680. $field = ''; 
  1681. $label_id = $args['id']; 
  1682. $sort = $args['priority'] ? $args['priority'] : ''; 
  1683. $field_container = '<p class="form-row %1$s" id="%2$s" data-priority="' . esc_attr( $sort ) . '">%3$s</p>'; 
  1684.  
  1685. switch ( $args['type'] ) { 
  1686. case 'country' : 
  1687.  
  1688. $countries = 'shipping_country' === $key ? WC()->countries->get_shipping_countries() : WC()->countries->get_allowed_countries(); 
  1689.  
  1690. if ( 1 === sizeof( $countries ) ) { 
  1691.  
  1692. $field .= '<strong>' . current( array_values( $countries ) ) . '</strong>'; 
  1693.  
  1694. $field .= '<input type="hidden" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" value="' . current( array_keys( $countries ) ) . '" ' . implode( ' ', $custom_attributes ) . ' class="country_to_state" />'; 
  1695.  
  1696. } else { 
  1697.  
  1698. $field = '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" class="country_to_state country_select ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" ' . implode( ' ', $custom_attributes ) . '>' . '<option value="">' . esc_html__( 'Select a country…', 'woocommerce' ) . '</option>'; 
  1699.  
  1700. foreach ( $countries as $ckey => $cvalue ) { 
  1701. $field .= '<option value="' . esc_attr( $ckey ) . '" ' . selected( $value, $ckey, false ) . '>' . $cvalue . '</option>'; 
  1702.  
  1703. $field .= '</select>'; 
  1704.  
  1705. $field .= '<noscript><input type="submit" name="woocommerce_checkout_update_totals" value="' . esc_attr__( 'Update country', 'woocommerce' ) . '" /></noscript>'; 
  1706.  
  1707.  
  1708. break; 
  1709. case 'state' : 
  1710.  
  1711. /** Get Country */ 
  1712. $country_key = 'billing_state' === $key ? 'billing_country' : 'shipping_country'; 
  1713. $current_cc = WC()->checkout->get_value( $country_key ); 
  1714. $states = WC()->countries->get_states( $current_cc ); 
  1715.  
  1716. if ( is_array( $states ) && empty( $states ) ) { 
  1717.  
  1718. $field_container = '<p class="form-row %1$s" id="%2$s" style="display: none">%3$s</p>'; 
  1719.  
  1720. $field .= '<input type="hidden" class="hidden" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" value="" ' . implode( ' ', $custom_attributes ) . ' placeholder="' . esc_attr( $args['placeholder'] ) . '" />'; 
  1721.  
  1722. } elseif ( ! is_null( $current_cc ) && is_array( $states ) ) { 
  1723.  
  1724. $field .= '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" class="state_select ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" ' . implode( ' ', $custom_attributes ) . ' data-placeholder="' . esc_attr( $args['placeholder'] ) . '"> 
  1725. <option value="">' . esc_html__( 'Select a state…', 'woocommerce' ) . '</option>'; 
  1726.  
  1727. foreach ( $states as $ckey => $cvalue ) { 
  1728. $field .= '<option value="' . esc_attr( $ckey ) . '" ' . selected( $value, $ckey, false ) . '>' . $cvalue . '</option>'; 
  1729.  
  1730. $field .= '</select>'; 
  1731.  
  1732. } else { 
  1733.  
  1734. $field .= '<input type="text" class="input-text ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" value="' . esc_attr( $value ) . '" placeholder="' . esc_attr( $args['placeholder'] ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" ' . implode( ' ', $custom_attributes ) . ' />'; 
  1735.  
  1736.  
  1737. break; 
  1738. case 'textarea' : 
  1739.  
  1740. $field .= '<textarea name="' . esc_attr( $key ) . '" class="input-text ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" id="' . esc_attr( $args['id'] ) . '" placeholder="' . esc_attr( $args['placeholder'] ) . '" ' . ( empty( $args['custom_attributes']['rows'] ) ? ' rows="2"' : '' ) . ( empty( $args['custom_attributes']['cols'] ) ? ' cols="5"' : '' ) . implode( ' ', $custom_attributes ) . '>' . esc_textarea( $value ) . '</textarea>'; 
  1741.  
  1742. break; 
  1743. case 'checkbox' : 
  1744.  
  1745. $field = '<label class="checkbox ' . implode( ' ', $args['label_class'] ) . '" ' . implode( ' ', $custom_attributes ) . '> 
  1746. <input type="' . esc_attr( $args['type'] ) . '" class="input-checkbox ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" value="1" ' . checked( $value, 1, false ) . ' /> ' 
  1747. . $args['label'] . $required . '</label>'; 
  1748.  
  1749. break; 
  1750. case 'password' : 
  1751. case 'text' : 
  1752. case 'email' : 
  1753. case 'tel' : 
  1754. case 'number' : 
  1755.  
  1756. $field .= '<input type="' . esc_attr( $args['type'] ) . '" class="input-text ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" placeholder="' . esc_attr( $args['placeholder'] ) . '" value="' . esc_attr( $value ) . '" ' . implode( ' ', $custom_attributes ) . ' />'; 
  1757.  
  1758. break; 
  1759. case 'select' : 
  1760.  
  1761. $options = $field = ''; 
  1762.  
  1763. if ( ! empty( $args['options'] ) ) { 
  1764. foreach ( $args['options'] as $option_key => $option_text ) { 
  1765. if ( '' === $option_key ) { 
  1766. // If we have a blank option, select2 needs a placeholder 
  1767. if ( empty( $args['placeholder'] ) ) { 
  1768. $args['placeholder'] = $option_text ? $option_text : __( 'Choose an option', 'woocommerce' ); 
  1769. $custom_attributes[] = 'data-allow_clear="true"'; 
  1770. $options .= '<option value="' . esc_attr( $option_key ) . '" ' . selected( $value, $option_key, false ) . '>' . esc_attr( $option_text ) . '</option>'; 
  1771.  
  1772. $field .= '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" class="select ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" ' . implode( ' ', $custom_attributes ) . ' data-placeholder="' . esc_attr( $args['placeholder'] ) . '"> 
  1773. ' . $options . ' 
  1774. </select>'; 
  1775.  
  1776. break; 
  1777. case 'radio' : 
  1778.  
  1779. $label_id = current( array_keys( $args['options'] ) ); 
  1780.  
  1781. if ( ! empty( $args['options'] ) ) { 
  1782. foreach ( $args['options'] as $option_key => $option_text ) { 
  1783. $field .= '<input type="radio" class="input-radio ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" value="' . esc_attr( $option_key ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '_' . esc_attr( $option_key ) . '"' . checked( $value, $option_key, false ) . ' />'; 
  1784. $field .= '<label for="' . esc_attr( $args['id'] ) . '_' . esc_attr( $option_key ) . '" class="radio ' . implode( ' ', $args['label_class'] ) . '">' . $option_text . '</label>'; 
  1785.  
  1786. break; 
  1787.  
  1788. if ( ! empty( $field ) ) { 
  1789. $field_html = ''; 
  1790.  
  1791. if ( $args['label'] && 'checkbox' != $args['type'] ) { 
  1792. $field_html .= '<label for="' . esc_attr( $label_id ) . '" class="' . esc_attr( implode( ' ', $args['label_class'] ) ) . '">' . $args['label'] . $required . '</label>'; 
  1793.  
  1794. $field_html .= $field; 
  1795.  
  1796. if ( $args['description'] ) { 
  1797. $field_html .= '<span class="description">' . esc_html( $args['description'] ) . '</span>'; 
  1798.  
  1799. $container_class = esc_attr( implode( ' ', $args['class'] ) ); 
  1800. $container_id = esc_attr( $args['id'] ) . '_field'; 
  1801. $field = sprintf( $field_container, $container_class, $container_id, $field_html ); 
  1802.  
  1803. $field = apply_filters( 'woocommerce_form_field_' . $args['type'], $field, $key, $args, $value ); 
  1804.  
  1805. if ( $args['return'] ) { 
  1806. return $field; 
  1807. } else { 
  1808. echo $field; 
  1809.  
  1810. if ( ! function_exists( 'get_product_search_form' ) ) { 
  1811.  
  1812. /** 
  1813. * Display product search form. 
  1814. * 
  1815. * Will first attempt to locate the product-searchform.php file in either the child or. 
  1816. * the parent, then load it. If it doesn't exist, then the default search form. 
  1817. * will be displayed. 
  1818. * 
  1819. * The default searchform uses html5. 
  1820. * 
  1821. * @subpackage Forms 
  1822. * @param bool $echo (default: true) 
  1823. * @return string 
  1824. */ 
  1825. function get_product_search_form( $echo = true ) { 
  1826. global $product_search_form_index; 
  1827.  
  1828. ob_start(); 
  1829.  
  1830. if ( empty( $product_search_form_index ) ) { 
  1831. $product_search_form_index = 0; 
  1832.  
  1833. do_action( 'pre_get_product_search_form' ); 
  1834.  
  1835. wc_get_template( 'product-searchform.php', array( 
  1836. 'index' => $product_search_form_index++,  
  1837. ) ); 
  1838.  
  1839. $form = apply_filters( 'get_product_search_form', ob_get_clean() ); 
  1840.  
  1841. if ( $echo ) { 
  1842. echo $form; 
  1843. } else { 
  1844. return $form; 
  1845.  
  1846. if ( ! function_exists( 'woocommerce_output_auth_header' ) ) { 
  1847.  
  1848. /** 
  1849. * Output the Auth header. 
  1850. */ 
  1851. function woocommerce_output_auth_header() { 
  1852. wc_get_template( 'auth/header.php' ); 
  1853.  
  1854. if ( ! function_exists( 'woocommerce_output_auth_footer' ) ) { 
  1855.  
  1856. /** 
  1857. * Output the Auth footer. 
  1858. */ 
  1859. function woocommerce_output_auth_footer() { 
  1860. wc_get_template( 'auth/footer.php' ); 
  1861.  
  1862. if ( ! function_exists( 'woocommerce_single_variation' ) ) { 
  1863.  
  1864. /** 
  1865. * Output placeholders for the single variation. 
  1866. */ 
  1867. function woocommerce_single_variation() { 
  1868. echo '<div class="woocommerce-variation single_variation"></div>'; 
  1869.  
  1870. if ( ! function_exists( 'woocommerce_single_variation_add_to_cart_button' ) ) { 
  1871.  
  1872. /** 
  1873. * Output the add to cart button for variations. 
  1874. */ 
  1875. function woocommerce_single_variation_add_to_cart_button() { 
  1876. wc_get_template( 'single-product/add-to-cart/variation-add-to-cart-button.php' ); 
  1877.  
  1878. if ( ! function_exists( 'wc_dropdown_variation_attribute_options' ) ) { 
  1879.  
  1880. /** 
  1881. * Output a list of variation attributes for use in the cart forms. 
  1882. * 
  1883. * @param array $args 
  1884. * @since 2.4.0 
  1885. */ 
  1886. function wc_dropdown_variation_attribute_options( $args = array() ) { 
  1887. $args = wp_parse_args( apply_filters( 'woocommerce_dropdown_variation_attribute_options_args', $args ), array( 
  1888. 'options' => false,  
  1889. 'attribute' => false,  
  1890. 'product' => false,  
  1891. 'selected' => false,  
  1892. 'name' => '',  
  1893. 'id' => '',  
  1894. 'class' => '',  
  1895. 'show_option_none' => __( 'Choose an option', 'woocommerce' ),  
  1896. ) ); 
  1897.  
  1898. $options = $args['options']; 
  1899. $product = $args['product']; 
  1900. $attribute = $args['attribute']; 
  1901. $name = $args['name'] ? $args['name'] : 'attribute_' . sanitize_title( $attribute ); 
  1902. $id = $args['id'] ? $args['id'] : sanitize_title( $attribute ); 
  1903. $class = $args['class']; 
  1904. $show_option_none = $args['show_option_none'] ? true : false; 
  1905. $show_option_none_text = $args['show_option_none'] ? $args['show_option_none'] : __( 'Choose an option', 'woocommerce' ); // We'll do our best to hide the placeholder, but we'll need to show something when resetting options. 
  1906.  
  1907. if ( empty( $options ) && ! empty( $product ) && ! empty( $attribute ) ) { 
  1908. $attributes = $product->get_variation_attributes(); 
  1909. $options = $attributes[ $attribute ]; 
  1910.  
  1911. $html = '<select id="' . esc_attr( $id ) . '" class="' . esc_attr( $class ) . '" name="' . esc_attr( $name ) . '" data-attribute_name="attribute_' . esc_attr( sanitize_title( $attribute ) ) . '" data-show_option_none="' . ( $show_option_none ? 'yes' : 'no' ) . '">'; 
  1912. $html .= '<option value="">' . esc_html( $show_option_none_text ) . '</option>'; 
  1913.  
  1914. if ( ! empty( $options ) ) { 
  1915. if ( $product && taxonomy_exists( $attribute ) ) { 
  1916. // Get terms if this is a taxonomy - ordered. We need the names too. 
  1917. $terms = wc_get_product_terms( $product->get_id(), $attribute, array( 'fields' => 'all' ) ); 
  1918.  
  1919. foreach ( $terms as $term ) { 
  1920. if ( in_array( $term->slug, $options ) ) { 
  1921. $html .= '<option value="' . esc_attr( $term->slug ) . '" ' . selected( sanitize_title( $args['selected'] ), $term->slug, false ) . '>' . esc_html( apply_filters( 'woocommerce_variation_option_name', $term->name ) ) . '</option>'; 
  1922. } else { 
  1923. foreach ( $options as $option ) { 
  1924. // This handles < 2.4.0 bw compatibility where text attributes were not sanitized. 
  1925. $selected = sanitize_title( $args['selected'] ) === $args['selected'] ? selected( $args['selected'], sanitize_title( $option ), false ) : selected( $args['selected'], $option, false ); 
  1926. $html .= '<option value="' . esc_attr( $option ) . '" ' . $selected . '>' . esc_html( apply_filters( 'woocommerce_variation_option_name', $option ) ) . '</option>'; 
  1927.  
  1928. $html .= '</select>'; 
  1929.  
  1930. echo apply_filters( 'woocommerce_dropdown_variation_attribute_options_html', $html, $args ); 
  1931.  
  1932. if ( ! function_exists( 'woocommerce_account_content' ) ) { 
  1933.  
  1934. /** 
  1935. * My Account content output. 
  1936. */ 
  1937. function woocommerce_account_content() { 
  1938. global $wp; 
  1939.  
  1940. foreach ( $wp->query_vars as $key => $value ) { 
  1941. // Ignore pagename param. 
  1942. if ( 'pagename' === $key ) { 
  1943. continue; 
  1944.  
  1945. if ( has_action( 'woocommerce_account_' . $key . '_endpoint' ) ) { 
  1946. do_action( 'woocommerce_account_' . $key . '_endpoint', $value ); 
  1947. return; 
  1948.  
  1949. // No endpoint found? Default to dashboard. 
  1950. wc_get_template( 'myaccount/dashboard.php', array( 
  1951. 'current_user' => get_user_by( 'id', get_current_user_id() ),  
  1952. ) ); 
  1953.  
  1954. if ( ! function_exists( 'woocommerce_account_navigation' ) ) { 
  1955.  
  1956. /** 
  1957. * My Account navigation template. 
  1958. */ 
  1959. function woocommerce_account_navigation() { 
  1960. wc_get_template( 'myaccount/navigation.php' ); 
  1961.  
  1962. if ( ! function_exists( 'woocommerce_account_orders' ) ) { 
  1963.  
  1964. /** 
  1965. * My Account > Orders template. 
  1966. * 
  1967. * @param int $current_page Current page number. 
  1968. */ 
  1969. function woocommerce_account_orders( $current_page ) { 
  1970. $current_page = empty( $current_page ) ? 1 : absint( $current_page ); 
  1971. $customer_orders = wc_get_orders( apply_filters( 'woocommerce_my_account_my_orders_query', array( 'customer' => get_current_user_id(), 'page' => $current_page, 'paginate' => true ) ) ); 
  1972.  
  1973. wc_get_template( 
  1974. 'myaccount/orders.php',  
  1975. array( 
  1976. 'current_page' => absint( $current_page ),  
  1977. 'customer_orders' => $customer_orders,  
  1978. 'has_orders' => 0 < $customer_orders->total,  
  1979. ); 
  1980.  
  1981. if ( ! function_exists( 'woocommerce_account_view_order' ) ) { 
  1982.  
  1983. /** 
  1984. * My Account > View order template. 
  1985. * 
  1986. * @param int $order_id Order ID. 
  1987. */ 
  1988. function woocommerce_account_view_order( $order_id ) { 
  1989. WC_Shortcode_My_Account::view_order( absint( $order_id ) ); 
  1990.  
  1991. if ( ! function_exists( 'woocommerce_account_downloads' ) ) { 
  1992.  
  1993. /** 
  1994. * My Account > Downloads template. 
  1995. */ 
  1996. function woocommerce_account_downloads() { 
  1997. wc_get_template( 'myaccount/downloads.php' ); 
  1998.  
  1999. if ( ! function_exists( 'woocommerce_account_edit_address' ) ) { 
  2000.  
  2001. /** 
  2002. * My Account > Edit address template. 
  2003. * 
  2004. * @param string $type Address type. 
  2005. */ 
  2006. function woocommerce_account_edit_address( $type ) { 
  2007. $type = wc_edit_address_i18n( sanitize_title( $type ), true ); 
  2008.  
  2009. WC_Shortcode_My_Account::edit_address( $type ); 
  2010.  
  2011. if ( ! function_exists( 'woocommerce_account_payment_methods' ) ) { 
  2012.  
  2013. /** 
  2014. * My Account > Downloads template. 
  2015. */ 
  2016. function woocommerce_account_payment_methods() { 
  2017. wc_get_template( 'myaccount/payment-methods.php' ); 
  2018.  
  2019. if ( ! function_exists( 'woocommerce_account_add_payment_method' ) ) { 
  2020.  
  2021. /** 
  2022. * My Account > Add payment method template. 
  2023. */ 
  2024. function woocommerce_account_add_payment_method() { 
  2025. WC_Shortcode_My_Account::add_payment_method(); 
  2026.  
  2027. if ( ! function_exists( 'woocommerce_account_edit_account' ) ) { 
  2028.  
  2029. /** 
  2030. * My Account > Edit account template. 
  2031. */ 
  2032. function woocommerce_account_edit_account() { 
  2033. WC_Shortcode_My_Account::edit_account(); 
  2034.  
  2035. if ( ! function_exists( 'wc_no_products_found' ) ) { 
  2036.  
  2037. /** 
  2038. * Show no products found message. 
  2039. */ 
  2040. function wc_no_products_found() { 
  2041. wc_get_template( 'loop/no-products-found.php' ); 
  2042.  
  2043.  
  2044. if ( ! function_exists( 'wc_get_email_order_items' ) ) { 
  2045. /** 
  2046. * Get HTML for the order items to be shown in emails. 
  2047. * @param WC_Order $order 
  2048. * @param array $args 
  2049. * @since 3.0.0 
  2050. */ 
  2051. function wc_get_email_order_items( $order, $args = array() ) { 
  2052. ob_start(); 
  2053.  
  2054. $defaults = array( 
  2055. 'show_sku' => false,  
  2056. 'show_image' => false,  
  2057. 'image_size' => array( 32, 32 ),  
  2058. 'plain_text' => false,  
  2059. 'sent_to_admin' => false,  
  2060. ); 
  2061.  
  2062. $args = wp_parse_args( $args, $defaults ); 
  2063. $template = $args['plain_text'] ? 'emails/plain/email-order-items.php' : 'emails/email-order-items.php'; 
  2064.  
  2065. wc_get_template( $template, apply_filters( 'woocommerce_email_order_items_args', array( 
  2066. 'order' => $order,  
  2067. 'items' => $order->get_items(),  
  2068. 'show_download_links' => $order->is_download_permitted() && ! $args['sent_to_admin'],  
  2069. 'show_sku' => $args['show_sku'],  
  2070. 'show_purchase_note' => $order->is_paid() && ! $args['sent_to_admin'],  
  2071. 'show_image' => $args['show_image'],  
  2072. 'image_size' => $args['image_size'],  
  2073. 'plain_text' => $args['plain_text'],  
  2074. 'sent_to_admin' => $args['sent_to_admin'],  
  2075. ) ) ); 
  2076.  
  2077. return apply_filters( 'woocommerce_email_order_items_table', ob_get_clean(), $order ); 
  2078.  
  2079. if ( ! function_exists( 'wc_display_item_meta' ) ) { 
  2080. /** 
  2081. * Display item meta data. 
  2082. * @since 3.0.0 
  2083. * @param WC_Item $item 
  2084. * @param array $args 
  2085. * @return string|void 
  2086. */ 
  2087. function wc_display_item_meta( $item, $args = array() ) { 
  2088. $strings = array(); 
  2089. $html = ''; 
  2090. $args = wp_parse_args( $args, array( 
  2091. 'before' => '<ul class="wc-item-meta"><li>',  
  2092. 'after' => '</li></ul>',  
  2093. 'separator' => '</li><li>',  
  2094. 'echo' => true,  
  2095. 'autop' => false,  
  2096. ) ); 
  2097.  
  2098. foreach ( $item->get_formatted_meta_data() as $meta_id => $meta ) { 
  2099. $value = $args['autop'] ? wp_kses_post( $meta->display_value ) : wp_kses_post( make_clickable( trim( strip_tags( $meta->display_value ) ) ) ); 
  2100. $strings[] = '<strong class="wc-item-meta-label">' . wp_kses_post( $meta->display_key ) . ':</strong> ' . $value; 
  2101.  
  2102. if ( $strings ) { 
  2103. $html = $args['before'] . implode( $args['separator'], $strings ) . $args['after']; 
  2104.  
  2105. $html = apply_filters( 'woocommerce_display_item_meta', $html, $item, $args ); 
  2106.  
  2107. if ( $args['echo'] ) { 
  2108. echo $html; 
  2109. } else { 
  2110. return $html; 
  2111.  
  2112. if ( ! function_exists( 'wc_display_item_downloads' ) ) { 
  2113. /** 
  2114. * Display item download links. 
  2115. * @since 3.0.0 
  2116. * @param WC_Item $item 
  2117. * @param array $args 
  2118. * @return string|void 
  2119. */ 
  2120. function wc_display_item_downloads( $item, $args = array() ) { 
  2121. $strings = array(); 
  2122. $html = ''; 
  2123. $args = wp_parse_args( $args, array( 
  2124. 'before' => '<ul class ="wc-item-downloads"><li>',  
  2125. 'after' => '</li></ul>',  
  2126. 'separator' => '</li><li>',  
  2127. 'echo' => true,  
  2128. 'show_url' => false,  
  2129. ) ); 
  2130.  
  2131. if ( is_object( $item ) && $item->is_type( 'line_item' ) && ( $downloads = $item->get_item_downloads() ) ) { 
  2132. $i = 0; 
  2133. foreach ( $downloads as $file ) { 
  2134. $i ++; 
  2135.  
  2136. if ( $args['show_url'] ) { 
  2137. $strings[] = '<strong class="wc-item-download-label">' . esc_html( $file['name'] ) . ':</strong> ' . esc_html( $file['download_url'] ); 
  2138. } else { 
  2139. $prefix = sizeof( $downloads ) > 1 ? sprintf( __( 'Download %d', 'woocommerce' ), $i ) : __( 'Download', 'woocommerce' ); 
  2140. $strings[] = '<strong class="wc-item-download-label">' . $prefix . ':</strong> <a href="' . esc_url( $file['download_url'] ) . '" target="_blank">' . esc_html( $file['name'] ) . '</a>'; 
  2141.  
  2142. if ( $strings ) { 
  2143. $html = $args['before'] . implode( $args['separator'], $strings ) . $args['after']; 
  2144.  
  2145. $html = apply_filters( 'woocommerce_display_item_downloads', $html, $item, $args ); 
  2146.  
  2147. if ( $args['echo'] ) { 
  2148. echo $html; 
  2149. } else { 
  2150. return $html; 
  2151.  
  2152. if ( ! function_exists( 'woocommerce_photoswipe' ) ) { 
  2153.  
  2154. /** 
  2155. * Get the shop sidebar template. 
  2156. * 
  2157. */ 
  2158. function woocommerce_photoswipe() { 
  2159. if ( current_theme_supports( 'wc-product-gallery-lightbox' ) ) { 
  2160. wc_get_template( 'single-product/photoswipe.php' ); 
  2161.  
  2162. /** 
  2163. * Outputs a list of product attributes for a product. 
  2164. * @since 3.0.0 
  2165. * @param WC_Product $product 
  2166. */ 
  2167. function wc_display_product_attributes( $product ) { 
  2168. wc_get_template( 'single-product/product-attributes.php', array( 
  2169. 'product' => $product,  
  2170. 'attributes' => array_filter( $product->get_attributes(), 'wc_attributes_array_filter_visible' ),  
  2171. 'display_dimensions' => apply_filters( 'wc_product_enable_dimensions_display', $product->has_weight() || $product->has_dimensions() ),  
  2172. ) ); 
  2173.  
  2174. /** 
  2175. * Get HTML to show product stock. 
  2176. * @since 3.0.0 
  2177. * @param WC_Product $product 
  2178. * @return string 
  2179. */ 
  2180. function wc_get_stock_html( $product ) { 
  2181.  
  2182. $html = ''; 
  2183. $availability = $product->get_availability(); 
  2184.  
  2185. if ( ! empty( $availability['availability'] ) ) { 
  2186. ob_start(); 
  2187.  
  2188. wc_get_template( 'single-product/stock.php', array( 
  2189. 'product' => $product,  
  2190. 'class' => $availability['class'],  
  2191. 'availability' => $availability['availability'],  
  2192. ) ); 
  2193.  
  2194. $html = ob_get_clean(); 
  2195.  
  2196. if ( has_filter( 'woocommerce_stock_html' ) ) { 
  2197. wc_deprecated_function( 'The woocommerce_stock_html filter', '', 'woocommerce_get_stock_html' ); 
  2198. $html = apply_filters( 'woocommerce_stock_html', $html, $availability['availability'], $product ); 
  2199.  
  2200. return apply_filters( 'woocommerce_get_stock_html', $html, $product ); 
  2201.  
  2202. /** 
  2203. * Get HTML for ratings. 
  2204. * 
  2205. * @since 3.0.0 
  2206. * @param float $rating Rating being shown. 
  2207. * @return string 
  2208. */ 
  2209. function wc_get_rating_html( $rating ) { 
  2210. if ( $rating > 0 ) { 
  2211. $rating_html = '<div class="star-rating" title="' . sprintf( esc_attr__( 'Rated %s out of 5', 'woocommerce' ), $rating ) . '">'; 
  2212. $rating_html .= '<span style="width:' . ( ( $rating / 5 ) * 100 ) . '%"><strong class="rating">' . $rating . '</strong> ' . esc_html__( 'out of 5', 'woocommerce' ) . '</span>'; 
  2213. $rating_html .= '</div>'; 
  2214. } else { 
  2215. $rating_html = ''; 
  2216. return apply_filters( 'woocommerce_product_get_rating_html', $rating_html, $rating ); 
  2217.  
  2218. /** 
  2219. * Returns a 'from' prefix if you want to show where prices start at. 
  2220. * 
  2221. * @since 3.0.0 
  2222. * @return string 
  2223. */ 
  2224. function wc_get_price_html_from_text() { 
  2225. return apply_filters( 'woocommerce_get_price_html_from_text', '<span class="from">' . _x( 'From:', 'min_price', 'woocommerce' ) . ' </span>' ); 
  2226.  
  2227. /** 
  2228. * Get logout endpoint. 
  2229. * 
  2230. * @since 2.6.9 
  2231. * @return string 
  2232. */ 
  2233. function wc_logout_url( $redirect = '' ) { 
  2234. $logout_endpoint = get_option( 'woocommerce_logout_endpoint' ); 
  2235. $redirect = $redirect ? $redirect : wc_get_page_permalink( 'myaccount' ); 
  2236.  
  2237. if ( $logout_endpoint ) { 
  2238. return wc_get_endpoint_url( 'customer-logout', '', $redirect ); 
  2239. } else { 
  2240. return wp_logout_url( $redirect ); 
.