Completed
Pull Request — master (#11541)
by Mike
08:49
created

wc-template-functions.php ➔ wc_query_string_form_fields()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 12
c 1
b 0
f 0
nc 12
nop 3
dl 0
loc 18
rs 8.8571
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()->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
 * @param array $values name value pairs
343
 * @param array $exclude keys to exclude
344
 * @since 2.7.0
345
 */
346
function wc_query_string_form_fields( $values = null, $exclude = array(), $currentkey = '' ) {
347
	if ( is_null( $values ) ) {
348
		$values = $_GET;
349
	}
350
	foreach ( $values as $key => $value ) {
351
		if ( in_array( $key, $exclude ) ) {
352
			continue;
353
		}
354
		if ( $currentkey ) {
355
			$key = $currentkey . '[' . $key . ']';
356
		}
357
		if ( is_array( $value ) ) {
358
			wc_query_string_form_fields( $value, $exclude, $key );
359
		} else {
360
			echo '<input type="hidden" name="' . esc_attr( $key ) . '" value="' . esc_attr( $value ) . '" />';
361
		}
362
	}
363
}
364
365
/** Template pages ********************************************************/
366
367
if ( ! function_exists( 'woocommerce_content' ) ) {
368
369
	/**
370
	 * Output WooCommerce content.
371
	 *
372
	 * This function is only used in the optional 'woocommerce.php' template.
373
	 * which people can add to their themes to add basic woocommerce support.
374
	 * without hooks or modifying core templates.
375
	 *
376
	 */
377
	function woocommerce_content() {
378
379
		if ( is_singular( 'product' ) ) {
380
381
			while ( have_posts() ) : the_post();
382
383
				wc_get_template_part( 'content', 'single-product' );
384
385
			endwhile;
386
387
		} else { ?>
388
389
			<?php if ( apply_filters( 'woocommerce_show_page_title', true ) ) : ?>
390
391
				<h1 class="page-title"><?php woocommerce_page_title(); ?></h1>
392
393
			<?php endif; ?>
394
395
			<?php do_action( 'woocommerce_archive_description' ); ?>
396
397 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...
398
399
				<?php do_action('woocommerce_before_shop_loop'); ?>
400
401
				<?php woocommerce_product_loop_start(); ?>
402
403
					<?php woocommerce_product_subcategories(); ?>
404
405
					<?php while ( have_posts() ) : the_post(); ?>
406
407
						<?php wc_get_template_part( 'content', 'product' ); ?>
408
409
					<?php endwhile; // end of the loop. ?>
410
411
				<?php woocommerce_product_loop_end(); ?>
412
413
				<?php do_action('woocommerce_after_shop_loop'); ?>
414
415
			<?php elseif ( ! woocommerce_product_subcategories( array( 'before' => woocommerce_product_loop_start( false ), 'after' => woocommerce_product_loop_end( false ) ) ) ) : ?>
416
417
				<?php wc_get_template( 'loop/no-products-found.php' ); ?>
418
419
			<?php endif;
420
421
		}
422
	}
423
}
424
425
/** Global ****************************************************************/
426
427
if ( ! function_exists( 'woocommerce_output_content_wrapper' ) ) {
428
429
	/**
430
	 * Output the start of the page wrapper.
431
	 *
432
	 */
433
	function woocommerce_output_content_wrapper() {
434
		wc_get_template( 'global/wrapper-start.php' );
435
	}
436
}
437
if ( ! function_exists( 'woocommerce_output_content_wrapper_end' ) ) {
438
439
	/**
440
	 * Output the end of the page wrapper.
441
	 *
442
	 */
443
	function woocommerce_output_content_wrapper_end() {
444
		wc_get_template( 'global/wrapper-end.php' );
445
	}
446
}
447
448
if ( ! function_exists( 'woocommerce_get_sidebar' ) ) {
449
450
	/**
451
	 * Get the shop sidebar template.
452
	 *
453
	 */
454
	function woocommerce_get_sidebar() {
455
		wc_get_template( 'global/sidebar.php' );
456
	}
457
}
458
459
if ( ! function_exists( 'woocommerce_demo_store' ) ) {
460
461
	/**
462
	 * Adds a demo store banner to the site if enabled.
463
	 *
464
	 */
465
	function woocommerce_demo_store() {
466
		if ( ! is_store_notice_showing() ) {
467
			return;
468
		}
469
470
		$notice = get_option( 'woocommerce_demo_store_notice' );
471
472
		if ( empty( $notice ) ) {
473
			$notice = __( 'This is a demo store for testing purposes &mdash; no orders shall be fulfilled.', 'woocommerce' );
474
		}
475
476
		echo apply_filters( 'woocommerce_demo_store', '<p class="demo_store">' . wp_kses_post( $notice ) . '</p>', $notice );
477
	}
478
}
479
480
/** Loop ******************************************************************/
481
482
if ( ! function_exists( 'woocommerce_page_title' ) ) {
483
484
	/**
485
	 * woocommerce_page_title function.
486
	 *
487
	 * @param  bool $echo
488
	 * @return string
489
	 */
490
	function woocommerce_page_title( $echo = true ) {
491
492
		if ( is_search() ) {
493
			$page_title = sprintf( __( 'Search Results: &ldquo;%s&rdquo;', 'woocommerce' ), get_search_query() );
494
495
			if ( get_query_var( 'paged' ) )
496
				$page_title .= sprintf( __( '&nbsp;&ndash; Page %s', 'woocommerce' ), get_query_var( 'paged' ) );
497
498
		} elseif ( is_tax() ) {
499
500
			$page_title = single_term_title( "", false );
501
502
		} else {
503
504
			$shop_page_id = wc_get_page_id( 'shop' );
505
			$page_title   = get_the_title( $shop_page_id );
506
507
		}
508
509
		$page_title = apply_filters( 'woocommerce_page_title', $page_title );
510
511
		if ( $echo )
512
			echo $page_title;
513
		else
514
			return $page_title;
515
	}
516
}
517
518
if ( ! function_exists( 'woocommerce_product_loop_start' ) ) {
519
520
	/**
521
	 * Output the start of a product loop. By default this is a UL.
522
	 *
523
	 * @param bool $echo
524
	 * @return string
525
	 */
526
	function woocommerce_product_loop_start( $echo = true ) {
527
		ob_start();
528
		$GLOBALS['woocommerce_loop']['loop'] = 0;
529
		wc_get_template( 'loop/loop-start.php' );
530
		if ( $echo )
531
			echo ob_get_clean();
532
		else
533
			return ob_get_clean();
534
	}
535
}
536
if ( ! function_exists( 'woocommerce_product_loop_end' ) ) {
537
538
	/**
539
	 * Output the end of a product loop. By default this is a UL.
540
	 *
541
	 * @param bool $echo
542
	 * @return string
543
	 */
544
	function woocommerce_product_loop_end( $echo = true ) {
545
		ob_start();
546
547
		wc_get_template( 'loop/loop-end.php' );
548
549
		if ( $echo )
550
			echo ob_get_clean();
551
		else
552
			return ob_get_clean();
553
	}
554
}
555
if (  ! function_exists( 'woocommerce_template_loop_product_title' ) ) {
556
557
	/**
558
	 * Show the product title in the product loop. By default this is an H3.
559
	 */
560
	function woocommerce_template_loop_product_title() {
561
		echo '<h3>' . get_the_title() . '</h3>';
562
	}
563
}
564
if (  ! function_exists( 'woocommerce_template_loop_category_title' ) ) {
565
566
	/**
567
	 * Show the subcategory title in the product loop.
568
	 */
569
	function woocommerce_template_loop_category_title( $category ) {
570
		?>
571
		<h3>
572
			<?php
573
				echo $category->name;
574
575
				if ( $category->count > 0 )
576
					echo apply_filters( 'woocommerce_subcategory_count_html', ' <mark class="count">(' . $category->count . ')</mark>', $category );
577
			?>
578
		</h3>
579
		<?php
580
	}
581
}
582
/**
583
 * Insert the opening anchor tag for products in the loop.
584
 */
585
function woocommerce_template_loop_product_link_open() {
586
	echo '<a href="' . get_the_permalink() . '" class="woocommerce-LoopProduct-link">';
587
}
588
/**
589
 * Insert the opening anchor tag for products in the loop.
590
 */
591
function woocommerce_template_loop_product_link_close() {
592
	echo '</a>';
593
}
594
/**
595
 * Insert the opening anchor tag for categories in the loop.
596
 */
597
function woocommerce_template_loop_category_link_open( $category ) {
598
	echo '<a href="' . get_term_link( $category, 'product_cat' ) . '">';
599
}
600
/**
601
 * Insert the opening anchor tag for categories in the loop.
602
 */
603
function woocommerce_template_loop_category_link_close() {
604
	echo '</a>';
605
}
606
if ( ! function_exists( 'woocommerce_taxonomy_archive_description' ) ) {
607
608
	/**
609
	 * Show an archive description on taxonomy archives.
610
	 *
611
	 * @subpackage	Archives
612
	 */
613
	function woocommerce_taxonomy_archive_description() {
614
		if ( is_tax( array( 'product_cat', 'product_tag' ) ) && 0 === absint( get_query_var( 'paged' ) ) ) {
615
			$description = wc_format_content( term_description() );
616
			if ( $description ) {
617
				echo '<div class="term-description">' . $description . '</div>';
618
			}
619
		}
620
	}
621
}
622
if ( ! function_exists( 'woocommerce_product_archive_description' ) ) {
623
624
	/**
625
	 * Show a shop page description on product archives.
626
	 *
627
	 * @subpackage	Archives
628
	 */
629
	function woocommerce_product_archive_description() {
630
		// Don't display the description on search results page
631
		if ( is_search() ) {
632
			return;
633
		}
634
635
		if ( is_post_type_archive( 'product' ) && 0 === absint( get_query_var( 'paged' ) ) ) {
636
			$shop_page   = get_post( wc_get_page_id( 'shop' ) );
637
			if ( $shop_page ) {
638
				$description = wc_format_content( $shop_page->post_content );
639
				if ( $description ) {
640
					echo '<div class="page-description">' . $description . '</div>';
641
				}
642
			}
643
		}
644
	}
645
}
646
647
if ( ! function_exists( 'woocommerce_template_loop_add_to_cart' ) ) {
648
649
	/**
650
	 * Get the add to cart template for the loop.
651
	 *
652
	 * @subpackage	Loop
653
	 */
654
	function woocommerce_template_loop_add_to_cart( $args = array() ) {
655
		global $product;
656
657
		if ( $product ) {
658
			$defaults = array(
659
				'quantity' => 1,
660
				'class'    => implode( ' ', array_filter( array(
661
						'button',
662
						'product_type_' . $product->product_type,
663
						$product->is_purchasable() && $product->is_in_stock() ? 'add_to_cart_button' : '',
664
						$product->supports( 'ajax_add_to_cart' ) ? 'ajax_add_to_cart' : ''
665
				) ) )
666
			);
667
668
			$args = apply_filters( 'woocommerce_loop_add_to_cart_args', wp_parse_args( $args, $defaults ), $product );
669
670
			wc_get_template( 'loop/add-to-cart.php', $args );
671
		}
672
	}
673
}
674
if ( ! function_exists( 'woocommerce_template_loop_product_thumbnail' ) ) {
675
676
	/**
677
	 * Get the product thumbnail for the loop.
678
	 *
679
	 * @subpackage	Loop
680
	 */
681
	function woocommerce_template_loop_product_thumbnail() {
682
		echo woocommerce_get_product_thumbnail();
683
	}
684
}
685
if ( ! function_exists( 'woocommerce_template_loop_price' ) ) {
686
687
	/**
688
	 * Get the product price for the loop.
689
	 *
690
	 * @subpackage	Loop
691
	 */
692
	function woocommerce_template_loop_price() {
693
		wc_get_template( 'loop/price.php' );
694
	}
695
}
696
if ( ! function_exists( 'woocommerce_template_loop_rating' ) ) {
697
698
	/**
699
	 * Display the average rating in the loop.
700
	 *
701
	 * @subpackage	Loop
702
	 */
703
	function woocommerce_template_loop_rating() {
704
		wc_get_template( 'loop/rating.php' );
705
	}
706
}
707
if ( ! function_exists( 'woocommerce_show_product_loop_sale_flash' ) ) {
708
709
	/**
710
	 * Get the sale flash for the loop.
711
	 *
712
	 * @subpackage	Loop
713
	 */
714
	function woocommerce_show_product_loop_sale_flash() {
715
		wc_get_template( 'loop/sale-flash.php' );
716
	}
717
}
718
719
if ( ! function_exists( 'woocommerce_get_product_schema' ) ) {
720
721
	/**
722
	 * Get a products Schema.
723
	 * @return string
724
	 */
725
	function woocommerce_get_product_schema() {
726
		global $product;
727
728
		$schema = "Product";
729
730
		// Downloadable product schema handling
731
		if ( $product->is_downloadable() ) {
732
			switch ( $product->download_type ) {
733
				case 'application' :
734
					$schema = "SoftwareApplication";
735
				break;
736
				case 'music' :
737
					$schema = "MusicAlbum";
738
				break;
739
				default :
740
					$schema = "Product";
741
				break;
742
			}
743
		}
744
745
		return 'http://schema.org/' . $schema;
746
	}
747
}
748
749
if ( ! function_exists( 'woocommerce_get_product_thumbnail' ) ) {
750
751
	/**
752
	 * Get the product thumbnail, or the placeholder if not set.
753
	 *
754
	 * @subpackage	Loop
755
	 * @param string $size (default: 'shop_catalog')
756
	 * @param int $deprecated1 Deprecated since WooCommerce 2.0 (default: 0)
757
	 * @param int $deprecated2 Deprecated since WooCommerce 2.0 (default: 0)
758
	 * @return string
759
	 */
760
	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...
761
		global $post;
762
		$image_size = apply_filters( 'single_product_archive_thumbnail_size', $size );
763
764
		if ( has_post_thumbnail() ) {
765
			$props = wc_get_product_attachment_props( get_post_thumbnail_id(), $post );
766
			return get_the_post_thumbnail( $post->ID, $image_size, array(
767
				'title'	 => $props['title'],
768
				'alt'    => $props['alt'],
769
			) );
770
		} elseif ( wc_placeholder_img_src() ) {
771
			return wc_placeholder_img( $image_size );
772
		}
773
	}
774
}
775
776
if ( ! function_exists( 'woocommerce_result_count' ) ) {
777
778
	/**
779
	 * Output the result count text (Showing x - x of x results).
780
	 *
781
	 * @subpackage	Loop
782
	 */
783
	function woocommerce_result_count() {
784
		wc_get_template( 'loop/result-count.php' );
785
	}
786
}
787
788
if ( ! function_exists( 'woocommerce_catalog_ordering' ) ) {
789
790
	/**
791
	 * Output the product sorting options.
792
	 *
793
	 * @subpackage	Loop
794
	 */
795
	function woocommerce_catalog_ordering() {
796
		global $wp_query;
797
798
		if ( 1 === $wp_query->found_posts || ! woocommerce_products_will_display() ) {
799
			return;
800
		}
801
802
		$orderby                 = isset( $_GET['orderby'] ) ? wc_clean( $_GET['orderby'] ) : apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
803
		$show_default_orderby    = 'menu_order' === apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
804
		$catalog_orderby_options = apply_filters( 'woocommerce_catalog_orderby', array(
805
			'menu_order' => __( 'Default sorting', 'woocommerce' ),
806
			'popularity' => __( 'Sort by popularity', 'woocommerce' ),
807
			'rating'     => __( 'Sort by average rating', 'woocommerce' ),
808
			'date'       => __( 'Sort by newness', 'woocommerce' ),
809
			'price'      => __( 'Sort by price: low to high', 'woocommerce' ),
810
			'price-desc' => __( 'Sort by price: high to low', 'woocommerce' )
811
		) );
812
813
		if ( ! $show_default_orderby ) {
814
			unset( $catalog_orderby_options['menu_order'] );
815
		}
816
817
		if ( 'no' === get_option( 'woocommerce_enable_review_rating' ) ) {
818
			unset( $catalog_orderby_options['rating'] );
819
		}
820
821
		wc_get_template( 'loop/orderby.php', array( 'catalog_orderby_options' => $catalog_orderby_options, 'orderby' => $orderby, 'show_default_orderby' => $show_default_orderby ) );
822
	}
823
}
824
825
if ( ! function_exists( 'woocommerce_pagination' ) ) {
826
827
	/**
828
	 * Output the pagination.
829
	 *
830
	 * @subpackage	Loop
831
	 */
832
	function woocommerce_pagination() {
833
		wc_get_template( 'loop/pagination.php' );
834
	}
835
}
836
837
/** Single Product ********************************************************/
838
839
if ( ! function_exists( 'woocommerce_show_product_images' ) ) {
840
841
	/**
842
	 * Output the product image before the single product summary.
843
	 *
844
	 * @subpackage	Product
845
	 */
846
	function woocommerce_show_product_images() {
847
		wc_get_template( 'single-product/product-image.php' );
848
	}
849
}
850
if ( ! function_exists( 'woocommerce_show_product_thumbnails' ) ) {
851
852
	/**
853
	 * Output the product thumbnails.
854
	 *
855
	 * @subpackage	Product
856
	 */
857
	function woocommerce_show_product_thumbnails() {
858
		wc_get_template( 'single-product/product-thumbnails.php' );
859
	}
860
}
861
if ( ! function_exists( 'woocommerce_output_product_data_tabs' ) ) {
862
863
	/**
864
	 * Output the product tabs.
865
	 *
866
	 * @subpackage	Product/Tabs
867
	 */
868
	function woocommerce_output_product_data_tabs() {
869
		wc_get_template( 'single-product/tabs/tabs.php' );
870
	}
871
}
872
if ( ! function_exists( 'woocommerce_template_single_title' ) ) {
873
874
	/**
875
	 * Output the product title.
876
	 *
877
	 * @subpackage	Product
878
	 */
879
	function woocommerce_template_single_title() {
880
		wc_get_template( 'single-product/title.php' );
881
	}
882
}
883
if ( ! function_exists( 'woocommerce_template_single_rating' ) ) {
884
885
	/**
886
	 * Output the product rating.
887
	 *
888
	 * @subpackage	Product
889
	 */
890
	function woocommerce_template_single_rating() {
891
		wc_get_template( 'single-product/rating.php' );
892
	}
893
}
894
if ( ! function_exists( 'woocommerce_template_single_price' ) ) {
895
896
	/**
897
	 * Output the product price.
898
	 *
899
	 * @subpackage	Product
900
	 */
901
	function woocommerce_template_single_price() {
902
		wc_get_template( 'single-product/price.php' );
903
	}
904
}
905
if ( ! function_exists( 'woocommerce_template_single_excerpt' ) ) {
906
907
	/**
908
	 * Output the product short description (excerpt).
909
	 *
910
	 * @subpackage	Product
911
	 */
912
	function woocommerce_template_single_excerpt() {
913
		wc_get_template( 'single-product/short-description.php' );
914
	}
915
}
916
if ( ! function_exists( 'woocommerce_template_single_meta' ) ) {
917
918
	/**
919
	 * Output the product meta.
920
	 *
921
	 * @subpackage	Product
922
	 */
923
	function woocommerce_template_single_meta() {
924
		wc_get_template( 'single-product/meta.php' );
925
	}
926
}
927
if ( ! function_exists( 'woocommerce_template_single_sharing' ) ) {
928
929
	/**
930
	 * Output the product sharing.
931
	 *
932
	 * @subpackage	Product
933
	 */
934
	function woocommerce_template_single_sharing() {
935
		wc_get_template( 'single-product/share.php' );
936
	}
937
}
938
if ( ! function_exists( 'woocommerce_show_product_sale_flash' ) ) {
939
940
	/**
941
	 * Output the product sale flash.
942
	 *
943
	 * @subpackage	Product
944
	 */
945
	function woocommerce_show_product_sale_flash() {
946
		wc_get_template( 'single-product/sale-flash.php' );
947
	}
948
}
949
950
if ( ! function_exists( 'woocommerce_template_single_add_to_cart' ) ) {
951
952
	/**
953
	 * Trigger the single product add to cart action.
954
	 *
955
	 * @subpackage	Product
956
	 */
957
	function woocommerce_template_single_add_to_cart() {
958
		global $product;
959
		do_action( 'woocommerce_' . $product->product_type . '_add_to_cart' );
960
	}
961
}
962
if ( ! function_exists( 'woocommerce_simple_add_to_cart' ) ) {
963
964
	/**
965
	 * Output the simple product add to cart area.
966
	 *
967
	 * @subpackage	Product
968
	 */
969
	function woocommerce_simple_add_to_cart() {
970
		wc_get_template( 'single-product/add-to-cart/simple.php' );
971
	}
972
}
973
if ( ! function_exists( 'woocommerce_grouped_add_to_cart' ) ) {
974
975
	/**
976
	 * Output the grouped product add to cart area.
977
	 *
978
	 * @subpackage	Product
979
	 */
980
	function woocommerce_grouped_add_to_cart() {
981
		global $product;
982
983
		wc_get_template( 'single-product/add-to-cart/grouped.php', array(
984
			'grouped_product'    => $product,
985
			'grouped_products'   => $product->get_children(),
986
			'quantites_required' => false
987
		) );
988
	}
989
}
990
if ( ! function_exists( 'woocommerce_variable_add_to_cart' ) ) {
991
992
	/**
993
	 * Output the variable product add to cart area.
994
	 *
995
	 * @subpackage	Product
996
	 */
997
	function woocommerce_variable_add_to_cart() {
998
		global $product;
999
1000
		// Enqueue variation scripts
1001
		wp_enqueue_script( 'wc-add-to-cart-variation' );
1002
1003
		// Get Available variations?
1004
		$get_variations = sizeof( $product->get_children() ) <= apply_filters( 'woocommerce_ajax_variation_threshold', 30, $product );
1005
1006
		// Load the template
1007
		wc_get_template( 'single-product/add-to-cart/variable.php', array(
1008
			'available_variations' => $get_variations ? $product->get_available_variations() : false,
1009
			'attributes'           => $product->get_variation_attributes(),
1010
			'selected_attributes'  => $product->get_variation_default_attributes()
1011
		) );
1012
	}
1013
}
1014
if ( ! function_exists( 'woocommerce_external_add_to_cart' ) ) {
1015
1016
	/**
1017
	 * Output the external product add to cart area.
1018
	 *
1019
	 * @subpackage	Product
1020
	 */
1021
	function woocommerce_external_add_to_cart() {
1022
		global $product;
1023
1024
		if ( ! $product->add_to_cart_url() ) {
1025
			return;
1026
		}
1027
1028
		wc_get_template( 'single-product/add-to-cart/external.php', array(
1029
			'product_url' => $product->add_to_cart_url(),
1030
			'button_text' => $product->single_add_to_cart_text()
1031
		) );
1032
	}
1033
}
1034
1035
if ( ! function_exists( 'woocommerce_quantity_input' ) ) {
1036
1037
	/**
1038
	 * Output the quantity input for add to cart forms.
1039
	 *
1040
	 * @param  array $args Args for the input
1041
	 * @param  WC_Product|null $product
1042
	 * @param  boolean $echo Whether to return or echo|string
1043
	 */
1044
	function woocommerce_quantity_input( $args = array(), $product = null, $echo = true ) {
1045
		if ( is_null( $product ) ) {
1046
			$product = $GLOBALS['product'];
1047
		}
1048
1049
		$defaults = array(
1050
			'input_name'  => 'quantity',
1051
			'input_value' => '1',
1052
			'max_value'   => apply_filters( 'woocommerce_quantity_input_max', '', $product ),
1053
			'min_value'   => apply_filters( 'woocommerce_quantity_input_min', '', $product ),
1054
			'step'        => apply_filters( 'woocommerce_quantity_input_step', '1', $product ),
1055
			'pattern'     => apply_filters( 'woocommerce_quantity_input_pattern', has_filter( 'woocommerce_stock_amount', 'intval' ) ? '[0-9]*' : '' ),
1056
			'inputmode'   => apply_filters( 'woocommerce_quantity_input_inputmode', has_filter( 'woocommerce_stock_amount', 'intval' ) ? 'numeric' : '' ),
1057
		);
1058
1059
		$args = apply_filters( 'woocommerce_quantity_input_args', wp_parse_args( $args, $defaults ), $product );
1060
1061
		// Set min and max value to empty string if not set.
1062
		$args['min_value'] = isset( $args['min_value'] ) ? $args['min_value'] : '';
1063
		$args['max_value'] = isset( $args['max_value'] ) ? $args['max_value'] : '';
1064
1065
		// Apply sanity to min/max args - min cannot be lower than 0
1066
		if ( '' !== $args['min_value'] && is_numeric( $args['min_value'] ) && $args['min_value'] < 0 ) {
1067
			$args['min_value'] = 0; // Cannot be lower than 0
1068
		}
1069
1070
		// Max cannot be lower than 0 or min
1071
		if ( '' !== $args['max_value'] && is_numeric( $args['max_value'] ) ) {
1072
			$args['max_value'] = $args['max_value'] < 0 ? 0 : $args['max_value'];
1073
			$args['max_value'] = $args['max_value'] < $args['min_value'] ? $args['min_value'] : $args['max_value'];
1074
		}
1075
1076
		ob_start();
1077
1078
		wc_get_template( 'global/quantity-input.php', $args );
1079
1080
		if ( $echo ) {
1081
			echo ob_get_clean();
1082
		} else {
1083
			return ob_get_clean();
1084
		}
1085
	}
1086
}
1087
1088
if ( ! function_exists( 'woocommerce_product_description_tab' ) ) {
1089
1090
	/**
1091
	 * Output the description tab content.
1092
	 *
1093
	 * @subpackage	Product/Tabs
1094
	 */
1095
	function woocommerce_product_description_tab() {
1096
		wc_get_template( 'single-product/tabs/description.php' );
1097
	}
1098
}
1099
if ( ! function_exists( 'woocommerce_product_additional_information_tab' ) ) {
1100
1101
	/**
1102
	 * Output the attributes tab content.
1103
	 *
1104
	 * @subpackage	Product/Tabs
1105
	 */
1106
	function woocommerce_product_additional_information_tab() {
1107
		wc_get_template( 'single-product/tabs/additional-information.php' );
1108
	}
1109
}
1110
if ( ! function_exists( 'woocommerce_product_reviews_tab' ) ) {
1111
1112
	/**
1113
	 * Output the reviews tab content.
1114
	 * @deprecated  2.4.0 Unused
1115
	 * @subpackage	Product/Tabs
1116
	 */
1117
	function woocommerce_product_reviews_tab() {
1118
		_deprecated_function( 'woocommerce_product_reviews_tab', '2.4', '' );
1119
	}
1120
}
1121
1122
if ( ! function_exists( 'woocommerce_default_product_tabs' ) ) {
1123
1124
	/**
1125
	 * Add default product tabs to product pages.
1126
	 *
1127
	 * @param array $tabs
1128
	 * @return array
1129
	 */
1130
	function woocommerce_default_product_tabs( $tabs = array() ) {
1131
		global $product, $post;
1132
1133
		// Description tab - shows product content
1134
		if ( $post->post_content ) {
1135
			$tabs['description'] = array(
1136
				'title'    => __( 'Description', 'woocommerce' ),
1137
				'priority' => 10,
1138
				'callback' => 'woocommerce_product_description_tab'
1139
			);
1140
		}
1141
1142
		// Additional information tab - shows attributes
1143
		if ( $product && ( $product->has_attributes() || ( $product->enable_dimensions_display() && ( $product->has_dimensions() || $product->has_weight() ) ) ) ) {
1144
			$tabs['additional_information'] = array(
1145
				'title'    => __( 'Additional Information', 'woocommerce' ),
1146
				'priority' => 20,
1147
				'callback' => 'woocommerce_product_additional_information_tab'
1148
			);
1149
		}
1150
1151
		// Reviews tab - shows comments
1152
		if ( comments_open() ) {
1153
			$tabs['reviews'] = array(
1154
				'title'    => sprintf( __( 'Reviews (%d)', 'woocommerce' ), $product->get_review_count() ),
1155
				'priority' => 30,
1156
				'callback' => 'comments_template'
1157
			);
1158
		}
1159
1160
		return $tabs;
1161
	}
1162
}
1163
1164
if ( ! function_exists( 'woocommerce_sort_product_tabs' ) ) {
1165
1166
	/**
1167
	 * Sort tabs by priority.
1168
	 *
1169
	 * @param array $tabs
1170
	 * @return array
1171
	 */
1172
	function woocommerce_sort_product_tabs( $tabs = array() ) {
1173
1174
		// Make sure the $tabs parameter is an array
1175
		if ( ! is_array( $tabs ) ) {
1176
			trigger_error( "Function woocommerce_sort_product_tabs() expects an array as the first parameter. Defaulting to empty array." );
1177
			$tabs = array( );
1178
		}
1179
1180
		// Re-order tabs by priority
1181
		if ( ! function_exists( '_sort_priority_callback' ) ) {
1182
			function _sort_priority_callback( $a, $b ) {
1183
				if ( $a['priority'] === $b['priority'] )
1184
					return 0;
1185
				return ( $a['priority'] < $b['priority'] ) ? -1 : 1;
1186
			}
1187
		}
1188
1189
		uasort( $tabs, '_sort_priority_callback' );
1190
1191
		return $tabs;
1192
	}
1193
}
1194
1195
if ( ! function_exists( 'woocommerce_comments' ) ) {
1196
1197
	/**
1198
	 * Output the Review comments template.
1199
	 *
1200
	 * @subpackage	Product
1201
	 * @param WP_Comment $comment
1202
	 * @param array $args
1203
	 * @param int $depth
1204
	 */
1205
	function woocommerce_comments( $comment, $args, $depth ) {
1206
		$GLOBALS['comment'] = $comment;
1207
		wc_get_template( 'single-product/review.php', array( 'comment' => $comment, 'args' => $args, 'depth' => $depth ) );
1208
	}
1209
}
1210
1211
if ( ! function_exists( 'woocommerce_review_display_gravatar' ) ) {
1212
	/**
1213
	 * Display the review authors gravatar
1214
	 *
1215
	 * @param array $comment WP_Comment.
1216
	 * @return void
1217
	 */
1218
	function woocommerce_review_display_gravatar( $comment ) {
1219
		echo get_avatar( $comment, apply_filters( 'woocommerce_review_gravatar_size', '60' ), '' );
1220
	}
1221
}
1222
1223
if ( ! function_exists( 'woocommerce_review_display_rating' ) ) {
1224
	/**
1225
	 * Display the reviewers star rating
1226
	 *
1227
	 * @return void
1228
	 */
1229
	function woocommerce_review_display_rating() {
1230
		wc_get_template( 'single-product/review-rating.php' );
1231
	}
1232
}
1233
1234
if ( ! function_exists( 'woocommerce_review_display_meta' ) ) {
1235
	/**
1236
	 * Display the review authors meta (name, verified owner, review date)
1237
	 *
1238
	 * @return void
1239
	 */
1240
	function woocommerce_review_display_meta() {
1241
		wc_get_template( 'single-product/review-meta.php' );
1242
	}
1243
}
1244
1245
if ( ! function_exists( 'woocommerce_review_display_comment_text' ) ) {
1246
1247
	/**
1248
	 * Display the review content.
1249
	 */
1250
	function woocommerce_review_display_comment_text() {
1251
		echo '<div itemprop="description" class="description">';
1252
		comment_text();
1253
		echo '</div>';
1254
	}
1255
}
1256
1257
if ( ! function_exists( 'woocommerce_output_related_products' ) ) {
1258
1259
	/**
1260
	 * Output the related products.
1261
	 *
1262
	 * @subpackage	Product
1263
	 */
1264
	function woocommerce_output_related_products() {
1265
1266
		$args = array(
1267
			'posts_per_page' 	=> 4,
1268
			'columns' 			=> 4,
1269
			'orderby' 			=> 'rand'
1270
		);
1271
1272
		woocommerce_related_products( apply_filters( 'woocommerce_output_related_products_args', $args ) );
1273
	}
1274
}
1275
1276
if ( ! function_exists( 'woocommerce_related_products' ) ) {
1277
1278
	/**
1279
	 * Output the related products.
1280
	 *
1281
	 * @param array Provided arguments
1282
	 * @param bool Columns argument for backwards compat
1283
	 * @param bool Order by argument for backwards compat
1284
	 */
1285
	function woocommerce_related_products( $args = array(), $columns = false, $orderby = false ) {
1286
		if ( ! is_array( $args ) ) {
1287
			_deprecated_argument( __FUNCTION__, '2.1', __( 'Use $args argument as an array instead. Deprecated argument will be removed in WC 2.2.', 'woocommerce' ) );
1288
1289
			$argsvalue = $args;
1290
1291
			$args = array(
1292
				'posts_per_page' => $argsvalue,
1293
				'columns'        => $columns,
1294
				'orderby'        => $orderby,
1295
			);
1296
		}
1297
1298
		$defaults = array(
1299
			'posts_per_page' => 2,
1300
			'columns'        => 2,
1301
			'orderby'        => 'rand'
1302
		);
1303
1304
		$args = wp_parse_args( $args, $defaults );
1305
1306
		wc_get_template( 'single-product/related.php', $args );
1307
	}
1308
}
1309
1310
if ( ! function_exists( 'woocommerce_upsell_display' ) ) {
1311
1312
	/**
1313
	 * Output product up sells.
1314
	 *
1315
	 * @param int $posts_per_page (default: -1)
1316
	 * @param int $columns (default: 4)
1317
	 * @param string $orderby (default: 'rand')
1318
	 */
1319
	function woocommerce_upsell_display( $posts_per_page = '-1', $columns = 4, $orderby = 'rand' ) {
1320
		$args = apply_filters( 'woocommerce_upsell_display_args', array(
1321
			'posts_per_page'	=> $posts_per_page,
1322
			'orderby'			=> apply_filters( 'woocommerce_upsells_orderby', $orderby ),
1323
			'columns'			=> $columns
1324
		) );
1325
1326
		wc_get_template( 'single-product/up-sells.php', $args );
1327
	}
1328
}
1329
1330
/** Cart ******************************************************************/
1331
1332
if ( ! function_exists( 'woocommerce_shipping_calculator' ) ) {
1333
1334
	/**
1335
	 * Output the cart shipping calculator.
1336
	 *
1337
	 * @subpackage	Cart
1338
	 */
1339
	function woocommerce_shipping_calculator() {
1340
		wc_get_template( 'cart/shipping-calculator.php' );
1341
	}
1342
}
1343
1344
if ( ! function_exists( 'woocommerce_cart_totals' ) ) {
1345
1346
	/**
1347
	 * Output the cart totals.
1348
	 *
1349
	 * @subpackage	Cart
1350
	 */
1351
	function woocommerce_cart_totals() {
1352
		if ( is_checkout() ) {
1353
			return;
1354
		}
1355
		wc_get_template( 'cart/cart-totals.php' );
1356
	}
1357
}
1358
1359
if ( ! function_exists( 'woocommerce_cross_sell_display' ) ) {
1360
1361
	/**
1362
	 * Output the cart cross-sells.
1363
	 *
1364
	 * @param  int $posts_per_page (default: 2)
1365
	 * @param  int $columns (default: 2)
1366
	 * @param  string $orderby (default: 'rand')
1367
	 */
1368
	function woocommerce_cross_sell_display( $posts_per_page = 2, $columns = 2, $orderby = 'rand' ) {
1369
		if ( is_checkout() ) {
1370
			return;
1371
		}
1372
		wc_get_template( 'cart/cross-sells.php', array(
1373
			'posts_per_page' => $posts_per_page,
1374
			'orderby'        => $orderby,
1375
			'columns'        => $columns
1376
		) );
1377
	}
1378
}
1379
1380
if ( ! function_exists( 'woocommerce_button_proceed_to_checkout' ) ) {
1381
1382
	/**
1383
	 * Output the proceed to checkout button.
1384
	 *
1385
	 * @subpackage	Cart
1386
	 */
1387
	function woocommerce_button_proceed_to_checkout() {
1388
		wc_get_template( 'cart/proceed-to-checkout-button.php' );
1389
	}
1390
}
1391
1392
1393
1394
/** Mini-Cart *************************************************************/
1395
1396
if ( ! function_exists( 'woocommerce_mini_cart' ) ) {
1397
1398
	/**
1399
	 * Output the Mini-cart - used by cart widget.
1400
	 *
1401
	 * @param array $args
1402
	 */
1403
	function woocommerce_mini_cart( $args = array() ) {
1404
1405
		$defaults = array(
1406
			'list_class' => ''
1407
		);
1408
1409
		$args = wp_parse_args( $args, $defaults );
1410
1411
		wc_get_template( 'cart/mini-cart.php', $args );
1412
	}
1413
}
1414
1415
/** Login *****************************************************************/
1416
1417
if ( ! function_exists( 'woocommerce_login_form' ) ) {
1418
1419
	/**
1420
	 * Output the WooCommerce Login Form.
1421
	 *
1422
	 * @subpackage	Forms
1423
	 * @param array $args
1424
	 */
1425
	function woocommerce_login_form( $args = array() ) {
1426
1427
		$defaults = array(
1428
			'message'  => '',
1429
			'redirect' => '',
1430
			'hidden'   => false
1431
		);
1432
1433
		$args = wp_parse_args( $args, $defaults  );
1434
1435
		wc_get_template( 'global/form-login.php', $args );
1436
	}
1437
}
1438
1439
if ( ! function_exists( 'woocommerce_checkout_login_form' ) ) {
1440
1441
	/**
1442
	 * Output the WooCommerce Checkout Login Form.
1443
	 *
1444
	 * @subpackage	Checkout
1445
	 */
1446
	function woocommerce_checkout_login_form() {
1447
		wc_get_template( 'checkout/form-login.php', array( 'checkout' => WC()->checkout() ) );
1448
	}
1449
}
1450
1451
if ( ! function_exists( 'woocommerce_breadcrumb' ) ) {
1452
1453
	/**
1454
	 * Output the WooCommerce Breadcrumb.
1455
	 *
1456
	 * @param array $args
1457
	 */
1458
	function woocommerce_breadcrumb( $args = array() ) {
1459
		$args = wp_parse_args( $args, apply_filters( 'woocommerce_breadcrumb_defaults', array(
1460
			'delimiter'   => '&nbsp;&#47;&nbsp;',
1461
			'wrap_before' => '<nav class="woocommerce-breadcrumb" ' . ( is_single() ? 'itemprop="breadcrumb"' : '' ) . '>',
1462
			'wrap_after'  => '</nav>',
1463
			'before'      => '',
1464
			'after'       => '',
1465
			'home'        => _x( 'Home', 'breadcrumb', 'woocommerce' )
1466
		) ) );
1467
1468
		$breadcrumbs = new WC_Breadcrumb();
1469
1470
		if ( ! empty( $args['home'] ) ) {
1471
			$breadcrumbs->add_crumb( $args['home'], apply_filters( 'woocommerce_breadcrumb_home_url', home_url() ) );
1472
		}
1473
1474
		$args['breadcrumb'] = $breadcrumbs->generate();
1475
1476
		wc_get_template( 'global/breadcrumb.php', $args );
1477
	}
1478
}
1479
1480
if ( ! function_exists( 'woocommerce_order_review' ) ) {
1481
1482
	/**
1483
	 * Output the Order review table for the checkout.
1484
	 *
1485
	 * @subpackage	Checkout
1486
	 */
1487
	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...
1488
		wc_get_template( 'checkout/review-order.php', array( 'checkout' => WC()->checkout() ) );
1489
	}
