first commit

This commit is contained in:
User A0264400
2026-04-01 23:20:16 +03:00
commit a766acdc90
23071 changed files with 4933189 additions and 0 deletions

View File

@@ -0,0 +1,130 @@
<?php
declare( strict_types = 1);
/**
* Brand Description Widget
*
* When viewing a brand archive, show the current brands description + image
*
* Important: For internal use only by the Automattic\WooCommerce\Internal\Brands package.
*
* @package WooCommerce\Widgets
* @version 9.4.0
*/
class WC_Widget_Brand_Description extends WP_Widget {
/**
* Widget class.
*
* @var string
*/
public $woo_widget_cssclass;
/**
* Widget description.
*
* @var string
*/
public $woo_widget_description;
/**
* Widget idbase.
*
* @var string
*/
public $woo_widget_idbase;
/**
* Widget name.
*
* @var string
*/
public $woo_widget_name;
/** Constructor */
public function __construct() {
/* Widget variable settings. */
$this->woo_widget_name = __( 'WooCommerce Brand Description', 'woocommerce' );
$this->woo_widget_description = __( 'When viewing a brand archive, show the current brands description.', 'woocommerce' );
$this->woo_widget_idbase = 'wc_brands_brand_description';
$this->woo_widget_cssclass = 'widget_brand_description';
/* Widget settings. */
$widget_ops = array(
'classname' => $this->woo_widget_cssclass,
'description' => $this->woo_widget_description,
);
/* Create the widget. */
parent::__construct( $this->woo_widget_idbase, $this->woo_widget_name, $widget_ops );
}
/**
* Echoes the widget content.
*
* @see WP_Widget
*
* @param array $args Display arguments including 'before_title', 'after_title',
* 'before_widget', and 'after_widget'.
* @param array $instance The settings for the particular instance of the widget.
*/
public function widget( $args, $instance ) {
extract( $args ); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
if ( ! is_tax( 'product_brand' ) ) {
return;
}
if ( ! get_query_var( 'term' ) ) {
return;
}
$thumbnail = '';
$term = get_term_by( 'slug', get_query_var( 'term' ), 'product_brand' );
$thumbnail = wc_get_brand_thumbnail_url( $term->term_id, 'large' );
echo $before_widget . $before_title . $term->name . $after_title; // phpcs:ignore WordPress.Security.EscapeOutput
wc_get_template(
'widgets/brand-description.php',
array(
'thumbnail' => $thumbnail,
'brand' => $term,
),
'woocommerce',
WC()->plugin_path() . '/templates/brands/'
);
echo $after_widget; // phpcs:ignore WordPress.Security.EscapeOutput
}
/**
* Updates widget instance.
*
* @see WP_Widget->update
*
* @param array $new_instance New widget instance.
* @param array $old_instance Old widget instance.
*/
public function update( $new_instance, $old_instance ) {
$instance['title'] = wp_strip_all_tags( stripslashes( $new_instance['title'] ) );
return $instance;
}
/**
* Outputs the settings update form.
*
* @param array $instance Current settings.
*/
public function form( $instance ) {
?>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'woocommerce' ); ?></label>
<input type="text" class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" value="<?php echo isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : ''; ?>" />
</p>
<?php
}
}

View File

