Merged in feature/plugins-update (pull request #9)

wp plugin updates from pantheon

* wp plugin updates from pantheon
This commit is contained in:
Tony Volpe
2023-12-15 18:08:21 +00:00
parent 28c21bf9b1
commit 779393381f
577 changed files with 154305 additions and 0 deletions

View 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
];
}
}

View 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 );
}
}

View 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 ];
}
}

View 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 '';
}
}

View 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( '&nbsp;&nbsp;', (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 );
}
}

View 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';
}
}

View 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', '&#8249; ' );
$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 );
}
}

View 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
];
}
}

View 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'"
]
];
}
}

View 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;
}

View 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 );
}
}

View 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'] . '">&#9733;</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' );
}
}

View 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'
]
];
}
}

View 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 ];
}
}

View 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
]
];
}
}

View 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;
}
}