1490
}
1491
1492
if ( ! function_exists( 'woocommerce_checkout_payment' ) ) {
1493
1494
	/**
1495
	 * Output the Payment Methods on the checkout.
1496
	 *
1497
	 * @subpackage	Checkout
1498
	 */
1499
	function woocommerce_checkout_payment() {
1500
		if ( WC()->cart->needs_payment() ) {
1501
			$available_gateways = WC()->payment_gateways()->get_available_payment_gateways();
1502
			WC()->payment_gateways()->set_current_gateway( $available_gateways );
1503
		} else {
1504
			$available_gateways = array();
1505
		}
1506
1507
		wc_get_template( 'checkout/payment.php', array(
1508
			'checkout'           => WC()->checkout(),
1509
			'available_gateways' => $available_gateways,
1510
			'order_button_text'  => apply_filters( 'woocommerce_order_button_text', __( 'Place order', 'woocommerce' ) )
1511
		) );
1512
	}
1513
}
1514
1515
if ( ! function_exists( 'woocommerce_checkout_coupon_form' ) ) {
1516
1517
	/**
1518
	 * Output the Coupon form for the checkout.
1519
	 *
1520
	 * @subpackage	Checkout
1521
	 */
1522
	function woocommerce_checkout_coupon_form() {
1523
		wc_get_template( 'checkout/form-coupon.php', array( 'checkout' => WC()->checkout() ) );
1524
	}
1525
}
1526
1527
if ( ! function_exists( 'woocommerce_products_will_display' ) ) {
1528
1529
	/**
1530
	 * Check if we will be showing products or not (and not sub-categories only).
1531
	 * @subpackage	Loop
1532
	 * @return bool
1533
	 */
1534
	function woocommerce_products_will_display() {
1535
		global $wpdb;
1536
1537
		if ( is_shop() ) {
1538
			return 'subcategories' !== get_option( 'woocommerce_shop_page_display' ) || is_search();
1539
		}
1540
1541
		if ( ! is_product_taxonomy() ) {
1542
			return false;
1543
		}
1544
1545
		if ( is_search() || is_filtered() || is_paged() ) {
1546
			return true;
1547
		}
1548
1549
		$term = get_queried_object();
1550
1551
		if ( is_product_category() ) {
1552
			switch ( get_woocommerce_term_meta( $term->term_id, 'display_type', true ) ) {
1553
				case 'subcategories' :
1554
					// Nothing - we want to continue to see if there are products/subcats
1555
				break;
1556
				case 'products' :
1557
				case 'both' :
1558
					return true;
1559
				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...
1560
				default :
1561
					// Default - no setting
1562
					if ( get_option( 'woocommerce_category_archive_display' ) != 'subcategories' ) {
1563
						return true;
1564
					}
1565
				break;
1566
			}
1567
		}
1568
1569
		// Begin subcategory logic
1570
		if ( empty( $term->term_id ) || empty( $term->taxonomy ) ) {
1571
			return true;
1572
		}
1573
1574
		$transient_name = 'wc_products_will_display_' . $term->term_id . '_' . WC_Cache_Helper::get_transient_version( 'product_query' );
1575
1576
		if ( false === ( $products_will_display = get_transient( $transient_name ) ) ) {
1577
			$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 ) );
1578
1579
			if ( $has_children ) {
1580
				// Check terms have products inside - parents first. If products are found inside, subcats will be shown instead of products so we can return false.
1581
				if ( sizeof( get_objects_in_term( $has_children, $term->taxonomy ) ) > 0 ) {
1582
					$products_will_display = false;
1583
				} else {
1584
					// If we get here, the parents were empty so we're forced to check children
1585
					foreach ( $has_children as $term_id ) {
1586
						$children = get_term_children( $term_id, $term->taxonomy );
1587
1588
						if ( sizeof( get_objects_in_term( $children, $term->taxonomy ) ) > 0 ) {
1589
							$products_will_display = false;
1590
							break;
1591
						}
1592
					}
1593
				}
1594
			} else {
1595
				$products_will_display = true;
1596
			}
1597
		}