@@ -0,0 +1,551 @@
<?php
declare( strict_types = 1);
/**
* Layered Navigation Widget for brands WC 2.6 version
*
* Important: For internal use only by the Automattic\WooCommerce\Internal\Brands package.
*
* @package WooCommerce\Widgets
* @version 9.4.0
* @extends WP_Widget
*/
class WC_Widget_Brand_Nav extends WC_Widget {
/**
* Constructor
*
* @return void
*/
public function __construct() {
/* Widget variable settings. */
$this->widget_cssclass = 'woocommerce widget_brand_nav widget_layered_nav';
$this->widget_description = __( 'Shows brands in a widget which lets you narrow down the list of products when viewing products.', 'woocommerce' );
$this->widget_id = 'woocommerce_brand_nav';
$this->widget_name = __( 'WooCommerce Brand Layered Nav', 'woocommerce' );
add_filter( 'woocommerce_product_subcategories_args', array( $this, 'filter_out_cats' ) );
/* Create the widget. */
parent::__construct();
}
/**
* Filter out all categories and not display them
*
* @param array $cat_args Category arguments.
*/
public function filter_out_cats( $cat_args ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( ! empty( $_GET['filter_product_brand'] ) ) {
return array( 'taxonomy' => '' );
}
return $cat_args;
}
/**
* Return the currently viewed taxonomy name.
*
* @return string
*/
protected function get_current_taxonomy() {
return is_tax() ? get_queried_object()->taxonomy : '';
}
/**
* Return the currently viewed term ID.
*
* @return int
*/
protected function get_current_term_id() {
return absint( is_tax() ? get_queried_object()->term_id : 0 );
}
/**
* Return the currently viewed term slug.
*
* @return int
*/
protected function get_current_term_slug() {
return absint( is_tax() ? get_queried_object()->slug : 0 );
}
/**
* Widget function.
*
* @see WP_Widget
*
* @param array $args Arguments.
* @param array $instance Widget instance.
* @return void
*/
public function widget( $args, $instance ) {
$attribute_array = array();
$attribute_taxonomies = wc_get_attribute_taxonomies();
if ( ! empty( $attribute_taxonomies ) ) {
foreach ( $attribute_taxonomies as $tax ) {
$taxonomy_name = wc_attribute_taxonomy_name( $tax->attribute_name );
if ( taxonomy_exists( $taxonomy_name ) ) {
$attribute_array[] = $taxonomy_name;
}
}
}
if ( ! is_post_type_archive( 'product' ) && ! is_tax( array_merge( $attribute_array, array( 'product_cat', 'product_tag', 'product_brand' ) ) ) ) {
return;
}
$_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes();
$current_term = $attribute_array && is_tax( $attribute_array ) ? get_queried_object()->term_id : '';
$current_tax = $attribute_array && is_tax( $attribute_array ) ? get_queried_object()->taxonomy : '';
/**
* Filter the widget's title.
*
* @since 9.4.0
*
* @param string $title Widget title
* @param array $instance The settings for the particular instance of the widget.
* @param string $woo_widget_idbase The widget's id base.
*/
$title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
$taxonomy = 'product_brand';
$display_type = isset( $instance['display_type'] ) ? $instance['display_type'] : 'list';
if ( ! taxonomy_exists( $taxonomy ) ) {
return;
}
// Get only parent terms. Methods will recursively retrieve children.
$terms = get_terms(
array(
'taxonomy' => $taxonomy,
'hide_empty' => true,
'parent' => 0,
)
);
if ( empty( $terms ) ) {
return;
}
ob_start();
$this->widget_start( $args, $instance );
if ( 'dropdown' === $display_type ) {
$found = $this->layered_nav_dropdown( $terms, $taxonomy );
} else {
$found = $this->layered_nav_list( $terms, $taxonomy );
}
$this->widget_end( $args );
// Force found when option is selected - do not force found on taxonomy attributes.
if ( ! is_tax() && is_array( $_chosen_attributes ) && array_key_exists( $taxonomy, $_chosen_attributes ) ) {
$found = true;
}
if ( ! $found ) {
ob_end_clean();
} else {
echo ob_get_clean(); // phpcs:ignore WordPress.Security.EscapeOutput
}
}
/**
* Update function.
*
* @see WP_Widget->update
*
* @param array $new_instance The new settings for the particular instance of the widget.
* @param array $old_instance The old settings for the particular instance of the widget.
* @return array
*/
public function update( $new_instance, $old_instance ) {
global $woocommerce;
if ( empty( $new_instance['title'] ) ) {
$new_instance['title'] = __( 'Brands', 'woocommerce' );
}
$instance['title'] = wp_strip_all_tags( stripslashes( $new_instance['title'] ) );
$instance['display_type'] = stripslashes( $new_instance['display_type'] );
return $instance;
}
/**
* Form function.
*
* @see WP_Widget->form
*
* @param array $instance Widget instance.
* @return void
*/
public function form( $instance ) {
global $woocommerce;
if ( ! isset( $instance['display_type'] ) ) {
$instance['display_type'] = 'list';
}
?>
<p><label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'woocommerce' ); ?></label>
<input type="text" class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" value="<?php echo isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : ''; ?>" />
</p>
<p><label for="<?php echo esc_attr( $this->get_field_id( 'display_type' ) ); ?>"><?php esc_html_e( 'Display Type:', 'woocommerce' ); ?></label>
<select id="<?php echo esc_attr( $this->get_field_id( 'display_type' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'display_type' ) ); ?>">
<option value="list" <?php selected( $instance['display_type'], 'list' ); ?>><?php esc_html_e( 'List', 'woocommerce' ); ?></option>
<option value="dropdown" <?php selected( $instance['display_type'], 'dropdown' ); ?>><?php esc_html_e( 'Dropdown', 'woocommerce' ); ?></option>
</select></p>
<?php
}
/**
* Get current page URL for layered nav items.
*
* @param string $taxonomy Taxonomy.
* @return string
*/
protected function get_page_base_url( $taxonomy ) {
if ( defined( 'SHOP_IS_ON_FRONT' ) ) {
$link = home_url();
} elseif ( is_post_type_archive( 'product' ) || is_page( wc_get_page_id( 'shop' ) ) ) {
$link = get_post_type_archive_link( 'product' );
} elseif ( is_product_category() ) {
$link = get_term_link( get_query_var( 'product_cat' ), 'product_cat' );
} elseif ( is_product_tag() ) {
$link = get_term_link( get_query_var( 'product_tag' ), 'product_tag' );
} elseif ( is_tax() ) {
// Handle any taxonomy archive, including attributes
$queried_object = get_queried_object();
if ( is_null( $queried_object ) ) {
$link = get_post_type_archive_link( 'product' );
} else {
$link = get_term_link( $queried_object->term_id, $queried_object->taxonomy );
}
} else {
$link = get_post_type_archive_link( 'product' );
}
// phpcs:disable WordPress.Security.NonceVerification.Recommended
// Min/Max.
if ( isset( $_GET['min_price'] ) ) {
$link = add_query_arg( 'min_price', wc_clean( wp_unslash( $_GET['min_price'] ) ), $link );
}
if ( isset( $_GET['max_price'] ) ) {
$link = add_query_arg( 'max_price', wc_clean( wp_unslash( $_GET['max_price'] ) ), $link );
}
// Orderby.
if ( isset( $_GET['orderby'] ) ) {
$link = add_query_arg( 'orderby', wc_clean( wp_unslash( $_GET['orderby'] ) ), $link );
}
/**
* Search Arg.
* To support quote characters, first they are decoded from &quot; entities, then URL encoded.
*/
if ( get_search_query() ) {
$link = add_query_arg( 's', rawurlencode( htmlspecialchars_decode( get_search_query() ) ), $link );
}
// Post Type Arg.
if ( isset( $_GET['post_type'] ) ) {
$link = add_query_arg( 'post_type', wc_clean( wp_unslash( $_GET['post_type'] ) ), $link );
}
// Min Rating Arg.
if ( isset( $_GET['min_rating'] ) ) {
$link = add_query_arg( 'min_rating', wc_clean( wp_unslash( $_GET['min_rating'] ) ), $link );
}
// All current filters.
$_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes();
if ( $_chosen_attributes ) {
foreach ( $_chosen_attributes as $name => $data ) {
if ( $name === $taxonomy ) {
continue;
}
$filter_name = sanitize_title( str_replace( 'pa_', '', $name ) );
if ( ! empty( $data['terms'] ) ) {
$link = add_query_arg( 'filter_' . $filter_name, implode( ',', $data['terms'] ), $link );
}
if ( 'or' === $data['query_type'] ) {
$link = add_query_arg( 'query_type_' . $filter_name, 'or', $link );
}
}
}
return $link;
}
/**
* Gets the currently selected attributes
*
* @return array
*/
public function get_chosen_attributes() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( ! empty( $_GET['filter_product_brand'] ) ) {
$filter_product_brand = wc_clean( wp_unslash( $_GET['filter_product_brand'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
return array_map( 'intval', explode( ',', $filter_product_brand ) );
}
return array();
}
/**
* Show dropdown layered nav.
*
* @param array $terms Terms.
* @param string $taxonomy Taxonomy.
* @param int $depth Depth.
* @return bool Will nav display?
*/
protected function layered_nav_dropdown( $terms, $taxonomy, $depth = 0 ) {
$found = false;
if ( $taxonomy !== $this->get_current_taxonomy() ) {
$term_counts = $this->get_filtered_term_product_counts( wp_list_pluck( $terms, 'term_id' ), $taxonomy, 'or' );
$_chosen_attributes = $this->get_chosen_attributes();
if ( 0 === $depth ) {
echo '<select class="wc-brand-dropdown-layered-nav-' . esc_attr( $taxonomy ) . '">';
echo '<option value="">' . esc_html__( 'Any Brand', 'woocommerce' ) . '</option>';
}
foreach ( $terms as $term ) {
// If on a term page, skip that term in widget list.
if ( $term->term_id === $this->get_current_term_id() ) {
continue;
}
// Get count based on current view.
$current_values = ! empty( $_chosen_attributes ) ? $_chosen_attributes : array();
$option_is_set = in_array( $term->term_id, $current_values, true );
$count = isset( $term_counts[ $term->term_id ] ) ? $term_counts[ $term->term_id ] : 0;
// Only show options with count > 0.
if ( 0 < $count ) {
$found = true;
} elseif ( 0 === $count && ! $option_is_set ) {
continue;
}
echo '<option value="' . esc_attr( $term->term_id ) . '" ' . selected( $option_is_set, true, false ) . '>' . esc_html( str_repeat( '&nbsp;', 2 * $depth ) . $term->name ) . '</option>';
$child_terms = get_terms(
array(
'taxonomy' => $taxonomy,
'hide_empty' => true,
'parent' => $term->term_id,
)
);
if ( ! empty( $child_terms ) ) {
$found |= $this->layered_nav_dropdown( $child_terms, $taxonomy, $depth + 1 );
}
}
if ( 0 === $depth ) {
$link = $this->get_page_base_url( $taxonomy );
echo '</select>';
$handle = 'wc-brand-widget-dropdown-layered-nav-' . $taxonomy;
wp_register_script( $handle, '', array(), WC_VERSION, array( 'in_footer' => true ) );
wp_enqueue_script( $handle );
$redirect_url = add_query_arg( 'filtering', '1', preg_replace( '%\/page\/[0-9]+%', '', esc_url_raw( $link ) ) );
wp_add_inline_script(
$handle,
"
(function() {
'use strict';
const dropdown = document.querySelector( '.wc-brand-dropdown-layered-nav-" . esc_js( $taxonomy ) . "' );
if ( dropdown ) {
dropdown.addEventListener( 'change', function() {
const slug = this.value;
location.href = '" . esc_js( $redirect_url ) . '&filter_' . esc_js( $taxonomy ) . "=' + slug;
} );
}
})();
"
);
}
}
return $found;
}
/**
* Show list based layered nav.
*
* @param array $terms Terms.
* @param string $taxonomy Taxonomy.
* @param int $depth Depth.
* @return bool Will nav display?
*/
protected function layered_nav_list( $terms, $taxonomy, $depth = 0 ) {
// List display.
echo '<ul class="' . ( 0 === $depth ? '' : 'children ' ) . 'wc-brand-list-layered-nav-' . esc_attr( $taxonomy ) . '">';
$term_counts = $this->get_filtered_term_product_counts( wp_list_pluck( $terms, 'term_id' ), $taxonomy, 'or' );
$_chosen_attributes = $this->get_chosen_attributes();
$current_values = ! empty( $_chosen_attributes ) ? $_chosen_attributes : array();
$found = false;
$filter_name = 'filter_' . $taxonomy;
foreach ( $terms as $term ) {
$option_is_set = in_array( $term->term_id, $current_values, true );
$count = isset( $term_counts[ $term->term_id ] ) ? $term_counts[ $term->term_id ] : 0;
// skip the term for the current archive.
if ( $this->get_current_term_id() === $term->term_id ) {
continue;
}
// Only show options with count > 0.
if ( 0 < $count ) {
$found = true;
} elseif ( 0 === $count && ! $option_is_set ) {
continue;
}
$current_filter = isset( $_GET[ $filter_name ] ) ? explode( ',', wc_clean( wp_unslash( $_GET[ $filter_name ] ) ) ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$current_filter = array_map( 'intval', $current_filter );
if ( ! in_array( $term->term_id, $current_filter, true ) ) {
$current_filter[] = $term->term_id;
}
$link = $this->get_page_base_url( $taxonomy );
// Add current filters to URL.
foreach ( $current_filter as $key => $value ) {
// Exclude query arg for current term archive term.
if ( $value === $this->get_current_term_id() ) {
unset( $current_filter[ $key ] );
}
// Exclude self so filter can be unset on click.
if ( $option_is_set && $value === $term->term_id ) {
unset( $current_filter[ $key ] );
}
}
if ( ! empty( $current_filter ) ) {
$link = add_query_arg(
array(
'filtering' => '1',
$filter_name => implode( ',', $current_filter ),
),
$link
);
}
echo '<li class="wc-layered-nav-term ' . ( $option_is_set ? 'chosen' : '' ) . '">';
echo ( $count > 0 || $option_is_set ) ? '<a href="' . esc_url( apply_filters( 'woocommerce_layered_nav_link', $link ) ) . '">' : '<span>'; // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
echo esc_html( $term->name );
echo ( $count > 0 || $option_is_set ) ? '</a> ' : '</span> ';
echo wp_kses_post( apply_filters( 'woocommerce_layered_nav_count', '<span class="count">(' . absint( $count ) . ')</span>', $count, $term ) );// phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
$child_terms = get_terms(
array(
'taxonomy' => $taxonomy,
'hide_empty' => true,
'parent' => $term->term_id,
)
);
if ( ! empty( $child_terms ) ) {
$found |= $this->layered_nav_list( $child_terms, $taxonomy, $depth + 1 );
}
echo '</li>';
}
echo '</ul>';
return $found;
}
/**
* Count products within certain terms, taking the main WP query into consideration.
*
* @param array $term_ids Term IDs.
* @param string $taxonomy Taxonomy.
* @param string $query_type Query type.
* @return array
*/
protected function get_filtered_term_product_counts( $term_ids, $taxonomy, $query_type = 'and' ) {
global $wpdb;
$tax_query = WC_Query::get_main_tax_query();
$meta_query = WC_Query::get_main_meta_query();
if ( 'or' === $query_type ) {
foreach ( $tax_query as $key => $query ) {
if ( is_array( $query ) && $taxonomy === $query['taxonomy'] ) {
unset( $tax_query[ $key ] );
}
}
}
$meta_query = new WP_Meta_Query( $meta_query );
$tax_query = new WP_Tax_Query( $tax_query );
$meta_query_sql = $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );
$tax_query_sql = $tax_query->get_sql( $wpdb->posts, 'ID' );
// Generate query.
$query = array();
$query['select'] = "SELECT COUNT( DISTINCT {$wpdb->posts}.ID ) as term_count, terms.term_id as term_count_id";
$query['from'] = "FROM {$wpdb->posts}";
$query['join'] = "
INNER JOIN {$wpdb->term_relationships} AS term_relationships ON {$wpdb->posts}.ID = term_relationships.object_id
INNER JOIN {$wpdb->term_taxonomy} AS term_taxonomy USING( term_taxonomy_id )
INNER JOIN {$wpdb->terms} AS terms USING( term_id )
" . $tax_query_sql['join'] . $meta_query_sql['join'];
$query['where'] = "
WHERE {$wpdb->posts}.post_type IN ( 'product' )
AND {$wpdb->posts}.post_status = 'publish'
" . $tax_query_sql['where'] . $meta_query_sql['where'] . '
AND terms.term_id IN (' . implode( ',', array_map( 'absint', $term_ids ) ) . ')
';
$query['group_by'] = 'GROUP BY terms.term_id';
$query = apply_filters( 'woocommerce_get_filtered_term_product_counts_query', $query ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
$query = implode( ' ', $query );
// We have a query - let's see if cached results of this query already exist.
$query_hash = md5( $query );
$cache = apply_filters( 'woocommerce_layered_nav_count_maybe_cache', true ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
if ( true === $cache ) {
$cached_counts = (array) get_transient( 'wc_layered_nav_counts_' . sanitize_title( $taxonomy ) );
} else {
$cached_counts = array();
}
if ( ! isset( $cached_counts[ $query_hash ] ) ) {
$results = $wpdb->get_results( $query, ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$counts = array_map( 'absint', wp_list_pluck( $results, 'term_count', 'term_count_id' ) );
$cached_counts[ $query_hash ] = $counts;
if ( true === $cache ) {
set_transient( 'wc_layered_nav_counts_' . sanitize_title( $taxonomy ), $cached_counts, HOUR_IN_SECONDS );
}
}
return array_map( 'absint', (array) $cached_counts[ $query_hash ] );
}
}

View File

@@ -0,0 +1,235 @@
<?php
declare( strict_types = 1);
/**
* Brand Thumbnails Widget
*
* Show brand images as thumbnails
*
* Important: For internal use only by the Automattic\WooCommerce\Internal\Brands package.
*
* @package WooCommerce\Widgets
* @version 9.4.0
*/
class WC_Widget_Brand_Thumbnails extends WP_Widget {
/**
* Widget CSS class.
*
* @var string
*/
public $woo_widget_cssclass;
/**
* Widget description.
*
* @var string
*/
public $woo_widget_description;
/**
* Widget id base.
*
* @var string
*/
public $woo_widget_idbase;
/**
* Widget name.
*
* @var string
*/
public $woo_widget_name;
/** Constructor */
public function __construct() {
/* Widget variable settings. */
$this->woo_widget_name = __( 'WooCommerce Brand Thumbnails', 'woocommerce' );
$this->woo_widget_description = __( 'Show a grid of brand thumbnails.', 'woocommerce' );
$this->woo_widget_idbase = 'wc_brands_brand_thumbnails';
$this->woo_widget_cssclass = 'widget_brand_thumbnails';
/* Widget settings. */
$widget_ops = array(
'classname' => $this->woo_widget_cssclass,
'description' => $this->woo_widget_description,
);
/* Create the widget. */
parent::__construct( $this->woo_widget_idbase, $this->woo_widget_name, $widget_ops );
}
/**
* Echoes the widget content.
*
* @see WP_Widget
*
* @param array $args Display arguments including 'before_title', 'after_title',
* 'before_widget', and 'after_widget'.
* @param array $instance The settings for the particular instance of the widget.
*/
public function widget( $args, $instance ) {
$instance = wp_parse_args(
$instance,
array(
'title' => '',
'columns' => 1,
'exclude' => '',
'orderby' => 'name',
'hide_empty' => 0,
'number' => '',
)
);
$exclude = array_map( 'intval', explode( ',', $instance['exclude'] ) );
$order = 'name' === $instance['orderby'] ? 'asc' : 'desc';
$brands = get_terms(
array(
'taxonomy' => 'product_brand',
'hide_empty' => $instance['hide_empty'],
'orderby' => $instance['orderby'],
'exclude' => $exclude,
'number' => $instance['number'],
'order' => $order,
)
);
if ( ! $brands ) {
return;
}
/**
* Filter the widget's title.
*
* @since 9.4.0
*
* @param string $title Widget title
* @param array $instance The settings for the particular instance of the widget.
* @param string $woo_widget_idbase The widget's id base.
*/
$title = apply_filters( 'widget_title', $instance['title'], $instance, $this->woo_widget_idbase );
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput
if ( '' !== $title ) {
echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput
}
wc_get_template(
'widgets/brand-thumbnails.php',
array(
'brands' => $brands,
'columns' => (int) $instance['columns'],
'fluid_columns' => ! empty( $instance['fluid_columns'] ) ? true : false,
),
'woocommerce',
WC()->plugin_path() . '/templates/brands/'
);
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput
}
/**
* Update widget instance.
*
* @param array $new_instance The new settings for the particular instance of the widget.
* @param array $old_instance The old settings for the particular instance of the widget.
*
* @see WP_Widget->update
*/
public function update( $new_instance, $old_instance ) {
$instance['title'] = wp_strip_all_tags( stripslashes( $new_instance['title'] ) );
$instance['columns'] = wp_strip_all_tags( stripslashes( $new_instance['columns'] ) );
$instance['fluid_columns'] = ! empty( $new_instance['fluid_columns'] ) ? true : false;
$instance['orderby'] = wp_strip_all_tags( stripslashes( $new_instance['orderby'] ) );
$instance['exclude'] = wp_strip_all_tags( stripslashes( $new_instance['exclude'] ) );
$instance['hide_empty'] = wp_strip_all_tags( stripslashes( (string) $new_instance['hide_empty'] ) );
$instance['number'] = wp_strip_all_tags( stripslashes( $new_instance['number'] ) );
if ( ! $instance['columns'] ) {
$instance['columns'] = 1;
}
if ( ! $instance['orderby'] ) {
$instance['orderby'] = 'name';
}
if ( ! $instance['exclude'] ) {
$instance['exclude'] = '';
}
if ( ! $instance['hide_empty'] ) {
$instance['hide_empty'] = 0;
}
if ( ! $instance['number'] ) {
$instance['number'] = '';
}
return $instance;
}
/**
* Outputs the settings update form.
*
* @param array $instance Current settings.
*/
public function form( $instance ) {
if ( ! isset( $instance['hide_empty'] ) ) {
$instance['hide_empty'] = 0;
}
if ( ! isset( $instance['orderby'] ) ) {
$instance['orderby'] = 'name';
}
if ( empty( $instance['fluid_columns'] ) ) {
$instance['fluid_columns'] = false;
}
?>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'woocommerce' ); ?></label>
<input type="text" class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" value="<?php echo isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : ''; ?>" />
</p>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'columns' ) ); ?>"><?php esc_html_e( 'Columns:', 'woocommerce' ); ?></label>
<input type="text" class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'columns' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'columns' ) ); ?>" value="<?php echo isset( $instance['columns'] ) ? esc_attr( $instance['columns'] ) : '1'; ?>" />
</p>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'fluid_columns' ) ); ?>"><?php esc_html_e( 'Fluid columns:', 'woocommerce' ); ?></label>
<input type="checkbox" <?php checked( $instance['fluid_columns'] ); ?> id="<?php echo esc_attr( $this->get_field_id( 'fluid_columns' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'fluid_columns' ) ); ?>" />
</p>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'number' ) ); ?>"><?php esc_html_e( 'Number:', 'woocommerce' ); ?></label>
<input type="text" class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'number' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'number' ) ); ?>" value="<?php if ( isset ( $instance['number'] ) ) { echo esc_attr( $instance['number'] ); } // phpcs:ignore ?>" placeholder="<?php esc_attr_e( 'All', 'woocommerce' ); ?>" />
</p>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'exclude' ) ); ?>"><?php esc_html_e( 'Exclude:', 'woocommerce' ); ?></label>
<input type="text" class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'exclude' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'exclude' ) ); ?>" value="<?php if ( isset ( $instance['exclude'] ) ) { echo esc_attr( $instance['exclude'] ); } // phpcs:ignore ?>" placeholder="<?php esc_attr_e( 'None', 'woocommerce' ); ?>" />
</p>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'hide_empty' ) ); ?>"><?php esc_html_e( 'Hide empty brands:', 'woocommerce' ); ?></label>
<select id="<?php echo esc_attr( $this->get_field_id( 'hide_empty' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'hide_empty' ) ); ?>">
<option value="1" <?php selected( $instance['hide_empty'], 1 ); ?>><?php esc_html_e( 'Yes', 'woocommerce' ); ?></option>
<option value="0" <?php selected( $instance['hide_empty'], 0 ); ?>><?php esc_html_e( 'No', 'woocommerce' ); ?></option>
</select>
</p>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'orderby' ) ); ?>"><?php esc_html_e( 'Order by:', 'woocommerce' ); ?></label>
<select id="<?php echo esc_attr( $this->get_field_id( 'orderby' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'orderby' ) ); ?>">
<option value="name" <?php selected( $instance['orderby'], 'name' ); ?>><?php esc_html_e( 'Name', 'woocommerce' ); ?></option>
<option value="count" <?php selected( $instance['orderby'], 'count' ); ?>><?php esc_html_e( 'Count', 'woocommerce' ); ?></option>
</select>
</p>
<?php
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Shopping Cart Widget.
*
* Displays shopping cart widget.
*
* @package WooCommerce\Widgets
* @version 2.3.0
*/
defined( 'ABSPATH' ) || exit;
/**
* Widget cart class.
*/
class WC_Widget_Cart extends WC_Widget {
/**
* Constructor.
*/
public function __construct() {
$this->widget_cssclass = 'woocommerce widget_shopping_cart';
$this->widget_description = __( 'Display the customer shopping cart.', 'woocommerce' );
$this->widget_id = 'woocommerce_widget_cart';
$this->widget_name = __( 'Cart', 'woocommerce' );
$this->settings = array(
'title' => array(
'type' => 'text',
'std' => __( 'Cart', 'woocommerce' ),
'label' => __( 'Title', 'woocommerce' ),
),
'hide_if_empty' => array(
'type' => 'checkbox',
'std' => 0,
'label' => __( 'Hide if cart is empty', 'woocommerce' ),
),
);
if ( is_customize_preview() ) {
wp_enqueue_script( 'wc-cart-fragments' );
}
parent::__construct();
}
/**
* Output widget.
*
* @see WP_Widget
*
* @param array $args Arguments.
* @param array $instance Widget instance.
*/
public function widget( $args, $instance ) {
if ( apply_filters( 'woocommerce_widget_cart_is_hidden', is_cart() || is_checkout() ) ) {
return;
}
wp_enqueue_script( 'wc-cart-fragments' );
$hide_if_empty = empty( $instance['hide_if_empty'] ) ? 0 : 1;
if ( ! isset( $instance['title'] ) ) {
$instance['title'] = __( 'Cart', 'woocommerce' );
}
$this->widget_start( $args, $instance );
if ( $hide_if_empty ) {
echo '<div class="hide_cart_widget_if_empty">';
}
// Insert cart widget placeholder - code in woocommerce.js will update this on page load.
echo '<div class="widget_shopping_cart_content"></div>';
if ( $hide_if_empty ) {
echo '</div>';
}
$this->widget_end( $args );
}
}

