Completed
Pull Request — master (#11622)
by Caleb
07:41
created

wc-template-functions.php ➔ woocommerce_subcategory_thumbnail()   C

Complexity

Conditions 7
Paths 15

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 1
Metric Value
cc 7
eloc 18
c 2
b 1
f 1
nc 15
nop 1
dl 0
loc 28
rs 6.7272
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
/**
18
 * Handle redirects before content is output - hooked into template_redirect so is_page works.
19
 */
20
function wc_template_redirect() {
21
	global $wp_query, $wp;
22
23
	// When default permalinks are enabled, redirect shop page to post type archive url
24
	if ( ! empty( $_GET['page_id'] ) && '' === get_option( 'permalink_structure' ) && $_GET['page_id'] == wc_get_page_id( 'shop' ) ) {
25
		wp_safe_redirect( get_post_type_archive_link('product') );
26
		exit;
27
	}
28
29
	// When on the checkout with an empty cart, redirect to cart page
30
	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'] ) ) {
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
36
	// Logout
37
	elseif ( isset( $wp->query_vars['customer-logout'] ) ) {
38
		wp_redirect( str_replace( '&amp;', '&', wp_logout_url( wc_get_page_permalink( 'myaccount' ) ) ) );
39
		exit;
40
	}
41
42
	// Redirect to the product page if we have a single product
43
	elseif ( is_search() && is_post_type_archive( 'product' ) && apply_filters( 'woocommerce_redirect_single_search_result', true ) && 1 === absint( $wp_query->found_posts ) ) {
44
		$product = wc_get_product( $wp_query->post );
45
46
		if ( $product && $product->is_visible() ) {
47
			wp_safe_redirect( get_permalink( $product->id ), 302 );
48
			exit;
49
		}
50
	}
51
52
	// Ensure payment gateways are loaded early
53
	elseif ( is_add_payment_method_page() ) {
54
55
		WC()->payment_gateways();
56
57
	}
58
59
	// Checkout pages handling
60
	elseif ( is_checkout() ) {
61
		// Buffer the checkout page
62
		ob_start();
63
64
		// Ensure gateways and shipping methods are loaded early
65
		WC()->payment_gateways();
66
		WC()->shipping();
67
	}
68
}
69
add_action( 'template_redirect', 'wc_template_redirect' );
70
71
/**
72
 * When loading sensitive checkout or account pages, send a HTTP header to limit rendering of pages to same origin iframes for security reasons.
73
 *
74
 * Can be disabled with: remove_action( 'template_redirect', 'wc_send_frame_options_header' );
75
 *
76
 * @since  2.3.10
77
 */
78
function wc_send_frame_options_header() {
79
	if ( is_checkout() || is_account_page() ) {
80
		send_frame_options_header();
81
	}
82
}
83
add_action( 'template_redirect', 'wc_send_frame_options_header' );
84
85
/**
86
 * No index our endpoints.
87
 * Prevent indexing pages like order-received.
88
 *
89
 * @since 2.5.3
90
 */
91
function wc_prevent_endpoint_indexing() {
92
	if ( is_wc_endpoint_url() || isset( $_GET['download_file'] ) ) {
93
		@header( 'X-Robots-Tag: noindex' );
94
	}
95
}
96
add_action( 'template_redirect', 'wc_prevent_endpoint_indexing' );
97
98
/**
99
 * When the_post is called, put product data into a global.
100
 *
101
 * @param mixed $post
102
 * @return WC_Product
103
 */
104
function wc_setup_product_data( $post ) {
105
	unset( $GLOBALS['product'] );
106
107
	if ( is_int( $post ) )
108
		$post = get_post( $post );
109
110
	if ( empty( $post->post_type ) || ! in_array( $post->post_type, array( 'product', 'product_variation' ) ) )
111
		return;
112
113
	$GLOBALS['product'] = wc_get_product( $post );
114
115
	return $GLOBALS['product'];
116
}
117
add_action( 'the_post', 'wc_setup_product_data' );
118
119
if ( ! function_exists( 'woocommerce_reset_loop' ) ) {
120
121
	/**
122
	 * Reset the loop's index and columns when we're done outputting a product loop.
123
	 * @subpackage	Loop
124
	 */
125
	function woocommerce_reset_loop() {
126
		$GLOBALS['woocommerce_loop'] = array(
127
			'loop'    => '',
128
			'columns' => '',
129
			'name'    => '',
130
		);
131
	}
132
}
133
add_filter( 'loop_end', 'woocommerce_reset_loop' );
134
135
/**
136
 * Products RSS Feed.
137
 * @deprecated 2.6
138
 * @access public
139
 */
140
function wc_products_rss_feed() {
141
	// Product RSS
142
	if ( is_post_type_archive( 'product' ) || is_singular( 'product' ) ) {
143
144
		$feed = get_post_type_archive_feed_link( 'product' );
145
146
		echo '<link rel="alternate" type="application/rss+xml"  title="' . esc_attr__( 'New products', 'woocommerce' ) . '" href="' . esc_url( $feed ) . '" />';
147
148
	} elseif ( is_tax( 'product_cat' ) ) {
149
150
		$term = get_term_by( 'slug', esc_attr( get_query_var('product_cat') ), 'product_cat' );
151
152 View Code Duplication
		if ( $term ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
153
			$feed = add_query_arg( 'product_cat', $term->slug, get_post_type_archive_feed_link( 'product' ) );
154
			echo '<link rel="alternate" type="application/rss+xml"  title="' . esc_attr( sprintf( __( 'New products added to %s', 'woocommerce' ), $term->name ) ) . '" href="' . esc_url( $feed ) . '" />';
155
		}
156
157
	} elseif ( is_tax( 'product_tag' ) ) {
158
159
		$term = get_term_by('slug', esc_attr( get_query_var('product_tag') ), 'product_tag');
160
161 View Code Duplication
		if ( $term ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
162
			$feed = add_query_arg('product_tag', $term->slug, get_post_type_archive_feed_link( 'product' ));
163
			echo '<link rel="alternate" type="application/rss+xml"  title="' . sprintf(__( 'New products tagged %s', 'woocommerce' ), urlencode($term->name)) . '" href="' . esc_url( $feed ) . '" />';
164
		}
165
	}
166
}
167
168
/**
169
 * Output generator tag to aid debugging.
170
 *
171
 * @access public
172
 */
173
function wc_generator_tag( $gen, $type ) {
174
	switch ( $type ) {
175
		case 'html':
176
			$gen .= "\n" . '<meta name="generator" content="WooCommerce ' . esc_attr( WC_VERSION ) . '">';
177
			break;
178
		case 'xhtml':
179
			$gen .= "\n" . '<meta name="generator" content="WooCommerce ' . esc_attr( WC_VERSION ) . '" />';
180
			break;
181
	}
182
	return $gen;
183
}
184
185
/**
186
 * Add body classes for WC pages.
187
 *
188
 * @param  array $classes
189
 * @return array
190
 */
191
function wc_body_class( $classes ) {
192
	$classes = (array) $classes;
193
194
	if ( is_woocommerce() ) {
195
		$classes[] = 'woocommerce';
196
		$classes[] = 'woocommerce-page';
197
	}
198
199
	elseif ( is_checkout() ) {
200
		$classes[] = 'woocommerce-checkout';
201
		$classes[] = 'woocommerce-page';
202
	}
203
204
	elseif ( is_cart() ) {
205
		$classes[] = 'woocommerce-cart';
206
		$classes[] = 'woocommerce-page';
207
	}
208
209
	elseif ( is_account_page() ) {
210
		$classes[] = 'woocommerce-account';
211
		$classes[] = 'woocommerce-page';
212
	}
213
214
	if ( is_store_notice_showing() ) {
215
		$classes[] = 'woocommerce-demo-store';
216
	}
217
218
	foreach ( WC()->query->query_vars as $key => $value ) {
219
		if ( is_wc_endpoint_url( $key ) ) {
220
			$classes[] = 'woocommerce-' . sanitize_html_class( $key );
221
		}
222
	}
223
224
	return array_unique( $classes );
225
}
226
227
/**
228
 * Display the classes for the product cat div.
229
 *
230
 * @since 2.4.0
231
 * @param string|array $class One or more classes to add to the class list.
232
 * @param object $category object Optional.
233
 */
234
function wc_product_cat_class( $class = '', $category = null ) {
235
	// Separates classes with a single space, collates classes for post DIV
236
	echo 'class="' . esc_attr( join( ' ', wc_get_product_cat_class( $class, $category ) ) ) . '"';
237
}
238
239
/**
240
 * Get classname for loops based on $woocommerce_loop global.
241
 * @since 2.6.0
242
 * @return string
243
 */
244
function wc_get_loop_class() {
245
	global $woocommerce_loop;
246
247
	$woocommerce_loop['loop']    = ! empty( $woocommerce_loop['loop'] ) ? $woocommerce_loop['loop'] + 1   : 1;
248
	$woocommerce_loop['columns'] = ! empty( $woocommerce_loop['columns'] ) ? $woocommerce_loop['columns'] : apply_filters( 'loop_shop_columns', 4 );
249
250
	if ( 0 === ( $woocommerce_loop['loop'] - 1 ) % $woocommerce_loop['columns'] || 1 === $woocommerce_loop['columns'] ) {
251
		return 'first';
252
	} elseif ( 0 === $woocommerce_loop['loop'] % $woocommerce_loop['columns'] ) {
253
		return 'last';
254
	} else {
255
		return '';
256
	}
257
}
258
259
/**
260
 * Get the classes for the product cat div.
261
 *
262
 * @since 2.4.0
263
 * @param string|array $class One or more classes to add to the class list.
264
 * @param object $category object Optional.
265
 */
266
function wc_get_product_cat_class( $class = '', $category = null ) {
267
	$classes   = is_array( $class ) ? $class : array_map( 'trim', explode( ' ', $class ) );
268
	$classes[] = 'product-category';
269
	$classes[] = 'product';
270
	$classes[] = wc_get_loop_class();
271
	$classes   = apply_filters( 'product_cat_class', $classes, $class, $category );
272
273
	return array_unique( array_filter( $classes ) );
274
}
275
276
/**
277
 * Adds extra post classes for products.
278
 *
279
 * @since 2.1.0
280
 * @param array $classes
281
 * @param string|array $class
282
 * @param int $post_id
283
 * @return array
284
 */
285
function wc_product_post_class( $classes, $class = '', $post_id = '' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $class is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
286
	if ( ! $post_id || 'product' !== get_post_type( $post_id ) ) {
287
		return $classes;
288
	}
289
290
	$product = wc_get_product( $post_id );
291
292
	if ( $product ) {
293
		$classes[] = wc_get_loop_class();
294
		$classes[] = $product->stock_status;
295
296
		if ( $product->is_on_sale() ) {
297
			$classes[] = 'sale';
298
		}
299
		if ( $product->is_featured() ) {
300
			$classes[] = 'featured';
301
		}
302
		if ( $product->is_downloadable() ) {
303
			$classes[] = 'downloadable';
304
		}
305
		if ( $product->is_virtual() ) {
306
			$classes[] = 'virtual';
307
		}
308
		if ( $product->is_sold_individually() ) {
309
			$classes[] = 'sold-individually';
310
		}
311
		if ( $product->is_taxable() ) {
312
			$classes[] = 'taxable';
313
		}
314
		if ( $product->is_shipping_taxable() ) {
315
			$classes[] = 'shipping-taxable';
316
		}
317
		if ( $product->is_purchasable() ) {
318
			$classes[] = 'purchasable';
319
		}
320
		if ( $product->get_type() ) {
321
			$classes[] = "product-type-" . $product->get_type();
322
		}
323
		if ( $product->is_type( 'variable' ) ) {
324
			if ( $product->has_default_attributes() ) {
325
				$classes[] = 'has-default-attributes';
326
			}
327
			if ( $product->has_child() ) {
328
				$classes[] = 'has-children';
329
			}
330
		}
331
	}
332
333
	if ( false !== ( $key = array_search( 'hentry', $classes ) ) ) {
334
		unset( $classes[ $key ] );
335
	}
336
337
	return $classes;
338
}
339
340
/**
341
 * Outputs hidden form inputs for each query string variable.
342
 * @since 2.7.0
343
 * @param array $values Name value pairs.
344
 * @param array $exclude Keys to exclude.
345
 * @param string $current_key Current key we are outputting.
346
 */
347
function wc_query_string_form_fields( $values = null, $exclude = array(), $current_key = '' ) {
348
	if ( is_null( $values ) ) {
349
		$values = $_GET;
350
	}
351
	foreach ( $values as $key => $value ) {
352
		if ( in_array( $key, $exclude ) ) {
353
			continue;
354
		}
355
		if ( $current_key ) {
356
			$key = $current_key . '[' . $key . ']';
357
		}
358
		if ( is_array( $value ) ) {
359
			wc_query_string_form_fields( $value, $exclude, $key );
360
		} else {
361
			echo '<input type="hidden" name="' . esc_attr( $key ) . '" value="' . esc_attr( $value ) . '" />';
362
		}
363
	}
364
}
365
366
/** Template pages ********************************************************/
367
368
if ( ! function_exists( 'woocommerce_content' ) ) {
369
370
	/**
371
	 * Output WooCommerce content.
372
	 *
373
	 * This function is only used in the optional 'woocommerce.php' template.
374
	 * which people can add to their themes to add basic woocommerce support.
375
	 * without hooks or modifying core templates.
376
	 *
377
	 */
378
	function woocommerce_content() {
379
380
		if ( is_singular( 'product' ) ) {
381
382
			while ( have_posts() ) : the_post();
383
384
				wc_get_template_part( 'content', 'single-product' );
385
386
			endwhile;
387
388
		} else { ?>
389
390
			<?php if ( apply_filters( 'woocommerce_show_page_title', true ) ) : ?>
391
392
				<h1 class="page-title"><?php woocommerce_page_title(); ?></h1>
393
394
			<?php endif; ?>
395
396
			<?php do_action( 'woocommerce_archive_description' ); ?>
397
398 View Code Duplication
			<?php if ( have_posts() ) : ?>
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
399
400
				<?php do_action('woocommerce_before_shop_loop'); ?>
401
402
				<?php woocommerce_product_loop_start(); ?>
403
404
					<?php woocommerce_product_subcategories(); ?>
405
406
					<?php while ( have_posts() ) : the_post(); ?>
407
408
						<?php wc_get_template_part( 'content', 'product' ); ?>
409
410
					<?php endwhile; // end of the loop. ?>
411
412
				<?php woocommerce_product_loop_end(); ?>
413
414
				<?php do_action('woocommerce_after_shop_loop'); ?>
415
416
			<?php elseif ( ! woocommerce_product_subcategories( array( 'before' => woocommerce_product_loop_start( false ), 'after' => woocommerce_product_loop_end( false ) ) ) ) : ?>
417
418
				<?php wc_get_template( 'loop/no-products-found.php' ); ?>
419
420
			<?php endif;
421
422
		}
423
	}
424
}
425
426
/** Global ****************************************************************/
427
428
if ( ! function_exists( 'woocommerce_output_content_wrapper' ) ) {
429
430
	/**
431
	 * Output the start of the page wrapper.
432
	 *
433
	 */
434
	function woocommerce_output_content_wrapper() {
435
		wc_get_template( 'global/wrapper-start.php' );
436
	}
437
}
438
if ( ! function_exists( 'woocommerce_output_content_wrapper_end' ) ) {
439
440
	/**
441
	 * Output the end of the page wrapper.
442
	 *
443
	 */
444
	function woocommerce_output_content_wrapper_end() {
445
		wc_get_template( 'global/wrapper-end.php' );
446
	}
447
}
448
449
if ( ! function_exists( 'woocommerce_get_sidebar' ) ) {
450
451
	/**
452
	 * Get the shop sidebar template.
453
	 *
454
	 */
455
	function woocommerce_get_sidebar() {
456
		wc_get_template( 'global/sidebar.php' );
457
	}
458
}
459
460
if ( ! function_exists( 'woocommerce_demo_store' ) ) {
461
462
	/**
463
	 * Adds a demo store banner to the site if enabled.
464
	 *
465
	 */
466
	function woocommerce_demo_store() {
467
		if ( ! is_store_notice_showing() ) {
468
			return;
469
		}
470
471
		$notice = get_option( 'woocommerce_demo_store_notice' );
472
473
		if ( empty( $notice ) ) {
474
			$notice = __( 'This is a demo store for testing purposes &mdash; no orders shall be fulfilled.', 'woocommerce' );
475
		}
476
477
		echo apply_filters( 'woocommerce_demo_store', '<p class="demo_store">' . wp_kses_post( $notice ) . '</p>', $notice );
478
	}
479
}
480
481
/** Loop ******************************************************************/
482
483
if ( ! function_exists( 'woocommerce_page_title' ) ) {
484
485
	/**
486
	 * woocommerce_page_title function.
487
	 *
488
	 * @param  bool $echo
489
	 * @return string
490
	 */
491
	function woocommerce_page_title( $echo = true ) {
492
493
		if ( is_search() ) {
494
			$page_title = sprintf( __( 'Search Results: &ldquo;%s&rdquo;', 'woocommerce' ), get_search_query() );
495
496
			if ( get_query_var( 'paged' ) )
497
				$page_title .= sprintf( __( '&nbsp;&ndash; Page %s', 'woocommerce' ), get_query_var( 'paged' ) );
498
499
		} elseif ( is_tax() ) {
500
501
			$page_title = single_term_title( "", false );
502
503
		} else {
504
505
			$shop_page_id = wc_get_page_id( 'shop' );
506
			$page_title   = get_the_title( $shop_page_id );
507
508
		}
509
510
		$page_title = apply_filters( 'woocommerce_page_title', $page_title );
511
512
		if ( $echo )
513
			echo $page_title;
514
		else
515
			return $page_title;
516
	}
517
}
518
519
if ( ! function_exists( 'woocommerce_product_loop_start' ) ) {
520
521
	/**
522
	 * Output the start of a product loop. By default this is a UL.
523
	 *
524
	 * @param bool $echo
525
	 * @return string
526
	 */
527
	function woocommerce_product_loop_start( $echo = true ) {
528
		ob_start();
529
		$GLOBALS['woocommerce_loop']['loop'] = 0;
530
		wc_get_template( 'loop/loop-start.php' );
531
		if ( $echo )
532
			echo ob_get_clean();
533
		else
534
			return ob_get_clean();
535
	}
536
}
537
if ( ! function_exists( 'woocommerce_product_loop_end' ) ) {
538
539
	/**
540
	 * Output the end of a product loop. By default this is a UL.
541
	 *
542
	 * @param bool $echo
543
	 * @return string
544
	 */
545
	function woocommerce_product_loop_end( $echo = true ) {
546
		ob_start();
547
548
		wc_get_template( 'loop/loop-end.php' );
549
550
		if ( $echo )
551
			echo ob_get_clean();
552
		else
553
			return ob_get_clean();
554
	}
555
}
556
if (  ! function_exists( 'woocommerce_template_loop_product_title' ) ) {
557
558
	/**
559
	 * Show the product title in the product loop. By default this is an H3.
560
	 */
561
	function woocommerce_template_loop_product_title() {
562
		echo '<h3>' . get_the_title() . '</h3>';
563
	}
564
}
565
if (  ! function_exists( 'woocommerce_template_loop_category_title' ) ) {
566
567
	/**
568
	 * Show the subcategory title in the product loop.
569
	 */
570
	function woocommerce_template_loop_category_title( $category ) {
571
		?>
572
		<h3>
573
			<?php
574
				echo $category->name;
575
576
				if ( $category->count > 0 )
577
					echo apply_filters( 'woocommerce_subcategory_count_html', ' <mark class="count">(' . $category->count . ')</mark>', $category );
578
			?>
579
		</h3>
580
		<?php
581
	}
582
}
583
/**
584
 * Insert the opening anchor tag for products in the loop.
585
 */
586
function woocommerce_template_loop_product_link_open() {
587
	echo '<a href="' . get_the_permalink() . '" class="woocommerce-LoopProduct-link">';
588
}
589
/**
590
 * Insert the opening anchor tag for products in the loop.
591
 */
592
function woocommerce_template_loop_product_link_close() {
593
	echo '</a>';
594
}
595
/**
596
 * Insert the opening anchor tag for categories in the loop.
597
 */
598
function woocommerce_template_loop_category_link_open( $category ) {
599
	echo '<a href="' . get_term_link( $category, 'product_cat' ) . '">';
600
}
601
/**
602
 * Insert the opening anchor tag for categories in the loop.
603
 */
604
function woocommerce_template_loop_category_link_close() {
605
	echo '</a>';
606
}
607
if ( ! function_exists( 'woocommerce_taxonomy_archive_description' ) ) {
608
609
	/**
610
	 * Show an archive description on taxonomy archives.
611
	 *
612
	 * @subpackage	Archives
613
	 */
614
	function woocommerce_taxonomy_archive_description() {
615
		if ( is_tax( array( 'product_cat', 'product_tag' ) ) && 0 === absint( get_query_var( 'paged' ) ) ) {
616
			$description = wc_format_content( term_description() );
617
			if ( $description ) {
618
				echo '<div class="term-description">' . $description . '</div>';
619
			}
620
		}
621
	}
622
}
623
if ( ! function_exists( 'woocommerce_product_archive_description' ) ) {
624
625
	/**
626
	 * Show a shop page description on product archives.
627
	 *
628
	 * @subpackage	Archives
629
	 */
630
	function woocommerce_product_archive_description() {
631
		// Don't display the description on search results page
632
		if ( is_search() ) {
633
			return;
634
		}
635
636
		if ( is_post_type_archive( 'product' ) && 0 === absint( get_query_var( 'paged' ) ) ) {
637
			$shop_page   = get_post( wc_get_page_id( 'shop' ) );
638
			if ( $shop_page ) {
639
				$description = wc_format_content( $shop_page->post_content );
640
				if ( $description ) {
641
					echo '<div class="page-description">' . $description . '</div>';
642
				}
643
			}
644
		}
645
	}
646
}
647
648
if ( ! function_exists( 'woocommerce_template_loop_add_to_cart' ) ) {
649
650
	/**
651
	 * Get the add to cart template for the loop.
652
	 *
653
	 * @subpackage	Loop
654
	 */
655
	function woocommerce_template_loop_add_to_cart( $args = array() ) {
656
		global $product;
657
658
		if ( $product ) {
659
			$defaults = array(
660
				'quantity' => 1,
661
				'class'    => implode( ' ', array_filter( array(
662
						'button',
663
						'product_type_' . $product->product_type,
664
						$product->is_purchasable() && $product->is_in_stock() ? 'add_to_cart_button' : '',
665
						$product->supports( 'ajax_add_to_cart' ) ? 'ajax_add_to_cart' : ''
666
				) ) )
667
			);
668
669
			$args = apply_filters( 'woocommerce_loop_add_to_cart_args', wp_parse_args( $args, $defaults ), $product );
670
671
			wc_get_template( 'loop/add-to-cart.php', $args );
672
		}
673
	}
674
}
675
if ( ! function_exists( 'woocommerce_template_loop_product_thumbnail' ) ) {
676
677
	/**
678
	 * Get the product thumbnail for the loop.
679
	 *
680
	 * @subpackage	Loop
681
	 */
682
	function woocommerce_template_loop_product_thumbnail() {
683
		echo woocommerce_get_product_thumbnail();
684
	}
685
}
686
if ( ! function_exists( 'woocommerce_template_loop_price' ) ) {
687
688
	/**
689
	 * Get the product price for the loop.
690
	 *
691
	 * @subpackage	Loop
692
	 */
693
	function woocommerce_template_loop_price() {
694
		wc_get_template( 'loop/price.php' );
695
	}
696
}
697
if ( ! function_exists( 'woocommerce_template_loop_rating' ) ) {
698
699
	/**
700
	 * Display the average rating in the loop.
701
	 *
702
	 * @subpackage	Loop
703
	 */
704
	function woocommerce_template_loop_rating() {
705
		wc_get_template( 'loop/rating.php' );
706
	}
707
}
708
if ( ! function_exists( 'woocommerce_show_product_loop_sale_flash' ) ) {
709
710
	/**
711
	 * Get the sale flash for the loop.
712
	 *
713
	 * @subpackage	Loop
714
	 */
715
	function woocommerce_show_product_loop_sale_flash() {
716
		wc_get_template( 'loop/sale-flash.php' );
717
	}
718
}
719
720
if ( ! function_exists( 'woocommerce_get_product_schema' ) ) {
721
722
	/**
723
	 * Get a products Schema.
724
	 * @return string
725
	 */
726
	function woocommerce_get_product_schema() {
727
		global $product;
728
729
		$schema = "Product";
730
731
		// Downloadable product schema handling
732
		if ( $product->is_downloadable() ) {
733
			switch ( $product->download_type ) {
734
				case 'application' :
735
					$schema = "SoftwareApplication";
736
				break;
737
				case 'music' :
738
					$schema = "MusicAlbum";
739
				break;
740
				default :
741
					$schema = "Product";
742
				break;
743
			}
744
		}
745
746
		return 'http://schema.org/' . $schema;
747
	}
748
}
749
750
if ( ! function_exists( 'woocommerce_get_product_thumbnail' ) ) {
751
752
	/**
753
	 * Get the product thumbnail, or the placeholder if not set.
754
	 *
755
	 * @subpackage	Loop
756
	 * @param string $size (default: 'shop_catalog')
757
	 * @param int $deprecated1 Deprecated since WooCommerce 2.0 (default: 0)
758
	 * @param int $deprecated2 Deprecated since WooCommerce 2.0 (default: 0)
759
	 * @return string
760
	 */
761
	function woocommerce_get_product_thumbnail( $size = 'shop_catalog', $deprecated1 = 0, $deprecated2 = 0 ) {
0 ignored issues
show
Unused Code introduced by
The parameter $deprecated1 is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $deprecated2 is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
762
		global $post;
763
		$image_size = apply_filters( 'single_product_archive_thumbnail_size', $size );
764
765
		if ( has_post_thumbnail() ) {
766
			$props = wc_get_product_attachment_props( get_post_thumbnail_id(), $post );
767
			return get_the_post_thumbnail( $post->ID, $image_size, array(
768
				'title'	 => $props['title'],
769
				'alt'    => $props['alt'],
770
			) );
771
		} elseif ( wc_placeholder_img_src() ) {
772
			return wc_placeholder_img( $image_size );
773
		}
774
	}
775
}
776
777
if ( ! function_exists( 'woocommerce_result_count' ) ) {
778
779
	/**
780
	 * Output the result count text (Showing x - x of x results).
781
	 *
782
	 * @subpackage	Loop
783
	 */
784
	function woocommerce_result_count() {
785
		wc_get_template( 'loop/result-count.php' );
786
	}
787
}
788
789
if ( ! function_exists( 'woocommerce_catalog_ordering' ) ) {
790
791
	/**
792
	 * Output the product sorting options.
793
	 *
794
	 * @subpackage	Loop
795
	 */
796
	function woocommerce_catalog_ordering() {
797
		global $wp_query;
798
799
		if ( 1 === $wp_query->found_posts || ! woocommerce_products_will_display() ) {
800
			return;
801
		}
802
803
		$orderby                 = isset( $_GET['orderby'] ) ? wc_clean( $_GET['orderby'] ) : apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
804
		$show_default_orderby    = 'menu_order' === apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
805
		$catalog_orderby_options = apply_filters( 'woocommerce_catalog_orderby', array(
806
			'menu_order' => __( 'Default sorting', 'woocommerce' ),
807
			'popularity' => __( 'Sort by popularity', 'woocommerce' ),
808
			'rating'     => __( 'Sort by average rating', 'woocommerce' ),
809
			'date'       => __( 'Sort by newness', 'woocommerce' ),
810
			'price'      => __( 'Sort by price: low to high', 'woocommerce' ),
811
			'price-desc' => __( 'Sort by price: high to low', 'woocommerce' )
812
		) );
813
814
		if ( ! $show_default_orderby ) {
815
			unset( $catalog_orderby_options['menu_order'] );
816
		}
817
818
		if ( 'no' === get_option( 'woocommerce_enable_review_rating' ) ) {
819
			unset( $catalog_orderby_options['rating'] );
820
		}
821
822
		wc_get_template( 'loop/orderby.php', array( 'catalog_orderby_options' => $catalog_orderby_options, 'orderby' => $orderby, 'show_default_orderby' => $show_default_orderby ) );
823
	}
824
}
825
826
if ( ! function_exists( 'woocommerce_pagination' ) ) {
827
828
	/**
829
	 * Output the pagination.
830
	 *
831
	 * @subpackage	Loop
832
	 */
833
	function woocommerce_pagination() {
834
		wc_get_template( 'loop/pagination.php' );
835
	}
836
}
837
838
/** Single Product ********************************************************/
839
840
if ( ! function_exists( 'woocommerce_show_product_images' ) ) {
841
842
	/**
843
	 * Output the product image before the single product summary.
844
	 *
845
	 * @subpackage	Product
846
	 */
847
	function woocommerce_show_product_images() {
848
		wc_get_template( 'single-product/product-image.php' );
849
	}
850
}
851
if ( ! function_exists( 'woocommerce_show_product_thumbnails' ) ) {
852
853
	/**
854
	 * Output the product thumbnails.
855
	 *
856
	 * @subpackage	Product
857
	 */
858
	function woocommerce_show_product_thumbnails() {
859
		wc_get_template( 'single-product/product-thumbnails.php' );
860
	}
861
}
862
if ( ! function_exists( 'woocommerce_output_product_data_tabs' ) ) {
863
864
	/**
865
	 * Output the product tabs.
866
	 *
867
	 * @subpackage	Product/Tabs
868
	 */
869
	function woocommerce_output_product_data_tabs() {
870
		wc_get_template( 'single-product/tabs/tabs.php' );
871
	}
872
}
873
if ( ! function_exists( 'woocommerce_template_single_title' ) ) {
874
875
	/**
876
	 * Output the product title.
877
	 *
878
	 * @subpackage	Product
879
	 */
880
	function woocommerce_template_single_title() {
881
		wc_get_template( 'single-product/title.php' );
882
	}
883
}
884
if ( ! function_exists( 'woocommerce_template_single_rating' ) ) {
885
886
	/**
887
	 * Output the product rating.
888
	 *
889
	 * @subpackage	Product
890
	 */
891
	function woocommerce_template_single_rating() {
892
		wc_get_template( 'single-product/rating.php' );
893
	}
894
}
895
if ( ! function_exists( 'woocommerce_template_single_price' ) ) {
896
897
	/**
898
	 * Output the product price.
899
	 *
900
	 * @subpackage	Product
901
	 */
902
	function woocommerce_template_single_price() {
903
		wc_get_template( 'single-product/price.php' );
904
	}
905
}
906
if ( ! function_exists( 'woocommerce_template_single_excerpt' ) ) {
907
908
	/**
909
	 * Output the product short description (excerpt).
910
	 *
911
	 * @subpackage	Product
912
	 */
913
	function woocommerce_template_single_excerpt() {
914
		wc_get_template( 'single-product/short-description.php' );
915
	}
916
}
917
if ( ! function_exists( 'woocommerce_template_single_meta' ) ) {
918
919
	/**
920
	 * Output the product meta.
921
	 *
922
	 * @subpackage	Product
923
	 */
924
	function woocommerce_template_single_meta() {
925
		wc_get_template( 'single-product/meta.php' );
926
	}
927
}
928
if ( ! function_exists( 'woocommerce_template_single_sharing' ) ) {
929
930
	/**
931
	 * Output the product sharing.
932
	 *
933
	 * @subpackage	Product
934
	 */
935
	function woocommerce_template_single_sharing() {
936
		wc_get_template( 'single-product/share.php' );
937
	}
938
}
939
if ( ! function_exists( 'woocommerce_show_product_sale_flash' ) ) {
940
941
	/**
942
	 * Output the product sale flash.
943
	 *
944
	 * @subpackage	Product
945
	 */
946
	function woocommerce_show_product_sale_flash() {
947
		wc_get_template( 'single-product/sale-flash.php' );
948
	}
949
}
950
951
if ( ! function_exists( 'woocommerce_template_single_add_to_cart' ) ) {
952
953
	/**
954
	 * Trigger the single product add to cart action.
955
	 *
956
	 * @subpackage	Product
957
	 */
958
	function woocommerce_template_single_add_to_cart() {
959
		global $product;
960
		do_action( 'woocommerce_' . $product->product_type . '_add_to_cart' );
961
	}
962
}
963
if ( ! function_exists( 'woocommerce_simple_add_to_cart' ) ) {
964
965
	/**
966
	 * Output the simple product add to cart area.
967
	 *
968
	 * @subpackage	Product
969
	 */
970
	function woocommerce_simple_add_to_cart() {
971
		wc_get_template( 'single-product/add-to-cart/simple.php' );
972
	}
973
}
974
if ( ! function_exists( 'woocommerce_grouped_add_to_cart' ) ) {
975
976
	/**
977
	 * Output the grouped product add to cart area.
978
	 *
979
	 * @subpackage	Product
980
	 */
981
	function woocommerce_grouped_add_to_cart() {
982
		global $product;
983
984
		wc_get_template( 'single-product/add-to-cart/grouped.php', array(
985
			'grouped_product'    => $product,
986
			'grouped_products'   => $product->get_children(),
987
			'quantites_required' => false
988
		) );
989
	}
990
}
991
if ( ! function_exists( 'woocommerce_variable_add_to_cart' ) ) {
992
993
	/**
994
	 * Output the variable product add to cart area.
995
	 *
996
	 * @subpackage	Product
997
	 */
998
	function woocommerce_variable_add_to_cart() {
999
		global $product;
1000
1001
		// Enqueue variation scripts
1002
		wp_enqueue_script( 'wc-add-to-cart-variation' );
1003
1004
		// Get Available variations?
1005
		$get_variations = sizeof( $product->get_children() ) <= apply_filters( 'woocommerce_ajax_variation_threshold', 30, $product );
1006
1007
		// Load the template
1008
		wc_get_template( 'single-product/add-to-cart/variable.php', array(
1009
			'available_variations' => $get_variations ? $product->get_available_variations() : false,
1010
			'attributes'           => $product->get_variation_attributes(),
1011
			'selected_attributes'  => $product->get_variation_default_attributes()
1012
		) );
1013
	}
1014
}
1015
if ( ! function_exists( 'woocommerce_external_add_to_cart' ) ) {
1016
1017
	/**
1018
	 * Output the external product add to cart area.
1019
	 *
1020
	 * @subpackage	Product
1021
	 */
1022
	function woocommerce_external_add_to_cart() {
1023
		global $product;
1024
1025
		if ( ! $product->add_to_cart_url() ) {
1026
			return;
1027
		}
1028
1029
		wc_get_template( 'single-product/add-to-cart/external.php', array(
1030
			'product_url' => $product->add_to_cart_url(),
1031
			'button_text' => $product->single_add_to_cart_text()
1032
		) );
1033
	}
1034
}
1035
1036
if ( ! function_exists( 'woocommerce_quantity_input' ) ) {
1037
1038
	/**
1039
	 * Output the quantity input for add to cart forms.
1040
	 *
1041
	 * @param  array $args Args for the input
1042
	 * @param  WC_Product|null $product
1043
	 * @param  boolean $echo Whether to return or echo|string
1044
	 */
1045
	function woocommerce_quantity_input( $args = array(), $product = null, $echo = true ) {
1046
		if ( is_null( $product ) ) {
1047
			$product = $GLOBALS['product'];
1048
		}
1049
1050
		$defaults = array(
1051
			'input_name'  => 'quantity',
1052
			'input_value' => '1',
1053
			'max_value'   => apply_filters( 'woocommerce_quantity_input_max', '', $product ),
1054
			'min_value'   => apply_filters( 'woocommerce_quantity_input_min', '', $product ),
1055
			'step'        => apply_filters( 'woocommerce_quantity_input_step', '1', $product ),
1056
			'pattern'     => apply_filters( 'woocommerce_quantity_input_pattern', has_filter( 'woocommerce_stock_amount', 'intval' ) ? '[0-9]*' : '' ),
1057
			'inputmode'   => apply_filters( 'woocommerce_quantity_input_inputmode', has_filter( 'woocommerce_stock_amount', 'intval' ) ? 'numeric' : '' ),
1058
		);
1059
1060
		$args = apply_filters( 'woocommerce_quantity_input_args', wp_parse_args( $args, $defaults ), $product );
1061
1062
		// Set min and max value to empty string if not set.
1063
		$args['min_value'] = isset( $args['min_value'] ) ? $args['min_value'] : '';
1064
		$args['max_value'] = isset( $args['max_value'] ) ? $args['max_value'] : '';
1065
1066
		// Apply sanity to min/max args - min cannot be lower than 0
1067
		if ( '' !== $args['min_value'] && is_numeric( $args['min_value'] ) && $args['min_value'] < 0 ) {
1068
			$args['min_value'] = 0; // Cannot be lower than 0
1069
		}
1070
1071
		// Max cannot be lower than 0 or min
1072
		if ( '' !== $args['max_value'] && is_numeric( $args['max_value'] ) ) {
1073
			$args['max_value'] = $args['max_value'] < 0 ? 0 : $args['max_value'];
1074
			$args['max_value'] = $args['max_value'] < $args['min_value'] ? $args['min_value'] : $args['max_value'];
1075
		}
1076
1077
		ob_start();
1078
1079
		wc_get_template( 'global/quantity-input.php', $args );
1080
1081
		if ( $echo ) {
1082
			echo ob_get_clean();
1083
		} else {
1084
			return ob_get_clean();
1085
		}
1086
	}
1087
}
1088
1089
if ( ! function_exists( 'woocommerce_product_description_tab' ) ) {
1090
1091
	/**
1092
	 * Output the description tab content.
1093
	 *
1094
	 * @subpackage	Product/Tabs
1095
	 */
1096
	function woocommerce_product_description_tab() {
1097
		wc_get_template( 'single-product/tabs/description.php' );
1098
	}
1099
}
1100
if ( ! function_exists( 'woocommerce_product_additional_information_tab' ) ) {
1101
1102
	/**
1103
	 * Output the attributes tab content.
1104
	 *
1105
	 * @subpackage	Product/Tabs
1106
	 */
1107
	function woocommerce_product_additional_information_tab() {
1108
		wc_get_template( 'single-product/tabs/additional-information.php' );
1109
	}
1110
}
1111
if ( ! function_exists( 'woocommerce_product_reviews_tab' ) ) {
1112
1113
	/**
1114
	 * Output the reviews tab content.
1115
	 * @deprecated  2.4.0 Unused
1116
	 * @subpackage	Product/Tabs
1117
	 */
1118
	function woocommerce_product_reviews_tab() {
1119
		_deprecated_function( 'woocommerce_product_reviews_tab', '2.4', '' );
1120
	}
1121
}
1122
1123
if ( ! function_exists( 'woocommerce_default_product_tabs' ) ) {
1124
1125
	/**
1126
	 * Add default product tabs to product pages.
1127
	 *
1128
	 * @param array $tabs
1129
	 * @return array
1130
	 */
1131
	function woocommerce_default_product_tabs( $tabs = array() ) {
1132
		global $product, $post;
1133
1134
		// Description tab - shows product content
1135
		if ( $post->post_content ) {
1136
			$tabs['description'] = array(
1137
				'title'    => __( 'Description', 'woocommerce' ),
1138
				'priority' => 10,
1139
				'callback' => 'woocommerce_product_description_tab'
1140
			);
1141
		}
1142
1143
		// Additional information tab - shows attributes
1144
		if ( $product && ( $product->has_attributes() || ( $product->enable_dimensions_display() && ( $product->has_dimensions() || $product->has_weight() ) ) ) ) {
1145
			$tabs['additional_information'] = array(
1146
				'title'    => __( 'Additional Information', 'woocommerce' ),
1147
				'priority' => 20,
1148
				'callback' => 'woocommerce_product_additional_information_tab'
1149
			);
1150
		}
1151
1152
		// Reviews tab - shows comments
1153
		if ( comments_open() ) {
1154
			$tabs['reviews'] = array(
1155
				'title'    => sprintf( __( 'Reviews (%d)', 'woocommerce' ), $product->get_review_count() ),
1156
				'priority' => 30,
1157
				'callback' => 'comments_template'
1158
			);
1159
		}
1160
1161
		return $tabs;
1162
	}
1163
}
1164
1165
if ( ! function_exists( 'woocommerce_sort_product_tabs' ) ) {
1166
1167
	/**
1168
	 * Sort tabs by priority.
1169
	 *
1170
	 * @param array $tabs
1171
	 * @return array
1172
	 */
1173
	function woocommerce_sort_product_tabs( $tabs = array() ) {
1174
1175
		// Make sure the $tabs parameter is an array
1176
		if ( ! is_array( $tabs ) ) {
1177
			trigger_error( "Function woocommerce_sort_product_tabs() expects an array as the first parameter. Defaulting to empty array." );
1178
			$tabs = array( );
1179
		}
1180
1181
		// Re-order tabs by priority
1182
		if ( ! function_exists( '_sort_priority_callback' ) ) {
1183
			function _sort_priority_callback( $a, $b ) {
1184
				if ( $a['priority'] === $b['priority'] )
1185
					return 0;
1186
				return ( $a['priority'] < $b['priority'] ) ? -1 : 1;
1187
			}
1188
		}
1189
1190
		uasort( $tabs, '_sort_priority_callback' );
1191
1192
		return $tabs;
1193
	}
1194
}
1195
1196
if ( ! function_exists( 'woocommerce_comments' ) ) {
1197
1198
	/**
1199
	 * Output the Review comments template.
1200
	 *
1201
	 * @subpackage	Product
1202
	 * @param WP_Comment $comment
1203
	 * @param array $args
1204
	 * @param int $depth
1205
	 */
1206
	function woocommerce_comments( $comment, $args, $depth ) {
1207
		$GLOBALS['comment'] = $comment;
1208
		wc_get_template( 'single-product/review.php', array( 'comment' => $comment, 'args' => $args, 'depth' => $depth ) );
1209
	}
1210
}
1211
1212
if ( ! function_exists( 'woocommerce_review_display_gravatar' ) ) {
1213
	/**
1214
	 * Display the review authors gravatar
1215
	 *
1216
	 * @param array $comment WP_Comment.
1217
	 * @return void
1218
	 */
1219
	function woocommerce_review_display_gravatar( $comment ) {
1220
		echo get_avatar( $comment, apply_filters( 'woocommerce_review_gravatar_size', '60' ), '' );
1221
	}
1222
}
1223
1224
if ( ! function_exists( 'woocommerce_review_display_rating' ) ) {
1225
	/**
1226
	 * Display the reviewers star rating
1227
	 *
1228
	 * @return void
1229
	 */
1230
	function woocommerce_review_display_rating() {
1231
		wc_get_template( 'single-product/review-rating.php' );
1232
	}
1233
}
1234
1235
if ( ! function_exists( 'woocommerce_review_display_meta' ) ) {
1236
	/**
1237
	 * Display the review authors meta (name, verified owner, review date)
1238
	 *
1239
	 * @return void
1240
	 */
1241
	function woocommerce_review_display_meta() {
1242
		wc_get_template( 'single-product/review-meta.php' );
1243
	}
1244
}
1245
1246
if ( ! function_exists( 'woocommerce_review_display_comment_text' ) ) {
1247
1248
	/**
1249
	 * Display the review content.
1250
	 */
1251
	function woocommerce_review_display_comment_text() {
1252
		echo '<div itemprop="description" class="description">';
1253
		comment_text();
1254
		echo '</div>';
1255
	}
1256
}
1257
1258
if ( ! function_exists( 'woocommerce_output_related_products' ) ) {
1259
1260
	/**
1261
	 * Output the related products.
1262
	 *
1263
	 * @subpackage	Product
1264
	 */
1265
	function woocommerce_output_related_products() {
1266
1267
		$args = array(
1268
			'posts_per_page' 	=> 4,
1269
			'columns' 			=> 4,
1270
			'orderby' 			=> 'rand'
1271
		);
1272
1273
		woocommerce_related_products( apply_filters( 'woocommerce_output_related_products_args', $args ) );
1274
	}
1275
}
1276
1277
if ( ! function_exists( 'woocommerce_related_products' ) ) {
1278
1279
	/**
1280
	 * Output the related products.
1281
	 *
1282
	 * @param array Provided arguments
1283
	 * @param bool Columns argument for backwards compat
1284
	 * @param bool Order by argument for backwards compat
1285
	 */
1286
	function woocommerce_related_products( $args = array(), $columns = false, $orderby = false ) {
1287
		if ( ! is_array( $args ) ) {
1288
			_deprecated_argument( __FUNCTION__, '2.1', __( 'Use $args argument as an array instead. Deprecated argument will be removed in WC 2.2.', 'woocommerce' ) );
1289
1290
			$argsvalue = $args;
1291
1292
			$args = array(
1293
				'posts_per_page' => $argsvalue,
1294
				'columns'        => $columns,
1295
				'orderby'        => $orderby,
1296
			);
1297
		}
1298
1299
		$defaults = array(
1300
			'posts_per_page' => 2,
1301
			'columns'        => 2,
1302
			'orderby'        => 'rand'
1303
		);
1304
1305
		$args = wp_parse_args( $args, $defaults );
1306
1307
		wc_get_template( 'single-product/related.php', $args );
1308
	}
1309
}
1310
1311
if ( ! function_exists( 'woocommerce_upsell_display' ) ) {
1312
1313
	/**
1314
	 * Output product up sells.
1315
	 *
1316
	 * @param int $posts_per_page (default: -1)
1317
	 * @param int $columns (default: 4)
1318
	 * @param string $orderby (default: 'rand')
1319
	 */
1320
	function woocommerce_upsell_display( $posts_per_page = '-1', $columns = 4, $orderby = 'rand' ) {
1321
		$args = apply_filters( 'woocommerce_upsell_display_args', array(
1322
			'posts_per_page'	=> $posts_per_page,
1323
			'orderby'			=> apply_filters( 'woocommerce_upsells_orderby', $orderby ),
1324
			'columns'			=> $columns
1325
		) );
1326
1327
		wc_get_template( 'single-product/up-sells.php', $args );
1328
	}
1329
}
1330
1331
/** Cart ******************************************************************/
1332
1333
if ( ! function_exists( 'woocommerce_shipping_calculator' ) ) {
1334
1335
	/**
1336
	 * Output the cart shipping calculator.
1337
	 *
1338
	 * @subpackage	Cart
1339
	 */
1340
	function woocommerce_shipping_calculator() {
1341
		wc_get_template( 'cart/shipping-calculator.php' );
1342
	}
1343
}
1344
1345
if ( ! function_exists( 'woocommerce_cart_totals' ) ) {
1346
1347
	/**
1348
	 * Output the cart totals.
1349
	 *
1350
	 * @subpackage	Cart
1351
	 */
1352
	function woocommerce_cart_totals() {
1353
		if ( is_checkout() ) {
1354
			return;
1355
		}
1356
		wc_get_template( 'cart/cart-totals.php' );
1357
	}
1358
}
1359
1360
if ( ! function_exists( 'woocommerce_cross_sell_display' ) ) {
1361
1362
	/**
1363
	 * Output the cart cross-sells.
1364
	 *
1365
	 * @param  int $posts_per_page (default: 2)
1366
	 * @param  int $columns (default: 2)
1367
	 * @param  string $orderby (default: 'rand')
1368
	 */
1369
	function woocommerce_cross_sell_display( $posts_per_page = 2, $columns = 2, $orderby = 'rand' ) {
1370
		if ( is_checkout() ) {
1371
			return;
1372
		}
1373
		wc_get_template( 'cart/cross-sells.php', array(
1374
			'posts_per_page' => $posts_per_page,
1375
			'orderby'        => $orderby,
1376
			'columns'        => $columns
1377
		) );
1378
	}
1379
}
1380
1381
if ( ! function_exists( 'woocommerce_button_proceed_to_checkout' ) ) {
1382
1383
	/**
1384
	 * Output the proceed to checkout button.
1385
	 *
1386
	 * @subpackage	Cart
1387
	 */
1388
	function woocommerce_button_proceed_to_checkout() {
1389
		wc_get_template( 'cart/proceed-to-checkout-button.php' );
1390
	}
1391
}
1392
1393 View Code Duplication
if ( ! function_exists( 'woocommerce_widget_shopping_cart_button_view_cart' ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1394
1395
	/**
1396
	 * Output the proceed to checkout button.
1397
	 *
1398
	 * @subpackage	Cart
1399
	 */
1400
	function woocommerce_widget_shopping_cart_button_view_cart() {
1401
		echo '<a href="' . esc_url( wc_get_cart_url() ) . '" class="button wc-forward">' . __( 'View Cart', 'woocommerce' ) . '</a>';
1402
	}
1403
}
1404
1405 View Code Duplication
if ( ! function_exists( 'woocommerce_widget_shopping_cart_proceed_to_checkout' ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1406
1407
	/**
1408
	 * Output the proceed to checkout button.
1409
	 *
1410
	 * @subpackage	Cart
1411
	 */
1412
	function woocommerce_widget_shopping_cart_proceed_to_checkout() {
1413
		echo '<a href="' . esc_url( wc_get_checkout_url() ) . '" class="button checkout wc-forward">' . __( 'Checkout', 'woocommerce' ) . '</a>';
1414
	}
1415
}
1416
1417
/** Mini-Cart *************************************************************/
1418
1419
if ( ! function_exists( 'woocommerce_mini_cart' ) ) {
1420
1421
	/**
1422
	 * Output the Mini-cart - used by cart widget.
1423
	 *
1424
	 * @param array $args
1425
	 */
1426
	function woocommerce_mini_cart( $args = array() ) {
1427
1428
		$defaults = array(
1429
			'list_class' => ''
1430
		);
1431
1432
		$args = wp_parse_args( $args, $defaults );
1433
1434
		wc_get_template( 'cart/mini-cart.php', $args );
1435
	}
1436
}
1437
1438
/** Login *****************************************************************/
1439
1440
if ( ! function_exists( 'woocommerce_login_form' ) ) {
1441
1442
	/**
1443
	 * Output the WooCommerce Login Form.
1444
	 *
1445
	 * @subpackage	Forms
1446
	 * @param array $args
1447
	 */
1448
	function woocommerce_login_form( $args = array() ) {
1449
1450
		$defaults = array(
1451
			'message'  => '',
1452
			'redirect' => '',
1453
			'hidden'   => false
1454
		);
1455
1456
		$args = wp_parse_args( $args, $defaults  );
1457
1458
		wc_get_template( 'global/form-login.php', $args );
1459
	}
1460
}
1461
1462
if ( ! function_exists( 'woocommerce_checkout_login_form' ) ) {
1463
1464
	/**
1465
	 * Output the WooCommerce Checkout Login Form.
1466
	 *
1467
	 * @subpackage	Checkout
1468
	 */
1469
	function woocommerce_checkout_login_form() {
1470
		wc_get_template( 'checkout/form-login.php', array( 'checkout' => WC()->checkout() ) );
1471
	}
1472
}
1473
1474
if ( ! function_exists( 'woocommerce_breadcrumb' ) ) {
1475
1476
	/**
1477
	 * Output the WooCommerce Breadcrumb.
1478
	 *
1479
	 * @param array $args
1480
	 */
1481
	function woocommerce_breadcrumb( $args = array() ) {
1482
		$args = wp_parse_args( $args, apply_filters( 'woocommerce_breadcrumb_defaults', array(
1483
			'delimiter'   => '&nbsp;&#47;&nbsp;',
1484
			'wrap_before' => '<nav class="woocommerce-breadcrumb" ' . ( is_single() ? 'itemprop="breadcrumb"' : '' ) . '>',
1485
			'wrap_after'  => '</nav>',
1486
			'before'      => '',
1487
			'after'       => '',
1488
			'home'        => _x( 'Home', 'breadcrumb', 'woocommerce' )
1489
		) ) );
1490
1491
		$breadcrumbs = new WC_Breadcrumb();
1492
1493
		if ( ! empty( $args['home'] ) ) {
1494
			$breadcrumbs->add_crumb( $args['home'], apply_filters( 'woocommerce_breadcrumb_home_url', home_url() ) );
1495
		}
1496
1497
		$args['breadcrumb'] = $breadcrumbs->generate();
1498
1499
		wc_get_template( 'global/breadcrumb.php', $args );
1500
	}
1501
}
1502
1503
if ( ! function_exists( 'woocommerce_order_review' ) ) {
1504
1505
	/**
1506
	 * Output the Order review table for the checkout.
1507
	 *
1508
	 * @subpackage	Checkout
1509
	 */
1510
	function woocommerce_order_review( $deprecated = false ) {
0 ignored issues
show
Unused Code introduced by
The parameter $deprecated is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1511
		wc_get_template( 'checkout/review-order.php', array( 'checkout' => WC()->checkout() ) );
1512
	}
1513
}
1514
1515
if ( ! function_exists( 'woocommerce_checkout_payment' ) ) {
1516
1517
	/**
1518
	 * Output the Payment Methods on the checkout.
1519
	 *
1520
	 * @subpackage	Checkout
1521
	 */
1522
	function woocommerce_checkout_payment() {
1523
		if ( WC()->cart->needs_payment() ) {
1524
			$available_gateways = WC()->payment_gateways()->get_available_payment_gateways();
1525
			WC()->payment_gateways()->set_current_gateway( $available_gateways );
1526
		} else {
1527
			$available_gateways = array();
1528
		}
1529
1530
		wc_get_template( 'checkout/payment.php', array(
1531
			'checkout'           => WC()->checkout(),
1532
			'available_gateways' => $available_gateways,
1533
			'order_button_text'  => apply_filters( 'woocommerce_order_button_text', __( 'Place order', 'woocommerce' ) )
1534
		) );
1535
	}
1536
}
1537
1538
if ( ! function_exists( 'woocommerce_checkout_coupon_form' ) ) {
1539
1540
	/**
1541
	 * Output the Coupon form for the checkout.
1542
	 *
1543
	 * @subpackage	Checkout
1544
	 */
1545
	function woocommerce_checkout_coupon_form() {
1546
		wc_get_template( 'checkout/form-coupon.php', array( 'checkout' => WC()->checkout() ) );
1547
	}
1548
}
1549
1550
if ( ! function_exists( 'woocommerce_products_will_display' ) ) {
1551
1552
	/**
1553
	 * Check if we will be showing products or not (and not sub-categories only).
1554
	 * @subpackage	Loop
1555
	 * @return bool
1556
	 */
1557
	function woocommerce_products_will_display() {
1558
		global $wpdb;
1559
1560
		if ( is_shop() ) {
1561
			return 'subcategories' !== get_option( 'woocommerce_shop_page_display' ) || is_search();
1562
		}
1563
1564
		if ( ! is_product_taxonomy() ) {
1565
			return false;
1566
		}
1567
1568
		if ( is_search() || is_filtered() || is_paged() ) {
1569
			return true;
1570
		}
1571
1572
		$term = get_queried_object();
1573
1574
		if ( is_product_category() ) {
1575
			switch ( get_woocommerce_term_meta( $term->term_id, 'display_type', true ) ) {
1576
				case 'subcategories' :
1577
					// Nothing - we want to continue to see if there are products/subcats
1578
				break;
1579
				case 'products' :
1580
				case 'both' :
1581
					return true;
1582
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1583
				default :
1584
					// Default - no setting
1585
					if ( get_option( 'woocommerce_category_archive_display' ) != 'subcategories' ) {
1586
						return true;
1587
					}
1588
				break;
1589
			}
1590
		}
1591
1592
		// Begin subcategory logic
1593
		if ( empty( $term->term_id ) || empty( $term->taxonomy ) ) {
1594
			return true;
1595
		}
1596
1597
		$transient_name = 'wc_products_will_display_' . $term->term_id . '_' . WC_Cache_Helper::get_transient_version( 'product_query' );
1598
1599
		if ( false === ( $products_will_display = get_transient( $transient_name ) ) ) {
1600
			$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 ) );
1601
1602
			if ( $has_children ) {
1603
				// Check terms have products inside - parents first. If products are found inside, subcats will be shown instead of products so we can return false.
1604
				if ( sizeof( get_objects_in_term( $has_children, $term->taxonomy ) ) > 0 ) {
1605
					$products_will_display = false;
1606
				} else {
1607
					// If we get here, the parents were empty so we're forced to check children
1608
					foreach ( $has_children as $term_id ) {
1609
						$children = get_term_children( $term_id, $term->taxonomy );
1610
1611
						if ( sizeof( get_objects_in_term( $children, $term->taxonomy ) ) > 0 ) {
1612
							$products_will_display = false;
1613
							break;
1614
						}
1615
					}
1616
				}
1617
			} else {
1618
				$products_will_display = true;
1619
			}
1620
		}
1621
1622
		set_transient( $transient_name, $products_will_display, DAY_IN_SECONDS * 30 );
1623
1624
		return $products_will_display;
1625
	}
1626
}
1627
1628
if ( ! function_exists( 'woocommerce_product_subcategories' ) ) {
1629
1630
	/**
1631
	 * Display product sub categories as thumbnails.
1632
	 *
1633
	 * @subpackage	Loop
1634
	 * @param array $args
1635
	 * @return null|boolean
1636
	 */
1637
	function woocommerce_product_subcategories( $args = array() ) {
1638
		global $wp_query;
1639
1640
		$defaults = array(
1641
			'before'        => '',
1642
			'after'         => '',
1643
			'force_display' => false
1644
		);
1645
1646
		$args = wp_parse_args( $args, $defaults );
1647
1648
		extract( $args );
1649
1650
		// Main query only
1651
		if ( ! is_main_query() && ! $force_display ) {
1652
			return;
1653
		}
1654
1655
		// Don't show when filtering, searching or when on page > 1 and ensure we're on a product archive
1656
		if ( is_search() || is_filtered() || is_paged() || ( ! is_product_category() && ! is_shop() ) ) {
1657
			return;
1658
		}
1659
1660
		// Check categories are enabled
1661
		if ( is_shop() && '' === get_option( 'woocommerce_shop_page_display' ) ) {
1662
			return;
1663
		}
1664
1665
		// Find the category + category parent, if applicable
1666
		$term 			= get_queried_object();
1667
		$parent_id 		= empty( $term->term_id ) ? 0 : $term->term_id;
1668
1669
		if ( is_product_category() ) {
1670
			$display_type = get_woocommerce_term_meta( $term->term_id, 'display_type', true );
1671
1672
			switch ( $display_type ) {
1673
				case 'products' :
1674
					return;
1675
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1676
				case '' :
1677
					if ( '' === get_option( 'woocommerce_category_archive_display' ) ) {
1678
						return;
1679
					}
1680
				break;
1681
			}
1682
		}
1683
1684
		// 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
1685
		$product_categories = get_categories( apply_filters( 'woocommerce_product_subcategories_args', array(
1686
			'parent'       => $parent_id,
1687
			'menu_order'   => 'ASC',
1688
			'hide_empty'   => 0,
1689
			'hierarchical' => 1,
1690
			'taxonomy'     => 'product_cat',
1691
			'pad_counts'   => 1
1692
		) ) );
1693
1694
		if ( ! apply_filters( 'woocommerce_product_subcategories_hide_empty', false ) ) {
1695
			$product_categories = wp_list_filter( $product_categories, array( 'count' => 0 ), 'NOT' );
1696
		}
1697
1698
		if ( $product_categories ) {
1699
			echo $before;
1700
1701
			foreach ( $product_categories as $category ) {
1702
				wc_get_template( 'content-product_cat.php', array(
1703
					'category' => $category
1704
				) );
1705
			}
1706
1707
			// If we are hiding products disable the loop and pagination
1708
			if ( is_product_category() ) {
1709
				$display_type = get_woocommerce_term_meta( $term->term_id, 'display_type', true );
1710
1711
				switch ( $display_type ) {
1712
					case 'subcategories' :
1713
						$wp_query->post_count    = 0;
1714
						$wp_query->max_num_pages = 0;
1715
					break;
1716
					case '' :
1717
						if ( 'subcategories' === get_option( 'woocommerce_category_archive_display' ) ) {
1718
							$wp_query->post_count    = 0;
1719
							$wp_query->max_num_pages = 0;
1720
						}
1721
					break;
1722
				}
1723
			}
1724
1725
			if ( is_shop() && 'subcategories' === get_option( 'woocommerce_shop_page_display' ) ) {
1726
				$wp_query->post_count    = 0;
1727
				$wp_query->max_num_pages = 0;
1728
			}
1729
1730
			echo $after;
1731
1732
			return true;
1733
		}
1734
	}
1735
}
1736
1737
if ( ! function_exists( 'woocommerce_subcategory_thumbnail' ) ) {
1738
1739
	/**
1740
	 * Show subcategory thumbnails.
1741
	 *
1742
	 * @param mixed $category
1743
	 * @subpackage	Loop
1744
	 */
1745
	function woocommerce_subcategory_thumbnail( $category ) {
1746
		$small_thumbnail_size  	= apply_filters( 'subcategory_archive_thumbnail_size', 'shop_catalog' );
1747
		$dimensions    			= wc_get_image_size( $small_thumbnail_size );
1748
		$thumbnail_id  			= get_woocommerce_term_meta( $category->term_id, 'thumbnail_id', true  );
1749
1750
		if ( $thumbnail_id ) {
1751
			$image        = wp_get_attachment_image_src( $thumbnail_id, $small_thumbnail_size  );
1752
			$image        = $image[0];
1753
			$image_srcset = function_exists( 'wp_get_attachment_image_srcset' ) ? wp_get_attachment_image_srcset( $thumbnail_id, $small_thumbnail_size ) : false;
1754
			$image_sizes  = function_exists( 'wp_get_attachment_image_sizes' ) ? wp_get_attachment_image_sizes( $thumbnail_id, $small_thumbnail_size ) : false;
1755
		} else {
1756
			$image        = wc_placeholder_img_src();
1757
			$image_srcset = $image_sizes = false;
1758
		}
1759
1760
		if ( $image ) {
1761
			// Prevent esc_url from breaking spaces in urls for image embeds
1762
			// Ref: https://core.trac.wordpress.org/ticket/23605
1763
			$image = str_replace( ' ', '%20', $image );
1764
1765
			// Add responsive image markup if available
1766
			if ( $image_srcset && $image_sizes ) {
1767
				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 ) . '" />';
1768
			} else {
1769
				echo '<img src="' . esc_url( $image ) . '" alt="' . esc_attr( $category->name ) . '" width="' . esc_attr( $dimensions['width'] ) . '" height="' . esc_attr( $dimensions['height'] ) . '" />';
1770
			}
1771
		}
1772
	}
1773
}
1774
1775
if ( ! function_exists( 'woocommerce_order_details_table' ) ) {
1776
1777
	/**
1778
	 * Displays order details in a table.
1779
	 *
1780
	 * @param mixed $order_id
1781
	 * @subpackage	Orders
1782
	 */
1783
	function woocommerce_order_details_table( $order_id ) {
1784
		if ( ! $order_id ) return;
1785
1786
		wc_get_template( 'order/order-details.php', array(
1787
			'order_id' => $order_id
1788
		) );
1789
	}
1790
}
1791
1792
1793
if ( ! function_exists( 'woocommerce_order_again_button' ) ) {
1794
1795
	/**
1796
	 * Display an 'order again' button on the view order page.
1797
	 *
1798
	 * @param object $order
1799
	 * @subpackage	Orders
1800
	 */
1801
	function woocommerce_order_again_button( $order ) {
1802
		if ( ! $order || ! $order->has_status( 'completed' ) || ! is_user_logged_in() ) {
1803
			return;
1804
		}
1805
1806
		wc_get_template( 'order/order-again.php', array(
1807
			'order' => $order
1808
		) );
1809
	}
1810
}
1811
1812
/** Forms ****************************************************************/
1813
1814
if ( ! function_exists( 'woocommerce_form_field' ) ) {
1815
1816
	/**
1817
	 * Outputs a checkout/address form field.
1818
	 *
1819
	 * @subpackage	Forms
1820
	 * @param string $key
1821
	 * @param mixed $args
1822
	 * @param string $value (default: null)
1823
	 * @todo This function needs to be broken up in smaller pieces
1824
	 */
1825
	function woocommerce_form_field( $key, $args, $value = null ) {
1826
		$defaults = array(
1827
			'type'              => 'text',
1828
			'label'             => '',
1829
			'description'       => '',
1830
			'placeholder'       => '',
1831
			'maxlength'         => false,
1832
			'required'          => false,
1833
			'autocomplete'      => false,
1834
			'id'                => $key,
1835
			'class'             => array(),
1836
			'label_class'       => array(),
1837
			'input_class'       => array(),
1838
			'return'            => false,
1839
			'options'           => array(),
1840
			'custom_attributes' => array(),
1841
			'validate'          => array(),
1842
			'default'           => '',
1843
		);
1844
1845
		$args = wp_parse_args( $args, $defaults );
1846
		$args = apply_filters( 'woocommerce_form_field_args', $args, $key, $value );
1847
1848
		if ( $args['required'] ) {
1849
			$args['class'][] = 'validate-required';
1850
			$required = ' <abbr class="required" title="' . esc_attr__( 'required', 'woocommerce'  ) . '">*</abbr>';
1851
		} else {
1852
			$required = '';
1853
		}
1854
1855
		$args['maxlength'] = ( $args['maxlength'] ) ? 'maxlength="' . absint( $args['maxlength'] ) . '"' : '';
1856
1857
		$args['autocomplete'] = ( $args['autocomplete'] ) ? 'autocomplete="' . esc_attr( $args['autocomplete'] ) . '"' : '';
1858
1859
		if ( is_string( $args['label_class'] ) ) {
1860
			$args['label_class'] = array( $args['label_class'] );
1861
		}
1862
1863
		if ( is_null( $value ) ) {
1864
			$value = $args['default'];
1865
		}
1866
1867
		// Custom attribute handling
1868
		$custom_attributes = array();
1869
1870 View Code Duplication
		if ( ! empty( $args['custom_attributes'] ) && is_array( $args['custom_attributes'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1871
			foreach ( $args['custom_attributes'] as $attribute => $attribute_value ) {
1872
				$custom_attributes[] = esc_attr( $attribute ) . '="' . esc_attr( $attribute_value ) . '"';
1873
			}
1874
		}
1875
1876
		if ( ! empty( $args['validate'] ) ) {
1877
			foreach( $args['validate'] as $validate ) {
0 ignored issues
show
Bug introduced by
The expression $args['validate'] of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1878
				$args['class'][] = 'validate-' . $validate;
1879
			}
1880
		}
1881
1882
		$field = '';
1883
		$label_id = $args['id'];
1884
		$field_container = '<p class="form-row %1$s" id="%2$s">%3$s</p>';
1885
1886
		switch ( $args['type'] ) {
1887
			case 'country' :
1888
1889
				$countries = 'shipping_country' === $key ? WC()->countries->get_shipping_countries() : WC()->countries->get_allowed_countries();
1890
1891
				if ( 1 === sizeof( $countries ) ) {
1892
1893
					$field .= '<strong>' . current( array_values( $countries ) ) . '</strong>';
1894
1895
					$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" />';
1896
1897
				} else {
1898
1899
					$field = '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" ' . $args['autocomplete'] . ' class="country_to_state country_select ' . esc_attr( implode( ' ', $args['input_class'] ) ) .'" ' . implode( ' ', $custom_attributes ) . '>'
1900
							. '<option value="">'.__( 'Select a country&hellip;', 'woocommerce' ) .'</option>';
1901
1902 View Code Duplication
					foreach ( $countries as $ckey => $cvalue ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1903
						$field .= '<option value="' . esc_attr( $ckey ) . '" '. selected( $value, $ckey, false ) . '>'. __( $cvalue, 'woocommerce' ) .'</option>';
1904
					}
1905
1906
					$field .= '</select>';
1907
1908
					$field .= '<noscript><input type="submit" name="woocommerce_checkout_update_totals" value="' . esc_attr__( 'Update country', 'woocommerce' ) . '" /></noscript>';
1909
1910
				}
1911
1912
				break;
1913
			case 'state' :
1914
1915
				/* Get Country */
1916
				$country_key = 'billing_state' === $key ? 'billing_country' : 'shipping_country';
1917
				$current_cc  = WC()->checkout->get_value( $country_key );
1918
				$states      = WC()->countries->get_states( $current_cc );
1919
1920
				if ( is_array( $states ) && empty( $states ) ) {
1921
1922
					$field_container = '<p class="form-row %1$s" id="%2$s" style="display: none">%3$s</p>';
1923
1924
					$field .= '<input type="hidden" class="hidden" name="' . esc_attr( $key )  . '" id="' . esc_attr( $args['id'] ) . '" value="" ' . implode( ' ', $custom_attributes ) . ' placeholder="' . esc_attr( $args['placeholder'] ) . '" />';
1925
1926
				} elseif ( is_array( $states ) ) {
1927
1928
					$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'] ) . '" ' . $args['autocomplete'] . '>
1929
						<option value="">'.__( 'Select a state&hellip;', 'woocommerce' ) .'</option>';
1930
1931 View Code Duplication
					foreach ( $states as $ckey => $cvalue ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1932
						$field .= '<option value="' . esc_attr( $ckey ) . '" '.selected( $value, $ckey, false ) .'>'.__( $cvalue, 'woocommerce' ) .'</option>';
1933
					}
1934
1935
					$field .= '</select>';
1936
1937
				} else {
1938
1939
					$field .= '<input type="text" class="input-text ' . esc_attr( implode( ' ', $args['input_class'] ) ) .'" value="' . esc_attr( $value ) . '"  placeholder="' . esc_attr( $args['placeholder'] ) . '" ' . $args['autocomplete'] . ' name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" ' . implode( ' ', $custom_attributes ) . ' />';
1940
1941
				}
1942
1943
				break;
1944
			case 'textarea' :
1945
1946
				$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'] ) . '" ' . $args['maxlength'] . ' ' . $args['autocomplete'] . ' ' . ( empty( $args['custom_attributes']['rows'] ) ? ' rows="2"' : '' ) . ( empty( $args['custom_attributes']['cols'] ) ? ' cols="5"' : '' ) . implode( ' ', $custom_attributes ) . '>'. esc_textarea( $value  ) .'</textarea>';
1947
1948
				break;
1949 View Code Duplication
			case 'checkbox' :
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1950
1951
				$field = '<label class="checkbox ' . implode( ' ', $args['label_class'] ) .'" ' . implode( ' ', $custom_attributes ) . '>
1952
						<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 ) .' /> '
1953
						 . $args['label'] . $required . '</label>';
1954
1955
				break;
1956
			case 'password' :
1957
			case 'text' :
1958
			case 'email' :
1959
			case 'tel' :
1960 View Code Duplication
			case 'number' :
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1961
1962
				$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'] ) . '" ' . $args['maxlength'] . ' ' . $args['autocomplete'] . ' value="' . esc_attr( $value ) . '" ' . implode( ' ', $custom_attributes ) . ' />';
1963
1964
				break;
1965
			case 'select' :
1966
1967
				$options = $field = '';
1968
1969
				if ( ! empty( $args['options'] ) ) {
1970
					foreach ( $args['options'] as $option_key => $option_text ) {
1971
						if ( '' === $option_key ) {
1972
							// If we have a blank option, select2 needs a placeholder
1973
							if ( empty( $args['placeholder'] ) ) {
1974
								$args['placeholder'] = $option_text ? $option_text : __( 'Choose an option', 'woocommerce' );
1975
							}
1976
							$custom_attributes[] = 'data-allow_clear="true"';
1977
						}
1978
						$options .= '<option value="' . esc_attr( $option_key ) . '" '. selected( $value, $option_key, false ) . '>' . esc_attr( $option_text ) .'</option>';
1979
					}
1980
1981
					$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'] ) . '" ' . $args['autocomplete'] . '>
1982
							' . $options . '
1983
						</select>';
1984
				}
1985
1986
				break;
1987
			case 'radio' :
1988
1989
				$label_id = current( array_keys( $args['options'] ) );
1990
1991
				if ( ! empty( $args['options'] ) ) {
1992
					foreach ( $args['options'] as $option_key => $option_text ) {
0 ignored issues
show
Bug introduced by
The expression $args['options'] of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1993
						$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 ) . ' />';
1994
						$field .= '<label for="' . esc_attr( $args['id'] ) . '_' . esc_attr( $option_key ) . '" class="radio ' . implode( ' ', $args['label_class'] ) .'">' . $option_text . '</label>';
1995
					}
1996
				}
1997
1998
				break;
1999
		}
2000
2001
		if ( ! empty( $field ) ) {
2002
			$field_html = '';
2003
2004
			if ( $args['label'] && 'checkbox' != $args['type'] ) {
2005
				$field_html .= '<label for="' . esc_attr( $label_id ) . '" class="' . esc_attr( implode( ' ', $args['label_class'] ) ) .'">' . $args['label'] . $required . '</label>';
2006
			}
2007
2008
			$field_html .= $field;
2009
2010
			if ( $args['description'] ) {
2011
				$field_html .= '<span class="description">' . esc_html( $args['description'] ) . '</span>';
2012
			}
2013
2014
			$container_class = 'form-row ' . esc_attr( implode( ' ', $args['class'] ) );
2015
			$container_id = esc_attr( $args['id'] ) . '_field';
2016
2017
			$after = ! empty( $args['clear'] ) ? '<div class="clear"></div>' : '';
2018
2019
			$field = sprintf( $field_container, $container_class, $container_id, $field_html ) . $after;
2020
		}
2021
2022
		$field = apply_filters( 'woocommerce_form_field_' . $args['type'], $field, $key, $args, $value );
2023
2024
		if ( $args['return'] ) {
2025
			return $field;
2026
		} else {
2027
			echo $field;
2028
		}
2029
	}
2030
}
2031
2032
if ( ! function_exists( 'get_product_search_form' ) ) {
2033
2034
	/**
2035
	 * Display product search form.
2036
	 *
2037
	 * Will first attempt to locate the product-searchform.php file in either the child or.
2038
	 * the parent, then load it. If it doesn't exist, then the default search form.
2039
	 * will be displayed.
2040
	 *
2041
	 * The default searchform uses html5.
2042
	 *
2043
	 * @subpackage	Forms
2044
	 * @param bool $echo (default: true)
2045
	 * @return string
2046
	 */
2047
	function get_product_search_form( $echo = true  ) {
2048
		global $product_search_form_index;
2049
2050
		ob_start();
2051
2052
		if ( empty( $product_search_form_index ) ) {
2053
			$product_search_form_index = 0;
2054
		}
2055
2056
		do_action( 'pre_get_product_search_form'  );
2057
2058
		wc_get_template( 'product-searchform.php', array(
2059
			'index' => $product_search_form_index++,
2060
		) );
2061
2062
		$form = apply_filters( 'get_product_search_form', ob_get_clean() );
2063
2064
		if ( $echo ) {
2065
			echo $form;
2066
		} else {
2067
			return $form;
2068
		}
2069
	}
2070
}
2071
2072
if ( ! function_exists( 'woocommerce_output_auth_header' ) ) {
2073
2074
	/**
2075
	 * Output the Auth header.
2076
	 */
2077
	function woocommerce_output_auth_header() {
2078
		wc_get_template( 'auth/header.php' );
2079
	}
2080
}
2081
2082
if ( ! function_exists( 'woocommerce_output_auth_footer' ) ) {
2083
2084
	/**
2085
	 * Output the Auth footer.
2086
	 */
2087
	function woocommerce_output_auth_footer() {
2088
		wc_get_template( 'auth/footer.php' );
2089
	}
2090
}
2091
2092
if ( ! function_exists( 'woocommerce_single_variation' ) ) {
2093
2094
	/**
2095
	 * Output placeholders for the single variation.
2096
	 */
2097
	function woocommerce_single_variation() {
2098
		echo '<div class="woocommerce-variation single_variation"></div>';
2099
	}
2100
}
2101
2102
if ( ! function_exists( 'woocommerce_single_variation_add_to_cart_button' ) ) {
2103
2104
	/**
2105
	 * Output the add to cart button for variations.
2106
	 */
2107
	function woocommerce_single_variation_add_to_cart_button() {
2108
		wc_get_template( 'single-product/add-to-cart/variation-add-to-cart-button.php' );
2109
	}
2110
}
2111
2112
if ( ! function_exists( 'wc_dropdown_variation_attribute_options' ) ) {
2113
2114
	/**
2115
	 * Output a list of variation attributes for use in the cart forms.
2116
	 *
2117
	 * @param array $args
2118
	 * @since 2.4.0
2119
	 */
2120
	function wc_dropdown_variation_attribute_options( $args = array() ) {
2121
		$args = wp_parse_args( apply_filters( 'woocommerce_dropdown_variation_attribute_options_args', $args ), array(
2122
			'options'          => false,
2123
			'attribute'        => false,
2124
			'product'          => false,
2125
			'selected' 	       => false,
2126
			'name'             => '',
2127
			'id'               => '',
2128
			'class'            => '',
2129
			'show_option_none' => __( 'Choose an option', 'woocommerce' )
2130
		) );
2131
2132
		$options   = $args['options'];
2133
		$product   = $args['product'];
2134
		$attribute = $args['attribute'];
2135
		$name      = $args['name'] ? $args['name'] : 'attribute_' . sanitize_title( $attribute );
2136
		$id        = $args['id'] ? $args['id'] : sanitize_title( $attribute );
2137
		$class     = $args['class'];
2138
2139
		if ( empty( $options ) && ! empty( $product ) && ! empty( $attribute ) ) {
2140
			$attributes = $product->get_variation_attributes();
2141
			$options    = $attributes[ $attribute ];
2142
		}
2143
2144
		$html = '<select id="' . esc_attr( $id ) . '" class="' . esc_attr( $class ) . '" name="' . esc_attr( $name ) . '" data-attribute_name="attribute_' . esc_attr( sanitize_title( $attribute ) ) . '">';
2145
2146
		if ( $args['show_option_none'] ) {
2147
			$html .= '<option value="">' . esc_html( $args['show_option_none'] ) . '</option>';
2148
		}
2149
2150
		if ( ! empty( $options ) ) {
2151
			if ( $product && taxonomy_exists( $attribute ) ) {
2152
				// Get terms if this is a taxonomy - ordered. We need the names too.
2153
				$terms = wc_get_product_terms( $product->id, $attribute, array( 'fields' => 'all' ) );
2154
2155 View Code Duplication
				foreach ( $terms as $term ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2156
					if ( in_array( $term->slug, $options ) ) {
2157
						$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>';
2158
					}
2159
				}
2160
			} else {
2161
				foreach ( $options as $option ) {
2162
					// This handles < 2.4.0 bw compatibility where text attributes were not sanitized.
2163
					$selected = sanitize_title( $args['selected'] ) === $args['selected'] ? selected( $args['selected'], sanitize_title( $option ), false ) : selected( $args['selected'], $option, false );
2164
					$html .= '<option value="' . esc_attr( $option ) . '" ' . $selected . '>' . esc_html( apply_filters( 'woocommerce_variation_option_name', $option ) ) . '</option>';
2165
				}
2166
			}
2167
		}
2168
2169
		$html .= '</select>';
2170
2171
		echo apply_filters( 'woocommerce_dropdown_variation_attribute_options_html', $html, $args );
2172
	}
2173
}
2174
2175
if ( ! function_exists( 'woocommerce_account_content' ) ) {
2176
2177
	/**
2178
	 * My Account content output.
2179
	 */
2180
	function woocommerce_account_content() {
2181
		global $wp;
2182
2183
		foreach ( $wp->query_vars as $key => $value ) {
2184
			// Ignore pagename param.
2185
			if ( 'pagename' === $key ) {
2186
				continue;
2187
			}
2188
2189
			if ( has_action( 'woocommerce_account_' . $key . '_endpoint' ) ) {
2190
				do_action( 'woocommerce_account_' . $key . '_endpoint', $value );
2191
				return;
2192
			}
2193
		}
2194
2195
		// No endpoint found? Default to dashboard.
2196
		wc_get_template( 'myaccount/dashboard.php', array(
2197
			'current_user' => get_user_by( 'id', get_current_user_id() ),
2198
		) );
2199
	}
2200
}
2201
2202
if ( ! function_exists( 'woocommerce_account_navigation' ) ) {
2203
2204
	/**
2205
	 * My Account navigation template.
2206
	 */
2207
	function woocommerce_account_navigation() {
2208
		wc_get_template( 'myaccount/navigation.php' );
2209
	}
2210
}
2211
2212
if ( ! function_exists( 'woocommerce_account_orders' ) ) {
2213
2214
	/**
2215
	 * My Account > Orders template.
2216
	 *
2217
	 * @param int $current_page Current page number.
2218
	 */
2219
	function woocommerce_account_orders( $current_page ) {
2220
		$current_page    = empty( $current_page ) ? 1 : absint( $current_page );
2221
		$customer_orders = wc_get_orders( apply_filters( 'woocommerce_my_account_my_orders_query', array( 'customer' => get_current_user_id(), 'page' => $current_page, 'paginate' => true ) ) );
2222
2223
		wc_get_template(
2224
			'myaccount/orders.php',
2225
			array(
2226
				'current_page' => absint( $current_page ),
2227
				'customer_orders' => $customer_orders,
2228
				'has_orders' => 0 < $customer_orders->total,
2229
			)
2230
		);
2231
	}
2232
}
2233
2234
if ( ! function_exists( 'woocommerce_account_view_order' ) ) {
2235
2236
	/**
2237
	 * My Account > View order template.
2238
	 *
2239
	 * @param int $order_id Order ID.
2240
	 */
2241
	function woocommerce_account_view_order( $order_id ) {
2242
		WC_Shortcode_My_Account::view_order( absint( $order_id ) );
2243
	}
2244
}
2245
2246
if ( ! function_exists( 'woocommerce_account_downloads' ) ) {
2247
2248
	/**
2249
	 * My Account > Downloads template.
2250
	 */
2251
	function woocommerce_account_downloads() {
2252
		wc_get_template( 'myaccount/downloads.php' );
2253
	}
2254
}
2255
2256
if ( ! function_exists( 'woocommerce_account_edit_address' ) ) {
2257
2258
	/**
2259
	 * My Account > Edit address template.
2260
	 *
2261
	 * @param string $type Address type.
2262
	 */
2263
	function woocommerce_account_edit_address( $type ) {
2264
		$type = wc_edit_address_i18n( sanitize_title( $type ), true );
2265
2266
		WC_Shortcode_My_Account::edit_address( $type );
2267
	}
2268
}
2269
2270
if ( ! function_exists( 'woocommerce_account_payment_methods' ) ) {
2271
2272
	/**
2273
	 * My Account > Downloads template.
2274
	 */
2275
	function woocommerce_account_payment_methods() {
2276
		wc_get_template( 'myaccount/payment-methods.php' );
2277
	}
2278
}
2279
2280
if ( ! function_exists( 'woocommerce_account_add_payment_method' ) ) {
2281
2282
	/**
2283
	 * My Account > Add payment method template.
2284
	 */
2285
	function woocommerce_account_add_payment_method() {
2286
		WC_Shortcode_My_Account::add_payment_method();
2287
	}
2288
}
2289
2290
if ( ! function_exists( 'woocommerce_account_edit_account' ) ) {
2291
2292
	/**
2293
	 * My Account > Edit account template.
2294
	 */
2295
	function woocommerce_account_edit_account() {
2296
		WC_Shortcode_My_Account::edit_account();
2297
	}
2298
}
2299