1598
1599
		set_transient( $transient_name, $products_will_display, DAY_IN_SECONDS * 30 );
1600
1601
		return $products_will_display;
1602
	}
1603
}
1604
1605
if ( ! function_exists( 'woocommerce_product_subcategories' ) ) {
1606
1607
	/**
1608
	 * Display product sub categories as thumbnails.
1609
	 *
1610
	 * @subpackage	Loop
1611
	 * @param array $args
1612
	 * @return null|boolean
1613
	 */
1614
	function woocommerce_product_subcategories( $args = array() ) {
1615
		global $wp_query;
1616
1617
		$defaults = array(
1618
			'before'        => '',
1619
			'after'         => '',
1620
			'force_display' => false
1621
		);
1622
1623
		$args = wp_parse_args( $args, $defaults );
1624
1625
		extract( $args );
1626
1627
		// Main query only
1628
		if ( ! is_main_query() && ! $force_display ) {
1629
			return;
1630
		}
1631
1632
		// Don't show when filtering, searching or when on page > 1 and ensure we're on a product archive
1633
		if ( is_search() || is_filtered() || is_paged() || ( ! is_product_category() && ! is_shop() ) ) {
1634
			return;
1635
		}
1636
1637
		// Check categories are enabled
1638
		if ( is_shop() && '' === get_option( 'woocommerce_shop_page_display' ) ) {
1639
			return;
1640
		}
1641
1642
		// Find the category + category parent, if applicable
1643
		$term 			= get_queried_object();
1644
		$parent_id 		= empty( $term->term_id ) ? 0 : $term->term_id;
1645
1646
		if ( is_product_category() ) {
1647
			$display_type = get_woocommerce_term_meta( $term->term_id, 'display_type', true );
1648
1649
			switch ( $display_type ) {
1650
				case 'products' :
1651
					return;
1652
				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...
1653
				case '' :
1654
					if ( '' === get_option( 'woocommerce_category_archive_display' ) ) {
1655
						return;
1656
					}
1657
				break;
1658
			}
1659
		}
1660
1661
		// 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
1662
		$product_categories = get_categories( apply_filters( 'woocommerce_product_subcategories_args', array(
1663
			'parent'       => $parent_id,
1664
			'menu_order'   => 'ASC',
1665
			'hide_empty'   => 0,
1666
			'hierarchical' => 1,
1667
			'taxonomy'     => 'product_cat',
1668
			'pad_counts'   => 1
1669
		) ) );
1670
1671
		if ( ! apply_filters( 'woocommerce_product_subcategories_hide_empty', false ) ) {
1672
			$product_categories = wp_list_filter( $product_categories, array( 'count' => 0 ), 'NOT' );
1673
		}
1674
1675
		if ( $product_categories ) {
1676
			echo $before;
1677
1678
			foreach ( $product_categories as $category ) {
1679
				wc_get_template( 'content-product_cat.php', array(
1680
					'category' => $category
1681
				) );
1682
			}
1683
1684
			// If we are hiding products disable the loop and pagination
1685
			if ( is_product_category() ) {
1686
				$display_type = get_woocommerce_term_meta( $term->term_id, 'display_type', true );
1687
1688
				switch ( $display_type ) {
1689
					case 'subcategories' :
1690
						$wp_query->post_count    = 0;
1691
						$wp_query->max_num_pages = 0;
1692
					break;
1693
					case '' :
1694
						if ( 'subcategories' === get_option( 'woocommerce_category_archive_display' ) ) {
1695
							$wp_query->post_count    = 0;
1696
							$wp_query->max_num_pages = 0;
1697
						}
1698
					break;
1699
				}
1700
			}
1701
1702
			if ( is_shop() && 'subcategories' === get_option( 'woocommerce_shop_page_display' ) ) {
1703
				$wp_query->post_count    = 0;
1704
				$wp_query->max_num_pages = 0;
1705
			}
1706
1707
			echo $after;
1708
1709
			return true;
1710
		}
1711
	}
1712
}
1713
1714
if ( ! function_exists( 'woocommerce_subcategory_thumbnail' ) ) {
1715
1716
	/**
1717
	 * Show subcategory thumbnails.
1718
	 *
1719
	 * @param mixed $category
1720
	 * @subpackage	Loop
1721
	 */
1722
	function woocommerce_subcategory_thumbnail( $category ) {
1723
		$small_thumbnail_size  	= apply_filters( 'subcategory_archive_thumbnail_size', 'shop_catalog' );
1724
		$dimensions    			= wc_get_image_size( $small_thumbnail_size );
1725
		$thumbnail_id  			= get_woocommerce_term_meta( $category->term_id, 'thumbnail_id', true  );
1726
1727
		if ( $thumbnail_id ) {
1728
			$image = wp_get_attachment_image_src( $thumbnail_id, $small_thumbnail_size  );
1729
			$image = $image[0];
1730
		} else {
1731
			$image = wc_placeholder_img_src();
1732
		}
1733
1734
		if ( $image ) {
1735
			// Prevent esc_url from breaking spaces in urls for image embeds
1736
			// Ref: https://core.trac.wordpress.org/ticket/23605
1737
			$image = str_replace( ' ', '%20', $image );
1738
1739
			echo '<img src="' . esc_url( $image ) . '" alt="' . esc_attr( $category->name ) . '" width="' . esc_attr( $dimensions['width'] ) . '" height="' . esc_attr( $dimensions['height'] ) . '" />';
1740
		}
1741
	}
1742
}
1743
1744
if ( ! function_exists( 'woocommerce_order_details_table' ) ) {
1745
1746
	/**
1747
	 * Displays order details in a table.
1748
	 *
1749
	 * @param mixed $order_id
1750
	 * @subpackage	Orders
1751
	 */
1752
	function woocommerce_order_details_table( $order_id ) {
1753
		if ( ! $order_id ) return;
1754
1755
		wc_get_template( 'order/order-details.php', array(
1756
			'order_id' => $order_id
1757
		) );
1758
	}
1759
}
1760
1761
1762
if ( ! function_exists( 'woocommerce_order_again_button' ) ) {
1763
1764
	/**
1765
	 * Display an 'order again' button on the view order page.
1766
	 *
1767
	 * @param object $order
1768
	 * @subpackage	Orders
1769
	 */
1770
	function woocommerce_order_again_button( $order ) {
1771
		if ( ! $order || ! $order->has_status( 'completed' ) || ! is_user_logged_in() ) {
1772
			return;
1773
		}
1774
1775
		wc_get_template( 'order/order-again.php', array(
1776
			'order' => $order
1777
		) );
1778
	}
1779
}
1780
1781
/** Forms ****************************************************************/
1782
1783
if ( ! function_exists( 'woocommerce_form_field' ) ) {
1784
1785
	/**
1786
	 * Outputs a checkout/address form field.
1787
	 *
1788
	 * @subpackage	Forms
1789
	 * @param string $key
1790
	 * @param mixed $args
1791
	 * @param string $value (default: null)
1792
	 * @todo This function needs to be broken up in smaller pieces
1793
	 */
1794
	function woocommerce_form_field( $key, $args, $value = null ) {
1795
		$defaults = array(
1796
			'type'              => 'text',
1797
			'label'             => '',
1798
			'description'       => '',
1799
			'placeholder'       => '',
1800
			'maxlength'         => false,
1801
			'required'          => false,
1802
			'autocomplete'      => false,
1803
			'id'                => $key,
1804
			'class'             => array(),
1805
			'label_class'       => array(),
1806
			'input_class'       => array(),
1807
			'return'            => false,
1808
			'options'           => array(),
1809
			'custom_attributes' => array(),
1810
			'validate'          => array(),
1811
			'default'           => '',
1812
		);
1813
1814
		$args = wp_parse_args( $args, $defaults );
1815
		$args = apply_filters( 'woocommerce_form_field_args', $args, $key, $value );
1816
1817
		if ( $args['required'] ) {
1818
			$args['class'][] = 'validate-required';
1819
			$required = ' <abbr class="required" title="' . esc_attr__( 'required', 'woocommerce'  ) . '">*</abbr>';
1820
		} else {
1821
			$required = '';
1822
		}
1823
1824
		$args['maxlength'] = ( $args['maxlength'] ) ? 'maxlength="' . absint( $args['maxlength'] ) . '"' : '';
1825
1826
		$args['autocomplete'] = ( $args['autocomplete'] ) ? 'autocomplete="' . esc_attr( $args['autocomplete'] ) . '"' : '';
1827
1828
		if ( is_string( $args['label_class'] ) ) {
1829
			$args['label_class'] = array( $args['label_class'] );
1830
		}
1831
1832
		if ( is_null( $value ) ) {
1833
			$value = $args['default'];
1834
		}
1835
1836
		// Custom attribute handling
1837
		$custom_attributes = array();
1838
1839 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...
1840
			foreach ( $args['custom_attributes'] as $attribute => $attribute_value ) {
1841
				$custom_attributes[] = esc_attr( $attribute ) . '="' . esc_attr( $attribute_value ) . '"';
1842
			}
1843
		}
1844
1845
		if ( ! empty( $args['validate'] ) ) {
1846
			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...
1847
				$args['class'][] = 'validate-' . $validate;
1848
			}
1849
		}