View File

@@ -0,0 +1,142 @@
<?php
/**
* Layered Navigation Filters Widget.
*
* @package WooCommerce\Widgets
* @version 2.3.0
*/
defined( 'ABSPATH' ) || exit;
/**
* Widget layered nav filters.
*/
class WC_Widget_Layered_Nav_Filters extends WC_Widget {
/**
* Constructor.
*/
public function __construct() {
$this->widget_cssclass = 'woocommerce widget_layered_nav_filters';
$this->widget_description = __( 'Display a list of active product filters.', 'woocommerce' );
$this->widget_id = 'woocommerce_layered_nav_filters';
$this->widget_name = __( 'Active Product Filters', 'woocommerce' );
$this->settings = array(
'title' => array(
'type' => 'text',
'std' => __( 'Active filters', 'woocommerce' ),
'label' => __( 'Title', 'woocommerce' ),
),
);
parent::__construct();
}
/**
* Output widget.
*
* @see WP_Widget
* @param array $args Arguments.
* @param array $instance Widget instance.
*/
public function widget( $args, $instance ) {
if ( ! is_shop() && ! is_product_taxonomy() ) {
return;
}
$_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes();
$min_price = isset( $_GET['min_price'] ) ? wc_clean( wp_unslash( $_GET['min_price'] ) ) : 0; // WPCS: input var ok, CSRF ok.
$max_price = isset( $_GET['max_price'] ) ? wc_clean( wp_unslash( $_GET['max_price'] ) ) : 0; // WPCS: input var ok, CSRF ok.
$rating_filter = isset( $_GET['rating_filter'] ) ? array_filter( array_map( 'absint', explode( ',', wp_unslash( $_GET['rating_filter'] ) ) ) ) : array(); // WPCS: sanitization ok, input var ok, CSRF ok.
$base_link = $this->get_current_page_url();
if ( 0 < count( $_chosen_attributes ) || 0 < $min_price || 0 < $max_price || ! empty( $rating_filter ) ) {
$this->widget_start( $args, $instance );
echo '<ul>';
/**
* Allow 3rd party developers to add their own filters to start the Layered Navigation Filters Widget.
*
* @since 7.6.0
*
* @param array $args Arguments.
* @param array $instance Widget instance.
*/
do_action( 'woocommerce_widget_layered_nav_filters_start', $args, $instance );
// Attributes.
if ( ! empty( $_chosen_attributes ) ) {
foreach ( $_chosen_attributes as $taxonomy => $data ) {
foreach ( $data['terms'] as $term_slug ) {
$term = get_term_by( 'slug', $term_slug, $taxonomy );
if ( ! $term ) {
continue;
}
$filter_name = 'filter_' . wc_attribute_taxonomy_slug( $taxonomy );
$current_filter = isset( $_GET[ $filter_name ] ) ? explode( ',', wc_clean( wp_unslash( $_GET[ $filter_name ] ) ) ) : array(); // WPCS: input var ok, CSRF ok.
$current_filter = array_map( 'sanitize_title', $current_filter );
$new_filter = array_diff( $current_filter, array( $term_slug ) );
$link = remove_query_arg( array( 'add-to-cart', $filter_name ), $base_link );
if ( count( $new_filter ) > 0 ) {
$link = add_query_arg( $filter_name, implode( ',', $new_filter ), $link );
}
$filter_classes = array( 'chosen', 'chosen-' . sanitize_html_class( str_replace( 'pa_', '', $taxonomy ) ), 'chosen-' . sanitize_html_class( str_replace( 'pa_', '', $taxonomy ) . '-' . $term_slug ) );
/**
* Allows the attribute term name to be modified before being output.
*
* @param string $term_name The name of the term.
* @param WP_Term $term The term object.
* @param string $taxonomy The taxonomy name.
*
* @since 8.8.0
*/
$anchor_text = apply_filters( 'woocommerce_widget_layered_nav_term_anchor_text', $term->name, $term, $taxonomy );
echo '<li class="' . esc_attr( implode( ' ', $filter_classes ) ) . '"><a rel="nofollow" aria-label="' . esc_attr__( 'Remove filter', 'woocommerce' ) . '" href="' . esc_url( $link ) . '">' . esc_html( $anchor_text ) . '</a></li>';
}
}
}
if ( $min_price ) {
$link = remove_query_arg( 'min_price', $base_link );
/* translators: %s: minimum price */
echo '<li class="chosen"><a rel="nofollow" aria-label="' . esc_attr__( 'Remove filter', 'woocommerce' ) . '" href="' . esc_url( $link ) . '">' . sprintf( __( 'Min %s', 'woocommerce' ), wc_price( $min_price ) ) . '</a></li>'; // WPCS: XSS ok.
}
if ( $max_price ) {
$link = remove_query_arg( 'max_price', $base_link );
/* translators: %s: maximum price */
echo '<li class="chosen"><a rel="nofollow" aria-label="' . esc_attr__( 'Remove filter', 'woocommerce' ) . '" href="' . esc_url( $link ) . '">' . sprintf( __( 'Max %s', 'woocommerce' ), wc_price( $max_price ) ) . '</a></li>'; // WPCS: XSS ok.
}
if ( ! empty( $rating_filter ) ) {
foreach ( $rating_filter as $rating ) {
$link_ratings = implode( ',', array_diff( $rating_filter, array( $rating ) ) );
$link = $link_ratings ? add_query_arg( 'rating_filter', $link_ratings ) : remove_query_arg( 'rating_filter', $base_link );
/* translators: %s: rating */
echo '<li class="chosen"><a rel="nofollow" aria-label="' . esc_attr__( 'Remove filter', 'woocommerce' ) . '" href="' . esc_url( $link ) . '">' . sprintf( esc_html__( 'Rated %s out of 5', 'woocommerce' ), esc_html( $rating ) ) . '</a></li>';
}
}
/**
* Allow 3rd party developers to add their own filters to end the Layered Navigation Filters Widget.
*
* @since 7.6.0
*
* @param array $args Arguments.
* @param array $instance Widget instance.
*/
do_action( 'woocommerce_widget_layered_nav_filters_end', $args, $instance );
echo '</ul>';
$this->widget_end( $args );
}
}
}

