Merged in feature/plugins-update (pull request #9)
wp plugin updates from pantheon * wp plugin updates from pantheon
This commit is contained in:
180
wp/wp-content/plugins/facetwp/includes/api/fetch.php
Normal file
180
wp/wp-content/plugins/facetwp/includes/api/fetch.php
Normal file
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_API_Fetch
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
add_action( 'rest_api_init', [ $this, 'register' ] );
|
||||
}
|
||||
|
||||
|
||||
// PHP < 5.3
|
||||
function register() {
|
||||
register_rest_route( 'facetwp/v1', '/fetch', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [ $this, 'callback' ],
|
||||
'permission_callback' => [ $this, 'permission_callback' ]
|
||||
] );
|
||||
}
|
||||
|
||||
|
||||
// PHP < 5.3
|
||||
function callback( $request ) {
|
||||
$data = $request->get_param( 'data' );
|
||||
|
||||
if ( ! $request->is_json_content_type()) {
|
||||
$data = empty( $data ) ? [] : json_decode( $data, true );
|
||||
}
|
||||
|
||||
return $this->process_request( $data );
|
||||
}
|
||||
|
||||
|
||||
// PHP < 5.3
|
||||
function permission_callback( $request ) {
|
||||
return apply_filters( 'facetwp_api_can_access', false, $request );
|
||||
}
|
||||
|
||||
|
||||
function process_request( $params = [] ) {
|
||||
global $wpdb;
|
||||
|
||||
$defaults = [
|
||||
'facets' => [
|
||||
// 'category' => [ 'acf' ]
|
||||
],
|
||||
'query_args' => [
|
||||
'post_type' => 'post',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => 10,
|
||||
'paged' => 1,
|
||||
],
|
||||
'settings' => [
|
||||
'first_load' => true
|
||||
]
|
||||
];
|
||||
|
||||
$params = array_merge( $defaults, $params );
|
||||
$facet_types = FWP()->helper->facet_types;
|
||||
$valid_facets = [];
|
||||
$facets = [];
|
||||
|
||||
// Validate input
|
||||
$page = (int) ( $params['query_args']['paged'] ?? 1 );
|
||||
$per_page = (int) ( $params['query_args']['posts_per_page'] ?? 10 );
|
||||
|
||||
$page = max( $page, 1 );
|
||||
$per_page = ( 0 === $per_page ) ? 10 : $per_page;
|
||||
$per_page = ( -1 > $per_page ) ? absint( $per_page ) : $per_page;
|
||||
|
||||
$params['query_args']['paged'] = $page;
|
||||
$params['query_args']['posts_per_page'] = $per_page;
|
||||
|
||||
// Generate FWP()->facet->facets
|
||||
// Required by FWP()->helper->facet_setting_exists()
|
||||
foreach ( $params['facets'] as $facet_name => $facet_value ) {
|
||||
$facet = FWP()->helper->get_facet_by_name( $facet_name );
|
||||
if ( false !== $facet ) {
|
||||
$facet['selected_values'] = (array) $facet_value;
|
||||
$valid_facets[ $facet_name ] = $facet;
|
||||
FWP()->facet->facets[ $facet_name ] = $facet;
|
||||
}
|
||||
}
|
||||
|
||||
// Get bucket of post IDs
|
||||
$query_args = $params['query_args'];
|
||||
FWP()->facet->query_args = $query_args;
|
||||
$post_ids = FWP()->facet->get_filtered_post_ids( $query_args );
|
||||
|
||||
// SQL WHERE used by facets
|
||||
$where_clause = ' AND post_id IN (' . implode( ',', $post_ids ) . ')';
|
||||
|
||||
// Check if empty
|
||||
if ( 0 === $post_ids[0] && 1 === count( $post_ids ) ) {
|
||||
$post_ids = [];
|
||||
}
|
||||
|
||||
// get_where_clause() needs "found_posts" (keep this BELOW the empty check)
|
||||
FWP()->facet->query = (object) [ 'found_posts' => count( $post_ids ) ];
|
||||
|
||||
// Get valid facets and their values
|
||||
foreach ( $valid_facets as $facet_name => $facet ) {
|
||||
$args = [
|
||||
'facet' => $facet,
|
||||
'where_clause' => $where_clause,
|
||||
'selected_values' => $facet['selected_values'],
|
||||
];
|
||||
|
||||
$facet_data = [
|
||||
'name' => $facet['name'],
|
||||
'label' => $facet['label'],
|
||||
'type' => $facet['type'],
|
||||
'selected' => $facet['selected_values'],
|
||||
];
|
||||
|
||||
// Load facet choices if available
|
||||
if ( method_exists( $facet_types[ $facet['type'] ], 'load_values' ) ) {
|
||||
$choices = $facet_types[ $facet['type'] ]->load_values( $args );
|
||||
foreach ( $choices as $key => $choice ) {
|
||||
$row = [
|
||||
'value' => $choice['facet_value'],
|
||||
'label' => $choice['facet_display_value'],
|
||||
'depth' => (int) $choice['depth'],
|
||||
'count' => (int) $choice['counter'],
|
||||
];
|
||||
|
||||
if ( isset( $choice['term_id'] ) ) {
|
||||
$row['term_id'] = (int) $choice['term_id'];
|
||||
}
|
||||
|
||||
if ( isset( $choice['parent_id'] ) ) {
|
||||
$row['parent_id'] = (int) $choice['parent_id'];
|
||||
}
|
||||
|
||||
$choices[ $key ] = $row;
|
||||
}
|
||||
|
||||
$facet_data['choices'] = $choices;
|
||||
}
|
||||
|
||||
// Load facet settings if available
|
||||
if ( method_exists( $facet_types[ $facet['type'] ], 'settings_js' ) ) {
|
||||
$facet_data['settings'] = $facet_types[ $facet['type'] ]->settings_js( $args );
|
||||
}
|
||||
|
||||
$facets[ $facet_name ] = $facet_data;
|
||||
}
|
||||
|
||||
$total_rows = count( $post_ids );
|
||||
|
||||
// Paginate?
|
||||
if ( 0 < $per_page ) {
|
||||
$total_pages = ceil( $total_rows / $per_page );
|
||||
|
||||
if ( $page > $total_pages ) {
|
||||
$post_ids = [];
|
||||
}
|
||||
else {
|
||||
$offset = ( $per_page * ( $page - 1 ) );
|
||||
$post_ids = array_slice( $post_ids, $offset, $per_page );
|
||||
}
|
||||
}
|
||||
else {
|
||||
$total_pages = ( 0 < $total_rows ) ? 1 : 0;
|
||||
}
|
||||
|
||||
// Generate the output
|
||||
$output = [
|
||||
'results' => $post_ids,
|
||||
'facets' => $facets,
|
||||
'pager' => [
|
||||
'page' => $page,
|
||||
'per_page' => $per_page,
|
||||
'total_rows' => $total_rows,
|
||||
'total_pages' => $total_pages,
|
||||
]
|
||||
];
|
||||
|
||||
return apply_filters( 'facetwp_api_output', $output );
|
||||
}
|
||||
}
|
||||
27
wp/wp-content/plugins/facetwp/includes/api/refresh.php
Normal file
27
wp/wp-content/plugins/facetwp/includes/api/refresh.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
add_action( 'rest_api_init', function() {
|
||||
register_rest_route( 'facetwp/v1', '/refresh', [
|
||||
'methods' => 'POST',
|
||||
'callback' => 'facetwp_api_refresh',
|
||||
'permission_callback' => '__return_true'
|
||||
] );
|
||||
});
|
||||
|
||||
function facetwp_api_refresh( $request ) {
|
||||
$params = $request->get_params();
|
||||
$action = $params['action'] ?? '';
|
||||
|
||||
$valid_actions = [
|
||||
'facetwp_refresh',
|
||||
'facetwp_autocomplete_load'
|
||||
];
|
||||
|
||||
$valid_actions = apply_filters( 'facetwp_api_valid_actions', $valid_actions );
|
||||
|
||||
if ( in_array( $action, $valid_actions ) ) {
|
||||
do_action( $action );
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
321
wp/wp-content/plugins/facetwp/includes/class-ajax.php
Normal file
321
wp/wp-content/plugins/facetwp/includes/class-ajax.php
Normal file
@@ -0,0 +1,321 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Ajax
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
add_action( 'init', [ $this, 'switchboard' ], 1000 );
|
||||
}
|
||||
|
||||
|
||||
function switchboard() {
|
||||
$valid_actions = [
|
||||
'resume_index',
|
||||
'save_settings',
|
||||
'rebuild_index',
|
||||
'get_info',
|
||||
'get_query_args',
|
||||
'heartbeat',
|
||||
'license',
|
||||
'backup'
|
||||
];
|
||||
|
||||
$action = isset( $_POST['action'] ) ? sanitize_key( $_POST['action'] ) : '';
|
||||
$is_valid = false;
|
||||
|
||||
if ( 0 === strpos( $action, 'facetwp_' ) ) {
|
||||
$action = substr( $action, 8 );
|
||||
$is_valid = in_array( $action, $valid_actions );
|
||||
}
|
||||
|
||||
if ( $is_valid ) {
|
||||
|
||||
// Non-authenticated
|
||||
if ( in_array( $action, [ 'resume_index' ] ) ) {
|
||||
$this->$action();
|
||||
}
|
||||
|
||||
// Authenticated
|
||||
elseif ( current_user_can( 'manage_options' ) ) {
|
||||
if ( wp_verify_nonce( $_POST['nonce'], 'fwp_admin_nonce' ) ) {
|
||||
$this->$action();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for API refresh call
|
||||
add_action( 'facetwp_refresh', [ $this, 'refresh' ] );
|
||||
|
||||
// Backwards compatibility
|
||||
$this->url_vars = FWP()->request->url_vars;
|
||||
$this->is_preload = FWP()->request->is_preload;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save admin settings
|
||||
*/
|
||||
function save_settings() {
|
||||
$settings = $_POST['data'];
|
||||
|
||||
if ( isset( $settings['settings'] ) ) {
|
||||
update_option( 'facetwp_settings', json_encode( $settings ), 'no' );
|
||||
|
||||
if ( FWP()->diff->is_reindex_needed() ) {
|
||||
$response = [
|
||||
'code' => 'error',
|
||||
'message' => __( 'Settings saved, please re-index', 'fwp' )
|
||||
];
|
||||
}
|
||||
else {
|
||||
$response = [
|
||||
'code' => 'success',
|
||||
'message' => __( 'Settings saved', 'fwp' )
|
||||
];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$response = [
|
||||
'code' => 'error',
|
||||
'message' => __( 'Error: invalid JSON', 'fwp' )
|
||||
];
|
||||
}
|
||||
|
||||
wp_send_json( $response );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rebuild the index table
|
||||
*/
|
||||
function rebuild_index() {
|
||||
update_option( 'facetwp_indexing_cancelled', 'no', 'no' );
|
||||
FWP()->indexer->index();
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
function get_info() {
|
||||
$type = $_POST['type'];
|
||||
|
||||
if ( 'post_types' == $type ) {
|
||||
$post_types = get_post_types( [ 'exclude_from_search' => false, '_builtin' => false ] );
|
||||
$post_types = [ 'post', 'page' ] + $post_types;
|
||||
sort( $post_types );
|
||||
|
||||
$response = [
|
||||
'code' => 'success',
|
||||
'message' => implode( ', ', $post_types )
|
||||
];
|
||||
}
|
||||
elseif ( 'indexer_stats' == $type ) {
|
||||
$last_indexed = get_option( 'facetwp_last_indexed' );
|
||||
$last_indexed = $last_indexed ? human_time_diff( $last_indexed ) . ' ago' : 'never';
|
||||
|
||||
$response = [
|
||||
'code' => 'success',
|
||||
'message' => "last indexed: $last_indexed"
|
||||
];
|
||||
}
|
||||
elseif ( 'cancel_reindex' == $type ) {
|
||||
update_option( 'facetwp_indexing_cancelled', 'yes' );
|
||||
|
||||
$response = [
|
||||
'code' => 'success',
|
||||
'message' => 'Indexing cancelled'
|
||||
];
|
||||
}
|
||||
elseif ( 'purge_index_table' == $type ) {
|
||||
global $wpdb;
|
||||
|
||||
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}facetwp_index" );
|
||||
delete_option( 'facetwp_version' );
|
||||
delete_option( 'facetwp_indexing' );
|
||||
delete_option( 'facetwp_transients' );
|
||||
|
||||
$response = [
|
||||
'code' => 'success',
|
||||
'message' => __( 'Done, please re-index', 'fwp' )
|
||||
];
|
||||
}
|
||||
|
||||
wp_send_json( $response );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return query arguments based on a Query Builder object
|
||||
*/
|
||||
function get_query_args() {
|
||||
$query_obj = $_POST['query_obj'];
|
||||
|
||||
if ( is_array( $query_obj ) ) {
|
||||
$query_args = FWP()->builder->parse_query_obj( $query_obj );
|
||||
}
|
||||
|
||||
wp_send_json( $query_args );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Keep track of indexing progress
|
||||
*/
|
||||
function heartbeat() {
|
||||
$output = [
|
||||
'pct' => FWP()->indexer->get_progress()
|
||||
];
|
||||
|
||||
if ( -1 == $output['pct'] ) {
|
||||
$output['rows'] = FWP()->helper->get_row_counts();
|
||||
}
|
||||
|
||||
wp_send_json( $output );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* License activation
|
||||
*/
|
||||
function license() {
|
||||
$license = sanitize_key( $_POST['license'] );
|
||||
|
||||
$request = wp_remote_post( 'https://api.facetwp.com', [
|
||||
'body' => [
|
||||
'action' => 'activate',
|
||||
'slug' => 'facetwp',
|
||||
'license' => $license,
|
||||
'host' => FWP()->helper->get_http_host(),
|
||||
]
|
||||
] );
|
||||
|
||||
if ( ! is_wp_error( $request ) || 200 == wp_remote_retrieve_response_code( $request ) ) {
|
||||
update_option( 'facetwp_license', $license );
|
||||
update_option( 'facetwp_activation', $request['body'] );
|
||||
update_option( 'facetwp_updater_last_checked', 0 );
|
||||
echo $request['body'];
|
||||
}
|
||||
else {
|
||||
echo json_encode( [
|
||||
'status' => 'error',
|
||||
'message' => __( 'Error', 'fwp' ) . ': ' . $request->get_error_message(),
|
||||
] );
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Import / export functionality
|
||||
*/
|
||||
function backup() {
|
||||
$action_type = $_POST['action_type'];
|
||||
$output = [];
|
||||
|
||||
if ( 'export' == $action_type ) {
|
||||
$items = $_POST['items'];
|
||||
|
||||
if ( ! empty( $items ) ) {
|
||||
foreach ( $items as $item ) {
|
||||
if ( 'facet' == substr( $item, 0, 5 ) ) {
|
||||
$item_name = substr( $item, 6 );
|
||||
$output['facets'][] = FWP()->helper->get_facet_by_name( $item_name );
|
||||
}
|
||||
elseif ( 'template' == substr( $item, 0, 8 ) ) {
|
||||
$item_name = substr( $item, 9 );
|
||||
$output['templates'][] = FWP()->helper->get_template_by_name( $item_name );
|
||||
}
|
||||
}
|
||||
}
|
||||
echo json_encode( $output );
|
||||
}
|
||||
elseif ( 'import' == $action_type ) {
|
||||
$settings = FWP()->helper->settings;
|
||||
$import_code = $_POST['import_code'];
|
||||
$overwrite = (int) $_POST['overwrite'];
|
||||
|
||||
if ( empty( $import_code ) || ! is_array( $import_code ) ) {
|
||||
_e( 'Nothing to import', 'fwp' );
|
||||
exit;
|
||||
}
|
||||
|
||||
$status = [
|
||||
'imported' => [],
|
||||
'skipped' => [],
|
||||
];
|
||||
|
||||
foreach ( $import_code as $object_type => $object_items ) {
|
||||
foreach ( $object_items as $object_item ) {
|
||||
$is_match = false;
|
||||
foreach ( $settings[$object_type] as $key => $settings_item ) {
|
||||
if ( $object_item['name'] == $settings_item['name'] ) {
|
||||
if ( $overwrite ) {
|
||||
$settings[$object_type][$key] = $object_item;
|
||||
$status['imported'][] = $object_item['label'];
|
||||
}
|
||||
else {
|
||||
$status['skipped'][] = $object_item['label'];
|
||||
}
|
||||
$is_match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $is_match ) {
|
||||
$settings[$object_type][] = $object_item;
|
||||
$status['imported'][] = $object_item['label'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_option( 'facetwp_settings', json_encode( $settings ) );
|
||||
|
||||
if ( ! empty( $status['imported'] ) ) {
|
||||
echo ' [<strong>' . __( 'Imported', 'fwp' ) . '</strong>] ' . implode( ', ', $status['imported'] );
|
||||
}
|
||||
if ( ! empty( $status['skipped'] ) ) {
|
||||
echo ' [<strong>' . __( 'Skipped', 'fwp' ) . '</strong>] ' . implode( ', ', $status['skipped'] );
|
||||
}
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The AJAX facet refresh handler
|
||||
*/
|
||||
function refresh() {
|
||||
global $wpdb;
|
||||
|
||||
$params = FWP()->request->process_post_data();
|
||||
$output = FWP()->facet->render( $params );
|
||||
$data = stripslashes_deep( $_POST['data'] );
|
||||
|
||||
// Ignore invalid UTF-8 characters in PHP 7.2+
|
||||
if ( version_compare( phpversion(), '7.2', '<' ) ) {
|
||||
$output = json_encode( $output );
|
||||
}
|
||||
else {
|
||||
$output = json_encode( $output, JSON_INVALID_UTF8_IGNORE );
|
||||
}
|
||||
|
||||
echo apply_filters( 'facetwp_ajax_response', $output, [
|
||||
'data' => $data
|
||||
] );
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resume stalled indexer
|
||||
*/
|
||||
function resume_index() {
|
||||
$touch = (int) FWP()->indexer->get_transient( 'touch' );
|
||||
if ( 0 < $touch && $_POST['touch'] == $touch ) {
|
||||
FWP()->indexer->index();
|
||||
}
|
||||
exit;
|
||||
}
|
||||
}
|
||||
839
wp/wp-content/plugins/facetwp/includes/class-builder.php
Normal file
839
wp/wp-content/plugins/facetwp/includes/class-builder.php
Normal file
@@ -0,0 +1,839 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Builder
|
||||
{
|
||||
|
||||
public $css = [];
|
||||
public $data = [];
|
||||
public $custom_css;
|
||||
|
||||
|
||||
function __construct() {
|
||||
add_filter( 'facetwp_query_args', [ $this, 'hydrate_date_values' ], 999 );
|
||||
add_filter( 'facetwp_builder_dynamic_tag_value', [ $this, 'dynamic_tag_value' ], 0, 3 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate CSS class string (helper method)
|
||||
* @since 3.9.3
|
||||
*/
|
||||
function get_classes( $type, $settings ) {
|
||||
$classes = [ $type, $settings['name'], $settings['css_class'] ];
|
||||
return trim( implode( ' ', $classes ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the layout HTML
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function render_layout( $layout ) {
|
||||
global $wp_query, $post;
|
||||
|
||||
$counter = 0;
|
||||
$settings = $layout['settings'];
|
||||
$this->custom_css = $settings['custom_css'];
|
||||
|
||||
$selector = '.fwpl-layout';
|
||||
$selector .= empty( $settings['name'] ) ? '' : '.' . $settings['name'];
|
||||
|
||||
$this->css = [
|
||||
'.fwpl-layout, .fwpl-row' => [
|
||||
'display' => 'grid'
|
||||
],
|
||||
$selector => [
|
||||
'grid-template-columns' => 'repeat(' . $settings['num_columns'] . ', 1fr)',
|
||||
'grid-gap' => $settings['grid_gap'] . 'px'
|
||||
],
|
||||
$selector . ' .fwpl-result' => $this->build_styles( $settings )
|
||||
];
|
||||
|
||||
$classes = $this->get_classes( 'fwpl-layout', $settings );
|
||||
|
||||
$output = '<div class="' . $classes . '">';
|
||||
|
||||
if ( have_posts() ) {
|
||||
while ( have_posts() ) : the_post();
|
||||
$counter++;
|
||||
|
||||
// Default dynamic tags
|
||||
$tags = [
|
||||
'post:id' => $post->ID,
|
||||
'post:name' => $post->post_name,
|
||||
'post:type' => $post->post_type,
|
||||
'post:title' => $post->post_title,
|
||||
'post:url' => get_permalink()
|
||||
];
|
||||
|
||||
$params = [
|
||||
'layout' => $layout,
|
||||
'post' => $post
|
||||
];
|
||||
|
||||
$this->data = apply_filters( 'facetwp_builder_dynamic_tags', $tags, $params );
|
||||
|
||||
$output .= '<div class="fwpl-result r' . $counter . '">';
|
||||
|
||||
foreach ( $layout['items'] as $row ) {
|
||||
$output .= $this->render_row( $row );
|
||||
}
|
||||
|
||||
$output .= '</div>';
|
||||
|
||||
$output = $this->parse_dynamic_tags( $output, $params );
|
||||
|
||||
endwhile;
|
||||
}
|
||||
else {
|
||||
$no_results_text = $settings['no_results_text'] ?? '';
|
||||
$output .= do_shortcode( $no_results_text );
|
||||
}
|
||||
|
||||
$output .= '</div>';
|
||||
|
||||
$output .= $this->render_css();
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the row HTML
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function render_row( $row ) {
|
||||
$settings = $row['settings'];
|
||||
|
||||
$this->css['.fwpl-row.' . $settings['name'] ] = $this->build_styles( $settings );
|
||||
|
||||
$classes = $this->get_classes( 'fwpl-row', $settings );
|
||||
|
||||
$output = '<div class="' . $classes . '">';
|
||||
|
||||
foreach ( $row['items'] as $col ) {
|
||||
$output .= $this->render_col( $col );
|
||||
}
|
||||
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the col HTML
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function render_col( $col ) {
|
||||
$settings = $col['settings'];
|
||||
|
||||
$this->css['.fwpl-col.' . $settings['name'] ] = $this->build_styles( $settings );
|
||||
|
||||
$classes = $this->get_classes( 'fwpl-col', $settings );
|
||||
|
||||
$output = '<div class="fwpl-col ' . $classes . '">';
|
||||
|
||||
foreach ( $col['items'] as $item ) {
|
||||
if ( 'row' == $item['type'] ) {
|
||||
$output .= $this->render_row( $item );
|
||||
}
|
||||
elseif ( 'item' == $item['type'] ) {
|
||||
$output .= $this->render_item( $item );
|
||||
}
|
||||
}
|
||||
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the item HTML
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function render_item( $item ) {
|
||||
global $post;
|
||||
|
||||
$settings = $item['settings'];
|
||||
$name = $settings['name'];
|
||||
$source = $item['source'];
|
||||
$value = $source;
|
||||
|
||||
$selector = '.fwpl-item.' . $name;
|
||||
$selector = ( 'button' == $source ) ? $selector . ' button' : $selector;
|
||||
$this->css[ $selector ] = $this->build_styles( $settings );
|
||||
|
||||
if ( 0 === strpos( $source, 'post_' ) || 'ID' == $source ) {
|
||||
if ( 'post_title' == $source ) {
|
||||
$value = $this->linkify( $post->$source, $settings['link'] );
|
||||
}
|
||||
elseif ( 'post_excerpt' == $source ) {
|
||||
$value = get_the_excerpt( $post->ID );
|
||||
}
|
||||
elseif ( 'post_content' == $source ) {
|
||||
$value = apply_filters( 'the_content', $post->post_content );
|
||||
}
|
||||
elseif ( 'post_author' == $source ) {
|
||||
$field = $settings['author_field'];
|
||||
$user = get_user_by( 'id', $post->$source );
|
||||
$value = $user->$field;
|
||||
}
|
||||
elseif ( 'post_type' == $source ) {
|
||||
$pt_obj = get_post_type_object( $post->$source );
|
||||
$value = $pt_obj->labels->singular_name ?? $post->$source;
|
||||
}
|
||||
else {
|
||||
$value = $post->$source;
|
||||
}
|
||||
}
|
||||
elseif ( 0 === strpos( $source, 'cf/' ) ) {
|
||||
$value = get_post_meta( $post->ID, substr( $source, 3 ), true );
|
||||
$value = $this->linkify( $value, $settings['link'] );
|
||||
}
|
||||
elseif ( 0 === strpos( $source, 'tax/' ) ) {
|
||||
$temp = [];
|
||||
$taxonomy = substr( $source, 4 );
|
||||
$terms = get_the_terms( $post->ID, $taxonomy );
|
||||
|
||||
if ( is_array( $terms ) ) {
|
||||
foreach ( $terms as $term_obj ) {
|
||||
$term = $this->linkify( $term_obj->name, $settings['term_link'], [
|
||||
'term_id' => $term_obj->term_id,
|
||||
'taxonomy' => $taxonomy
|
||||
] );
|
||||
|
||||
$temp[] = '<span class="fwpl-term fwpl-term-' . $term_obj->slug . ' fwpl-tax-' . $taxonomy . '">' . $term . '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
$value = implode( $settings['separator'], $temp );
|
||||
}
|
||||
elseif ( 0 === strpos( $source, 'woo/' ) ) {
|
||||
$field = substr( $source, 4 );
|
||||
$product = wc_get_product( $post->ID );
|
||||
|
||||
// Invalid product
|
||||
if ( ! is_object( $product ) ) {
|
||||
$value = '';
|
||||
}
|
||||
|
||||
// Price
|
||||
elseif ( 'price' == $field || 'sale_price' == $field || 'regular_price' == $field ) {
|
||||
if ( $product->is_type( 'variable' ) ) {
|
||||
$method_name = "get_variation_$field";
|
||||
$value = $product->$method_name( 'min' ); // get_variation_price()
|
||||
}
|
||||
else {
|
||||
$method_name = "get_$field";
|
||||
$value = $product->$method_name(); // get_price()
|
||||
}
|
||||
}
|
||||
|
||||
// Average Rating
|
||||
elseif ( 'average_rating' == $field ) {
|
||||
$value = $product->get_average_rating();
|
||||
}
|
||||
|
||||
// Stock Status
|
||||
elseif ( 'stock_status' == $field ) {
|
||||
$value = $product->is_in_stock() ? __( 'In Stock', 'fwp' ) : __( 'Out of Stock', 'fwp' );
|
||||
}
|
||||
|
||||
// On Sale
|
||||
elseif ( 'on_sale' == $field ) {
|
||||
$value = $product->is_on_sale() ? __( 'On Sale', 'fwp' ) : '';
|
||||
}
|
||||
|
||||
// Product Type
|
||||
elseif ( 'product_type' == $field ) {
|
||||
$value = $product->get_type();
|
||||
}
|
||||
}
|
||||
elseif ( 0 === strpos( $source, 'acf/' ) && isset( FWP()->acf ) ) {
|
||||
$value = FWP()->acf->get_field( $source, $post->ID );
|
||||
}
|
||||
elseif ( 'featured_image' == $source ) {
|
||||
$value = get_the_post_thumbnail( $post->ID, $settings['image_size'] );
|
||||
$value = $this->linkify( $value, $settings['link'] );
|
||||
}
|
||||
elseif ( 'button' == $source ) {
|
||||
$value = '<button>' . $settings['button_text'] . '</button>';
|
||||
$value = $this->linkify( $value, $settings['link'] );
|
||||
}
|
||||
elseif ( 'html' == $source ) {
|
||||
$value = do_shortcode( $settings['content'] );
|
||||
}
|
||||
|
||||
// Date format
|
||||
if ( ! empty( $settings['date_format'] ) && ! empty( $value ) ) {
|
||||
if ( ! empty( $settings['input_format'] ) ) {
|
||||
$date = DateTime::createFromFormat( $settings['input_format'], $value );
|
||||
}
|
||||
else {
|
||||
$date = new DateTime( $value );
|
||||
}
|
||||
|
||||
// Use wp_date() to support i18n
|
||||
if ( $date ) {
|
||||
$value = wp_date( $settings['date_format'], $date->getTimestamp(), new DateTimeZone( 'UTC' ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Number format
|
||||
if ( ! empty( $settings['number_format'] ) && ! empty( $value ) ) {
|
||||
$decimals = 2;
|
||||
$format = $settings['number_format'];
|
||||
$decimal_sep = FWP()->helper->get_setting( 'decimal_separator' );
|
||||
$thousands_sep = FWP()->helper->get_setting( 'thousands_separator' );
|
||||
|
||||
// No thousands separator
|
||||
if ( false === strpos( $format, ',' ) ) {
|
||||
$thousands_sep = '';
|
||||
}
|
||||
|
||||
// Handle decimals
|
||||
if ( false === ( $pos = strpos( $format, '.' ) ) ) {
|
||||
$decimals = 0;
|
||||
}
|
||||
else {
|
||||
$decimals = strlen( $format ) - $pos - 1;
|
||||
}
|
||||
|
||||
$value = number_format( $value, $decimals, $decimal_sep, $thousands_sep );
|
||||
}
|
||||
|
||||
$output = '';
|
||||
$prefix = $settings['prefix'] ?? '';
|
||||
$suffix = $settings['suffix'] ?? '';
|
||||
|
||||
// Allow value hooks
|
||||
$value = apply_filters( 'facetwp_builder_item_value', $value, $item );
|
||||
|
||||
// Convert array to string
|
||||
if ( is_array( $value ) ) {
|
||||
$value = implode( ', ', $value );
|
||||
}
|
||||
|
||||
// Store the RAW short-tag
|
||||
$this->data[ "$name:raw" ] = $value;
|
||||
|
||||
// Attach the prefix / suffix to the value
|
||||
if ( '' != $value ) {
|
||||
$value = $prefix . $value . $suffix;
|
||||
}
|
||||
|
||||
// Store the short-tag
|
||||
$this->data[ $name ] = $value;
|
||||
|
||||
// Build the list of CSS classes
|
||||
$classes = $this->get_classes( 'fwpl-item', $settings );
|
||||
|
||||
if ( '' == $value ) {
|
||||
$classes .= ' is-empty';
|
||||
}
|
||||
|
||||
// Prevent output
|
||||
if ( ! $settings['is_hidden'] ) {
|
||||
$output = '<div class="' . $classes . '">' . $value . '</div>';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse dynamic tags, e.g. {{ first_name }}
|
||||
*/
|
||||
function parse_dynamic_tags( $output, $params ) {
|
||||
$pattern = '/({{[ ]?(.*?)[ ]?}})/s';
|
||||
|
||||
return preg_replace_callback( $pattern, function( $matches ) use( $params ) {
|
||||
$tag_name = $matches[2];
|
||||
$tag_value = $this->data[ $tag_name ] ?? '';
|
||||
return apply_filters( 'facetwp_builder_dynamic_tag_value', $tag_value, $tag_name, $params );
|
||||
}, $output );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate some dynamic tag values on-the-fly, to prevent
|
||||
* unnecessary queries and extra load time
|
||||
*/
|
||||
function dynamic_tag_value( $tag_value, $tag_name, $params ) {
|
||||
if ( 'post:image' == $tag_name ) {
|
||||
$tag_value = get_the_post_thumbnail_url( $params['post']->ID, 'full' );
|
||||
}
|
||||
|
||||
return $tag_value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build the redundant styles (border, padding,etc)
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function build_styles( $settings ) {
|
||||
$styles = [];
|
||||
|
||||
if ( isset( $settings['grid_template_columns'] ) ) {
|
||||
$styles['grid-template-columns'] = $settings['grid_template_columns'];
|
||||
}
|
||||
if ( isset( $settings['border'] ) ) {
|
||||
$styles['border-style'] = $settings['border']['style'];
|
||||
$styles['border-color'] = $settings['border']['color'];
|
||||
$styles['border-width'] = $this->get_widths( $settings['border']['width'] );
|
||||
}
|
||||
if ( isset( $settings['background_color'] ) ) {
|
||||
$styles['background-color'] = $settings['background_color'];
|
||||
}
|
||||
if ( isset( $settings['padding'] ) ) {
|
||||
$styles['padding'] = $this->get_widths( $settings['padding'] );
|
||||
}
|
||||
if ( isset( $settings['text_style'] ) ) {
|
||||
$styles['text-align'] = $settings['text_style']['align'];
|
||||
$styles['font-weight'] = $settings['text_style']['bold'] ? 'bold' : '';
|
||||
$styles['font-style'] = $settings['text_style']['italic'] ? 'italic' : '';
|
||||
}
|
||||
if ( isset( $settings['font_size'] ) ) {
|
||||
$styles['font-size'] = $settings['font_size']['size'] . $settings['font_size']['unit'];
|
||||
}
|
||||
if ( isset( $settings['text_color'] ) ) {
|
||||
$styles['color'] = $settings['text_color'];
|
||||
}
|
||||
if ( isset( $settings['button_border'] ) ) {
|
||||
$border = $settings['button_border'];
|
||||
$width = $border['width'];
|
||||
$unit = $width['unit'];
|
||||
|
||||
$styles['color'] = $settings['button_text_color'];
|
||||
$styles['background-color'] = $settings['button_color'];
|
||||
$styles['padding'] = $this->get_widths( $settings['button_padding'] );
|
||||
$styles['border-style'] = $border['style'];
|
||||
$styles['border-color'] = $border['color'];
|
||||
$styles['border-top-width'] = $width['top'] . $unit;
|
||||
$styles['border-right-width'] = $width['right'] . $unit;
|
||||
$styles['border-bottom-width'] = $width['bottom'] . $unit;
|
||||
$styles['border-left-width'] = $width['left'] . $unit;
|
||||
}
|
||||
|
||||
return $styles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build the CSS widths, e.g. for "padding" or "border-width"
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function get_widths( $data ) {
|
||||
$unit = $data['unit'];
|
||||
$top = $data['top'];
|
||||
$right = $data['right'];
|
||||
$bottom = $data['bottom'];
|
||||
$left = $data['left'];
|
||||
|
||||
if ( $top == $right && $right == $bottom && $bottom == $left ) {
|
||||
return "$top$unit";
|
||||
}
|
||||
elseif ( $top == $bottom && $left == $right ) {
|
||||
return "$top$unit $left$unit";
|
||||
}
|
||||
|
||||
return "$top$unit $right$unit $bottom$unit $left$unit";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a value into a link
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function linkify( $value, $link_data, $term_data = [] ) {
|
||||
global $post;
|
||||
|
||||
$type = $link_data['type'];
|
||||
$href = $link_data['href'];
|
||||
$target = $link_data['target'];
|
||||
|
||||
if ( 'none' !== $type ) {
|
||||
if ( 'post' == $type ) {
|
||||
$href = get_permalink();
|
||||
}
|
||||
if ( 'term' == $type ) {
|
||||
$href = get_term_link( $term_data['term_id'], $term_data['taxonomy'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $target ) ) {
|
||||
$target = ' target="' . $target . '"';
|
||||
}
|
||||
|
||||
$value = '<a href="' . $href . '"' . $target . '>' . $value . '</a>';
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Turn the CSS array into valid CSS
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function render_css() {
|
||||
$output = "\n<style>\n";
|
||||
|
||||
foreach ( $this->css as $selector => $props ) {
|
||||
$valid_rules = $this->get_valid_css_rules( $props );
|
||||
|
||||
if ( ! empty( $valid_rules ) ) {
|
||||
$output .= $selector . " {\n";
|
||||
foreach ( $valid_rules as $prop => $value ) {
|
||||
$output .= " $prop: $value;\n";
|
||||
}
|
||||
$output .= "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $this->custom_css ) ) {
|
||||
$output .= $this->custom_css . "\n";
|
||||
}
|
||||
|
||||
$output .= "</style>\n";
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter out empty or invalid rules
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function get_valid_css_rules( $props ) {
|
||||
$rules = [];
|
||||
|
||||
foreach ( $props as $prop => $value ) {
|
||||
if ( $this->is_valid_css_rule( $prop, $value ) ) {
|
||||
$rules[ $prop ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Optimize CSS rules
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function is_valid_css_rule( $prop, $value ) {
|
||||
$return = true;
|
||||
|
||||
if ( empty( $value ) || 'px' === $value || '0px' === $value || 'none' === $value ) {
|
||||
$return = false;
|
||||
}
|
||||
|
||||
if ( 'font-size' === $prop && '0px' === $value ) {
|
||||
$return = false;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make sure the query is valid
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function parse_query_obj( $query_obj ) {
|
||||
$output = [];
|
||||
$tax_query = [];
|
||||
$meta_query = [];
|
||||
$date_query = [];
|
||||
$post_type = 'any';
|
||||
$post_status = [ 'publish' ];
|
||||
$posts_per_page = 10;
|
||||
$post_in = [];
|
||||
$post_not_in = [];
|
||||
$author_in = [];
|
||||
$author_not_in = [];
|
||||
$orderby = [];
|
||||
|
||||
if ( ! empty( $query_obj['posts_per_page'] ) ) {
|
||||
$posts_per_page = (int) $query_obj['posts_per_page'];
|
||||
}
|
||||
|
||||
if ( ! empty( $query_obj['post_type'] ) ) {
|
||||
$post_type = array_column( $query_obj['post_type'], 'value' );
|
||||
}
|
||||
|
||||
if ( empty( $query_obj['filters'] ) ) {
|
||||
$query_obj['filters'] = [];
|
||||
}
|
||||
|
||||
if ( empty( $query_obj['orderby'] ) ) {
|
||||
$query_obj['orderby'] = [];
|
||||
}
|
||||
|
||||
foreach ( $query_obj['filters'] as $filter ) {
|
||||
$key = $filter['key'];
|
||||
$value = $filter['value'];
|
||||
$compare = $filter['compare'];
|
||||
$type = $filter['type'];
|
||||
|
||||
// Cast as decimal for more accuracy
|
||||
$type = ( 'NUMERIC' == $type ) ? 'DECIMAL(16,4)' : $type;
|
||||
$exists_bypass = false;
|
||||
$value_bypass = false;
|
||||
|
||||
// Clear the value for certain compare types
|
||||
if ( in_array( $compare, [ 'EXISTS', 'NOT EXISTS', 'EMPTY', 'NOT EMPTY' ] ) ) {
|
||||
$value_bypass = true;
|
||||
$value = '';
|
||||
}
|
||||
|
||||
if ( in_array( $compare, [ 'EXISTS', 'NOT EXISTS' ] ) ) {
|
||||
$exists_bypass = true;
|
||||
}
|
||||
|
||||
// If "EMPTY", use "=" compare type w/ empty string value
|
||||
if ( in_array( $compare, [ 'EMPTY', 'NOT EMPTY' ] ) ) {
|
||||
$compare = ( 'EMPTY' == $compare ) ? '=' : '!=';
|
||||
}
|
||||
|
||||
// Handle multiple values
|
||||
if ( is_array( $value ) ) {
|
||||
if ( in_array( $compare, [ '=', '!=' ] ) ) {
|
||||
$compare = ( '=' == $compare ) ? 'IN' : 'NOT IN';
|
||||
}
|
||||
|
||||
if ( ! in_array( $compare, [ 'IN', 'NOT IN' ] ) ) {
|
||||
$value = $value[0];
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $value ) && ! $value_bypass ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Support dynamic URL vars
|
||||
$value = $this->parse_uri_tags( $value );
|
||||
|
||||
// Prepend with "date|" so we can populate with hydrate_date_values()
|
||||
if ( 'DATE' == $type ) {
|
||||
$value = 'date|' . $value;
|
||||
}
|
||||
|
||||
if ( 'ID' == $key ) {
|
||||
$arg_name = ( 'IN' == $compare ) ? 'post_in' : 'post_not_in';
|
||||
$$arg_name = $value;
|
||||
}
|
||||
elseif ( 'post_author' == $key ) {
|
||||
$arg_name = ( 'IN' == $compare ) ? 'author_in' : 'author_not_in';
|
||||
$$arg_name = $value;
|
||||
}
|
||||
elseif ( 'post_status' == $key ) {
|
||||
$post_status = $value;
|
||||
}
|
||||
elseif ( 'post_date' == $key || 'post_modified' == $key ) {
|
||||
if ( '>' == $compare || '>=' == $compare ) {
|
||||
$date_query[] = [
|
||||
'after' => $value,
|
||||
'inclusive' => ( '>=' == $compare )
|
||||
];
|
||||
}
|
||||
if ( '<' == $compare || '<=' == $compare ) {
|
||||
$date_query[] = [
|
||||
'before' => $value,
|
||||
'inclusive' => ( '<=' == $compare )
|
||||
];
|
||||
}
|
||||
}
|
||||
elseif ( 0 === strpos( $key, 'tax/' ) ) {
|
||||
$temp = [
|
||||
'taxonomy' => substr( $key, 4 ),
|
||||
'field' => 'slug',
|
||||
'operator' => $compare
|
||||
];
|
||||
|
||||
if ( ! $exists_bypass ) {
|
||||
$temp['terms'] = $value;
|
||||
}
|
||||
|
||||
$tax_query[] = $temp;
|
||||
}
|
||||
else {
|
||||
$temp = [
|
||||
'key' => substr( $key, strpos( $key, '/' ) + 1 ),
|
||||
'compare' => $compare,
|
||||
'type' => $type
|
||||
];
|
||||
|
||||
if ( ! $exists_bypass ) {
|
||||
$temp['value'] = $value;
|
||||
}
|
||||
|
||||
$meta_query[] = $temp;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $query_obj['orderby'] as $index => $data ) {
|
||||
if ( 'cf/' == substr( $data['key'], 0, 3 ) ) {
|
||||
$type = $data['type'];
|
||||
|
||||
// Cast as decimal for more accuracy
|
||||
$type = ( 'NUMERIC' == $type ) ? 'DECIMAL(16,4)' : $type;
|
||||
|
||||
$meta_query['sort_' . $index] = [
|
||||
'key' => substr( $data['key'], 3 ),
|
||||
'type' => $type
|
||||
];
|
||||
|
||||
$orderby['sort_' . $index] = $data['order'];
|
||||
}
|
||||
else {
|
||||
$orderby[ $data['key'] ] = $data['order'];
|
||||
}
|
||||
}
|
||||
|
||||
$temp = [
|
||||
'post_type' => $post_type,
|
||||
'post_status' => $post_status,
|
||||
'meta_query' => $meta_query,
|
||||
'tax_query' => $tax_query,
|
||||
'date_query' => $date_query,
|
||||
'post__in' => $post_in,
|
||||
'post__not_in' => $post_not_in,
|
||||
'author__in' => $author_in,
|
||||
'author__not_in' => $author_not_in,
|
||||
'orderby' => $orderby,
|
||||
'posts_per_page' => $posts_per_page
|
||||
];
|
||||
|
||||
foreach ( $temp as $key => $val ) {
|
||||
if ( ! empty( $val ) ) {
|
||||
$output[ $key ] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get necessary values for the layout builder
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function get_layout_data() {
|
||||
$sources = FWP()->helper->get_data_sources();
|
||||
unset( $sources['post'] );
|
||||
|
||||
// Static options
|
||||
$output = [
|
||||
'row' => 'Child Row',
|
||||
'html' => 'HTML',
|
||||
'button' => 'Button',
|
||||
'featured_image' => 'Featured Image',
|
||||
'ID' => 'Post ID',
|
||||
'post_title' => 'Post Title',
|
||||
'post_name' => 'Post Name',
|
||||
'post_content' => 'Post Content',
|
||||
'post_excerpt' => 'Post Excerpt',
|
||||
'post_date' => 'Post Date',
|
||||
'post_modified' => 'Post Modified',
|
||||
'post_author' => 'Post Author',
|
||||
'post_type' => 'Post Type'
|
||||
];
|
||||
|
||||
foreach ( $sources as $group ) {
|
||||
foreach ( $group['choices'] as $name => $label ) {
|
||||
$output[ $name ] = $label;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get necessary data for the query builder
|
||||
* @since 3.0.0
|
||||
*/
|
||||
function get_query_data() {
|
||||
$builder_post_types = [];
|
||||
|
||||
$post_types = get_post_types( [ 'public' => true ], 'objects' );
|
||||
$data_sources = FWP()->helper->get_data_sources( 'builder' );
|
||||
|
||||
// Remove ACF choices
|
||||
unset( $data_sources['acf'] );
|
||||
|
||||
foreach ( $post_types as $type ) {
|
||||
$builder_post_types[] = [
|
||||
'label' => $type->labels->name,
|
||||
'value' => $type->name
|
||||
];
|
||||
}
|
||||
|
||||
$data_sources['posts']['choices'] = [
|
||||
'ID' => 'ID',
|
||||
'post_author' => 'Post Author',
|
||||
'post_status' => 'Post Status',
|
||||
'post_date' => 'Post Date',
|
||||
'post_modified' => 'Post Modified'
|
||||
];
|
||||
|
||||
return apply_filters( 'facetwp_builder_query_data', [
|
||||
'post_types' => $builder_post_types,
|
||||
'filter_by' => $data_sources
|
||||
] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace "date|" placeholders with actual dates
|
||||
*/
|
||||
function hydrate_date_values( $query_args ) {
|
||||
if ( isset( $query_args['meta_query'] ) ) {
|
||||
foreach ( $query_args['meta_query'] as $index => $row ) {
|
||||
if ( isset( $row['value'] ) && is_string( $row['value'] ) && 0 === strpos( $row['value'], 'date|' ) ) {
|
||||
$value = trim( substr( $row['value'], 5 ) );
|
||||
$value = date( 'Y-m-d', strtotime( $value ) );
|
||||
$query_args['meta_query'][ $index ]['value'] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $query_args;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Let users pull URI or GET params into the query builder
|
||||
* E.g. "http:uri", "http:uri:0", or "http:get:year"
|
||||
* @since 3.6.0
|
||||
*/
|
||||
function parse_uri_tags( $values ) {
|
||||
$temp = (array) $values;
|
||||
|
||||
foreach ( $temp as $key => $value ) {
|
||||
if ( 0 === strpos( $value, 'http:uri' ) ) {
|
||||
$uri = FWP()->helper->get_uri();
|
||||
$uri_parts = explode( '/', $uri );
|
||||
$tag_parts = explode( ':', $value );
|
||||
if ( isset( $tag_parts[2] ) ) {
|
||||
$index = (int) $tag_parts[2];
|
||||
$index = ( $index < 0 ) ? count( $uri_parts ) + $index : $index;
|
||||
$temp[ $key ] = $uri_parts[ $index ] ?? '';
|
||||
}
|
||||
else {
|
||||
$temp[ $key ] = $uri;
|
||||
}
|
||||
}
|
||||
elseif ( 0 === strpos( $value, 'http:get:' ) ) {
|
||||
$tag_parts = explode( ':', $value );
|
||||
$temp[ $key ] = $_GET[ $tag_parts[2] ] ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
return is_array( $values ) ? $temp : $temp[0];
|
||||
}
|
||||
}
|
||||
73
wp/wp-content/plugins/facetwp/includes/class-diff.php
Normal file
73
wp/wp-content/plugins/facetwp/includes/class-diff.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Diff
|
||||
{
|
||||
|
||||
/**
|
||||
* Compare "facetwp_settings" with "facetwp_settings_last_index" to determine
|
||||
* whether the user needs to rebuild the index
|
||||
* @since 3.0.9
|
||||
*/
|
||||
function is_reindex_needed() {
|
||||
$s1 = FWP()->helper->load_settings();
|
||||
$s2 = FWP()->helper->load_settings( true );
|
||||
|
||||
// Compare settings
|
||||
$to_check = [ 'thousands_separator', 'decimal_separator', 'wc_enable_variations', 'wc_index_all' ];
|
||||
|
||||
foreach ( $to_check as $name ) {
|
||||
$attr1 = $this->get_attr( $name, $s1['settings'] );
|
||||
$attr2 = $this->get_attr( $name, $s2['settings'] );
|
||||
if ( $attr1 !== $attr2 ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Get facets, removing non-indexable ones
|
||||
$f1 = array_filter( $s1['facets'], [ $this, 'is_indexable' ] );
|
||||
$f2 = array_filter( $s2['facets'], [ $this, 'is_indexable' ] );
|
||||
|
||||
// The facet count is different
|
||||
if ( count( $f1 ) !== count( $f2 ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sort the facets alphabetically
|
||||
usort( $f1, function( $a, $b ) {
|
||||
return strcmp( $a['name'], $b['name'] );
|
||||
});
|
||||
|
||||
usort( $f2, function( $a, $b ) {
|
||||
return strcmp( $a['name'], $b['name'] );
|
||||
});
|
||||
|
||||
// Compare facet properties
|
||||
$to_check = [ 'name', 'type', 'source', 'source_other', 'parent_term', 'hierarchical', 'modifier_type', 'modifier_values' ];
|
||||
|
||||
foreach ( $f1 as $index => $facet ) {
|
||||
foreach ( $to_check as $attr ) {
|
||||
$attr1 = $this->get_attr( $attr, $facet );
|
||||
$attr2 = $this->get_attr( $attr, $f2[ $index ] );
|
||||
if ( $attr1 !== $attr2 ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function is_indexable( $facet ) {
|
||||
return ! in_array( $facet['type'], [ 'search', 'pager', 'reset', 'sort' ] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array element
|
||||
* @since 3.0.9
|
||||
*/
|
||||
function get_attr( $name, $collection ) {
|
||||
return $collection[ $name ] ?? false;
|
||||
}
|
||||
}
|
||||
244
wp/wp-content/plugins/facetwp/includes/class-display.php
Normal file
244
wp/wp-content/plugins/facetwp/includes/class-display.php
Normal file
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Display
|
||||
{
|
||||
|
||||
/* (array) Facet types being used on the page */
|
||||
public $active_types = [];
|
||||
|
||||
/* (array) Facets being used on the page */
|
||||
public $active_facets = [];
|
||||
|
||||
/* (array) Extra features used on the page */
|
||||
public $active_extras = [];
|
||||
|
||||
/* (array) Saved shortcode attributes */
|
||||
public $shortcode_atts = [];
|
||||
|
||||
/* (boolean) Whether to enable FacetWP for the current page */
|
||||
public $load_assets = false;
|
||||
|
||||
/* (array) Scripts and stylesheets to enqueue */
|
||||
public $assets = [];
|
||||
|
||||
/* (array) Data to pass to front-end JS */
|
||||
public $json = [];
|
||||
|
||||
|
||||
function __construct() {
|
||||
add_filter( 'widget_text', 'do_shortcode' );
|
||||
add_action( 'loop_start', [ $this, 'add_template_tag' ] );
|
||||
add_action( 'loop_no_results', [ $this, 'add_template_tag' ] );
|
||||
add_action( 'wp_footer', [ $this, 'front_scripts' ], 25 );
|
||||
add_shortcode( 'facetwp', [ $this, 'shortcode' ] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Detect the loop container if the "facetwp-template" class is missing
|
||||
*/
|
||||
function add_template_tag( $wp_query ) {
|
||||
if ( true === $wp_query->get( 'facetwp' ) && did_action( 'wp_head' ) ) {
|
||||
echo "<!--fwp-loop-->\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set default values for atts
|
||||
*
|
||||
* Old: [facetwp template="foo" static]
|
||||
* New: [facetwp template="foo" static="true"]
|
||||
*/
|
||||
function normalize_atts( $atts ) {
|
||||
foreach ( $atts as $key => $val ) {
|
||||
if ( is_int( $key ) ) {
|
||||
$atts[ $val ] = true;
|
||||
unset( $atts[ $key ] );
|
||||
}
|
||||
}
|
||||
return $atts;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register shortcodes
|
||||
*/
|
||||
function shortcode( $atts ) {
|
||||
$atts = $this->normalize_atts( $atts );
|
||||
$this->shortcode_atts[] = $atts;
|
||||
|
||||
$output = '';
|
||||
if ( isset( $atts['facet'] ) ) {
|
||||
$facet = FWP()->helper->get_facet_by_name( $atts['facet'] );
|
||||
|
||||
if ( $facet ) {
|
||||
$ui = empty( $facet['ui_type'] ) ? $facet['type'] : $facet['ui_type'];
|
||||
$ui_attr = empty( $facet['ui_type'] ) ? '' : ' data-ui="' . $ui . '"';
|
||||
$output = '<div class="facetwp-facet facetwp-facet-' . $facet['name'] . ' facetwp-type-' . $ui . '" data-name="' . $facet['name'] . '" data-type="' . $facet['type'] . '"' . $ui_attr . '></div>';
|
||||
|
||||
// Build list of active facet types
|
||||
$this->active_types[ $facet['type'] ] = $facet['type'];
|
||||
$this->active_facets[ $facet['name'] ] = $facet['name'];
|
||||
$this->load_assets = true;
|
||||
}
|
||||
}
|
||||
elseif ( isset( $atts['template'] ) ) {
|
||||
$template = FWP()->helper->get_template_by_name( $atts['template'] );
|
||||
|
||||
if ( $template ) {
|
||||
$class_name = 'facetwp-template';
|
||||
|
||||
// Static template
|
||||
if ( isset( $atts['static'] ) ) {
|
||||
$renderer = new FacetWP_Renderer();
|
||||
$renderer->template = $template;
|
||||
$renderer->query_args = $renderer->get_query_args();
|
||||
$renderer->query = new WP_Query( $renderer->query_args );
|
||||
$html = $renderer->get_template_html();
|
||||
$class_name .= '-static';
|
||||
}
|
||||
// Preload template (search engine visible)
|
||||
else {
|
||||
global $wp_query;
|
||||
|
||||
$temp_query = $wp_query;
|
||||
$args = FWP()->request->process_preload_data( $template['name'] );
|
||||
$preload_data = FWP()->facet->render( $args );
|
||||
$html = $preload_data['template'];
|
||||
$wp_query = $temp_query;
|
||||
}
|
||||
|
||||
$output = '<div class="{class}" data-name="{name}">{html}</div>';
|
||||
$output = str_replace( '{class}', $class_name, $output );
|
||||
$output = str_replace( '{name}', $atts['template'], $output );
|
||||
$output = str_replace( '{html}', $html, $output );
|
||||
|
||||
$this->load_assets = true;
|
||||
}
|
||||
}
|
||||
elseif ( isset( $atts['sort'] ) ) {
|
||||
$this->active_extras['sort'] = true;
|
||||
$output = '<div class="facetwp-sort"></div>';
|
||||
}
|
||||
elseif ( isset( $atts['selections'] ) ) {
|
||||
$output = '<div class="facetwp-selections"></div>';
|
||||
}
|
||||
elseif ( isset( $atts['counts'] ) ) {
|
||||
$this->active_extras['counts'] = true;
|
||||
$output = '<div class="facetwp-counts"></div>';
|
||||
}
|
||||
elseif ( isset( $atts['pager'] ) ) {
|
||||
$this->active_extras['pager'] = true;
|
||||
$output = '<div class="facetwp-pager"></div>';
|
||||
}
|
||||
elseif ( isset( $atts['per_page'] ) ) {
|
||||
$this->active_extras['per_page'] = true;
|
||||
$output = '<div class="facetwp-per-page"></div>';
|
||||
}
|
||||
|
||||
$output = apply_filters( 'facetwp_shortcode_html', $output, $atts );
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output facet scripts
|
||||
*/
|
||||
function front_scripts() {
|
||||
|
||||
// Not enqueued - front.js needs to load before front_scripts()
|
||||
if ( apply_filters( 'facetwp_load_assets', $this->load_assets ) ) {
|
||||
|
||||
// Load CSS?
|
||||
if ( apply_filters( 'facetwp_load_css', true ) ) {
|
||||
$this->assets['front.css'] = FACETWP_URL . '/assets/css/front.css';
|
||||
}
|
||||
|
||||
// Load required JS
|
||||
$this->assets['front.js'] = FACETWP_URL . '/assets/js/dist/front.min.js';
|
||||
|
||||
// Backwards compat?
|
||||
if ( apply_filters( 'facetwp_load_deprecated', false ) ) {
|
||||
$this->assets['front-deprecated.js'] = FACETWP_URL . '/assets/js/src/deprecated.js';
|
||||
}
|
||||
|
||||
// Load a11y?
|
||||
$a11y = FWP()->helper->get_setting( 'load_a11y', 'no' );
|
||||
$a11y_hook = apply_filters( 'facetwp_load_a11y', false );
|
||||
|
||||
if ( 'yes' == $a11y || $a11y_hook ) {
|
||||
$this->assets['accessibility.js'] = FACETWP_URL . '/assets/js/src/accessibility.js';
|
||||
$this->json['a11y'] = [
|
||||
'label_page' => __( 'Go to page', 'fwp-front' ),
|
||||
'label_page_next' => __( 'Go to next page', 'fwp-front' ),
|
||||
'label_page_prev' => __( 'Go to previous page', 'fwp-front' )
|
||||
];
|
||||
}
|
||||
|
||||
// Pass GET and URI params
|
||||
$http_params = [
|
||||
'get' => $_GET,
|
||||
'uri' => FWP()->helper->get_uri(),
|
||||
'url_vars' => FWP()->request->url_vars,
|
||||
];
|
||||
|
||||
// See FWP()->facet->get_query_args()
|
||||
if ( ! empty( FWP()->facet->archive_args ) ) {
|
||||
$http_params['archive_args'] = FWP()->facet->archive_args;
|
||||
}
|
||||
|
||||
// Populate the FWP_JSON object
|
||||
$this->json['prefix'] = FWP()->helper->get_setting( 'prefix' );
|
||||
$this->json['no_results_text'] = __( 'No results found', 'fwp-front' );
|
||||
$this->json['ajaxurl'] = get_rest_url() . 'facetwp/v1/refresh';
|
||||
$this->json['nonce'] = wp_create_nonce( 'wp_rest' );
|
||||
|
||||
if ( apply_filters( 'facetwp_use_preloader', true ) ) {
|
||||
$overrides = FWP()->request->process_preload_overrides([
|
||||
'facets' => $this->active_facets,
|
||||
'extras' => $this->active_extras,
|
||||
]);
|
||||
$args = FWP()->request->process_preload_data( false, $overrides );
|
||||
$this->json['preload_data'] = FWP()->facet->render( $args );
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
foreach ( $this->active_types as $type ) {
|
||||
$facet_class = FWP()->helper->facet_types[ $type ];
|
||||
if ( method_exists( $facet_class, 'front_scripts' ) ) {
|
||||
$facet_class->front_scripts();
|
||||
}
|
||||
}
|
||||
|
||||
$inline_scripts = ob_get_clean();
|
||||
$assets = apply_filters( 'facetwp_assets', $this->assets );
|
||||
|
||||
foreach ( $assets as $slug => $data ) {
|
||||
$data = (array) $data;
|
||||
$is_css = ( 'css' == substr( $slug, -3 ) );
|
||||
$version = empty( $data[1] ) ? FACETWP_VERSION : $data[1];
|
||||
$url = $data[0];
|
||||
|
||||
if ( false !== strpos( $url, 'facetwp' ) ) {
|
||||
$prefix = ( false !== strpos( $url, '?' ) ) ? '&' : '?';
|
||||
$url .= $prefix . 'ver=' . $version;
|
||||
}
|
||||
|
||||
$html = $is_css ? '<link href="{url}" rel="stylesheet">' : '<script src="{url}"></script>';
|
||||
$html = apply_filters( 'facetwp_asset_html', $html, $url );
|
||||
echo str_replace( '{url}', $url, $html ) . "\n";
|
||||
}
|
||||
|
||||
echo $inline_scripts;
|
||||
?>
|
||||
<script>
|
||||
window.FWP_JSON = <?php echo json_encode( $this->json ); ?>;
|
||||
window.FWP_HTTP = <?php echo json_encode( $http_params ); ?>;
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
600
wp/wp-content/plugins/facetwp/includes/class-helper.php
Normal file
600
wp/wp-content/plugins/facetwp/includes/class-helper.php
Normal file
@@ -0,0 +1,600 @@
|
||||
<?php
|
||||
|
||||
final class FacetWP_Helper
|
||||
{
|
||||
|
||||
/* (array) The facetwp_settings option (after hooks) */
|
||||
public $settings;
|
||||
|
||||
/* (array) Associative array of facet objects */
|
||||
public $facet_types;
|
||||
|
||||
/* (array) Cached data sources */
|
||||
public $data_sources;
|
||||
|
||||
/* (array) Cached terms */
|
||||
public $term_cache;
|
||||
|
||||
/* (array) Index table row counts */
|
||||
public $row_counts;
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->facet_types = $this->get_facet_types();
|
||||
$this->settings = $this->load_settings();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse the URL hostname
|
||||
*/
|
||||
function get_http_host() {
|
||||
return parse_url( get_option( 'home' ), PHP_URL_HOST );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the current page URI
|
||||
*/
|
||||
function get_uri() {
|
||||
if ( isset( FWP()->facet->http_params ) ) {
|
||||
return FWP()->facet->http_params['uri'];
|
||||
}
|
||||
|
||||
$uri = parse_url( $_SERVER['REQUEST_URI'] );
|
||||
return isset( $uri['path'] ) ? trim( $uri['path'], '/' ) : '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get available facet types
|
||||
*/
|
||||
function get_facet_types() {
|
||||
if ( ! empty( $this->facet_types ) ) {
|
||||
return $this->facet_types;
|
||||
}
|
||||
|
||||
include( FACETWP_DIR . '/includes/facets/base.php' );
|
||||
|
||||
$types = [
|
||||
'checkboxes' => 'Facetwp_Facet_Checkboxes',
|
||||
'dropdown' => 'Facetwp_Facet_Dropdown',
|
||||
'radio' => 'Facetwp_Facet_Radio_Core',
|
||||
'fselect' => 'Facetwp_Facet_fSelect',
|
||||
'hierarchy' => 'Facetwp_Facet_Hierarchy',
|
||||
'slider' => 'Facetwp_Facet_Slider',
|
||||
'search' => 'Facetwp_Facet_Search',
|
||||
'autocomplete' => 'Facetwp_Facet_Autocomplete',
|
||||
'date_range' => 'Facetwp_Facet_Date_Range',
|
||||
'number_range' => 'Facetwp_Facet_Number_Range',
|
||||
'rating' => 'FacetWP_Facet_Rating',
|
||||
'proximity' => 'Facetwp_Facet_Proximity_Core',
|
||||
'pager' => 'FacetWP_Facet_Pager',
|
||||
'reset' => 'FacetWP_Facet_Reset',
|
||||
'sort' => 'FacetWP_Facet_Sort'
|
||||
];
|
||||
|
||||
$facet_types = [];
|
||||
|
||||
foreach ( $types as $slug => $class_name ) {
|
||||
include( FACETWP_DIR . "/includes/facets/$slug.php" );
|
||||
$facet_types[ $slug ] = new $class_name();
|
||||
}
|
||||
|
||||
return apply_filters( 'facetwp_facet_types', $facet_types );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get settings and allow for developer hooks
|
||||
*/
|
||||
function load_settings( $last_index = false ) {
|
||||
$name = $last_index ? 'facetwp_settings_last_index' : 'facetwp_settings';
|
||||
$option = get_option( $name );
|
||||
|
||||
$defaults = [
|
||||
'facets' => [],
|
||||
'templates' => [],
|
||||
'settings' => [
|
||||
'thousands_separator' => ',',
|
||||
'decimal_separator' => '.',
|
||||
'prefix' => '_',
|
||||
'load_jquery' => 'no'
|
||||
]
|
||||
];
|
||||
|
||||
$settings = ( false !== $option ) ? json_decode( $option, true ) : [];
|
||||
$settings = array_merge( $defaults, $settings );
|
||||
$settings['settings'] = array_merge( $defaults['settings'], $settings['settings'] );
|
||||
|
||||
// Store DB-based facet & template names
|
||||
$db_names = [];
|
||||
|
||||
foreach ( $settings['facets'] as $facet ) {
|
||||
$db_names[ 'facet-' . $facet['name'] ] = true;
|
||||
}
|
||||
|
||||
foreach ( $settings['templates'] as $template ) {
|
||||
$db_names[ 'template-' . $template['name'] ] = true;
|
||||
}
|
||||
|
||||
// Programmatically registered
|
||||
$facets = apply_filters( 'facetwp_facets', $settings['facets'] );
|
||||
$templates = apply_filters( 'facetwp_templates', $settings['templates'] );
|
||||
|
||||
$tmp_facets = [];
|
||||
$tmp_templates = [];
|
||||
|
||||
// Merge DB + code-based facets
|
||||
foreach ( $facets as $facet ) {
|
||||
$name = $facet['name'];
|
||||
$is_db_based = isset( $db_names[ "facet-$name" ] );
|
||||
|
||||
if ( ! $is_db_based ) {
|
||||
$facet['_code'] = true;
|
||||
}
|
||||
|
||||
if ( ! $is_db_based || empty( $tmp_facets[ $name ] ) ) {
|
||||
|
||||
// Valid facet type?
|
||||
if ( in_array( $facet['type'], array_keys( $this->facet_types ) ) ) {
|
||||
$tmp_facets[ $name ] = $facet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge DB + code-based templates
|
||||
foreach ( $templates as $template ) {
|
||||
$name = $template['name'];
|
||||
$is_db_based = isset( $db_names[ "template-$name" ] );
|
||||
|
||||
if ( ! $is_db_based ) {
|
||||
$template['_code'] = true;
|
||||
}
|
||||
|
||||
if ( ! $is_db_based || empty( $tmp_templates[ $name ] ) ) {
|
||||
$tmp_templates[ $name ] = $template;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert back to numerical arrays
|
||||
$settings['facets'] = array_values( $tmp_facets );
|
||||
$settings['templates'] = array_values( $tmp_templates );
|
||||
|
||||
// Filtered settings
|
||||
return $settings;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a general setting value
|
||||
*
|
||||
* @param string $name The setting name
|
||||
* @param mixed $default The default value
|
||||
* @since 1.9
|
||||
*/
|
||||
function get_setting( $name, $default = '' ) {
|
||||
return $this->settings['settings'][ $name ] ?? $default;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of all facets
|
||||
* @return array
|
||||
*/
|
||||
function get_facets() {
|
||||
return $this->settings['facets'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of all templates
|
||||
* @return array
|
||||
*/
|
||||
function get_templates() {
|
||||
return $this->settings['templates'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all properties for a single facet
|
||||
* @param string $facet_name
|
||||
* @return mixed An array of facet info, or false
|
||||
*/
|
||||
function get_facet_by_name( $facet_name ) {
|
||||
foreach ( $this->get_facets() as $facet ) {
|
||||
if ( $facet_name == $facet['name'] ) {
|
||||
return $facet;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all properties for a single template
|
||||
*
|
||||
* @param string $template_name
|
||||
* @return mixed An array of template info, or false
|
||||
*/
|
||||
function get_template_by_name( $template_name ) {
|
||||
foreach ( $this->get_templates() as $template ) {
|
||||
if ( $template_name == $template['name'] ) {
|
||||
return $template;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetch facets using one of its settings
|
||||
* @param string $setting_name
|
||||
* @param mixed $setting_value
|
||||
* @return array
|
||||
*/
|
||||
function get_facets_by( $setting, $value ) {
|
||||
$matches = [];
|
||||
|
||||
foreach ( $this->get_facets() as $facet ) {
|
||||
if ( isset( $facet[ $setting ] ) && $value === $facet[ $setting ] ) {
|
||||
$matches[] = $facet;
|
||||
}
|
||||
}
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get terms across all languages (thanks, WPML)
|
||||
* @since 3.8.5
|
||||
*/
|
||||
function get_terms( $taxonomy ) {
|
||||
global $wpdb;
|
||||
|
||||
$sql = "
|
||||
SELECT t.term_id, t.name, t.slug, tt.parent FROM {$wpdb->term_taxonomy} tt
|
||||
INNER JOIN {$wpdb->terms} t ON t.term_id = tt.term_id
|
||||
WHERE tt.taxonomy = %s";
|
||||
|
||||
return $wpdb->get_results( $wpdb->prepare( $sql, $taxonomy ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of term information, including depth
|
||||
* @param string $taxonomy The taxonomy name
|
||||
* @return array Term information
|
||||
* @since 0.9.0
|
||||
*/
|
||||
function get_term_depths( $taxonomy ) {
|
||||
|
||||
if ( isset( $this->term_cache[ $taxonomy ] ) ) {
|
||||
return $this->term_cache[ $taxonomy ];
|
||||
}
|
||||
|
||||
$output = [];
|
||||
$parents = [];
|
||||
|
||||
$terms = $this->get_terms( $taxonomy );
|
||||
|
||||
// Get term parents
|
||||
foreach ( $terms as $term ) {
|
||||
$parents[ $term->term_id ] = $term->parent;
|
||||
}
|
||||
|
||||
// Build the term array
|
||||
foreach ( $terms as $term ) {
|
||||
$output[ $term->term_id ] = [
|
||||
'term_id' => $term->term_id,
|
||||
'name' => $term->name,
|
||||
'slug' => $term->slug,
|
||||
'parent_id' => $term->parent,
|
||||
'depth' => 0
|
||||
];
|
||||
|
||||
$current_parent = $term->parent;
|
||||
while ( 0 < (int) $current_parent ) {
|
||||
$current_parent = $parents[ $current_parent ];
|
||||
$output[ $term->term_id ]['depth']++;
|
||||
|
||||
// Prevent an infinite loop
|
||||
if ( 50 < $output[ $term->term_id ]['depth'] ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->term_cache[ $taxonomy ] = $output;
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finish sorting the facet values
|
||||
* The results are already sorted by depth and (name OR count), we just need
|
||||
* to move the children directly below their parents
|
||||
*/
|
||||
function sort_taxonomy_values( $values = [], $orderby = 'count' ) {
|
||||
$final = [];
|
||||
$cache = [];
|
||||
|
||||
// Create an "order" sort value based on the top-level items
|
||||
foreach ( $values as $key => $val ) {
|
||||
if ( 0 == $val['depth'] ) {
|
||||
$val['order'] = $key;
|
||||
$cache[ $val['term_id'] ] = $key;
|
||||
$final[] = $val;
|
||||
}
|
||||
elseif ( isset( $cache[ $val['parent_id'] ] ) ) { // skip orphans
|
||||
$val['order'] = $cache[ $val['parent_id'] ] . ".$key"; // dot-separated hierarchy string
|
||||
$cache[ $val['term_id'] ] = $val['order'];
|
||||
$final[] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the array based on the new "order" element
|
||||
// Since this is a dot-separated hierarchy string, use version_compare
|
||||
usort( $final, function( $a, $b ) {
|
||||
return version_compare( $a['order'], $b['order'] );
|
||||
});
|
||||
|
||||
return $final;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sanitize SQL data
|
||||
* @return mixed The sanitized value(s)
|
||||
* @since 3.0.7
|
||||
*/
|
||||
function sanitize( $input ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( is_array( $input ) ) {
|
||||
$output = [];
|
||||
|
||||
foreach ( $input as $key => $val ) {
|
||||
$output[ $key ] = $this->sanitize( $val );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( $wpdb->dbh && $wpdb->use_mysqli ) {
|
||||
$output = mysqli_real_escape_string( $wpdb->dbh, $input );
|
||||
}
|
||||
else {
|
||||
$output = addslashes( $input );
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does an active facet with the specified setting exist?
|
||||
* @return boolean
|
||||
* @since 1.4.0
|
||||
*/
|
||||
function facet_setting_exists( $setting_name, $setting_value ) {
|
||||
foreach ( FWP()->facet->facets as $f ) {
|
||||
if ( isset( $f[ $setting_name ] ) && $f[ $setting_name ] == $setting_value ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does this facet have a setting with the specified value?
|
||||
* @return boolean
|
||||
* @since 2.3.4
|
||||
*/
|
||||
function facet_is( $facet, $setting_name, $setting_value ) {
|
||||
if ( is_string( $facet ) ) {
|
||||
$facet = $this->get_facet_by_name( $facet );
|
||||
}
|
||||
|
||||
if ( isset( $facet[ $setting_name ] ) && $facet[ $setting_name ] == $setting_value ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hash a facet value if needed
|
||||
* @return string
|
||||
* @since 2.1
|
||||
*/
|
||||
function safe_value( $value ) {
|
||||
$value = remove_accents( $value );
|
||||
|
||||
if ( preg_match( '/[^a-z0-9_.\- ]/i', $value ) ) {
|
||||
if ( ! preg_match( '/^\d{4}-(0[1-9]|1[012])-([012]\d|3[01])/', $value ) ) {
|
||||
$value = md5( $value );
|
||||
}
|
||||
}
|
||||
|
||||
$value = str_replace( ' ', '-', strtolower( $value ) );
|
||||
$value = preg_replace( '/[-]{2,}/', '-', $value );
|
||||
$value = ( 50 < strlen( $value ) ) ? substr( $value, 0, 50 ) : $value;
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Properly format numbers, taking separators into account
|
||||
* @return number
|
||||
* @since 2.7.5
|
||||
*/
|
||||
function format_number( $num ) {
|
||||
$sep_decimal = $this->get_setting( 'decimal_separator' );
|
||||
$sep_thousands = $this->get_setting( 'thousands_separator' );
|
||||
|
||||
$num = str_replace( $sep_thousands, '', $num );
|
||||
$num = ( ',' == $sep_decimal ) ? str_replace( ',', '.', $num ) : $num;
|
||||
$num = preg_replace( '/[^0-9-.]/', '', $num );
|
||||
|
||||
return $num;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get facet data sources
|
||||
* @return array
|
||||
* @since 2.2.1
|
||||
*/
|
||||
function get_data_sources( $context = 'default' ) {
|
||||
global $wpdb;
|
||||
|
||||
// Cached?
|
||||
if ( ! empty( $this->data_sources ) ) {
|
||||
$sources = $this->data_sources;
|
||||
}
|
||||
else {
|
||||
|
||||
// Get excluded meta keys
|
||||
$excluded_fields = apply_filters( 'facetwp_excluded_custom_fields', [
|
||||
'_edit_last',
|
||||
'_edit_lock',
|
||||
] );
|
||||
|
||||
// Get taxonomies
|
||||
$taxonomies = get_taxonomies( [], 'object' );
|
||||
|
||||
// Get custom fields
|
||||
$meta_keys = $wpdb->get_col( "SELECT DISTINCT meta_key FROM {$wpdb->postmeta} ORDER BY meta_key" );
|
||||
$custom_fields = array_diff( $meta_keys, $excluded_fields );
|
||||
|
||||
$sources = [
|
||||
'posts' => [
|
||||
'label' => __( 'Posts', 'fwp' ),
|
||||
'choices' => [
|
||||
'post_type' => __( 'Post Type', 'fwp' ),
|
||||
'post_date' => __( 'Post Date', 'fwp' ),
|
||||
'post_modified' => __( 'Post Modified', 'fwp' ),
|
||||
'post_title' => __( 'Post Title', 'fwp' ),
|
||||
'post_author' => __( 'Post Author', 'fwp' )
|
||||
],
|
||||
'weight' => 10
|
||||
],
|
||||
'taxonomies' => [
|
||||
'label' => __( 'Taxonomies', 'fwp' ),
|
||||
'choices' => [],
|
||||
'weight' => 20
|
||||
],
|
||||
'custom_fields' => [
|
||||
'label' => __( 'Custom Fields', 'fwp' ),
|
||||
'choices' => [],
|
||||
'weight' => 30
|
||||
]
|
||||
];
|
||||
|
||||
foreach ( $taxonomies as $tax ) {
|
||||
$sources['taxonomies']['choices'][ 'tax/' . $tax->name ] = $tax->labels->name . ' (' . $tax->name . ')';
|
||||
}
|
||||
|
||||
foreach ( $custom_fields as $cf ) {
|
||||
if ( 0 !== strpos( $cf, '_oembed_' ) ) {
|
||||
$sources['custom_fields']['choices'][ 'cf/' . $cf ] = $cf;
|
||||
}
|
||||
}
|
||||
|
||||
$this->data_sources = $sources;
|
||||
}
|
||||
|
||||
$sources = apply_filters( 'facetwp_facet_sources', $sources, $context );
|
||||
|
||||
uasort( $sources, [ $this, 'sort_by_weight' ] );
|
||||
|
||||
return $sources;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort facetwp_facet_sources by weight
|
||||
* @since 2.7.5
|
||||
*/
|
||||
function sort_by_weight( $a, $b ) {
|
||||
$a['weight'] = $a['weight'] ?? 10;
|
||||
$b['weight'] = $b['weight'] ?? 10;
|
||||
|
||||
if ( $a['weight'] == $b['weight'] ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ( $a['weight'] < $b['weight'] ) ? -1 : 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get row counts for all facets
|
||||
* @since 3.3.4
|
||||
*/
|
||||
function get_row_counts() {
|
||||
if ( isset( $this->row_counts ) ) {
|
||||
return $this->row_counts;
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$output = [];
|
||||
$results = $wpdb->get_results( "SELECT facet_name, COUNT(*) AS row_count FROM {$wpdb->prefix}facetwp_index GROUP BY facet_name" );
|
||||
|
||||
foreach ( $results as $result ) {
|
||||
$output[ $result->facet_name ] = (int) $result->row_count;
|
||||
}
|
||||
|
||||
$this->row_counts = $output;
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grab the license key
|
||||
* @since 3.0.3
|
||||
*/
|
||||
function get_license_key() {
|
||||
$license_key = defined( 'FACETWP_LICENSE_KEY' ) ? FACETWP_LICENSE_KEY : get_option( 'facetwp_license' );
|
||||
$license_key = apply_filters( 'facetwp_license_key', $license_key );
|
||||
return sanitize_key( trim( $license_key ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether the license is active
|
||||
* @since 3.3.0
|
||||
*/
|
||||
function is_license_active() {
|
||||
return ( 'success' == $this->get_license_meta( 'status' ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a license meta value
|
||||
* Possible keys: status, message, expiration, payment_id, price_id
|
||||
* @since 3.5.3
|
||||
*/
|
||||
function get_license_meta( $key = 'status' ) {
|
||||
$activation = get_option( 'facetwp_activation' );
|
||||
|
||||
if ( ! empty( $activation ) ) {
|
||||
$data = json_decode( $activation, true );
|
||||
|
||||
if ( isset( $data[ $key ] ) ) {
|
||||
return $data[ $key ];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
691
wp/wp-content/plugins/facetwp/includes/class-indexer.php
Normal file
691
wp/wp-content/plugins/facetwp/includes/class-indexer.php
Normal file
@@ -0,0 +1,691 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Indexer
|
||||
{
|
||||
|
||||
/* (boolean) wp_insert_post running? */
|
||||
public $is_saving = false;
|
||||
|
||||
/* (boolean) Whether to index a single post */
|
||||
public $index_all = false;
|
||||
|
||||
/* (int) Number of posts to index before updating progress */
|
||||
public $chunk_size = 10;
|
||||
|
||||
/* (string) Whether a temporary table is active */
|
||||
public $table;
|
||||
|
||||
/* (array) Facet properties for the value being indexed */
|
||||
public $facet;
|
||||
|
||||
/* (array) Value modifiers set via the admin UI */
|
||||
public $modifiers;
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->set_table( 'auto' );
|
||||
$this->run_cron();
|
||||
|
||||
if ( apply_filters( 'facetwp_indexer_is_enabled', true ) ) {
|
||||
$this->run_hooks();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Event listeners
|
||||
* @since 2.8.4
|
||||
*/
|
||||
function run_hooks() {
|
||||
add_action( 'save_post', [ $this, 'save_post' ] );
|
||||
add_action( 'delete_post', [ $this, 'delete_post' ] );
|
||||
add_action( 'edited_term', [ $this, 'edit_term' ], 10, 3 );
|
||||
add_action( 'delete_term', [ $this, 'delete_term' ], 10, 3 );
|
||||
add_action( 'set_object_terms', [ $this, 'set_object_terms' ] );
|
||||
add_action( 'facetwp_indexer_cron', [ $this, 'get_progress' ] );
|
||||
add_filter( 'wp_insert_post_parent', [ $this, 'is_wp_insert_post' ] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cron task
|
||||
* @since 2.8.5
|
||||
*/
|
||||
function run_cron() {
|
||||
if ( ! wp_next_scheduled( 'facetwp_indexer_cron' ) ) {
|
||||
wp_schedule_single_event( time() + 300, 'facetwp_indexer_cron' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the index when posts get saved
|
||||
* @since 0.1.0
|
||||
*/
|
||||
function save_post( $post_id ) {
|
||||
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( false !== wp_is_post_revision( $post_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'auto-draft' == get_post_status( $post_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->index( $post_id );
|
||||
$this->is_saving = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the index when posts get deleted
|
||||
* @since 0.6.0
|
||||
*/
|
||||
function delete_post( $post_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$wpdb->query( "DELETE FROM {$this->table} WHERE post_id = $post_id" );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the index when terms get saved
|
||||
* @since 0.6.0
|
||||
*/
|
||||
function edit_term( $term_id, $tt_id, $taxonomy ) {
|
||||
global $wpdb;
|
||||
|
||||
$term = get_term( $term_id, $taxonomy );
|
||||
$slug = FWP()->helper->safe_value( $term->slug );
|
||||
$matches = FWP()->helper->get_facets_by( 'source', "tax/$taxonomy" );
|
||||
|
||||
if ( ! empty( $matches ) ) {
|
||||
$facet_names = wp_list_pluck( $matches, 'name' );
|
||||
$facet_names = implode( "','", esc_sql( $facet_names ) );
|
||||
|
||||
$wpdb->query( $wpdb->prepare( "
|
||||
UPDATE {$this->table}
|
||||
SET facet_value = %s, facet_display_value = %s
|
||||
WHERE facet_name IN ('$facet_names') AND term_id = %d",
|
||||
$slug, $term->name, $term_id
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the index when terms get deleted
|
||||
* @since 0.6.0
|
||||
*/
|
||||
function delete_term( $term_id, $tt_id, $taxonomy ) {
|
||||
global $wpdb;
|
||||
|
||||
$matches = FWP()->helper->get_facets_by( 'source', "tax/$taxonomy" );
|
||||
|
||||
if ( ! empty( $matches ) ) {
|
||||
$facet_names = wp_list_pluck( $matches, 'name' );
|
||||
$facet_names = implode( "','", esc_sql( $facet_names ) );
|
||||
|
||||
$wpdb->query( "
|
||||
DELETE FROM {$this->table}
|
||||
WHERE facet_name IN ('$facet_names') AND term_id = $term_id"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* We're hijacking wp_insert_post_parent
|
||||
* Prevent our set_object_terms() hook from firing within wp_insert_post
|
||||
* @since 2.2.2
|
||||
*/
|
||||
function is_wp_insert_post( $post_parent ) {
|
||||
$this->is_saving = true;
|
||||
return $post_parent;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Support for manual taxonomy associations
|
||||
* @since 0.8.0
|
||||
*/
|
||||
function set_object_terms( $object_id ) {
|
||||
if ( ! $this->is_saving ) {
|
||||
$this->index( $object_id );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rebuild the facet index
|
||||
* @param mixed $post_id The post ID (set to FALSE to re-index everything)
|
||||
*/
|
||||
function index( $post_id = false ) {
|
||||
global $wpdb;
|
||||
|
||||
$this->index_all = ( false === $post_id );
|
||||
|
||||
// Index everything
|
||||
if ( $this->index_all ) {
|
||||
|
||||
// Store the pre-index settings (see FacetWP_Diff)
|
||||
update_option( 'facetwp_settings_last_index', get_option( 'facetwp_settings' ), 'no' );
|
||||
|
||||
// PHP sessions are blocking, so close if active
|
||||
if ( PHP_SESSION_ACTIVE === session_status() ) {
|
||||
session_write_close();
|
||||
}
|
||||
|
||||
// Bypass the PHP timeout
|
||||
ini_set( 'max_execution_time', 0 );
|
||||
|
||||
// Prevent multiple indexing processes
|
||||
$touch = (int) $this->get_transient( 'touch' );
|
||||
|
||||
if ( 0 < $touch ) {
|
||||
// Run only if the indexer is inactive or stalled
|
||||
if ( ( time() - $touch ) < 60 ) {
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Create temp table
|
||||
$this->manage_temp_table( 'create' );
|
||||
}
|
||||
}
|
||||
// Index a single post
|
||||
elseif ( is_int( $post_id ) ) {
|
||||
|
||||
// Clear table values
|
||||
$wpdb->query( "DELETE FROM {$this->table} WHERE post_id = $post_id" );
|
||||
}
|
||||
// Exit
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Resume indexing?
|
||||
$offset = (int) ( $_POST['offset'] ?? 0 );
|
||||
$attempt = (int) ( $_POST['retries'] ?? 0 );
|
||||
|
||||
if ( 0 < $offset ) {
|
||||
$post_ids = json_decode( get_option( 'facetwp_indexing' ), true );
|
||||
}
|
||||
else {
|
||||
$post_ids = $this->get_post_ids_to_index( $post_id );
|
||||
|
||||
// Store post IDs
|
||||
if ( $this->index_all ) {
|
||||
update_option( 'facetwp_indexing', json_encode( $post_ids ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Count total posts
|
||||
$num_total = count( $post_ids );
|
||||
|
||||
// Get all facet sources
|
||||
$facets = FWP()->helper->get_facets();
|
||||
|
||||
// Populate an array of facet value modifiers
|
||||
$this->load_value_modifiers( $facets );
|
||||
|
||||
foreach ( $post_ids as $counter => $post_id ) {
|
||||
|
||||
// Advance until we reach the offset
|
||||
if ( $counter < $offset ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update the progress bar
|
||||
if ( $this->index_all ) {
|
||||
if ( 0 == ( $counter % $this->chunk_size ) ) {
|
||||
$num_retries = (int) $this->get_transient( 'retries' );
|
||||
|
||||
// Exit if newer retries exist
|
||||
if ( $attempt < $num_retries ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Exit if the indexer was cancelled
|
||||
wp_cache_delete( 'facetwp_indexing_cancelled', 'options' );
|
||||
|
||||
if ( 'yes' === get_option( 'facetwp_indexing_cancelled', 'no' ) ) {
|
||||
update_option( 'facetwp_transients', '' );
|
||||
update_option( 'facetwp_indexing', '' );
|
||||
$this->manage_temp_table( 'delete' );
|
||||
exit;
|
||||
}
|
||||
|
||||
$transients = [
|
||||
'num_indexed' => $counter,
|
||||
'num_total' => $num_total,
|
||||
'retries' => $attempt,
|
||||
'touch' => time(),
|
||||
];
|
||||
update_option( 'facetwp_transients', json_encode( $transients ) );
|
||||
}
|
||||
}
|
||||
|
||||
// If the indexer stalled, start from the last valid chunk
|
||||
if ( 0 < $offset && ( $counter - $offset < $this->chunk_size ) ) {
|
||||
$wpdb->query( "DELETE FROM {$this->table} WHERE post_id = $post_id" );
|
||||
}
|
||||
|
||||
$this->index_post( $post_id, $facets );
|
||||
}
|
||||
|
||||
// Indexing complete
|
||||
if ( $this->index_all ) {
|
||||
update_option( 'facetwp_last_indexed', time(), 'no' );
|
||||
update_option( 'facetwp_transients', '', 'no' );
|
||||
update_option( 'facetwp_indexing', '', 'no' );
|
||||
|
||||
$this->manage_temp_table( 'replace' );
|
||||
$this->manage_temp_table( 'delete' );
|
||||
}
|
||||
|
||||
do_action( 'facetwp_indexer_complete' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of post IDs to index
|
||||
* @since 3.6.8
|
||||
*/
|
||||
function get_post_ids_to_index( $post_id = false ) {
|
||||
$args = [
|
||||
'post_type' => 'any',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
'orderby' => 'ID',
|
||||
'cache_results' => false,
|
||||
'no_found_rows' => true,
|
||||
];
|
||||
|
||||
if ( is_int( $post_id ) ) {
|
||||
$args['p'] = $post_id;
|
||||
$args['posts_per_page'] = 1;
|
||||
}
|
||||
|
||||
$args = apply_filters( 'facetwp_indexer_query_args', $args );
|
||||
|
||||
$query = new WP_Query( $args );
|
||||
return (array) $query->posts;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index an individual post
|
||||
* @since 3.6.8
|
||||
*/
|
||||
function index_post( $post_id, $facets ) {
|
||||
|
||||
// Force WPML to change the language
|
||||
do_action( 'facetwp_indexer_post', [ 'post_id' => $post_id ] );
|
||||
|
||||
// Loop through all facets
|
||||
foreach ( $facets as $facet ) {
|
||||
|
||||
// Do not index search facets
|
||||
if ( 'search' == $facet['type'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->facet = $facet;
|
||||
$source = $facet['source'] ?? '';
|
||||
|
||||
// Set default index_row() params
|
||||
$defaults = [
|
||||
'post_id' => $post_id,
|
||||
'facet_name' => $facet['name'],
|
||||
'facet_source' => $source,
|
||||
'facet_value' => '',
|
||||
'facet_display_value' => '',
|
||||
'term_id' => 0,
|
||||
'parent_id' => 0,
|
||||
'depth' => 0,
|
||||
'variation_id' => 0,
|
||||
];
|
||||
|
||||
$defaults = apply_filters( 'facetwp_indexer_post_facet_defaults', $defaults, [
|
||||
'facet' => $facet
|
||||
] );
|
||||
|
||||
// Set flag for custom handling
|
||||
$this->is_overridden = true;
|
||||
|
||||
// Bypass default indexing
|
||||
$bypass = apply_filters( 'facetwp_indexer_post_facet', false, [
|
||||
'defaults' => $defaults,
|
||||
'facet' => $facet
|
||||
] );
|
||||
|
||||
if ( $bypass ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->is_overridden = false;
|
||||
|
||||
// Get rows to insert
|
||||
$rows = $this->get_row_data( $defaults );
|
||||
|
||||
foreach ( $rows as $row ) {
|
||||
$this->index_row( $row );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get data for a table row
|
||||
* @since 2.1.1
|
||||
*/
|
||||
function get_row_data( $defaults ) {
|
||||
$output = [];
|
||||
|
||||
$facet = $this->facet;
|
||||
$post_id = $defaults['post_id'];
|
||||
$source = $defaults['facet_source'];
|
||||
|
||||
if ( 'tax/' == substr( $source, 0, 4 ) ) {
|
||||
$used_terms = [];
|
||||
$taxonomy = substr( $source, 4 );
|
||||
$term_objects = wp_get_object_terms( $post_id, $taxonomy );
|
||||
if ( is_wp_error( $term_objects ) ) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
// Store the term depths
|
||||
$hierarchy = FWP()->helper->get_term_depths( $taxonomy );
|
||||
|
||||
// Only index child terms
|
||||
$children = false;
|
||||
if ( ! empty( $facet['parent_term'] ) ) {
|
||||
$children = get_term_children( $facet['parent_term'], $taxonomy );
|
||||
}
|
||||
|
||||
foreach ( $term_objects as $term ) {
|
||||
|
||||
// If "parent_term" is set, only index children
|
||||
if ( false !== $children && ! in_array( $term->term_id, $children ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prevent duplicate terms
|
||||
if ( isset( $used_terms[ $term->term_id ] ) ) {
|
||||
continue;
|
||||
}
|
||||
$used_terms[ $term->term_id ] = true;
|
||||
|
||||
// Handle hierarchical taxonomies
|
||||
$term_info = $hierarchy[ $term->term_id ];
|
||||
$depth = $term_info['depth'];
|
||||
|
||||
// Adjust depth if parent_term is set
|
||||
if ( ! empty( $facet['parent_term'] ) ) {
|
||||
if ( isset( $hierarchy[ $facet['parent_term'] ] ) ) {
|
||||
$anchor = (int) $hierarchy[ $facet['parent_term'] ]['depth'] + 1;
|
||||
$depth = ( $depth - $anchor );
|
||||
}
|
||||
}
|
||||
|
||||
$params = $defaults;
|
||||
$params['facet_value'] = $term->slug;
|
||||
$params['facet_display_value'] = $term->name;
|
||||
$params['term_id'] = $term->term_id;
|
||||
$params['parent_id'] = $term_info['parent_id'];
|
||||
$params['depth'] = $depth;
|
||||
$output[] = $params;
|
||||
|
||||
// Automatically index implicit parents
|
||||
if ( 'hierarchy' == $facet['type'] || FWP()->helper->facet_is( $facet, 'hierarchical', 'yes' ) ) {
|
||||
while ( $depth > 0 ) {
|
||||
$term_id = $term_info['parent_id'];
|
||||
$term_info = $hierarchy[ $term_id ];
|
||||
$depth = $depth - 1;
|
||||
|
||||
if ( ! isset( $used_terms[ $term_id ] ) ) {
|
||||
$used_terms[ $term_id ] = true;
|
||||
|
||||
$params = $defaults;
|
||||
$params['facet_value'] = $term_info['slug'];
|
||||
$params['facet_display_value'] = $term_info['name'];
|
||||
$params['term_id'] = $term_id;
|
||||
$params['parent_id'] = $term_info['parent_id'];
|
||||
$params['depth'] = $depth;
|
||||
$output[] = $params;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ( 'cf/' == substr( $source, 0, 3 ) ) {
|
||||
$source_noprefix = substr( $source, 3 );
|
||||
$values = get_post_meta( $post_id, $source_noprefix, false );
|
||||
foreach ( $values as $value ) {
|
||||
$params = $defaults;
|
||||
$params['facet_value'] = $value;
|
||||
$params['facet_display_value'] = $value;
|
||||
$output[] = $params;
|
||||
}
|
||||
}
|
||||
elseif ( 'post' == substr( $source, 0, 4 ) ) {
|
||||
$post = get_post( $post_id );
|
||||
$value = $post->{$source};
|
||||
$display_value = $value;
|
||||
if ( 'post_author' == $source ) {
|
||||
$user = get_user_by( 'id', $value );
|
||||
$display_value = ( $user instanceof WP_User ) ? $user->display_name : $value;
|
||||
}
|
||||
elseif ( 'post_type' == $source ) {
|
||||
$post_type = get_post_type_object( $value );
|
||||
if ( isset( $post_type->labels->name ) ) {
|
||||
$display_value = $post_type->labels->name;
|
||||
}
|
||||
}
|
||||
|
||||
$params = $defaults;
|
||||
$params['facet_value'] = $value;
|
||||
$params['facet_display_value'] = $display_value;
|
||||
$output[] = $params;
|
||||
}
|
||||
|
||||
return apply_filters( 'facetwp_indexer_row_data', $output, [
|
||||
'defaults' => $defaults,
|
||||
'facet' => $this->facet
|
||||
] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index a facet value
|
||||
* @since 0.6.0
|
||||
*/
|
||||
function index_row( $params ) {
|
||||
|
||||
// Allow for custom indexing
|
||||
$params = apply_filters( 'facetwp_index_row', $params, $this );
|
||||
|
||||
// Allow hooks to bypass the row insertion
|
||||
if ( is_array( $params ) ) {
|
||||
$this->insert( $params );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save a facet value to DB
|
||||
* This can be trigged by "facetwp_index_row" to handle multiple values
|
||||
* @since 1.2.5
|
||||
*/
|
||||
function insert( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$value = $params['facet_value'];
|
||||
$display_value = $params['facet_display_value'];
|
||||
|
||||
// Only accept scalar values
|
||||
if ( '' === $value || ! is_scalar( $value ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply UI-based modifiers
|
||||
if ( isset( $this->modifiers[ $params['facet_name'] ] ) ) {
|
||||
$mod = $this->modifiers[ $params['facet_name' ] ];
|
||||
$is_match = in_array( $display_value, $mod['values'] );
|
||||
|
||||
if ( ( 'exclude' == $mod['type'] && $is_match ) || ( 'include' == $mod['type'] && ! $is_match ) ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$wpdb->query( $wpdb->prepare( "INSERT INTO {$this->table}
|
||||
(post_id, facet_name, facet_value, facet_display_value, term_id, parent_id, depth, variation_id) VALUES (%d, %s, %s, %s, %d, %d, %d, %d)",
|
||||
$params['post_id'],
|
||||
$params['facet_name'],
|
||||
FWP()->helper->safe_value( $value ),
|
||||
$display_value,
|
||||
$params['term_id'],
|
||||
$params['parent_id'],
|
||||
$params['depth'],
|
||||
$params['variation_id']
|
||||
) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the indexing completion percentage
|
||||
* @return mixed The decimal percentage, or -1
|
||||
* @since 0.1.0
|
||||
*/
|
||||
function get_progress() {
|
||||
$return = -1;
|
||||
$num_indexed = (int) $this->get_transient( 'num_indexed' );
|
||||
$num_total = (int) $this->get_transient( 'num_total' );
|
||||
$retries = (int) $this->get_transient( 'retries' );
|
||||
$touch = (int) $this->get_transient( 'touch' );
|
||||
|
||||
if ( 0 < $num_total ) {
|
||||
|
||||
// Resume a stalled indexer
|
||||
if ( 60 < ( time() - $touch ) ) {
|
||||
$post_data = [
|
||||
'blocking' => false,
|
||||
'timeout' => 0.02,
|
||||
'body' => [
|
||||
'action' => 'facetwp_resume_index',
|
||||
'offset' => $num_indexed,
|
||||
'retries' => $retries + 1,
|
||||
'touch' => $touch
|
||||
]
|
||||
];
|
||||
wp_remote_post( admin_url( 'admin-ajax.php' ), $post_data );
|
||||
}
|
||||
|
||||
// Calculate the percent completion
|
||||
if ( $num_indexed != $num_total ) {
|
||||
$return = round( 100 * ( $num_indexed / $num_total ), 2 );
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get indexer transient variables
|
||||
* @since 1.7.8
|
||||
*/
|
||||
function get_transient( $name = false ) {
|
||||
$transients = get_option( 'facetwp_transients' );
|
||||
|
||||
if ( ! empty( $transients ) ) {
|
||||
$transients = json_decode( $transients, true );
|
||||
if ( $name ) {
|
||||
return $transients[ $name ] ?? false;
|
||||
}
|
||||
|
||||
return $transients;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set either the index or temp table
|
||||
* @param string $table 'auto', 'index', or 'temp'
|
||||
* @since 4.1.4
|
||||
*/
|
||||
function set_table( $table = 'auto' ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( 'auto' == $table ) {
|
||||
$table = ( '' == get_option( 'facetwp_indexing', '' ) ) ? 'index' : 'temp';
|
||||
}
|
||||
|
||||
$this->table = $wpdb->prefix . 'facetwp_' . $table;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index table management
|
||||
* @since 3.5
|
||||
*/
|
||||
function manage_temp_table( $action = 'create' ) {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->prefix . 'facetwp_index';
|
||||
$temp_table = $wpdb->prefix . 'facetwp_temp';
|
||||
|
||||
if ( 'create' == $action ) {
|
||||
$wpdb->query( "CREATE TABLE $temp_table LIKE $table" );
|
||||
$this->set_table( 'temp' );
|
||||
}
|
||||
elseif ( 'replace' == $action ) {
|
||||
$wpdb->query( "TRUNCATE TABLE $table" );
|
||||
$wpdb->query( "INSERT INTO $table SELECT * FROM $temp_table" );
|
||||
}
|
||||
elseif ( 'delete' == $action ) {
|
||||
$wpdb->query( "DROP TABLE IF EXISTS $temp_table" );
|
||||
$this->set_table( 'index' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Populate an array of facet value modifiers (defined in the admin UI)
|
||||
* @since 3.5.6
|
||||
*/
|
||||
function load_value_modifiers( $facets ) {
|
||||
$output = [];
|
||||
|
||||
foreach ( $facets as $facet ) {
|
||||
$name = $facet['name'];
|
||||
$type = empty( $facet['modifier_type'] ) ? 'off' : $facet['modifier_type'];
|
||||
|
||||
if ( 'include' == $type || 'exclude' == $type ) {
|
||||
$temp = preg_split( '/\r\n|\r|\n/', trim( $facet['modifier_values'] ) );
|
||||
$values = [];
|
||||
|
||||
// Compare using both original and encoded values
|
||||
foreach ( $temp as $val ) {
|
||||
$val = trim( $val );
|
||||
$val_encoded = htmlentities( $val );
|
||||
$val_decoded = html_entity_decode( $val );
|
||||
$values[ $val ] = true;
|
||||
$values[ $val_encoded ] = true;
|
||||
$values[ $val_decoded ] = true;
|
||||
}
|
||||
|
||||
$output[ $name ] = [ 'type' => $type, 'values' => array_keys( $values ) ];
|
||||
}
|
||||
}
|
||||
|
||||
$this->modifiers = $output;
|
||||
}
|
||||
}
|
||||
157
wp/wp-content/plugins/facetwp/includes/class-init.php
Normal file
157
wp/wp-content/plugins/facetwp/includes/class-init.php
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Init
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
add_action( 'init', [ $this, 'init' ], 20 );
|
||||
add_filter( 'woocommerce_is_rest_api_request', [ $this, 'is_rest_api_request' ] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize classes and WP hooks
|
||||
*/
|
||||
function init() {
|
||||
|
||||
// i18n
|
||||
$this->load_textdomain();
|
||||
|
||||
// is_plugin_active
|
||||
include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
|
||||
|
||||
$includes = [
|
||||
'api/fetch',
|
||||
'api/refresh',
|
||||
'class-helper',
|
||||
'class-ajax',
|
||||
'class-request',
|
||||
'class-renderer',
|
||||
'class-diff',
|
||||
'class-indexer',
|
||||
'class-display',
|
||||
'class-builder',
|
||||
'class-overrides',
|
||||
'class-settings',
|
||||
'class-upgrade',
|
||||
'functions'
|
||||
];
|
||||
|
||||
foreach ( $includes as $inc ) {
|
||||
include ( FACETWP_DIR . "/includes/$inc.php" );
|
||||
}
|
||||
|
||||
new FacetWP_Upgrade();
|
||||
new FacetWP_Overrides();
|
||||
|
||||
FWP()->api = new FacetWP_API_Fetch();
|
||||
FWP()->helper = new FacetWP_Helper();
|
||||
FWP()->facet = new FacetWP_Renderer();
|
||||
FWP()->settings = new FacetWP_Settings();
|
||||
FWP()->diff = new FacetWP_Diff();
|
||||
FWP()->indexer = new FacetWP_Indexer();
|
||||
FWP()->display = new FacetWP_Display();
|
||||
FWP()->builder = new FacetWP_Builder();
|
||||
FWP()->request = new FacetWP_Request();
|
||||
FWP()->ajax = new FacetWP_Ajax();
|
||||
|
||||
// integrations
|
||||
include( FACETWP_DIR . '/includes/integrations/searchwp/searchwp.php' );
|
||||
include( FACETWP_DIR . '/includes/integrations/woocommerce/woocommerce.php' );
|
||||
include( FACETWP_DIR . '/includes/integrations/edd/edd.php' );
|
||||
include( FACETWP_DIR . '/includes/integrations/acf/acf.php' );
|
||||
include( FACETWP_DIR . '/includes/integrations/wp-cli/wp-cli.php' );
|
||||
include( FACETWP_DIR . '/includes/integrations/wp-rocket/wp-rocket.php' );
|
||||
|
||||
// update checks
|
||||
include( FACETWP_DIR . '/includes/class-updater.php' );
|
||||
|
||||
// hooks
|
||||
add_action( 'admin_menu', [ $this, 'admin_menu' ] );
|
||||
add_action( 'wp_enqueue_scripts', [ $this, 'front_scripts' ] );
|
||||
add_filter( 'redirect_canonical', [ $this, 'redirect_canonical' ], 10, 2 );
|
||||
add_filter( 'plugin_action_links_facetwp/index.php', [ $this, 'plugin_action_links' ] );
|
||||
|
||||
do_action( 'facetwp_init' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* i18n support
|
||||
*/
|
||||
function load_textdomain() {
|
||||
|
||||
// admin-facing
|
||||
load_plugin_textdomain( 'fwp' );
|
||||
|
||||
// front-facing
|
||||
load_plugin_textdomain( 'fwp-front', false, basename( FACETWP_DIR ) . '/languages' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register the FacetWP settings page
|
||||
*/
|
||||
function admin_menu() {
|
||||
add_options_page( 'FacetWP', 'FacetWP', 'manage_options', 'facetwp', [ $this, 'settings_page' ] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enqueue jQuery
|
||||
*/
|
||||
function front_scripts() {
|
||||
if ( 'yes' == FWP()->helper->get_setting( 'load_jquery', 'yes' ) ) {
|
||||
wp_enqueue_script( 'jquery' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Route to the correct edit screen
|
||||
*/
|
||||
function settings_page() {
|
||||
include( FACETWP_DIR . '/templates/page-settings.php' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prevent WP from redirecting FWP pager to /page/X
|
||||
*/
|
||||
function redirect_canonical( $redirect_url, $requested_url ) {
|
||||
if ( false !== strpos( $redirect_url, FWP()->helper->get_setting( 'prefix' ) . 'paged' ) ) {
|
||||
return false;
|
||||
}
|
||||
return $redirect_url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add "Settings" link to plugin listing page
|
||||
*/
|
||||
function plugin_action_links( $links ) {
|
||||
$settings_link = admin_url( 'options-general.php?page=facetwp' );
|
||||
$settings_link = '<a href=" ' . $settings_link . '">' . __( 'Settings', 'fwp' ) . '</a>';
|
||||
array_unshift( $links, $settings_link );
|
||||
return $links;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* WooCommerce 3.6+ doesn't load its frontend includes for REST API requests
|
||||
* We need to force-load these includes for FacetWP refreshes
|
||||
* See includes() within class-woocommerce.php
|
||||
*
|
||||
* This code isn't within /integrations/woocommerce/ because it runs *before* init
|
||||
*
|
||||
* @since 3.3.10
|
||||
*/
|
||||
function is_rest_api_request( $request ) {
|
||||
if ( false !== strpos( $_SERVER['REQUEST_URI'], 'facetwp' ) ) {
|
||||
return false;
|
||||
}
|
||||
return $request;
|
||||
}
|
||||
}
|
||||
|
||||
$this->init = new FacetWP_Init();
|
||||
103
wp/wp-content/plugins/facetwp/includes/class-overrides.php
Normal file
103
wp/wp-content/plugins/facetwp/includes/class-overrides.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Overrides
|
||||
{
|
||||
|
||||
public $raw;
|
||||
|
||||
|
||||
function __construct() {
|
||||
add_filter( 'facetwp_index_row', [ $this, 'index_row' ], 5, 2 );
|
||||
add_filter( 'facetwp_index_row', [ $this, 'format_numbers' ], 15, 2 );
|
||||
add_filter( 'facetwp_is_main_query', [ $this, 'ignore_post_types' ], 10, 2 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indexer modifications
|
||||
*/
|
||||
function index_row( $params, $class ) {
|
||||
if ( $class->is_overridden ) {
|
||||
return $params;
|
||||
}
|
||||
|
||||
$facet = FWP()->helper->get_facet_by_name( $params['facet_name'] );
|
||||
|
||||
// Store raw numbers to format later
|
||||
if ( in_array( $facet['type'], [ 'number_range', 'slider' ] ) ) {
|
||||
$this->raw = [
|
||||
'value' => $params['facet_value'],
|
||||
'label' => $params['facet_display_value']
|
||||
];
|
||||
}
|
||||
|
||||
// Support "Other data source" values
|
||||
if ( ! empty( $facet['source_other'] ) ) {
|
||||
$other_params = $params;
|
||||
$other_params['facet_source'] = $facet['source_other'];
|
||||
$rows = $class->get_row_data( $other_params );
|
||||
$params['facet_display_value'] = $rows[0]['facet_display_value'];
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make sure that numbers are properly formatted
|
||||
*/
|
||||
function format_numbers( $params, $class ) {
|
||||
|
||||
if ( empty( $this->raw ) ) {
|
||||
return $params;
|
||||
}
|
||||
|
||||
$value = $params['facet_value'];
|
||||
$label = $params['facet_display_value'];
|
||||
|
||||
// Only format if un-altered
|
||||
if ( $this->raw['value'] === $value && $this->raw['label'] === $label ) {
|
||||
$params['facet_value'] = FWP()->helper->format_number( $this->raw['value'] );
|
||||
$params['facet_display_value'] = FWP()->helper->format_number( $this->raw['label'] );
|
||||
}
|
||||
|
||||
$this->raw = null;
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ignore certain post types
|
||||
*/
|
||||
function ignore_post_types( $is_main_query, $query ) {
|
||||
$blacklist = [
|
||||
'acf-field',
|
||||
'acf-field-group',
|
||||
'advanced_ads',
|
||||
'carts',
|
||||
'cookielawinfo',
|
||||
'edd_wish_list',
|
||||
'ms_relationship',
|
||||
'nav_menu_item',
|
||||
'wc_user_membership',
|
||||
'wp_block',
|
||||
'wp_global_styles',
|
||||
'wp_navigation',
|
||||
'wp_template',
|
||||
'wp_template_part'
|
||||
];
|
||||
$post_type = $query->get( 'post_type' );
|
||||
|
||||
if ( is_string( $post_type ) && in_array( $post_type, $blacklist ) ) {
|
||||
$is_main_query = false;
|
||||
}
|
||||
|
||||
// Ignore the "WP GDPR Compliance" plugin
|
||||
if ( '[wpgdprc_access_request_form]' == $query->get( 's' ) ) {
|
||||
$is_main_query = false;
|
||||
}
|
||||
|
||||
return $is_main_query;
|
||||
}
|
||||
}
|
||||
662
wp/wp-content/plugins/facetwp/includes/class-renderer.php
Normal file
662
wp/wp-content/plugins/facetwp/includes/class-renderer.php
Normal file
@@ -0,0 +1,662 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Renderer
|
||||
{
|
||||
|
||||
/* (array) Data for the current facets */
|
||||
public $facets;
|
||||
|
||||
/* (array) Data for the current template */
|
||||
public $template;
|
||||
|
||||
/* (array) WP_Query arguments */
|
||||
public $query_args;
|
||||
|
||||
/* (array) Data used to build the pager */
|
||||
public $pager_args;
|
||||
|
||||
/* (string) MySQL WHERE clause passed to each facet */
|
||||
public $where_clause = '';
|
||||
|
||||
/* (array) AJAX parameters passed in */
|
||||
public $ajax_params;
|
||||
|
||||
/* (array) HTTP parameters from the original page (URI, GET) */
|
||||
public $http_params;
|
||||
|
||||
/* (boolean) Is search active? */
|
||||
public $is_search = false;
|
||||
|
||||
/* (boolean) Are we preloading? */
|
||||
public $is_preload = false;
|
||||
|
||||
/* (array) Cache preloaded facet values */
|
||||
public $preloaded_values;
|
||||
|
||||
/* (array) The final WP_Query object */
|
||||
public $query;
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->facet_types = FWP()->helper->facet_types;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet output
|
||||
* @param array $params An array of arrays (see the FacetWP->refresh() method)
|
||||
* @return array
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = [
|
||||
'facets' => [],
|
||||
'template' => '',
|
||||
'settings' => [],
|
||||
];
|
||||
|
||||
// Hook params
|
||||
$params = apply_filters( 'facetwp_render_params', $params );
|
||||
|
||||
// First ajax refresh?
|
||||
$first_load = (bool) $params['first_load'];
|
||||
$is_bfcache = (bool) $params['is_bfcache'];
|
||||
|
||||
// Initial pageload?
|
||||
$this->is_preload = isset( $params['is_preload'] );
|
||||
|
||||
// Set the AJAX and HTTP params
|
||||
$this->ajax_params = $params;
|
||||
$this->http_params = $params['http_params'];
|
||||
|
||||
// Validate facets
|
||||
$this->facets = [];
|
||||
foreach ( $params['facets'] as $f ) {
|
||||
$name = $f['facet_name'];
|
||||
$facet = FWP()->helper->get_facet_by_name( $name );
|
||||
if ( $facet ) {
|
||||
|
||||
// Default to "OR" mode
|
||||
$facet['operator'] = $facet['operator'] ?? 'or';
|
||||
|
||||
// Support the "facetwp_preload_url_vars" hook
|
||||
if ( $first_load && empty( $f['selected_values'] ) && ! empty( $this->http_params['url_vars'][ $name ] ) ) {
|
||||
$f['selected_values'] = $this->http_params['url_vars'][ $name ];
|
||||
}
|
||||
|
||||
// Support commas within search / autocomplete facets
|
||||
if ( 'search' == $facet['type'] || 'autocomplete' == $facet['type'] ) {
|
||||
$f['selected_values'] = implode( ',', (array) $f['selected_values'] );
|
||||
}
|
||||
|
||||
$facet['selected_values'] = FWP()->helper->sanitize( $f['selected_values'] );
|
||||
|
||||
$this->facets[ $name ] = $facet;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the template from $helper->settings
|
||||
if ( 'wp' == $params['template'] ) {
|
||||
$this->template = [ 'name' => 'wp' ];
|
||||
$query_args = FWP()->request->query_vars ?? [];
|
||||
}
|
||||
else {
|
||||
$this->template = FWP()->helper->get_template_by_name( $params['template'] );
|
||||
$query_args = $this->get_query_args();
|
||||
}
|
||||
|
||||
// Detect search string
|
||||
if ( ! empty( $query_args['s'] ) ) {
|
||||
$this->is_search = true;
|
||||
}
|
||||
|
||||
// Run the query once (prevent duplicate queries when preloading)
|
||||
if ( empty( $this->query_args ) ) {
|
||||
|
||||
// Support "post__in"
|
||||
if ( empty( $query_args['post__in'] ) ) {
|
||||
$query_args['post__in'] = [];
|
||||
}
|
||||
|
||||
// Get the template "query" field
|
||||
$query_args = apply_filters( 'facetwp_query_args', $query_args, $this );
|
||||
|
||||
// Pagination
|
||||
$query_args['paged'] = empty( $params['paged'] ) ? 1 : (int) $params['paged'];
|
||||
|
||||
// Preserve SQL_CALC_FOUND_ROWS
|
||||
unset( $query_args['no_found_rows'] );
|
||||
|
||||
// Narrow posts based on facet selections
|
||||
$post_ids = $this->get_filtered_post_ids( $query_args );
|
||||
|
||||
// Update the SQL query
|
||||
if ( ! empty( $post_ids ) ) {
|
||||
if ( FWP()->is_filtered ) {
|
||||
$query_args['post__in'] = $post_ids;
|
||||
}
|
||||
|
||||
$this->where_clause = ' AND post_id IN (' . implode( ',', $post_ids ) . ')';
|
||||
}
|
||||
|
||||
// Set the default limit
|
||||
if ( empty( $query_args['posts_per_page'] ) ) {
|
||||
$query_args['posts_per_page'] = (int) get_option( 'posts_per_page' );
|
||||
}
|
||||
|
||||
// Adhere to the "per page" box
|
||||
$per_page = isset( $params['extras']['per_page'] ) ? $params['extras']['per_page'] : '';
|
||||
if ( ! empty( $per_page ) && 'default' != $per_page ) {
|
||||
$query_args['posts_per_page'] = (int) $per_page;
|
||||
}
|
||||
|
||||
$this->query_args = apply_filters( 'facetwp_filtered_query_args', $query_args, $this );
|
||||
|
||||
// Run the WP_Query
|
||||
$this->query = new WP_Query( $this->query_args );
|
||||
}
|
||||
|
||||
// Debug
|
||||
if ( 'on' == FWP()->helper->get_setting( 'debug_mode', 'off' ) ) {
|
||||
$output['settings']['debug'] = $this->get_debug_info();
|
||||
}
|
||||
|
||||
// Generate the template HTML
|
||||
// For performance gains, skip the template on pageload
|
||||
if ( 'wp' != $this->template['name'] ) {
|
||||
if ( ! $first_load || $is_bfcache || apply_filters( 'facetwp_template_force_load', false ) ) {
|
||||
$output['template'] = $this->get_template_html();
|
||||
}
|
||||
}
|
||||
|
||||
// Don't render these facets
|
||||
$frozen_facets = $params['frozen_facets'];
|
||||
|
||||
// Calculate pager args
|
||||
$pager_args = [
|
||||
'page' => (int) $this->query_args['paged'],
|
||||
'per_page' => (int) $this->query_args['posts_per_page'],
|
||||
'total_rows' => (int) $this->query->found_posts,
|
||||
'total_pages' => 1,
|
||||
];
|
||||
|
||||
if ( 0 < $pager_args['per_page'] ) {
|
||||
$pager_args['total_pages'] = ceil( $pager_args['total_rows'] / $pager_args['per_page'] );
|
||||
}
|
||||
|
||||
$pager_args = apply_filters( 'facetwp_pager_args', $pager_args, $this );
|
||||
|
||||
$this->pager_args = $pager_args;
|
||||
|
||||
// Stick the pager args into the JSON response
|
||||
$output['settings']['pager'] = $pager_args;
|
||||
|
||||
// Display the pagination HTML
|
||||
if ( isset( $params['extras']['pager'] ) ) {
|
||||
$output['pager'] = $this->paginate( $pager_args );
|
||||
}
|
||||
|
||||
// Display the "per page" HTML
|
||||
if ( isset( $params['extras']['per_page'] ) ) {
|
||||
$output['per_page'] = $this->get_per_page_box();
|
||||
}
|
||||
|
||||
// Display the counts HTML
|
||||
if ( isset( $params['extras']['counts'] ) ) {
|
||||
$output['counts'] = $this->get_result_count( $pager_args );
|
||||
}
|
||||
|
||||
// Not paging
|
||||
if ( 0 == $params['soft_refresh'] ) {
|
||||
$output['settings']['num_choices'] = [];
|
||||
}
|
||||
|
||||
// Get facet data
|
||||
foreach ( $this->facets as $facet_name => $the_facet ) {
|
||||
$facet_type = $the_facet['type'];
|
||||
$ui_type = empty( $the_facet['ui_type'] ) ? $facet_type : $the_facet['ui_type'];
|
||||
|
||||
// Invalid facet type
|
||||
if ( ! isset( $this->facet_types[ $facet_type ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip facets when paging
|
||||
if ( 0 < $params['soft_refresh'] && 'pager' != $facet_type ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get facet labels
|
||||
if ( 0 == $params['soft_refresh'] ) {
|
||||
$output['settings']['labels'][ $facet_name ] = facetwp_i18n( $the_facet['label'] );
|
||||
}
|
||||
|
||||
// Load all facets on back / forward button press (first_load = true)
|
||||
if ( ! $first_load ) {
|
||||
|
||||
// Skip frozen facets
|
||||
if ( isset( $frozen_facets[ $facet_name ] ) ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$args = [
|
||||
'facet' => $the_facet,
|
||||
'where_clause' => $this->where_clause,
|
||||
'selected_values' => $the_facet['selected_values'],
|
||||
];
|
||||
|
||||
// Load facet values if needed
|
||||
if ( method_exists( $this->facet_types[ $facet_type ], 'load_values' ) ) {
|
||||
|
||||
// Grab preloaded values if available
|
||||
if ( isset( $this->preloaded_values[ $facet_name ] ) ) {
|
||||
$args['values'] = $this->preloaded_values[ $facet_name ];
|
||||
}
|
||||
else {
|
||||
$args['values'] = $this->facet_types[ $facet_type ]->load_values( $args );
|
||||
|
||||
if ( $this->is_preload ) {
|
||||
$this->preloaded_values[ $facet_name ] = $args['values'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter the render args
|
||||
$args = apply_filters( 'facetwp_facet_render_args', $args );
|
||||
|
||||
// Return the number of available choices
|
||||
if ( isset( $args['values'] ) ) {
|
||||
$num_choices = 0;
|
||||
$is_ghost = FWP()->helper->facet_is( $the_facet, 'ghosts', 'yes' );
|
||||
|
||||
foreach ( $args['values'] as $choice ) {
|
||||
if ( isset( $choice['counter'] ) && ( 0 < $choice['counter'] || $is_ghost ) ) {
|
||||
$num_choices++;
|
||||
}
|
||||
}
|
||||
|
||||
$output['settings']['num_choices'][ $facet_name ] = $num_choices;
|
||||
}
|
||||
|
||||
// Generate the facet HTML
|
||||
$html = $this->facet_types[ $ui_type ]->render( $args );
|
||||
$output['facets'][ $facet_name ] = apply_filters( 'facetwp_facet_html', $html, $args );
|
||||
|
||||
// Return any JS settings
|
||||
if ( method_exists( $this->facet_types[ $ui_type ], 'settings_js' ) ) {
|
||||
$output['settings'][ $facet_name ] = $this->facet_types[ $ui_type ]->settings_js( $args );
|
||||
}
|
||||
|
||||
// Grab num_choices for slider facets
|
||||
if ( 'slider' == $the_facet['type'] ) {
|
||||
$min = $output['settings'][ $facet_name ]['range']['min'];
|
||||
$max = $output['settings'][ $facet_name ]['range']['max'];
|
||||
$output['settings']['num_choices'][ $facet_name ] = ( $min == $max ) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
return apply_filters( 'facetwp_render_output', $output, $params );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get WP_Query arguments by executing the template "query" field
|
||||
* @return null
|
||||
*/
|
||||
function get_query_args() {
|
||||
|
||||
$defaults = [];
|
||||
|
||||
// Allow templates to piggyback archives
|
||||
if ( apply_filters( 'facetwp_template_use_archive', false ) ) {
|
||||
$main_query = $GLOBALS['wp_the_query'];
|
||||
|
||||
// Initial pageload
|
||||
if ( $main_query->is_archive || $main_query->is_search ) {
|
||||
if ( $main_query->is_category ) {
|
||||
$defaults['cat'] = $main_query->get( 'cat' );
|
||||
}
|
||||
elseif ( $main_query->is_tag ) {
|
||||
$defaults['tag_id'] = $main_query->get( 'tag_id' );
|
||||
}
|
||||
elseif ( $main_query->is_tax ) {
|
||||
$defaults['taxonomy'] = $main_query->get( 'taxonomy' );
|
||||
$defaults['term'] = $main_query->get( 'term' );
|
||||
}
|
||||
elseif ( $main_query->is_search ) {
|
||||
$defaults['s'] = $main_query->get( 's' );
|
||||
}
|
||||
|
||||
$this->archive_args = $defaults;
|
||||
}
|
||||
// Subsequent ajax requests
|
||||
elseif ( ! empty( $this->http_params['archive_args'] ) ) {
|
||||
foreach ( $this->http_params['archive_args'] as $key => $val ) {
|
||||
if ( in_array( $key, [ 'cat', 'tag_id', 'taxonomy', 'term', 's' ] ) ) {
|
||||
$defaults[ $key ] = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the query builder
|
||||
if ( isset( $this->template['modes'] ) && 'visual' == $this->template['modes']['query'] ) {
|
||||
$query_args = FWP()->builder->parse_query_obj( $this->template['query_obj'] );
|
||||
}
|
||||
else {
|
||||
|
||||
// remove UTF-8 non-breaking spaces
|
||||
$query_args = preg_replace( "/\xC2\xA0/", ' ', $this->template['query'] );
|
||||
$query_args = (array) eval( '?>' . $query_args );
|
||||
}
|
||||
|
||||
// Merge the two arrays
|
||||
return array_merge( $defaults, $query_args );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get ALL post IDs for the matching query
|
||||
* @return array An array of post IDs
|
||||
*/
|
||||
function get_filtered_post_ids( $query_args = [] ) {
|
||||
|
||||
if ( empty( $query_args ) ) {
|
||||
$query_args = $this->query_args;
|
||||
}
|
||||
|
||||
// Only get relevant post IDs
|
||||
$args = array_merge( $query_args, [
|
||||
'paged' => 1,
|
||||
'posts_per_page' => -1,
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
'cache_results' => false,
|
||||
'no_found_rows' => true,
|
||||
'nopaging' => true, // prevent "offset" issues
|
||||
'facetwp' => false,
|
||||
'fields' => 'ids',
|
||||
] );
|
||||
|
||||
$query = new WP_Query( $args );
|
||||
|
||||
// Allow hooks to modify the default post IDs
|
||||
$post_ids = apply_filters( 'facetwp_pre_filtered_post_ids', $query->posts, $this );
|
||||
|
||||
// Store the unfiltered post IDs
|
||||
FWP()->unfiltered_post_ids = $post_ids;
|
||||
|
||||
foreach ( $this->facets as $facet_name => $the_facet ) {
|
||||
$facet_type = $the_facet['type'];
|
||||
|
||||
// Stop looping
|
||||
if ( empty( $post_ids ) ) {
|
||||
break;
|
||||
}
|
||||
|
||||
$matches = [];
|
||||
$selected_values = $the_facet['selected_values'];
|
||||
|
||||
if ( empty( $selected_values ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle each facet
|
||||
if ( isset( $this->facet_types[ $facet_type ] ) ) {
|
||||
|
||||
$hook_params = [
|
||||
'facet' => $the_facet,
|
||||
'selected_values' => $selected_values,
|
||||
];
|
||||
|
||||
// Hook to support custom filter_posts() handler
|
||||
$matches = apply_filters( 'facetwp_facet_filter_posts', false, $hook_params );
|
||||
|
||||
if ( false === $matches ) {
|
||||
$matches = $this->facet_types[ $facet_type ]->filter_posts( $hook_params );
|
||||
}
|
||||
}
|
||||
|
||||
// Skip this facet
|
||||
if ( 'continue' == $matches ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Force array
|
||||
$matches = (array) $matches;
|
||||
|
||||
// Store post IDs per facet (needed for "OR" mode)
|
||||
FWP()->or_values[ $facet_name ] = $matches;
|
||||
|
||||
if ( 'search' == $facet_type ) {
|
||||
$this->is_search = true;
|
||||
}
|
||||
|
||||
// For search facets, loop through $matches to set order
|
||||
// For other facets, loop through $post_ids to preserve the existing order
|
||||
$needles = ( 'search' == $facet_type ) ? $matches : $post_ids;
|
||||
$haystack = ( 'search' == $facet_type ) ? $post_ids : $matches;
|
||||
$haystack = array_flip( $haystack );
|
||||
$intersected_ids = [];
|
||||
|
||||
foreach ( $needles as $post_id ) {
|
||||
if ( isset( $haystack[ $post_id ] ) ) {
|
||||
$intersected_ids[] = $post_id;
|
||||
}
|
||||
}
|
||||
|
||||
$post_ids = $intersected_ids;
|
||||
}
|
||||
|
||||
$post_ids = apply_filters( 'facetwp_filtered_post_ids', array_values( $post_ids ), $this );
|
||||
|
||||
// Store the filtered post IDs
|
||||
FWP()->filtered_post_ids = $post_ids;
|
||||
|
||||
// Set a flag for whether filtering is applied
|
||||
FWP()->is_filtered = ( FWP()->filtered_post_ids !== FWP()->unfiltered_post_ids );
|
||||
|
||||
// Return a zero array if no matches
|
||||
return empty( $post_ids ) ? [ 0 ] : $post_ids;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run the template display code
|
||||
* @return string (HTML)
|
||||
*/
|
||||
function get_template_html() {
|
||||
global $post, $wp_query;
|
||||
|
||||
$output = apply_filters( 'facetwp_template_html', false, $this );
|
||||
|
||||
if ( false === $output ) {
|
||||
ob_start();
|
||||
|
||||
// Preserve globals
|
||||
$temp_post = is_object( $post ) ? clone $post : $post;
|
||||
$temp_wp_query = is_object( $wp_query ) ? clone $wp_query : $wp_query;
|
||||
|
||||
$query = $this->query;
|
||||
$wp_query = $query; // Make $query->blah() optional
|
||||
|
||||
if ( isset( $this->template['modes'] ) && 'visual' == $this->template['modes']['display'] ) {
|
||||
echo FWP()->builder->render_layout( $this->template['layout'] );
|
||||
}
|
||||
else {
|
||||
|
||||
// Remove UTF-8 non-breaking spaces
|
||||
$display_code = $this->template['template'];
|
||||
$display_code = preg_replace( "/\xC2\xA0/", ' ', $display_code );
|
||||
eval( '?>' . $display_code );
|
||||
}
|
||||
|
||||
// Reset globals
|
||||
$post = $temp_post;
|
||||
$wp_query = $temp_wp_query;
|
||||
|
||||
// Store buffered output
|
||||
$output = ob_get_clean();
|
||||
}
|
||||
|
||||
$output = preg_replace( "/\xC2\xA0/", ' ', $output );
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Result count (1-10 of 234)
|
||||
* @param array $params An array with "page", "per_page", and "total_rows"
|
||||
* @return string
|
||||
*/
|
||||
function get_result_count( $params = [] ) {
|
||||
$text_of = __( 'of', 'fwp-front' );
|
||||
|
||||
$page = (int) $params['page'];
|
||||
$per_page = (int) $params['per_page'];
|
||||
$total_rows = (int) $params['total_rows'];
|
||||
|
||||
if ( $per_page < $total_rows ) {
|
||||
$lower = ( 1 + ( ( $page - 1 ) * $per_page ) );
|
||||
$upper = ( $page * $per_page );
|
||||
$upper = ( $total_rows < $upper ) ? $total_rows : $upper;
|
||||
$output = "$lower-$upper $text_of $total_rows";
|
||||
}
|
||||
else {
|
||||
$lower = ( 0 < $total_rows ) ? 1 : 0;
|
||||
$upper = $total_rows;
|
||||
$output = $total_rows;
|
||||
}
|
||||
|
||||
return apply_filters( 'facetwp_result_count', $output, [
|
||||
'lower' => $lower,
|
||||
'upper' => $upper,
|
||||
'total' => $total_rows,
|
||||
] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pagination
|
||||
* @param array $params An array with "page", "per_page", and "total_rows"
|
||||
* @return string
|
||||
*/
|
||||
function paginate( $params = [] ) {
|
||||
$pager_class = FWP()->helper->facet_types['pager'];
|
||||
$pager_class->pager_args = $params;
|
||||
|
||||
$output = $pager_class->render_numbers([
|
||||
'inner_size' => 2,
|
||||
'dots_label' => '…',
|
||||
'prev_label' => '<<',
|
||||
'next_label' => '>>',
|
||||
]);
|
||||
|
||||
return apply_filters( 'facetwp_pager_html', $output, $params );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* "Per Page" dropdown box
|
||||
* @return string
|
||||
*/
|
||||
function get_per_page_box() {
|
||||
$pager_class = FWP()->helper->facet_types['pager'];
|
||||
$pager_class->pager_args = $this->pager_args;
|
||||
|
||||
$options = apply_filters( 'facetwp_per_page_options', [ 10, 25, 50, 100 ] );
|
||||
|
||||
$output = $pager_class->render_per_page([
|
||||
'default_label' => __( 'Per page', 'fwp-front' ),
|
||||
'per_page_options' => implode( ',', $options )
|
||||
]);
|
||||
|
||||
return apply_filters( 'facetwp_per_page_html', $output, [
|
||||
'options' => $options
|
||||
] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get debug info for the browser console
|
||||
* @since 3.5.7
|
||||
*/
|
||||
function get_debug_info() {
|
||||
$last_indexed = get_option( 'facetwp_last_indexed' );
|
||||
$last_indexed = $last_indexed ? human_time_diff( $last_indexed ) : 'never';
|
||||
|
||||
$debug = [
|
||||
'query_args' => $this->query_args,
|
||||
'sql' => $this->query->request,
|
||||
'facets' => $this->facets,
|
||||
'template' => $this->template,
|
||||
'settings' => FWP()->helper->settings['settings'],
|
||||
'last_indexed' => $last_indexed,
|
||||
'row_counts' => FWP()->helper->get_row_counts(),
|
||||
'hooks_used' => $this->get_hooks_used()
|
||||
];
|
||||
|
||||
// Reduce debug payload
|
||||
if ( ! empty( $this->query_args['post__in'] ) ) {
|
||||
$debug['query_args']['post__in_count'] = count( $this->query_args['post__in'] );
|
||||
$debug['query_args']['post__in'] = array_slice( $this->query_args['post__in'], 0, 10 );
|
||||
|
||||
$debug['sql'] = preg_replace_callback( '/posts.ID IN \((.*?)\)/s', function( $matches ) {
|
||||
$count = substr_count( $matches[1], ',' ) + 1;
|
||||
return ( $count <= 10 ) ? $matches[0] : "posts.ID IN (<$count IDs>)";
|
||||
}, $debug['sql'] );
|
||||
}
|
||||
|
||||
return $debug;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display the location of relevant hooks (for Debug Mode)
|
||||
* @since 3.5.7
|
||||
*/
|
||||
function get_hooks_used() {
|
||||
$relevant_hooks = [];
|
||||
|
||||
foreach ( $GLOBALS['wp_filter'] as $tag => $hook_data ) {
|
||||
if ( 0 === strpos( $tag, 'facetwp' ) || 'pre_get_posts' == $tag ) {
|
||||
foreach ( $hook_data->callbacks as $callbacks ) {
|
||||
foreach ( $callbacks as $cb ) {
|
||||
if ( is_string( $cb['function'] ) && false !== strpos( $cb['function'], '::' ) ) {
|
||||
$cb['function'] = explode( '::', $cb['function'] );
|
||||
}
|
||||
|
||||
if ( is_array( $cb['function'] ) ) {
|
||||
$class = is_object( $cb['function'][0] ) ? get_class( $cb['function'][0] ) : $cb['function'][0];
|
||||
$ref = new ReflectionMethod( $class, $cb['function'][1] );
|
||||
}
|
||||
elseif ( is_object( $cb['function'] ) ) {
|
||||
if ( is_a( $cb['function'], 'Closure' ) ) {
|
||||
$ref = new ReflectionFunction( $cb['function'] );
|
||||
}
|
||||
else {
|
||||
$class = get_class( $cb['function'] );
|
||||
$ref = new ReflectionMethod( $class, '__invoke' );
|
||||
}
|
||||
}
|
||||
else {
|
||||
$ref = new ReflectionFunction( $cb['function'] );
|
||||
}
|
||||
|
||||
$filename = str_replace( ABSPATH, '', $ref->getFileName() );
|
||||
|
||||
// ignore built-in hooks
|
||||
if ( false === strpos( $filename, 'plugins/facetwp' ) ) {
|
||||
if ( false !== strpos( $filename, 'wp-content' ) ) {
|
||||
$relevant_hooks[ $tag ][] = $filename . ':' . $ref->getStartLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $relevant_hooks;
|
||||
}
|
||||
}
|
||||
379
wp/wp-content/plugins/facetwp/includes/class-request.php
Normal file
379
wp/wp-content/plugins/facetwp/includes/class-request.php
Normal file
@@ -0,0 +1,379 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Request
|
||||
{
|
||||
|
||||
/* (array) FacetWP-related GET variables */
|
||||
public $url_vars = [];
|
||||
|
||||
/* (mixed) The main query vars */
|
||||
public $query_vars = null;
|
||||
|
||||
/* (boolean) FWP template shortcode? */
|
||||
public $is_shortcode = false;
|
||||
|
||||
/* (boolean) Is a FacetWP refresh? */
|
||||
public $is_refresh = false;
|
||||
|
||||
/* (boolean) Initial load? */
|
||||
public $is_preload = false;
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->process_json();
|
||||
$this->intercept_request();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* application/json requires processing the raw PHP input stream
|
||||
*/
|
||||
function process_json() {
|
||||
$json = file_get_contents( 'php://input' );
|
||||
if ( 0 === strpos( $json, '{' ) ) {
|
||||
$post_data = json_decode( $json, true );
|
||||
$action = $post_data['action'] ?? '';
|
||||
|
||||
if ( is_string( $action ) && 0 === strpos( $action, 'facetwp' ) ) {
|
||||
$_POST = $post_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If AJAX and the template is "wp", return the buffered HTML
|
||||
* Otherwise, store the GET variables for later use
|
||||
*/
|
||||
function intercept_request() {
|
||||
$action = isset( $_POST['action'] ) ? sanitize_key( $_POST['action'] ) : '';
|
||||
|
||||
$valid_actions = [
|
||||
'facetwp_refresh',
|
||||
'facetwp_autocomplete_load'
|
||||
];
|
||||
|
||||
$this->is_refresh = ( 'facetwp_refresh' == $action );
|
||||
$this->is_preload = ! in_array( $action, $valid_actions );
|
||||
$prefix = FWP()->helper->get_setting( 'prefix' );
|
||||
$is_css_tpl = isset( $_POST['data']['template'] ) && 'wp' == $_POST['data']['template'];
|
||||
|
||||
// Disable the admin bar to prevent JSON issues
|
||||
if ( $this->is_refresh ) {
|
||||
add_filter( 'show_admin_bar', '__return_false' );
|
||||
}
|
||||
|
||||
// Pageload
|
||||
if ( $this->is_preload ) {
|
||||
$features = [ 'paged', 'per_page', 'sort' ];
|
||||
$valid_names = wp_list_pluck( FWP()->helper->get_facets(), 'name' );
|
||||
$valid_names = array_merge( $valid_names, $features );
|
||||
|
||||
// Store GET variables
|
||||
foreach ( $valid_names as $name ) {
|
||||
if ( isset( $_GET[ $prefix . $name ] ) && '' !== $_GET[ $prefix . $name ] ) {
|
||||
$new_val = stripslashes_deep( $_GET[ $prefix . $name ] );
|
||||
$new_val = in_array( $name, $features ) ? $new_val : explode( ',', $new_val );
|
||||
$this->url_vars[ $name ] = $new_val;
|
||||
}
|
||||
}
|
||||
|
||||
$this->url_vars = apply_filters( 'facetwp_preload_url_vars', $this->url_vars );
|
||||
}
|
||||
// Populate $_GET
|
||||
else {
|
||||
$data = stripslashes_deep( $_POST['data'] );
|
||||
|
||||
if ( ! empty( $data['http_params']['get'] ) ) {
|
||||
foreach ( $data['http_params']['get'] as $key => $val ) {
|
||||
if ( ! isset( $_GET[ $key ] ) ) {
|
||||
$_GET[ $key ] = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $this->is_preload || $is_css_tpl ) {
|
||||
add_filter( 'posts_pre_query', [ $this, 'maybe_abort_query' ], 10, 2 );
|
||||
add_action( 'pre_get_posts', [ $this, 'sacrificial_lamb' ], 998 );
|
||||
add_action( 'pre_get_posts', [ $this, 'update_query_vars' ], 999 );
|
||||
}
|
||||
|
||||
if ( ! $this->is_preload && $is_css_tpl && 'facetwp_autocomplete_load' != $action ) {
|
||||
add_action( 'shutdown', [ $this, 'inject_template' ], 0 );
|
||||
ob_start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* FacetWP runs the archive query before WP gets the chance.
|
||||
* This hook prevents the query from running twice, by letting us inject the
|
||||
* first query's posts (and counts) into the "main" query.
|
||||
*/
|
||||
function maybe_abort_query( $posts, $query ) {
|
||||
$do_abort = apply_filters( 'facetwp_archive_abort_query', true, $query );
|
||||
$has_query_run = ( ! empty( FWP()->facet->query ) );
|
||||
|
||||
if ( $do_abort && $has_query_run && isset( $this->query_vars ) ) {
|
||||
|
||||
// New var; any changes to $query will cause is_main_query() to return false
|
||||
$query_vars = $query->query_vars;
|
||||
|
||||
// If paged = 0, set to 1 or the compare will fail
|
||||
if ( empty( $query_vars['paged'] ) ) {
|
||||
$query_vars['paged'] = 1;
|
||||
}
|
||||
|
||||
// Only intercept the identical query
|
||||
if ( $query_vars === $this->query_vars ) {
|
||||
$posts = FWP()->facet->query->posts;
|
||||
$query->found_posts = FWP()->facet->query->found_posts;
|
||||
$query->max_num_pages = FWP()->facet->query->max_num_pages;
|
||||
}
|
||||
}
|
||||
|
||||
return $posts;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fixes https://core.trac.wordpress.org/ticket/40393
|
||||
*/
|
||||
function sacrificial_lamb( $query ) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Force FacetWP to use the default WP query
|
||||
*/
|
||||
function update_query_vars( $query ) {
|
||||
|
||||
if ( isset( $this->query_vars ) // Ran already
|
||||
|| $this->is_shortcode // Skip shortcode template
|
||||
|| ( is_admin() && ! wp_doing_ajax() ) // Skip admin
|
||||
|| ( wp_doing_ajax() && ! $this->is_refresh ) // Skip other ajax
|
||||
|| ! $this->is_main_query( $query ) // Not the main query
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the flag
|
||||
$query->set( 'facetwp', true );
|
||||
|
||||
// If "s" is an empty string and no post_type is set, WP sets
|
||||
// post_type = "any". We want to prevent this except on the search page.
|
||||
if ( '' == $query->get( 's' ) && ! isset( $_GET['s'] ) ) {
|
||||
$query->set( 's', null );
|
||||
}
|
||||
|
||||
// Set the initial query vars, needed for render()
|
||||
$this->query_vars = $query->query_vars;
|
||||
|
||||
// Notify
|
||||
do_action( 'facetwp_found_main_query' );
|
||||
|
||||
// Generate the FWP output
|
||||
$data = ( $this->is_preload ) ? $this->process_preload_data() : $this->process_post_data();
|
||||
$this->output = FWP()->facet->render( $data );
|
||||
|
||||
// Set the updated query vars, needed for maybe_abort_query()
|
||||
$this->query_vars = FWP()->facet->query->query_vars;
|
||||
|
||||
// Set the updated query vars
|
||||
$force_query = apply_filters( 'facetwp_preload_force_query', false, $query );
|
||||
|
||||
if ( ! $this->is_preload || ! empty( $this->url_vars ) || $force_query ) {
|
||||
$query->query_vars = FWP()->facet->query_args;
|
||||
}
|
||||
|
||||
if ( 'product_query' == $query->get( 'wc_query' ) ) {
|
||||
wc_set_loop_prop( 'total', FWP()->facet->pager_args['total_rows'] );
|
||||
wc_set_loop_prop( 'per_page', FWP()->facet->pager_args['per_page'] );
|
||||
wc_set_loop_prop( 'total_pages', FWP()->facet->pager_args['total_pages'] );
|
||||
wc_set_loop_prop( 'current_page', FWP()->facet->pager_args['page'] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is this the main query?
|
||||
*/
|
||||
function is_main_query( $query ) {
|
||||
if ( 'yes' == FWP()->helper->get_setting( 'strict_query_detection', 'no' ) ) {
|
||||
$is_main_query = ( $query->is_main_query() );
|
||||
}
|
||||
else {
|
||||
$is_main_query = ( $query->is_main_query() || $query->is_archive );
|
||||
}
|
||||
|
||||
$is_main_query = ( $query->is_singular || $query->is_feed ) ? false : $is_main_query;
|
||||
$is_main_query = ( $query->get( 'suppress_filters', false ) ) ? false : $is_main_query; // skip get_posts()
|
||||
$is_main_query = ( '' !== $query->get( 'facetwp' ) ) ? (bool) $query->get( 'facetwp' ) : $is_main_query; // flag
|
||||
return apply_filters( 'facetwp_is_main_query', $is_main_query, $query );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the AJAX $_POST data
|
||||
* This gets passed into FWP()->facet->render()
|
||||
*/
|
||||
function process_post_data() {
|
||||
$data = stripslashes_deep( $_POST['data'] );
|
||||
$facets = $data['facets'];
|
||||
$extras = $data['extras'] ?? [];
|
||||
$frozen_facets = $data['frozen_facets'] ?? [];
|
||||
|
||||
$params = [
|
||||
'facets' => [],
|
||||
'template' => $data['template'],
|
||||
'frozen_facets' => $frozen_facets,
|
||||
'http_params' => $data['http_params'],
|
||||
'extras' => $extras,
|
||||
'soft_refresh' => (int) $data['soft_refresh'],
|
||||
'is_bfcache' => (int) $data['is_bfcache'],
|
||||
'first_load' => (int) $data['first_load'], // skip the template?
|
||||
'paged' => (int) $data['paged'],
|
||||
];
|
||||
|
||||
foreach ( $facets as $facet_name => $selected_values ) {
|
||||
$params['facets'][] = [
|
||||
'facet_name' => $facet_name,
|
||||
'selected_values' => $selected_values,
|
||||
];
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* On initial pageload, preload the data
|
||||
*
|
||||
* This gets called twice; once in the template shortcode (to grab only the template)
|
||||
* and again in FWP()->display->front_scripts() to grab everything else.
|
||||
*
|
||||
* Two calls are needed for timing purposes; the template shortcode often renders
|
||||
* before some or all of the other FacetWP-related shortcodes.
|
||||
*/
|
||||
function process_preload_data( $template_name = false, $overrides = [] ) {
|
||||
|
||||
if ( false === $template_name ) {
|
||||
$template_name = $this->template_name ?? 'wp';
|
||||
}
|
||||
|
||||
$this->template_name = $template_name;
|
||||
|
||||
// Is this a template shortcode?
|
||||
$this->is_shortcode = ( 'wp' != $template_name );
|
||||
|
||||
$params = [
|
||||
'facets' => [],
|
||||
'template' => $template_name,
|
||||
'http_params' => [
|
||||
'get' => $_GET,
|
||||
'uri' => FWP()->helper->get_uri(),
|
||||
'url_vars' => $this->url_vars,
|
||||
],
|
||||
'frozen_facets' => [],
|
||||
'soft_refresh' => 1, // skip the facets
|
||||
'is_preload' => 1,
|
||||
'is_bfcache' => 0,
|
||||
'first_load' => 0, // load the template
|
||||
'extras' => [],
|
||||
'paged' => 1,
|
||||
];
|
||||
|
||||
// Support "/page/X/" on preload
|
||||
if ( ! empty( $this->query_vars['paged'] ) ) {
|
||||
$params['paged'] = (int) $this->query_vars['paged'];
|
||||
}
|
||||
|
||||
foreach ( $this->url_vars as $key => $val ) {
|
||||
if ( 'paged' == $key ) {
|
||||
$params['paged'] = $val;
|
||||
}
|
||||
elseif ( 'per_page' == $key || 'sort' == $key ) {
|
||||
$params['extras'][ $key ] = $val;
|
||||
}
|
||||
else {
|
||||
$params['facets'][] = [
|
||||
'facet_name' => $key,
|
||||
'selected_values' => $val,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Override the defaults
|
||||
$params = array_merge( $params, $overrides );
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This gets called from FWP()->display->front_scripts(), when we finally
|
||||
* know which shortcodes are on the page.
|
||||
*
|
||||
* Since we already got the template HTML on the first process_preload_data() call,
|
||||
* this time we're grabbing everything but the template.
|
||||
*
|
||||
* The return value of this method gets passed into the 2nd argument of
|
||||
* process_preload_data().
|
||||
*/
|
||||
function process_preload_overrides( $items ) {
|
||||
$overrides = [];
|
||||
$url_vars = FWP()->request->url_vars;
|
||||
|
||||
foreach ( $items['facets'] as $name ) {
|
||||
$overrides['facets'][] = [
|
||||
'facet_name' => $name,
|
||||
'selected_values' => $url_vars[ $name ] ?? [],
|
||||
];
|
||||
}
|
||||
|
||||
if ( isset( $items['extras']['counts'] ) ) {
|
||||
$overrides['extras']['counts'] = true;
|
||||
}
|
||||
if ( isset( $items['extras']['pager'] ) ) {
|
||||
$overrides['extras']['pager'] = true;
|
||||
}
|
||||
if ( isset( $items['extras']['per_page'] ) ) {
|
||||
$overrides['extras']['per_page'] = $url_vars['per_page'] ?? 'default';
|
||||
}
|
||||
if ( isset( $items['extras']['sort'] ) ) {
|
||||
$overrides['extras']['sort'] = $url_vars['sort'] ?? 'default';
|
||||
}
|
||||
|
||||
$overrides['soft_refresh'] = 0; // load the facets
|
||||
$overrides['first_load'] = 1; // skip the template
|
||||
|
||||
return $overrides;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inject the page HTML into the JSON response
|
||||
* We'll cherry-pick the content from the HTML using front.js
|
||||
*/
|
||||
function inject_template() {
|
||||
$html = ob_get_clean();
|
||||
|
||||
// Throw an error
|
||||
if ( empty( $this->output['settings'] ) ) {
|
||||
$html = __( 'FacetWP was unable to auto-detect the post listing', 'fwp' );
|
||||
}
|
||||
// Grab the <body> contents
|
||||
else {
|
||||
preg_match( "/<body(.*?)>(.*?)<\/body>/s", $html, $matches );
|
||||
|
||||
if ( ! empty( $matches ) ) {
|
||||
$html = trim( $matches[2] );
|
||||
}
|
||||
}
|
||||
|
||||
$this->output['template'] = $html;
|
||||
do_action( 'facetwp_inject_template', $this->output );
|
||||
wp_send_json( $this->output );
|
||||
}
|
||||
}
|
||||
646
wp/wp-content/plugins/facetwp/includes/class-settings.php
Normal file
646
wp/wp-content/plugins/facetwp/includes/class-settings.php
Normal file
@@ -0,0 +1,646 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Settings
|
||||
{
|
||||
|
||||
/**
|
||||
* Get the field settings array
|
||||
* @since 3.0.0
|
||||
*/
|
||||
function get_registered_settings() {
|
||||
$defaults = [
|
||||
'general' => [
|
||||
'label' => __( 'General', 'fwp' ),
|
||||
'fields' => [
|
||||
'license_key' => [
|
||||
'label' => __( 'License key', 'fwp' ),
|
||||
'html' => $this->get_setting_html( 'license_key' )
|
||||
],
|
||||
'gmaps_api_key' => [
|
||||
'label' => __( 'Google Maps API key', 'fwp' ),
|
||||
'html' => $this->get_setting_html( 'gmaps_api_key' )
|
||||
],
|
||||
'separators' => [
|
||||
'label' => __( 'Separators', 'fwp' ),
|
||||
'notes' => 'Enter the thousands and decimal separators, respectively',
|
||||
'html' => $this->get_setting_html( 'separators' )
|
||||
],
|
||||
'prefix' => [
|
||||
'label' => __( 'URL prefix', 'fwp' ),
|
||||
'html' => $this->get_setting_html( 'prefix', 'dropdown', [
|
||||
'choices' => [ 'fwp_' => 'fwp_', '_' => '_' ]
|
||||
])
|
||||
],
|
||||
'load_jquery' => [
|
||||
'label' => __( 'Load jQuery', 'fwp' ),
|
||||
'notes' => 'FacetWP no longer requires jQuery, but enable if needed',
|
||||
'html' => $this->get_setting_html( 'load_jquery', 'toggle', [
|
||||
'true_value' => 'yes',
|
||||
'false_value' => 'no'
|
||||
])
|
||||
],
|
||||
'load_a11y' => [
|
||||
'label' => __( 'Load a11y support', 'fwp' ),
|
||||
'notes' => 'Improved accessibility for users with disabilities',
|
||||
'html' => $this->get_setting_html( 'load_a11y', 'toggle', [
|
||||
'true_value' => 'yes',
|
||||
'false_value' => 'no'
|
||||
])
|
||||
],
|
||||
'strict_query_detection' => [
|
||||
'label' => __( 'Strict query detection', 'fwp' ),
|
||||
'notes' => 'Enable if FacetWP auto-detects the wrong archive query',
|
||||
'html' => $this->get_setting_html( 'strict_query_detection', 'toggle', [
|
||||
'true_value' => 'yes',
|
||||
'false_value' => 'no'
|
||||
])
|
||||
],
|
||||
'debug_mode' => [
|
||||
'label' => __( 'Debug mode', 'fwp' ),
|
||||
'notes' => 'After enabling, type "FWP.settings.debug" into the browser console on your front-end facet page',
|
||||
'html' => $this->get_setting_html( 'debug_mode', 'toggle', [
|
||||
'true_value' => 'on',
|
||||
'false_value' => 'off'
|
||||
])
|
||||
]
|
||||
]
|
||||
],
|
||||
'woocommerce' => [
|
||||
'label' => __( 'WooCommerce', 'fwp' ),
|
||||
'fields' => [
|
||||
'wc_enable_variations' => [
|
||||
'label' => __( 'Support product variations?', 'fwp' ),
|
||||
'notes' => __( 'Enable if your store uses variable products.', 'fwp' ),
|
||||
'html' => $this->get_setting_html( 'wc_enable_variations', 'toggle' )
|
||||
],
|
||||
'wc_index_all' => [
|
||||
'label' => __( 'Index out-of-stock products?', 'fwp' ),
|
||||
'notes' => __( 'Show facet choices for out-of-stock products?', 'fwp' ),
|
||||
'html' => $this->get_setting_html( 'wc_index_all', 'toggle' )
|
||||
]
|
||||
]
|
||||
],
|
||||
'backup' => [
|
||||
'label' => __( 'Import / Export', 'fwp' ),
|
||||
'fields' => [
|
||||
'export' => [
|
||||
'label' => __( 'Export', 'fwp' ),
|
||||
'html' => $this->get_setting_html( 'export' )
|
||||
],
|
||||
'import' => [
|
||||
'label' => __( 'Import', 'fwp' ),
|
||||
'html' => $this->get_setting_html( 'import' )
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
if ( ! is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
|
||||
unset( $defaults['woocommerce'] );
|
||||
}
|
||||
|
||||
if ( '_' == FWP()->helper->settings['settings']['prefix'] ) {
|
||||
unset( $defaults['general']['fields']['prefix'] );
|
||||
}
|
||||
|
||||
return apply_filters( 'facetwp_settings_admin', $defaults, $this );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* All facet admin fields
|
||||
* @since 3.9
|
||||
*/
|
||||
function get_registered_facet_fields() {
|
||||
$settings = [
|
||||
'label_any' => [
|
||||
'label' => __( 'Default label', 'fwp' ),
|
||||
'notes' => 'Customize the "Any" label',
|
||||
'default' => __( 'Any', 'fwp' )
|
||||
],
|
||||
'placeholder' => [
|
||||
'label' => __( 'Placeholder text', 'fwp' )
|
||||
],
|
||||
'parent_term' => [
|
||||
'label' => __( 'Parent term', 'fwp' ),
|
||||
'notes' => 'To show only child terms, enter the parent <a href="https://facetwp.com/how-to-find-a-wordpress-terms-id/" target="_blank">term ID</a>. Otherwise, leave blank.',
|
||||
'show' => "facet.source.substr(0, 3) == 'tax'"
|
||||
],
|
||||
'hierarchical' => [
|
||||
'type' => 'toggle',
|
||||
'label' => __( 'Hierarchical', 'fwp' ),
|
||||
'notes' => 'Is this a hierarchical taxonomy?',
|
||||
'show' => "facet.source.substr(0, 3) == 'tax'"
|
||||
],
|
||||
'show_expanded' => [
|
||||
'type' => 'toggle',
|
||||
'label' => __( 'Show expanded', 'fwp' ),
|
||||
'notes' => 'Should child terms be visible by default?',
|
||||
'show' => "facet.hierarchical == 'yes'"
|
||||
],
|
||||
'multiple' => [
|
||||
'type' => 'toggle',
|
||||
'label' => __( 'Multi-select', 'fwp' ),
|
||||
'notes' => 'Allow multiple selections?'
|
||||
],
|
||||
'ghosts' => [
|
||||
'type' => 'alias',
|
||||
'items' => [
|
||||
'ghosts' => [
|
||||
'type' => 'toggle',
|
||||
'label' => __( 'Show ghosts', 'fwp' ),
|
||||
'notes' => 'Show choices that would return zero results?'
|
||||
],
|
||||
'preserve_ghosts' => [
|
||||
'type' => 'toggle',
|
||||
'label' => __( 'Preserve ghost order', 'fwp' ),
|
||||
'notes' => 'Keep ghost choices in the same order?',
|
||||
'show' => "facet.ghosts == 'yes'"
|
||||
]
|
||||
]
|
||||
],
|
||||
'modifiers' => [
|
||||
'type' => 'alias',
|
||||
'items' => [
|
||||
'modifier_type' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Value modifiers', 'fwp' ),
|
||||
'notes' => 'Include or exclude certain values?',
|
||||
'choices' => [
|
||||
'off' => __( 'Off', 'fwp' ),
|
||||
'exclude' => __( 'Exclude these values', 'fwp' ),
|
||||
'include' => __( 'Show only these values', 'fwp' )
|
||||
]
|
||||
],
|
||||
'modifier_values' => [
|
||||
'type' => 'textarea',
|
||||
'label' => '',
|
||||
'placeholder' => 'Add one value per line',
|
||||
'show' => "facet.modifier_type != 'off'"
|
||||
]
|
||||
]
|
||||
],
|
||||
'operator' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Facet logic', 'fwp' ),
|
||||
'notes' => 'How should multiple selections affect the results?',
|
||||
'choices' => [
|
||||
'and' => __( 'AND (match all)', 'fwp' ),
|
||||
'or' => __( 'OR (match any)', 'fwp' )
|
||||
]
|
||||
],
|
||||
'orderby' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Sort by', 'fwp' ),
|
||||
'choices' => [
|
||||
'count' => __( 'Highest count', 'fwp' ),
|
||||
'display_value' => __( 'Display value', 'fwp' ),
|
||||
'raw_value' => __( 'Raw value', 'fwp' ),
|
||||
'term_order' => __( 'Term order', 'fwp' )
|
||||
]
|
||||
],
|
||||
'count' => [
|
||||
'label' => __( 'Count', 'fwp' ),
|
||||
'notes' => 'The maximum number of choices to show (-1 for no limit)',
|
||||
'default' => 10
|
||||
],
|
||||
'soft_limit' => [
|
||||
'label' => __( 'Soft limit', 'fwp' ),
|
||||
'notes' => 'Show a toggle link after this many choices',
|
||||
'default' => 5,
|
||||
'show' => "facet.hierarchical != 'yes'"
|
||||
],
|
||||
'source_other' => [
|
||||
'label' => __( 'Other data source', 'fwp' ),
|
||||
'notes' => 'Use a separate value for the upper limit?',
|
||||
'html' => '<data-sources :facet="facet" setting-name="source_other"></data-sources>'
|
||||
],
|
||||
'compare_type' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Compare type', 'fwp' ),
|
||||
'notes' => "<strong>Basic</strong> - entered range surrounds the post's range<br /><strong>Enclose</strong> - entered range is fully inside the post's range<br /><strong>Intersect</strong> - entered range overlaps the post's range<br /><br />When in doubt, choose <strong>Basic</strong>",
|
||||
'choices' => [
|
||||
'' => __( 'Basic', 'fwp' ),
|
||||
'enclose' => __( 'Enclose', 'fwp' ),
|
||||
'intersect' => __( 'Intersect', 'fwp' )
|
||||
]
|
||||
],
|
||||
'ui_type' => [
|
||||
'label' => __( 'UI type', 'fwp' ),
|
||||
'html' => '<ui-type :facet="facet"></ui-type>'
|
||||
],
|
||||
'reset_text' => [
|
||||
'label' => __( 'Reset text', 'fwp' ),
|
||||
'default' => 'Reset'
|
||||
]
|
||||
];
|
||||
|
||||
foreach ( FWP()->helper->facet_types as $name => $obj ) {
|
||||
if ( method_exists( $obj, 'register_fields' ) ) {
|
||||
$settings = array_merge( $settings, $obj->register_fields() );
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return HTML for a single facet field (supports aliases)
|
||||
* @since 3.9
|
||||
*/
|
||||
function get_facet_field_html( $name ) {
|
||||
ob_start();
|
||||
|
||||
$fields = FWP()->settings->get_registered_facet_fields();
|
||||
|
||||
if ( isset( $fields[ $name ] ) ) {
|
||||
$field = $fields[ $name ];
|
||||
|
||||
if ( isset( $field['type'] ) && 'alias' == $field['type'] ) {
|
||||
foreach ( $field['items'] as $k => $v ) {
|
||||
$v['name'] = $k;
|
||||
$this->render_facet_field( $v );
|
||||
}
|
||||
}
|
||||
else {
|
||||
$field['name'] = $name;
|
||||
$this->render_facet_field( $field );
|
||||
}
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render a facet field
|
||||
* @since 3.9
|
||||
*/
|
||||
function render_facet_field( $field ) {
|
||||
$name = str_replace( '_', '-', $field['name'] );
|
||||
$type = $field['type'] ?? 'text';
|
||||
$placeholder = $field['placeholder'] ?? '';
|
||||
$show = isset( $field['show'] ) ? ' v-show="' . $field['show'] . '"' : '';
|
||||
$default = isset( $field['default'] ) ? ' value="' . $field['default'] . '"' : '';
|
||||
$label = empty( $field['label'] ) ? '' : $field['label'];
|
||||
|
||||
if ( isset( $field['notes'] ) ) {
|
||||
$label = '<div class="facetwp-tooltip">' . $label . '<div class="facetwp-tooltip-content">' . $field['notes'] . '</div></div>';
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
if ( isset( $field['html'] ) ) {
|
||||
echo $field['html'];
|
||||
}
|
||||
elseif ( 'text' == $type ) {
|
||||
?>
|
||||
<input type="text" class="facet-<?php echo $name; ?>" placeholder="<?php echo $placeholder; ?>"<?php echo $default; ?> />
|
||||
<?php
|
||||
}
|
||||
elseif ( 'textarea' == $type ) {
|
||||
?>
|
||||
<textarea class="facet-<?php echo $name; ?>" placeholder="<?php echo $placeholder; ?>"></textarea>
|
||||
<?php
|
||||
}
|
||||
elseif ( 'toggle' == $type ) {
|
||||
?>
|
||||
<label class="facetwp-switch">
|
||||
<input type="checkbox" class="facet-<?php echo $name; ?>" true-value="yes" false-value="no" />
|
||||
<span class="facetwp-slider"></span>
|
||||
</label>
|
||||
<?php
|
||||
}
|
||||
elseif ( 'select' == $type ) {
|
||||
?>
|
||||
<select class="facet-<?php echo $name; ?>">
|
||||
<?php foreach ( $field['choices'] as $k => $v ) : ?>
|
||||
<option value="<?php echo $k; ?>"><?php echo $v; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php
|
||||
}
|
||||
|
||||
$html = ob_get_clean();
|
||||
?>
|
||||
<div class="facetwp-row"<?php echo $show; ?>>
|
||||
<div><?php echo $label; ?></div>
|
||||
<div><?php echo $html; ?></div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return HTML for a setting field
|
||||
* @since 3.0.0
|
||||
*/
|
||||
function get_setting_html( $setting_name, $field_type = 'text', $atts = [] ) {
|
||||
ob_start();
|
||||
|
||||
if ( 'license_key' == $setting_name ) : ?>
|
||||
|
||||
<input type="text" class="facetwp-license" style="width:360px" value="<?php echo FWP()->helper->get_license_key(); ?>"<?php echo defined( 'FACETWP_LICENSE_KEY' ) ? ' disabled' : ''; ?> />
|
||||
<div @click="activate" class="btn-normal"><?php _e( 'Activate', 'fwp' ); ?></div>
|
||||
<div class="facetwp-activation-status field-notes"><?php echo $this->get_activation_status(); ?></div>
|
||||
|
||||
<?php elseif ( 'gmaps_api_key' == $setting_name ) : ?>
|
||||
|
||||
<input type="text" v-model="app.settings.gmaps_api_key" style="width:360px" />
|
||||
<a href="https://developers.google.com/maps/documentation/javascript/get-api-key" target="_blank"><?php _e( 'Get an API key', 'fwp' ); ?></a>
|
||||
|
||||
<?php elseif ( 'separators' == $setting_name ) : ?>
|
||||
|
||||
Thousands:
|
||||
<input type="text" v-model="app.settings.thousands_separator" style="width:30px" />
|
||||
Decimal:
|
||||
<input type="text" v-model="app.settings.decimal_separator" style="width:30px" />
|
||||
|
||||
<?php elseif ( 'export' == $setting_name ) : ?>
|
||||
|
||||
<select class="export-items" multiple="multiple">
|
||||
<?php foreach ( $this->get_export_choices() as $val => $label ) : ?>
|
||||
<option value="<?php echo $val; ?>"><?php echo $label; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<div class="btn-normal export-submit">
|
||||
<?php _e( 'Export', 'fwp' ); ?>
|
||||
</div>
|
||||
|
||||
<?php elseif ( 'import' == $setting_name ) : ?>
|
||||
|
||||
<div><textarea class="import-code" placeholder="<?php _e( 'Paste the import code here', 'fwp' ); ?>"></textarea></div>
|
||||
<div><input type="checkbox" class="import-overwrite" /> <?php _e( 'Overwrite existing items?', 'fwp' ); ?></div>
|
||||
<div style="margin-top:5px">
|
||||
<div class="btn-normal import-submit"><?php _e( 'Import', 'fwp' ); ?></div>
|
||||
</div>
|
||||
|
||||
<?php elseif ( 'dropdown' == $field_type ) : ?>
|
||||
|
||||
<select class="facetwp-setting" v-model="app.settings.<?php echo $setting_name; ?>">
|
||||
<?php foreach ( $atts['choices'] as $val => $label ) : ?>
|
||||
<option value="<?php echo $val; ?>"><?php echo $label; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
||||
<?php elseif ( 'toggle' == $field_type ) : ?>
|
||||
<?php
|
||||
|
||||
$true_value = $atts['true_value'] ?? 'yes';
|
||||
$false_value = $atts['false_value'] ?? 'no';
|
||||
|
||||
?>
|
||||
<label class="facetwp-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="app.settings.<?php echo $setting_name; ?>"
|
||||
true-value="<?php echo $true_value; ?>"
|
||||
false-value="<?php echo $false_value; ?>"
|
||||
/>
|
||||
<span class="facetwp-slider"></span>
|
||||
</label>
|
||||
|
||||
<?php endif;
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of all facets and templates
|
||||
* @since 3.0.0
|
||||
*/
|
||||
function get_export_choices() {
|
||||
$export = [];
|
||||
|
||||
$settings = FWP()->helper->settings;
|
||||
|
||||
foreach ( $settings['facets'] as $facet ) {
|
||||
$export['facet-' . $facet['name']] = 'Facet - ' . $facet['label'];
|
||||
}
|
||||
|
||||
foreach ( $settings['templates'] as $template ) {
|
||||
$export['template-' . $template['name']] = 'Template - '. $template['label'];
|
||||
}
|
||||
|
||||
return $export;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the activation status
|
||||
* @since 3.0.0
|
||||
*/
|
||||
function get_activation_status() {
|
||||
$message = __( 'Not yet activated', 'fwp' );
|
||||
$status = FWP()->helper->get_license_meta( 'status' );
|
||||
|
||||
if ( false !== $status ) {
|
||||
if ( 'success' == $status ) {
|
||||
$expiration = FWP()->helper->get_license_meta( 'expiration' );
|
||||
$expiration = date( 'M j, Y', strtotime( $expiration ) );
|
||||
$message = __( 'Valid until', 'fwp' ) . ' ' . $expiration;
|
||||
}
|
||||
else {
|
||||
$message = FWP()->helper->get_license_meta( 'message' );
|
||||
}
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load i18n admin strings
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function get_i18n_strings() {
|
||||
return [
|
||||
'Number of grid columns' => __( 'Number of grid columns', 'fwp' ),
|
||||
'Spacing between results' => __( 'Spacing between results', 'fwp' ),
|
||||
'No results text' => __( 'No results text', 'fwp' ),
|
||||
'Text style' => __( 'Text style', 'fwp' ),
|
||||
'Text color' => __( 'Text color', 'fwp' ),
|
||||
'Font size' => __( 'Font size', 'fwp' ),
|
||||
'Background color' => __( 'Background color', 'fwp' ),
|
||||
'Border' => __( 'Border', 'fwp' ),
|
||||
'Border style' => __( 'Border style', 'fwp' ),
|
||||
'None' => __( 'None', 'fwp' ),
|
||||
'Solid' => __( 'Solid', 'fwp' ),
|
||||
'Dashed' => __( 'Dashed', 'fwp' ),
|
||||
'Dotted' => __( 'Dotted', 'fwp' ),
|
||||
'Double' => __( 'Double', 'fwp' ),
|
||||
'Border color' => __( 'Border color', 'fwp' ),
|
||||
'Border width' => __( 'Border width', 'fwp' ),
|
||||
'Button text' => __( 'Button text', 'fwp' ),
|
||||
'Button text color' => __( 'Button text color', 'fwp' ),
|
||||
'Button padding' => __( 'Button padding', 'fwp' ),
|
||||
'Separator' => __( 'Separator', 'fwp' ),
|
||||
'Custom CSS' => __( 'Custom CSS', 'fwp' ),
|
||||
'Column widths' => __( 'Column widths', 'fwp' ),
|
||||
'Content' => __( 'Content', 'fwp' ),
|
||||
'Image size' => __( 'Image size', 'fwp' ),
|
||||
'Author field' => __( 'Author field', 'fwp' ),
|
||||
'Display name' => __( 'Display name', 'fwp' ),
|
||||
'User login' => __( 'User login', 'fwp' ),
|
||||
'User ID' => __( 'User ID', 'fwp' ),
|
||||
'Field type' => __( 'Field type', 'fwp' ),
|
||||
'Text' => __( 'Text', 'fwp' ),
|
||||
'Date' => __( 'Date', 'fwp' ),
|
||||
'Number' => __( 'Number', 'fwp' ),
|
||||
'Date format' => __( 'Date format', 'fwp' ),
|
||||
'Input format' => __( 'Input format', 'fwp' ),
|
||||
'Number format' => __( 'Number format', 'fwp' ),
|
||||
'Link' => __( 'Link', 'fwp' ),
|
||||
'Link type' => __( 'Link type', 'fwp' ),
|
||||
'Post URL' => __( 'Post URL', 'fwp' ),
|
||||
'Custom URL' => __( 'Custom URL', 'fwp' ),
|
||||
'Open in new tab?' => __( 'Open in new tab?', 'fwp' ),
|
||||
'Prefix' => __( 'Prefix', 'fwp' ),
|
||||
'Suffix' => __( 'Suffix', 'fwp' ),
|
||||
'Hide item?' => __( 'Hide item?', 'fwp' ),
|
||||
'Padding' => __( 'Padding', 'fwp' ),
|
||||
'CSS class' => __( 'CSS class', 'fwp' ),
|
||||
'Button Border' => __( 'Button border', 'fwp' ),
|
||||
'Term URL' => __( 'Term URL', 'fwp' ),
|
||||
'Fetch' => __( 'Fetch', 'fwp' ),
|
||||
'All post types' => __( 'All post types', 'fwp' ),
|
||||
'and show' => __( 'and show', 'fwp' ),
|
||||
'per page' => __( 'per page', 'fwp' ),
|
||||
'Sort by' => __( 'Sort by', 'fwp' ),
|
||||
'Posts' => __( 'Posts', 'fwp' ),
|
||||
'Post Title' => __( 'Post Title', 'fwp' ),
|
||||
'Post Name' => __( 'Post Name', 'fwp' ),
|
||||
'Post Type' => __( 'Post Type', 'fwp' ),
|
||||
'Post Date' => __( 'Post Date', 'fwp' ),
|
||||
'Post Modified' => __( 'Post Modified', 'fwp' ),
|
||||
'Comment Count' => __( 'Comment Count', 'fwp' ),
|
||||
'Menu Order' => __( 'Menu Order', 'fwp' ),
|
||||
'Custom Fields' => __( 'Custom Fields', 'fwp' ),
|
||||
'Narrow results by' => __( 'Narrow results by', 'fwp' ),
|
||||
'Hit Enter' => __( 'Hit Enter', 'fwp' ),
|
||||
'Add query sort' => __( 'Add query sort', 'fwp' ),
|
||||
'Add query filter' => __( 'Add query filter', 'fwp' ),
|
||||
'Clear' => __( 'Clear', 'fwp' ),
|
||||
'Enter term slugs' => __( 'Enter term slugs', 'fwp' ),
|
||||
'Enter values' => __( 'Enter values', 'fwp' ),
|
||||
'Layout' => __( 'Layout', 'fwp' ),
|
||||
'Content' => __( 'Content', 'fwp' ),
|
||||
'Style' => __( 'Style', 'fwp' ),
|
||||
'Row' => __( 'Row', 'fwp' ),
|
||||
'Column' => __( 'Column', 'fwp' ),
|
||||
'Start typing' => __( 'Start typing', 'fwp' ),
|
||||
'Label' => __( 'Label', 'fwp' ),
|
||||
'Unique name' => __( 'Unique name', 'fwp' ),
|
||||
'Facet type' => __( 'Facet type', 'fwp' ),
|
||||
'Copy shortcode' => __( 'Copy shortcode', 'fwp' ),
|
||||
'Data source' => __( 'Data source', 'fwp' ),
|
||||
'Switch to advanced mode' => __( 'Switch to advanced mode', 'fwp' ),
|
||||
'Switch to visual mode' => __( 'Switch to visual mode', 'fwp' ),
|
||||
'Display' => __( 'Display', 'fwp' ),
|
||||
'Query' => __( 'Query', 'fwp' ),
|
||||
'Help' => __( 'Help', 'fwp' ),
|
||||
'Display Code' => __( 'Display Code', 'fwp' ),
|
||||
'Query Arguments' => __( 'Query Arguments', 'fwp' ),
|
||||
'Saving' => __( 'Saving', 'fwp' ),
|
||||
'Indexing' => __( 'Indexing', 'fwp' ),
|
||||
'The index table is empty' => __( 'The index table is empty', 'fwp' ),
|
||||
'Indexing complete' => __( 'Indexing complete', 'fwp' ),
|
||||
'Looking' => __( 'Looking', 'fwp' ),
|
||||
'Purging' => __( 'Purging', 'fwp' ),
|
||||
'Copied!' => __( 'Copied!', 'fwp' ),
|
||||
'Press CTRL+C to copy' => __( 'Press CTRL+C to copy', 'fwp' ),
|
||||
'Activating' => __( 'Activating', 'fwp' ),
|
||||
'Re-index' => __( 'Re-index', 'fwp' ),
|
||||
'Stop indexer' => __( 'Stop indexer', 'fwp' ),
|
||||
'Loading' => __( 'Loading', 'fwp' ),
|
||||
'Importing' => __( 'Importing', 'fwp' ),
|
||||
'Convert to query args' => __( 'Convert to query args', 'fwp' )
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get available image sizes
|
||||
* @since 3.2.7
|
||||
*/
|
||||
function get_image_sizes() {
|
||||
global $_wp_additional_image_sizes;
|
||||
|
||||
$sizes = [];
|
||||
|
||||
$default_sizes = [ 'thumbnail', 'medium', 'medium_large', 'large', 'full' ];
|
||||
|
||||
foreach ( get_intermediate_image_sizes() as $size ) {
|
||||
if ( in_array( $size, $default_sizes ) ) {
|
||||
$sizes[ $size ]['width'] = (int) get_option( "{$size}_size_w" );
|
||||
$sizes[ $size ]['height'] = (int) get_option( "{$size}_size_h" );
|
||||
}
|
||||
elseif ( isset( $_wp_additional_image_sizes[ $size ] ) ) {
|
||||
$sizes[ $size ] = $_wp_additional_image_sizes[ $size ];
|
||||
}
|
||||
}
|
||||
|
||||
return $sizes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return an array of formatted image sizes
|
||||
* @since 3.2.7
|
||||
*/
|
||||
function get_image_size_labels() {
|
||||
$labels = [];
|
||||
$sizes = $this->get_image_sizes();
|
||||
|
||||
foreach ( $sizes as $size => $data ) {
|
||||
$height = ( 0 === $data['height'] ) ? 'w' : 'x' . $data['height'];
|
||||
$label = $size . ' (' . $data['width'] . $height . ')';
|
||||
$labels[ $size ] = $label;
|
||||
}
|
||||
|
||||
$labels['full'] = __( 'full', 'fwp' );
|
||||
|
||||
return $labels;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create SVG images (based on Font Awesome)
|
||||
* @license https://fontawesome.com/license/free CC BY 4.0
|
||||
* @since 3.6.5
|
||||
*/
|
||||
function get_svg() {
|
||||
$output = [];
|
||||
|
||||
$icons = [
|
||||
// [viewBox width, viewBox height, icon width, svg data]
|
||||
"align-center" => [448, 512, 14, "M432 160H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0 256H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM108.1 96h231.81A12.09 12.09 0 0 0 352 83.9V44.09A12.09 12.09 0 0 0 339.91 32H108.1A12.09 12.09 0 0 0 96 44.09V83.9A12.1 12.1 0 0 0 108.1 96zm231.81 256A12.09 12.09 0 0 0 352 339.9v-39.81A12.09 12.09 0 0 0 339.91 288H108.1A12.09 12.09 0 0 0 96 300.09v39.81a12.1 12.1 0 0 0 12.1 12.1z"],
|
||||
"align-left" => [448, 512, 14, "M12.83 352h262.34A12.82 12.82 0 0 0 288 339.17v-38.34A12.82 12.82 0 0 0 275.17 288H12.83A12.82 12.82 0 0 0 0 300.83v38.34A12.82 12.82 0 0 0 12.83 352zm0-256h262.34A12.82 12.82 0 0 0 288 83.17V44.83A12.82 12.82 0 0 0 275.17 32H12.83A12.82 12.82 0 0 0 0 44.83v38.34A12.82 12.82 0 0 0 12.83 96zM432 160H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0 256H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z"],
|
||||
"align-right" => [448, 512, 14, "M16 224h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16zm416 192H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm3.17-384H172.83A12.82 12.82 0 0 0 160 44.83v38.34A12.82 12.82 0 0 0 172.83 96h262.34A12.82 12.82 0 0 0 448 83.17V44.83A12.82 12.82 0 0 0 435.17 32zm0 256H172.83A12.82 12.82 0 0 0 160 300.83v38.34A12.82 12.82 0 0 0 172.83 352h262.34A12.82 12.82 0 0 0 448 339.17v-38.34A12.82 12.82 0 0 0 435.17 288z"],
|
||||
"arrow-circle-up" => [512, 512, 16, "M8 256C8 119 119 8 256 8s248 111 248 248-111 248-248 248S8 393 8 256zm143.6 28.9l72.4-75.5V392c0 13.3 10.7 24 24 24h16c13.3 0 24-10.7 24-24V209.4l72.4 75.5c9.3 9.7 24.8 9.9 34.3.4l10.9-11c9.4-9.4 9.4-24.6 0-33.9L273 107.7c-9.4-9.4-24.6-9.4-33.9 0L106.3 240.4c-9.4 9.4-9.4 24.6 0 33.9l10.9 11c9.6 9.5 25.1 9.3 34.4-.4z"],
|
||||
"bold" => [384, 512, 12, "M333.49 238a122 122 0 0 0 27-65.21C367.87 96.49 308 32 233.42 32H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h31.87v288H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h209.32c70.8 0 134.14-51.75 141-122.4 4.74-48.45-16.39-92.06-50.83-119.6zM145.66 112h87.76a48 48 0 0 1 0 96h-87.76zm87.76 288h-87.76V288h87.76a56 56 0 0 1 0 112z"],
|
||||
"caret-down" => [320, 512, 10, "M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"],
|
||||
"cog" => [512, 512, 16, "M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z"],
|
||||
"columns" => [512, 512, 16, "M464 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V80c0-26.51-21.49-48-48-48zM224 416H64V160h160v256zm224 0H288V160h160v256z"],
|
||||
"eye-slash" => [640, 512, 20, "M320 400c-75.85 0-137.25-58.71-142.9-133.11L72.2 185.82c-13.79 17.3-26.48 35.59-36.72 55.59a32.35 32.35 0 0 0 0 29.19C89.71 376.41 197.07 448 320 448c26.91 0 52.87-4 77.89-10.46L346 397.39a144.13 144.13 0 0 1-26 2.61zm313.82 58.1l-110.55-85.44a331.25 331.25 0 0 0 81.25-102.07 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64a308.15 308.15 0 0 0-147.32 37.7L45.46 3.37A16 16 0 0 0 23 6.18L3.37 31.45A16 16 0 0 0 6.18 53.9l588.36 454.73a16 16 0 0 0 22.46-2.81l19.64-25.27a16 16 0 0 0-2.82-22.45zm-183.72-142l-39.3-30.38A94.75 94.75 0 0 0 416 256a94.76 94.76 0 0 0-121.31-92.21A47.65 47.65 0 0 1 304 192a46.64 46.64 0 0 1-1.54 10l-73.61-56.89A142.31 142.31 0 0 1 320 112a143.92 143.92 0 0 1 144 144c0 21.63-5.29 41.79-13.9 60.11z"],
|
||||
"italic" => [320, 512, 10, "M320 48v32a16 16 0 0 1-16 16h-62.76l-80 320H208a16 16 0 0 1 16 16v32a16 16 0 0 1-16 16H16a16 16 0 0 1-16-16v-32a16 16 0 0 1 16-16h62.76l80-320H112a16 16 0 0 1-16-16V48a16 16 0 0 1 16-16h192a16 16 0 0 1 16 16z"],
|
||||
"lock" => [448, 512, 14, "M400 224h-24v-72C376 68.2 307.8 0 224 0S72 68.2 72 152v72H48c-26.5 0-48 21.5-48 48v192c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V272c0-26.5-21.5-48-48-48zm-104 0H152v-72c0-39.7 32.3-72 72-72s72 32.3 72 72v72z"],
|
||||
"lock-open" => [576, 512, 18, "M423.5 0C339.5.3 272 69.5 272 153.5V224H48c-26.5 0-48 21.5-48 48v192c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V272c0-26.5-21.5-48-48-48h-48v-71.1c0-39.6 31.7-72.5 71.3-72.9 40-.4 72.7 32.1 72.7 72v80c0 13.3 10.7 24 24 24h32c13.3 0 24-10.7 24-24v-80C576 68 507.5-.3 423.5 0z"],
|
||||
"minus-circle" => [512, 512, 16, "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zM124 296c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h264c6.6 0 12 5.4 12 12v56c0 6.6-5.4 12-12 12H124z"],
|
||||
"plus" => [448, 512, 14, "M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z"],
|
||||
"plus-circle" => [512, 512, 16, "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm144 276c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92h-92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"],
|
||||
"times" => [352, 512, 11, "M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"]
|
||||
];
|
||||
|
||||
foreach ( $icons as $name => $attr ) {
|
||||
$svg = '<svg aria-hidden="true" focusable="false" class="svg-inline--fa fa-{name} fa-w-{faw}" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {w} {h}"><path fill="currentColor" d="{d}"></path></svg>';
|
||||
$search = [ '{name}', '{w}', '{h}', '{faw}', '{d}' ];
|
||||
$replace = [ $name, $attr[0], $attr[1], $attr[2], $attr[3] ];
|
||||
$output[ $name ] = str_replace( $search, $replace, $svg );
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
183
wp/wp-content/plugins/facetwp/includes/class-updater.php
Normal file
183
wp/wp-content/plugins/facetwp/includes/class-updater.php
Normal file
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Updater
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
add_filter( 'plugins_api', [ $this, 'plugins_api' ], 10, 3 );
|
||||
add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'check_update' ] );
|
||||
add_action( 'in_plugin_update_message-' . FACETWP_BASENAME, [ $this, 'in_plugin_update_message' ], 10, 2 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get info for FacetWP and its add-ons
|
||||
*/
|
||||
function get_plugins_to_check() {
|
||||
$output = [];
|
||||
$plugins = get_plugins();
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$active_plugins = get_site_option( 'active_sitewide_plugins', [] );
|
||||
$active_plugins = array_flip( $active_plugins ); // [plugin] => path
|
||||
}
|
||||
else {
|
||||
$active_plugins = get_option( 'active_plugins', [] );
|
||||
}
|
||||
|
||||
foreach ( $active_plugins as $plugin_path ) {
|
||||
if ( is_multisite() ) {
|
||||
$plugin_path = str_replace( WP_PLUGIN_DIR, '', $plugin_path );
|
||||
}
|
||||
|
||||
if ( isset( $plugins[ $plugin_path ] ) ) {
|
||||
$info = $plugins[ $plugin_path ];
|
||||
$slug = trim( dirname( $plugin_path ), '/' );
|
||||
|
||||
// only intercept FacetWP and its add-ons
|
||||
$is_valid = in_array( $slug, [ 'facetwp', 'user-post-type' ] );
|
||||
$is_valid = ( 0 === strpos( $slug, 'facetwp-' ) ) ? true : $is_valid;
|
||||
|
||||
if ( $is_valid ) {
|
||||
$output[ $slug ] = [
|
||||
'name' => $info['Name'],
|
||||
'version' => $info['Version'],
|
||||
'description' => $info['Description'],
|
||||
'plugin_path' => $plugin_path,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle the "View Details" popup
|
||||
*
|
||||
* $args->slug = "facetwp-flyout"
|
||||
* plugin_path = "facetwp-flyout/facetwp-flyout.php"
|
||||
*/
|
||||
function plugins_api( $result, $action, $args ) {
|
||||
if ( 'plugin_information' == $action ) {
|
||||
$slug = $args->slug;
|
||||
$to_check = $this->get_plugins_to_check();
|
||||
|
||||
$response = get_option( 'facetwp_updater_response', '' );
|
||||
$response = json_decode( $response, true );
|
||||
|
||||
if ( isset( $to_check[ $slug ] ) && isset( $response[ $slug ] ) ) {
|
||||
$local_data = $to_check[ $slug ];
|
||||
$remote_data = $response[ $slug ];
|
||||
|
||||
return (object) [
|
||||
'name' => $local_data['name'],
|
||||
'slug' => $local_data['plugin_path'],
|
||||
'version' => $remote_data['version'],
|
||||
'last_updated' => $remote_data['last_updated'],
|
||||
'download_link' => $remote_data['package'],
|
||||
'sections' => [
|
||||
'description' => $local_data['description'],
|
||||
'changelog' => $remote_data['sections']['changelog']
|
||||
],
|
||||
'homepage' => 'https://facetwp.com/',
|
||||
'rating' => 100,
|
||||
'num_ratings' => 1
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grab (and cache) plugin update data
|
||||
*/
|
||||
function check_update( $transient ) {
|
||||
if ( empty( $transient->checked ) ) {
|
||||
return $transient;
|
||||
}
|
||||
|
||||
$now = strtotime( 'now' );
|
||||
$response = get_option( 'facetwp_updater_response', '' );
|
||||
$ts = (int) get_option( 'facetwp_updater_last_checked' );
|
||||
$plugins = $this->get_plugins_to_check();
|
||||
|
||||
if ( empty( $response ) || $ts + 14400 < $now ) {
|
||||
|
||||
$request = wp_remote_post( 'https://api.facetwp.com', [
|
||||
'body' => [
|
||||
'action' => 'check_plugins',
|
||||
'slugs' => array_keys( $plugins ),
|
||||
'license' => FWP()->helper->get_license_key(),
|
||||
'host' => FWP()->helper->get_http_host(),
|
||||
'wp_v' => get_bloginfo( 'version' ),
|
||||
'fwp_v' => FACETWP_VERSION,
|
||||
'php_v' => phpversion(),
|
||||
]
|
||||
] );
|
||||
|
||||
if ( ! is_wp_error( $request ) || 200 == wp_remote_retrieve_response_code( $request ) ) {
|
||||
$body = json_decode( $request['body'], true );
|
||||
$activation = json_encode( $body['activation'] );
|
||||
$response = json_encode( $body['slugs'] );
|
||||
}
|
||||
|
||||
update_option( 'facetwp_activation', $activation );
|
||||
update_option( 'facetwp_updater_response', $response, 'no' );
|
||||
update_option( 'facetwp_updater_last_checked', $now, 'no' );
|
||||
}
|
||||
|
||||
if ( ! empty( $response ) ) {
|
||||
$response = json_decode( $response, true );
|
||||
|
||||
foreach ( $response as $slug => $info ) {
|
||||
if ( isset( $plugins[ $slug ] ) ) {
|
||||
$plugin_path = $plugins[ $slug ]['plugin_path'];
|
||||
$response_obj = (object) [
|
||||
'slug' => $slug,
|
||||
'plugin' => $plugin_path,
|
||||
'new_version' => $info['version'],
|
||||
'package' => $info['package'],
|
||||
'requires' => $info['requires'],
|
||||
'tested' => $info['tested']
|
||||
];
|
||||
|
||||
if ( version_compare( $plugins[ $slug ]['version'], $info['version'], '<' ) ) {
|
||||
$transient->response[ $plugin_path ] = $response_obj;
|
||||
}
|
||||
else {
|
||||
$transient->no_update[ $plugin_path ] = $response_obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $transient;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display a license reminder on the plugin list screen
|
||||
*/
|
||||
function in_plugin_update_message( $plugin_data, $response ) {
|
||||
if ( ! FWP()->helper->is_license_active() ) {
|
||||
$price_id = (int) FWP()->helper->get_license_meta( 'price_id' );
|
||||
$license = FWP()->helper->get_license_key();
|
||||
|
||||
if ( 0 < $price_id ) {
|
||||
echo '<br />' . sprintf(
|
||||
__( 'Please <a href="%s" target="_blank">renew your license</a> for automatic updates.', 'fwp' ),
|
||||
esc_url( "https://facetwp.com/checkout/?edd_action=add_to_cart&download_id=24&edd_options[price_id]=$price_id&discount=$license" )
|
||||
);
|
||||
}
|
||||
else {
|
||||
echo '<br />' . __( 'Please activate your FacetWP license for automatic updates.', 'fwp' );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new FacetWP_Updater();
|
||||
106
wp/wp-content/plugins/facetwp/includes/class-upgrade.php
Normal file
106
wp/wp-content/plugins/facetwp/includes/class-upgrade.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Upgrade
|
||||
{
|
||||
function __construct() {
|
||||
$this->version = FACETWP_VERSION;
|
||||
$this->last_version = get_option( 'facetwp_version' );
|
||||
|
||||
if ( version_compare( $this->last_version, $this->version, '<' ) ) {
|
||||
if ( version_compare( $this->last_version, '0.1.0', '<' ) ) {
|
||||
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
||||
$this->clean_install();
|
||||
}
|
||||
else {
|
||||
$this->run_upgrade();
|
||||
}
|
||||
|
||||
update_option( 'facetwp_version', $this->version );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function clean_install() {
|
||||
global $wpdb;
|
||||
|
||||
$int = apply_filters( 'facetwp_use_bigint', false ) ? 'BIGINT' : 'INT';
|
||||
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
|
||||
$sql = "
|
||||
CREATE TABLE IF NOT EXISTS {$wpdb->prefix}facetwp_index (
|
||||
id BIGINT unsigned not null auto_increment,
|
||||
post_id $int unsigned,
|
||||
facet_name VARCHAR(50),
|
||||
facet_value VARCHAR(50),
|
||||
facet_display_value VARCHAR(200),
|
||||
term_id $int unsigned default '0',
|
||||
parent_id $int unsigned default '0',
|
||||
depth INT unsigned default '0',
|
||||
variation_id $int unsigned default '0',
|
||||
PRIMARY KEY (id),
|
||||
INDEX post_id_idx (post_id),
|
||||
INDEX facet_name_idx (facet_name),
|
||||
INDEX facet_name_value_idx (facet_name, facet_value)
|
||||
) $charset_collate";
|
||||
dbDelta( $sql );
|
||||
|
||||
// Add default settings
|
||||
$settings = file_get_contents( FACETWP_DIR . '/assets/js/src/sample.json' );
|
||||
add_option( 'facetwp_settings', $settings );
|
||||
}
|
||||
|
||||
|
||||
private function run_upgrade() {
|
||||
global $wpdb;
|
||||
|
||||
$table = sanitize_key( $wpdb->prefix . 'facetwp_index' );
|
||||
|
||||
if ( version_compare( $this->last_version, '3.1.0', '<' ) ) {
|
||||
$wpdb->query( "ALTER TABLE $table MODIFY facet_name VARCHAR(50)" );
|
||||
$wpdb->query( "ALTER TABLE $table MODIFY facet_value VARCHAR(50)" );
|
||||
$wpdb->query( "ALTER TABLE $table MODIFY facet_display_value VARCHAR(200)" );
|
||||
$wpdb->query( "CREATE INDEX facet_name_value_idx ON $table (facet_name, facet_value)" );
|
||||
}
|
||||
|
||||
if ( version_compare( $this->last_version, '3.3.2', '<' ) ) {
|
||||
$wpdb->query( "CREATE INDEX post_id_idx ON $table (post_id)" );
|
||||
$wpdb->query( "DROP INDEX facet_source_idx ON $table" );
|
||||
$wpdb->query( "ALTER TABLE $table DROP COLUMN facet_source" );
|
||||
}
|
||||
|
||||
if ( version_compare( $this->last_version, '3.3.3', '<' ) ) {
|
||||
if ( function_exists( 'SWP' ) ) {
|
||||
$engines = array_keys( SWP()->settings['engines'] );
|
||||
$settings = get_option( 'facetwp_settings' );
|
||||
$settings = json_decode( $settings, true );
|
||||
|
||||
foreach ( $settings['facets'] as $key => $facet ) {
|
||||
if ( 'search' == $facet['type'] ) {
|
||||
if ( in_array( $facet['search_engine'], $engines ) ) {
|
||||
$settings['facets'][ $key ]['search_engine'] = 'swp_' . $facet['search_engine'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_option( 'facetwp_settings', json_encode( $settings ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( version_compare( $this->last_version, '3.5.3', '<' ) ) {
|
||||
update_option( 'facetwp_updater_response', '', 'no' );
|
||||
update_option( 'facetwp_updater_last_checked', '', 'no' );
|
||||
}
|
||||
|
||||
if ( version_compare( $this->last_version, '3.5.4', '<' ) ) {
|
||||
$settings = get_option( 'facetwp_settings' );
|
||||
$settings = json_decode( $settings, true );
|
||||
|
||||
if ( ! isset( $settings['settings']['prefix'] ) ) {
|
||||
$settings['settings']['prefix'] = 'fwp_';
|
||||
|
||||
update_option( 'facetwp_settings', json_encode( $settings ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
163
wp/wp-content/plugins/facetwp/includes/facets/autocomplete.php
Normal file
163
wp/wp-content/plugins/facetwp/includes/facets/autocomplete.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Autocomplete extends FacetWP_Facet
|
||||
{
|
||||
|
||||
public $is_buffering = false;
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Autocomplete', 'fwp' );
|
||||
$this->fields = [ 'placeholder' ];
|
||||
|
||||
// ajax
|
||||
add_action( 'facetwp_autocomplete_load', [ $this, 'ajax_load' ] );
|
||||
|
||||
// css-based template
|
||||
add_action( 'facetwp_init', [ $this, 'maybe_buffer_output' ] );
|
||||
add_action( 'facetwp_found_main_query', [ $this, 'template_handler' ] );
|
||||
|
||||
// result limit
|
||||
$this->limit = (int) apply_filters( 'facetwp_facet_autocomplete_limit', 10 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For page templates with a custom WP_Query, we need to prevent the
|
||||
* page header from being output with the autocomplete JSON
|
||||
*/
|
||||
function maybe_buffer_output() {
|
||||
if ( isset( $_POST['action'] ) && 'facetwp_autocomplete_load' == $_POST['action'] ) {
|
||||
$this->is_buffering = true;
|
||||
ob_start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For CSS-based templates, the "facetwp_autocomplete_load" action isn't fired
|
||||
* so we need to manually check the action
|
||||
*/
|
||||
function template_handler() {
|
||||
if ( isset( $_POST['action'] ) && 'facetwp_autocomplete_load' == $_POST['action'] ) {
|
||||
if ( $this->is_buffering ) {
|
||||
while ( ob_get_level() ) {
|
||||
ob_end_clean();
|
||||
}
|
||||
}
|
||||
$this->ajax_load();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$facet = $params['facet'];
|
||||
$value = (array) $params['selected_values'];
|
||||
$value = empty( $value ) ? '' : stripslashes( $value[0] );
|
||||
$placeholder = empty( $facet['placeholder'] ) ? __( 'Start typing', 'fwp-front' ) : $facet['placeholder'];
|
||||
$placeholder = facetwp_i18n( $placeholder );
|
||||
$output .= '<input type="text" class="facetwp-autocomplete" value="' . esc_attr( $value ) . '" placeholder="' . esc_attr( $placeholder ) . '" autocomplete="off" />';
|
||||
$output .= '<input type="button" class="facetwp-autocomplete-update" value="' . __( 'Go', 'fwp-front' ) . '" />';
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$selected_values = $params['selected_values'];
|
||||
$selected_values = is_array( $selected_values ) ? $selected_values[0] : $selected_values;
|
||||
$selected_values = stripslashes( $selected_values );
|
||||
|
||||
if ( empty( $selected_values ) ) {
|
||||
return 'continue';
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT DISTINCT post_id FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE facet_name = %s AND facet_display_value LIKE %s";
|
||||
|
||||
$sql = $wpdb->prepare( $sql, $facet['name'], '%' . $selected_values . '%' );
|
||||
return facetwp_sql( $sql, $facet );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output any front-end scripts
|
||||
*/
|
||||
function front_scripts() {
|
||||
FWP()->display->json['no_results'] = __( 'No results', 'fwp-front' );
|
||||
FWP()->display->assets['fComplete.js'] = FACETWP_URL . '/assets/vendor/fComplete/fComplete.js';
|
||||
FWP()->display->assets['fComplete.css'] = FACETWP_URL . '/assets/vendor/fComplete/fComplete.css';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load facet values via AJAX
|
||||
*/
|
||||
function ajax_load() {
|
||||
global $wpdb;
|
||||
|
||||
// optimizations
|
||||
$_POST['data']['soft_refresh'] = 1;
|
||||
$_POST['data']['extras'] = [];
|
||||
|
||||
$query = stripslashes( $_POST['query'] );
|
||||
$query = FWP()->helper->sanitize( $wpdb->esc_like( $query ) );
|
||||
$facet_name = FWP()->helper->sanitize( $_POST['facet_name'] );
|
||||
$output = [];
|
||||
|
||||
// simulate a refresh
|
||||
FWP()->facet->render(
|
||||
FWP()->request->process_post_data()
|
||||
);
|
||||
|
||||
// then grab the matching post IDs
|
||||
$where_clause = $this->get_where_clause( [ 'name' => $facet_name ] );
|
||||
|
||||
if ( ! empty( $query ) && ! empty( $facet_name ) ) {
|
||||
$sql = "
|
||||
SELECT DISTINCT facet_display_value
|
||||
FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE
|
||||
facet_name = '$facet_name' AND
|
||||
facet_display_value LIKE '%$query%'
|
||||
$where_clause
|
||||
ORDER BY facet_display_value ASC
|
||||
LIMIT $this->limit";
|
||||
|
||||
$results = $wpdb->get_results( $sql );
|
||||
|
||||
foreach ( $results as $result ) {
|
||||
$output[] = [
|
||||
'value' => $result->facet_display_value,
|
||||
'label' => $result->facet_display_value,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
wp_send_json( $output );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Front-end) Attach settings to the AJAX response
|
||||
*/
|
||||
function settings_js( $params ) {
|
||||
return [
|
||||
'loadingText' => __( 'Loading', 'fwp-front' ) . '...',
|
||||
'minCharsText' => __( 'Enter {n} or more characters', 'fwp-front' ),
|
||||
'noResultsText' => __( 'No results', 'fwp-front' ),
|
||||
'maxResults' => $this->limit
|
||||
];
|
||||
}
|
||||
}
|
||||
106
wp/wp-content/plugins/facetwp/includes/facets/base.php
Normal file
106
wp/wp-content/plugins/facetwp/includes/facets/base.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet
|
||||
{
|
||||
|
||||
/**
|
||||
* Grab the orderby, as needed by several facet types
|
||||
* @since 3.0.4
|
||||
*/
|
||||
function get_orderby( $facet ) {
|
||||
$key = $facet['orderby'];
|
||||
|
||||
// Count (default)
|
||||
$orderby = 'counter DESC, f.facet_display_value ASC';
|
||||
|
||||
// Display value
|
||||
if ( 'display_value' == $key ) {
|
||||
$orderby = 'f.facet_display_value ASC';
|
||||
}
|
||||
// Raw value
|
||||
elseif ( 'raw_value' == $key ) {
|
||||
$orderby = 'f.facet_value ASC';
|
||||
}
|
||||
// Term order
|
||||
elseif ( 'term_order' == $key && 'tax' == substr( $facet['source'], 0, 3 ) ) {
|
||||
$term_ids = get_terms( [
|
||||
'taxonomy' => str_replace( 'tax/', '', $facet['source'] ),
|
||||
'term_order' => true, // Custom flag
|
||||
'fields' => 'ids',
|
||||
] );
|
||||
|
||||
if ( ! empty( $term_ids ) && ! is_wp_error( $term_ids ) ) {
|
||||
$term_ids = implode( ',', $term_ids );
|
||||
$orderby = "FIELD(f.term_id, $term_ids)";
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by depth
|
||||
if ( FWP()->helper->facet_is( $facet, 'hierarchical', 'yes' ) ) {
|
||||
$orderby = "f.depth, $orderby";
|
||||
}
|
||||
|
||||
return $orderby;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grab the limit, and support -1
|
||||
* @since 3.5.4
|
||||
*/
|
||||
function get_limit( $facet, $default = 10 ) {
|
||||
$count = $facet['count'];
|
||||
|
||||
if ( '-1' == $count ) {
|
||||
return 1000;
|
||||
}
|
||||
elseif ( ctype_digit( $count ) ) {
|
||||
return $count;
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adjust the $where_clause for facets in "OR" mode
|
||||
*
|
||||
* FWP()->or_values contains EVERY facet and their matching post IDs
|
||||
* FWP()->unfiltered_post_ids contains original post IDs
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function get_where_clause( $facet ) {
|
||||
|
||||
// Ignore the current facet's selections
|
||||
if ( isset( FWP()->or_values ) && ( 1 < count( FWP()->or_values ) || ! isset( FWP()->or_values[ $facet['name'] ] ) ) ) {
|
||||
$post_ids = [];
|
||||
$or_values = FWP()->or_values; // Preserve original
|
||||
unset( $or_values[ $facet['name'] ] );
|
||||
|
||||
$counter = 0;
|
||||
foreach ( $or_values as $name => $vals ) {
|
||||
$post_ids = ( 0 == $counter ) ? $vals : array_intersect( $post_ids, $vals );
|
||||
$counter++;
|
||||
}
|
||||
|
||||
$post_ids = array_intersect( $post_ids, FWP()->unfiltered_post_ids );
|
||||
}
|
||||
else {
|
||||
$post_ids = FWP()->unfiltered_post_ids;
|
||||
}
|
||||
|
||||
$post_ids = empty( $post_ids ) ? [ 0 ] : $post_ids;
|
||||
return ' AND post_id IN (' . implode( ',', $post_ids ) . ')';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render some commonly used admin settings
|
||||
* @since 3.5.6
|
||||
* @deprecated 3.9
|
||||
*/
|
||||
function render_setting( $name ) {
|
||||
echo FWP()->settings->get_facet_field_html( $name );
|
||||
}
|
||||
}
|
||||
258
wp/wp-content/plugins/facetwp/includes/facets/checkboxes.php
Normal file
258
wp/wp-content/plugins/facetwp/includes/facets/checkboxes.php
Normal file
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Checkboxes extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Checkboxes', 'fwp' );
|
||||
$this->fields = [ 'parent_term', 'modifiers', 'hierarchical', 'show_expanded',
|
||||
'ghosts', 'operator', 'orderby', 'count', 'soft_limit' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the available choices
|
||||
*/
|
||||
function load_values( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$from_clause = $wpdb->prefix . 'facetwp_index f';
|
||||
$where_clause = $params['where_clause'];
|
||||
|
||||
// Orderby
|
||||
$orderby = $this->get_orderby( $facet );
|
||||
|
||||
// Limit
|
||||
$limit = $this->get_limit( $facet );
|
||||
|
||||
// Use "OR" mode when necessary
|
||||
$is_single = FWP()->helper->facet_is( $facet, 'multiple', 'no' );
|
||||
$using_or = FWP()->helper->facet_is( $facet, 'operator', 'or' );
|
||||
|
||||
// Facet in "OR" mode
|
||||
if ( $is_single || $using_or ) {
|
||||
$where_clause = $this->get_where_clause( $facet );
|
||||
}
|
||||
|
||||
$orderby = apply_filters( 'facetwp_facet_orderby', $orderby, $facet );
|
||||
$from_clause = apply_filters( 'facetwp_facet_from', $from_clause, $facet );
|
||||
$where_clause = apply_filters( 'facetwp_facet_where', $where_clause, $facet );
|
||||
|
||||
$sql = "
|
||||
SELECT f.facet_value, f.facet_display_value, f.term_id, f.parent_id, f.depth, COUNT(DISTINCT f.post_id) AS counter
|
||||
FROM $from_clause
|
||||
WHERE f.facet_name = '{$facet['name']}' $where_clause
|
||||
GROUP BY f.facet_value
|
||||
ORDER BY $orderby
|
||||
LIMIT $limit";
|
||||
|
||||
$output = $wpdb->get_results( $sql, ARRAY_A );
|
||||
|
||||
// Show "ghost" facet choices
|
||||
// For performance gains, only run if facets are in use
|
||||
$show_ghosts = FWP()->helper->facet_is( $facet, 'ghosts', 'yes' );
|
||||
|
||||
if ( $show_ghosts && FWP()->is_filtered ) {
|
||||
$raw_post_ids = implode( ',', FWP()->unfiltered_post_ids );
|
||||
|
||||
$sql = "
|
||||
SELECT f.facet_value, f.facet_display_value, f.term_id, f.parent_id, f.depth, 0 AS counter
|
||||
FROM $from_clause
|
||||
WHERE f.facet_name = '{$facet['name']}' AND post_id IN ($raw_post_ids)
|
||||
GROUP BY f.facet_value
|
||||
ORDER BY $orderby
|
||||
LIMIT $limit";
|
||||
|
||||
$ghost_output = $wpdb->get_results( $sql, ARRAY_A );
|
||||
$tmp = [];
|
||||
|
||||
$preserve_ghosts = FWP()->helper->facet_is( $facet, 'preserve_ghosts', 'yes' );
|
||||
$orderby_count = FWP()->helper->facet_is( $facet, 'orderby', 'count' );
|
||||
|
||||
// Keep the facet placement intact
|
||||
if ( $preserve_ghosts && ! $orderby_count ) {
|
||||
foreach ( $ghost_output as $row ) {
|
||||
$tmp[ $row['facet_value'] . ' ' ] = $row;
|
||||
}
|
||||
|
||||
foreach ( $output as $row ) {
|
||||
$tmp[ $row['facet_value'] . ' ' ] = $row;
|
||||
}
|
||||
|
||||
$output = $tmp;
|
||||
}
|
||||
else {
|
||||
// Make the array key equal to the facet_value (for easy lookup)
|
||||
foreach ( $output as $row ) {
|
||||
$tmp[ $row['facet_value'] . ' ' ] = $row; // Force a string array key
|
||||
}
|
||||
|
||||
$output = $tmp;
|
||||
|
||||
foreach ( $ghost_output as $row ) {
|
||||
$facet_value = $row['facet_value'];
|
||||
if ( ! isset( $output[ "$facet_value " ] ) ) {
|
||||
$output[ "$facet_value " ] = $row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$output = array_splice( $output, 0, $limit );
|
||||
$output = array_values( $output );
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
$facet = $params['facet'];
|
||||
|
||||
if ( FWP()->helper->facet_is( $facet, 'hierarchical', 'yes' ) ) {
|
||||
return $this->render_hierarchy( $params );
|
||||
}
|
||||
|
||||
$output = '';
|
||||
$values = (array) $params['values'];
|
||||
$soft_limit = empty( $facet['soft_limit'] ) ? 0 : (int) $facet['soft_limit'];
|
||||
|
||||
$key = 0;
|
||||
foreach ( $values as $key => $row ) {
|
||||
if ( 0 < $soft_limit && $key == $soft_limit ) {
|
||||
$output .= '<div class="facetwp-overflow facetwp-hidden">';
|
||||
}
|
||||
$output .= $this->render_choice( $row, $params );
|
||||
}
|
||||
|
||||
if ( 0 < $soft_limit && $soft_limit <= $key ) {
|
||||
$output .= '</div>';
|
||||
$output .= '<a class="facetwp-toggle">' . __( 'See {num} more', 'fwp-front' ) . '</a>';
|
||||
$output .= '<a class="facetwp-toggle facetwp-hidden">' . __( 'See less', 'fwp-front' ) . '</a>';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML (hierarchical taxonomies)
|
||||
*/
|
||||
function render_hierarchy( $params ) {
|
||||
|
||||
$output = '';
|
||||
$facet = $params['facet'];
|
||||
$values = FWP()->helper->sort_taxonomy_values( $params['values'], $facet['orderby'] );
|
||||
|
||||
$init_depth = -1;
|
||||
$last_depth = -1;
|
||||
|
||||
foreach ( $values as $row ) {
|
||||
$depth = (int) $row['depth'];
|
||||
|
||||
if ( -1 == $last_depth ) {
|
||||
$init_depth = $depth;
|
||||
}
|
||||
elseif ( $depth > $last_depth ) {
|
||||
$output .= '<div class="facetwp-depth">';
|
||||
}
|
||||
elseif ( $depth < $last_depth ) {
|
||||
for ( $i = $last_depth; $i > $depth; $i-- ) {
|
||||
$output .= '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
$output .= $this->render_choice( $row, $params );
|
||||
|
||||
$last_depth = $depth;
|
||||
}
|
||||
|
||||
for ( $i = $last_depth; $i > $init_depth; $i-- ) {
|
||||
$output .= '</div>';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render a single facet choice
|
||||
*/
|
||||
function render_choice( $row, $params ) {
|
||||
$label = esc_html( $row['facet_display_value'] );
|
||||
|
||||
$output = '';
|
||||
$selected_values = (array) $params['selected_values'];
|
||||
$selected = in_array( $row['facet_value'], $selected_values ) ? ' checked' : '';
|
||||
$selected .= ( '' != $row['counter'] && 0 == $row['counter'] && '' == $selected ) ? ' disabled' : '';
|
||||
$output .= '<div class="facetwp-checkbox' . $selected . '" data-value="' . esc_attr( $row['facet_value'] ) . '">';
|
||||
$output .= '<span class="facetwp-display-value">';
|
||||
$output .= apply_filters( 'facetwp_facet_display_value', $label, [
|
||||
'selected' => ( '' !== $selected ),
|
||||
'facet' => $params['facet'],
|
||||
'row' => $row
|
||||
]);
|
||||
$output .= '</span>';
|
||||
$output .= '<span class="facetwp-counter">(' . $row['counter'] . ')</span>';
|
||||
$output .= '</div>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$output = [];
|
||||
$facet = $params['facet'];
|
||||
$selected_values = $params['selected_values'];
|
||||
|
||||
$sql = $wpdb->prepare( "SELECT DISTINCT post_id
|
||||
FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE facet_name = %s",
|
||||
$facet['name']
|
||||
);
|
||||
|
||||
// Match ALL values
|
||||
if ( 'and' == $facet['operator'] ) {
|
||||
foreach ( $selected_values as $key => $value ) {
|
||||
$results = facetwp_sql( $sql . " AND facet_value IN ('$value')", $facet );
|
||||
$output = ( $key > 0 ) ? array_intersect( $output, $results ) : $results;
|
||||
|
||||
if ( empty( $output ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Match ANY value
|
||||
else {
|
||||
$selected_values = implode( "','", $selected_values );
|
||||
$output = facetwp_sql( $sql . " AND facet_value IN ('$selected_values')", $facet );
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output any front-end scripts
|
||||
*/
|
||||
function front_scripts() {
|
||||
FWP()->display->json['expand'] = '[+]';
|
||||
FWP()->display->json['collapse'] = '[-]';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Front-end) Attach settings to the AJAX response
|
||||
*/
|
||||
function settings_js( $params ) {
|
||||
$expand = empty( $params['facet']['show_expanded'] ) ? 'no' : $params['facet']['show_expanded'];
|
||||
return [ 'show_expanded' => $expand ];
|
||||
}
|
||||
}
|
||||
292
wp/wp-content/plugins/facetwp/includes/facets/date_range.php
Normal file
292
wp/wp-content/plugins/facetwp/includes/facets/date_range.php
Normal file
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Date_Range extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Date Range', 'fwp' );
|
||||
$this->fields = [ 'source_other', 'compare_type', 'date_fields', 'date_format' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$value = $params['selected_values'];
|
||||
$value = empty( $value ) ? [ '', '' ] : $value;
|
||||
$fields = empty( $params['facet']['fields'] ) ? 'both' : $params['facet']['fields'];
|
||||
|
||||
$text_date = facetwp_i18n( __( 'Date', 'fwp-front' ) );
|
||||
$text_start_date = facetwp_i18n( __( 'Start Date', 'fwp-front' ) );
|
||||
$text_end_date = facetwp_i18n( __( 'End Date', 'fwp-front' ) );
|
||||
|
||||
if ( 'exact' == $fields ) {
|
||||
$output .= '<input type="text" class="facetwp-date facetwp-date-min" value="' . esc_attr( $value[0] ) . '" placeholder="' . esc_attr( $text_date ) . '" />';
|
||||
}
|
||||
if ( 'both' == $fields || 'start_date' == $fields ) {
|
||||
$output .= '<input type="text" class="facetwp-date facetwp-date-min" value="' . esc_attr( $value[0] ) . '" placeholder="' . esc_attr( $text_start_date ) . '" />';
|
||||
}
|
||||
if ( 'both' == $fields || 'end_date' == $fields ) {
|
||||
$output .= '<input type="text" class="facetwp-date facetwp-date-max" value="' . esc_attr( $value[1] ) . '" placeholder="' . esc_attr( $text_end_date ) . '" />';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$values = $params['selected_values'];
|
||||
$where = '';
|
||||
|
||||
$min = empty( $values[0] ) ? false : $values[0];
|
||||
$max = empty( $values[1] ) ? false : $values[1];
|
||||
|
||||
$fields = $facet['fields'] ?? 'both';
|
||||
$compare_type = empty( $facet['compare_type'] ) ? 'basic' : $facet['compare_type'];
|
||||
$is_dual = ! empty( $facet['source_other'] );
|
||||
|
||||
if ( $is_dual && 'basic' != $compare_type ) {
|
||||
if ( 'exact' == $fields ) {
|
||||
$max = $min;
|
||||
}
|
||||
|
||||
$min = ( false !== $min ) ? $min : '0000-00-00';
|
||||
$max = ( false !== $max ) ? $max : '3000-12-31';
|
||||
|
||||
/**
|
||||
* Enclose compare
|
||||
* The post's range must surround the user-defined range
|
||||
*/
|
||||
if ( 'enclose' == $compare_type ) {
|
||||
$where .= " AND LEFT(facet_value, 10) <= '$min'";
|
||||
$where .= " AND LEFT(facet_display_value, 10) >= '$max'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersect compare
|
||||
* @link http://stackoverflow.com/a/325964
|
||||
*/
|
||||
if ( 'intersect' == $compare_type ) {
|
||||
$where .= " AND LEFT(facet_value, 10) <= '$max'";
|
||||
$where .= " AND LEFT(facet_display_value, 10) >= '$min'";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic compare
|
||||
* The user-defined range must surround the post's range
|
||||
*/
|
||||
else {
|
||||
if ( 'exact' == $fields ) {
|
||||
$max = $min;
|
||||
}
|
||||
if ( false !== $min ) {
|
||||
$where .= " AND LEFT(facet_value, 10) >= '$min'";
|
||||
}
|
||||
if ( false !== $max ) {
|
||||
$where .= " AND LEFT(facet_display_value, 10) <= '$max'";
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT DISTINCT post_id FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE facet_name = '{$facet['name']}' $where";
|
||||
return facetwp_sql( $sql, $facet );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output any front-end scripts
|
||||
*/
|
||||
function front_scripts() {
|
||||
FWP()->display->assets['fDate.css'] = FACETWP_URL . '/assets/vendor/fDate/fDate.css';
|
||||
FWP()->display->assets['fDate.js'] = FACETWP_URL . '/assets/vendor/fDate/fDate.min.js';
|
||||
}
|
||||
|
||||
|
||||
function register_fields() {
|
||||
return [
|
||||
'date_fields' => [
|
||||
'type' => 'alias',
|
||||
'items' => [
|
||||
'fields' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Fields to show', 'fwp' ),
|
||||
'choices' => [
|
||||
'both' => __( 'Start + End Dates', 'fwp' ),
|
||||
'exact' => __( 'Exact Date', 'fwp' ),
|
||||
'start_date' => __( 'Start Date', 'fwp' ),
|
||||
'end_date' => __( 'End Date', 'fwp' )
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
'date_format' => [
|
||||
'type' => 'alias',
|
||||
'items' => [
|
||||
'format' => [
|
||||
'label' => __( 'Display format', 'fwp' ),
|
||||
'notes' => 'See available <a href="https://facetwp.com/help-center/facets/facet-types/date-range/#tokens" target="_blank">formatting tokens</a>',
|
||||
'placeholder' => 'Y-m-d'
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Front-end) Attach settings to the AJAX response
|
||||
*/
|
||||
function settings_js( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$selected_values = $params['selected_values'];
|
||||
$fields = empty( $facet['fields'] ) ? 'both' : $facet['fields'];
|
||||
$format = empty( $facet['format'] ) ? '' : $facet['format'];
|
||||
|
||||
// Use "OR" mode by excluding the facet's own selection
|
||||
$where_clause = $this->get_where_clause( $facet );
|
||||
|
||||
$sql = "
|
||||
SELECT MIN(facet_value) AS `minDate`, MAX(facet_display_value) AS `maxDate` FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE facet_name = '{$facet['name']}' AND facet_display_value != '' $where_clause";
|
||||
$row = $wpdb->get_row( $sql );
|
||||
|
||||
$min = substr( $row->minDate, 0, 10 );
|
||||
$max = substr( $row->maxDate, 0, 10 );
|
||||
|
||||
if ( 'both' == $fields ) {
|
||||
$min_upper = ! empty( $selected_values[1] ) ? $selected_values[1] : $max;
|
||||
$max_lower = ! empty( $selected_values[0] ) ? $selected_values[0] : $min;
|
||||
|
||||
$range = [
|
||||
'min' => [
|
||||
'minDate' => $min,
|
||||
'maxDate' => $min_upper
|
||||
],
|
||||
'max' => [
|
||||
'minDate' => $max_lower,
|
||||
'maxDate' => $max
|
||||
]
|
||||
];
|
||||
}
|
||||
else {
|
||||
$range = [
|
||||
'minDate' => $min,
|
||||
'maxDate' => $max
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'locale' => $this->get_i18n_labels(),
|
||||
'format' => $format,
|
||||
'fields' => $fields,
|
||||
'range' => $range
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
function get_i18n_labels() {
|
||||
$locale = get_locale();
|
||||
$locale = empty( $locale ) ? 'en' : substr( $locale, 0, 2 );
|
||||
|
||||
$locales = [
|
||||
'ca' => [
|
||||
'weekdays_short' => ['Dg', 'Dl', 'Dt', 'Dc', 'Dj', 'Dv', 'Ds'],
|
||||
'months_short' => ['Gen', 'Febr', 'Març', 'Abr', 'Maig', 'Juny', 'Jul', 'Ag', 'Set', 'Oct', 'Nov', 'Des'],
|
||||
'months' => ['Gener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juliol', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Desembre'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'da' => [
|
||||
'weekdays_short' => ['søn', 'man', 'tir', 'ons', 'tors', 'fre', 'lør'],
|
||||
'months_short' => ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'],
|
||||
'months' => ['januar', 'februar', 'marts', 'april', 'maj', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'december'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'de' => [
|
||||
'weekdays_short' => ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
|
||||
'months_short' => ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
|
||||
'months' => ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'es' => [
|
||||
'weekdays_short' => ['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb'],
|
||||
'months_short' => ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'],
|
||||
'months' => ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'fr' => [
|
||||
'weekdays_short' => ['dim', 'lun', 'mar', 'mer', 'jeu', 'ven', 'sam'],
|
||||
'months_short' => ['janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'],
|
||||
'months' => ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'it' => [
|
||||
'weekdays_short' => ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'],
|
||||
'months_short' => ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'],
|
||||
'months' => ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'nb' => [
|
||||
'weekdays_short' => ['Søn', 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør'],
|
||||
'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'],
|
||||
'months' => ['Januar', 'Februar', 'Mars', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Desember'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'nl' => [
|
||||
'weekdays_short' => ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
|
||||
'months_short' => ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sept', 'okt', 'nov', 'dec'],
|
||||
'months' => ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'pl' => [
|
||||
'weekdays_short' => ['Nd', 'Pn', 'Wt', 'Śr', 'Cz', 'Pt', 'So'],
|
||||
'months_short' => ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'],
|
||||
'months' => ['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'pt' => [
|
||||
'weekdays_short' => ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
|
||||
'months_short' => ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'],
|
||||
'months' => ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
|
||||
'firstDayOfWeek' => 0
|
||||
],
|
||||
'ro' => [
|
||||
'weekdays_short' => ['Dum', 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sâm'],
|
||||
'months_short' => ['Ian', 'Feb', 'Mar', 'Apr', 'Mai', 'Iun', 'Iul', 'Aug', 'Sep', 'Oct', 'Noi', 'Dec'],
|
||||
'months' => ['Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai', 'Iunie', 'Iulie', 'August', 'Septembrie', 'Octombrie', 'Noiembrie', 'Decembrie'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'ru' => [
|
||||
'weekdays_short' => ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
|
||||
'months_short' => ['Янв', 'Фев', 'Март', 'Апр', 'Май', 'Июнь', 'Июль', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
|
||||
'months' => ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'sv' => [
|
||||
'weekdays_short' => ['Sön', 'Mån', 'Tis', 'Ons', 'Tor', 'Fre', 'Lör'],
|
||||
'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
|
||||
'months' => ['Januari', 'Februari', 'Mars', 'April', 'Maj', 'Juni', 'Juli', 'Augusti', 'September', 'Oktober', 'November', 'December'],
|
||||
'firstDayOfWeek' => 1
|
||||
]
|
||||
];
|
||||
|
||||
if ( isset( $locales[ $locale ] ) ) {
|
||||
$locales[ $locale ]['clearText'] = __( 'Clear', 'fwp-front' );
|
||||
return $locales[ $locale ];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
73
wp/wp-content/plugins/facetwp/includes/facets/dropdown.php
Normal file
73
wp/wp-content/plugins/facetwp/includes/facets/dropdown.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Dropdown extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Dropdown', 'fwp' );
|
||||
$this->fields = [ 'label_any', 'parent_term', 'modifiers', 'hierarchical', 'orderby', 'count' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the available choices
|
||||
*/
|
||||
function load_values( $params ) {
|
||||
return FWP()->helper->facet_types['checkboxes']->load_values( $params );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$facet = $params['facet'];
|
||||
$values = (array) $params['values'];
|
||||
$selected_values = (array) $params['selected_values'];
|
||||
$is_hierarchical = FWP()->helper->facet_is( $facet, 'hierarchical', 'yes' );
|
||||
|
||||
if ( $is_hierarchical ) {
|
||||
$values = FWP()->helper->sort_taxonomy_values( $params['values'], $facet['orderby'] );
|
||||
}
|
||||
|
||||
$label_any = empty( $facet['label_any'] ) ? __( 'Any', 'fwp-front' ) : $facet['label_any'];
|
||||
$label_any = facetwp_i18n( $label_any );
|
||||
|
||||
$output .= '<select class="facetwp-dropdown">';
|
||||
$output .= '<option value="">' . esc_attr( $label_any ) . '</option>';
|
||||
|
||||
foreach ( $values as $row ) {
|
||||
$selected = in_array( $row['facet_value'], $selected_values ) ? ' selected' : '';
|
||||
$indent = $is_hierarchical ? str_repeat( ' ', (int) $row['depth'] ) : '';
|
||||
|
||||
// Determine whether to show counts
|
||||
$label = esc_attr( $row['facet_display_value'] );
|
||||
$label = apply_filters( 'facetwp_facet_display_value', $label, [
|
||||
'selected' => ( '' !== $selected ),
|
||||
'facet' => $facet,
|
||||
'row' => $row
|
||||
]);
|
||||
|
||||
$show_counts = apply_filters( 'facetwp_facet_dropdown_show_counts', true, [ 'facet' => $facet ] );
|
||||
|
||||
if ( $show_counts ) {
|
||||
$label .= ' (' . $row['counter'] . ')';
|
||||
}
|
||||
|
||||
$output .= '<option value="' . esc_attr( $row['facet_value'] ) . '"' . $selected . '>' . $indent . $label . '</option>';
|
||||
}
|
||||
|
||||
$output .= '</select>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
return FWP()->helper->facet_types['checkboxes']->filter_posts( $params );
|
||||
}
|
||||
}
|
||||
100
wp/wp-content/plugins/facetwp/includes/facets/fselect.php
Normal file
100
wp/wp-content/plugins/facetwp/includes/facets/fselect.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_fSelect extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'fSelect', 'fwp' );
|
||||
$this->fields = [ 'label_any', 'parent_term', 'modifiers', 'hierarchical', 'multiple',
|
||||
'ghosts', 'operator', 'orderby', 'count' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the available choices
|
||||
*/
|
||||
function load_values( $params ) {
|
||||
return FWP()->helper->facet_types['checkboxes']->load_values( $params );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$facet = $params['facet'];
|
||||
$values = (array) $params['values'];
|
||||
$selected_values = (array) $params['selected_values'];
|
||||
$is_hierarchical = FWP()->helper->facet_is( $facet, 'hierarchical', 'yes' );
|
||||
|
||||
if ( $is_hierarchical ) {
|
||||
$values = FWP()->helper->sort_taxonomy_values( $params['values'], $facet['orderby'] );
|
||||
}
|
||||
|
||||
$multiple = FWP()->helper->facet_is( $facet, 'multiple', 'yes' ) ? ' multiple="multiple"' : '';
|
||||
$label_any = empty( $facet['label_any'] ) ? __( 'Any', 'fwp-front' ) : $facet['label_any'];
|
||||
$label_any = facetwp_i18n( $label_any );
|
||||
|
||||
$output .= '<select class="facetwp-dropdown"' . $multiple . '>';
|
||||
$output .= '<option value="">' . esc_html( $label_any ) . '</option>';
|
||||
|
||||
foreach ( $values as $row ) {
|
||||
$selected = in_array( $row['facet_value'], $selected_values, true ) ? ' selected' : '';
|
||||
$disabled = ( 0 == $row['counter'] && '' == $selected ) ? ' disabled' : '';
|
||||
|
||||
$label = esc_html( $row['facet_display_value'] );
|
||||
$label = apply_filters( 'facetwp_facet_display_value', $label, [
|
||||
'selected' => ( '' !== $selected ),
|
||||
'facet' => $facet,
|
||||
'row' => $row
|
||||
]);
|
||||
|
||||
$show_counts = apply_filters( 'facetwp_facet_dropdown_show_counts', true, [ 'facet' => $facet ] );
|
||||
$counter = $show_counts ? $row['counter'] : '';
|
||||
$depth = $is_hierarchical ? $row['depth'] : 0;
|
||||
|
||||
$output .= '<option value="' . esc_attr( $row['facet_value'] ) . '" data-counter="' . $counter . '" class="d' . $depth . '"' . $selected . $disabled . '>' . $label . '</option>';
|
||||
}
|
||||
|
||||
$output .= '</select>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
return FWP()->helper->facet_types['checkboxes']->filter_posts( $params );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Front-end) Attach settings to the AJAX response
|
||||
*/
|
||||
function settings_js( $params ) {
|
||||
$facet = $params['facet'];
|
||||
|
||||
$label_any = empty( $facet['label_any'] ) ? __( 'Any', 'fwp-front' ) : $facet['label_any'];
|
||||
$label_any = facetwp_i18n( $label_any );
|
||||
|
||||
return [
|
||||
'placeholder' => $label_any,
|
||||
'overflowText' => __( '{n} selected', 'fwp-front' ),
|
||||
'searchText' => __( 'Search', 'fwp-front' ),
|
||||
'noResultsText' => __( 'No results found', 'fwp-front' ),
|
||||
'operator' => $facet['operator']
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output any front-end scripts
|
||||
*/
|
||||
function front_scripts() {
|
||||
FWP()->display->assets['fSelect.css'] = FACETWP_URL . '/assets/vendor/fSelect/fSelect.css';
|
||||
FWP()->display->assets['fSelect.js'] = FACETWP_URL . '/assets/vendor/fSelect/fSelect.js';
|
||||
}
|
||||
}
|
||||
179
wp/wp-content/plugins/facetwp/includes/facets/hierarchy.php
Normal file
179
wp/wp-content/plugins/facetwp/includes/facets/hierarchy.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Hierarchy extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Hierarchy', 'fwp' );
|
||||
$this->fields = [ 'label_any', 'modifiers', 'orderby', 'count' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the available choices
|
||||
*/
|
||||
function load_values( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$from_clause = $wpdb->prefix . 'facetwp_index f';
|
||||
$where_clause = $params['where_clause'];
|
||||
|
||||
$selected_values = (array) $params['selected_values'];
|
||||
$facet_parent_id = 0;
|
||||
$output = [];
|
||||
|
||||
$label_any = empty( $facet['label_any'] ) ? __( 'Any', 'fwp-front' ) : $facet['label_any'];
|
||||
$label_any = facetwp_i18n( $label_any );
|
||||
|
||||
// Orderby
|
||||
$orderby = $this->get_orderby( $facet );
|
||||
|
||||
// Determine the parent_id and depth
|
||||
if ( ! empty( $selected_values[0] ) ) {
|
||||
|
||||
// Get term ID from slug
|
||||
$sql = "
|
||||
SELECT t.term_id
|
||||
FROM {$wpdb->terms} t
|
||||
INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_id = t.term_id AND tt.taxonomy = %s
|
||||
WHERE t.slug = %s
|
||||
LIMIT 1";
|
||||
|
||||
$value = $selected_values[0];
|
||||
$taxonomy = str_replace( 'tax/', '', $facet['source'] );
|
||||
$facet_parent_id = (int) $wpdb->get_var( $wpdb->prepare( $sql, $taxonomy, $value ) );
|
||||
|
||||
// Invalid term
|
||||
if ( $facet_parent_id < 1 ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Create term lookup array
|
||||
$depths = FWP()->helper->get_term_depths( $taxonomy );
|
||||
$max_depth = (int) $depths[ $facet_parent_id ]['depth'];
|
||||
$last_parent_id = $facet_parent_id;
|
||||
|
||||
// Loop backwards
|
||||
for ( $i = 0; $i <= $max_depth; $i++ ) {
|
||||
$output[] = [
|
||||
'facet_value' => $depths[ $last_parent_id ]['slug'],
|
||||
'facet_display_value' => $depths[ $last_parent_id ]['name'],
|
||||
'depth' => $depths[ $last_parent_id ]['depth'] + 1,
|
||||
'counter' => 1, // FWP.settings.num_choices
|
||||
];
|
||||
|
||||
$last_parent_id = (int) $depths[ $last_parent_id ]['parent_id'];
|
||||
}
|
||||
|
||||
$output[] = [
|
||||
'facet_value' => '',
|
||||
'facet_display_value' => $label_any,
|
||||
'depth' => 0,
|
||||
'counter' => 1,
|
||||
];
|
||||
|
||||
// Reverse it
|
||||
$output = array_reverse( $output );
|
||||
}
|
||||
|
||||
// Update the WHERE clause
|
||||
$where_clause .= " AND parent_id = '$facet_parent_id'";
|
||||
|
||||
$orderby = apply_filters( 'facetwp_facet_orderby', $orderby, $facet );
|
||||
$from_clause = apply_filters( 'facetwp_facet_from', $from_clause, $facet );
|
||||
$where_clause = apply_filters( 'facetwp_facet_where', $where_clause, $facet );
|
||||
|
||||
$sql = "
|
||||
SELECT f.facet_value, f.facet_display_value, COUNT(DISTINCT f.post_id) AS counter
|
||||
FROM $from_clause
|
||||
WHERE f.facet_name = '{$facet['name']}' $where_clause
|
||||
GROUP BY f.facet_value
|
||||
ORDER BY $orderby";
|
||||
|
||||
$results = $wpdb->get_results( $sql, ARRAY_A );
|
||||
$new_depth = empty( $output ) ? 0 : $output[ count( $output ) - 1 ]['depth'] + 1;
|
||||
|
||||
foreach ( $results as $result ) {
|
||||
$result['depth'] = $new_depth;
|
||||
$result['is_choice'] = true;
|
||||
$output[] = $result;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
$facet = $params['facet'];
|
||||
$values = (array) $params['values'];
|
||||
$selected_values = (array) $params['selected_values'];
|
||||
|
||||
$output = '';
|
||||
$num_visible = $this->get_limit( $facet );
|
||||
$num = 0;
|
||||
|
||||
if ( ! empty( $values ) ) {
|
||||
foreach ( $values as $row ) {
|
||||
$last_depth = $last_depth ?? $row['depth'];
|
||||
$selected = ( ! empty( $selected_values ) && $row['facet_value'] == $selected_values[0] );
|
||||
|
||||
$label = esc_html( $row['facet_display_value'] );
|
||||
$label = apply_filters( 'facetwp_facet_display_value', $label, [
|
||||
'selected' => $selected,
|
||||
'facet' => $facet,
|
||||
'row' => $row
|
||||
]);
|
||||
|
||||
if ( $row['depth'] > $last_depth ) {
|
||||
$output .= '<div class="facetwp-depth">';
|
||||
}
|
||||
|
||||
if ( $num == $num_visible ) {
|
||||
$output .= '<div class="facetwp-overflow facetwp-hidden">';
|
||||
}
|
||||
|
||||
if ( ! $selected ) {
|
||||
if ( isset( $row['is_choice'] ) ) {
|
||||
$label .= ' <span class="facetwp-counter">(' . $row['counter'] . ')</span>';
|
||||
}
|
||||
else {
|
||||
$arrow = apply_filters( 'facetwp_facet_hierarchy_arrow', '‹ ' );
|
||||
$label = $arrow . $label;
|
||||
}
|
||||
}
|
||||
|
||||
$output .= '<div class="facetwp-link' . ( $selected ? ' checked' : '' ) . '" data-value="' . esc_attr( $row['facet_value'] ) . '">' . $label . '</div>';
|
||||
|
||||
if ( isset( $row['is_choice'] ) ) {
|
||||
$num++;
|
||||
}
|
||||
|
||||
$last_depth = $row['depth'];
|
||||
}
|
||||
|
||||
if ( $num_visible < $num ) {
|
||||
$output .= '</div>';
|
||||
$output .= '<a class="facetwp-toggle">' . __( 'See more', 'fwp-front' ) . '</a>';
|
||||
$output .= '<a class="facetwp-toggle facetwp-hidden">' . __( 'See less', 'fwp-front' ) . '</a>';
|
||||
}
|
||||
|
||||
for ( $i = 0; $i <= $last_depth; $i++ ) {
|
||||
$output .= '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
return FWP()->helper->facet_types['checkboxes']->filter_posts( $params );
|
||||
}
|
||||
}
|
||||
137
wp/wp-content/plugins/facetwp/includes/facets/number_range.php
Normal file
137
wp/wp-content/plugins/facetwp/includes/facets/number_range.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Number_Range extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Number Range', 'fwp' );
|
||||
$this->fields = [ 'source_other', 'compare_type', 'number_fields' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$value = $params['selected_values'];
|
||||
$value = empty( $value ) ? [ '', '', ] : $value;
|
||||
$fields = empty( $params['facet']['fields'] ) ? 'both' : $params['facet']['fields'];
|
||||
|
||||
if ( 'exact' == $fields ) {
|
||||
$output .= '<input type="text" class="facetwp-number facetwp-number-min" value="' . esc_attr( $value[0] ) . '" placeholder="' . __( 'Number', 'fwp-front' ) . '" />';
|
||||
}
|
||||
if ( 'both' == $fields || 'min' == $fields ) {
|
||||
$output .= '<input type="text" class="facetwp-number facetwp-number-min" value="' . esc_attr( $value[0] ) . '" placeholder="' . __( 'Min', 'fwp-front' ) . '" />';
|
||||
}
|
||||
if ( 'both' == $fields || 'max' == $fields ) {
|
||||
$output .= '<input type="text" class="facetwp-number facetwp-number-max" value="' . esc_attr( $value[1] ) . '" placeholder="' . __( 'Max', 'fwp-front' ) . '" />';
|
||||
}
|
||||
|
||||
$output .= '<input type="button" class="facetwp-submit" value="' . __( 'Go', 'fwp-front' ) . '" />';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$values = $params['selected_values'];
|
||||
$where = '';
|
||||
|
||||
$min = ( '' == $values[0] ) ? false : $values[0];
|
||||
$max = ( '' == $values[1] ) ? false : $values[1];
|
||||
|
||||
$fields = $facet['fields'] ?? 'both';
|
||||
$compare_type = empty( $facet['compare_type'] ) ? 'basic' : $facet['compare_type'];
|
||||
$is_dual = ! empty( $facet['source_other'] );
|
||||
|
||||
if ( $is_dual && 'basic' != $compare_type ) {
|
||||
if ( 'exact' == $fields ) {
|
||||
$max = $min;
|
||||
}
|
||||
|
||||
$min = ( false !== $min ) ? $min : -999999999999;
|
||||
$max = ( false !== $max ) ? $max : 999999999999;
|
||||
|
||||
/**
|
||||
* Enclose compare
|
||||
* The post's range must surround the user-defined range
|
||||
*/
|
||||
if ( 'enclose' == $compare_type ) {
|
||||
$where .= " AND (facet_value + 0) <= '$min'";
|
||||
$where .= " AND (facet_display_value + 0) >= '$max'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersect compare
|
||||
* @link http://stackoverflow.com/a/325964
|
||||
*/
|
||||
if ( 'intersect' == $compare_type ) {
|
||||
$where .= " AND (facet_value + 0) <= '$max'";
|
||||
$where .= " AND (facet_display_value + 0) >= '$min'";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic compare
|
||||
* The user-defined range must surround the post's range
|
||||
*/
|
||||
else {
|
||||
if ( 'exact' == $fields ) {
|
||||
$max = $min;
|
||||
}
|
||||
if ( false !== $min ) {
|
||||
$where .= " AND (facet_value + 0) >= '$min'";
|
||||
}
|
||||
if ( false !== $max ) {
|
||||
$where .= " AND (facet_display_value + 0) <= '$max'";
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT DISTINCT post_id FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE facet_name = '{$facet['name']}' $where";
|
||||
return facetwp_sql( $sql, $facet );
|
||||
}
|
||||
|
||||
|
||||
function register_fields() {
|
||||
return [
|
||||
'number_fields' => [
|
||||
'type' => 'alias',
|
||||
'items' => [
|
||||
'fields' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Fields to show', 'fwp' ),
|
||||
'choices' => [
|
||||
'both' => __( 'Min + Max', 'fwp' ),
|
||||
'exact' => __( 'Exact', 'fwp' ),
|
||||
'min' => __( 'Min', 'fwp' ),
|
||||
'max' => __( 'Max', 'fwp' )
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Front-end) Attach settings to the AJAX response
|
||||
*/
|
||||
function settings_js( $params ) {
|
||||
$facet = $params['facet'];
|
||||
$fields = empty( $facet['fields'] ) ? 'both' : $facet['fields'];
|
||||
|
||||
return [
|
||||
'fields' => $fields
|
||||
];
|
||||
}
|
||||
}
|
||||
277
wp/wp-content/plugins/facetwp/includes/facets/pager.php
Normal file
277
wp/wp-content/plugins/facetwp/includes/facets/pager.php
Normal file
@@ -0,0 +1,277 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Pager extends FacetWP_Facet
|
||||
{
|
||||
|
||||
public $pager_args;
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Pager', 'fwp' );
|
||||
$this->fields = [ 'pager_type', 'inner_size', 'dots_label', 'prev_label', 'next_label',
|
||||
'count_text_plural', 'count_text_singular', 'count_text_none', 'load_more_text',
|
||||
'loading_text', 'default_label', 'per_page_options' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
$facet = $params['facet'];
|
||||
$pager_type = $facet['pager_type'];
|
||||
$this->pager_args = FWP()->facet->pager_args;
|
||||
|
||||
$method = 'render_' . $pager_type;
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return $this->$method( $facet );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function render_numbers( $facet ) {
|
||||
$inner_size = (int) $facet['inner_size'];
|
||||
$dots_label = facetwp_i18n( $facet['dots_label'] );
|
||||
$prev_label = facetwp_i18n( $facet['prev_label'] );
|
||||
$next_label = facetwp_i18n( $facet['next_label'] );
|
||||
|
||||
$output = '';
|
||||
$page = (int) $this->pager_args['page'];
|
||||
$total_pages = (int) $this->pager_args['total_pages'];
|
||||
$inner_first = max( $page - $inner_size, 2 );
|
||||
$inner_last = min( $page + $inner_size, $total_pages - 1 );
|
||||
|
||||
if ( 1 < $total_pages ) {
|
||||
|
||||
// Prev button
|
||||
if ( 1 < $page && '' != $prev_label ) {
|
||||
$output .= $this->render_page( $page - 1, $prev_label, 'prev' );
|
||||
}
|
||||
|
||||
// First page
|
||||
$output .= $this->render_page( 1, false, 'first' );
|
||||
|
||||
// Dots
|
||||
if ( 2 < $inner_first && '' != $dots_label ) {
|
||||
$output .= $this->render_page( '', $dots_label, 'dots' );
|
||||
}
|
||||
|
||||
for ( $i = $inner_first; $i <= $inner_last; $i++ ) {
|
||||
$output .= $this->render_page( $i );
|
||||
}
|
||||
|
||||
// Dots
|
||||
if ( $inner_last < $total_pages - 1 && '' != $dots_label ) {
|
||||
$output .= $this->render_page( '', $dots_label, 'dots' );
|
||||
}
|
||||
|
||||
// Last page
|
||||
$output .= $this->render_page( $total_pages, false, 'last' );
|
||||
|
||||
// Next button
|
||||
if ( $page < $total_pages && '' != $next_label ) {
|
||||
$output .= $this->render_page( $page + 1, $next_label, 'next' );
|
||||
}
|
||||
}
|
||||
|
||||
return '<div class="facetwp-pager">' . $output . '</div>';
|
||||
}
|
||||
|
||||
|
||||
function render_page( $page, $label = false, $extra_class = false ) {
|
||||
$label = ( false === $label ) ? $page : $label;
|
||||
$class = 'facetwp-page';
|
||||
|
||||
if ( ! empty( $extra_class ) ) {
|
||||
$class .= ' ' . $extra_class;
|
||||
}
|
||||
|
||||
if ( $page == $this->pager_args['page'] ) {
|
||||
$class .= ' active';
|
||||
}
|
||||
|
||||
$data = empty( $page ) ? '' : ' data-page="' . $page . '"';
|
||||
$html = '<a class="' . $class . '"' . $data . '>' . $label . '</a>';
|
||||
|
||||
return apply_filters( 'facetwp_facet_pager_link', $html, [
|
||||
'page' => $page,
|
||||
'label' => $label,
|
||||
'extra_class' => $extra_class
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
function render_counts( $facet ) {
|
||||
$text_singular = facetwp_i18n( $facet['count_text_singular'] );
|
||||
$text_plural = facetwp_i18n( $facet['count_text_plural'] );
|
||||
$text_none = facetwp_i18n( $facet['count_text_none'] );
|
||||
|
||||
$page = $this->pager_args['page'];
|
||||
$per_page = $this->pager_args['per_page'];
|
||||
$total_rows = $this->pager_args['total_rows'];
|
||||
$total_pages = $this->pager_args['total_pages'];
|
||||
|
||||
if ( -1 == $per_page ) {
|
||||
$per_page = $total_rows;
|
||||
}
|
||||
|
||||
if ( 1 < $total_rows ) {
|
||||
$lower = ( 1 + ( ( $page - 1 ) * $per_page ) );
|
||||
$upper = ( $page * $per_page );
|
||||
$upper = ( $total_rows < $upper ) ? $total_rows : $upper;
|
||||
|
||||
// If a load_more pager is in use, force $lower = 1
|
||||
if ( FWP()->helper->facet_setting_exists( 'pager_type', 'load_more' ) ) {
|
||||
$lower = 1;
|
||||
}
|
||||
|
||||
$output = $text_plural;
|
||||
$output = str_replace( '[lower]', $lower, $output );
|
||||
$output = str_replace( '[upper]', $upper, $output );
|
||||
$output = str_replace( '[total]', $total_rows, $output );
|
||||
$output = str_replace( '[page]', $page, $output );
|
||||
$output = str_replace( '[per_page]', $per_page, $output );
|
||||
$output = str_replace( '[total_pages]', $total_pages, $output );
|
||||
}
|
||||
else {
|
||||
$output = ( 0 < $total_rows ) ? $text_singular : $text_none;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
function render_load_more( $facet ) {
|
||||
$text = facetwp_i18n( $facet['load_more_text'] );
|
||||
$loading_text = facetwp_i18n( $facet['loading_text'] );
|
||||
|
||||
return '<button class="facetwp-load-more" data-loading="' . esc_attr( $loading_text ) . '">' . esc_attr( $text ) . '</button>';
|
||||
}
|
||||
|
||||
|
||||
function render_per_page( $facet ) {
|
||||
$default = facetwp_i18n( $facet['default_label'] );
|
||||
$options = explode( ',', $facet['per_page_options'] );
|
||||
$options = array_map( 'trim', $options );
|
||||
$output = '';
|
||||
|
||||
if ( ! empty( $default ) ) {
|
||||
$output .= '<option value="">' . esc_attr( $default ) . '</option>';
|
||||
}
|
||||
|
||||
$per_page = $this->pager_args['per_page'];
|
||||
$var_exists = isset( FWP()->request->url_vars['per_page'] );
|
||||
|
||||
foreach ( $options as $option ) {
|
||||
$val = $label = $option;
|
||||
|
||||
// Support "All" option
|
||||
if ( ! ctype_digit( $val ) ) {
|
||||
$val = -1;
|
||||
$label = facetwp_i18n( $label );
|
||||
}
|
||||
|
||||
$selected = ( $var_exists && $val == $per_page ) ? ' selected' : '';
|
||||
$output .= '<option value="' . $val . '"' . $selected . '>' . esc_attr( $label ) . '</option>';
|
||||
}
|
||||
|
||||
return '<select class="facetwp-per-page-select">' . $output . '</select>';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
return 'continue';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Front-end) Attach settings to the AJAX response
|
||||
*/
|
||||
function settings_js( $params ) {
|
||||
$facet = $params['facet'];
|
||||
|
||||
return [
|
||||
'pager_type' => $facet['pager_type']
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
function register_fields() {
|
||||
return [
|
||||
'pager_type' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Pager type', 'fwp' ),
|
||||
'choices' => [
|
||||
'numbers' => __( 'Page numbers', 'fwp' ),
|
||||
'counts' => __( 'Result counts', 'fwp' ),
|
||||
'load_more' => __( 'Load more', 'fwp' ),
|
||||
'per_page' => __( 'Per page', 'fwp' )
|
||||
]
|
||||
],
|
||||
'inner_size' => [
|
||||
'label' => __( 'Inner size', 'fwp' ),
|
||||
'notes' => 'Number of pages to show on each side of the current page',
|
||||
'default' => 2,
|
||||
'show' => "facet.pager_type == 'numbers'"
|
||||
],
|
||||
'dots_label' => [
|
||||
'label' => __( 'Dots label', 'fwp' ),
|
||||
'notes' => 'The filler between the inner and outer pages',
|
||||
'default' => '…',
|
||||
'show' => "facet.pager_type == 'numbers'"
|
||||
],
|
||||
'prev_label' => [
|
||||
'label' => __( 'Prev button label', 'fwp' ),
|
||||
'notes' => 'Leave blank to hide',
|
||||
'default' => '« Prev',
|
||||
'show' => "facet.pager_type == 'numbers'"
|
||||
],
|
||||
'next_label' => [
|
||||
'label' => __( 'Next button label', 'fwp' ),
|
||||
'notes' => 'Leave blank to hide',
|
||||
'default' => 'Next »',
|
||||
'show' => "facet.pager_type == 'numbers'"
|
||||
],
|
||||
'count_text_plural' => [
|
||||
'label' => __( 'Count text (plural)', 'fwp' ),
|
||||
'notes' => 'Available tags: [lower], [upper], [total], [page], [per_page], [total_pages]',
|
||||
'default' => '[lower] - [upper] of [total] results',
|
||||
'show' => "facet.pager_type == 'counts'"
|
||||
],
|
||||
'count_text_singular' => [
|
||||
'label' => __( 'Count text (singular)', 'fwp' ),
|
||||
'default' => '1 result',
|
||||
'show' => "facet.pager_type == 'counts'"
|
||||
],
|
||||
'count_text_none' => [
|
||||
'label' => __( 'Count text (no results)', 'fwp' ),
|
||||
'default' => 'No results',
|
||||
'show' => "facet.pager_type == 'counts'"
|
||||
],
|
||||
'load_more_text' => [
|
||||
'label' => __( 'Load more text', 'fwp' ),
|
||||
'default' => 'Load more',
|
||||
'show' => "facet.pager_type == 'load_more'"
|
||||
],
|
||||
'loading_text' => [
|
||||
'label' => __( 'Loading text', 'fwp' ),
|
||||
'default' => 'Loading...',
|
||||
'show' => "facet.pager_type == 'load_more'"
|
||||
],
|
||||
'default_label' => [
|
||||
'label' => __( 'Default label', 'fwp' ),
|
||||
'default' => 'Per page',
|
||||
'show' => "facet.pager_type == 'per_page'"
|
||||
],
|
||||
'per_page_options' => [
|
||||
'label' => __( 'Per page options', 'fwp' ),
|
||||
'notes' => 'A comma-separated list of choices. Optionally add a non-numeric choice to be used as a "Show all" option.',
|
||||
'default' => '10, 25, 50, 100',
|
||||
'show' => "facet.pager_type == 'per_page'"
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
354
wp/wp-content/plugins/facetwp/includes/facets/proximity.php
Normal file
354
wp/wp-content/plugins/facetwp/includes/facets/proximity.php
Normal file
@@ -0,0 +1,354 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Proximity_Core extends FacetWP_Facet
|
||||
{
|
||||
|
||||
/* (array) Associative array containing post_id => distance */
|
||||
public $distance = [];
|
||||
|
||||
/* (array) Associative array containing post_id => [lat, lng] */
|
||||
public $post_latlng = [];
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Proximity', 'fwp' );
|
||||
$this->fields = [ 'longitude', 'unit', 'radius_ui', 'radius_options', 'radius_min', 'radius_max', 'radius_default' ];
|
||||
|
||||
add_filter( 'facetwp_index_row', [ $this, 'index_latlng' ], 1, 2 );
|
||||
add_filter( 'facetwp_sort_options', [ $this, 'sort_options' ], 1, 2 );
|
||||
add_filter( 'facetwp_filtered_post_ids', [ $this, 'sort_by_distance' ], 10, 2 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$facet = $params['facet'];
|
||||
$value = $params['selected_values'];
|
||||
$unit = empty( $facet['unit'] ) ? 'mi' : $facet['unit'];
|
||||
|
||||
$lat = empty( $value[0] ) ? '' : $value[0];
|
||||
$lng = empty( $value[1] ) ? '' : $value[1];
|
||||
$chosen_radius = empty( $value[2] ) ? '' : (float) $value[2];
|
||||
$location_name = empty( $value[3] ) ? '' : urldecode( $value[3] );
|
||||
|
||||
$radius_options = [ 10, 25, 50, 100, 250 ];
|
||||
|
||||
// Grab the radius UI
|
||||
$radius_ui = empty( $facet['radius_ui'] ) ? 'dropdown' : $facet['radius_ui'];
|
||||
|
||||
// Grab radius options from the UI
|
||||
if ( ! empty( $facet['radius_options'] ) ) {
|
||||
$radius_options = explode( ',', preg_replace( '/\s+/', '', $facet['radius_options'] ) );
|
||||
}
|
||||
|
||||
// Grab default radius from the UI
|
||||
if ( empty( $chosen_radius ) && ! empty( $facet['radius_default'] ) ) {
|
||||
$chosen_radius = (float) $facet['radius_default'];
|
||||
}
|
||||
|
||||
// Support dynamic radius
|
||||
if ( ! empty( $chosen_radius ) && 0 < $chosen_radius ) {
|
||||
if ( ! in_array( $chosen_radius, $radius_options ) ) {
|
||||
$radius_options[] = $chosen_radius;
|
||||
}
|
||||
}
|
||||
|
||||
$radius_options = apply_filters( 'facetwp_proximity_radius_options', $radius_options );
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<span class="facetwp-input-wrap">
|
||||
<i class="facetwp-icon locate-me"></i>
|
||||
<input type="text" class="facetwp-location" value="<?php echo esc_attr( $location_name ); ?>" placeholder="<?php _e( 'Enter location', 'fwp-front' ); ?>" autocomplete="off" />
|
||||
<div class="location-results facetwp-hidden"></div>
|
||||
</span>
|
||||
|
||||
<?php if ( 'dropdown' == $radius_ui ) : ?>
|
||||
|
||||
<select class="facetwp-radius facetwp-radius-dropdown">
|
||||
<?php foreach ( $radius_options as $radius ) : ?>
|
||||
<?php $selected = ( $chosen_radius == $radius ) ? ' selected' : ''; ?>
|
||||
<option value="<?php echo $radius; ?>"<?php echo $selected; ?>><?php echo "$radius $unit"; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
||||
<?php elseif ( 'slider' == $radius_ui ) : ?>
|
||||
|
||||
<div class="facetwp-radius-wrap">
|
||||
<input class="facetwp-radius facetwp-radius-slider" type="range"
|
||||
min="<?php echo $facet['radius_min']; ?>"
|
||||
max="<?php echo $facet['radius_max']; ?>"
|
||||
value="<?php echo $chosen_radius; ?>"
|
||||
/>
|
||||
<div class="facetwp-radius-label">
|
||||
<span class="facetwp-radius-dist"><?php echo $chosen_radius; ?></span>
|
||||
<span class="facetwp-radius-unit"><?php echo $facet['unit']; ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php elseif ( 'none' == $radius_ui ) : ?>
|
||||
|
||||
<input class="facetwp-radius facetwp-hidden" value="<?php echo $chosen_radius; ?>" />
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<input type="hidden" class="facetwp-lat" value="<?php echo esc_attr( $lat ); ?>" />
|
||||
<input type="hidden" class="facetwp-lng" value="<?php echo esc_attr( $lng ); ?>" />
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$selected_values = $params['selected_values'];
|
||||
$unit = empty( $facet['unit'] ) ? 'mi' : $facet['unit'];
|
||||
$earth_radius = ( 'mi' == $unit ) ? 3959 : 6371;
|
||||
|
||||
if ( empty( $selected_values ) || empty( $selected_values[0] ) ) {
|
||||
return 'continue';
|
||||
}
|
||||
|
||||
$lat1 = (float) $selected_values[0];
|
||||
$lng1 = (float) $selected_values[1];
|
||||
$radius = (float) $selected_values[2];
|
||||
$rad = M_PI / 180;
|
||||
|
||||
$sql = "
|
||||
SELECT DISTINCT post_id, facet_value AS `lat`, facet_display_value AS `lng`
|
||||
FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE facet_name = '{$facet['name']}'";
|
||||
|
||||
$results = $wpdb->get_results( $sql );
|
||||
|
||||
foreach ( $results as $row ) {
|
||||
$lat2 = (float) $row->lat;
|
||||
$lng2 = (float) $row->lng;
|
||||
|
||||
if ( ( $lat1 == $lat2 ) && ( $lng1 == $lng2 ) ) {
|
||||
$dist = 0;
|
||||
}
|
||||
else {
|
||||
$calc = sin( $lat1 * $rad ) * sin( $lat2 * $rad ) +
|
||||
cos( $lat1 * $rad ) * cos( $lat2 * $rad ) *
|
||||
cos( $lng2 * $rad - $lng1 * $rad );
|
||||
|
||||
// acos() must be between -1 and 1
|
||||
$dist = acos( max( -1, min( 1, $calc ) ) ) * $earth_radius;
|
||||
}
|
||||
|
||||
if ( $dist <= $radius ) {
|
||||
$existing = $this->distance[ $row->post_id ] ?? -1;
|
||||
|
||||
if ( -1 == $existing || $dist < $existing ) {
|
||||
$this->distance[ $row->post_id ] = $dist;
|
||||
|
||||
if ( apply_filters( 'facetwp_proximity_store_latlng', false ) ) {
|
||||
$this->post_latlng[ $row->post_id ] = [ $lat2, $lng2 ];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
asort( $this->distance, SORT_NUMERIC );
|
||||
|
||||
return array_keys( $this->distance );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output front-end scripts
|
||||
*/
|
||||
function front_scripts() {
|
||||
if ( apply_filters( 'facetwp_proximity_load_js', true ) ) {
|
||||
|
||||
// hard-coded
|
||||
$api_key = defined( 'GMAPS_API_KEY' ) ? GMAPS_API_KEY : '';
|
||||
|
||||
// admin ui
|
||||
$tmp_key = FWP()->helper->get_setting( 'gmaps_api_key' );
|
||||
$api_key = empty( $tmp_key ) ? $api_key : $tmp_key;
|
||||
|
||||
// hook
|
||||
$api_key = apply_filters( 'facetwp_gmaps_api_key', $api_key );
|
||||
|
||||
FWP()->display->assets['gmaps'] = '//maps.googleapis.com/maps/api/js?libraries=places&key=' . trim( $api_key ) . '&callback=Function.prototype';
|
||||
}
|
||||
|
||||
// Pass extra options into Places Autocomplete
|
||||
$options = apply_filters( 'facetwp_proximity_autocomplete_options', [] );
|
||||
FWP()->display->json['proximity']['autocomplete_options'] = $options;
|
||||
FWP()->display->json['proximity']['clearText'] = __( 'Clear location', 'fwp-front' );
|
||||
FWP()->display->json['proximity']['queryDelay'] = 250;
|
||||
FWP()->display->json['proximity']['minLength'] = 3;
|
||||
}
|
||||
|
||||
|
||||
function register_fields() {
|
||||
return [
|
||||
'longitude' => [
|
||||
'type' => 'alias',
|
||||
'items' => [
|
||||
'source_other' => [
|
||||
'label' => __( 'Longitude', 'fwp' ),
|
||||
'notes' => '(Optional) use a separate longitude field',
|
||||
'html' => '<data-sources :facet="facet" setting-name="source_other"></data-sources>'
|
||||
]
|
||||
]
|
||||
],
|
||||
'unit' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Unit of measurement', 'fwp' ),
|
||||
'choices' => [
|
||||
'mi' => __( 'Miles', 'fwp' ),
|
||||
'km' => __( 'Kilometers', 'fwp' )
|
||||
]
|
||||
],
|
||||
'radius_ui' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Radius UI', 'fwp' ),
|
||||
'choices' => [
|
||||
'dropdown' => __( 'Dropdown', 'fwp' ),
|
||||
'slider' => __( 'Slider', 'fwp' ),
|
||||
'none' => __( 'None', 'fwp' )
|
||||
]
|
||||
],
|
||||
'radius_options' => [
|
||||
'label' => __( 'Radius options', 'fwp' ),
|
||||
'notes' => 'A comma-separated list of radius choices',
|
||||
'default' => '10, 25, 50, 100, 250',
|
||||
'show' => "facet.radius_ui == 'dropdown'"
|
||||
],
|
||||
'radius_min' => [
|
||||
'label' => __( 'Range (min)', 'fwp' ),
|
||||
'default' => 1,
|
||||
'show' => "facet.radius_ui == 'slider'"
|
||||
],
|
||||
'radius_max' => [
|
||||
'label' => __( 'Range (max)', 'fwp' ),
|
||||
'default' => 50,
|
||||
'show' => "facet.radius_ui == 'slider'"
|
||||
],
|
||||
'radius_default' => [
|
||||
'label' => __( 'Default radius', 'fwp' ),
|
||||
'default' => 25
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index the coordinates
|
||||
* We expect a comma-separated "latitude, longitude"
|
||||
*/
|
||||
function index_latlng( $params, $class ) {
|
||||
|
||||
$facet = FWP()->helper->get_facet_by_name( $params['facet_name'] );
|
||||
|
||||
if ( false !== $facet && 'proximity' == $facet['type'] ) {
|
||||
$latlng = $params['facet_value'];
|
||||
|
||||
// Only handle "lat, lng" strings
|
||||
if ( ! empty( $latlng ) && is_string( $latlng ) ) {
|
||||
$latlng = preg_replace( '/[^0-9.,-]/', '', $latlng );
|
||||
|
||||
if ( ! empty( $facet['source_other'] ) ) {
|
||||
$other_params = $params;
|
||||
$other_params['facet_source'] = $facet['source_other'];
|
||||
$rows = $class->get_row_data( $other_params );
|
||||
|
||||
if ( ! empty( $rows ) && false === strpos( $latlng, ',' ) ) {
|
||||
$lng = $rows[0]['facet_display_value'];
|
||||
$lng = preg_replace( '/[^0-9.,-]/', '', $lng );
|
||||
$latlng .= ',' . $lng;
|
||||
}
|
||||
}
|
||||
|
||||
if ( preg_match( "/^([\d.-]+),([\d.-]+)$/", $latlng ) ) {
|
||||
$latlng = explode( ',', $latlng );
|
||||
$params['facet_value'] = $latlng[0];
|
||||
$params['facet_display_value'] = $latlng[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add "Distance" to the sort box
|
||||
*/
|
||||
function sort_options( $options, $params ) {
|
||||
|
||||
if ( FWP()->helper->facet_setting_exists( 'type', 'proximity' ) ) {
|
||||
$options['distance'] = [
|
||||
'label' => __( 'Distance', 'fwp-front' ),
|
||||
'query_args' => [
|
||||
'orderby' => 'post__in',
|
||||
'order' => 'ASC',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort the final (filtered) post IDs by distance
|
||||
*/
|
||||
function sort_by_distance( $post_ids, $class ) {
|
||||
|
||||
$distance = FWP()->helper->facet_types['proximity']->distance;
|
||||
|
||||
if ( ! empty( $distance ) ) {
|
||||
$ordered_posts = array_keys( $distance );
|
||||
$filtered_posts = array_flip( $post_ids );
|
||||
$intersected_ids = [];
|
||||
|
||||
foreach ( $ordered_posts as $p ) {
|
||||
if ( isset( $filtered_posts[ $p ] ) ) {
|
||||
$intersected_ids[] = $p;
|
||||
}
|
||||
}
|
||||
|
||||
$post_ids = $intersected_ids;
|
||||
}
|
||||
|
||||
return $post_ids;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a post's distance
|
||||
*/
|
||||
function facetwp_get_distance( $post_id = false ) {
|
||||
global $post;
|
||||
|
||||
// Get the post ID
|
||||
$post_id = ( false === $post_id ) ? $post->ID : $post_id;
|
||||
|
||||
// Get the proximity class
|
||||
$facet_type = FWP()->helper->facet_types['proximity'];
|
||||
|
||||
// Get the distance
|
||||
$distance = $facet_type->distance[ $post_id ] ?? -1;
|
||||
|
||||
if ( -1 < $distance ) {
|
||||
return apply_filters( 'facetwp_proximity_distance_output', $distance );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
64
wp/wp-content/plugins/facetwp/includes/facets/radio.php
Normal file
64
wp/wp-content/plugins/facetwp/includes/facets/radio.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Radio_Core extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Radio', 'fwp' );
|
||||
$this->fields = [ 'label_any', 'parent_term', 'modifiers', 'ghosts', 'orderby', 'count' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the available choices
|
||||
*/
|
||||
function load_values( $params ) {
|
||||
$params['facet']['operator'] = 'or';
|
||||
return FWP()->helper->facet_types['checkboxes']->load_values( $params );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$facet = $params['facet'];
|
||||
$values = (array) $params['values'];
|
||||
$selected_values = (array) $params['selected_values'];
|
||||
$label_any = empty( $facet['label_any'] ) ? false : facetwp_i18n( $facet['label_any'] );
|
||||
|
||||
if ( $label_any ) {
|
||||
$selected = empty( $selected_values ) ? ' checked' : '';
|
||||
$output .= '<div class="facetwp-radio' . $selected . '" data-value="">' . esc_attr( $label_any ) . '</div>';
|
||||
}
|
||||
|
||||
foreach ( $values as $row ) {
|
||||
$label = esc_html( $row['facet_display_value'] );
|
||||
$selected = in_array( $row['facet_value'], $selected_values ) ? ' checked' : '';
|
||||
$selected .= ( 0 == $row['counter'] && '' == $selected ) ? ' disabled' : '';
|
||||
$output .= '<div class="facetwp-radio' . $selected . '" data-value="' . esc_attr( $row['facet_value'] ) . '">';
|
||||
$output .= '<span class="facetwp-display-value">';
|
||||
$output .= apply_filters( 'facetwp_facet_display_value', $label, [
|
||||
'selected' => ( '' !== $selected ),
|
||||
'facet' => $facet,
|
||||
'row' => $row
|
||||
]);
|
||||
$output .= '</span>';
|
||||
$output .= '<span class="facetwp-counter">(' . $row['counter'] . ')</span>';
|
||||
$output .= '</div>';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
$params['facet']['operator'] = 'or';
|
||||
return FWP()->helper->facet_types['checkboxes']->filter_posts( $params );
|
||||
}
|
||||
}
|
||||
114
wp/wp-content/plugins/facetwp/includes/facets/rating.php
Normal file
114
wp/wp-content/plugins/facetwp/includes/facets/rating.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Rating extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Star Rating', 'fwp' );
|
||||
$this->fields = [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the available choices
|
||||
*/
|
||||
function load_values( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$from_clause = $wpdb->prefix . 'facetwp_index f';
|
||||
|
||||
// Facet in "OR" mode
|
||||
$where_clause = $this->get_where_clause( $facet );
|
||||
|
||||
$output = [
|
||||
1 => [ 'counter' => 0 ],
|
||||
2 => [ 'counter' => 0 ],
|
||||
3 => [ 'counter' => 0 ],
|
||||
4 => [ 'counter' => 0 ],
|
||||
5 => [ 'counter' => 0 ]
|
||||
];
|
||||
|
||||
$sql = "
|
||||
SELECT COUNT(*) AS `count`, FLOOR(f.facet_value) AS `rating`
|
||||
FROM $from_clause
|
||||
WHERE f.facet_name = '{$facet['name']}' AND FLOOR(f.facet_value) >= 1 $where_clause
|
||||
GROUP BY rating";
|
||||
|
||||
$results = $wpdb->get_results( $sql );
|
||||
|
||||
foreach ( $results as $result ) {
|
||||
$output[ $result->rating ]['counter'] = $result->count;
|
||||
}
|
||||
|
||||
$total = 0;
|
||||
|
||||
// The lower rating should include higher rating counts
|
||||
for ( $i = 5; $i > 0; $i-- ) {
|
||||
$output[ $i ]['counter'] += $total;
|
||||
$total = $output[ $i ]['counter'];
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$facet = $params['facet'];
|
||||
$values = (array) $params['values'];
|
||||
$selected_values = (array) $params['selected_values'];
|
||||
|
||||
$num_stars = 0;
|
||||
foreach ( $values as $val ) {
|
||||
if ( 0 < $val['counter'] ) {
|
||||
$num_stars++;
|
||||
}
|
||||
}
|
||||
|
||||
if ( 0 < $num_stars ) {
|
||||
$output .= '<span class="facetwp-stars">';
|
||||
|
||||
for ( $i = $num_stars; $i >= 1; $i-- ) {
|
||||
$class = in_array( $i, $selected_values ) ? ' selected' : '';
|
||||
$output .= '<span class="facetwp-star' . $class . '" data-value="' . $i . '" data-counter="' . $values[ $i ]['counter'] . '">★</span>';
|
||||
}
|
||||
|
||||
$output .= '</span>';
|
||||
$output .= ' <span class="facetwp-star-label"></span>';
|
||||
$output .= ' <span class="facetwp-counter"></span>';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$selected_values = $params['selected_values'];
|
||||
$selected_values = is_array( $selected_values ) ? $selected_values[0] : $selected_values;
|
||||
|
||||
$sql = "
|
||||
SELECT DISTINCT post_id FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE facet_name = '{$facet['name']}' AND facet_value >= '$selected_values'";
|
||||
return $wpdb->get_col( $sql );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output front-end scripts
|
||||
*/
|
||||
function front_scripts() {
|
||||
FWP()->display->json['rating']['& up'] = __( '& up', 'fwp-front' );
|
||||
FWP()->display->json['rating']['Undo'] = __( 'Undo', 'fwp-front' );
|
||||
}
|
||||
}
|
||||
85
wp/wp-content/plugins/facetwp/includes/facets/reset.php
Normal file
85
wp/wp-content/plugins/facetwp/includes/facets/reset.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Reset extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Reset', 'fwp' );
|
||||
$this->fields = [ 'reset_ui', 'reset_text', 'reset_mode', 'reset_facets', 'auto_hide' ];
|
||||
}
|
||||
|
||||
|
||||
function render( $params ) {
|
||||
$facet = $params['facet'];
|
||||
$reset_ui = $facet['reset_ui'];
|
||||
$reset_text = empty( $facet['reset_text'] ) ? __( 'Reset', 'fwp-front' ) : $facet['reset_text'];
|
||||
$reset_text = facetwp_i18n( $reset_text );
|
||||
|
||||
$classes = [ 'facetwp-reset' ];
|
||||
$attrs = '';
|
||||
|
||||
if ( ! FWP()->helper->facet_is( $facet, 'reset_mode', 'off' ) ) {
|
||||
if ( ! empty( $facet['reset_facets'] ) ) {
|
||||
$vals = implode( ',', $facet['reset_facets'] );
|
||||
$attrs = ' data-mode="{mode}" data-values="{vals}"';
|
||||
$attrs = str_replace( '{mode}', $facet['reset_mode'], $attrs );
|
||||
$attrs = str_replace( '{vals}', esc_attr( $vals ), $attrs );
|
||||
}
|
||||
}
|
||||
|
||||
if ( FWP()->helper->facet_is( $facet, 'auto_hide', 'yes' ) ) {
|
||||
$classes[] = 'facetwp-hide-empty';
|
||||
}
|
||||
|
||||
if ( 'button' == $reset_ui ) {
|
||||
$output = '<button class="{classes}"{attrs}>{label}</button>';
|
||||
}
|
||||
else {
|
||||
$output = '<a class="{classes}" href="javascript:;"{attrs}>{label}</a>';
|
||||
}
|
||||
|
||||
$output = str_replace( '{classes}', implode( ' ', $classes ), $output );
|
||||
$output = str_replace( '{label}', esc_attr( $reset_text ), $output );
|
||||
$output = str_replace( '{attrs}', $attrs, $output );
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
function filter_posts( $params ) {
|
||||
return 'continue';
|
||||
}
|
||||
|
||||
|
||||
function register_fields() {
|
||||
return [
|
||||
'reset_ui' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Reset UI', 'fwp' ),
|
||||
'choices' => [
|
||||
'button' => __( 'Button', 'fwp' ),
|
||||
'link' => __( 'Link', 'fwp' )
|
||||
]
|
||||
],
|
||||
'reset_mode' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Include / exclude', 'fwp' ),
|
||||
'notes' => 'Include or exclude certain facets?',
|
||||
'choices' => [
|
||||
'off' => __( 'Reset everything', 'fwp' ),
|
||||
'include' => __( 'Reset only these facets', 'fwp' ),
|
||||
'exclude' => __( 'Reset all except these facets', 'fwp' )
|
||||
]
|
||||
],
|
||||
'reset_facets' => [
|
||||
'label' => '',
|
||||
'html' => '<facet-names :facet="facet" setting="reset_facets"></facet-names>',
|
||||
'show' => "facet.reset_mode != 'off'"
|
||||
],
|
||||
'auto_hide' => [
|
||||
'type' => 'toggle',
|
||||
'label' => __( 'Auto-hide', 'fwp' ),
|
||||
'notes' => 'Hide when no facets have selected values'
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
89
wp/wp-content/plugins/facetwp/includes/facets/search.php
Normal file
89
wp/wp-content/plugins/facetwp/includes/facets/search.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Search extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Search', 'fwp' );
|
||||
$this->fields = [ 'search_engine', 'placeholder', 'auto_refresh' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$facet = $params['facet'];
|
||||
$value = (array) $params['selected_values'];
|
||||
$value = empty( $value ) ? '' : stripslashes( $value[0] );
|
||||
$placeholder = empty( $facet['placeholder'] ) ? __( 'Enter keywords', 'fwp-front' ) : $facet['placeholder'];
|
||||
$placeholder = facetwp_i18n( $placeholder );
|
||||
$output .= '<span class="facetwp-input-wrap">';
|
||||
$output .= '<i class="facetwp-icon"></i>';
|
||||
$output .= '<input type="text" class="facetwp-search" value="' . esc_attr( $value ) . '" placeholder="' . esc_attr( $placeholder ) . '" autocomplete="off" />';
|
||||
$output .= '</span>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
|
||||
$facet = $params['facet'];
|
||||
$selected_values = $params['selected_values'];
|
||||
$selected_values = is_array( $selected_values ) ? $selected_values[0] : $selected_values;
|
||||
|
||||
if ( empty( $selected_values ) ) {
|
||||
return 'continue';
|
||||
}
|
||||
|
||||
// Default WP search
|
||||
$search_args = [
|
||||
's' => $selected_values,
|
||||
'posts_per_page' => 200,
|
||||
'fields' => 'ids',
|
||||
];
|
||||
|
||||
$search_args = apply_filters( 'facetwp_search_query_args', $search_args, $params );
|
||||
|
||||
$query = new WP_Query( $search_args );
|
||||
|
||||
return (array) $query->posts;
|
||||
}
|
||||
|
||||
|
||||
function register_fields() {
|
||||
$engines = apply_filters( 'facetwp_facet_search_engines', [] );
|
||||
$choices = [ '' => __( 'WP Default', 'fwp' ) ];
|
||||
|
||||
foreach ( $engines as $key => $label ) {
|
||||
$choices[ $key ] = $label;
|
||||
}
|
||||
|
||||
return [
|
||||
'search_engine' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Search engine', 'fwp' ),
|
||||
'choices' => $choices
|
||||
],
|
||||
'auto_refresh' => [
|
||||
'type' => 'toggle',
|
||||
'label' => __( 'Auto refresh', 'fwp' ),
|
||||
'notes' => 'Automatically refresh the results while typing?'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Front-end) Attach settings to the AJAX response
|
||||
*/
|
||||
function settings_js( $params ) {
|
||||
$auto_refresh = empty( $params['facet']['auto_refresh'] ) ? 'no' : $params['facet']['auto_refresh'];
|
||||
return [ 'auto_refresh' => $auto_refresh ];
|
||||
}
|
||||
}
|
||||
160
wp/wp-content/plugins/facetwp/includes/facets/slider.php
Normal file
160
wp/wp-content/plugins/facetwp/includes/facets/slider.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Slider extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Slider', 'fwp' );
|
||||
$this->fields = [ 'source_other', 'compare_type', 'prefix', 'suffix',
|
||||
'reset_text', 'slider_format', 'step' ];
|
||||
|
||||
add_filter( 'facetwp_render_output', [ $this, 'maybe_prevent_facet_html' ], 10, 2 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
$facet = $params['facet'];
|
||||
$reset_text = __( 'Reset', 'fwp-front' );
|
||||
|
||||
if ( ! empty( $facet['reset_text'] ) ) {
|
||||
$reset_text = facetwp_i18n( $facet['reset_text'] );
|
||||
}
|
||||
|
||||
$output = '<div class="facetwp-slider-wrap">';
|
||||
$output .= '<div class="facetwp-slider"></div>';
|
||||
$output .= '</div>';
|
||||
$output .= '<span class="facetwp-slider-label"></span>';
|
||||
$output .= '<div><input type="button" class="facetwp-slider-reset" value="' . esc_attr( $reset_text ) . '" /></div>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
return FWP()->helper->facet_types['number_range']->filter_posts( $params );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Front-end) Attach settings to the AJAX response
|
||||
*/
|
||||
function settings_js( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$where_clause = $this->get_where_clause( $facet );
|
||||
$selected_values = $params['selected_values'];
|
||||
|
||||
// Set default slider values
|
||||
$defaults = [
|
||||
'format' => '',
|
||||
'prefix' => '',
|
||||
'suffix' => '',
|
||||
'step' => 1,
|
||||
];
|
||||
$facet = array_merge( $defaults, $facet );
|
||||
|
||||
$sql = "
|
||||
SELECT MIN(facet_value + 0) AS `min`, MAX(facet_display_value + 0) AS `max` FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE facet_name = '{$facet['name']}' AND facet_display_value != '' $where_clause";
|
||||
$row = $wpdb->get_row( $sql );
|
||||
|
||||
$range_min = (float) $row->min;
|
||||
$range_max = (float) $row->max;
|
||||
|
||||
$selected_min = (float) ( $selected_values[0] ?? $range_min );
|
||||
$selected_max = (float) ( $selected_values[1] ?? $range_max );
|
||||
|
||||
return [
|
||||
'range' => [ // outer (bar)
|
||||
'min' => min( $range_min, $selected_min ),
|
||||
'max' => max( $range_max, $selected_max )
|
||||
],
|
||||
'decimal_separator' => FWP()->helper->get_setting( 'decimal_separator' ),
|
||||
'thousands_separator' => FWP()->helper->get_setting( 'thousands_separator' ),
|
||||
'start' => [ $selected_min, $selected_max ], // inner (handles)
|
||||
'format' => $facet['format'],
|
||||
'prefix' => facetwp_i18n( $facet['prefix'] ),
|
||||
'suffix' => facetwp_i18n( $facet['suffix'] ),
|
||||
'step' => $facet['step']
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prevent the slider HTML from refreshing when active
|
||||
* @since 3.8.11
|
||||
*/
|
||||
function maybe_prevent_facet_html( $output, $params ) {
|
||||
if ( ! empty( $output['facets'] && 0 === $params['first_load' ] ) ) {
|
||||
foreach ( FWP()->facet->facets as $name => $facet ) {
|
||||
if ( 'slider' == $facet['type'] && ! empty( $facet['selected_values'] ) ) {
|
||||
unset( $output['facets'][ $name ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output any front-end scripts
|
||||
*/
|
||||
function front_scripts() {
|
||||
FWP()->display->assets['nouislider.css'] = FACETWP_URL . '/assets/vendor/noUiSlider/nouislider.css';
|
||||
FWP()->display->assets['nouislider.js'] = FACETWP_URL . '/assets/vendor/noUiSlider/nouislider.min.js';
|
||||
FWP()->display->assets['nummy.js'] = FACETWP_URL . '/assets/vendor/nummy/nummy.min.js';
|
||||
}
|
||||
|
||||
|
||||
function register_fields() {
|
||||
$thousands = FWP()->helper->get_setting( 'thousands_separator' );
|
||||
$decimal = FWP()->helper->get_setting( 'decimal_separator' );
|
||||
$choices = [];
|
||||
|
||||
if ( '' != $thousands ) {
|
||||
$choices['0,0'] = "5{$thousands}280";
|
||||
$choices['0,0.0'] = "5{$thousands}280{$decimal}4";
|
||||
$choices['0,0.00'] = "5{$thousands}280{$decimal}42";
|
||||
}
|
||||
|
||||
$choices['0'] = '5280';
|
||||
$choices['0.0'] = "5280{$decimal}4";
|
||||
$choices['0.00'] = "5280{$decimal}42";
|
||||
$choices['0a'] = '5k';
|
||||
$choices['0.0a'] = "5{$decimal}3k";
|
||||
$choices['0.00a'] = "5{$decimal}28k";
|
||||
|
||||
return [
|
||||
'prefix' => [
|
||||
'label' => __( 'Prefix', 'fwp' ),
|
||||
'notes' => 'Text that appears before each slider value',
|
||||
],
|
||||
'suffix' => [
|
||||
'label' => __( 'Suffix', 'fwp' ),
|
||||
'notes' => 'Text that appears after each slider value',
|
||||
],
|
||||
'slider_format' => [
|
||||
'type' => 'alias',
|
||||
'items' => [
|
||||
'format' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Format', 'fwp' ),
|
||||
'notes' => 'If the number separators are wrong, change the [Separators] setting in the Settings tab, then save and reload the page',
|
||||
'choices' => $choices
|
||||
]
|
||||
]
|
||||
],
|
||||
'step' => [
|
||||
'label' => __( 'Step', 'fwp' ),
|
||||
'notes' => 'The amount of increase between intervals',
|
||||
'default' => 1
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
226
wp/wp-content/plugins/facetwp/includes/facets/sort.php
Normal file
226
wp/wp-content/plugins/facetwp/includes/facets/sort.php
Normal file
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Sort extends FacetWP_Facet
|
||||
{
|
||||
|
||||
public $sort_options = [];
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Sort', 'fwp' );
|
||||
$this->fields = [ 'sort_default_label', 'sort_options' ];
|
||||
|
||||
add_filter( 'facetwp_filtered_query_args', [ $this, 'apply_sort' ], 1, 2 );
|
||||
add_filter( 'facetwp_render_output', [ $this, 'render_sort_feature' ], 1, 2 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render the sort facet
|
||||
*/
|
||||
function render( $params ) {
|
||||
$facet = $this->parse_sort_facet( $params['facet'] );
|
||||
$selected_values = (array) $params['selected_values'];
|
||||
|
||||
$label = facetwp_i18n( $facet['default_label'] );
|
||||
$output = '<option value="">' . esc_attr( $label ) . '</option>';
|
||||
|
||||
foreach ( $facet['sort_options'] as $key => $choice ) {
|
||||
$label = facetwp_i18n( $choice['label'] );
|
||||
$selected = in_array( $key, $selected_values ) ? ' selected' : '';
|
||||
$output .= '<option value="' . esc_attr( $key ) . '"' . $selected . '>' . esc_attr( $label ) . '</option>';
|
||||
}
|
||||
|
||||
return '<select>' . $output . '</select>';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort facets don't narrow results
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
return 'continue';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register admin settings
|
||||
*/
|
||||
function register_fields() {
|
||||
return [
|
||||
'sort_default_label' => [
|
||||
'type' => 'alias',
|
||||
'items' => [
|
||||
'default_label' => [
|
||||
'label' => __( 'Default label', 'fwp' ),
|
||||
'notes' => 'The sort box placeholder text',
|
||||
'default' => __( 'Sort by', 'fwp' )
|
||||
]
|
||||
]
|
||||
],
|
||||
'sort_options' => [
|
||||
'label' => __( 'Sort options', 'fwp' ),
|
||||
'notes' => 'Define the choices that appear in the sort box',
|
||||
'html' => '<sort-options :facet="facet"></sort-options><input type="hidden" class="facet-sort-options" value="[]" />'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a sort facet's sort options into WP_Query arguments
|
||||
* @since 4.0.8
|
||||
*/
|
||||
function parse_sort_facet( $facet ) {
|
||||
$sort_options = [];
|
||||
|
||||
foreach ( $facet['sort_options'] as $row ) {
|
||||
$parsed = FWP()->builder->parse_query_obj([ 'orderby' => $row['orderby'] ]);
|
||||
|
||||
$sort_options[ $row['name'] ] = [
|
||||
'label' => $row['label'],
|
||||
'query_args' => array_intersect_key( $parsed, [
|
||||
'meta_query' => true,
|
||||
'orderby' => true
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
$sort_options = apply_filters( 'facetwp_facet_sort_options', $sort_options, [
|
||||
'facet' => $facet,
|
||||
'template_name' => FWP()->facet->template['name']
|
||||
]);
|
||||
|
||||
$facet['sort_options'] = $sort_options;
|
||||
|
||||
return $facet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle both sort facets and the (old) sort feature
|
||||
* @since 4.0.6
|
||||
*/
|
||||
function apply_sort( $query_args, $class ) {
|
||||
|
||||
foreach ( $class->facets as $facet ) {
|
||||
if ( 'sort' == $facet['type'] ) {
|
||||
$sort_facet = $this->parse_sort_facet( $facet );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Support the (old) sort feature
|
||||
$sort_value = 'default';
|
||||
$this->sort_options = $this->get_sort_options();
|
||||
|
||||
if ( ! empty( $class->ajax_params['extras']['sort'] ) ) {
|
||||
$sort_value = $class->ajax_params['extras']['sort'];
|
||||
|
||||
if ( ! empty( $this->sort_options[ $sort_value ] ) ) {
|
||||
$args = $this->sort_options[ $sort_value ]['query_args'];
|
||||
$query_args = array_merge( $query_args, $args );
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve relevancy sort
|
||||
$use_relevancy = apply_filters( 'facetwp_use_search_relevancy', true, $class );
|
||||
$is_default_sort = ( 'default' == $sort_value && empty( $class->http_params['get']['orderby'] ) );
|
||||
if ( $class->is_search && $use_relevancy && $is_default_sort && FWP()->is_filtered ) {
|
||||
$query_args['orderby'] = 'post__in';
|
||||
}
|
||||
|
||||
// Support the (new) sort facet
|
||||
if ( ! empty( $sort_facet['selected_values'] ) ) {
|
||||
$chosen = $sort_facet['selected_values'][0];
|
||||
$sort_options = $sort_facet['sort_options'];
|
||||
|
||||
if ( isset( $sort_options[ $chosen ] ) ) {
|
||||
$qa = $sort_options[ $chosen ]['query_args'];
|
||||
|
||||
if ( isset( $qa['meta_query'] ) ) {
|
||||
$meta_query = $query_args['meta_query'] ?? [];
|
||||
$query_args['meta_query'] = array_merge( $meta_query, $qa['meta_query'] );
|
||||
}
|
||||
|
||||
$query_args['orderby'] = $qa['orderby'];
|
||||
}
|
||||
}
|
||||
|
||||
return $query_args;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate choices for the (old) sort feature
|
||||
* @since 4.0.6
|
||||
*/
|
||||
function get_sort_options() {
|
||||
|
||||
$options = [
|
||||
'default' => [
|
||||
'label' => __( 'Sort by', 'fwp-front' ),
|
||||
'query_args' => []
|
||||
],
|
||||
'title_asc' => [
|
||||
'label' => __( 'Title (A-Z)', 'fwp-front' ),
|
||||
'query_args' => [
|
||||
'orderby' => 'title',
|
||||
'order' => 'ASC',
|
||||
]
|
||||
],
|
||||
'title_desc' => [
|
||||
'label' => __( 'Title (Z-A)', 'fwp-front' ),
|
||||
'query_args' => [
|
||||
'orderby' => 'title',
|
||||
'order' => 'DESC',
|
||||
]
|
||||
],
|
||||
'date_desc' => [
|
||||
'label' => __( 'Date (Newest)', 'fwp-front' ),
|
||||
'query_args' => [
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
]
|
||||
],
|
||||
'date_asc' => [
|
||||
'label' => __( 'Date (Oldest)', 'fwp-front' ),
|
||||
'query_args' => [
|
||||
'orderby' => 'date',
|
||||
'order' => 'ASC',
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
return apply_filters( 'facetwp_sort_options', $options, [
|
||||
'template_name' => FWP()->facet->template['name'],
|
||||
] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render the (old) sort feature
|
||||
* @since 4.0.6
|
||||
*/
|
||||
function render_sort_feature( $output, $params ) {
|
||||
$has_sort = isset( $params['extras']['sort'] );
|
||||
$has_choices = isset( $this->sort_options );
|
||||
|
||||
if ( 0 == $params['soft_refresh'] && $has_sort && $has_choices ) {
|
||||
$html = '';
|
||||
|
||||
foreach ( $this->sort_options as $key => $atts ) {
|
||||
$html .= '<option value="' . $key . '">' . $atts['label'] . '</option>';
|
||||
}
|
||||
|
||||
$html = '<select class="facetwp-sort-select">' . $html . '</select>';
|
||||
|
||||
$output['sort'] = apply_filters( 'facetwp_sort_html', $html, [
|
||||
'sort_options' => $this->sort_options,
|
||||
'template_name' => FWP()->facet->template['name'],
|
||||
]);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
43
wp/wp-content/plugins/facetwp/includes/functions.php
Normal file
43
wp/wp-content/plugins/facetwp/includes/functions.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
// DO NOT MODIFY THIS FILE!
|
||||
// Use your theme's functions.php instead
|
||||
|
||||
/**
|
||||
* An alternate to using do_shortcode()
|
||||
*
|
||||
* facetwp_display( 'pager' );
|
||||
* facetwp_display( 'template', 'cars' );
|
||||
* facetwp_display( 'template', 'cars', [ 'static' => true ] );
|
||||
*
|
||||
* @since 1.7.5
|
||||
*/
|
||||
function facetwp_display() {
|
||||
$args = array_replace( [ 'pager', true, [] ], func_get_args() );
|
||||
|
||||
$atts = (array) $args[2];
|
||||
$atts[ $args[0] ] = $args[1];
|
||||
|
||||
return FWP()->display->shortcode( $atts );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allow for translation of dynamic strings
|
||||
* @since 2.1
|
||||
*/
|
||||
function facetwp_i18n( $string ) {
|
||||
return apply_filters( 'facetwp_i18n', $string );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Support SQL modifications
|
||||
* @since 2.7
|
||||
*/
|
||||
function facetwp_sql( $sql, $facet ) {
|
||||
global $wpdb;
|
||||
|
||||
$sql = apply_filters( 'facetwp_wpdb_sql', $sql, $facet );
|
||||
return apply_filters( 'facetwp_wpdb_get_col', $wpdb->get_col( $sql ), $sql, $facet );
|
||||
}
|
||||
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