1850
1851
		$field = '';
1852
		$label_id = $args['id'];
1853
		$field_container = '<p class="form-row %1$s" id="%2$s">%3$s</p>';
1854
1855
		switch ( $args['type'] ) {
1856
			case 'country' :
1857
1858
				$countries = 'shipping_country' === $key ? WC()->countries->get_shipping_countries() : WC()->countries->get_allowed_countries();
1859
1860
				if ( 1 === sizeof( $countries ) ) {
1861
1862
					$field .= '<strong>' . current( array_values( $countries ) ) . '</strong>';
1863
1864
					$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" />';
1865
1866
				} else {
1867
1868
					$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 ) . '>'
1869
							. '<option value="">'.__( 'Select a country&hellip;', 'woocommerce' ) .'</option>';
1870
1871 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...
1872
						$field .= '<option value="' . esc_attr( $ckey ) . '" '. selected( $value, $ckey, false ) . '>'. __( $cvalue, 'woocommerce' ) .'</option>';
1873
					}
1874
1875
					$field .= '</select>';
1876
1877
					$field .= '<noscript><input type="submit" name="woocommerce_checkout_update_totals" value="' . esc_attr__( 'Update country', 'woocommerce' ) . '" /></noscript>';
1878
1879
				}
1880
1881
				break;