View File

@@ -0,0 +1,473 @@
<?php
/**
* Layered nav widget
*
* @package WooCommerce\Widgets
* @version 2.6.0
*/
use Automattic\WooCommerce\Internal\ProductAttributesLookup\Filterer;
defined( 'ABSPATH' ) || exit;
/**
* Widget layered nav class.
*/
class WC_Widget_Layered_Nav extends WC_Widget {
/**
* Constructor.
*/
public function __construct() {
$this->widget_cssclass = 'woocommerce widget_layered_nav woocommerce-widget-layered-nav';
$this->widget_description = __( 'Display a list of attributes to filter products in your store.', 'woocommerce' );
$this->widget_id = 'woocommerce_layered_nav';
$this->widget_name = __( 'Filter Products by Attribute', 'woocommerce' );
parent::__construct();
}
/**
* Updates a particular instance of a widget.
*
* @see WP_Widget->update
*
* @param array $new_instance New Instance.
* @param array $old_instance Old Instance.
*
* @return array
*/
public function update( $new_instance, $old_instance ) {
$this->init_settings();
return parent::update( $new_instance, $old_instance );
}
/**
* Outputs the settings update form.
*
* @see WP_Widget->form
*
* @param array $instance Instance.
*/
public function form( $instance ) {
$this->init_settings();
parent::form( $instance );
}
/**
* Init settings after post types are registered.
*/
public function init_settings() {
$attribute_array = array();
$std_attribute = '';
$attribute_taxonomies = wc_get_attribute_taxonomies();
if ( ! empty( $attribute_taxonomies ) ) {
foreach ( $attribute_taxonomies as $tax ) {
if ( taxonomy_exists( wc_attribute_taxonomy_name( $tax->attribute_name ) ) ) {
$attribute_array[ $tax->attribute_name ] = $tax->attribute_name;
}
}
$std_attribute = current( $attribute_array );
}
$this->settings = array(
'title' => array(
'type' => 'text',
'std' => __( 'Filter by', 'woocommerce' ),
'label' => __( 'Title', 'woocommerce' ),
),
'attribute' => array(
'type' => 'select',
'std' => $std_attribute,
'label' => __( 'Attribute', 'woocommerce' ),
'options' => $attribute_array,
),
'display_type' => array(
'type' => 'select',
'std' => 'list',
'label' => __( 'Display type', 'woocommerce' ),
'options' => array(
'list' => __( 'List', 'woocommerce' ),
'dropdown' => __( 'Dropdown', 'woocommerce' ),
),
),
'query_type' => array(
'type' => 'select',
'std' => 'and',
'label' => __( 'Query type', 'woocommerce' ),
'options' => array(
'and' => __( 'AND', 'woocommerce' ),
'or' => __( 'OR', 'woocommerce' ),
),
),
);
}
/**
* Get this widgets taxonomy.
*
* @param array $instance Array of instance options.
* @return string
*/
protected function get_instance_taxonomy( $instance ) {
if ( isset( $instance['attribute'] ) ) {
return wc_attribute_taxonomy_name( $instance['attribute'] );
}
$attribute_taxonomies = wc_get_attribute_taxonomies();
if ( ! empty( $attribute_taxonomies ) ) {
foreach ( $attribute_taxonomies as $tax ) {
if ( taxonomy_exists( wc_attribute_taxonomy_name( $tax->attribute_name ) ) ) {
return wc_attribute_taxonomy_name( $tax->attribute_name );
}
}
}
return '';
}
/**
* Get this widgets query type.
*
* @param array $instance Array of instance options.
* @return string
*/
protected function get_instance_query_type( $instance ) {
return isset( $instance['query_type'] ) ? $instance['query_type'] : 'and';
}
/**
* Get this widgets display type.
*
* @param array $instance Array of instance options.
* @return string
*/
protected function get_instance_display_type( $instance ) {
return isset( $instance['display_type'] ) ? $instance['display_type'] : 'list';
}
/**
* Output widget.
*
* @see WP_Widget
*
* @param array $args Arguments.
* @param array $instance Instance.
*/
public function widget( $args, $instance ) {
if ( ! is_shop() && ! is_product_taxonomy() ) {
return;
}
$_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes();
$taxonomy = $this->get_instance_taxonomy( $instance );
$query_type = $this->get_instance_query_type( $instance );
$display_type = $this->get_instance_display_type( $instance );
if ( ! taxonomy_exists( $taxonomy ) ) {
return;
}
$terms = get_terms( $taxonomy, array( 'hide_empty' => '1' ) );
if ( 0 === count( $terms ) ) {
return;
}
ob_start();
$this->widget_start( $args, $instance );
if ( 'dropdown' === $display_type ) {
wp_enqueue_script( 'selectWoo' );
wp_enqueue_style( 'select2' );
$found = $this->layered_nav_dropdown( $terms, $taxonomy, $query_type );
} else {
$found = $this->layered_nav_list( $terms, $taxonomy, $query_type );
}
$this->widget_end( $args );
// Force found when option is selected - do not force found on taxonomy attributes.
if ( ! is_tax() && is_array( $_chosen_attributes ) && array_key_exists( $taxonomy, $_chosen_attributes ) ) {
$found = true;
}
if ( ! $found ) {
ob_end_clean();
} else {
echo ob_get_clean(); // @codingStandardsIgnoreLine
}
}
/**
* Return the currently viewed taxonomy name.
*
* @return string
*/
protected function get_current_taxonomy() {
return is_tax() ? get_queried_object()->taxonomy : '';
}
/**
* Return the currently viewed term ID.
*
* @return int
*/
protected function get_current_term_id() {
return absint( is_tax() ? get_queried_object()->term_id : 0 );
}
/**
* Return the currently viewed term slug.
*
* @return int
*/
protected function get_current_term_slug() {
return absint( is_tax() ? get_queried_object()->slug : 0 );
}
/**
* Show dropdown layered nav.
*
* @param array $terms Terms.
* @param string $taxonomy Taxonomy.
* @param string $query_type Query Type.
* @return bool Will nav display?
*/
protected function layered_nav_dropdown( $terms, $taxonomy, $query_type ) {
global $wp;
$found = false;
if ( $taxonomy !== $this->get_current_taxonomy() ) {
$term_counts = $this->get_filtered_term_product_counts( wp_list_pluck( $terms, 'term_id' ), $taxonomy, $query_type );
$_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes();
$taxonomy_filter_name = wc_attribute_taxonomy_slug( $taxonomy );
$taxonomy_label = wc_attribute_label( $taxonomy );
/* translators: %s: taxonomy name */
$any_label = apply_filters( 'woocommerce_layered_nav_any_label', sprintf( __( 'Any %s', 'woocommerce' ), $taxonomy_label ), $taxonomy_label, $taxonomy );
$multiple = 'or' === $query_type;
$current_values = isset( $_chosen_attributes[ $taxonomy ]['terms'] ) ? $_chosen_attributes[ $taxonomy ]['terms'] : array();
if ( '' === get_option( 'permalink_structure' ) ) {
$form_action = remove_query_arg( array( 'page', 'paged' ), add_query_arg( $wp->query_string, '', home_url( $wp->request ) ) );
} else {
$form_action = preg_replace( '%\/page/[0-9]+%', '', home_url( user_trailingslashit( $wp->request ) ) );
}
echo '<form method="get" action="' . esc_url( $form_action ) . '" class="woocommerce-widget-layered-nav-dropdown">';
echo '<select class="woocommerce-widget-layered-nav-dropdown dropdown_layered_nav_' . esc_attr( $taxonomy_filter_name ) . '"' . ( $multiple ? 'multiple="multiple"' : '' ) . '>';
echo '<option value="">' . esc_html( $any_label ) . '</option>';
foreach ( $terms as $term ) {
// If on a term page, skip that term in widget list.
if ( $term->term_id === $this->get_current_term_id() ) {
continue;
}
// Get count based on current view.
$option_is_set = in_array( $term->slug, $current_values, true );
$count = isset( $term_counts[ $term->term_id ] ) ? $term_counts[ $term->term_id ] : 0;
// Only show options with count > 0.
if ( 0 < $count ) {
$found = true;
} elseif ( 0 === $count && ! $option_is_set ) {
continue;
}
echo '<option value="' . esc_attr( urldecode( $term->slug ) ) . '" ' . selected( $option_is_set, true, false ) . '>' . esc_html( $term->name ) . '</option>';
}
echo '</select>';
if ( $multiple ) {
echo '<button class="woocommerce-widget-layered-nav-dropdown__submit" type="submit" value="' . esc_attr__( 'Apply', 'woocommerce' ) . '">' . esc_html__( 'Apply', 'woocommerce' ) . '</button>';
}
if ( 'or' === $query_type ) {
echo '<input type="hidden" name="query_type_' . esc_attr( $taxonomy_filter_name ) . '" value="or" />';
}
echo '<input type="hidden" name="filter_' . esc_attr( $taxonomy_filter_name ) . '" value="' . esc_attr( implode( ',', $current_values ) ) . '" />';
echo wc_query_string_form_fields( null, array( 'filter_' . $taxonomy_filter_name, 'query_type_' . $taxonomy_filter_name ), '', true ); // @codingStandardsIgnoreLine
echo '</form>';
$handle = 'wc-widget-dropdown-layered-nav-' . $taxonomy_filter_name;
wp_register_script( $handle, '', array( 'jquery' ), WC_VERSION, array( 'in_footer' => true ) );
wp_enqueue_script( $handle );
wp_add_inline_script(
$handle,
"
// Update value on change.
jQuery( '.dropdown_layered_nav_" . esc_js( $taxonomy_filter_name ) . "' ).on( 'change', function() {
var slug = jQuery( this ).val();
jQuery( ':input[name=\"filter_" . esc_js( $taxonomy_filter_name ) . "\"]' ).val( slug );
// Submit form on change if standard dropdown.
if ( ! jQuery( this ).attr( 'multiple' ) ) {
jQuery( this ).closest( 'form' ).trigger( 'submit' );
}
});
// Use Select2 enhancement if possible
if ( jQuery().selectWoo ) {
var wc_layered_nav_select = function() {
jQuery( '.dropdown_layered_nav_" . esc_js( $taxonomy_filter_name ) . "' ).selectWoo( {
placeholder: decodeURIComponent('" . rawurlencode( (string) wp_specialchars_decode( $any_label ) ) . "'),
minimumResultsForSearch: 5,
width: '100%',
allowClear: " . ( $multiple ? 'false' : 'true' ) . ",
language: {
noResults: function() {
return '" . esc_js( _x( 'No matches found', 'enhanced select', 'woocommerce' ) ) . "';
}
}
} );
};
wc_layered_nav_select();
}
"
);
}
return $found;
}
/**
* Count products within certain terms, taking the main WP query into consideration.
*
* This query allows counts to be generated based on the viewed products, not all products.
*
* @param array $term_ids Term IDs.
* @param string $taxonomy Taxonomy.
* @param string $query_type Query Type.
* @return array
*/
protected function get_filtered_term_product_counts( $term_ids, $taxonomy, $query_type ) {
return wc_get_container()->get( Filterer::class )->get_filtered_term_product_counts( $term_ids, $taxonomy, $query_type );
}
/**
* Wrapper for WC_Query::get_main_tax_query() to ease unit testing.
*
* @since 4.4.0
* @return array
*/
protected function get_main_tax_query() {
return WC_Query::get_main_tax_query();
}
/**
* Wrapper for WC_Query::get_main_search_query_sql() to ease unit testing.
*
* @since 4.4.0
* @return string
*/
protected function get_main_search_query_sql() {
return WC_Query::get_main_search_query_sql();
}
/**
* Wrapper for WC_Query::get_main_search_queryget_main_meta_query to ease unit testing.
*
* @since 4.4.0
* @return array
*/
protected function get_main_meta_query() {
return WC_Query::get_main_meta_query();
}
/**
* Show list based layered nav.
*
* @param array $terms Terms.
* @param string $taxonomy Taxonomy.
* @param string $query_type Query Type.
* @return bool Will nav display?
*/
protected function layered_nav_list( $terms, $taxonomy, $query_type ) {
// List display.
echo '<ul class="woocommerce-widget-layered-nav-list">';
$term_counts = $this->get_filtered_term_product_counts( wp_list_pluck( $terms, 'term_id' ), $taxonomy, $query_type );
$_chosen_attributes = WC_Query::get_layered_nav_chosen_attributes();
$found = false;
$base_link = $this->get_current_page_url();
foreach ( $terms as $term ) {
$current_values = isset( $_chosen_attributes[ $taxonomy ]['terms'] ) ? $_chosen_attributes[ $taxonomy ]['terms'] : array();
$option_is_set = in_array( $term->slug, $current_values, true );
$count = isset( $term_counts[ $term->term_id ] ) ? $term_counts[ $term->term_id ] : 0;
// Skip the term for the current archive.
if ( $this->get_current_term_id() === $term->term_id ) {
continue;
}
// Only show options with count > 0.
if ( 0 < $count ) {
$found = true;
} elseif ( 0 === $count && ! $option_is_set ) {
continue;
}
$filter_name = 'filter_' . wc_attribute_taxonomy_slug( $taxonomy );
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$current_filter = isset( $_GET[ $filter_name ] ) ? explode( ',', wc_clean( wp_unslash( $_GET[ $filter_name ] ) ) ) : array();
$current_filter = array_map( 'sanitize_title', $current_filter );
if ( ! in_array( $term->slug, $current_filter, true ) ) {
$current_filter[] = $term->slug;
}
$link = remove_query_arg( $filter_name, $base_link );
// Add current filters to URL.
foreach ( $current_filter as $key => $value ) {
// Exclude query arg for current term archive term.
if ( $value === $this->get_current_term_slug() ) {
unset( $current_filter[ $key ] );
}
// Exclude self so filter can be unset on click.
if ( $option_is_set && $value === $term->slug ) {
unset( $current_filter[ $key ] );
}
}
if ( ! empty( $current_filter ) ) {
asort( $current_filter );
$link = add_query_arg( $filter_name, implode( ',', $current_filter ), $link );
// Add Query type Arg to URL.
if ( 'or' === $query_type && ! ( 1 === count( $current_filter ) && $option_is_set ) ) {
$link = add_query_arg( 'query_type_' . wc_attribute_taxonomy_slug( $taxonomy ), 'or', $link );
}
$link = str_replace( '%2C', ',', $link );
}
if ( $count > 0 || $option_is_set ) {
$link = apply_filters( 'woocommerce_layered_nav_link', $link, $term, $taxonomy );
$term_html = '<a rel="nofollow" href="' . esc_url( $link ) . '">' . esc_html( $term->name ) . '</a>';
} else {
$link = false;
$term_html = '<span>' . esc_html( $term->name ) . '</span>';
}
$term_html .= ' ' . apply_filters( 'woocommerce_layered_nav_count', '<span class="count">(' . absint( $count ) . ')</span>', $count, $term );
echo '<li class="woocommerce-widget-layered-nav-list__item wc-layered-nav-term ' . ( $option_is_set ? 'woocommerce-widget-layered-nav-list__item--chosen chosen' : '' ) . '">';
// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.EscapeOutput.OutputNotEscaped
echo apply_filters( 'woocommerce_layered_nav_term_html', $term_html, $term, $link, $count );
echo '</li>';
}
echo '</ul>';
return $found;
}
}

