Merged in feature/plugins-update (pull request #9)
wp plugin updates from pantheon * wp plugin updates from pantheon
This commit is contained in:
517
wp/wp-content/plugins/facetwp/includes/integrations/acf/acf.php
Normal file
517
wp/wp-content/plugins/facetwp/includes/integrations/acf/acf.php
Normal file
@@ -0,0 +1,517 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Integration_ACF
|
||||
{
|
||||
|
||||
public $fields = [];
|
||||
public $parent_type_lookup = [];
|
||||
public $repeater_row;
|
||||
|
||||
|
||||
function __construct() {
|
||||
add_filter( 'facetwp_facet_sources', [ $this, 'facet_sources' ] );
|
||||
add_filter( 'facetwp_facet_orderby', [ $this, 'facet_orderby' ], 10, 2 );
|
||||
add_filter( 'facetwp_indexer_query_args', [ $this, 'lookup_acf_fields' ] );
|
||||
add_filter( 'facetwp_indexer_post_facet', [ $this, 'index_acf_values' ], 1, 2 );
|
||||
add_filter( 'facetwp_acf_display_value', [ $this, 'index_source_other' ], 1, 2 );
|
||||
add_filter( 'facetwp_builder_item_value', [ $this, 'layout_builder_values' ], 999, 2 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add ACF fields to the Data Sources dropdown
|
||||
*/
|
||||
function facet_sources( $sources ) {
|
||||
$fields = $this->get_fields();
|
||||
$choices = [];
|
||||
|
||||
foreach ( $fields as $field ) {
|
||||
$field_id = $field['hierarchy'];
|
||||
$field_name = $field['name'];
|
||||
$field_label = '[' . $field['group_title'] . '] ' . $field['parents'] . $field['label'];
|
||||
$choices[ "acf/$field_id" ] = $field_label;
|
||||
|
||||
// remove "hidden" ACF fields
|
||||
unset( $sources['custom_fields']['choices'][ "cf/_$field_name" ] );
|
||||
}
|
||||
|
||||
if ( ! empty( $choices ) ) {
|
||||
$sources['acf'] = [
|
||||
'label' => 'ACF',
|
||||
'choices' => $choices,
|
||||
'weight' => 5
|
||||
];
|
||||
}
|
||||
|
||||
return $sources;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the facet "Sort by" value is "Term order", then preserve
|
||||
* the custom order of certain ACF fields (checkboxes, radio, etc.)
|
||||
*/
|
||||
function facet_orderby( $orderby, $facet ) {
|
||||
if ( isset( $facet['source'] ) && isset( $facet['orderby'] ) ) {
|
||||
if ( 0 === strpos( $facet['source'], 'acf/' ) && 'term_order' == $facet['orderby'] ) {
|
||||
$source_parts = explode( '/', $facet['source'] );
|
||||
$field_id = array_pop( $source_parts );
|
||||
$field_object = get_field_object( $field_id );
|
||||
if ( ! empty( $field_object['choices'] ) ) {
|
||||
$choices = $field_object['choices'];
|
||||
$choices = implode( "','", esc_sql( $choices ) );
|
||||
$orderby = "FIELD(f.facet_display_value, '$choices')";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $orderby;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index ACF field data
|
||||
*/
|
||||
function index_acf_values( $return, $params ) {
|
||||
$defaults = $params['defaults'];
|
||||
$facet = $params['facet'];
|
||||
|
||||
if ( isset( $facet['source'] ) && 'acf/' == substr( $facet['source'], 0, 4 ) ) {
|
||||
$hierarchy = explode( '/', substr( $facet['source'], 4 ) );
|
||||
|
||||
// support "User Post Type" plugin
|
||||
$object_id = apply_filters( 'facetwp_acf_object_id', $defaults['post_id'] );
|
||||
|
||||
// get values (for sub-fields, use the parent repeater)
|
||||
$value = get_field( $hierarchy[0], $object_id, false );
|
||||
|
||||
// handle repeater values
|
||||
if ( 1 < count( $hierarchy ) ) {
|
||||
|
||||
$parent_field_key = array_shift( $hierarchy );
|
||||
$value = $this->process_field_value( $value, $hierarchy, $parent_field_key );
|
||||
|
||||
// get the sub-field properties
|
||||
$sub_field = get_field_object( $hierarchy[0], $object_id, false, false );
|
||||
|
||||
foreach ( $value as $key => $val ) {
|
||||
$this->repeater_row = $key;
|
||||
$rows = $this->get_values_to_index( $val, $sub_field, $defaults );
|
||||
$this->index_field_values( $rows );
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// get the field properties
|
||||
$field = get_field_object( $hierarchy[0], $object_id, false, false );
|
||||
|
||||
// index values
|
||||
$rows = $this->get_values_to_index( $value, $field, $defaults );
|
||||
$this->index_field_values( $rows );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hijack the "facetwp_indexer_query_args" hook to lookup the fields once
|
||||
*/
|
||||
function lookup_acf_fields( $args ) {
|
||||
$this->get_fields();
|
||||
return $args;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grab all ACF fields
|
||||
*/
|
||||
function get_fields() {
|
||||
|
||||
add_action( 'pre_get_posts', [ $this, 'disable_wpml' ] );
|
||||
$field_groups = acf_get_field_groups();
|
||||
remove_action( 'pre_get_posts', [ $this, 'disable_wpml' ] );
|
||||
|
||||
foreach ( $field_groups as $field_group ) {
|
||||
$fields = acf_get_fields( $field_group );
|
||||
|
||||
if ( ! empty( $fields ) ) {
|
||||
$this->flatten_fields( $fields, $field_group );
|
||||
}
|
||||
}
|
||||
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* We need to get field groups in ALL languages
|
||||
*/
|
||||
function disable_wpml( $query ) {
|
||||
$query->set( 'suppress_filters', true );
|
||||
$query->set( 'lang', '' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract field values from the repeater array
|
||||
*/
|
||||
function process_field_value( $value, $hierarchy, $parent_field_key ) {
|
||||
$temp_val = [];
|
||||
|
||||
// prevent PHP8 fatal error on invalid lookup field
|
||||
$parent_field_type = $this->parent_type_lookup[ $parent_field_key ] ?? 'none';
|
||||
|
||||
if ( ! is_array( $value ) || 'none' == $parent_field_type ) {
|
||||
return $temp_val;
|
||||
}
|
||||
|
||||
// reduce the hierarchy array
|
||||
$field_key = array_shift( $hierarchy );
|
||||
|
||||
// group
|
||||
if ( 'group' == $parent_field_type ) {
|
||||
if ( 0 == count( $hierarchy ) ) {
|
||||
$temp_val[] = $value[ $field_key ];
|
||||
}
|
||||
else {
|
||||
return $this->process_field_value( $value[ $field_key ], $hierarchy, $field_key );
|
||||
}
|
||||
}
|
||||
// repeater
|
||||
else {
|
||||
if ( 0 == count( $hierarchy ) ) {
|
||||
foreach ( $value as $val ) {
|
||||
$temp_val[] = $val[ $field_key ];
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach ( $value as $outer ) {
|
||||
if ( isset( $outer[ $field_key ] ) ) {
|
||||
foreach ( $outer[ $field_key ] as $inner ) {
|
||||
$temp_val[] = $inner;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->process_field_value( $temp_val, $hierarchy, $field_key );
|
||||
}
|
||||
}
|
||||
|
||||
return $temp_val;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of $params arrays
|
||||
* Useful for indexing and grabbing values for the Layout Builder
|
||||
* @since 3.4.0
|
||||
*/
|
||||
function get_values_to_index( $value, $field, $params ) {
|
||||
$value = maybe_unserialize( $value );
|
||||
$type = $field['type'];
|
||||
$output = [];
|
||||
|
||||
// checkboxes
|
||||
if ( 'checkbox' == $type || 'select' == $type || 'radio' == $type ) {
|
||||
if ( false !== $value ) {
|
||||
foreach ( (array) $value as $val ) {
|
||||
$display_value = isset( $field['choices'][ $val ] ) ?
|
||||
$field['choices'][ $val ] :
|
||||
$val;
|
||||
|
||||
$params['facet_value'] = $val;
|
||||
$params['facet_display_value'] = $display_value;
|
||||
$output[] = $params;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// relationship
|
||||
elseif ( 'relationship' == $type || 'post_object' == $type || 'page_link' == $type ) {
|
||||
if ( false !== $value ) {
|
||||
foreach ( (array) $value as $val ) {
|
||||
|
||||
// does the post exist?
|
||||
if ( false !== get_post_type( $val ) ) {
|
||||
$params['facet_value'] = $val;
|
||||
$params['facet_display_value'] = get_the_title( $val );
|
||||
$output[] = $params;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// user
|
||||
elseif ( 'user' == $type ) {
|
||||
if ( false !== $value ) {
|
||||
foreach ( (array) $value as $val ) {
|
||||
$user = get_user_by( 'id', $val );
|
||||
|
||||
// does the user exist?
|
||||
if ( false !== $user ) {
|
||||
$params['facet_value'] = $val;
|
||||
$params['facet_display_value'] = $user->display_name;
|
||||
$output[] = $params;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// taxonomy
|
||||
elseif ( 'taxonomy' == $type ) {
|
||||
if ( ! empty( $value ) ) {
|
||||
foreach ( (array) $value as $val ) {
|
||||
global $wpdb;
|
||||
|
||||
$term_id = (int) $val;
|
||||
$term = $wpdb->get_row( "SELECT name, slug FROM {$wpdb->terms} WHERE term_id = '$term_id' LIMIT 1" );
|
||||
|
||||
// does the term exist?
|
||||
if ( null !== $term ) {
|
||||
$params['facet_value'] = $term->slug;
|
||||
$params['facet_display_value'] = $term->name;
|
||||
$params['term_id'] = $term_id;
|
||||
$output[] = $params;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// date_picker
|
||||
elseif ( 'date_picker' == $type ) {
|
||||
$formatted = $this->format_date( $value );
|
||||
$params['facet_value'] = $formatted;
|
||||
$params['facet_display_value'] = apply_filters( 'facetwp_acf_display_value', $formatted, $params );
|
||||
$output[] = $params;
|
||||
}
|
||||
|
||||
// true_false
|
||||
elseif ( 'true_false' == $type ) {
|
||||
$display_value = ( 0 < (int) $value ) ? __( 'Yes', 'fwp-front' ) : __( 'No', 'fwp-front' );
|
||||
$params['facet_value'] = $value;
|
||||
$params['facet_display_value'] = $display_value;
|
||||
$output[] = $params;
|
||||
}
|
||||
|
||||
// google_map
|
||||
elseif ( 'google_map' == $type ) {
|
||||
if ( isset( $value['lat'] ) && isset( $value['lng'] ) ) {
|
||||
$params['facet_value'] = $value['lat'];
|
||||
$params['facet_display_value'] = $value['lng'];
|
||||
$params['place_details'] = $value;
|
||||
$output[] = $params;
|
||||
}
|
||||
}
|
||||
|
||||
// text
|
||||
else {
|
||||
$params['facet_value'] = $value;
|
||||
$params['facet_display_value'] = apply_filters( 'facetwp_acf_display_value', $value, $params );
|
||||
$output[] = $params;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index values
|
||||
*/
|
||||
function index_field_values( $rows ) {
|
||||
foreach ( $rows as $params ) {
|
||||
FWP()->indexer->index_row( $params );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle "source_other" setting
|
||||
*/
|
||||
function index_source_other( $value, $params ) {
|
||||
if ( ! empty( $params['facet_name'] ) ) {
|
||||
$facet = FWP()->helper->get_facet_by_name( $params['facet_name'] );
|
||||
|
||||
if ( ! empty( $facet['source_other'] ) ) {
|
||||
$hierarchy = explode( '/', substr( $facet['source_other'], 4 ) );
|
||||
|
||||
// support "User Post Type" plugin
|
||||
$object_id = apply_filters( 'facetwp_acf_object_id', $params['post_id'] );
|
||||
|
||||
// get the value
|
||||
$value = get_field( $hierarchy[0], $object_id, false );
|
||||
|
||||
// handle repeater values
|
||||
if ( 1 < count( $hierarchy ) ) {
|
||||
$parent_field_key = array_shift( $hierarchy );
|
||||
$value = $this->process_field_value( $value, $hierarchy, $parent_field_key );
|
||||
$value = $value[ $this->repeater_row ];
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'date_range' == $facet['type'] ) {
|
||||
$value = $this->format_date( $value );
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Format dates in YYYY-MM-DD
|
||||
*/
|
||||
function format_date( $str ) {
|
||||
if ( 8 == strlen( $str ) && ctype_digit( $str ) ) {
|
||||
$str = substr( $str, 0, 4 ) . '-' . substr( $str, 4, 2 ) . '-' . substr( $str, 6, 2 );
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a flat array of fields within a specific field group
|
||||
*/
|
||||
function flatten_fields( $fields, $field_group, $hierarchy = '', $parents = '' ) {
|
||||
foreach ( $fields as $field ) {
|
||||
|
||||
// append the hierarchy string
|
||||
$new_hierarchy = $hierarchy . '/' . $field['key'];
|
||||
|
||||
// loop again for repeater or group fields
|
||||
if ( 'repeater' == $field['type'] || 'group' == $field['type'] ) {
|
||||
$new_parents = $parents . $field['label'] . ' → ';
|
||||
|
||||
$this->parent_type_lookup[ $field['key'] ] = $field['type'];
|
||||
$this->flatten_fields( $field['sub_fields'], $field_group, $new_hierarchy, $new_parents );
|
||||
}
|
||||
else {
|
||||
$this->fields[] = [
|
||||
'key' => $field['key'],
|
||||
'name' => $field['name'],
|
||||
'label' => $field['label'],
|
||||
'hierarchy' => trim( $new_hierarchy, '/' ),
|
||||
'parents' => $parents,
|
||||
'group_title' => $field_group['title'],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the field value (support User Post Type)
|
||||
* @since 3.4.1
|
||||
*/
|
||||
function get_field( $source, $post_id ) {
|
||||
$hierarchy = explode( '/', substr( $source, 4 ) );
|
||||
$object_id = apply_filters( 'facetwp_acf_object_id', $post_id );
|
||||
return get_field( $hierarchy[0], $object_id );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fallback values for the layout builder
|
||||
* @since 3.4.0
|
||||
*
|
||||
* ACF return formats:
|
||||
* [image, file] = array, url, id
|
||||
* [select, checkbox, radio, button_group] = value, label, array (both)
|
||||
* [post_object, relationship, taxonomy] = object, id
|
||||
* [user] = array, object, id
|
||||
* [link] = array, url
|
||||
*/
|
||||
function layout_builder_values( $value, $item ) {
|
||||
global $post;
|
||||
|
||||
// exit if not an object or array
|
||||
if ( is_scalar( $value ) || is_null( $value ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$hierarchy = explode( '/', substr( $item['source'], 4 ) );
|
||||
|
||||
// support "User Post Type" plugin
|
||||
$object_id = apply_filters( 'facetwp_acf_object_id', $post->ID );
|
||||
|
||||
// get the field properties
|
||||
$field = get_field_object( $hierarchy[0], $object_id, false, false );
|
||||
|
||||
$type = $field['type'];
|
||||
$format = $field['return_format'] ?? '';
|
||||
$is_multiple = (bool) ( $field['multiple'] ?? false );
|
||||
|
||||
if ( ( 'post_object' == $type || 'relationship' == $type ) && 'object' == $format ) {
|
||||
$output = [];
|
||||
|
||||
$value = is_array( $value ) ? $value : [ $value ];
|
||||
|
||||
foreach ( $value as $val ) {
|
||||
$output[] = '<a href="' . get_permalink( $val->ID ) . '">' . esc_html( $val->post_title ) . '</a>';
|
||||
}
|
||||
|
||||
$value = $output;
|
||||
}
|
||||
|
||||
if ( 'taxonomy' == $type && 'object' == $format ) {
|
||||
$output = [];
|
||||
|
||||
foreach ( $value as $val ) {
|
||||
$output[] = $val->name;
|
||||
}
|
||||
|
||||
$value = $output;
|
||||
}
|
||||
|
||||
if ( ( 'select' == $type || 'checkbox' == $type || 'radio' == $type || 'button_group' == $type ) && 'array' == $format ) {
|
||||
$value = $value['label'] ?? wp_list_pluck( $value, 'label' );
|
||||
}
|
||||
|
||||
if ( ( 'image' == $type || 'gallery' == $type ) && 'array' == $format ) {
|
||||
$value = ( 'image' == $type ) ? [ $value ] : $value;
|
||||
|
||||
foreach ( $value as $val ) {
|
||||
$value = '<img src="' . esc_url( $val['url'] ) . '" title="' . esc_attr( $val['title'] ) . '" alt="' . esc_attr( $val['alt'] ) . '" />';
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'file' == $type && 'array' == $format ) {
|
||||
$value = '<a href="' . esc_url( $value['url'] ) . '">' . esc_html( $value['filename'] ) . '</a> (' . size_format( $value['filesize'], 1 ) . ')';
|
||||
}
|
||||
|
||||
if ( 'link' == $type && 'array' == $format ) {
|
||||
$value = '<a href="' . esc_url( $value['url'] ) . '" target="' . esc_attr( $value['target'] ) . '">' . esc_html( $value['title'] ) . '</a>';
|
||||
}
|
||||
|
||||
if ( 'google_map' == $type ) {
|
||||
$value = '<a href="https://www.google.com/maps/?q=' . $value['lat'] . ',' . $value['lng'] . '" target="_blank">' . esc_html( $value['address'] ) . '</a>';
|
||||
}
|
||||
|
||||
if ( 'user' == $type && ( 'object' == $format || 'array' == $format ) ) {
|
||||
$output = [];
|
||||
|
||||
$value = $is_multiple ? $value : [ $value ];
|
||||
|
||||
foreach ( $value as $val ) {
|
||||
if ( 'object' == $format ) {
|
||||
$output[] = $val->display_name;
|
||||
}
|
||||
elseif ( 'array' == $format ) {
|
||||
$output[] = $val['display_name'];
|
||||
}
|
||||
}
|
||||
$value = $output;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( function_exists( 'acf' ) && version_compare( acf()->settings['version'], '5.0', '>=' ) ) {
|
||||
FWP()->acf = new FacetWP_Integration_ACF();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
(function($) {
|
||||
$().on('facetwp-loaded', function() {
|
||||
$('.edd-no-js').addClass('facetwp-hidden');
|
||||
$('a.edd-add-to-cart').addClass('edd-has-js');
|
||||
});
|
||||
})(fUtil);
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Integration_EDD
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
add_filter( 'facetwp_facet_sources', [ $this, 'exclude_data_sources' ] );
|
||||
add_filter( 'edd_downloads_query', [ $this, 'edd_downloads_query' ] );
|
||||
add_action( 'facetwp_assets', [ $this, 'assets' ] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Trigger some EDD code on facetwp-loaded
|
||||
* @since 2.0.4
|
||||
*/
|
||||
function assets( $assets ) {
|
||||
$assets['edd.js'] = FACETWP_URL . '/includes/integrations/edd/edd.js';
|
||||
return $assets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Help FacetWP auto-detect the [downloads] shortcode
|
||||
* @since 2.0.4
|
||||
*/
|
||||
function edd_downloads_query( $query ) {
|
||||
$query['facetwp'] = true;
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exclude specific EDD custom fields
|
||||
* @since 2.4
|
||||
*/
|
||||
function exclude_data_sources( $sources ) {
|
||||
foreach ( $sources['custom_fields']['choices'] as $key => $val ) {
|
||||
if ( 0 === strpos( $val, '_edd_' ) ) {
|
||||
unset( $sources['custom_fields']['choices'][ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $sources;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( is_plugin_active( 'easy-digital-downloads/easy-digital-downloads.php' ) ) {
|
||||
new FacetWP_Integration_EDD();
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Integration_SearchWP
|
||||
{
|
||||
|
||||
public $keywords;
|
||||
public $swp_query;
|
||||
public $first_run = true;
|
||||
|
||||
|
||||
function __construct() {
|
||||
add_filter( 'facetwp_is_main_query', [ $this, 'is_main_query' ], 10, 2 );
|
||||
add_action( 'pre_get_posts', [ $this, 'pre_get_posts' ], 1000, 2 );
|
||||
add_filter( 'posts_pre_query', [ $this, 'posts_pre_query' ], 10, 2 );
|
||||
add_filter( 'posts_results', [ $this, 'posts_results' ], 10, 2 );
|
||||
add_filter( 'facetwp_facet_filter_posts', [ $this, 'search_facet' ], 10, 2 );
|
||||
add_filter( 'facetwp_facet_search_engines', [ $this, 'search_engines' ] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run and cache the \SWP_Query
|
||||
*/
|
||||
function is_main_query( $is_main_query, $query ) {
|
||||
if ( $is_main_query && $query->is_search() && ! empty( $query->get( 's' ) ) ) {
|
||||
$args = stripslashes_deep( $this->get_valid_args( $query ) );
|
||||
$this->keywords = $args['s'];
|
||||
$this->swp_query = $this->run_query( $args );
|
||||
$query->set( 'using_searchwp', true );
|
||||
$query->set( 'searchwp', false );
|
||||
}
|
||||
|
||||
return $is_main_query;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whitelist supported SWP_Query arguments
|
||||
*
|
||||
* @link https://searchwp.com/documentation/classes/swp_query/#arguments
|
||||
*/
|
||||
function get_valid_args( $query ) {
|
||||
$output = [];
|
||||
|
||||
$valid = [
|
||||
's', 'engine', 'post__in', 'post__not_in', 'post_type', 'post_status',
|
||||
'tax_query', 'meta_query', 'date_query', 'order', 'orderby'
|
||||
];
|
||||
|
||||
foreach ( $valid as $arg ) {
|
||||
$val = $query->get( $arg );
|
||||
if ( ! empty( $val ) ) {
|
||||
$output[ $arg ] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Modify FacetWP's render() query to use SearchWP's results while bypassing
|
||||
* WP core search. We're using this additional query to support custom query
|
||||
* modifications, such as for FacetWP's sort box.
|
||||
*
|
||||
* The hook priority (1000) is important because this needs to run after
|
||||
* FacetWP_Request->update_query_vars()
|
||||
*/
|
||||
function pre_get_posts( $query ) {
|
||||
if ( true === $query->get( 'using_searchwp' ) ) {
|
||||
if ( true === $query->get( 'facetwp' ) && ! $this->first_run ) {
|
||||
$query->set( 's', '' );
|
||||
|
||||
$post_ids = FWP()->filtered_post_ids;
|
||||
$post_ids = empty( $post_ids ) ? [ 0 ] : $post_ids;
|
||||
$query->set( 'post__in', $post_ids );
|
||||
|
||||
if ( '' === $query->get( 'post_type' ) ) {
|
||||
$query->set( 'post_type', 'any' );
|
||||
$query->set( 'post_status', 'any' );
|
||||
}
|
||||
|
||||
if ( '' === $query->get( 'orderby' ) ) {
|
||||
$query->set( 'orderby', 'post__in' );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If [facetwp => false] then it's the get_filtered_post_ids() query. Return
|
||||
* the raw SearchWP results to prevent the additional query.
|
||||
*
|
||||
* If [facetwp => true] and [first_run => true] then it's the main WP query. Return
|
||||
* a non-null value to kill the query, since we don't use the results.
|
||||
*
|
||||
* If [facetwp => true] and [first_run => false] then it's the FacetWP render() query.
|
||||
*/
|
||||
function posts_pre_query( $posts, $query ) {
|
||||
if ( true === $query->get( 'using_searchwp' ) ) {
|
||||
if ( true === $query->get( 'facetwp' ) ) {
|
||||
$query->set( 's', $this->keywords );
|
||||
|
||||
// kill the main WP query
|
||||
if ( $this->first_run ) {
|
||||
$this->first_run = false;
|
||||
|
||||
$page = max( $query->get( 'paged' ), 1 );
|
||||
$per_page = (int) $query->get( 'posts_per_page', get_option( 'posts_per_page' ) );
|
||||
$query->found_posts = count( FWP()->filtered_post_ids );
|
||||
$query->max_num_pages = ( 0 < $per_page ) ? ceil( $query->found_posts / $per_page ) : 0;
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
else {
|
||||
return $this->swp_query->posts;
|
||||
}
|
||||
}
|
||||
|
||||
return $posts;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply highlighting if available
|
||||
*/
|
||||
function posts_results( $posts, $query ) {
|
||||
if ( true === $query->get( 'using_searchwp' ) ) {
|
||||
if ( true === $query->get( 'facetwp' ) && ! $this->first_run ) {
|
||||
|
||||
// SearchWP 4.1+
|
||||
if ( isset( $this->swp_query->query ) ) {
|
||||
foreach ( $posts as $index => $post ) {
|
||||
$source = \SearchWP\Utils::get_post_type_source_name( $post->post_type );
|
||||
$entry = new \SearchWP\Entry( $source, $post->ID, false );
|
||||
$posts[ $index ] = $entry->native( $this->swp_query->query );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $posts;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For search facets, run \SWP_Query and return matching post IDs
|
||||
*/
|
||||
function search_facet( $return, $params ) {
|
||||
$facet = $params['facet'];
|
||||
$selected_values = $params['selected_values'];
|
||||
$selected_values = is_array( $selected_values ) ? $selected_values[0] : $selected_values;
|
||||
$engine = $facet['search_engine'] ?? '';
|
||||
|
||||
if ( 'search' == $facet['type'] && 0 === strpos( $engine, 'swp_' ) ) {
|
||||
$return = [];
|
||||
|
||||
if ( empty( $selected_values ) ) {
|
||||
$return = 'continue';
|
||||
}
|
||||
elseif ( ! empty( FWP()->unfiltered_post_ids ) ) {
|
||||
$swp_query = $this->run_query([
|
||||
's' => $selected_values,
|
||||
'engine' => substr( $engine, 4 ),
|
||||
'post__in' => FWP()->unfiltered_post_ids
|
||||
]);
|
||||
$return = $swp_query->posts;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run a search and return the \SWP_Query object
|
||||
*/
|
||||
function run_query( $args ) {
|
||||
$overrides = [ 'posts_per_page' => 200, 'fields' => 'ids', 'facetwp' => true ];
|
||||
$args = array_merge( $args, $overrides );
|
||||
return new \SWP_Query( $args );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add engines to the search facet
|
||||
*/
|
||||
function search_engines( $engines ) {
|
||||
|
||||
if ( version_compare( SEARCHWP_VERSION, '4.0', '>=' ) ) {
|
||||
$settings = get_option( SEARCHWP_PREFIX . 'engines' );
|
||||
|
||||
foreach ( $settings as $key => $info ) {
|
||||
$engines[ 'swp_' . $key ] = 'SearchWP - ' . $info['label'];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$settings = get_option( SEARCHWP_PREFIX . 'settings' );
|
||||
|
||||
foreach ( $settings['engines'] as $key => $info ) {
|
||||
$label = $info['searchwp_engine_label'] ?? __( 'Default', 'fwp' );
|
||||
$engines[ 'swp_' . $key ] = 'SearchWP - ' . $label;
|
||||
}
|
||||
}
|
||||
|
||||
return $engines;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( defined( 'SEARCHWP_VERSION' ) ) {
|
||||
new FacetWP_Integration_SearchWP();
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Integration_WooCommerce_Taxonomy
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
add_action( 'woocommerce_after_template_part', [ $this, 'add_loop_tag'] );
|
||||
add_filter( 'get_terms', [ $this, 'adjust_term_counts' ], 10, 3 );
|
||||
add_filter( 'term_link', [ $this, 'append_url_vars' ], 10, 3 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Support category listings (Shop page display: Show categories)
|
||||
* @since 3.3.10
|
||||
*/
|
||||
function add_loop_tag( $template_name ) {
|
||||
if ( 'loop/loop-start.php' == $template_name ) {
|
||||
echo "<!--fwp-loop-->\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adjust the category listing counts when facets are selected
|
||||
* @since 3.3.10
|
||||
*/
|
||||
function adjust_term_counts( $terms, $taxonomy, $query_vars ) {
|
||||
if ( FWP()->request->is_refresh || ! empty( FWP()->request->url_vars ) ) {
|
||||
if ( 'product_cat' == reset( $taxonomy ) ) {
|
||||
global $wpdb, $wp_query;
|
||||
|
||||
$sql = $wp_query->request;
|
||||
if ( false !== ( $pos = strpos( $sql, ' ORDER BY' ) ) ) {
|
||||
$sql = substr( $sql, 0, $pos );
|
||||
}
|
||||
|
||||
$post_ids = $wpdb->get_col( $sql );
|
||||
|
||||
if ( ! empty( $post_ids ) ) {
|
||||
$term_counts = [];
|
||||
$post_ids_str = implode( ',', $post_ids );
|
||||
|
||||
$query = "
|
||||
SELECT term_id, COUNT(term_id) AS term_count
|
||||
FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE post_id IN ($post_ids_str)
|
||||
GROUP BY term_id";
|
||||
|
||||
$results = $wpdb->get_results( $query );
|
||||
|
||||
foreach ( $results as $row ) {
|
||||
$term_counts[ $row->term_id ] = (int) $row->term_count;
|
||||
}
|
||||
|
||||
foreach ( $terms as $term ) {
|
||||
$term->count = $term_counts[ $term->term_id ] ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $terms;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append facet URL variables to the category archive links
|
||||
* @since 3.3.10
|
||||
*/
|
||||
function append_url_vars( $term_link, $term, $taxonomy ) {
|
||||
if ( 'product_cat' == $taxonomy ) {
|
||||
$query_string = filter_var( $_SERVER['QUERY_STRING'], FILTER_SANITIZE_URL );
|
||||
|
||||
if ( ! empty( $query_string ) ) {
|
||||
$prefix = ( false !== strpos( $query_string, '?' ) ) ? '&' : '?';
|
||||
$term_link .= $prefix . $query_string;
|
||||
}
|
||||
}
|
||||
|
||||
return $term_link;
|
||||
}
|
||||
}
|
||||
|
||||
new FacetWP_Integration_WooCommerce_Taxonomy();
|
||||
@@ -0,0 +1,34 @@
|
||||
(function($) {
|
||||
|
||||
$().on('facetwp-refresh', function() {
|
||||
if (! FWP.loaded) {
|
||||
setup_woocommerce();
|
||||
}
|
||||
});
|
||||
|
||||
function setup_woocommerce() {
|
||||
|
||||
// Intercept WooCommerce pagination
|
||||
$().on('click', '.woocommerce-pagination a', function(e) {
|
||||
e.preventDefault();
|
||||
var matches = $(this).attr('href').match(/\/page\/(\d+)/);
|
||||
if (null !== matches) {
|
||||
FWP.paged = parseInt(matches[1]);
|
||||
FWP.soft_refresh = true;
|
||||
FWP.refresh();
|
||||
}
|
||||
});
|
||||
|
||||
// Disable sort handler
|
||||
$('.woocommerce-ordering').attr('onsubmit', 'event.preventDefault()');
|
||||
|
||||
// Intercept WooCommerce sorting
|
||||
$().on('change', '.woocommerce-ordering .orderby', function(e) {
|
||||
var qs = new URLSearchParams(window.location.search);
|
||||
qs.set('orderby', $(this).val());
|
||||
history.pushState(null, null, window.location.pathname + '?' + qs.toString());
|
||||
FWP.soft_refresh = true;
|
||||
FWP.refresh();
|
||||
});
|
||||
}
|
||||
})(fUtil);
|
||||
@@ -0,0 +1,570 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Integration_WooCommerce
|
||||
{
|
||||
|
||||
public $cache = [];
|
||||
public $lookup = [];
|
||||
public $storage = [];
|
||||
public $variations = [];
|
||||
public $post_clauses = false;
|
||||
|
||||
|
||||
function __construct() {
|
||||
add_action( 'facetwp_assets', [ $this, 'assets' ] );
|
||||
add_filter( 'facetwp_facet_sources', [ $this, 'facet_sources' ] );
|
||||
add_filter( 'facetwp_facet_display_value', [ $this, 'translate_hardcoded_choices' ], 10, 2 );
|
||||
add_filter( 'facetwp_indexer_post_facet', [ $this, 'index_woo_values' ], 10, 2 );
|
||||
|
||||
// Support WooCommerce product variations
|
||||
$is_enabled = ( 'yes' === FWP()->helper->get_setting( 'wc_enable_variations', 'no' ) );
|
||||
|
||||
if ( apply_filters( 'facetwp_enable_product_variations', $is_enabled ) ) {
|
||||
add_filter( 'facetwp_indexer_post_facet_defaults', [ $this, 'force_taxonomy' ], 10, 2 );
|
||||
add_filter( 'facetwp_indexer_query_args', [ $this, 'index_variations' ] );
|
||||
add_filter( 'facetwp_index_row', [ $this, 'attribute_variations' ], 1 );
|
||||
add_filter( 'facetwp_wpdb_sql', [ $this, 'wpdb_sql' ], 10, 2 );
|
||||
add_filter( 'facetwp_wpdb_get_col', [ $this, 'wpdb_get_col' ], 10, 3 );
|
||||
add_filter( 'facetwp_filtered_post_ids', [ $this, 'process_variations' ] );
|
||||
add_filter( 'facetwp_facet_where', [ $this, 'facet_where' ], 10, 2 );
|
||||
}
|
||||
|
||||
// Preserve the WooCommerce sort
|
||||
add_filter( 'posts_clauses', [ $this, 'preserve_sort' ], 20, 2 );
|
||||
|
||||
// Prevent WooCommerce from redirecting to a single result page
|
||||
add_filter( 'woocommerce_redirect_single_search_result', [ $this, 'redirect_single_search_result' ] );
|
||||
|
||||
// Prevent WooCommerce sort (posts_clauses) when doing FacetWP sort
|
||||
add_filter( 'woocommerce_default_catalog_orderby', [ $this, 'default_catalog_orderby' ] );
|
||||
|
||||
// Dynamic counts when Shop Page Display = "Categories" or "Both"
|
||||
if ( apply_filters( 'facetwp_woocommerce_support_categories_display', false ) ) {
|
||||
include( FACETWP_DIR . '/includes/integrations/woocommerce/taxonomy.php' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run WooCommerce handlers on facetwp-refresh
|
||||
* @since 2.0.9
|
||||
*/
|
||||
function assets( $assets ) {
|
||||
$assets['woocommerce.js'] = FACETWP_URL . '/includes/integrations/woocommerce/woocommerce.js';
|
||||
return $assets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add WooCommerce-specific data sources
|
||||
* @since 2.1.4
|
||||
*/
|
||||
function facet_sources( $sources ) {
|
||||
$sources['woocommerce'] = [
|
||||
'label' => __( 'WooCommerce', 'fwp' ),
|
||||
'choices' => [
|
||||
'woo/price' => __( 'Price' ),
|
||||
'woo/sale_price' => __( 'Sale Price' ),
|
||||
'woo/regular_price' => __( 'Regular Price' ),
|
||||
'woo/average_rating' => __( 'Average Rating' ),
|
||||
'woo/stock_status' => __( 'Stock Status' ),
|
||||
'woo/on_sale' => __( 'On Sale' ),
|
||||
'woo/featured' => __( 'Featured' ),
|
||||
'woo/product_type' => __( 'Product Type' ),
|
||||
],
|
||||
'weight' => 5
|
||||
];
|
||||
|
||||
// Move WC taxonomy choices
|
||||
foreach ( $sources['taxonomies']['choices'] as $key => $label ) {
|
||||
if ( 'tax/product_cat' == $key || 'tax/product_tag' == $key || 0 === strpos( $key, 'tax/pa_' ) ) {
|
||||
$sources['woocommerce']['choices'][ $key ] = $label;
|
||||
unset( $sources['taxonomies']['choices'][ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $sources;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attributes for WC product variations are stored in postmeta
|
||||
* @since 2.7.2
|
||||
*/
|
||||
function force_taxonomy( $defaults, $params ) {
|
||||
if ( 0 === strpos( $defaults['facet_source'], 'tax/pa_' ) ) {
|
||||
$post_id = (int) $defaults['post_id'];
|
||||
|
||||
if ( 'product_variation' == get_post_type( $post_id ) ) {
|
||||
$defaults['facet_source'] = str_replace( 'tax/', 'cf/attribute_', $defaults['facet_source'] );
|
||||
}
|
||||
}
|
||||
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index product variations
|
||||
* @since 2.7
|
||||
*/
|
||||
function index_variations( $args ) {
|
||||
|
||||
// Saving a single product
|
||||
if ( ! empty( $args['p'] ) ) {
|
||||
$post_id = (int) $args['p'];
|
||||
if ( 'product' == get_post_type( $post_id ) ) {
|
||||
if ( 'variable' == $this->get_product_type( $post_id ) ) {
|
||||
$product = wc_get_product( $post_id );
|
||||
|
||||
if ( false !== $product ) {
|
||||
$children = $product->get_children();
|
||||
$args['post_type'] = [ 'product', 'product_variation' ];
|
||||
$args['post__in'] = $children;
|
||||
$args['post__in'][] = $post_id;
|
||||
$args['posts_per_page'] = -1;
|
||||
unset( $args['p'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Force product variations to piggyback products
|
||||
else {
|
||||
$pt = (array) $args['post_type'];
|
||||
|
||||
if ( in_array( 'any', $pt ) ) {
|
||||
$pt = get_post_types();
|
||||
}
|
||||
if ( in_array( 'product', $pt ) ) {
|
||||
$pt[] = 'product_variation';
|
||||
}
|
||||
|
||||
$args['post_type'] = $pt;
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When indexing product variations, attribute its parent product
|
||||
* @since 2.7
|
||||
*/
|
||||
function attribute_variations( $params ) {
|
||||
$post_id = (int) $params['post_id'];
|
||||
|
||||
// Set variation_id for all posts
|
||||
$params['variation_id'] = $post_id;
|
||||
|
||||
if ( 'product_variation' == get_post_type( $post_id ) ) {
|
||||
$params['post_id'] = wp_get_post_parent_id( $post_id );
|
||||
|
||||
// Lookup the term name for variation values
|
||||
if ( 0 === strpos( $params['facet_source'], 'cf/attribute_pa_' ) ) {
|
||||
$taxonomy = str_replace( 'cf/attribute_', '', $params['facet_source'] );
|
||||
$term = get_term_by( 'slug', $params['facet_value'], $taxonomy );
|
||||
|
||||
if ( false !== $term ) {
|
||||
$params['term_id'] = $term->term_id;
|
||||
$params['facet_display_value'] = $term->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hijack filter_posts() to grab variation IDs
|
||||
* @since 2.7
|
||||
*/
|
||||
function wpdb_sql( $sql, $facet ) {
|
||||
$sql = str_replace(
|
||||
'DISTINCT post_id',
|
||||
'DISTINCT post_id, GROUP_CONCAT(variation_id) AS variation_ids',
|
||||
$sql
|
||||
);
|
||||
|
||||
$sql .= ' GROUP BY post_id';
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store a facet's variation IDs
|
||||
* @since 2.7
|
||||
*/
|
||||
function wpdb_get_col( $result, $sql, $facet ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet_name = $facet['name'];
|
||||
$post_ids = $wpdb->get_col( $sql, 0 ); // arrays of product IDs
|
||||
$variations = $wpdb->get_col( $sql, 1 ); // variation IDs as arrays of comma-separated strings
|
||||
|
||||
foreach ( $post_ids as $index => $post_id ) {
|
||||
$variations_array = explode( ',', $variations[ $index ] );
|
||||
$type = in_array( $post_id, $variations_array ) ? 'products' : 'variations';
|
||||
|
||||
if ( isset( $this->cache[ $facet_name ][ $type ] ) ) {
|
||||
foreach ( $variations_array as $id ) {
|
||||
$this->cache[ $facet_name ][ $type ][] = $id;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->cache[ $facet_name ][ $type ] = $variations_array;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* We need lookup arrays for both products and variations
|
||||
* @since 2.7.1
|
||||
*/
|
||||
function generate_lookup_array( $post_ids ) {
|
||||
global $wpdb;
|
||||
|
||||
$output = [];
|
||||
|
||||
if ( ! empty( $post_ids ) ) {
|
||||
$sql = "
|
||||
SELECT DISTINCT post_id, variation_id
|
||||
FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE post_id IN (" . implode( ',', $post_ids ) . ")";
|
||||
$results = $wpdb->get_results( $sql );
|
||||
|
||||
foreach ( $results as $result ) {
|
||||
$output['get_variations'][ $result->post_id ][] = $result->variation_id;
|
||||
$output['get_product'][ $result->variation_id ] = $result->post_id;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine valid variation IDs
|
||||
* @since 2.7
|
||||
*/
|
||||
function process_variations( $post_ids ) {
|
||||
if ( empty( $this->cache ) ) {
|
||||
return $post_ids;
|
||||
}
|
||||
|
||||
$this->lookup = $this->generate_lookup_array( FWP()->unfiltered_post_ids );
|
||||
|
||||
// Loop through each facet's data
|
||||
foreach ( $this->cache as $facet_name => $groups ) {
|
||||
$this->storage[ $facet_name ] = [];
|
||||
|
||||
// Create an array of variation IDs
|
||||
foreach ( $groups as $type => $ids ) { // products or variations
|
||||
foreach ( $ids as $id ) {
|
||||
$this->storage[ $facet_name ][] = $id;
|
||||
|
||||
// Lookup variation IDs for each product
|
||||
if ( 'products' == $type ) {
|
||||
if ( ! empty( $this->lookup['get_variations'][ $id ] ) ) {
|
||||
foreach ( $this->lookup['get_variations'][ $id ] as $variation_id ) {
|
||||
$this->storage[ $facet_name ][] = $variation_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result = $this->calculate_variations();
|
||||
$this->variations = $result['variations'];
|
||||
$post_ids = array_intersect( $post_ids, $result['products'] );
|
||||
$post_ids = empty( $post_ids ) ? [ 0 ] : $post_ids;
|
||||
return $post_ids;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate variation IDs
|
||||
* @param mixed $facet_name Facet name to ignore, or FALSE
|
||||
* @return array Associative array of product IDs + variation IDs
|
||||
* @since 2.8
|
||||
*/
|
||||
function calculate_variations( $facet_name = false ) {
|
||||
|
||||
$new = true;
|
||||
$final_products = [];
|
||||
$final_variations = [];
|
||||
|
||||
// Intersect product + variation IDs across facets
|
||||
foreach ( $this->storage as $name => $variation_ids ) {
|
||||
|
||||
// Skip facets in "OR" mode
|
||||
if ( $facet_name === $name ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$final_variations = ( $new ) ? $variation_ids : array_intersect( $final_variations, $variation_ids );
|
||||
$new = false;
|
||||
}
|
||||
|
||||
// Lookup each variation's product ID
|
||||
foreach ( $final_variations as $variation_id ) {
|
||||
if ( isset( $this->lookup['get_product'][ $variation_id ] ) ) {
|
||||
$final_products[ $this->lookup['get_product'][ $variation_id ] ] = true; // prevent duplicates
|
||||
}
|
||||
}
|
||||
|
||||
// Append product IDs to the variations array
|
||||
$final_products = array_keys( $final_products );
|
||||
|
||||
foreach ( $final_products as $id ) {
|
||||
$final_variations[] = $id;
|
||||
}
|
||||
|
||||
return [
|
||||
'products' => $final_products,
|
||||
'variations' => array_unique( $final_variations )
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply variation IDs to load_values() method
|
||||
* @since 2.7
|
||||
*/
|
||||
function facet_where( $where_clause, $facet ) {
|
||||
|
||||
// Support facets in "OR" mode
|
||||
if ( FWP()->helper->facet_is( $facet, 'operator', 'or' ) ) {
|
||||
$result = $this->calculate_variations( $facet['name'] );
|
||||
$variations = $result['variations'];
|
||||
}
|
||||
else {
|
||||
$variations = $this->variations;
|
||||
}
|
||||
|
||||
if ( ! empty( $variations ) ) {
|
||||
$where_clause .= ' AND variation_id IN (' . implode( ',', $variations ) . ')';
|
||||
}
|
||||
|
||||
return $where_clause;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Efficiently grab the product type without wc_get_product()
|
||||
* @since 3.3.8
|
||||
*/
|
||||
function get_product_type( $post_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$sql = "
|
||||
SELECT t.name
|
||||
FROM $wpdb->terms t
|
||||
INNER JOIN $wpdb->term_taxonomy tt ON tt.term_id = t.term_id AND tt.taxonomy = 'product_type'
|
||||
INNER JOIN $wpdb->term_relationships tr ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tr.object_id = %d";
|
||||
|
||||
$type = $wpdb->get_var(
|
||||
$wpdb->prepare( $sql, $post_id )
|
||||
);
|
||||
|
||||
return ( null !== $type ) ? $type : 'simple';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index WooCommerce-specific values
|
||||
* @since 2.1.4
|
||||
*/
|
||||
function index_woo_values( $return, $params ) {
|
||||
$facet = $params['facet'];
|
||||
$defaults = $params['defaults'];
|
||||
$post_id = (int) $defaults['post_id'];
|
||||
$post_type = get_post_type( $post_id );
|
||||
|
||||
// Index out of stock products?
|
||||
$index_all = ( 'yes' === FWP()->helper->get_setting( 'wc_index_all', 'no' ) );
|
||||
$index_all = apply_filters( 'facetwp_index_all_products', $index_all );
|
||||
|
||||
if ( 'product' == $post_type || 'product_variation' == $post_type ) {
|
||||
$product = wc_get_product( $post_id );
|
||||
|
||||
if ( ! $product || ( ! $index_all && ! $product->is_in_stock() ) ) {
|
||||
return true; // skip
|
||||
}
|
||||
}
|
||||
|
||||
// Default handling
|
||||
if ( 'product' != $post_type || empty( $facet['source'] ) ) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
// Ignore product attributes with "Used for variations" ticked
|
||||
if ( 0 === strpos( $facet['source'], 'tax/pa_' ) ) {
|
||||
if ( 'variable' == $this->get_product_type( $post_id ) ) {
|
||||
$attrs = $product->get_attributes();
|
||||
$attr_name = str_replace( 'tax/', '', $facet['source'] );
|
||||
if ( isset( $attrs[ $attr_name ] ) && 1 === $attrs[ $attr_name ]['is_variation'] ) {
|
||||
return true; // skip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Custom woo fields
|
||||
if ( 0 === strpos( $facet['source'], 'woo/' ) ) {
|
||||
$source = substr( $facet['source'], 4 );
|
||||
|
||||
// Price
|
||||
if ( 'price' == $source || 'sale_price' == $source || 'regular_price' == $source ) {
|
||||
if ( $product->is_type( 'variable' ) ) {
|
||||
$method_name = "get_variation_$source";
|
||||
$price_min = $product->$method_name( 'min' ); // get_variation_price()
|
||||
$price_max = $product->$method_name( 'max' );
|
||||
}
|
||||
else {
|
||||
$method_name = "get_$source";
|
||||
$price_min = $price_max = $product->$method_name(); // get_price()
|
||||
}
|
||||
|
||||
$defaults['facet_value'] = $price_min;
|
||||
$defaults['facet_display_value'] = $price_max;
|
||||
FWP()->indexer->index_row( $defaults );
|
||||
}
|
||||
|
||||
// Average Rating
|
||||
elseif ( 'average_rating' == $source ) {
|
||||
$rating = $product->get_average_rating();
|
||||
$defaults['facet_value'] = $rating;
|
||||
$defaults['facet_display_value'] = $rating;
|
||||
FWP()->indexer->index_row( $defaults );
|
||||
}
|
||||
|
||||
// Stock Status
|
||||
elseif ( 'stock_status' == $source ) {
|
||||
$in_stock = $product->is_in_stock();
|
||||
$defaults['facet_value'] = (int) $in_stock;
|
||||
$defaults['facet_display_value'] = $in_stock ? 'In Stock' : 'Out of Stock';
|
||||
FWP()->indexer->index_row( $defaults );
|
||||
}
|
||||
|
||||
// On Sale
|
||||
elseif ( 'on_sale' == $source ) {
|
||||
if ( $product->is_on_sale() ) {
|
||||
$defaults['facet_value'] = 1;
|
||||
$defaults['facet_display_value'] = 'On Sale';
|
||||
FWP()->indexer->index_row( $defaults );
|
||||
}
|
||||
}
|
||||
|
||||
// Featured
|
||||
elseif ( 'featured' == $source ) {
|
||||
if ( $product->is_featured() ) {
|
||||
$defaults['facet_value'] = 1;
|
||||
$defaults['facet_display_value'] = 'Featured';
|
||||
FWP()->indexer->index_row( $defaults );
|
||||
}
|
||||
}
|
||||
|
||||
// Product Type
|
||||
elseif ( 'product_type' == $source ) {
|
||||
$type = $product->get_type();
|
||||
$defaults['facet_value'] = $type;
|
||||
$defaults['facet_display_value'] = $type;
|
||||
FWP()->indexer->index_row( $defaults );
|
||||
}
|
||||
|
||||
return true; // skip
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allow certain hard-coded choices to be translated dynamically
|
||||
* instead of stored as translated in the index table
|
||||
* @since 3.9.6
|
||||
*/
|
||||
function translate_hardcoded_choices( $label, $params ) {
|
||||
$source = $params['facet']['source'];
|
||||
|
||||
if ( 'woo/stock_status' == $source ) {
|
||||
$label = ( 'In Stock' == $label ) ? __( 'In Stock', 'fwp-front' ) : __( 'Out of Stock', 'fwp-front' );
|
||||
}
|
||||
elseif ( 'woo/on_sale' == $source ) {
|
||||
$label = __( 'On Sale', 'fwp-front' );
|
||||
}
|
||||
elseif ( 'woo/featured' == $source ) {
|
||||
$label = __( 'Featured', 'fwp-front' );
|
||||
}
|
||||
|
||||
return $label;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* WooCommerce removes its sort hooks after the main product_query runs
|
||||
* We need to preserve the sort for FacetWP to work
|
||||
*
|
||||
* @since 3.2.8
|
||||
*/
|
||||
function preserve_sort( $clauses, $query ) {
|
||||
|
||||
if ( ! apply_filters( 'facetwp_woocommerce_preserve_sort', true ) ) {
|
||||
return $clauses;
|
||||
}
|
||||
|
||||
$prefix = FWP()->helper->get_setting( 'prefix' );
|
||||
$using_sort = isset( FWP()->facet->http_params['get'][ $prefix . 'sort' ] );
|
||||
|
||||
if ( 'product_query' == $query->get( 'wc_query' ) && true === $query->get( 'facetwp' ) && ! $using_sort ) {
|
||||
if ( false === $this->post_clauses ) {
|
||||
$this->post_clauses = $clauses;
|
||||
}
|
||||
else {
|
||||
$clauses['join'] = $this->post_clauses['join'];
|
||||
$clauses['where'] = $this->post_clauses['where'];
|
||||
$clauses['orderby'] = $this->post_clauses['orderby'];
|
||||
|
||||
// Narrow the main query results
|
||||
$where_clause = FWP()->facet->where_clause;
|
||||
|
||||
if ( ! empty( $where_clause ) ) {
|
||||
$column = $GLOBALS['wpdb']->posts;
|
||||
$clauses['where'] .= str_replace( 'post_id', "$column.ID", $where_clause );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $clauses;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prevent WooCommerce from redirecting to single result page
|
||||
* @since 3.3.7
|
||||
*/
|
||||
function redirect_single_search_result( $bool ) {
|
||||
$using_facetwp = ( FWP()->request->is_refresh || ! empty( FWP()->request->url_vars ) );
|
||||
return $using_facetwp ? false : $bool;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prevent WooCommerce sort when a FacetWP sort is active
|
||||
* @since 3.6.8
|
||||
*/
|
||||
function default_catalog_orderby( $orderby ) {
|
||||
$sort = FWP()->helper->get_setting( 'prefix' ) . 'sort';
|
||||
return isset( $_GET[ $sort ] ) ? 'menu_order' : $orderby;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
|
||||
new FacetWP_Integration_WooCommerce();
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Builds or purges the FacetWP index.
|
||||
*/
|
||||
class FacetWP_Integration_WP_CLI
|
||||
{
|
||||
|
||||
/**
|
||||
* Index facet data.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* [--ids=<ids>]
|
||||
* : Index specific post IDs (comma-separated).
|
||||
*
|
||||
* [--facets=<facets>]
|
||||
* : Index specific facet names (comma-separated).
|
||||
*/
|
||||
function index( $args, $assoc_args ) {
|
||||
$index_all = true;
|
||||
|
||||
if ( isset( $assoc_args['ids'] ) ) {
|
||||
if ( empty( $assoc_args['ids'] ) ) {
|
||||
WP_CLI::error( 'IDs empty.' );
|
||||
}
|
||||
|
||||
$ids = preg_replace( '/\s+/', '', $assoc_args['ids'] );
|
||||
$ids = explode( ',', $ids );
|
||||
$post_ids = array_filter( $ids, 'ctype_digit' );
|
||||
$index_all = false;
|
||||
}
|
||||
else {
|
||||
$post_ids = FWP()->indexer->get_post_ids_to_index();
|
||||
}
|
||||
|
||||
if ( isset( $assoc_args['facets'] ) ) {
|
||||
if ( empty( $assoc_args['facets'] ) ) {
|
||||
WP_CLI::error( 'Facets empty.' );
|
||||
}
|
||||
|
||||
$facets = [];
|
||||
$facet_names = preg_replace( '/\s+/', '', $assoc_args['facets'] );
|
||||
$facet_names = explode( ',', $facet_names );
|
||||
foreach ( $facet_names as $name ) {
|
||||
$facet = FWP()->helper->get_facet_by_name( $name );
|
||||
if ( false !== $facet ) {
|
||||
$facets[] = $facet;
|
||||
}
|
||||
}
|
||||
|
||||
$index_all = false;
|
||||
}
|
||||
else {
|
||||
$facets = FWP()->helper->get_facets();
|
||||
}
|
||||
|
||||
$progress = WP_CLI\Utils\make_progress_bar( 'Indexing:', count( $post_ids ) );
|
||||
|
||||
// prep
|
||||
if ( $index_all ) {
|
||||
FWP()->indexer->manage_temp_table( 'create' );
|
||||
}
|
||||
else {
|
||||
$assoc_args['pre_index'] = true;
|
||||
$this->purge( $args, $assoc_args );
|
||||
}
|
||||
|
||||
// manually load value modifiers
|
||||
FWP()->indexer->load_value_modifiers( $facets );
|
||||
|
||||
// index
|
||||
foreach ( $post_ids as $post_id ) {
|
||||
FWP()->indexer->index_post( $post_id, $facets );
|
||||
$progress->tick();
|
||||
}
|
||||
|
||||
// cleanup
|
||||
if ( $index_all ) {
|
||||
update_option( 'facetwp_last_indexed', time(), 'no' );
|
||||
FWP()->indexer->manage_temp_table( 'replace' );
|
||||
FWP()->indexer->manage_temp_table( 'delete' );
|
||||
}
|
||||
|
||||
$progress->finish();
|
||||
|
||||
WP_CLI::success( 'Indexing complete.' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Purge facet data.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* [--ids=<ids>]
|
||||
* : Purge specific post IDs (comma-separated).
|
||||
*
|
||||
* [--facets=<facets>]
|
||||
* : Purge specific facet names (comma-separated).
|
||||
*/
|
||||
function purge( $args, $assoc_args ) {
|
||||
global $wpdb;
|
||||
|
||||
$table = FWP()->indexer->table;
|
||||
|
||||
if ( ! isset( $assoc_args['ids'] ) && ! isset( $assoc_args['facets'] ) ) {
|
||||
$sql = "TRUNCATE TABLE $table";
|
||||
}
|
||||
else {
|
||||
$where = [];
|
||||
|
||||
if ( isset( $assoc_args['ids'] ) ) {
|
||||
if ( empty( $assoc_args['ids'] ) ) {
|
||||
WP_CLI::error( 'IDs empty.' );
|
||||
}
|
||||
|
||||
$ids = preg_replace( '/\s+/', '', ',' . $assoc_args['ids'] );
|
||||
$ids = explode( ',', $ids );
|
||||
$post_ids = array_filter( $ids, 'ctype_digit' );
|
||||
$post_ids = implode( "','", $post_ids );
|
||||
$where[] = "post_id IN ('$post_ids')";
|
||||
}
|
||||
|
||||
if ( isset( $assoc_args['facets'] ) ) {
|
||||
if ( empty( $assoc_args['facets'] ) ) {
|
||||
WP_CLI::error( 'Facets empty.' );
|
||||
}
|
||||
|
||||
$facet_names = preg_replace( '/\s+/', '', $assoc_args['facets'] );
|
||||
$facet_names = explode( ',', $facet_names );
|
||||
$facet_names = array_map( 'esc_sql', $facet_names );
|
||||
$facet_names = implode( "','", $facet_names );
|
||||
$where[] = "facet_name IN ('$facet_names')";
|
||||
}
|
||||
|
||||
$sql = "DELETE FROM $table WHERE " . implode( ' AND ', $where );
|
||||
}
|
||||
|
||||
$wpdb->query( $sql );
|
||||
|
||||
if ( ! isset( $assoc_args['pre_index'] ) ) {
|
||||
WP_CLI::success( 'Purge complete.' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
||||
WP_CLI::add_command( 'facetwp', 'FacetWP_Integration_WP_CLI' );
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Integration_WP_Rocket
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
add_filter( 'rocket_exclude_defer_js', [ $this, 'get_exclusions' ] );
|
||||
add_filter( 'rocket_delay_js_exclusions', [ $this, 'get_exclusions' ] );
|
||||
add_filter( 'rocket_cdn_reject_files', [ $this, 'get_exclusions' ] );
|
||||
}
|
||||
|
||||
|
||||
function get_exclusions( $excluded ) {
|
||||
$excluded[] = '(.*)facetwp(.*)';
|
||||
$excluded[] = '(.*)maps.googleapis(.*)';
|
||||
$excluded[] = '/jquery-?[0-9.]*(.min|.slim|.slim.min)?.js';
|
||||
return $excluded;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( defined( 'WP_ROCKET_VERSION' ) ) {
|
||||
new FacetWP_Integration_WP_Rocket();
|
||||
}
|
||||
Reference in New Issue
Block a user