1882
			case 'state' :
1883
1884
				/* Get Country */
1885
				$country_key = 'billing_state' === $key ? 'billing_country' : 'shipping_country';
1886
				$current_cc  = WC()->checkout->get_value( $country_key );
1887
				$states      = WC()->countries->get_states( $current_cc );
1888
1889
				if ( is_array( $states ) && empty( $states ) ) {
1890
1891
					$field_container = '<p class="form-row %1$s" id="%2$s" style="display: none">%3$s</p>';
1892
1893
					$field .= '<input type="hidden" class="hidden" name="' . esc_attr( $key )  . '" id="' . esc_attr( $args['id'] ) . '" value="" ' . implode( ' ', $custom_attributes ) . ' placeholder="' . esc_attr( $args['placeholder'] ) . '" />';
1894
1895
				} elseif ( is_array( $states ) ) {
1896
1897
					$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'] . '>
1898
						<option value="">'.__( 'Select a state&hellip;', 'woocommerce' ) .'</option>';
1899
1900 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...
1901
						$field .= '<option value="' . esc_attr( $ckey ) . '" '.selected( $value, $ckey, false ) .'>'.__( $cvalue, 'woocommerce' ) .'</option>';
1902
					}
1903
1904
					$field .= '</select>';
1905
1906
				} else {
1907
1908
					$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 ) . ' />';