View File

@@ -0,0 +1,189 @@
<?php
/**
* Price Filter Widget and related functions.
*
* Generates a range slider to filter products by price.
*
* @package WooCommerce\Widgets
* @version 2.3.0
*/
use Automattic\Jetpack\Constants;
defined( 'ABSPATH' ) || exit;
/**
* Widget price filter class.
*/
class WC_Widget_Price_Filter extends WC_Widget {
/**
* Constructor.
*/
public function __construct() {
$this->widget_cssclass = 'woocommerce widget_price_filter';
$this->widget_description = __( 'Display a slider to filter products in your store by price.', 'woocommerce' );
$this->widget_id = 'woocommerce_price_filter';
$this->widget_name = __( 'Filter Products by Price', 'woocommerce' );
$this->settings = array(
'title' => array(
'type' => 'text',
'std' => __( 'Filter by price', 'woocommerce' ),
'label' => __( 'Title', 'woocommerce' ),
),
);
$suffix = Constants::is_true( 'SCRIPT_DEBUG' ) ? '' : '.min';
$version = Constants::get_constant( 'WC_VERSION' );
wp_register_script( 'wc-accounting', WC()->plugin_url() . '/assets/js/accounting/accounting' . $suffix . '.js', array( 'jquery' ), '0.4.2', true );
// This script is deprecated, but we need to register it for backwards compatibility.
// This can be removed in WooCommerce 10.5.0.
wp_register_script( 'accounting', false, array( 'wc-accounting' ), '0.4.2', true );
wp_register_script( 'wc-jquery-ui-touchpunch', WC()->plugin_url() . '/assets/js/jquery-ui-touch-punch/jquery-ui-touch-punch' . $suffix . '.js', array( 'jquery-ui-slider' ), $version, true );
wp_register_script( 'wc-price-slider', WC()->plugin_url() . '/assets/js/frontend/price-slider' . $suffix . '.js', array( 'jquery-ui-slider', 'wc-jquery-ui-touchpunch', 'wc-accounting' ), $version, true );
wp_localize_script(
'wc-price-slider',
'woocommerce_price_slider_params',
array(
'currency_format_num_decimals' => 0,
'currency_format_symbol' => get_woocommerce_currency_symbol(),
'currency_format_decimal_sep' => esc_attr( wc_get_price_decimal_separator() ),
'currency_format_thousand_sep' => esc_attr( wc_get_price_thousand_separator() ),
'currency_format' => esc_attr( str_replace( array( '%1$s', '%2$s' ), array( '%s', '%v' ), get_woocommerce_price_format() ) ),
)
);
if ( is_customize_preview() ) {
wp_enqueue_script( 'wc-price-slider' );
}
parent::__construct();
}
/**
* Output widget.
*
* @see WP_Widget
*
* @param array $args Arguments.
* @param array $instance Widget instance.
*/
public function widget( $args, $instance ) {
global $wp;
// Requires lookup table added in 3.6.
if ( version_compare( get_option( 'woocommerce_db_version', null ), '3.6', '<' ) ) {
return;
}
if ( ! is_shop() && ! is_product_taxonomy() ) {
return;
}
// If there are not posts and we're not filtering, hide the widget.
if ( ! WC()->query->get_main_query()->post_count && ! isset( $_GET['min_price'] ) && ! isset( $_GET['max_price'] ) ) { // WPCS: input var ok, CSRF ok.
return;
}
wp_enqueue_script( 'wc-price-slider' );
// Round values to nearest 10 by default.
$step = max( apply_filters( 'woocommerce_price_filter_widget_step', 10 ), 1 );
// Find min and max price in current result set.
$prices = $this->get_filtered_price();
$min_price = $prices->min_price;
$max_price = $prices->max_price;
// Check to see if we should add taxes to the prices if store are excl tax but display incl.
$tax_display_mode = get_option( 'woocommerce_tax_display_shop' );
if ( wc_tax_enabled() && ! wc_prices_include_tax() && 'incl' === $tax_display_mode ) {
$tax_class = apply_filters( 'woocommerce_price_filter_widget_tax_class', '' ); // Uses standard tax class.
$tax_rates = WC_Tax::get_rates( $tax_class );
if ( $tax_rates ) {
$min_price += WC_Tax::get_tax_total( WC_Tax::calc_exclusive_tax( $min_price, $tax_rates ) );
$max_price += WC_Tax::get_tax_total( WC_Tax::calc_exclusive_tax( $max_price, $tax_rates ) );
}
}
$min_price = apply_filters( 'woocommerce_price_filter_widget_min_amount', floor( $min_price / $step ) * $step );
$max_price = apply_filters( 'woocommerce_price_filter_widget_max_amount', ceil( $max_price / $step ) * $step );
// If both min and max are equal, we don't need a slider.
if ( $min_price === $max_price ) {
return;
}
$current_min_price = isset( $_GET['min_price'] ) ? floor( floatval( wp_unslash( $_GET['min_price'] ) ) / $step ) * $step : $min_price; // WPCS: input var ok, CSRF ok.
$current_max_price = isset( $_GET['max_price'] ) ? ceil( floatval( wp_unslash( $_GET['max_price'] ) ) / $step ) * $step : $max_price; // WPCS: input var ok, CSRF ok.
$this->widget_start( $args, $instance );
if ( '' === get_option( 'permalink_structure' ) ) {
$form_action = remove_query_arg( array( 'page', 'paged', 'product-page' ), add_query_arg( $wp->query_string, '', home_url( $wp->request ) ) );
} else {
$form_action = preg_replace( '%\/page/[0-9]+%', '', home_url( trailingslashit( $wp->request ) ) );
}
wc_get_template(
'content-widget-price-filter.php',
array(
'form_action' => $form_action,
'step' => $step,
'min_price' => $min_price,
'max_price' => $max_price,
'current_min_price' => $current_min_price,
'current_max_price' => $current_max_price,
)
);
$this->widget_end( $args );
}
/**
* Get filtered min price for current products.
*
* @return int
*/
protected function get_filtered_price() {
global $wpdb;
$args = WC()->query->get_main_query()->query_vars;
$tax_query = isset( $args['tax_query'] ) ? $args['tax_query'] : array();
$meta_query = isset( $args['meta_query'] ) ? $args['meta_query'] : array();
if ( ! is_post_type_archive( 'product' ) && ! empty( $args['taxonomy'] ) && ! empty( $args['term'] ) ) {
$tax_query[] = WC()->query->get_main_tax_query();
}
foreach ( $meta_query + $tax_query as $key => $query ) {
if ( ! empty( $query['price_filter'] ) || ! empty( $query['rating_filter'] ) ) {
unset( $meta_query[ $key ] );
}
}
$meta_query = new WP_Meta_Query( $meta_query );
$tax_query = new WP_Tax_Query( $tax_query );
$search = WC_Query::get_main_search_query_sql();
$meta_query_sql = $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );
$tax_query_sql = $tax_query->get_sql( $wpdb->posts, 'ID' );
$search_query_sql = $search ? ' AND ' . $search : '';
$sql = "
SELECT min( min_price ) as min_price, MAX( max_price ) as max_price
FROM {$wpdb->wc_product_meta_lookup}
WHERE product_id IN (
SELECT ID FROM {$wpdb->posts}
" . $tax_query_sql['join'] . $meta_query_sql['join'] . "
WHERE {$wpdb->posts}.post_type IN ('" . implode( "','", array_map( 'esc_sql', apply_filters( 'woocommerce_price_filter_post_type', array( 'product' ) ) ) ) . "')
AND {$wpdb->posts}.post_status = 'publish'
" . $tax_query_sql['where'] . $meta_query_sql['where'] . $search_query_sql . '
)';
$sql = apply_filters( 'woocommerce_price_filter_sql', $sql, $meta_query_sql, $tax_query_sql );
return $wpdb->get_row( $sql ); // WPCS: unprepared SQL ok.
}
}

