Issues (1182)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

includes/widgets/class-wc-widget-layered-nav.php (6 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
if ( ! defined( 'ABSPATH' ) ) {
4
	exit;
5
}
6
7
/**
8
 * Layered Navigation Widget.
9
 *
10
 * @author   WooThemes
11
 * @category Widgets
12
 * @package  WooCommerce/Widgets
13
 * @version  2.6.0
14
 * @extends  WC_Widget
15
 */
16
class WC_Widget_Layered_Nav extends WC_Widget {
17
18
	/**
19
	 * Constructor.
20
	 */
21
	public function __construct() {
22
		$this->widget_cssclass    = 'woocommerce widget_layered_nav';
23
		$this->widget_description = __( 'Shows a custom attribute in a widget which lets you narrow down the list of products when viewing product categories.', 'woocommerce' );
24
		$this->widget_id          = 'woocommerce_layered_nav';
25
		$this->widget_name        = __( 'WooCommerce Layered Nav', 'woocommerce' );
26
		parent::__construct();
27
	}
28
29
	/**
30
	 * Updates a particular instance of a widget.
31
	 *
32
	 * @see WP_Widget->update
33
	 *
34
	 * @param array $new_instance
35
	 * @param array $old_instance
36
	 *
37
	 * @return array
38
	 */
39
	public function update( $new_instance, $old_instance ) {
40
		$this->init_settings();
41
		return parent::update( $new_instance, $old_instance );
42
	}
43
44
	/**
45
	 * Outputs the settings update form.
46
	 *
47
	 * @see WP_Widget->form
48
	 *
49
	 * @param array $instance
50
	 */
51
	public function form( $instance ) {
52
		$this->init_settings();
53
		parent::form( $instance );
54
	}
55
56
	/**
57
	 * Init settings after post types are registered.
58
	 */
59
	public function init_settings() {
60
		$attribute_array      = array();
61
		$attribute_taxonomies = wc_get_attribute_taxonomies();
62
63
		if ( ! empty( $attribute_taxonomies ) ) {
64
			foreach ( $attribute_taxonomies as $tax ) {
65
				if ( taxonomy_exists( wc_attribute_taxonomy_name( $tax->attribute_name ) ) ) {
66
					$attribute_array[ $tax->attribute_name ] = $tax->attribute_name;
67
				}
68
			}
69
		}
70
71
		$this->settings = array(
72
			'title' => array(
73
				'type'  => 'text',
74
				'std'   => __( 'Filter by', 'woocommerce' ),
75
				'label' => __( 'Title', 'woocommerce' )
76
			),
77
			'attribute' => array(
78
				'type'    => 'select',
79
				'std'     => '',
80
				'label'   => __( 'Attribute', 'woocommerce' ),
81
				'options' => $attribute_array
82
			),
83
			'display_type' => array(
84
				'type'    => 'select',
85
				'std'     => 'list',
86
				'label'   => __( 'Display type', 'woocommerce' ),
87
				'options' => array(
88
					'list'     => __( 'List', 'woocommerce' ),
89
					'dropdown' => __( 'Dropdown', 'woocommerce' )
90
				)
91
			),
92
			'query_type' => array(
93
				'type'    => 'select',
94
				'std'     => 'and',
95
				'label'   => __( 'Query type', 'woocommerce' ),
96
				'options' => array(
97
					'and' => __( 'AND', 'woocommerce' ),
98
					'or'  => __( 'OR', 'woocommerce' )
99
				)
100
			),
101
		);
102
	}
103
104
	/**
105
	 * Output widget.
106
	 *
107
	 * @see WP_Widget
108
	 *
109
	 * @param array $args
110
	 * @param array $instance
111
	 */
112
	public function widget( $args, $instance ) {
113
		if ( ! is_post_type_archive( 'product' ) && ! is_tax( get_object_taxonomies( 'product' ) ) ) {
114
			return;
115
		}
116
117
		$_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes();
118
		$taxonomy           = isset( $instance['attribute'] ) ? wc_attribute_taxonomy_name( $instance['attribute'] ) : $this->settings['attribute']['std'];
119
		$query_type         = isset( $instance['query_type'] ) ? $instance['query_type'] : $this->settings['query_type']['std'];
120
		$display_type       = isset( $instance['display_type'] ) ? $instance['display_type'] : $this->settings['display_type']['std'];
121
122
		if ( ! taxonomy_exists( $taxonomy ) ) {
123
			return;
124
		}
125
126
		$get_terms_args = array( 'hide_empty' => '1' );
127
128
		$orderby = wc_attribute_orderby( $taxonomy );
129
130
		switch ( $orderby ) {
131
			case 'name' :
132
				$get_terms_args['orderby']    = 'name';
133
				$get_terms_args['menu_order'] = false;
134
			break;
135
			case 'id' :
136
				$get_terms_args['orderby']    = 'id';
137
				$get_terms_args['order']      = 'ASC';
138
				$get_terms_args['menu_order'] = false;
139
			break;
140
			case 'menu_order' :
141
				$get_terms_args['menu_order'] = 'ASC';
142
			break;
143
		}
144
145
		$terms = get_terms( $taxonomy, $get_terms_args );
146
147
		if ( 0 === sizeof( $terms ) ) {
148
			return;
149
		}
150
151
		ob_start();
152
153
		$this->widget_start( $args, $instance );
154
155
		if ( 'dropdown' === $display_type ) {
156
			$found = $this->layered_nav_dropdown( $terms, $taxonomy, $query_type );
157
		} else {
158
			$found = $this->layered_nav_list( $terms, $taxonomy, $query_type );
159
		}
160
161
		$this->widget_end( $args );
162
163
		// Force found when option is selected - do not force found on taxonomy attributes
164
		if ( ! is_tax() && is_array( $_chosen_attributes ) && array_key_exists( $taxonomy, $_chosen_attributes ) ) {
165
			$found = true;
166
		}
167
168
		if ( ! $found ) {
169
			ob_end_clean();
170
		} else {
171
			echo ob_get_clean();
172
		}
173
	}
174
175
	/**
176
	 * Return the currently viewed taxonomy name.
177
	 * @return string
178
	 */
179
	protected function get_current_taxonomy() {
180
		return is_tax() ? get_queried_object()->taxonomy : '';
181
	}
182
183
	/**
184
	 * Return the currently viewed term ID.
185
	 * @return int
186
	 */
187
	protected function get_current_term_id() {
188
		return absint( is_tax() ? get_queried_object()->term_id : 0 );
189
	}
190
191
	/**
192
	 * Return the currently viewed term slug.
193
	 * @return int
194
	 */
195
	protected function get_current_term_slug() {
196
		return absint( is_tax() ? get_queried_object()->slug : 0 );
197
	}
198
199
	/**
200
	 * Show dropdown layered nav.
201
	 * @param  array $terms
202
	 * @param  string $taxonomy
203
	 * @param  string $query_type
204
	 * @return bool Will nav display?
205
	 */
206
	protected function layered_nav_dropdown( $terms, $taxonomy, $query_type ) {
207
		$found = false;
208
209
		if ( $taxonomy !== $this->get_current_taxonomy() ) {
210
			$term_counts          = $this->get_filtered_term_product_counts( wp_list_pluck( $terms, 'term_id' ), $taxonomy, $query_type );
211
			$_chosen_attributes   = WC_Query::get_layered_nav_chosen_attributes();
212
			$taxonomy_filter_name = str_replace( 'pa_', '', $taxonomy );
213
214
			echo '<select class="dropdown_layered_nav_' . esc_attr( $taxonomy_filter_name ) . '">';
215
			echo '<option value="">' . sprintf( __( 'Any %s', 'woocommerce' ), wc_attribute_label( $taxonomy ) ) . '</option>';
216
217
			foreach ( $terms as $term ) {
218
219
				// If on a term page, skip that term in widget list
220
				if ( $term->term_id === $this->get_current_term_id() ) {
221
					continue;
222
				}
223
224
				// Get count based on current view
225
				$current_values    = isset( $_chosen_attributes[ $taxonomy ]['terms'] ) ? $_chosen_attributes[ $taxonomy ]['terms'] : array();
226
				$option_is_set     = in_array( $term->slug, $current_values );
227
				$count             = isset( $term_counts[ $term->term_id ] ) ? $term_counts[ $term->term_id ] : 0;
228
229
				// Only show options with count > 0
230 View Code Duplication
				if ( 0 < $count ) {
0 ignored issues
show
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...
231
					$found = true;
232
				} elseif ( 'and' === $query_type && 0 === $count && ! $option_is_set ) {
233
					continue;
234
				}
235
236
				echo '<option value="' . esc_attr( $term->slug ) . '" ' . selected( $option_is_set, true, false ) . '>' . esc_html( $term->name ) . '</option>';
237
			}
238
239
			echo '</select>';
240
241
			wc_enqueue_js( "
242
				jQuery( '.dropdown_layered_nav_". esc_js( $taxonomy_filter_name ) . "' ).change( function() {
243
					var slug = jQuery( this ).val();
244
					location.href = '" . preg_replace( '%\/page\/[0-9]+%', '', str_replace( array( '&amp;', '%2C' ), array( '&', ',' ), esc_js( add_query_arg( 'filtering', '1', remove_query_arg( array( 'page', 'filter_' . $taxonomy_filter_name ) ) ) ) ) ) . "&filter_". esc_js( $taxonomy_filter_name ) . "=' + slug;
245
				});
246
			" );
247
		}
248
249
		return $found;
250
	}
251
252
	/**
253
	 * Get current page URL for layered nav items.
254
	 * @return string
255
	 */
256
	protected function get_page_base_url( $taxonomy ) {
257 View Code Duplication
		if ( defined( 'SHOP_IS_ON_FRONT' ) ) {
0 ignored issues
show
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...
258
			$link = home_url();
259
		} elseif ( is_post_type_archive( 'product' ) || is_page( wc_get_page_id('shop') ) ) {
260
			$link = get_post_type_archive_link( 'product' );
261
		} else {
262
			$link = get_term_link( get_query_var('term'), get_query_var('taxonomy') );
263
		}
264
265
		// Min/Max
266
		if ( isset( $_GET['min_price'] ) ) {
267
			$link = add_query_arg( 'min_price', wc_clean( $_GET['min_price'] ), $link );
268
		}
269
270
		if ( isset( $_GET['max_price'] ) ) {
271
			$link = add_query_arg( 'max_price', wc_clean( $_GET['max_price'] ), $link );
272
		}
273
274
		// Orderby
275 View Code Duplication
		if ( isset( $_GET['orderby'] ) ) {
0 ignored issues
show
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...
276
			$link = add_query_arg( 'orderby', wc_clean( $_GET['orderby'] ), $link );
277
		}
278
279
		/**
280
		 * Search Arg.
281
		 * To support quote characters, first they are decoded from &quot; entities, then URL encoded.
282
		 */
283
		if ( get_search_query() ) {
284
			$link = add_query_arg( 's', rawurlencode( htmlspecialchars_decode( get_search_query() ) ), $link );
285
		}
286
287
		// Post Type Arg
288
		if ( isset( $_GET['post_type'] ) ) {
289
			$link = add_query_arg( 'post_type', wc_clean( $_GET['post_type'] ), $link );
290
		}
291
292
		// Min Rating Arg
293 View Code Duplication
		if ( isset( $_GET['min_rating'] ) ) {
0 ignored issues
show
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...
294
			$link = add_query_arg( 'min_rating', wc_clean( $_GET['min_rating'] ), $link );
295
		}
296
297
		// All current filters
298 View Code Duplication
		if ( $_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes() ) {
0 ignored issues
show
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...
299
			foreach ( $_chosen_attributes as $name => $data ) {
300
				if ( $name === $taxonomy ) {
301
					continue;
302
				}
303
				$filter_name = sanitize_title( str_replace( 'pa_', '', $name ) );
304
				if ( ! empty( $data['terms'] ) ) {
305
					$link = add_query_arg( 'filter_' . $filter_name, implode( ',', $data['terms'] ), $link );
306
				}
307
				if ( 'or' == $data['query_type'] ) {
308
					$link = add_query_arg( 'query_type_' . $filter_name, 'or', $link );
309
				}
310
			}
311
		}
312
313
		return $link;
314
	}
315
316
	/**
317
	 * Count products within certain terms, taking the main WP query into consideration.
318
	 * @param  array $term_ids
319
	 * @param  string $taxonomy
320
	 * @param  string $query_type
321
	 * @return array
322
	 */
323
	protected function get_filtered_term_product_counts( $term_ids, $taxonomy, $query_type ) {
324
		global $wpdb;
325
326
		$tax_query  = WC_Query::get_main_tax_query();
327
		$meta_query = WC_Query::get_main_meta_query();
328
329
		if ( 'or' === $query_type ) {
330
			foreach ( $tax_query as $key => $query ) {
331
				if ( $taxonomy === $query['taxonomy'] ) {
332
					unset( $tax_query[ $key ] );
333
				}
334
			}
335
		}
336
337
		$meta_query     = new WP_Meta_Query( $meta_query );
338
		$tax_query      = new WP_Tax_Query( $tax_query );
339
		$meta_query_sql = $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );
340
		$tax_query_sql  = $tax_query->get_sql( $wpdb->posts, 'ID' );
341
342
		$sql  = "
343
			SELECT COUNT( {$wpdb->posts}.ID ) as term_count, term_count_relationships.term_taxonomy_id as term_count_id FROM {$wpdb->posts}
344
			INNER JOIN {$wpdb->term_relationships} AS term_count_relationships ON ({$wpdb->posts}.ID = term_count_relationships.object_id)
345
			" . $tax_query_sql['join'] . $meta_query_sql['join'] . "
346
			WHERE {$wpdb->posts}.post_type = 'product' AND {$wpdb->posts}.post_status = 'publish'
347
			" . $tax_query_sql['where'] . $meta_query_sql['where'] . "
348
			AND term_count_relationships.term_taxonomy_id IN (" . implode( ',', array_map( 'absint', $term_ids ) ) . ")
349
			GROUP BY term_count_relationships.term_taxonomy_id;
350
		";
351
352
		$results = $wpdb->get_results( $sql );
353
354
		return wp_list_pluck( $results, 'term_count', 'term_count_id' );
355
	}
356
357
	/**
358
	 * Show list based layered nav.
359
	 * @param  array $terms
360
	 * @param  string $taxonomy
361
	 * @param  string $query_type
362
	 * @return bool Will nav display?
363
	 */
364
	protected function layered_nav_list( $terms, $taxonomy, $query_type ) {
365
		// List display
366
		echo '<ul>';
367
368
		$term_counts        = $this->get_filtered_term_product_counts( wp_list_pluck( $terms, 'term_id' ), $taxonomy, $query_type );
369
		$_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes();
370
		$found              = false;
371
372
		foreach ( $terms as $term ) {
373
			$current_values    = isset( $_chosen_attributes[ $taxonomy ]['terms'] ) ? $_chosen_attributes[ $taxonomy ]['terms'] : array();
374
			$option_is_set     = in_array( $term->slug, $current_values );
375
			$count             = isset( $term_counts[ $term->term_id ] ) ? $term_counts[ $term->term_id ] : 0;
376
377
			// skip the term for the current archive
378
			if ( $this->get_current_term_id() === $term->term_id ) {
379
				continue;
380
			}
381
382
			// Only show options with count > 0
383 View Code Duplication
			if ( 0 < $count ) {
0 ignored issues
show
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...
384
				$found = true;
385
			} elseif ( 'and' === $query_type && 0 === $count && ! $option_is_set ) {
386
				continue;
387
			}
388
389
			$filter_name    = 'filter_' . sanitize_title( str_replace( 'pa_', '', $taxonomy ) );
390
			$current_filter = isset( $_GET[ $filter_name ] ) ? explode( ',', wc_clean( $_GET[ $filter_name ] ) ) : array();
391
			$current_filter = array_map( 'sanitize_title', $current_filter );
392
393
			if ( ! in_array( $term->slug, $current_filter ) ) {
394
				$current_filter[] = $term->slug;
395
			}
396
397
			$link = $this->get_page_base_url( $taxonomy );
398
399
			// Add current filters to URL.
400
			foreach ( $current_filter as $key => $value ) {
401
				// Exclude query arg for current term archive term
402
				if ( $value === $this->get_current_term_slug() ) {
403
					unset( $current_filter[ $key ] );
404
				}
405
406
				// Exclude self so filter can be unset on click.
407
				if ( $option_is_set && $value === $term->slug ) {
408
					unset( $current_filter[ $key ] );
409
				}
410
			}
411
412
			if ( ! empty( $current_filter ) ) {
413
				$link = add_query_arg( $filter_name, implode( ',', $current_filter ), $link );
414
415
				// Add Query type Arg to URL
416
				if ( $query_type === 'or' && ! ( 1 === sizeof( $current_filter ) && $option_is_set ) ) {
417
					$link = add_query_arg( 'query_type_' . sanitize_title( str_replace( 'pa_', '', $taxonomy ) ), 'or', $link );
418
				}
419
			}
420
421
			echo '<li class="wc-layered-nav-term ' . ( $option_is_set ? 'chosen' : '' ) . '">';
422
423
			echo ( $count > 0 || $option_is_set ) ? '<a href="' . esc_url( apply_filters( 'woocommerce_layered_nav_link', $link ) ) . '">' : '<span>';
424
425
			echo esc_html( $term->name );
426
427
			echo ( $count > 0 || $option_is_set ) ? '</a>' : '</span>';
428
429
			echo ' <span class="count">(' . absint( $count ) . ')</span></li>';
430
		}
431
432
		echo '</ul>';
433
434
		return $found;
435
	}
436
}
437