1909
1910
				}
1911
1912
				break;
1913
			case 'textarea' :
1914
1915
				$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>';
1916
1917
				break;
1918 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...
1919
1920
				$field = '<label class="checkbox ' . implode( ' ', $args['label_class'] ) .'" ' . implode( ' ', $custom_attributes ) . '>
1921
						<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 ) .' /> '
1922
						 . $args['label'] . $required . '</label>';
1923
1924
				break;
1925
			case 'password' :
1926
			case 'text' :
1927
			case 'email' :
1928
			case 'tel' :
1929 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...
1930
1931
				$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 ) . ' />';
1932
1933
				break;
1934
			case 'select' :
1935
1936
				$options = $field = '';
1937
1938
				if ( ! empty( $args['options'] ) ) {
1939
					foreach ( $args['options'] as $option_key => $option_text ) {
1940
						if ( '' === $option_key ) {
1941
							// If we have a blank option, select2 needs a placeholder
1942
							if ( empty( $args['placeholder'] ) ) {
1943
								$args['placeholder'] = $option_text ? $option_text : __( 'Choose an option', 'woocommerce' );
1944
							}
1945
							$custom_attributes[] = 'data-allow_clear="true"';
1946
						}
1947
						$options .= '<option value="' . esc_attr( $option_key ) . '" '. selected( $value, $option_key, false ) . '>' . esc_attr( $option_text ) .'</option>';
1948
					}
1949
1950
					$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'] . '>
1951
							' . $options . '
1952
						</select>';
1953
				}