View File

@@ -0,0 +1,301 @@
<?php
/**
* Product Categories Widget
*
* @package WooCommerce\Widgets
* @version 2.3.0
*/
defined( 'ABSPATH' ) || exit;
/**
* Product categories widget class.
*
* @extends WC_Widget
*/
class WC_Widget_Product_Categories extends WC_Widget {
/**
* Category ancestors.
*
* @var array
*/
public $cat_ancestors;
/**
* Current Category.
*
* @var bool
*/
public $current_cat;
/**
* Constructor.
*/
public function __construct() {
$this->widget_cssclass = 'woocommerce widget_product_categories';
$this->widget_description = __( 'A list or dropdown of product categories.', 'woocommerce' );
$this->widget_id = 'woocommerce_product_categories';
$this->widget_name = __( 'Product Categories', 'woocommerce' );
$this->settings = array(
'title' => array(
'type' => 'text',
'std' => __( 'Product categories', 'woocommerce' ),
'label' => __( 'Title', 'woocommerce' ),
),
'orderby' => array(
'type' => 'select',
'std' => 'name',
'label' => __( 'Order by', 'woocommerce' ),
'options' => array(
'order' => __( 'Category order', 'woocommerce' ),
'name' => __( 'Name', 'woocommerce' ),
),
),
'dropdown' => array(
'type' => 'checkbox',
'std' => 0,
'label' => __( 'Show as dropdown', 'woocommerce' ),
),
'count' => array(
'type' => 'checkbox',
'std' => 0,
'label' => __( 'Show product counts', 'woocommerce' ),
),
'hierarchical' => array(
'type' => 'checkbox',
'std' => 1,
'label' => __( 'Show hierarchy', 'woocommerce' ),
),
'show_children_only' => array(
'type' => 'checkbox',
'std' => 0,
'label' => __( 'Only show children of the current category', 'woocommerce' ),
),
'hide_empty' => array(
'type' => 'checkbox',
'std' => 0,
'label' => __( 'Hide empty categories', 'woocommerce' ),
),
'max_depth' => array(
'type' => 'text',
'std' => '',
'label' => __( 'Maximum depth', 'woocommerce' ),
),
);
parent::__construct();
}
/**
* Output widget.
*
* @see WP_Widget
* @param array $args Widget arguments.
* @param array $instance Widget instance.
*/
public function widget( $args, $instance ) {
global $wp_query, $post;
$count = isset( $instance['count'] ) ? $instance['count'] : $this->settings['count']['std'];
$hierarchical = isset( $instance['hierarchical'] ) ? $instance['hierarchical'] : $this->settings['hierarchical']['std'];
$show_children_only = isset( $instance['show_children_only'] ) ? $instance['show_children_only'] : $this->settings['show_children_only']['std'];
$dropdown = isset( $instance['dropdown'] ) ? $instance['dropdown'] : $this->settings['dropdown']['std'];
$orderby = isset( $instance['orderby'] ) ? $instance['orderby'] : $this->settings['orderby']['std'];
$hide_empty = isset( $instance['hide_empty'] ) ? $instance['hide_empty'] : $this->settings['hide_empty']['std'];
$dropdown_args = array(
'hide_empty' => $hide_empty,
);
$list_args = array(
'show_count' => $count,
'hierarchical' => $hierarchical,
'taxonomy' => 'product_cat',
'hide_empty' => $hide_empty,
);
$max_depth = absint( isset( $instance['max_depth'] ) ? $instance['max_depth'] : $this->settings['max_depth']['std'] );
$list_args['menu_order'] = false;
$dropdown_args['depth'] = $max_depth;
$list_args['depth'] = $max_depth;
if ( 'order' === $orderby ) {
$list_args['orderby'] = 'meta_value_num';
$dropdown_args['orderby'] = 'meta_value_num';
$list_args['meta_key'] = 'order';
$dropdown_args['meta_key'] = 'order';
}
$this->current_cat = false;
$this->cat_ancestors = array();
if ( is_tax( 'product_cat' ) ) {
$this->current_cat = $wp_query->queried_object;
$this->cat_ancestors = get_ancestors( $this->current_cat->term_id, 'product_cat' );
} elseif ( is_singular( 'product' ) ) {
$terms = wc_get_product_terms(
$post->ID,
'product_cat',
apply_filters(
'woocommerce_product_categories_widget_product_terms_args',
array(
'orderby' => 'parent',
'order' => 'DESC',
)
)
);
if ( $terms ) {
$main_term = apply_filters( 'woocommerce_product_categories_widget_main_term', $terms[0], $terms );
$this->current_cat = $main_term;
$this->cat_ancestors = get_ancestors( $main_term->term_id, 'product_cat' );
}
}
// Show Siblings and Children Only.
if ( $show_children_only && $this->current_cat ) {
if ( $hierarchical ) {
$include = array_merge(
$this->cat_ancestors,
array( $this->current_cat->term_id ),
get_terms(
'product_cat',
array(
'fields' => 'ids',
'parent' => 0,
'hierarchical' => true,
'hide_empty' => false,
)
),
get_terms(
'product_cat',
array(
'fields' => 'ids',
'parent' => $this->current_cat->term_id,
'hierarchical' => true,
'hide_empty' => false,
)
)
);
// Gather siblings of ancestors.
if ( $this->cat_ancestors ) {
foreach ( $this->cat_ancestors as $ancestor ) {
$include = array_merge(
$include,
get_terms(
'product_cat',
array(
'fields' => 'ids',
'parent' => $ancestor,
'hierarchical' => false,
'hide_empty' => false,
)
)
);
}
}
} else {
// Direct children.
$include = get_terms(
'product_cat',
array(
'fields' => 'ids',
'parent' => $this->current_cat->term_id,
'hierarchical' => true,
'hide_empty' => false,
)
);
}
$list_args['include'] = implode( ',', $include );
$dropdown_args['include'] = $list_args['include'];
if ( empty( $include ) ) {
return;
}
} elseif ( $show_children_only ) {
$dropdown_args['depth'] = 1;
$dropdown_args['child_of'] = 0;
$dropdown_args['hierarchical'] = 1;
$list_args['depth'] = 1;
$list_args['child_of'] = 0;
$list_args['hierarchical'] = 1;
}
$this->widget_start( $args, $instance );
if ( $dropdown ) {
wc_product_dropdown_categories(
apply_filters(
'woocommerce_product_categories_widget_dropdown_args',
wp_parse_args(
$dropdown_args,
array(
'show_count' => $count,
'hierarchical' => $hierarchical,
'show_uncategorized' => 0,
'selected' => $this->current_cat ? $this->current_cat->slug : '',
)
)
)
);
$handle = 'wc-product-category-dropdown-widget';
wp_register_script( $handle, '', array( 'jquery', 'selectWoo' ), WC_VERSION, array( 'in_footer' => true ) );
wp_enqueue_style( 'select2' );
wp_enqueue_script( $handle );
wp_add_inline_script(
$handle,
"
jQuery( '.dropdown_product_cat' ).on( 'change', function() {
const categoryValue = jQuery(this).val();
if ( categoryValue ) {
const homeUrl = '" . esc_js( home_url( '/' ) ) . "';
const url = new URL( homeUrl, window.location.origin );
url.searchParams.set( 'product_cat', categoryValue );
location.href = url.toString();
} else {
location.href = '" . esc_js( wc_get_page_permalink( 'shop' ) ) . "';
}
});
if ( jQuery().selectWoo ) {
var wc_product_cat_select = function() {
jQuery( '.dropdown_product_cat' ).selectWoo( {
placeholder: '" . esc_js( __( 'Select a category', 'woocommerce' ) ) . "',
minimumResultsForSearch: 5,
width: '100%',
allowClear: true,
language: {
noResults: function() {
return '" . esc_js( _x( 'No matches found', 'enhanced select', 'woocommerce' ) ) . "';
}
}
} );
};
wc_product_cat_select();
}
"
);
} else {
include_once WC()->plugin_path() . '/includes/walkers/class-wc-product-cat-list-walker.php';
$list_args['walker'] = new WC_Product_Cat_List_Walker();
$list_args['title_li'] = '';
$list_args['pad_counts'] = 1;
$list_args['show_option_none'] = __( 'No product categories exist.', 'woocommerce' );
$list_args['current_category'] = ( $this->current_cat ) ? $this->current_cat->term_id : '';
$list_args['current_category_ancestors'] = $this->cat_ancestors;
$list_args['max_depth'] = $max_depth;
echo '<ul class="product-categories">';
wp_list_categories( apply_filters( 'woocommerce_product_categories_widget_args', $list_args ) );
echo '</ul>';
}
$this->widget_end( $args );
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* Product Search Widget.
*
* @package WooCommerce\Widgets
* @version 2.3.0
*/
defined( 'ABSPATH' ) || exit;
/**
* Widget product search class.
*/
class WC_Widget_Product_Search extends WC_Widget {
/**
* Constructor.
*/
public function __construct() {
$this->widget_cssclass = 'woocommerce widget_product_search';
$this->widget_description = __( 'A search form for your store.', 'woocommerce' );
$this->widget_id = 'woocommerce_product_search';
$this->widget_name = __( 'Product Search', 'woocommerce' );
$this->settings = array(
'title' => array(
'type' => 'text',
'std' => '',
'label' => __( 'Title', 'woocommerce' ),
),
);
parent::__construct();
}
/**
* Output widget.
*
* @see WP_Widget
*
* @param array $args Arguments.
* @param array $instance Widget instance.
*/
public function widget( $args, $instance ) {
$this->widget_start( $args, $instance );
get_product_search_form();
$this->widget_end( $args );
}
}

View File

@@ -0,0 +1,121 @@
<?php
/**
* Tag Cloud Widget.
*
* @package WooCommerce\Widgets
* @version 3.4.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Widget product tag cloud
*/
class WC_Widget_Product_Tag_Cloud extends WC_Widget {
/**
* Constructor.
*/
public function __construct() {
$this->widget_cssclass = 'woocommerce widget_product_tag_cloud';
$this->widget_description = __( 'A cloud of your most used product tags.', 'woocommerce' );
$this->widget_id = 'woocommerce_product_tag_cloud';
$this->widget_name = __( 'Product Tag Cloud', 'woocommerce' );
$this->settings = array(
'title' => array(
'type' => 'text',
'std' => __( 'Product tags', 'woocommerce' ),
'label' => __( 'Title', 'woocommerce' ),
),
);
parent::__construct();
}
/**
* Output widget.
*
* @see WP_Widget
*
* @param array $args Arguments.
* @param array $instance Widget instance.
*/
public function widget( $args, $instance ) {
$current_taxonomy = $this->get_current_taxonomy( $instance );
if ( empty( $instance['title'] ) ) {
$taxonomy = get_taxonomy( $current_taxonomy );
$instance['title'] = $taxonomy->labels->name;
}
$this->widget_start( $args, $instance );
echo '<div class="tagcloud">';
wp_tag_cloud(
apply_filters(
'woocommerce_product_tag_cloud_widget_args',
array(
'taxonomy' => $current_taxonomy,
'topic_count_text_callback' => array( $this, 'topic_count_text' ),
)
)
);
echo '</div>';
$this->widget_end( $args );
}
/**
* Return the taxonomy being displayed.
*
* @param object $instance Widget instance.
* @return string
*/
public function get_current_taxonomy( $instance ) {
return 'product_tag';
}
/**
* Returns topic count text.
*
* @since 3.4.0
* @param int $count Count text.
* @return string
*/
public function topic_count_text( $count ) {
/* translators: %s: product count */
return sprintf( _n( '%s product', '%s products', $count, 'woocommerce' ), number_format_i18n( $count ) );
}
// Ignore whole block to avoid warnings about PSR2.Methods.MethodDeclaration.Underscore violation.
// @codingStandardsIgnoreStart
/**
* Return the taxonomy being displayed.
*
* @deprecated 3.4.0
* @param object $instance Widget instance.
* @return string
*/
public function _get_current_taxonomy( $instance ) {
wc_deprecated_function( '_get_current_taxonomy', '3.4.0', 'WC_Widget_Product_Tag_Cloud->get_current_taxonomy' );
return $this->get_current_taxonomy( $instance );
}
/**
* Returns topic count text.
*
* @deprecated 3.4.0
* @since 2.6.0
* @param int $count Count text.
* @return string
*/
public function _topic_count_text( $count ) {
wc_deprecated_function( '_topic_count_text', '3.4.0', 'WC_Widget_Product_Tag_Cloud->topic_count_text' );
return $this->topic_count_text( $count );
}
// @codingStandardsIgnoreEnd
}

View File

@@ -0,0 +1,222 @@
<?php
/**
* List products. One widget to rule them all.
*
* @package WooCommerce\Widgets
* @version 3.3.0
*/
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Enums\ProductStockStatus;
/**
* Widget products.
*/
class WC_Widget_Products extends WC_Widget {
/**
* Constructor.
*/
public function __construct() {
$this->widget_cssclass = 'woocommerce widget_products';
$this->widget_description = __( "A list of your store's products.", 'woocommerce' );
$this->widget_id = 'woocommerce_products';
$this->widget_name = __( 'Products list', 'woocommerce' );
$this->settings = array(
'title' => array(
'type' => 'text',
'std' => __( 'Products', 'woocommerce' ),
'label' => __( 'Title', 'woocommerce' ),
),
'number' => array(
'type' => 'number',
'step' => 1,
'min' => 1,
'max' => '',
'std' => 5,
'label' => __( 'Number of products to show', 'woocommerce' ),
),
'show' => array(
'type' => 'select',
'std' => '',
'label' => __( 'Show', 'woocommerce' ),
'options' => array(
'' => __( 'All products', 'woocommerce' ),
'featured' => __( 'Featured products', 'woocommerce' ),
'onsale' => __( 'On-sale products', 'woocommerce' ),
),
),
'orderby' => array(
'type' => 'select',
'std' => 'date',
'label' => __( 'Order by', 'woocommerce' ),
'options' => array(
'menu_order' => __( 'Menu order', 'woocommerce' ),
'date' => __( 'Date', 'woocommerce' ),
'price' => __( 'Price', 'woocommerce' ),
'rand' => __( 'Random', 'woocommerce' ),
'sales' => __( 'Sales', 'woocommerce' ),
),
),
'order' => array(
'type' => 'select',
'std' => 'desc',
'label' => _x( 'Order', 'Sorting order', 'woocommerce' ),
'options' => array(
'asc' => __( 'ASC', 'woocommerce' ),
'desc' => __( 'DESC', 'woocommerce' ),
),
),
'hide_free' => array(
'type' => 'checkbox',
'std' => 0,
'label' => __( 'Hide free products', 'woocommerce' ),
),
'show_hidden' => array(
'type' => 'checkbox',
'std' => 0,
'label' => __( 'Show hidden products', 'woocommerce' ),
),
);
parent::__construct();
}
/**
* Query the products and return them.
*
* @param array $args Arguments.
* @param array $instance Widget instance.
*
* @return WP_Query
*/
public function get_products( $args, $instance ) {
$number = ! empty( $instance['number'] ) ? absint( $instance['number'] ) : $this->settings['number']['std'];
$show = ! empty( $instance['show'] ) ? sanitize_title( $instance['show'] ) : $this->settings['show']['std'];
$orderby = ! empty( $instance['orderby'] ) ? sanitize_title( $instance['orderby'] ) : $this->settings['orderby']['std'];
$order = ! empty( $instance['order'] ) ? sanitize_title( $instance['order'] ) : $this->settings['order']['std'];
$product_visibility_term_ids = wc_get_product_visibility_term_ids();
$query_args = array(
'posts_per_page' => $number,
'post_status' => 'publish',
'post_type' => 'product',
'no_found_rows' => 1,
'order' => $order,
'meta_query' => array(),
'tax_query' => array(
'relation' => 'AND',
),
); // WPCS: slow query ok.
if ( empty( $instance['show_hidden'] ) ) {
$query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'field' => 'term_taxonomy_id',
'terms' => is_search() ? $product_visibility_term_ids['exclude-from-search'] : $product_visibility_term_ids['exclude-from-catalog'],
'operator' => 'NOT IN',
);
$query_args['post_parent'] = 0;
}
if ( ! empty( $instance['hide_free'] ) ) {
$query_args['meta_query'][] = array(
'key' => '_price',
'value' => 0,
'compare' => '>',
'type' => 'DECIMAL',
);
}
if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
$query_args['tax_query'][] = array(
array(
'taxonomy' => 'product_visibility',
'field' => 'term_taxonomy_id',
'terms' => $product_visibility_term_ids[ ProductStockStatus::OUT_OF_STOCK ],
'operator' => 'NOT IN',
),
); // WPCS: slow query ok.
}
switch ( $show ) {
case 'featured':
$query_args['tax_query'][] = array(
'taxonomy' => 'product_visibility',
'field' => 'term_taxonomy_id',
'terms' => $product_visibility_term_ids['featured'],
);
break;
case 'onsale':
$product_ids_on_sale = wc_get_product_ids_on_sale();
$product_ids_on_sale[] = 0;
$query_args['post__in'] = $product_ids_on_sale;
break;
}
switch ( $orderby ) {
case 'menu_order':
$query_args['orderby'] = 'menu_order';
break;
case 'price':
$query_args['meta_key'] = '_price'; // WPCS: slow query ok.
$query_args['orderby'] = 'meta_value_num';
break;
case 'rand':
$query_args['orderby'] = 'rand';
break;
case 'sales':
$query_args['meta_key'] = 'total_sales'; // WPCS: slow query ok.
$query_args['orderby'] = 'meta_value_num';
break;
default:
$query_args['orderby'] = 'date';
}
return new WP_Query( apply_filters( 'woocommerce_products_widget_query_args', $query_args ) );
}
/**
* Output widget.
*
* @param array $args Arguments.
* @param array $instance Widget instance.
*
* @see WP_Widget
*/
public function widget( $args, $instance ) {
if ( $this->get_cached_widget( $args ) ) {
return;
}
ob_start();
wc_set_loop_prop( 'name', 'widget' );
$products = $this->get_products( $args, $instance );
if ( $products && $products->have_posts() ) {
$this->widget_start( $args, $instance );
echo wp_kses_post( apply_filters( 'woocommerce_before_widget_product_list', '<ul class="product_list_widget">' ) );
$template_args = array(
'widget_id' => isset( $args['widget_id'] ) ? $args['widget_id'] : $this->widget_id,
'show_rating' => true,
);
while ( $products->have_posts() ) {
$products->the_post();
wc_get_template( 'content-widget-product.php', $template_args );
}
echo wp_kses_post( apply_filters( 'woocommerce_after_widget_product_list', '</ul>' ) );
$this->widget_end( $args );
}
wp_reset_postdata();
echo $this->cache_widget( $args, ob_get_clean() ); // WPCS: XSS ok.
}
}

View File

@@ -0,0 +1,147 @@
<?php
/**
* Rating Filter Widget and related functions.
*
* @package WooCommerce\Widgets
* @version 2.6.0
*/
defined( 'ABSPATH' ) || exit;
/**
* Widget rating filter class.
*/
class WC_Widget_Rating_Filter extends WC_Widget {
/**
* Constructor.
*/
public function __construct() {
$this->widget_cssclass = 'woocommerce widget_rating_filter';
$this->widget_description = __( 'Display a list of star ratings to filter products in your store.', 'woocommerce' );
$this->widget_id = 'woocommerce_rating_filter';
$this->widget_name = __( 'Filter Products by Rating', 'woocommerce' );
$this->settings = array(
'title' => array(
'type' => 'text',
'std' => __( 'Average rating', 'woocommerce' ),
'label' => __( 'Title', 'woocommerce' ),
),
);
parent::__construct();
}
/**
* Count products after other filters have occurred by adjusting the main query.
*
* @param int $rating Rating.
* @return int
*/
protected function get_filtered_product_count( $rating ) {
global $wpdb;
$tax_query = WC_Query::get_main_tax_query();
$meta_query = WC_Query::get_main_meta_query();
// Unset current rating filter.
foreach ( $tax_query as $key => $query ) {
if ( ! empty( $query['rating_filter'] ) ) {
unset( $tax_query[ $key ] );
break;
}
}
// Set new rating filter.
$product_visibility_terms = wc_get_product_visibility_term_ids();
$tax_query[] = array(
'taxonomy' => 'product_visibility',
'field' => 'term_taxonomy_id',
'terms' => $product_visibility_terms[ 'rated-' . $rating ],
'operator' => 'IN',
'rating_filter' => true,
);
$meta_query = new WP_Meta_Query( $meta_query );
$tax_query = new WP_Tax_Query( $tax_query );
$meta_query_sql = $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );
$tax_query_sql = $tax_query->get_sql( $wpdb->posts, 'ID' );
$sql = "SELECT COUNT( DISTINCT {$wpdb->posts}.ID ) FROM {$wpdb->posts} ";
$sql .= $tax_query_sql['join'] . $meta_query_sql['join'];
$sql .= " WHERE {$wpdb->posts}.post_type = 'product' AND {$wpdb->posts}.post_status = 'publish' ";
$sql .= $tax_query_sql['where'] . $meta_query_sql['where'];
$search = WC_Query::get_main_search_query_sql();
if ( $search ) {
$sql .= ' AND ' . $search;
}
return absint( $wpdb->get_var( $sql ) ); // WPCS: unprepared SQL ok.
}
/**
* Widget function.
*
* @see WP_Widget
* @param array $args Arguments.
* @param array $instance Widget instance.
*/
public function widget( $args, $instance ) {
if ( ! is_shop() && ! is_product_taxonomy() ) {
return;
}
if ( ! WC()->query->get_main_query()->post_count ) {
return;
}
ob_start();
$found = false;
$rating_filter = isset( $_GET['rating_filter'] ) ? array_filter( array_map( 'absint', explode( ',', wp_unslash( $_GET['rating_filter'] ) ) ) ) : array(); // WPCS: input var ok, CSRF ok, sanitization ok.
$base_link = remove_query_arg( 'paged', $this->get_current_page_url() );
$this->widget_start( $args, $instance );
echo '<ul>';
for ( $rating = 5; $rating >= 1; $rating-- ) {
$count = $this->get_filtered_product_count( $rating );
if ( empty( $count ) ) {
continue;
}
$found = true;
$link = $base_link;
if ( in_array( $rating, $rating_filter, true ) ) {
$link_ratings = implode( ',', array_diff( $rating_filter, array( $rating ) ) );
} else {
$link_ratings = implode( ',', array_merge( $rating_filter, array( $rating ) ) );
}
$class = in_array( $rating, $rating_filter, true ) ? 'wc-layered-nav-rating chosen' : 'wc-layered-nav-rating';
$link = apply_filters( 'woocommerce_rating_filter_link', $link_ratings ? add_query_arg( 'rating_filter', $link_ratings, $link ) : remove_query_arg( 'rating_filter' ) );
$rating_html = wc_get_star_rating_html( $rating );
$count_html = wp_kses(
apply_filters( 'woocommerce_rating_filter_count', "({$count})", $count, $rating ),
array(
'em' => array(),
'span' => array(),
'strong' => array(),
)
);
printf( '<li class="%s"><a href="%s"><span class="star-rating">%s</span> %s</a></li>', esc_attr( $class ), esc_url( $link ), $rating_html, $count_html ); // WPCS: XSS ok.
}
echo '</ul>';
$this->widget_end( $args );
if ( ! $found ) {
ob_end_clean();
} else {
echo ob_get_clean(); // WPCS: XSS ok.
}
}
}