1954
1955
				break;
1956
			case 'radio' :
1957
1958
				$label_id = current( array_keys( $args['options'] ) );
1959
1960
				if ( ! empty( $args['options'] ) ) {
1961
					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...
1962
						$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 ) . ' />';
1963
						$field .= '<label for="' . esc_attr( $args['id'] ) . '_' . esc_attr( $option_key ) . '" class="radio ' . implode( ' ', $args['label_class'] ) .'">' . $option_text . '</label>';
1964
					}
1965
				}
1966
1967
				break;
1968
		}
1969
1970
		if ( ! empty( $field ) ) {
1971
			$field_html = '';
1972
1973
			if ( $args['label'] && 'checkbox' != $args['type'] ) {
1974
				$field_html .= '<label for="' . esc_attr( $label_id ) . '" class="' . esc_attr( implode( ' ', $args['label_class'] ) ) .'">' . $args['label'] . $required . '</label>';
1975
			}
1976
1977
			$field_html .= $field;
1978
1979
			if ( $args['description'] ) {
1980
				$field_html .= '<span class="description">' . esc_html( $args['description'] ) . '</span>';
1981
			}
1982
1983
			$container_class = 'form-row ' . esc_attr( implode( ' ', $args['class'] ) );
1984
			$container_id = esc_attr( $args['id'] ) . '_field';
1985
1986
			$after = ! empty( $args['clear'] ) ? '<div class="clear"></div>' : '';
1987
1988
			$field = sprintf( $field_container, $container_class, $container_id, $field_html ) . $after;
1989
		}
1990
1991
		$field = apply_filters( 'woocommerce_form_field_' . $args['type'], $field, $key, $args, $value );
1992
1993
		if ( $args['return'] ) {
1994
			return $field;
1995
		} else {
1996
			echo $field;
1997
		}
1998
	}
1999
}
2000
2001
if ( ! function_exists( 'get_product_search_form' ) ) {
2002
2003
	/**
2004
	 * Display product search form.
2005
	 *
2006
	 * Will first attempt to locate the product-searchform.php file in either the child or.
2007
	 * the parent, then load it. If it doesn't exist, then the default search form.
2008
	 * will be displayed.
2009
	 *
2010
	 * The default searchform uses html5.
2011
	 *
2012
	 * @subpackage	Forms
2013
	 * @param bool $echo (default: true)
2014
	 * @return string
2015
	 */
2016
	function get_product_search_form( $echo = true  ) {
2017
		ob_start();
2018
2019
		do_action( 'pre_get_product_search_form'  );
2020
2021
		wc_get_template( 'product-searchform.php' );
2022
2023
		$form = apply_filters( 'get_product_search_form', ob_get_clean() );
2024
2025
		if ( $echo ) {
2026
			echo $form;
2027
		} else {
2028
			return $form;
2029
		}
2030
	}
2031
}
2032
2033
if ( ! function_exists( 'woocommerce_output_auth_header' ) ) {
2034
2035
	/**
2036
	 * Output the Auth header.
2037
	 */
2038
	function woocommerce_output_auth_header() {
2039
		wc_get_template( 'auth/header.php' );
2040
	}
2041
}
2042
2043
if ( ! function_exists( 'woocommerce_output_auth_footer' ) ) {
2044
2045
	/**
2046
	 * Output the Auth footer.
2047
	 */
2048
	function woocommerce_output_auth_footer() {
2049
		wc_get_template( 'auth/footer.php' );
2050
	}
2051
}
2052
2053
if ( ! function_exists( 'woocommerce_single_variation' ) ) {
2054
2055
	/**
2056
	 * Output placeholders for the single variation.
2057
	 */
2058
	function woocommerce_single_variation() {
2059
		echo '<div class="woocommerce-variation single_variation"></div>';
2060
	}
2061
}
2062
2063
if ( ! function_exists( 'woocommerce_single_variation_add_to_cart_button' ) ) {
2064
2065
	/**
2066
	 * Output the add to cart button for variations.
2067
	 */
2068
	function woocommerce_single_variation_add_to_cart_button() {
2069
		wc_get_template( 'single-product/add-to-cart/variation-add-to-cart-button.php' );
2070
	}
2071
}
2072
2073
if ( ! function_exists( 'wc_dropdown_variation_attribute_options' ) ) {
2074
2075
	/**
2076
	 * Output a list of variation attributes for use in the cart forms.
2077
	 *
2078
	 * @param array $args
2079
	 * @since 2.4.0
2080
	 */
2081
	function wc_dropdown_variation_attribute_options( $args = array() ) {
2082
		$args = wp_parse_args( apply_filters( 'woocommerce_dropdown_variation_attribute_options_args', $args ), array(
2083
			'options'          => false,
2084
			'attribute'        => false,
2085
			'product'          => false,
2086
			'selected' 	       => false,
2087
			'name'             => '',
2088
			'id'               => '',
2089
			'class'            => '',
2090
			'show_option_none' => __( 'Choose an option', 'woocommerce' )
2091
		) );
2092
2093
		$options   = $args['options'];
2094
		$product   = $args['product'];
2095
		$attribute = $args['attribute'];
2096
		$name      = $args['name'] ? $args['name'] : 'attribute_' . sanitize_title( $attribute );
2097
		$id        = $args['id'] ? $args['id'] : sanitize_title( $attribute );
2098
		$class     = $args['class'];
2099
2100
		if ( empty( $options ) && ! empty( $product ) && ! empty( $attribute ) ) {
2101
			$attributes = $product->get_variation_attributes();
2102
			$options    = $attributes[ $attribute ];
2103
		}
2104
2105
		$html = '<select id="' . esc_attr( $id ) . '" class="' . esc_attr( $class ) . '" name="' . esc_attr( $name ) . '" data-attribute_name="attribute_' . esc_attr( sanitize_title( $attribute ) ) . '">';
2106
2107
		if ( $args['show_option_none'] ) {
2108
			$html .= '<option value="">' . esc_html( $args['show_option_none'] ) . '</option>';
2109
		}
2110
2111
		if ( ! empty( $options ) ) {
2112
			if ( $product && taxonomy_exists( $attribute ) ) {
2113
				// Get terms if this is a taxonomy - ordered. We need the names too.
2114
				$terms = wc_get_product_terms( $product->id, $attribute, array( 'fields' => 'all' ) );
2115
2116 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...
2117
					if ( in_array( $term->slug, $options ) ) {
2118
						$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>';
2119
					}
2120
				}
2121
			} else {
2122
				foreach ( $options as $option ) {
2123
					// This handles < 2.4.0 bw compatibility where text attributes were not sanitized.
2124
					$selected = sanitize_title( $args['selected'] ) === $args['selected'] ? selected( $args['selected'], sanitize_title( $option ), false ) : selected( $args['selected'], $option, false );
2125
					$html .= '<option value="' . esc_attr( $option ) . '" ' . $selected . '>' . esc_html( apply_filters( 'woocommerce_variation_option_name', $option ) ) . '</option>';
2126
				}
2127
			}
2128
		}
2129
2130
		$html .= '</select>';
2131
2132
		echo apply_filters( 'woocommerce_dropdown_variation_attribute_options_html', $html, $args );
2133
	}
2134
}
2135
2136
if ( ! function_exists( 'woocommerce_account_content' ) ) {
2137
2138
	/**
2139
	 * My Account content output.
2140
	 */
2141
	function woocommerce_account_content() {
2142
		global $wp;
2143
2144
		foreach ( $wp->query_vars as $key => $value ) {
2145
			// Ignore pagename param.
2146
			if ( 'pagename' === $key ) {
2147
				continue;
2148
			}
2149
2150
			if ( has_action( 'woocommerce_account_' . $key . '_endpoint' ) ) {
2151
				do_action( 'woocommerce_account_' . $key . '_endpoint', $value );
2152
				return;
2153
			}
2154
		}
2155
2156
		// No endpoint found? Default to dashboard.
2157
		wc_get_template( 'myaccount/dashboard.php', array(
2158
			'current_user' => get_user_by( 'id', get_current_user_id() ),
2159
		) );
2160
	}
2161
}
2162
2163
if ( ! function_exists( 'woocommerce_account_navigation' ) ) {
2164
2165
	/**
2166
	 * My Account navigation template.
2167
	 */
2168
	function woocommerce_account_navigation() {
2169
		wc_get_template( 'myaccount/navigation.php' );
2170
	}
2171
}
2172
2173
if ( ! function_exists( 'woocommerce_account_orders' ) ) {
2174
2175
	/**
2176
	 * My Account > Orders template.
2177
	 *
2178
	 * @param int $current_page Current page number.
2179
	 */
2180
	function woocommerce_account_orders( $current_page ) {
2181
		$current_page    = empty( $current_page ) ? 1 : absint( $current_page );
2182
		$customer_orders = wc_get_orders( apply_filters( 'woocommerce_my_account_my_orders_query', array( 'customer' => get_current_user_id(), 'page' => $current_page, 'paginate' => true ) ) );
2183
2184
		wc_get_template(
2185
			'myaccount/orders.php',
2186
			array(
2187
				'current_page' => absint( $current_page ),
2188
				'customer_orders' => $customer_orders,
2189
				'has_orders' => 0 < $customer_orders->total,
2190
			)
2191
		);
2192
	}
2193
}
2194
2195
if ( ! function_exists( 'woocommerce_account_view_order' ) ) {
2196
2197
	/**
2198
	 * My Account > View order template.
2199
	 *
2200
	 * @param int $order_id Order ID.
2201
	 */
2202
	function woocommerce_account_view_order( $order_id ) {
2203
		WC_Shortcode_My_Account::view_order( absint( $order_id ) );
2204
	}
2205
}
2206
2207
if ( ! function_exists( 'woocommerce_account_downloads' ) ) {
2208
2209
	/**
2210
	 * My Account > Downloads template.
2211
	 */
2212
	function woocommerce_account_downloads() {
2213
		wc_get_template( 'myaccount/downloads.php' );
2214
	}
2215
}
2216
2217
if ( ! function_exists( 'woocommerce_account_edit_address' ) ) {
2218
2219
	/**
2220
	 * My Account > Edit address template.
2221
	 *
2222
	 * @param string $type Address type.
2223
	 */
2224
	function woocommerce_account_edit_address( $type ) {
2225
		$type = wc_edit_address_i18n( sanitize_title( $type ), true );
2226
2227
		WC_Shortcode_My_Account::edit_address( $type );
2228
	}
2229
}
2230
2231
if ( ! function_exists( 'woocommerce_account_payment_methods' ) ) {
2232
2233
	/**
2234
	 * My Account > Downloads template.
2235
	 */
2236
	function woocommerce_account_payment_methods() {
2237
		wc_get_template( 'myaccount/payment-methods.php' );
2238
	}
2239
}
2240
2241
if ( ! function_exists( 'woocommerce_account_add_payment_method' ) ) {
2242
2243
	/**
2244
	 * My Account > Add payment method template.
2245
	 */
2246
	function woocommerce_account_add_payment_method() {
2247
		WC_Shortcode_My_Account::add_payment_method();
2248
	}
2249
}
2250
2251
if ( ! function_exists( 'woocommerce_account_edit_account' ) ) {
2252
2253
	/**
2254
	 * My Account > Edit account template.
2255
	 */
2256
	function woocommerce_account_edit_account() {
2257
		WC_Shortcode_My_Account::edit_account();
2258
	}
2259
}
2260