View File

@@ -0,0 +1,97 @@
<?php
/**
* Recent Reviews Widget.
*
* @package WooCommerce\Widgets
* @version 2.3.0
*/
defined( 'ABSPATH' ) || exit;
/**
* Widget recent reviews class.
*/
class WC_Widget_Recent_Reviews extends WC_Widget {
/**
* Constructor.
*/
public function __construct() {
$this->widget_cssclass = 'woocommerce widget_recent_reviews';
$this->widget_description = __( 'Display a list of recent reviews from your store.', 'woocommerce' );
$this->widget_id = 'woocommerce_recent_reviews';
$this->widget_name = __( 'Recent Product Reviews', 'woocommerce' );
$this->settings = array(
'title' => array(
'type' => 'text',
'std' => __( 'Recent reviews', 'woocommerce' ),
'label' => __( 'Title', 'woocommerce' ),
),
'number' => array(
'type' => 'number',
'step' => 1,
'min' => 1,
'max' => '',
'std' => 10,
'label' => __( 'Number of reviews to show', 'woocommerce' ),
),
);
parent::__construct();
}
/**
* Output widget.
*
* @see WP_Widget
* @param array $args Arguments.
* @param array $instance Widget instance.
*/
public function widget( $args, $instance ) {
global $comments, $comment;
if ( $this->get_cached_widget( $args ) ) {
return;
}
ob_start();
$number = ! empty( $instance['number'] ) ? absint( $instance['number'] ) : $this->settings['number']['std'];
$comments = get_comments(
array(
'number' => $number,
'status' => 'approve',
'post_status' => 'publish',
'post_type' => 'product',
'parent' => 0,
)
); // WPCS: override ok.
if ( $comments ) {
$this->widget_start( $args, $instance );
echo wp_kses_post( apply_filters( 'woocommerce_before_widget_product_review_list', '<ul class="product_list_widget">' ) );
foreach ( (array) $comments as $comment ) {
wc_get_template(
'content-widget-reviews.php',
array(
'comment' => $comment,
'product' => wc_get_product( $comment->comment_post_ID ),
)
);
}
echo wp_kses_post( apply_filters( 'woocommerce_after_widget_product_review_list', '</ul>' ) );
$this->widget_end( $args );
}
$content = ob_get_clean();
echo $content; // WPCS: XSS ok.
$this->cache_widget( $args, $content );
}
}

View File

@@ -0,0 +1,112 @@
<?php
/**
* Recent Products Widget.
*
* @package WooCommerce\Widgets
* @version 3.3.0
*/
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Enums\ProductStockStatus;
/**
* Widget recently viewed.
*/
class WC_Widget_Recently_Viewed extends WC_Widget {
/**
* Constructor.
*/
public function __construct() {
$this->widget_cssclass = 'woocommerce widget_recently_viewed_products';
$this->widget_description = __( "Display a list of a customer's recently viewed products.", 'woocommerce' );
$this->widget_id = 'woocommerce_recently_viewed_products';
$this->widget_name = __( 'Recently Viewed Products list', 'woocommerce' );
$this->settings = array(
'title' => array(
'type' => 'text',
'std' => __( 'Recently Viewed Products', 'woocommerce' ),
'label' => __( 'Title', 'woocommerce' ),
),
'number' => array(
'type' => 'number',
'step' => 1,
'min' => 1,
'max' => 15,
'std' => 10,
'label' => __( 'Number of products to show', 'woocommerce' ),
),
);
parent::__construct();
}
/**
* Output widget.
*
* @see WP_Widget
* @param array $args Arguments.
* @param array $instance Widget instance.
*/
public function widget( $args, $instance ) {
$viewed_products = ! empty( $_COOKIE['woocommerce_recently_viewed'] ) ? (array) explode( '|', wp_unslash( $_COOKIE['woocommerce_recently_viewed'] ) ) : array(); // @codingStandardsIgnoreLine
$viewed_products = array_reverse( array_filter( array_map( 'absint', $viewed_products ) ) );
if ( empty( $viewed_products ) ) {
return;
}
ob_start();
$number = ! empty( $instance['number'] ) ? absint( $instance['number'] ) : $this->settings['number']['std'];
$query_args = array(
'posts_per_page' => $number,
'no_found_rows' => 1,
'post_status' => 'publish',
'post_type' => 'product',
'post__in' => $viewed_products,
'orderby' => 'post__in',
);
if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
$query_args['tax_query'] = array(
array(
'taxonomy' => 'product_visibility',
'field' => 'name',
'terms' => ProductStockStatus::OUT_OF_STOCK,
'operator' => 'NOT IN',
),
); // WPCS: slow query ok.
}
$r = new WP_Query( apply_filters( 'woocommerce_recently_viewed_products_widget_query_args', $query_args ) );
if ( $r->have_posts() ) {
$this->widget_start( $args, $instance );
echo wp_kses_post( apply_filters( 'woocommerce_before_widget_product_list', '<ul class="product_list_widget">' ) );
$template_args = array(
'widget_id' => isset( $args['widget_id'] ) ? $args['widget_id'] : $this->widget_id,
);
while ( $r->have_posts() ) {
$r->the_post();
wc_get_template( 'content-widget-product.php', $template_args );
}
echo wp_kses_post( apply_filters( 'woocommerce_after_widget_product_list', '</ul>' ) );
$this->widget_end( $args );
}
wp_reset_postdata();
$content = ob_get_clean();
echo $content; // WPCS: XSS ok.
}
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* Top Rated Products Widget.
* Gets and displays top rated products in an unordered list.
*
* @package WooCommerce\Widgets
* @version 3.3.0
*/
defined( 'ABSPATH' ) || exit;
/**
* Widget top rated products class.
*/
class WC_Widget_Top_Rated_Products extends WC_Widget {
/**
* Constructor.
*/
public function __construct() {
$this->widget_cssclass = 'woocommerce widget_top_rated_products';
$this->widget_description = __( "A list of your store's top-rated products.", 'woocommerce' );
$this->widget_id = 'woocommerce_top_rated_products';
$this->widget_name = __( 'Products by Rating list', 'woocommerce' );
$this->settings = array(
'title' => array(
'type' => 'text',
'std' => __( 'Top rated products', 'woocommerce' ),
'label' => __( 'Title', 'woocommerce' ),
),
'number' => array(
'type' => 'number',
'step' => 1,
'min' => 1,
'max' => '',
'std' => 5,
'label' => __( 'Number of products to show', 'woocommerce' ),
),
);
parent::__construct();
}
/**
* Output widget.
*
* @see WP_Widget
* @param array $args Arguments.
* @param array $instance Widget instance.
*/
public function widget( $args, $instance ) {
if ( $this->get_cached_widget( $args ) ) {
return;
}
ob_start();
$number = ! empty( $instance['number'] ) ? absint( $instance['number'] ) : $this->settings['number']['std'];
$query_args = apply_filters(
'woocommerce_top_rated_products_widget_args',
array(
'posts_per_page' => $number,
'no_found_rows' => 1,
'post_status' => 'publish',
'post_type' => 'product',
'meta_key' => '_wc_average_rating',
'orderby' => 'meta_value_num',
'order' => 'DESC',
'meta_query' => WC()->query->get_meta_query(),
'tax_query' => WC()->query->get_tax_query(),
)
); // WPCS: slow query ok.
$r = new WP_Query( $query_args );
if ( $r->have_posts() ) {
$this->widget_start( $args, $instance );
echo wp_kses_post( apply_filters( 'woocommerce_before_widget_product_list', '<ul class="product_list_widget">' ) );
$template_args = array(
'widget_id' => isset( $args['widget_id'] ) ? $args['widget_id'] : $this->widget_id,
'show_rating' => true,
);
while ( $r->have_posts() ) {
$r->the_post();
wc_get_template( 'content-widget-product.php', $template_args );
}
echo wp_kses_post( apply_filters( 'woocommerce_after_widget_product_list', '</ul>' ) );
$this->widget_end( $args );
}
wp_reset_postdata();
$content = ob_get_clean();
echo $content; // WPCS: XSS ok.
$this->cache_widget( $args, $content );
}
}