=i?o.length:o.indexOf(t)}));return n?c.filter((function(t){return-1!==t})):c}function x(t,e){var n=this;this.$nextTick((function(){return n.$emit(t.toLowerCase(),e)}))}function O(t){var e=this;return function(n){null!==e.realList&&e["onDrag"+t](n),x.call(e,t,n)}}function S(t){return["transition-group","TransitionGroup"].includes(t)}function w(t){if(!t||1!==t.length)return!1;var e=s(t,1),n=e[0].componentOptions;return!!n&&S(n.tag)}function j(t,e,n){return t[n]||(e[n]?e[n]():void 0)}function M(t,e,n){var r=0,o=0,i=j(e,n,"header");i&&(r=i.length,t=t?[].concat(p(i),p(t)):p(i));var c=j(e,n,"footer");return c&&(o=c.length,t=t?[].concat(p(t),p(c)):p(c)),{children:t,headerOffset:r,footerOffset:o}}function C(t,e){var n=null,r=function(t,e){n=b(n,t,e)},o=Object.keys(t).filter((function(t){return"id"===t||t.startsWith("data-")})).reduce((function(e,n){return e[n]=t[n],e}),{});if(r("attrs",o),!e)return n;var i=e.on,c=e.props,u=e.attrs;return r("on",i),r("props",c),Object.assign(n.attrs,u),n}var T=["Start","Add","Remove","Update","End"],_=["Choose","Unchoose","Sort","Filter","Clone"],L=["Move"].concat(T,_).map((function(t){return"on"+t})),I=null,E={options:Object,list:{type:Array,required:!1,default:null},value:{type:Array,required:!1,default:null},noTransitionOnDrag:{type:Boolean,default:!1},clone:{type:Function,default:function(t){return t}},element:{type:String,default:"div"},tag:{type:String,default:null},move:{type:Function,default:null},componentData:{type:Object,required:!1,default:null}},P={name:"draggable",inheritAttrs:!1,props:E,data:function(){return{transitionMode:!1,noneFunctionalComponentMode:!1}},render:function(t){var e=this.$slots.default;this.transitionMode=w(e);var n=M(e,this.$slots,this.$scopedSlots),r=n.children,o=n.headerOffset,i=n.footerOffset;this.headerOffset=o,this.footerOffset=i;var c=C(this.$attrs,this.componentData);return t(this.getTag(),c,r)},created:function(){null!==this.list&&null!==this.value&&g["b"].error("Value and list props are mutually exclusive! Please set one or another."),"div"!==this.element&&g["b"].warn("Element props is deprecated please use tag props instead. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#element-props"),void 0!==this.options&&g["b"].warn("Options props is deprecated, add sortable options directly as vue.draggable item, or use v-bind. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#options-props")},mounted:function(){var t=this;if(this.noneFunctionalComponentMode=this.getTag().toLowerCase()!==this.$el.nodeName.toLowerCase()&&!this.getIsFunctional(),this.noneFunctionalComponentMode&&this.transitionMode)throw new Error("Transition-group inside component is not supported. Please alter tag value or remove transition-group. Current tag value: ".concat(this.getTag()));var e={};T.forEach((function(n){e["on"+n]=O.call(t,n)})),_.forEach((function(n){e["on"+n]=x.bind(t,n)}));var n=Object.keys(this.$attrs).reduce((function(e,n){return e[Object(g["a"])(n)]=t.$attrs[n],e}),{}),r=Object.assign({},this.options,n,e,{onMove:function(e,n){return t.onDragMove(e,n)}});!("draggable"in r)&&(r.draggable=">*"),this._sortable=new v.a(this.rootContainer,r),this.computeIndexes()},beforeDestroy:function(){void 0!==this._sortable&&this._sortable.destroy()},computed:{rootContainer:function(){return this.transitionMode?this.$el.children[0]:this.$el},realList:function(){return this.list?this.list:this.value}},watch:{options:{handler:function(t){this.updateOptions(t)},deep:!0},$attrs:{handler:function(t){this.updateOptions(t)},deep:!0},realList:function(){this.computeIndexes()}},methods:{getIsFunctional:function(){var t=this._vnode.fnOptions;return t&&t.functional},getTag:function(){return this.tag||this.element},updateOptions:function(t){for(var e in t){var n=Object(g["a"])(e);-1===L.indexOf(n)&&this._sortable.option(n,t[e])}},getChildrenNodes:function(){if(this.noneFunctionalComponentMode)return this.$children[0].$slots.default;var t=this.$slots.default;return this.transitionMode?t[0].child.$slots.default:t},computeIndexes:function(){var t=this;this.$nextTick((function(){t.visibleIndexes=y(t.getChildrenNodes(),t.rootContainer.children,t.transitionMode,t.footerOffset)}))},getUnderlyingVm:function(t){var e=m(this.getChildrenNodes()||[],t);if(-1===e)return null;var n=this.realList[e];return{index:e,element:n}},getUnderlyingPotencialDraggableComponent:function(t){var e=t.__vue__;return e&&e.$options&&S(e.$options._componentTag)?e.$parent:!("realList"in e)&&1===e.$children.length&&"realList"in e.$children[0]?e.$children[0]:e},emitChanges:function(t){var e=this;this.$nextTick((function(){e.$emit("change",t)}))},alterList:function(t){if(this.list)t(this.list);else{var e=p(this.value);t(e),this.$emit("input",e)}},spliceList:function(){var t=arguments,e=function(e){return e.splice.apply(e,p(t))};this.alterList(e)},updatePosition:function(t,e){var n=function(n){return n.splice(e,0,n.splice(t,1)[0])};this.alterList(n)},getRelatedContextFromMoveEvent:function(t){var e=t.to,n=t.related,r=this.getUnderlyingPotencialDraggableComponent(e);if(!r)return{component:r};var o=r.realList,i={list:o,component:r};if(e!==n&&o&&r.getUnderlyingVm){var c=r.getUnderlyingVm(n);if(c)return Object.assign(c,i)}return i},getVmIndex:function(t){var e=this.visibleIndexes,n=e.length;return t>n-1?n:e[t]},getComponent:function(){return this.$slots.default[0].componentInstance},resetTransitionData:function(t){if(this.noTransitionOnDrag&&this.transitionMode){var e=this.getChildrenNodes();e[t].data=null;var n=this.getComponent();n.children=[],n.kept=void 0}},onDragStart:function(t){this.context=this.getUnderlyingVm(t.item),t.item._underlying_vm_=this.clone(this.context.element),I=t.item},onDragAdd:function(t){var e=t.item._underlying_vm_;if(void 0!==e){Object(g["d"])(t.item);var n=this.getVmIndex(t.newIndex);this.spliceList(n,0,e),this.computeIndexes();var r={element:e,newIndex:n};this.emitChanges({added:r})}},onDragRemove:function(t){if(Object(g["c"])(this.rootContainer,t.item,t.oldIndex),"clone"!==t.pullMode){var e=this.context.index;this.spliceList(e,1);var n={element:this.context.element,oldIndex:e};this.resetTransitionData(e),this.emitChanges({removed:n})}else Object(g["d"])(t.clone)},onDragUpdate:function(t){Object(g["d"])(t.item),Object(g["c"])(t.from,t.item,t.oldIndex);var e=this.context.index,n=this.getVmIndex(t.newIndex);this.updatePosition(e,n);var r={element:this.context.element,oldIndex:e,newIndex:n};this.emitChanges({moved:r})},updateProperty:function(t,e){t.hasOwnProperty(e)&&(t[e]+=this.headerOffset)},computeFutureIndex:function(t,e){if(!t.element)return 0;var n=p(e.to.children).filter((function(t){return"none"!==t.style["display"]})),r=n.indexOf(e.related),o=t.component.getVmIndex(r),i=-1!==n.indexOf(I);return i||!e.willInsertAfter?o:o+1},onDragMove:function(t,e){var n=this.move;if(!n||!this.realList)return!0;var r=this.getRelatedContextFromMoveEvent(t),o=this.context,i=this.computeFutureIndex(r,t);Object.assign(o,{futureIndex:i});var c=Object.assign({},t,{relatedContext:r,draggedContext:o});return n(c,e)},onDragEnd:function(){this.computeIndexes(),I=null}}};"undefined"!==typeof window&&"Vue"in window&&window.Vue.component("draggable",P);var $=P;e["default"]=$}})["default"]}));
diff --git a/wp-content/plugins/facetwp/includes/api/fetch.php b/wp-content/plugins/facetwp/includes/api/fetch.php
new file mode 100644
index 000000000..07334663a
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/api/fetch.php
@@ -0,0 +1,180 @@
+ '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 );
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/api/refresh.php b/wp-content/plugins/facetwp/includes/api/refresh.php
new file mode 100644
index 000000000..bbc08212d
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/api/refresh.php
@@ -0,0 +1,27 @@
+ '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 [];
+}
diff --git a/wp-content/plugins/facetwp/includes/class-ajax.php b/wp-content/plugins/facetwp/includes/class-ajax.php
new file mode 100755
index 000000000..f295bc0a8
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/class-ajax.php
@@ -0,0 +1,326 @@
+$action();
+ }
+
+ // Authenticated
+ elseif ( current_user_can( apply_filters( 'facetwp_admin_settings_capability', '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 ) {
+
+ $types = FWP()->helper->get_indexable_types();
+
+ $response = [
+ 'code' => 'success',
+ 'message' => implode( ', ', $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" );
+ $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}facetwp_temp" );
+ delete_option( 'facetwp_version' );
+ delete_option( 'facetwp_indexing' );
+ delete_option( 'facetwp_indexing_data' );
+
+ $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 ' [' . __( 'Imported', 'fwp' ) . '] ' . implode( ', ', $status['imported'] );
+ }
+ if ( ! empty( $status['skipped'] ) ) {
+ echo ' [' . __( 'Skipped', 'fwp' ) . '] ' . 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;
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/class-builder.php b/wp-content/plugins/facetwp/includes/class-builder.php
new file mode 100644
index 000000000..7a634cb11
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/class-builder.php
@@ -0,0 +1,899 @@
+custom_css = "@media (max-width: 480px) { \n body .facetwp-template .fwpl-layout, \n body .facetwp-template-static .fwpl-layout { grid-template-columns: 1fr; } \n} \n";
+
+ $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'
+ ],
+ '.fwpl-btn' => [
+ 'text-decoration' => 'none'
+ ],
+ $selector . ' .fwpl-result' => $this->build_styles( $settings )
+ ];
+
+ $classes = $this->get_classes( 'fwpl-layout', $settings );
+
+ $output = '';
+
+ 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 .= '
';
+
+ foreach ( $layout['items'] as $row ) {
+ $output .= $this->render_row( $row );
+ }
+
+ $output .= '
';
+
+ $output = $this->parse_dynamic_tags( $output, $params );
+
+ endwhile;
+ }
+ else {
+ $no_results_text = $settings['no_results_text'] ?? '';
+ $output .= do_shortcode( $no_results_text );
+ }
+
+ $output .= '
';
+
+ $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 = '';
+
+ foreach ( $row['items'] as $col ) {
+ $output .= $this->render_col( $col );
+ }
+
+ $output .= '
';
+
+ 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 = '';
+
+ foreach ( $col['items'] as $item ) {
+ if ( 'row' == $item['type'] ) {
+ $output .= $this->render_row( $item );
+ }
+ elseif ( 'item' == $item['type'] ) {
+ $output .= $this->render_item( $item );
+ }
+ }
+
+ $output .= '
';
+
+ 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 . ' .fwpl-btn' : $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/attribute_' ) && 'product' == get_post_type( $post->ID ) ) {
+ $value = '';
+ $product = wc_get_product( $post->ID );
+ $attr = substr( $source, 13 );
+ $attributes = array_filter( $product->get_attributes(), 'wc_attributes_array_filter_visible' );
+ if ( isset( $attributes[ $attr ] ) ) {
+ $attribute = $attributes[ $attr ];
+ if ( $attribute->is_taxonomy() ) {
+ $attribute_taxonomy = $attribute->get_taxonomy_object();
+ $attribute_values = wc_get_product_terms( $product->get_id(), $attribute->get_name(), [ 'fields' => 'all' ] );
+
+ foreach ( $attribute_values as $attribute_value ) {
+ $value_name = esc_html( $attribute_value->name );
+
+ if ( $attribute_taxonomy->attribute_public ) {
+ $values[] = '' . $value_name . '';
+ }
+ else {
+ $values[] = $value_name;
+ }
+ }
+ }
+ else {
+ $values = $attribute->get_options();
+
+ foreach ( $values as &$value ) {
+ $value = make_clickable( esc_html( $value ) );
+ }
+ }
+
+ $value = implode( ", ", $values );
+ }
+ }
+ 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[] = '' . $term . '';
+ }
+ }
+
+ $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 ) {
+ $settings['link']['class'] = 'fwpl-btn';
+ $value = $this->linkify( facetwp_i18n( $settings['button_text'] ), $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 = '' . $value . '
';
+ }
+
+ 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'] ?? '';
+ $class = $link_data['class'] ?? '';
+ $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 . '"';
+ }
+
+ if ( ! empty( $class ) ) {
+ $class = ' class="' . $class . '"';
+ }
+
+ $value = '' . $value . '';
+ }
+
+ return $value;
+ }
+
+
+ /**
+ * Turn the CSS array into valid CSS
+ * @since 3.2.0
+ */
+ function render_css() {
+ $output = "\n\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;
+ }
+
+ if ( 'text-decoration' === $prop && 'none' === $value ) {
+ $return = true;
+ }
+
+ 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];
+ }
+
+ /**
+ * Initialize CodeMirror for Listing Builder editors
+ * @since 4.3.2
+ */
+ function initialize_builder_editor( $hook ) {
+ if ( 'settings_page_facetwp' == $hook ) {
+ $fwp_editor_settings = wp_enqueue_code_editor( array( 'type' => 'php' ) );
+ wp_localize_script( 'jquery', 'fwp_editor_settings', $fwp_editor_settings );
+ }
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/class-diff.php b/wp-content/plugins/facetwp/includes/class-diff.php
new file mode 100644
index 000000000..1f16795e1
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/class-diff.php
@@ -0,0 +1,73 @@
+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;
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/class-display.php b/wp-content/plugins/facetwp/includes/class-display.php
new file mode 100644
index 000000000..e7f26f862
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/class-display.php
@@ -0,0 +1,304 @@
+get( 'facetwp' ) && did_action( 'wp_head' ) ) {
+ echo "\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 = '';
+
+ // 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;
+
+ $this->load_assets = true;
+ }
+
+ $output = '{html}
';
+ $output = str_replace( '{class}', $class_name, $output );
+ $output = str_replace( '{name}', $atts['template'], $output );
+ $output = str_replace( '{html}', $html, $output );
+ }
+ }
+ elseif ( isset( $atts['sort'] ) ) {
+ $this->active_extras['sort'] = true;
+ $output = '';
+ }
+ elseif ( isset( $atts['selections'] ) ) {
+ $output = '';
+ }
+ elseif ( isset( $atts['counts'] ) ) {
+ $this->active_extras['counts'] = true;
+ $output = '';
+ }
+ elseif ( isset( $atts['pager'] ) ) {
+ $this->active_extras['pager'] = true;
+ $output = '';
+ }
+ elseif ( isset( $atts['per_page'] ) ) {
+ $this->active_extras['per_page'] = true;
+ $output = '';
+ }
+
+ $output = apply_filters( 'facetwp_shortcode_html', $output, $atts );
+
+ return $output;
+ }
+
+ /**
+ * get google api key from GMAPS_API_KEY, facetwp_gmaps_api_key filter, or gmaps_api_key setting
+ * @since 4.4
+ * */
+ function get_gmaps_api_key() {
+
+ // 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;
+
+ // API key hook
+ return apply_filters( 'facetwp_gmaps_api_key', $api_key );
+
+ }
+
+ /**
+ * backwards compatibility for params added in facetwp_gmaps_url filter
+ * @since 4.4
+ */
+ function gmaps_params( $params ) {
+ if ( has_filter( 'facetwp_gmaps_url' ) ) {
+ $gmaps_url = apply_filters( 'facetwp_gmaps_url', '//maps.googleapis.com/maps/api/js?libraries=places' ); // old url
+ parse_str( parse_url( $gmaps_url, PHP_URL_QUERY ), $query_params ); // get array of params
+ $query_params = array_diff_key( $query_params, [ 'libraries' => '', 'key' => '', 'callback' => '' ] ); // remove unneeded params
+ $params = array_merge( $params, $query_params );
+ }
+ return $params;
+ }
+
+
+ /**
+ * 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();
+
+ if ( apply_filters( 'facetwp_load_gmaps', false ) ) {
+
+ // remove non-async gmaps
+ add_filter( 'facetwp_assets', function( $assets ) {
+ unset( $assets['gmaps'] );
+ return $assets;
+ });
+
+ $params = [
+ 'key' => $this->get_gmaps_api_key(),
+ 'v' => 'quarterly',
+ ];
+ $params_string = '';
+ foreach ( apply_filters( 'facetwp_gmaps_params', $params ) AS $param => $val ) {
+ $params_string .= $param . ': "' . esc_attr($val) . '",';
+ }
+?>
+
+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 ? '' : '';
+ $html = apply_filters( 'facetwp_asset_html', $html, $url );
+ echo str_replace( '{url}', $url, $html ) . "\n";
+ }
+
+ echo $inline_scripts;
+
+ do_action( 'facetwp_scripts' );
+?>
+
+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 *
+ * @since 4.4 Added Map type facet
+ */
+ 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',
+ 'map' => 'Facetwp_Facet_Map',
+ '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 ) ) ) {
+ $defaults = $this->facet_types[ $facet['type'] ]->field_defaults ?? [];
+ $facet = array_merge( $defaults, $facet );
+ $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;
+ }
+
+
+ /**
+ * Fetch facets by data source type (prefix, ie tax, cf, acf ...)
+ * @param string $value
+ * @return array
+ * @since 4.4
+ */
+ function get_facets_by_datasource_type( $value ) {
+ $matches = [];
+
+ foreach ( $this->get_facets() as $facet ) {
+ if ( isset( $facet[ 'source' ] ) && false !== strpos( $facet['source'], $value ) ) {
+ $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 {
+ // Use mysqli_real_escape_string if WP 6.4+ or $wpdb->use_mysqli is true
+ // The $wpdb->use_mysqli property was removed in WP 6.4
+ // Since the mysqli driver is now required
+ if ( ! isset( $wpdb->use_mysqli ) || ( $wpdb->dbh && $wpdb->use_mysqli ) ) {
+ $output = mysqli_real_escape_string( $wpdb->dbh, $input );
+ }
+ else {
+ $output = addslashes( $input );
+ }
+ }
+
+ return $output;
+ }
+
+
+ /**
+ * Escape output data
+ * @return mixed the escaped value(s)
+ * @since 4.2.0
+ */
+ function escape( $input ) {
+ if ( is_array( $input ) ) {
+ $output = [];
+
+ foreach ( $input as $key => $val ) {
+ $output[ $key ] = $this->escape( $val );
+ }
+ }
+ else {
+ $output = htmlspecialchars( $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() {
+ 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;
+ }
+
+ return $output;
+ }
+
+ /**
+ * Get indexable post types
+ */
+ function get_indexable_types() {
+
+ $args = FWP()->indexer->get_query_args();
+ $types = (array) ( $args['post_type'] ?? 'post' );
+ $statuses = (array) ( $args['post_status'] ?? 'publish' );
+
+ if ( ! in_array( 'inherit', $statuses ) && in_array( 'attachment', $types ) ) {
+ $types = array_values( array_diff( $types, [ 'attachment' ] ) );
+ }
+
+ sort( $types );
+
+ return $types;
+
+ }
+
+ /**
+ * 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;
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/class-indexer.php b/wp-content/plugins/facetwp/includes/class-indexer.php
new file mode 100644
index 000000000..44af6b12c
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/class-indexer.php
@@ -0,0 +1,721 @@
+set_table( 'auto' );
+
+ add_action( 'facetwp_indexer_cron', [ $this, 'get_progress' ] );
+
+ $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_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;
+ }
+
+ // late check to disable indexing after __construct is already loaded
+ if ( !apply_filters( 'facetwp_indexer_is_enabled', true ) ) {
+ 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 ) {
+ // late check to disable indexing after __construct is already loaded
+ if ( ! $this->is_saving && apply_filters( 'facetwp_indexer_is_enabled', true ) ) {
+ $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['facetwp_offset'] ?? 0 );
+ $attempt = (int) ( $_POST['facetwp_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_indexing_data', '' );
+ 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_indexing_data', 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_indexing_data', '', 'no' );
+ update_option( 'facetwp_indexing', '', 'no' );
+
+ $this->manage_temp_table( 'replace' );
+ $this->manage_temp_table( 'delete' );
+ }
+
+ do_action( 'facetwp_indexer_complete' );
+ }
+
+
+ /**
+ * Get the array of indexer query args
+ * @since 4.1.8
+ */
+ function get_query_args( $post_id = false ) {
+ $post_types = get_post_types( [
+ 'exclude_from_search' => false
+ ] );
+
+ $args = [
+ 'post_type' => $post_types,
+ 'post_status' => 'publish',
+ 'posts_per_page' => -1,
+ 'fields' => 'ids',
+ 'orderby' => 'ID',
+ 'cache_results' => false,
+ 'no_found_rows' => true,
+ 'ignore_custom_sort' => true,
+ 'suppress_filters' => true
+ ];
+
+ if ( is_int( $post_id ) ) {
+ $args['p'] = $post_id;
+ $args['posts_per_page'] = 1;
+ }
+
+ return apply_filters( 'facetwp_indexer_query_args', $args );
+ }
+
+
+ /**
+ * Get an array of post IDs to index
+ * @since 3.6.8
+ */
+ function get_post_ids_to_index( $post_id = false ) {
+ $args = $this->get_query_args( $post_id );
+ $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',
+ 'facetwp_offset' => $num_indexed,
+ 'facetwp_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_indexing_data' );
+
+ 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 );
+ if ( empty( $val ) ) {
+ continue;
+ }
+ $val_encoded = htmlentities( $val );
+ $val_decoded = html_entity_decode( $val );
+ $values[ $val ] = true;
+ $values[ $val_encoded ] = true;
+ $values[ $val_decoded ] = true;
+ }
+
+ if ( ! empty( $values) ) {
+ $output[ $name ] = [ 'type' => $type, 'values' => array_keys( $values ) ];
+ }
+ }
+ }
+
+ $this->modifiers = $output;
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/class-init.php b/wp-content/plugins/facetwp/includes/class-init.php
new file mode 100644
index 000000000..d3d41b76d
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/class-init.php
@@ -0,0 +1,223 @@
+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', apply_filters( 'facetwp_admin_settings_capability', 'manage_options' ), 'facetwp', [ $this, 'settings_page' ] );
+ }
+
+
+ /**
+ * Notify users to install necessary integrations
+ */
+ function admin_notices() {
+ if ( apply_filters( 'facetwp_dismiss_notices', false ) ) {
+ return;
+ }
+
+ $reqs = [
+ 'Beaver Builder' => [
+ 'is_active' => class_exists( 'FLBuilderLoader' ),
+ 'addon' => 'facetwp-beaver-builder/facetwp-beaver-builder.php',
+ 'slug' => 'beaver-builder'
+ ],
+ 'Bricks' => [
+ 'is_active' => function_exists( 'bricks_is_builder' ),
+ 'addon' => 'facetwp-bricks/facetwp-bricks.php',
+ 'slug' => 'bricks'
+ ],
+ 'Elementor' => [
+ 'is_active' => defined( 'ELEMENTOR_VERSION' ),
+ 'addon' => 'facetwp-elementor/facetwp-elementor.php',
+ 'slug' => 'elementor'
+ ],
+ 'Multilingual (Polylang)' => [
+ 'is_active' => function_exists( 'pll_register_string' ),
+ 'addon' => 'facetwp-i18n/facetwp-i18n.php',
+ 'slug' => 'multilingual'
+ ],
+ 'Relevanssi' => [
+ 'is_active' => function_exists( 'relevanssi_search' ),
+ 'addon' => 'facetwp-relevanssi/facetwp-relevanssi.php',
+ 'slug' => 'relevanssi'
+ ],
+ 'Recipes (Tasty Recipes)' => [
+ 'is_active' => function_exists( 'Tasty_Recipes' ),
+ 'addon' => 'facetwp-recipes/facetwp-recipes.php',
+ 'slug' => 'recipes'
+ ],
+ 'Multilingual (WPML)' => [
+ 'is_active' => defined( 'ICL_SITEPRESS_VERSION' ),
+ 'addon' => 'facetwp-i18n/facetwp-i18n.php',
+ 'slug' => 'multilingual'
+ ],
+ 'Recipes (WP Recipe Maker)' => [
+ 'is_active' => function_exists( 'run_wp_recipe_maker' ),
+ 'addon' => 'facetwp-recipes/facetwp-recipes.php',
+ 'slug' => 'recipes'
+ ]
+ ];
+
+ $needed = [];
+
+ foreach ( $reqs as $name => $req ) {
+ if ( $req['is_active'] && ! is_plugin_active( $req['addon'] ) ) {
+ $needed[] = sprintf( '%s', $req['slug'], $name );
+ }
+ }
+
+ if ( ! empty( $needed ) ) {
+ echo 'Please install these FacetWP integration add-ons (then re-index): ' . implode( ', ', $needed ) . '
';
+ }
+ }
+
+
+ /**
+ * 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 = '' . __( 'Settings', 'fwp' ) . '';
+ 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();
diff --git a/wp-content/plugins/facetwp/includes/class-overrides.php b/wp-content/plugins/facetwp/includes/class-overrides.php
new file mode 100644
index 000000000..f97aa7066
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/class-overrides.php
@@ -0,0 +1,116 @@
+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'] = apply_filters( 'facetwp_index_source_other_value', $rows[0]['facet_display_value'] ?? $params['facet_display_value'], $params );
+ }
+
+ 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;
+ }
+
+ /**
+ * Disable indexer
+ */
+ function disable_indexer($enabled) {
+
+ if ('no' == FWP()->helper->get_setting('enable_indexer', 'yes')) {
+ return false; // enable indexer setting
+ }
+
+ return $enabled;
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/class-renderer.php b/wp-content/plugins/facetwp/includes/class-renderer.php
new file mode 100644
index 000000000..141e2e1a3
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/class-renderer.php
@@ -0,0 +1,689 @@
+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';
+
+ // Fix operator for non-multiselect
+ $ui_type = empty( $facet['ui_type'] ) ? $facet['type'] : $facet['ui_type'];
+ if ( 'fselect' == $ui_type ) {
+ $multi = $facet['multiple'] ?? 'no';
+ $facet['operator'] = ( 'no' == $multi ) ? 'or' : $facet['operator'];
+ } else if ( in_array( $ui_type, [ 'radio', 'dropdown' ] ) ) {
+ $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 || FWP()->is_modified ) {
+ $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();
+ } else {
+ $output['settings']['debug'] = "Enable debug mode in [Settings > FacetWP > Settings]";
+ }
+
+ // 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_rows_unfiltered' => (int) count( FWP()->unfiltered_post_ids ),
+ '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 original post IDs (before facet filtering is applied)
+ 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 final post IDs (after facet filtering has been applied)
+ FWP()->filtered_post_ids = $post_ids;
+
+ // Have any facets applied changes?
+ FWP()->is_filtered = ( FWP()->unfiltered_post_ids !== $post_ids );
+
+ // Have any hooks modified the unfiltered post IDs?
+ FWP()->is_modified = ( FWP()->unfiltered_post_ids !== $query->posts );
+
+ // 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(),
+ 'post_types' => FWP()->helper->get_indexable_types()
+ ];
+
+ // 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 = [];
+
+ $match_tags = apply_filters( 'facetwp_debug_hooks', [
+ 'pre_get_posts',
+ 'posts_results'
+ ]);
+
+ foreach ( $GLOBALS['wp_filter'] as $tag => $hook_data ) {
+ if ( 0 === strpos( $tag, 'facetwp' ) || in_array( $tag, $match_tags ) ) {
+ 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' ) || false !== strpos( $tag, 'facetwp' ) ) {
+ $relevant_hooks[ $tag ][] = $filename . ':' . $ref->getStartLine();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return $relevant_hooks;
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/class-request.php b/wp-content/plugins/facetwp/includes/class-request.php
new file mode 100644
index 000000000..0df9d08ff
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/class-request.php
@@ -0,0 +1,387 @@
+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 same query
+ // We're using "==", which doesn't care if assoc order is different
+ 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
+ $is_main_query = ( doing_filter( 'get_the_excerpt' ) ) ? false : $is_main_query; // skip for excerpts
+ 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 contents
+ else {
+ preg_match( "/(.*?)<\/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 );
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/class-settings.php b/wp-content/plugins/facetwp/includes/class-settings.php
new file mode 100755
index 000000000..79350bcd9
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/class-settings.php
@@ -0,0 +1,742 @@
+ [
+ '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' )
+ ],
+ 'gmaps_api_key_test' => [
+ 'label' => __( 'Test Google APIs', 'fwp' ),
+ 'notes' => 'Test if the required Google API\'s are connected:
1. Maps JavaScript API: required for the Map / Proximity facets to work.
2. Geocoding API: required for the Proximity facet\'s "Locate me" button.
3. Places API (Legacy): required for the Legacy Proximity facet\'s autocomplete function (with the "Use Legacy Proximity facet" setting enabled).
4. Places API (New): required for the new Proximity facet\'s autocomplete function (with the "Use Legacy Proximity facet" setting disabled).
Note: changes in the Google Cloud Console settings may take up to five minutes to take effect. Clear your browser cache before testing changed settings.',
+ 'html' => $this->get_setting_html( 'gmaps_api_key_test' )
+ ],
+ 'places_version' => [
+ 'label' => __( 'Use Legacy Proximity facet', 'fwp' ),
+ 'notes' => 'Enable to use the Legacy Proximity facet type. This legacy facet type uses Google\'s legacy "Places" API, instead of the "Places (New)" API. Important: make sure the correct Places API is enabled in your Google Cloud Console for your project: enable "Places" API for the Legacy Proximity facet and "Places (New)" for the normal Proximity facet. ',
+ 'html' => $this->get_setting_html( 'places_version', 'toggle', [
+ 'true_value' => 'places-service',
+ 'false_value' => 'place-class'
+ ]),
+ 'show' => '"" != app.settings.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',
+ 'message' => 'Debug Mode exposes some of your settings and can influence loading speeds.
Make sure to disable it when not needed for troubleshooting.'
+ ]),
+ ],
+ 'enable_indexer' => [
+ 'label' => __( 'Enable automatic indexing', 'fwp' ),
+ 'notes' => 'Disable to prevent editing posts and terms from updating the indexing table.',
+ 'html' => $this->get_setting_html( 'enable_indexer', 'toggle', [
+ 'true_value' => 'yes',
+ 'false_value' => 'no'
+ ])
+ ]
+ ]
+ ],
+ '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 ( $this-> get_gmaps_key_other() ) {
+ unset( $defaults['general']['fields']['places_version']['show'] );
+ }
+
+ 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 );
+ }
+
+
+
+ /**
+ * Get gmaps api key if set from filter or constant
+ * @since 4.4
+ */
+ function get_gmaps_key_other() {
+
+ $params = apply_filters( 'facetwp_gmaps_params', [] );
+ if ( !empty( $params['key'] ) ) {
+ return $params['key'];
+ }
+
+ return apply_filters( 'facetwp_gmaps_api_key', defined( 'GMAPS_API_KEY' ) ? GMAPS_API_KEY : '' );
+ }
+
+
+ /**
+ * 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' ),
+ 'show' => "facet.ui_type != 'checkboxes'"
+ ],
+ 'placeholder' => [
+ 'label' => __( 'Placeholder text', 'fwp' )
+ ],
+ 'parent_term' => [
+ 'label' => __( 'Parent term', 'fwp' ),
+ 'notes' => 'To show only child terms, enter the parent term ID. 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' && facet.ui_type != 'radio'"
+ ],
+ 'show_expanded' => [
+ 'type' => 'toggle',
+ 'label' => __( 'Show expanded', 'fwp' ),
+ 'notes' => 'Should child terms be visible by default?',
+ 'show' => "facet.hierarchical == 'yes' && !['radio','fselect','dropdown'].includes(facet.ui_type)"
+ ],
+ 'multiple' => [
+ 'type' => 'toggle',
+ 'label' => __( 'Multi-select', 'fwp' ),
+ 'notes' => 'Allow multiple selections?',
+ 'show' => "!['radio','checkboxes', 'dropdown'].includes(facet.ui_type)"
+ ],
+ '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' && facet.orderby != 'count'"
+ ]
+ ]
+ ],
+ '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' ),
+ ],
+ 'show' => "facet.ui_type == 'checkboxes' || facet.multiple == 'yes' || facet.type == 'checkboxes' || facet.type == 'color'"
+ ],
+ 'orderby' => [
+ 'type' => 'select',
+ 'label' => __( 'Sort by', 'fwp' ),
+ 'default' => 'count',
+ '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' && !['radio','fselect', 'dropdown'].includes(facet.ui_type)"
+ ],
+ 'source_other' => [
+ 'label' => __( 'Other data source', 'fwp' ),
+ 'notes' => 'Use a separate value for the upper limit?',
+ 'html' => ''
+ ],
+ 'compare_type' => [
+ 'type' => 'select',
+ 'label' => __( 'Compare type', 'fwp' ),
+ 'notes' => "Basic - entered range surrounds the post's range
Enclose - entered range is fully inside the post's range
Intersect - entered range overlaps the post's range
When in doubt, choose Basic",
+ 'choices' => [
+ '' => __( 'Basic', 'fwp' ),
+ 'enclose' => __( 'Enclose', 'fwp' ),
+ 'intersect' => __( 'Intersect', 'fwp' )
+ ],
+ 'show' => "'undefined' != typeof facet.source_other && facet.source_other != ''"
+ ],
+ 'ui_type' => [
+ 'label' => __( 'UI type', 'fwp' ),
+ 'type' => 'select',
+ 'choices' => [
+ 'checkboxes' => __( 'Checkboxes', 'fwp' ),
+ 'radio' => __( 'Radio', 'fwp' ),
+ 'dropdown' => __( 'Dropdown', 'fwp' ),
+ 'fselect' => __( 'fSelect', 'fwp' )
+ ]
+ ],
+ 'ui_ghosts' => [
+ 'type' => 'toggle',
+ 'label' => __( 'Show ghosts', 'fwp' ),
+ 'notes' => 'Show choices that would return zero results?'
+ ],
+ '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'];
+ $checked = ( isset( $field['default'] ) && 'checked' == $field['default'] ) ? ' checked="checked"' : '';
+
+ if ( isset( $field['notes'] ) ) {
+ $label = '';
+ }
+
+ ob_start();
+
+ if ( isset( $field['html'] ) ) {
+ echo $field['html'];
+ }
+ elseif ( 'text' == $type ) {
+?>
+ />
+
+
+
+
+
+
+
+
+
+
+ />
+
+ get_activation_status(); ?>
+
+
+
+ get_gmaps_key_other() ? ' disabled placeholder="'.$this->get_gmaps_key_other().'"' : ''; ?> />
+
+ get_gmaps_key_other() ? 'The API key is set with a hook or in wp-config.php
' : ''; ?>
+
+
+
+ get_gmaps_key_other() ) ) : ?>
+
+ get_gmaps_key_other(); ?>')" class="apitestcheck btn-normal" >
+ get_gmaps_key_other(); ?>')" class="apitestcheck btn-normal" >
+ get_gmaps_key_other(); ?>')" class="apitestcheck btn-normal" v-show="app.settings.places_version == 'places-service'">
+ get_gmaps_key_other(); ?>')" class="apitestcheck btn-normal" >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Thousands:
+
+ Decimal:
+
+
+
+
+
+ Select: | | |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+helper->settings;
+
+ foreach ( $settings['facets'] as $facet ) {
+ $export['facet-' . $facet['name']] = 'Facet - ' . $facet['label'];
+ }
+
+ foreach ( $settings['templates'] as $template ) {
+ $export['template-' . $template['name']] = 'Listing - '. $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 = '';
+ $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;
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/class-updater.php b/wp-content/plugins/facetwp/includes/class-updater.php
new file mode 100644
index 000000000..2121b8fca
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/class-updater.php
@@ -0,0 +1,174 @@
+ $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();
+ $force_check = ! empty( $_GET['force-check'] ); // 'Check again' link on Dashboard > Updates page
+
+ if ( empty( $response ) || $ts + 14400 < $now || $force_check ) {
+
+ $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 '
' . sprintf(
+ __( 'Please renew your license 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 '
' . __( 'Please activate your FacetWP license for automatic updates.', 'fwp' );
+ }
+ }
+ }
+}
+
+new FacetWP_Updater();
diff --git a/wp-content/plugins/facetwp/includes/class-upgrade.php b/wp-content/plugins/facetwp/includes/class-upgrade.php
new file mode 100644
index 000000000..c2b4be98c
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/class-upgrade.php
@@ -0,0 +1,142 @@
+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' );
+
+ $changed = false;
+ $settings = get_option( 'facetwp_settings' );
+ $settings = json_decode( $settings, true );
+
+ 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'] );
+
+ foreach ( $settings['facets'] as $key => $facet ) {
+ if ( 'search' == $facet['type'] && in_array( $facet['search_engine'], $engines ) ) {
+ $settings['facets'][ $key ]['search_engine'] = 'swp_' . $facet['search_engine'];
+ $changed = true;
+ }
+ }
+ }
+ }
+
+ 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', '<' ) ) {
+ if ( ! isset( $settings['settings']['prefix'] ) ) {
+ $settings['settings']['prefix'] = 'fwp_';
+ $changed = true;
+ }
+ }
+
+ if ( version_compare( $this->last_version, '4.1.8', '<' ) ) {
+ foreach ( $settings['facets'] as $key => $facet ) {
+ if ( 'hierarchy' == $facet['type'] ) {
+ $settings['facets'][ $key ]['soft_limit'] = $settings['facets'][ $key ]['count'];
+ unset( $settings['facets'][ $key ]['count'] );
+ $changed = true;
+ }
+ }
+ }
+
+ if ( version_compare( $this->last_version, '4.3.4', '<' ) ) {
+ if ( ! isset( $settings['settings']['enable_indexer'] ) ) {
+ $settings['settings']['enable_indexer'] = 'yes';
+ $changed = true;
+ }
+ }
+
+ if ( version_compare( $this->last_version, '4.4', '<' ) ) {
+ if ( ! isset( $settings['settings']['gmaps_api_key'] ) ) {
+ $settings['settings']['gmaps_api_key'] = '';
+ $changed = true;
+ }
+
+ if ( ! isset( $settings['settings']['places_version'] ) ) {
+ $api_key = defined( 'GMAPS_API_KEY' ) ? GMAPS_API_KEY : $settings['settings']['gmaps_api_key'];
+ $is_legacy = apply_filters( 'facetwp_gmaps_api_key', $api_key );
+
+ $settings['settings']['places_version'] = $is_legacy ? 'places-service' : 'place-class';
+ $changed = true;
+ }
+ }
+
+ if ( $changed ) {
+ update_option( 'facetwp_settings', json_encode( $settings ) );
+ }
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/autocomplete.php b/wp-content/plugins/facetwp/includes/facets/autocomplete.php
new file mode 100644
index 000000000..5f22975b2
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/autocomplete.php
@@ -0,0 +1,170 @@
+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 .= '';
+ $output .= '';
+ 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_value, 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, ARRAY_A );
+
+ foreach ( $results as $row ) {
+ $label = $row['facet_display_value'];
+
+ $output[] = [
+ 'value' => $label,
+ 'label' => apply_filters( 'facetwp_facet_display_value', $label, [
+ 'selected' => false,
+ 'facet' => FWP()->helper->get_facet_by_name( $facet_name ),
+ 'row' => $row
+ ])
+ ];
+ }
+ }
+
+ 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
+ ];
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/base.php b/wp-content/plugins/facetwp/includes/facets/base.php
new file mode 100644
index 000000000..91e0e6a40
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/base.php
@@ -0,0 +1,110 @@
+ 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, $field = 'count' ) {
+ $count = $facet[ $field ] ?? $default;
+
+ if ( '-1' == $count ) {
+ return 1000;
+ }
+ elseif ( ctype_digit( "$count" ) ) { // ctype_digit expects input string
+ 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 );
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/checkboxes.php b/wp-content/plugins/facetwp/includes/facets/checkboxes.php
new file mode 100644
index 000000000..d50451f1b
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/checkboxes.php
@@ -0,0 +1,258 @@
+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 .= '';
+ }
+ $output .= $this->render_choice( $row, $params );
+ }
+
+ if ( 0 < $soft_limit && $soft_limit <= $key ) {
+ $output .= '
';
+ $output .= '' . facetwp_i18n( __( 'See {num} more', 'fwp-front' ) ) . '';
+ $output .= '' . facetwp_i18n( __( 'See less', 'fwp-front' ) ) . '';
+ }
+
+ 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 .= '';
+ }
+ elseif ( $depth < $last_depth ) {
+ for ( $i = $last_depth; $i > $depth; $i-- ) {
+ $output .= '
';
+ }
+ }
+
+ $output .= $this->render_choice( $row, $params );
+
+ $last_depth = $depth;
+ }
+
+ for ( $i = $last_depth; $i > $init_depth; $i-- ) {
+ $output .= '';
+ }
+
+ 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 .= '';
+ $output .= '';
+ $output .= apply_filters( 'facetwp_facet_display_value', $label, [
+ 'selected' => ( '' !== $selected ),
+ 'facet' => $params['facet'],
+ 'row' => $row
+ ]);
+ $output .= '';
+ $output .= '(' . $row['counter'] . ')';
+ $output .= '
';
+ 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 ];
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/date_range.php b/wp-content/plugins/facetwp/includes/facets/date_range.php
new file mode 100644
index 000000000..01f3bcc66
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/date_range.php
@@ -0,0 +1,306 @@
+label = __( 'Date Range', 'fwp' );
+ $this->fields = [ 'source_other', 'compare_type', 'date_fields', 'date_format' ];
+ }
+
+
+ /**
+ * Generate the facet HTML
+ */
+ function render( $params ) {
+
+ $output = '';
+ $value = (array) $params['selected_values'];
+ $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' ) );
+
+ $text_date_empty = facetwp_i18n( __( 'No dates', 'fwp-front' ) );
+ $text_start_date_empty = facetwp_i18n( __( 'No start dates', 'fwp-front' ) );
+ $text_end_date_empty = facetwp_i18n( __( 'No end dates', 'fwp-front' ) );
+
+
+ if ( 'exact' == $fields ) {
+ $output .= '';
+ }
+ if ( 'both' == $fields || 'start_date' == $fields ) {
+ $output .= '';
+ }
+ if ( 'both' == $fields || 'end_date' == $fields ) {
+ $output .= '';
+ }
+
+ 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;
+ }
+
+ /**
+ * Enclose type defaults
+ * default min or max to
+ * opposite value
+ **/
+ if ( 'enclose' == $compare_type ) {
+ $min = ( false !== $min ) ? $min : $max;
+ $max = ( false !== $max ) ? $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 formatting tokens',
+ '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 '';
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/dropdown.php b/wp-content/plugins/facetwp/includes/facets/dropdown.php
new file mode 100644
index 000000000..be99a9e09
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/dropdown.php
@@ -0,0 +1,74 @@
+label = __( 'Dropdown', 'fwp' );
+ $this->fields = [ 'label_any', 'parent_term', 'modifiers', 'hierarchical', 'ghosts', '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 .= '';
+ return $output;
+ }
+
+
+ /**
+ * Filter the query based on selected values
+ */
+ function filter_posts( $params ) {
+ return FWP()->helper->facet_types['checkboxes']->filter_posts( $params );
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/fselect.php b/wp-content/plugins/facetwp/includes/facets/fselect.php
new file mode 100644
index 000000000..fd5308033
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/fselect.php
@@ -0,0 +1,100 @@
+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 .= '';
+ 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';
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/hierarchy.php b/wp-content/plugins/facetwp/includes/facets/hierarchy.php
new file mode 100644
index 000000000..5e6bdf957
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/hierarchy.php
@@ -0,0 +1,182 @@
+label = __( 'Hierarchy', 'fwp' );
+ $this->fields = [ 'label_any', 'modifiers', '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'];
+
+ $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 );
+ }
+
+ $limit = $this->get_limit( $facet );
+
+ // 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
+ LIMIT $limit";
+
+ $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 = '';
+ $soft_limit = empty( $facet['soft_limit'] ) ? 0 : (int) $facet['soft_limit'];
+
+ $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 .= '';
+ }
+
+ if ( 0 < $soft_limit && $num == $soft_limit ) {
+ $output .= '
';
+ }
+
+ if ( ! $selected ) {
+ if ( isset( $row['is_choice'] ) ) {
+ $label .= '
(' . $row['counter'] . ')';
+ }
+ else {
+ $arrow = apply_filters( 'facetwp_facet_hierarchy_arrow', '‹ ' );
+ $label = $arrow . $label;
+ }
+ }
+
+ $output .= '
' . $label . '
';
+
+ if ( isset( $row['is_choice'] ) ) {
+ $num++;
+ }
+
+ $last_depth = $row['depth'];
+ }
+
+ if ( 0 < $soft_limit && $soft_limit < $num ) {
+ $output .= '
';
+ $output .= '
' . facetwp_i18n( __( 'See {num} more', 'fwp-front' ) ) . '';
+ $output .= '
' . facetwp_i18n( __( 'See less', 'fwp-front' ) ) . '';
+ }
+
+ for ( $i = 0; $i <= $last_depth; $i++ ) {
+ $output .= '
';
+ }
+ }
+
+ return $output;
+ }
+
+
+ /**
+ * Filter the query based on selected values
+ */
+ function filter_posts( $params ) {
+ return FWP()->helper->facet_types['checkboxes']->filter_posts( $params );
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/map.php b/wp-content/plugins/facetwp/includes/facets/map.php
new file mode 100644
index 000000000..86bd906aa
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/map.php
@@ -0,0 +1,548 @@
+label = __( 'Map', 'fwp' );
+ $this->fields = [ 'longitude', 'mapid', 'map_design', 'btn_label', 'reset_label', 'cluster', 'limit', 'map_width','map_height', 'min_zoom', 'max_zoom', 'default_lat', 'default_lng', 'default_zoom', 'ajax_markers', 'marker_content' ];
+ $this->field_defaults = [
+ 'mapid' => 'DEMO_MAP_ID'
+ ];
+
+ add_filter( 'facetwp_index_row', [ $this, 'index_latlng' ], 1, 2 );
+ add_filter( 'facetwp_render_output', [ $this, 'add_marker_data' ], 10, 2 );
+
+ // ajax load of marker content
+ add_action( 'facetwp_init', function() {
+ if ( isset( $_POST['action'] ) && 'facetwp_map_marker_content' == $_POST['action'] ) {
+ $post_id = (int) $_POST['post_id'];
+ $facet_name = $_POST['facet_name'];
+
+ echo $this->get_marker_content( $post_id, $facet_name );
+ wp_die();
+ }
+ });
+ }
+
+ /**
+ * Generate the facet HTML
+ */
+ function render( $params ) {
+
+ $width = $params['facet']['map_width'];
+ $width = empty( $width ) ? 600 : $width;
+ $width = is_numeric( $width ) ? $width . 'px' : $width;
+
+ $height = $params['facet']['map_height'];
+ $height = empty( $height ) ? 300 : $height;
+ $height = is_numeric( $height ) ? $height . 'px' : $height;
+
+ $class = '';
+ $btn_label = ( isset( $params['facet']['btn_label'] ) && '' != $params['facet']['btn_label'] ) ? $params['facet']['btn_label'] : __( 'Enable map filtering', 'facetwp-map-facet' );
+
+ if ( $this->is_map_filtering_enabled() ) {
+ $class = ' enabled';
+ $btn_label = ( isset( $params['facet']['reset_label'] ) && '' != $params['facet']['reset_label'] ) ? $params['facet']['reset_label'] : __( 'Reset', 'facetwp-map-facet' ) ;
+ }
+
+ $output = '';
+ $output .= '';
+ return $output;
+ }
+
+
+ /**
+ * Is filtering turned on for the map?
+ * @return bool
+ */
+ function is_map_filtering_enabled() {
+ foreach ( FWP()->facet->facets as $facet ) {
+ if ( 'map' == $facet['type'] && ! empty( $facet['selected_values'] ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Is a proximity facet in use? If so, return a lat/lng array
+ * @return mixed array of coordinates, or FALSE
+ */
+ function is_proximity_in_use() {
+ foreach ( FWP()->facet->facets as $facet ) {
+ if ( 'proximity' == $facet['type'] && ! empty( $facet['selected_values'] ) ) {
+ $this->proximity_facet = $facet;
+
+ return [
+ 'lat' => (float) $facet['selected_values'][0],
+ 'lng' => (float) $facet['selected_values'][1],
+ 'radius' => (int) $facet['selected_values'][2]
+ ];
+ }
+ }
+
+ return false;
+ }
+
+
+ function add_marker_data( $output, $params ) {
+ if ( ! $this->is_map_active() ) {
+ return $output;
+ }
+
+ // Exit if paging and limit = "all"
+ if ( 0 < (bool) FWP()->facet->ajax_params['soft_refresh'] ) {
+ if ( 'all' == FWP()->helper->facet_is( $this->map_facet, 'limit', 'all' ) ) {
+ $output['settings']['map'] = '';
+ return $output;
+ }
+ }
+
+ $settings = [
+ 'locations' => []
+ ];
+
+ $settings['config'] = [
+ 'default_lat' => (float) $this->map_facet['default_lat'],
+ 'default_lng' => (float) $this->map_facet['default_lng'],
+ 'default_zoom' => (int) $this->map_facet['default_zoom'],
+ 'spiderfy' => [
+ 'markersWontMove' => true,
+ 'markersWontHide' => true,
+ 'basicFormatEvents' => true,
+ 'keepSpiderfied' => true
+ ]
+ ];
+
+ if ( 'yes' == $this->map_facet['cluster'] ) {
+ $settings['config']['cluster'] = [
+ 'zoomOnClick' => true,
+ 'maxZoom' => 15,
+ 'minimumClusterSize' => 2
+ ];
+ }
+
+ $settings['init'] = [
+ 'mapId' => ( '' != ( $this->map_facet['mapid'] ?? '' ) ) ? $this->map_facet['mapid'] : 'DEMO_MAP_ID',
+ 'gestureHandling' => 'auto',
+ 'zoom' => (int) $this->map_facet['default_zoom'] ?: 5,
+ 'minZoom' => (int) $this->map_facet['min_zoom'] ?: 1,
+ 'maxZoom' => (int) $this->map_facet['max_zoom'] ?: 20,
+ 'center' => [
+ 'lat' => (float) $this->map_facet['default_lat'],
+ 'lng' => (float) $this->map_facet['default_lng'],
+ ],
+ ];
+
+ $settings = apply_filters( 'facetwp_map_init_args', $settings );
+
+ // Get the proximity facet's coordinates (if available)
+ $this->proximity_coords = $this->is_proximity_in_use();
+
+ if ( false !== $this->proximity_coords ) {
+ $marker_args = [
+ 'title' => __( 'Your location', 'facetwp-map-facet' ),
+ 'position' => $this->proximity_coords,
+ 'pinOptions' => [ 'background' => '#FFD700', 'borderColor' => '#DAA520', 'scale' => 0.85, 'glyphColor' => '#DAA520'],
+ 'infoWindowContent' => ''.__( 'Your location', 'facetwp-map-facet' ).'
',
+ ];
+
+ $marker_args = apply_filters( 'facetwp_map_proximity_marker_args', $marker_args );
+
+ if ( ! empty( $marker_args ) ) {
+ $settings['locations'][] = $marker_args;
+ }
+ }
+
+ // get all post IDs
+ if ( isset( $this->map_facet['limit'] ) && 'all' == $this->map_facet['limit'] ) {
+ $post_ids = isset( FWP()->filtered_post_ids ) ?
+ FWP()->filtered_post_ids :
+ FWP()->facet->query_args['post__in'];
+ }
+ // get paginated post IDs
+ else {
+ $post_ids = (array) wp_list_pluck( FWP()->facet->query->posts, 'ID' );
+ }
+
+ // remove duplicates
+ $post_ids = array_unique( $post_ids );
+
+ $all_coords = $this->get_coordinates( $post_ids, $this->map_facet );
+
+ foreach ( $post_ids as $post_id ) {
+ if ( isset( $all_coords[ $post_id ] ) ) {
+ foreach ( $all_coords[ $post_id ] as $coords ) {
+ $args = [
+ 'position' => $coords,
+ 'post_id' => $post_id,
+ ];
+
+ if ( 'yes' !== $this->map_facet['ajax_markers'] ) {
+ $args['infoWindowContent'] = $this->get_marker_content( $post_id );
+ }
+
+ $args = apply_filters( 'facetwp_map_marker_args', $args, $post_id );
+
+ // back compat for legacy content arg
+ // change content to infoWindowContent (if it exists*)
+ // because content is now the pin arg in markers
+ // *for ajax markers, infoWindowContent should not be set to ''
+ if ( isset( $args['content'] ) ) $args['infoWindowContent'] = $args['content'];
+
+ if ( false !== $args ) {
+ $settings['locations'][] = $args;
+ }
+ }
+ }
+ }
+
+ $output['settings']['map'] = $settings;
+
+ return $output;
+ }
+
+
+ /**
+ * Grab all coordinates from the index table
+ */
+ function get_coordinates( $post_ids, $facet ) {
+ global $wpdb;
+
+ $output = [];
+
+ if ( ! empty( $post_ids ) ) {
+ $post_ids = implode( ',', $post_ids );
+
+ $sql = "
+ SELECT post_id, facet_value AS lat, facet_display_value AS lng
+ FROM {$wpdb->prefix}facetwp_index
+ WHERE facet_name = '{$facet['name']}' AND post_id IN ($post_ids)";
+
+ $result = $wpdb->get_results( $sql );
+
+ foreach ( $result as $row ) {
+ $output[ $row->post_id ][] = [
+ 'lat' => (float) $row->lat,
+ 'lng' => (float) $row->lng,
+ ];
+ }
+
+ // Support ACF repeaters
+ if ( false !== $this->proximity_coords) {
+ foreach ( $output as $post_id => $coords ) {
+ if ( 1 < count( $coords ) ) {
+ $output[ $post_id ] = [];
+
+ foreach ( $coords as $latlng ) {
+ if ( $this->is_within_bounds( $latlng ) ) {
+ $output[ $post_id ][] = $latlng;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return $output;
+ }
+
+
+ /**
+ * Is the current point within the proximity bounds?
+ */
+ function is_within_bounds( $latlng ) {
+ $lat1 = $latlng['lat'];
+ $lng1 = $latlng['lng'];
+ $lat2 = $this->proximity_coords['lat'];
+ $lng2 = $this->proximity_coords['lng'];
+
+ $radius = $this->proximity_coords['radius'];
+ $unit = $this->proximity_facet['unit'];
+
+ if ( $lat1 == $lat2 && $lng1 == $lng2 ) {
+ return true;
+ }
+
+ $dist = sin( deg2rad( $lat1 ) ) * sin( deg2rad( $lat2 ) ) + cos( deg2rad( $lat1 ) ) * cos( deg2rad( $lat2 ) ) * cos( deg2rad( $lng1 - $lng2 ) );
+ $dist = min( max( $dist, -1 ), 1 ); // force value between -1 and 1
+ $dist = rad2deg( acos( $dist ) );
+
+ $miles = $dist * 60 * 1.1515;
+ $needle = ( 'km' == $unit ) ? $miles * 1.609344 : $miles;
+ return $needle <= $radius;
+ }
+
+
+ /**
+ * Is this page using a map facet?
+ */
+ function is_map_active() {
+ foreach ( FWP()->facet->facets as $name => $facet ) {
+ if ( 'map' == $facet['type'] ) {
+ $this->map_facet = $facet; // save the facet
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Get marker content (pulled via ajax)
+ */
+ function get_marker_content( $post_id, $facet_name = false ) {
+ if ( false !== $facet_name ) {
+ $facet = FWP()->helper->get_facet_by_name( $facet_name );
+ $content = $facet['marker_content'];
+ }
+ else {
+ $content = $this->map_facet['marker_content'];
+ }
+
+ if ( empty( $content ) ) {
+ return '';
+ }
+
+ global $post;
+
+ ob_start();
+
+ // Set the main $post object
+ $post = get_post( $post_id );
+
+ setup_postdata( $post );
+
+ // Remove UTF-8 non-breaking spaces
+ $html = preg_replace( "/\xC2\xA0/", ' ', $content );
+
+ eval( '?>' . $html );
+
+ // Reset globals
+ wp_reset_postdata();
+
+ // Store buffered output
+ return ob_get_clean();
+ }
+
+
+ /**
+ * Filter the query based on the map bounds
+ */
+ function filter_posts( $params ) {
+ global $wpdb;
+
+ $facet = $params['facet'];
+ $selected_values = (array) $params['selected_values'];
+
+ $swlat = (float) $selected_values[0];
+ $swlng = (float) $selected_values[1];
+ $nelat = (float) $selected_values[2];
+ $nelng = (float) $selected_values[3];
+
+ // @url https://stackoverflow.com/a/35944747
+ if ( $swlng < $nelng ) {
+ $compare_lng = "facet_display_value BETWEEN $swlng AND $nelng";
+ }
+ else {
+ $compare_lng = "facet_display_value BETWEEN $swlng AND 180 OR ";
+ $compare_lng .= "facet_display_value BETWEEN -180 AND $nelng";
+ }
+
+ $sql = "
+ SELECT DISTINCT post_id FROM {$wpdb->prefix}facetwp_index
+ WHERE facet_name = '{$facet['name']}' AND
+ facet_value BETWEEN $swlat AND $nelat AND ($compare_lng)";
+
+ return $wpdb->get_col( $sql );
+ }
+
+
+ /**
+ * Output any front-end scripts
+ */
+ function front_scripts() {
+
+ add_filter( 'facetwp_load_gmaps', '__return_true', 4 );
+
+ FWP()->display->assets['oms'] = [ FACETWP_URL . '/assets/js/src/oms.min.js', FACETWP_MAP_FACET_VERSION ];
+
+ if ( 'yes' == $this->map_facet['cluster'] ) {
+ FWP()->display->assets['markerclusterer'] = [ 'https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js', FACETWP_MAP_FACET_VERSION ];
+ }
+
+ FWP()->display->assets['facetwp-map'] = [ FACETWP_URL . '/assets/js/src/map.js', FACETWP_MAP_FACET_VERSION ];
+
+ $btn_label = ( isset( $this->map_facet['btn_label'] ) && '' != $this->map_facet['btn_label'] ) ? $this->map_facet['btn_label'] : __( 'Enable map filtering', 'fwp-front' ) ;
+ FWP()->display->json['map']['filterText'] = facetwp_i18n( $btn_label );
+
+ $reset_label = ( isset( $this->map_facet['reset_label'] ) && '' != $this->map_facet['reset_label'] ) ? $this->map_facet['reset_label'] : __( 'Reset', 'fwp-front');
+ FWP()->display->json['map']['resetText'] = facetwp_i18n( $reset_label );
+ FWP()->display->json['map']['facet_name'] = $this->map_facet['name'];
+ FWP()->display->json['map']['ajaxurl'] = admin_url( 'admin-ajax.php' );
+ }
+
+
+ function register_fields() {
+ return [
+ 'longitude' => [
+ 'type' => 'alias',
+ 'items' => [
+ 'source_other' => [
+ 'label' => __( 'Longitude', 'fwp' ),
+ 'notes' => __( '(Optional) use a separate longitude field.', 'fwp' ),
+ 'html' => ''
+ ]
+ ]
+ ],
+ 'mapid' => [
+ 'label' => __( 'Map ID', 'fwp' ),
+ 'placeholder' => 'DEMO_MAP_ID',
+ 'notes' => __( 'In the Google Cloud Console\'s Map Management section, create a Map ID and enter it here. Choose a map rendering type (raster or vector) in the Map ID settings. To change your map styling, create a Map Style in the Map Styles section and attach it to the Map ID.', 'fwp' )
+ ],
+ 'map_design' => [
+ 'label' => __( 'Map design', 'fwp' ),
+ 'notes' => __( 'In the Google Cloud Console\'s Map Management section, create a Map ID and enter it in the above Map ID setting. Then create a Map Style in the Map Styles section and attach it to the Map ID.', 'fwp' ),
+ 'html' => 'This setting has been replaced with
+ cloud-based map styling.
+ '
+ ],
+ 'btn_label' => [
+ 'label' => __( 'Enable filtering button label', 'fwp' ),
+ 'placeholder' => facetwp_i18n( __( 'Enable map filtering', 'fwp' ) )
+ ],
+ 'reset_label' => [
+ 'label' => __( 'Reset button label', 'fwp', 'fwp' ),
+ 'placeholder' => facetwp_i18n( __( 'Reset', 'fwp' ) )
+ ],
+ 'cluster' => [
+ 'type' => 'toggle',
+ 'label' => __( 'Marker clustering', 'fwp' ),
+ 'notes' => __( 'Group markers into clusters?', 'fwp' )
+ ],
+ 'limit' => [
+ 'type' => 'select',
+ 'label' => __( 'Marker limit', 'fwp' ),
+ 'choices' => [
+ 'all' => __( 'Show all results', 'fwp' ),
+ 'paged' => __( 'Show current page results', 'fwp' )
+ ]
+ ],
+ 'map_width' => [
+ 'type' => 'text',
+ 'html' => '',
+ 'label' => __( 'Map width / height', 'fwp' ),
+ 'notes' => __( 'Set width and height of the map. Without units, px is assumed. Use other CSS units if needed, e.g. 100% for responsive full width of the parent container. Don\'t use 100% for the height if the map\'s container does not have a fixed height, else the map will have no height and will not show.', 'fwp' )
+ ],
+ 'map_height' => [
+ 'show' => '0==1'
+ ],
+ 'min_zoom' => [
+ 'type' => 'text',
+ 'html' => '
+ ',
+ 'label' => __( 'Zoom min / max', 'fwp' ),
+ 'notes' => __( 'Set zoom bounds (between 1 and 20).', 'fwp' )
+ ],
+ 'max_zoom' => [
+ 'show' => '0==1'
+ ],
+ 'default_lat' => [
+ 'type' => 'text',
+ 'html' => '
+
+ ',
+ 'label' => __( 'Fallback lat / lng / zoom', 'fwp' ),
+ 'notes' => __( 'Center the map here, and set a custom zoom level, if there are no results.', 'fwp' )
+ ],
+ 'default_lng' => [
+ 'show' => '0==1'
+ ],
+ 'default_zoom' => [
+ 'show' => '0==1'
+ ],
+ 'ajax_markers' => [
+ 'type' => 'toggle',
+ 'label' => __( 'Info window ajax loading', 'fwp' ),
+ 'notes' => __( 'Dynamically load marker info window content on click of markers, which could improve load times for pages with many markers.', 'fwp' )
+ ],
+ 'marker_content' => [
+ 'type' => 'textarea',
+ 'label' => __( 'Info window content', 'fwp' ),
+ 'html' => '
+
+ ' . __( 'To search your code, click in the editor, then use Ctrl+F (Windows/Linux) or Cmd+F (Mac).', 'fwp' ) . '
+ '
+ ],
+ ];
+ }
+
+
+ /**
+ * 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 && 'map' == $facet['type'] ) {
+ $latlng = $params['facet_value'];
+
+ // Only handle "lat, lng" strings
+ if ( 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 ( false === strpos( $latlng, ',' ) && ! empty( $rows ) ) {
+ $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];
+ }
+
+ /** make sure lat and lng are valid floats **/
+ $params['facet_value'] = $params['facet_value'] == (float)$params['facet_value'] ? (float)$params['facet_value'] : '';
+ $params['facet_display_value'] = $params['facet_display_value'] == (float)$params['facet_display_value'] ? (float)$params['facet_display_value'] : '';
+
+ /** check for valid range of lat and lng */
+ if ( '' == $params['facet_value'] || '' == $params['facet_display_value'] || 90 < abs( $params['facet_value'] ) || 180 < abs( $params['facet_display_value'] ) ) {
+ $params['facet_value'] = ''; // don't index
+ }
+ }
+ }
+
+ return $params;
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/number_range.php b/wp-content/plugins/facetwp/includes/facets/number_range.php
new file mode 100644
index 000000000..889c22338
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/number_range.php
@@ -0,0 +1,137 @@
+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 .= '';
+ }
+ if ( 'both' == $fields || 'min' == $fields ) {
+ $output .= '';
+ }
+ if ( 'both' == $fields || 'max' == $fields ) {
+ $output .= '';
+ }
+
+ $output .= '';
+
+ 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
+ ];
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/pager.php b/wp-content/plugins/facetwp/includes/facets/pager.php
new file mode 100644
index 000000000..b0c663832
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/pager.php
@@ -0,0 +1,297 @@
+label = __( 'Pager', 'fwp' );
+ $this->fields = [ 'pager_type', 'inner_size', 'dots_label', 'prev_label', 'next_label',
+ 'count_text_plural', 'count_text_singular', 'count_text_none', 'scroll_target', 'scroll_offset',
+ '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 '';
+ }
+
+
+ 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 = '' . $label . '';
+
+ 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_rows_unfiltered = $this->pager_args['total_rows_unfiltered'];
+ $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( '[total_unfiltered]', $total_rows_unfiltered, $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 '';
+ }
+
+
+ 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 .= '';
+ }
+
+ $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 .= '';
+ }
+
+ return '';
+ }
+
+
+ /**
+ * 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'];
+ $settings[ 'pager_type' ] = $facet['pager_type'];
+
+ if ( 'numbers' == $facet['pager_type'] ) {
+ $settings[ 'scroll_target' ] = $facet['scroll_target'] ?? '';
+ $settings[ 'scroll_offset' ] = (int) ( $facet['scroll_offset'] ?? '' );
+ }
+
+ return $settings;
+ }
+
+
+ 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'"
+ ],
+ 'scroll_target' => [
+ 'label' => __( 'Scroll target', 'fwp' ),
+ 'notes' => 'Add a class or ID to target for scroll up on paging. Use "body" for top of page or ".facetwp-template" for top of results. Leave blank for no scrolling.',
+ 'default' => '',
+ 'placeholder' => 'e.g. .facetwp-template',
+ 'show' => "facet.pager_type == 'numbers'"
+ ],
+ 'scroll_offset' => [
+ 'label' => __( 'Scroll offset', 'fwp' ),
+ 'notes' => 'Number of px to modify scroll target position, for example 100 will be 100px below target, -100 will be 100px above target',
+ 'default' => '',
+ 'show' => "facet.scroll_target != '' && 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'"
+ ]
+ ];
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/proximity.php b/wp-content/plugins/facetwp/includes/facets/proximity.php
new file mode 100644
index 000000000..5e49bb2af
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/proximity.php
@@ -0,0 +1,415 @@
+ 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', 'placeholder' ];
+
+ 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 );
+ add_filter( 'facetwp_render_output', [ $this, 'add_places_version' ], 10, 2 );
+ }
+
+ /**
+ * Get places version from setting, default to legacy version - places-service
+ * Also can be set with filter
+ * add_filter( 'facetwp_proximity_places_version', function($version) { return 'place-class'; } );
+ * @since 4.4
+ */
+ function get_places_version() {
+ $places_version = FWP()->helper->get_setting( 'places_version', 'places-service' );
+ return apply_filters( 'facetwp_proximity_places_version', $places_version );
+ }
+
+ /**
+ * Add a places version as FWP.settings.places
+ * @since 4.4
+ */
+ function add_places_version( $output, $params ) {
+ $output['settings']['places'] = $this->get_places_version();
+ return $output;
+ }
+
+ /**
+ * Generate the facet HTML
+ */
+ function render( $params ) {
+
+ $output = '';
+ $facet = $params['facet'];
+ $value = $params['selected_values'];
+ $unit = empty( $facet['unit'] ) ? 'mi' : $facet['unit'];
+ $unit_display = 'km' == $facet['unit'] ? __( 'km', 'fwp-front' ) : ( 'mi' == $facet['unit'] ? __( 'mi', 'fwp-front' ) : $facet['unit'] );
+ $placeholder = empty( $facet['placeholder'] ) ? __( 'Enter location', 'fwp-front' ) : $facet['placeholder'];
+ $placeholder = facetwp_i18n( $placeholder );
+
+ $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();
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+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 ) ) { // back compatibility for filter
+
+ if ( version_compare( FACETWP_MAP_FACET_VERSION, '2.0', '<' ) || 'places-service' == $this->get_places_version() ) {
+
+ // 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( esc_attr( $api_key ) ) . '&callback=Function.prototype';
+
+ } else {
+
+ add_filter( 'facetwp_load_gmaps', '__return_true', 5 );
+
+ }
+ }
+
+ // Pass extra options into Places Autocomplete
+ $options = apply_filters( 'facetwp_proximity_autocomplete_options', [] );
+
+ if ( 'place-class' == $this->get_places_version() ) {
+
+ // Backward compatibility with documented legacy Places API options that don't exist in Places (New) Autocomplete
+ if ( isset( $options['componentRestrictions'] ) ) {
+ if ( isset( $options['componentRestrictions']['country'] ) ) {
+ $options['includedRegionCodes'] = $options['componentRestrictions']['country'];
+ }
+ unset( $options['componentRestrictions'] );
+ }
+ if ( isset( $options['types'] ) ) {
+ $options['includedPrimaryTypes'] = $options['types'];
+ unset( $options['types'] );
+ }
+ if ( isset( $options['bounds'] ) ) {
+ $options['locationBias'] = $options['bounds'];
+ unset( $options['bounds'] );
+ }
+
+ }
+
+
+ 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' => ''
+ ]
+ ]
+ ],
+ '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 ( 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 ( false === strpos( $latlng, ',' ) && ! empty( $rows ) ) {
+ $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];
+ }
+
+ /** make sure lat and lng are valid floats **/
+ $params['facet_value'] = $params['facet_value'] == (float)$params['facet_value'] ? (float)$params['facet_value'] : '';
+ $params['facet_display_value'] = $params['facet_display_value'] == (float)$params['facet_display_value'] ? (float)$params['facet_display_value'] : '';
+
+ /** check for valid range of lat and lng */
+ if ( '' == $params['facet_value'] || '' == $params['facet_display_value'] || 90 < abs( $params['facet_value'] ) || 180 < abs( $params['facet_display_value'] ) ) {
+ $params['facet_value'] = ''; // don't index
+ }
+ }
+ }
+
+ 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;
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/radio.php b/wp-content/plugins/facetwp/includes/facets/radio.php
new file mode 100644
index 000000000..9407ecb68
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/radio.php
@@ -0,0 +1,64 @@
+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 .= '' . esc_attr( $label_any ) . '
';
+ }
+
+ 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 .= '';
+ $output .= '';
+ $output .= apply_filters( 'facetwp_facet_display_value', $label, [
+ 'selected' => ( '' !== $selected ),
+ 'facet' => $facet,
+ 'row' => $row
+ ]);
+ $output .= '';
+ $output .= '(' . $row['counter'] . ')';
+ $output .= '
';
+ }
+
+ 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 );
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/rating.php b/wp-content/plugins/facetwp/includes/facets/rating.php
new file mode 100644
index 000000000..481f6b16f
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/rating.php
@@ -0,0 +1,191 @@
+label = __( 'Star Rating', 'fwp' );
+ $this->fields = [ 'ratings_icon', 'ghost_ratings', 'color', 'color_selected', 'color_undo', 'color_ghosts' ];
+ }
+
+
+ /**
+ * 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'];
+ $show_ghosts = FWP()->helper->facet_is( $facet, 'ghost_ratings', 'yes' );
+ $ratings_icon = ( isset( $facet[ 'ratings_icon' ] ) && '' != $facet[ 'ratings_icon' ] ) ? $facet[ 'ratings_icon' ] : '★';
+
+ $num_stars = 0;
+ foreach ( $values as $val ) {
+ if ( 0 < $val['counter'] ) {
+ $num_stars++;
+ }
+ }
+
+ $num_stars = $show_ghosts ? 5 : $num_stars;
+
+ if ( 0 < $num_stars ) {
+ $output .= '';
+
+ for ( $i = $num_stars; $i >= 1; $i-- ) {
+ $class = in_array( $i, $selected_values ) ? ' selected' : '';
+ $is_disabled = ! ( 0 < $values[ $i ]['counter'] ) ? true : false;
+ $class = $is_disabled ? $class . ' disabled' : $class;
+ $output .= '' . apply_filters( 'facetwp_ratings_icon', $ratings_icon, $is_disabled, $facet ) . '';
+ }
+
+ $output .= '';
+ $output .= ' ';
+ $output .= ' ';
+ }
+
+ 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 );
+ }
+
+
+ function register_fields() {
+
+ return [
+ 'ratings_icon' => [
+ 'type' => 'select',
+ 'label' => __( 'Rating icon', 'fwp' ),
+ 'notes' => 'Select icon for ratings.',
+ 'choices' => [
+ '★' => __( 'Stars', 'fwp' ) . ' ★★★★★',
+ '☆' => __( 'Star Outlines', 'fwp' ) . ' ☆☆☆☆☆',
+ '♥' => __( 'Hearts', 'fwp' ) . ' ♥♥♥♥♥',
+ ]
+ ],
+ 'ghost_ratings' => [
+ 'type' => 'toggle',
+ 'label' => __( 'Show ghost ratings', 'fwp' ),
+ 'notes' => 'Always show 5 icons even when there are no matches.'
+ ],
+ 'color' => [
+ 'type' => 'color-picker',
+ 'label' => __( 'Color', 'fwp' ),
+ 'notes' => 'Set icon color.',
+ 'html' => '',
+ 'default' => '#cccccc'
+ ],
+ 'color_selected' => [
+ 'type' => 'color-picker',
+ 'label' => __( 'Selected color', 'fwp' ),
+ 'notes' => 'Set icon hover and selected color.',
+ 'html' => '',
+ ],
+ 'color_undo' => [
+ 'type' => 'color-picker',
+ 'label' => __( 'Undo color', 'fwp' ),
+ 'notes' => 'Set icon undo color.',
+ 'html' => '',
+ ],
+ 'color_ghosts' => [
+ 'type' => 'color-picker',
+ 'label' => __( 'Ghost color', 'fwp' ),
+ 'notes' => 'Set icon ghost color.',
+ 'html' => '',
+ 'show' => "facet.ghost_ratings != 'no'"
+ ]
+ ];
+ }
+
+
+ /**
+ * Output front-end scripts
+ */
+ function front_scripts() {
+ FWP()->display->json['rating']['& up'] = facetwp_i18n( __( '& up', 'fwp-front' ) );
+ FWP()->display->json['rating']['Undo'] = facetwp_i18n( __( 'Undo', 'fwp-front' ) );
+
+ $facets = FWP()->helper->get_facets_by( 'type', 'rating' );
+
+ $styles = '';
+
+ foreach ( $facets AS $facet ) {
+
+ $color = ( isset( $facet[ 'color' ] ) ) ? $facet[ 'color' ] : '#cccccc';
+ $selected = ( isset( $facet[ 'color_selected' ] ) ) ? $facet[ 'color_selected' ] : '#000000';
+ $undo = ( isset( $facet[ 'color_undo' ] ) ) ? $facet[ 'color_undo' ] : '#ff0000';
+ $ghosts = ( isset( $facet[ 'color_ghosts' ] ) ) ? $facet[ 'color_ghosts' ] : '#eeeeee';
+
+ $styles .= '
+ .facetwp-facet-' . $facet['name'] . ' .facetwp-star { color: ' . esc_attr( $color ) . ' }
+ .facetwp-facet-' . $facet['name'] . ' .facetwp-star:not(.disabled):hover, .facetwp-star:not(.disabled):hover ~ .facetwp-star, .facetwp-star.selected, .facetwp-star.selected ~ .facetwp-star { color: ' . esc_attr( $selected ) . '; }
+ .facetwp-facet-' . $facet['name'] . ' .facetwp-star.selected:hover, .facetwp-star.selected:hover ~ .facetwp-star { color: ' . esc_attr( $undo ) . '; }
+ .facetwp-facet-' . $facet['name'] . ' .facetwp-star.disabled, .facetwp-facet-' . $facet['name'] . ' .facetwp-star.disabled:hover { color: ' . esc_attr( $ghosts ) . '; }
+ ';
+ }
+
+ if ( !empty( $styles ) ) {
+ echo '';
+ }
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/reset.php b/wp-content/plugins/facetwp/includes/facets/reset.php
new file mode 100644
index 000000000..75048d85c
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/reset.php
@@ -0,0 +1,85 @@
+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 = '';
+ }
+ else {
+ $output = '{label}';
+ }
+
+ $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' => '',
+ 'show' => "facet.reset_mode != 'off'"
+ ],
+ 'auto_hide' => [
+ 'type' => 'toggle',
+ 'label' => __( 'Auto-hide', 'fwp' ),
+ 'notes' => 'Hide when no facets have selected values'
+ ]
+ ];
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/search.php b/wp-content/plugins/facetwp/includes/facets/search.php
new file mode 100644
index 000000000..d582f0168
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/search.php
@@ -0,0 +1,102 @@
+label = __( 'Search', 'fwp' );
+ $this->fields = [ 'search_engine', 'placeholder', 'auto_refresh', 'enable_relevance' ];
+ $this->field_defaults = [ 'enable_relevance' => 'yes' ];
+ }
+
+
+ /**
+ * 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 .= '';
+ $output .= '';
+ $output .= '';
+ $output .= '';
+ return $output;
+ }
+
+
+ /**
+ * Filter the query based on selected values
+ */
+ function filter_posts( $params ) {
+
+ $facet = $params['facet'];
+
+ if ( 'no' == $facet['enable_relevance'] ) {
+ add_filter( 'facetwp_use_search_relevancy', '__return_false' );
+ }
+
+ $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?'
+ ],
+ 'enable_relevance' => [
+ 'type' => 'toggle',
+ 'label' => __( 'Order by relevance', 'fwp' ),
+ 'notes' => 'By default, search results are ordered by relevance. Disable to use the original listing query order.',
+ 'default' => 'checked'
+ ]
+ ];
+ }
+
+
+ /**
+ * (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 ];
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/slider.php b/wp-content/plugins/facetwp/includes/facets/slider.php
new file mode 100644
index 000000000..6b3e4e4c7
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/slider.php
@@ -0,0 +1,164 @@
+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 = '';
+ $output .= '
';
+ $output .= '
';
+ $output .= '';
+ $output .= '';
+ 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'],
+ 'handle_attributes' => [
+ [ 'aria-label' => facetwp_i18n('lower') ],
+ [ 'aria-label' => facetwp_i18n('upper') ]
+ ]
+ ];
+ }
+
+
+ /**
+ * 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
+ ]
+ ];
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/facets/sort.php b/wp-content/plugins/facetwp/includes/facets/sort.php
new file mode 100644
index 000000000..6c56c98e0
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/facets/sort.php
@@ -0,0 +1,226 @@
+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 = '';
+
+ foreach ( $facet['sort_options'] as $key => $choice ) {
+ $label = facetwp_i18n( $choice['label'] );
+ $selected = in_array( $key, $selected_values ) ? ' selected' : '';
+ $output .= '';
+ }
+
+ return '';
+ }
+
+
+ /**
+ * 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' => ''
+ ]
+ ];
+ }
+
+
+ /**
+ * 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 .= '';
+ }
+
+ $html = '';
+
+ $output['sort'] = apply_filters( 'facetwp_sort_html', $html, [
+ 'sort_options' => $this->sort_options,
+ 'template_name' => FWP()->facet->template['name'],
+ ]);
+ }
+
+ return $output;
+ }
+}
diff --git a/wp-content/plugins/facetwp/includes/functions.php b/wp-content/plugins/facetwp/includes/functions.php
new file mode 100644
index 000000000..25ea10dc3
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/functions.php
@@ -0,0 +1,43 @@
+ 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 );
+}
diff --git a/wp-content/plugins/facetwp/includes/integrations/acf/acf.php b/wp-content/plugins/facetwp/includes/integrations/acf/acf.php
new file mode 100644
index 000000000..5eba7361d
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/integrations/acf/acf.php
@@ -0,0 +1,644 @@
+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'];
+ $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 ( function_exists( 'wc_get_product' ) && ( 'product' == $post_type || 'product_variation' == $post_type ) ) {
+ $product = wc_get_product( $post_id );
+
+ if ( ! $product || ( ! $index_all && ! $product->is_in_stock() ) ) {
+ return true; // skip
+ }
+ }
+
+ 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 );
+
+ // prevent null values from being run through format_date()
+ if ( $value === null ) {
+ return true; // skip
+ }
+
+ // 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( end($hierarchy), $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 || 'button_group' == $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 ) {
+
+ // Optionally index 'false' value as default for unsaved posts (for which $value is int(0))
+ $default_false = apply_filters( 'facetwp_index_acf_truefalse_default_false', false );
+
+ // Skip indexing if no explicit default (true) is set in the field settings, unless enabled with the hook.
+ if ($value === 0 && !$default_false) {
+ $value = '';
+ }
+
+ $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'] ) ) {
+
+ if ( 0 === strpos( $facet['source_other'], 'acf/' ) ) {
+ $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 ];
+ }
+
+ } else {
+
+ $other_params = $params;
+ $other_params['facet_source'] = $facet['source_other'];
+ $rows = FWP()->indexer->get_row_data( $other_params );
+ $value = $rows[0]['facet_display_value'] ?? $params['facet_display_value'];
+ }
+ }
+
+ if ( 'date_range' == $facet['type'] ) {
+
+ // prevent null values from being run through format_date()
+ if ( $value === null ) {
+ return ''; // skip
+ }
+
+ $value = $this->format_date( $value );
+ }
+ }
+
+ return $value;
+ }
+
+ /**
+ * Format dates in YYYY-MM-DD
+ */
+ function format_date( $str ) {
+
+ if ( $str === null ) {
+ return '';
+ }
+
+ 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 = '' ) {
+ if ( !empty( $fields ) ) {
+ 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'] ) && !empty( $field['sub_fields'] ) ) {
+ $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[] = '' . esc_html( $val->post_title ) . '';
+ }
+
+ $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 = '
';
+ }
+ }
+
+ if ( 'file' == $type && 'array' == $format ) {
+ $value = '' . esc_html( $value['filename'] ) . ' (' . size_format( $value['filesize'], 1 ) . ')';
+ }
+
+ if ( 'link' == $type && 'array' == $format ) {
+ $value = '' . esc_html( $value['title'] ) . '';
+ }
+
+ if ( 'google_map' == $type ) {
+ $value = '' . esc_html( $value['address'] ) . '';
+ }
+
+ 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;
+ }
+
+ /**
+ * Update the index when terms get saved
+ * @since 4.4
+ */
+ 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_datasource_type( 'acf' );
+
+ if ( ! empty( $matches ) ) {
+
+ $facet_names = [];
+
+ foreach ( $matches AS $facet ) {
+ $source_parts = explode( '/', $facet['source'] );
+ $field_id = array_pop( $source_parts );
+ $field_object = get_field_object( $field_id );
+ if ( !empty( $field_object ) && 'taxonomy' == $field_object['type'] ) {
+ $facet_names[] = $facet['name'];
+ }
+ }
+
+ if ( !empty( $facet_names ) ) {
+
+ $facet_names = implode( "','", esc_sql( $facet_names ) );
+
+ $wpdb->query( $wpdb->prepare( "
+ UPDATE {$wpdb->prefix}facetwp_index
+ 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 4.4
+ */
+ function delete_term( $term_id, $tt_id, $taxonomy ) {
+ global $wpdb;
+
+ $matches = FWP()->helper->get_facets_by_datasource_type( 'acf' );
+
+ if ( ! empty( $matches ) ) {
+
+ $facet_names = [];
+
+ foreach ( $matches AS $facet ) {
+ $source_parts = explode( '/', $facet['source'] );
+ $field_id = array_pop( $source_parts );
+ $field_object = get_field_object( $field_id );
+ if ( !empty( $field_object ) && 'taxonomy' == $field_object['type'] ) {
+ $facet_names[] = $facet['name'];
+ }
+ }
+
+ if ( !empty( $facet_names ) ) {
+
+ $facet_names = implode( "','", esc_sql( $facet_names ) );
+
+ $wpdb->query( "
+ DELETE FROM {$wpdb->prefix}facetwp_index
+ WHERE facet_name IN ('$facet_names') AND term_id = $term_id"
+ );
+
+ }
+ }
+ }
+}
+
+
+if ( function_exists( 'acf' ) && version_compare( acf()->settings['version'], '5.0', '>=' ) ) {
+ FWP()->acf = new FacetWP_Integration_ACF();
+}
diff --git a/wp-content/plugins/facetwp/includes/integrations/edd/edd.js b/wp-content/plugins/facetwp/includes/integrations/edd/edd.js
new file mode 100644
index 000000000..f7745467a
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/integrations/edd/edd.js
@@ -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);
\ No newline at end of file
diff --git a/wp-content/plugins/facetwp/includes/integrations/edd/edd.php b/wp-content/plugins/facetwp/includes/integrations/edd/edd.php
new file mode 100644
index 000000000..77d0caa36
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/integrations/edd/edd.php
@@ -0,0 +1,51 @@
+ $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();
+}
diff --git a/wp-content/plugins/facetwp/includes/integrations/searchwp/searchwp.php b/wp-content/plugins/facetwp/includes/integrations/searchwp/searchwp.php
new file mode 100644
index 000000000..3fe995fd1
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/integrations/searchwp/searchwp.php
@@ -0,0 +1,239 @@
+is_main_query() && $query->is_search() ) {
+ $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 ) || 's' == $arg ) {
+ $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 ) ) {
+
+ if ( 'no' == $facet['enable_relevance'] ) {
+ add_filter( 'facetwp_use_search_relevancy', '__return_false' );
+ }
+
+ $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;
+ }
+
+
+ /**
+ * Short-circuit SearchWP when "s" is empty and a search facet is in use
+ * @since 4.2.6
+ */
+ function short_circuit( $bool, $query ) {
+ if ( $query->is_search() && '' == $query->get( 's' ) ) {
+ $facets = FWP()->facet->facets ?? FWP()->ajax->url_vars;
+ foreach ( $facets AS $name => $value ) {
+ if ( FWP()->helper->facet_is( $name, 'type', 'search' ) ) {
+ return true;
+ }
+ }
+ }
+
+ return $bool;
+ }
+}
+
+
+if ( defined( 'SEARCHWP_VERSION' ) ) {
+ new FacetWP_Integration_SearchWP();
+}
diff --git a/wp-content/plugins/facetwp/includes/integrations/woocommerce/taxonomy.php b/wp-content/plugins/facetwp/includes/integrations/woocommerce/taxonomy.php
new file mode 100644
index 000000000..173eca16c
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/integrations/woocommerce/taxonomy.php
@@ -0,0 +1,86 @@
+\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 ) ) {
+ $fields = $query_vars['fields'] ?? 'all';
+ if ( 'product_cat' == reset( $taxonomy ) && 'all' == $fields ) {
+ 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(DISTINCT post_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 && did_action( 'woocommerce_shop_loop_header' ) && !did_action( 'woocommerce_after_shop_loop' ) ) {
+ $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();
diff --git a/wp-content/plugins/facetwp/includes/integrations/woocommerce/woocommerce.js b/wp-content/plugins/facetwp/includes/integrations/woocommerce/woocommerce.js
new file mode 100644
index 000000000..cf8b1dfc5
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/integrations/woocommerce/woocommerce.js
@@ -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);
\ No newline at end of file
diff --git a/wp-content/plugins/facetwp/includes/integrations/woocommerce/woocommerce.php b/wp-content/plugins/facetwp/includes/integrations/woocommerce/woocommerce.php
new file mode 100644
index 000000000..32f20f05a
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/integrations/woocommerce/woocommerce.php
@@ -0,0 +1,617 @@
+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' );
+ }
+
+ // Prevent WooCommerce breadcrumb from showing "Page X" on page 1 and higher when there are facet selections or pre-selected facets
+ add_filter( 'woocommerce_get_breadcrumb', [ $this, 'prevent_wc_breadcrumb_paged' ], 9999, 2 );
+
+ }
+
+
+ /**
+ * 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( '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['parent_id'] = $term->parent;
+ $params['facet_display_value'] = $term->name;
+
+ $params['depth'] = count( get_ancestors( $term->term_id, $taxonomy, 'taxonomy' ) );
+
+ $facet = FWP()->helper->get_facet_by_name( $params['facet_name'] );
+
+ // handle parent_term setting
+ if ( isset( $facet['parent_term'] ) && 0 < $facet['parent_term'] && !term_is_ancestor_of( $facet['parent_term'], $params['term_id'], $taxonomy ) ) {
+ $params['facet_value'] = ''; // don't index
+ }
+ }
+ }
+ }
+
+ 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;
+ }
+
+
+ /**
+ * Exclude specific WC custom fields
+ * @since 4.2.3
+ */
+ function exclude_data_sources( $sources ) {
+ unset( $sources['custom_fields']['choices']['cf/_product_attributes'] );
+ return $sources;
+ }
+
+
+ /**
+ * 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 ) {
+
+ // We aren't using a ternary here in case the user
+ // assigned a custom stock status label
+ if ( 'In Stock' == $label ) {
+ $label = __( 'In Stock', 'fwp-front' );
+ }
+ elseif ( 'Out of Stock' == $label ) {
+ $label = __( '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;
+ }
+
+
+ /**
+ * Prevent WooCommerce breadcrumb from showing "Page X" on page 1 and higher when there are facet selections or pre-selected facets
+ * @since 4.3.6
+ */
+ function prevent_wc_breadcrumb_paged( $crumbs, $breadcrumb ) {
+
+ if ( function_exists( 'FWP' ) && !empty( FWP()->request->url_vars ) ) {
+ if ( get_query_var( 'paged' ) >= 1 && 'subcategories' !== woocommerce_get_loop_display_mode() ) {
+ array_pop( $crumbs );
+ }
+ }
+
+ return $crumbs;
+ }
+
+}
+
+
+if ( is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
+ new FacetWP_Integration_WooCommerce();
+}
diff --git a/wp-content/plugins/facetwp/includes/integrations/wp-cli/wp-cli.php b/wp-content/plugins/facetwp/includes/integrations/wp-cli/wp-cli.php
new file mode 100644
index 000000000..9cf36073f
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/integrations/wp-cli/wp-cli.php
@@ -0,0 +1,150 @@
+]
+ * : Index specific post IDs (comma-separated).
+ *
+ * [--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=]
+ * : Purge specific post IDs (comma-separated).
+ *
+ * [--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' );
+}
diff --git a/wp-content/plugins/facetwp/includes/integrations/wp-rocket/wp-rocket.php b/wp-content/plugins/facetwp/includes/integrations/wp-rocket/wp-rocket.php
new file mode 100644
index 000000000..b0799aaee
--- /dev/null
+++ b/wp-content/plugins/facetwp/includes/integrations/wp-rocket/wp-rocket.php
@@ -0,0 +1,24 @@
+.
+*/
+
+defined( 'ABSPATH' ) or exit;
+
+class FacetWP
+{
+
+ private static $instance;
+
+ public $filtered_post_ids;
+ public $unfiltered_post_ids;
+ public $is_filtered;
+ public $is_modified;
+ public $or_values;
+
+ public $init;
+ public $api;
+ public $helper;
+ public $facet;
+ public $settings;
+ public $diff;
+ public $indexer;
+ public $display;
+ public $builder;
+ public $request;
+ public $ajax;
+ public $acf;
+
+
+ function __construct() {
+
+ // php check
+ if ( version_compare( phpversion(), '7.0', '<' ) ) {
+ add_action( 'admin_notices', array( $this, 'upgrade_notice' ) );
+ return;
+ }
+
+ // setup variables
+ define( 'FACETWP_VERSION', '4.4.1' );
+ define( 'FACETWP_DIR', dirname( __FILE__ ) );
+ define( 'FACETWP_URL', plugins_url( '', __FILE__ ) );
+ define( 'FACETWP_BASENAME', plugin_basename( __FILE__ ) );
+
+ // get the gears turning
+ include( FACETWP_DIR . '/includes/class-init.php' );
+ }
+
+
+ /**
+ * Singleton
+ */
+ public static function instance() {
+ if ( ! isset( self::$instance ) ) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+
+ /**
+ * Require PHP 7.0+
+ */
+ function upgrade_notice() {
+ $message = __( 'FacetWP requires PHP %s or above. Please contact your host and request a PHP upgrade.', 'fwp' );
+ echo '' . sprintf( $message, '7.0' ) . '
';
+ }
+}
+
+
+function FWP() {
+ return FacetWP::instance();
+}
+
+
+FWP();
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-ca.mo b/wp-content/plugins/facetwp/languages/fwp-front-ca.mo
new file mode 100644
index 000000000..f2315316e
Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-ca.mo differ
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-ca.po b/wp-content/plugins/facetwp/languages/fwp-front-ca.po
new file mode 100644
index 000000000..99f0afccb
--- /dev/null
+++ b/wp-content/plugins/facetwp/languages/fwp-front-ca.po
@@ -0,0 +1,226 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: FacetWP (front)\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-02-21 14:01+0000\n"
+"PO-Revision-Date: 2025-04-18 10:44+0000\n"
+"Last-Translator: \n"
+"Language-Team: Catalan\n"
+"Language: ca\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Loco https://localise.biz/\n"
+"X-Loco-Version: 2.7.1; wp-6.7.1\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: includes/facets/rating.php:165
+msgid "& up"
+msgstr "& up"
+
+#. Description of the plugin
+#: index.php
+msgid "Advanced Filtering for WordPress"
+msgstr ""
+
+#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37
+#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26
+msgid "Any"
+msgstr "Qualsevol"
+
+#: includes/facets/date_range.php:300
+msgid "Clear"
+msgstr "Esborrar"
+
+#: includes/facets/proximity.php:243
+msgid "Clear location"
+msgstr "Localització de clara"
+
+#: includes/facets/date_range.php:21
+msgid "Date"
+msgstr "Data"
+
+#: includes/facets/sort.php:180
+msgid "Date (Newest)"
+msgstr "Data (més recent)"
+
+#: includes/facets/sort.php:187
+msgid "Date (Oldest)"
+msgstr "Data (més antic)"
+
+#: includes/facets/proximity.php:357
+msgid "Distance"
+msgstr "Distància"
+
+#: includes/facets/map.php:397
+msgid "Enable map filtering"
+msgstr ""
+
+#: includes/facets/date_range.php:23
+msgid "End date"
+msgstr "Data final"
+
+#: includes/facets/search.php:23
+msgid "Enter keywords"
+msgstr "Introduïu les paraules clau"
+
+#: includes/facets/proximity.php:52
+msgid "Enter location"
+msgstr "Introduïu la ubicació"
+
+#: includes/facets/autocomplete.php:165
+msgid "Enter {n} or more characters"
+msgstr ""
+
+#. Plugin Name of the plugin
+#: index.php
+msgid "FacetWP"
+msgstr ""
+
+#. Author of the plugin
+#: index.php
+msgid "FacetWP, LLC"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:532
+msgid "Featured"
+msgstr ""
+
+#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32
+msgid "Go"
+msgstr "Cercar"
+
+#: includes/class-display.php:208
+msgid "Go to next page"
+msgstr ""
+
+#: includes/class-display.php:207
+msgid "Go to page"
+msgstr ""
+
+#: includes/class-display.php:209
+msgid "Go to previous page"
+msgstr ""
+
+#. Author URI of the plugin
+#: index.php
+msgid "https://facetwp.com/"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:522
+msgid "In Stock"
+msgstr "En Stock"
+
+#: includes/facets/proximity.php:51
+msgid "km"
+msgstr ""
+
+#: includes/facets/autocomplete.php:164
+msgid "Loading"
+msgstr ""
+
+#: includes/facets/number_range.php:29
+msgid "Max"
+msgstr "Màx"
+
+#: includes/facets/proximity.php:51
+msgid "mi"
+msgstr ""
+
+#: includes/facets/number_range.php:26
+msgid "Min"
+msgstr "Min"
+
+#: includes/integrations/acf/acf.php:325
+msgid "No"
+msgstr "No"
+
+#: includes/facets/date_range.php:25
+msgid "No dates"
+msgstr ""
+
+#: includes/facets/date_range.php:27
+msgid "No end dates"
+msgstr ""
+
+#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166
+msgid "No results"
+msgstr "No hi ha resultats"
+
+#: includes/class-display.php:227 includes/facets/fselect.php:87
+msgid "No results found"
+msgstr "No s'han trobat resultats"
+
+#: includes/facets/date_range.php:26
+msgid "No start dates"
+msgstr ""
+
+#: includes/facets/number_range.php:23
+msgid "Number"
+msgstr "Número"
+
+#: includes/class-renderer.php:535
+msgid "of"
+msgstr "de"
+
+#: includes/integrations/woocommerce/woocommerce.php:529
+msgid "On Sale"
+msgstr "Oferta"
+
+#: includes/integrations/woocommerce/woocommerce.php:525
+msgid "Out of Stock"
+msgstr "Sense Stock"
+
+#: includes/class-renderer.php:592
+msgid "Per page"
+msgstr "Per pàgina"
+
+#: includes/facets/map.php:400 includes/facets/reset.php:15
+#: includes/facets/slider.php:20
+msgid "Reset"
+msgstr "Restablir"
+
+#: includes/facets/fselect.php:86
+msgid "Search"
+msgstr "Cercar"
+
+#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164
+msgid "See less"
+msgstr "Veure menys"
+
+#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163
+msgid "See {num} more"
+msgstr "Veure més {num}"
+
+#: includes/facets/sort.php:162
+msgid "Sort by"
+msgstr "Ordena per"
+
+#: includes/facets/date_range.php:22
+msgid "Start date"
+msgstr "Data inici"
+
+#: includes/facets/autocomplete.php:63
+msgid "Start typing"
+msgstr "Comenceu a escriure"
+
+#: includes/facets/sort.php:166
+msgid "Title (A-Z)"
+msgstr "Títol (A-Z)"
+
+#: includes/facets/sort.php:173
+msgid "Title (Z-A)"
+msgstr "Títol (Z-A)"
+
+#: includes/facets/rating.php:166
+msgid "Undo"
+msgstr "Desfer"
+
+#: includes/integrations/acf/acf.php:325
+msgid "Yes"
+msgstr "Sí"
+
+#: includes/facets/fselect.php:85
+msgid "{n} selected"
+msgstr "{n} seleccionats"
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-da_DK.mo b/wp-content/plugins/facetwp/languages/fwp-front-da_DK.mo
new file mode 100644
index 000000000..bfc0777ef
Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-da_DK.mo differ
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-da_DK.po b/wp-content/plugins/facetwp/languages/fwp-front-da_DK.po
new file mode 100644
index 000000000..227a0ce9f
--- /dev/null
+++ b/wp-content/plugins/facetwp/languages/fwp-front-da_DK.po
@@ -0,0 +1,226 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: FacetWP (front)\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-02-21 14:01+0000\n"
+"PO-Revision-Date: 2025-04-18 10:45+0000\n"
+"Last-Translator: \n"
+"Language-Team: Danish\n"
+"Language: da-DK\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Loco https://localise.biz/\n"
+"X-Loco-Version: 2.7.1; wp-6.7.1\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: includes/facets/rating.php:165
+msgid "& up"
+msgstr "& op"
+
+#. Description of the plugin
+#: index.php
+msgid "Advanced Filtering for WordPress"
+msgstr ""
+
+#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37
+#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26
+msgid "Any"
+msgstr "Enhver"
+
+#: includes/facets/date_range.php:300
+msgid "Clear"
+msgstr "Ryd"
+
+#: includes/facets/proximity.php:243
+msgid "Clear location"
+msgstr "Rud beliggenhed"
+
+#: includes/facets/date_range.php:21
+msgid "Date"
+msgstr "Dato"
+
+#: includes/facets/sort.php:180
+msgid "Date (Newest)"
+msgstr "Dato (nyeste)"
+
+#: includes/facets/sort.php:187
+msgid "Date (Oldest)"
+msgstr "Dato (ældste)"
+
+#: includes/facets/proximity.php:357
+msgid "Distance"
+msgstr "Afstand"
+
+#: includes/facets/map.php:397
+msgid "Enable map filtering"
+msgstr ""
+
+#: includes/facets/date_range.php:23
+msgid "End date"
+msgstr "Slut dato"
+
+#: includes/facets/search.php:23
+msgid "Enter keywords"
+msgstr "Indtast søgeord"
+
+#: includes/facets/proximity.php:52
+msgid "Enter location"
+msgstr "Indtast lokation"
+
+#: includes/facets/autocomplete.php:165
+msgid "Enter {n} or more characters"
+msgstr ""
+
+#. Plugin Name of the plugin
+#: index.php
+msgid "FacetWP"
+msgstr ""
+
+#. Author of the plugin
+#: index.php
+msgid "FacetWP, LLC"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:532
+msgid "Featured"
+msgstr ""
+
+#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32
+msgid "Go"
+msgstr "Gå"
+
+#: includes/class-display.php:208
+msgid "Go to next page"
+msgstr ""
+
+#: includes/class-display.php:207
+msgid "Go to page"
+msgstr ""
+
+#: includes/class-display.php:209
+msgid "Go to previous page"
+msgstr ""
+
+#. Author URI of the plugin
+#: index.php
+msgid "https://facetwp.com/"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:522
+msgid "In Stock"
+msgstr "På lager"
+
+#: includes/facets/proximity.php:51
+msgid "km"
+msgstr ""
+
+#: includes/facets/autocomplete.php:164
+msgid "Loading"
+msgstr ""
+
+#: includes/facets/number_range.php:29
+msgid "Max"
+msgstr "Maks"
+
+#: includes/facets/proximity.php:51
+msgid "mi"
+msgstr ""
+
+#: includes/facets/number_range.php:26
+msgid "Min"
+msgstr "Min"
+
+#: includes/integrations/acf/acf.php:325
+msgid "No"
+msgstr "Nej"
+
+#: includes/facets/date_range.php:25
+msgid "No dates"
+msgstr ""
+
+#: includes/facets/date_range.php:27
+msgid "No end dates"
+msgstr ""
+
+#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166
+msgid "No results"
+msgstr "Ingen resultater"
+
+#: includes/class-display.php:227 includes/facets/fselect.php:87
+msgid "No results found"
+msgstr "Ingen resultater fundet"
+
+#: includes/facets/date_range.php:26
+msgid "No start dates"
+msgstr ""
+
+#: includes/facets/number_range.php:23
+msgid "Number"
+msgstr "Nummer"
+
+#: includes/class-renderer.php:535
+msgid "of"
+msgstr "af"
+
+#: includes/integrations/woocommerce/woocommerce.php:529
+msgid "On Sale"
+msgstr "Udsalg"
+
+#: includes/integrations/woocommerce/woocommerce.php:525
+msgid "Out of Stock"
+msgstr "Ikke på lager"
+
+#: includes/class-renderer.php:592
+msgid "Per page"
+msgstr "Per side"
+
+#: includes/facets/map.php:400 includes/facets/reset.php:15
+#: includes/facets/slider.php:20
+msgid "Reset"
+msgstr "Nulstil"
+
+#: includes/facets/fselect.php:86
+msgid "Search"
+msgstr "Søg"
+
+#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164
+msgid "See less"
+msgstr "Se mindre"
+
+#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163
+msgid "See {num} more"
+msgstr "Se {num} mere"
+
+#: includes/facets/sort.php:162
+msgid "Sort by"
+msgstr "Sorter efter"
+
+#: includes/facets/date_range.php:22
+msgid "Start date"
+msgstr "Start dato"
+
+#: includes/facets/autocomplete.php:63
+msgid "Start typing"
+msgstr "Begynd at skrive"
+
+#: includes/facets/sort.php:166
+msgid "Title (A-Z)"
+msgstr "Titel (A - Å)"
+
+#: includes/facets/sort.php:173
+msgid "Title (Z-A)"
+msgstr "Titel (Å-A)"
+
+#: includes/facets/rating.php:166
+msgid "Undo"
+msgstr "Fortryd"
+
+#: includes/integrations/acf/acf.php:325
+msgid "Yes"
+msgstr "Ja"
+
+#: includes/facets/fselect.php:85
+msgid "{n} selected"
+msgstr "{n} valgt"
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-de_DE.mo b/wp-content/plugins/facetwp/languages/fwp-front-de_DE.mo
new file mode 100644
index 000000000..3d97bf780
Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-de_DE.mo differ
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-de_DE.po b/wp-content/plugins/facetwp/languages/fwp-front-de_DE.po
new file mode 100644
index 000000000..90c4383c6
--- /dev/null
+++ b/wp-content/plugins/facetwp/languages/fwp-front-de_DE.po
@@ -0,0 +1,226 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: FacetWP (front)\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-02-21 14:01+0000\n"
+"PO-Revision-Date: 2025-04-18 10:28+0000\n"
+"Last-Translator: \n"
+"Language-Team: German\n"
+"Language: de-DE\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Loco https://localise.biz/\n"
+"X-Loco-Version: 2.7.1; wp-6.7.1\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: includes/facets/rating.php:165
+msgid "& up"
+msgstr "& up"
+
+#. Description of the plugin
+#: index.php
+msgid "Advanced Filtering for WordPress"
+msgstr ""
+
+#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37
+#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26
+msgid "Any"
+msgstr "Alle"
+
+#: includes/facets/date_range.php:300
+msgid "Clear"
+msgstr "Zurücksetzen"
+
+#: includes/facets/proximity.php:243
+msgid "Clear location"
+msgstr "Location löschen"
+
+#: includes/facets/date_range.php:21
+msgid "Date"
+msgstr "Datum"
+
+#: includes/facets/sort.php:180
+msgid "Date (Newest)"
+msgstr "Datum (Neueste)"
+
+#: includes/facets/sort.php:187
+msgid "Date (Oldest)"
+msgstr "Datum (älteste)"
+
+#: includes/facets/proximity.php:357
+msgid "Distance"
+msgstr "Entfernung"
+
+#: includes/facets/map.php:397
+msgid "Enable map filtering"
+msgstr ""
+
+#: includes/facets/date_range.php:23
+msgid "End date"
+msgstr "Enddatum"
+
+#: includes/facets/search.php:23
+msgid "Enter keywords"
+msgstr "Suchbegriff eingeben"
+
+#: includes/facets/proximity.php:52
+msgid "Enter location"
+msgstr "Geben Sie den Ort an"
+
+#: includes/facets/autocomplete.php:165
+msgid "Enter {n} or more characters"
+msgstr ""
+
+#. Plugin Name of the plugin
+#: index.php
+msgid "FacetWP"
+msgstr ""
+
+#. Author of the plugin
+#: index.php
+msgid "FacetWP, LLC"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:532
+msgid "Featured"
+msgstr ""
+
+#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32
+msgid "Go"
+msgstr "Los"
+
+#: includes/class-display.php:208
+msgid "Go to next page"
+msgstr ""
+
+#: includes/class-display.php:207
+msgid "Go to page"
+msgstr ""
+
+#: includes/class-display.php:209
+msgid "Go to previous page"
+msgstr ""
+
+#. Author URI of the plugin
+#: index.php
+msgid "https://facetwp.com/"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:522
+msgid "In Stock"
+msgstr "Auf Lager"
+
+#: includes/facets/proximity.php:51
+msgid "km"
+msgstr "km"
+
+#: includes/facets/autocomplete.php:164
+msgid "Loading"
+msgstr ""
+
+#: includes/facets/number_range.php:29
+msgid "Max"
+msgstr "Max"
+
+#: includes/facets/proximity.php:51
+msgid "mi"
+msgstr "mi"
+
+#: includes/facets/number_range.php:26
+msgid "Min"
+msgstr "Min"
+
+#: includes/integrations/acf/acf.php:325
+msgid "No"
+msgstr "Nein"
+
+#: includes/facets/date_range.php:25
+msgid "No dates"
+msgstr ""
+
+#: includes/facets/date_range.php:27
+msgid "No end dates"
+msgstr ""
+
+#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166
+msgid "No results"
+msgstr "Keine Ergebnisse"
+
+#: includes/class-display.php:227 includes/facets/fselect.php:87
+msgid "No results found"
+msgstr "Keine Ergebnisse gefunden"
+
+#: includes/facets/date_range.php:26
+msgid "No start dates"
+msgstr ""
+
+#: includes/facets/number_range.php:23
+msgid "Number"
+msgstr "Nummer"
+
+#: includes/class-renderer.php:535
+msgid "of"
+msgstr "von"
+
+#: includes/integrations/woocommerce/woocommerce.php:529
+msgid "On Sale"
+msgstr "Im Angebot"
+
+#: includes/integrations/woocommerce/woocommerce.php:525
+msgid "Out of Stock"
+msgstr "Ausverkauft"
+
+#: includes/class-renderer.php:592
+msgid "Per page"
+msgstr "Pro Seite"
+
+#: includes/facets/map.php:400 includes/facets/reset.php:15
+#: includes/facets/slider.php:20
+msgid "Reset"
+msgstr "Zurücksetzen"
+
+#: includes/facets/fselect.php:86
+msgid "Search"
+msgstr "Suche"
+
+#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164
+msgid "See less"
+msgstr "Weniger sehen"
+
+#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163
+msgid "See {num} more"
+msgstr "{num} weitere anzeigen"
+
+#: includes/facets/sort.php:162
+msgid "Sort by"
+msgstr "Sortieren nach"
+
+#: includes/facets/date_range.php:22
+msgid "Start date"
+msgstr "Startdatum"
+
+#: includes/facets/autocomplete.php:63
+msgid "Start typing"
+msgstr "Adresse eingeben"
+
+#: includes/facets/sort.php:166
+msgid "Title (A-Z)"
+msgstr "Titel (A-Z)"
+
+#: includes/facets/sort.php:173
+msgid "Title (Z-A)"
+msgstr "Titel (Z-A)"
+
+#: includes/facets/rating.php:166
+msgid "Undo"
+msgstr "Rückgängig"
+
+#: includes/integrations/acf/acf.php:325
+msgid "Yes"
+msgstr "Ja"
+
+#: includes/facets/fselect.php:85
+msgid "{n} selected"
+msgstr "{n} ausgewählt"
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-es_AR.mo b/wp-content/plugins/facetwp/languages/fwp-front-es_AR.mo
new file mode 100644
index 000000000..6773f8380
Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-es_AR.mo differ
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-es_AR.po b/wp-content/plugins/facetwp/languages/fwp-front-es_AR.po
new file mode 100644
index 000000000..c94937a5c
--- /dev/null
+++ b/wp-content/plugins/facetwp/languages/fwp-front-es_AR.po
@@ -0,0 +1,226 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: FacetWP (front)\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-02-21 14:01+0000\n"
+"PO-Revision-Date: 2025-04-18 10:46+0000\n"
+"Last-Translator: \n"
+"Language-Team: Spanish (Argentina)\n"
+"Language: es-AR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Loco https://localise.biz/\n"
+"X-Loco-Version: 2.7.1; wp-6.7.1\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: includes/facets/rating.php:165
+msgid "& up"
+msgstr "& up"
+
+#. Description of the plugin
+#: index.php
+msgid "Advanced Filtering for WordPress"
+msgstr ""
+
+#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37
+#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26
+msgid "Any"
+msgstr "Cualquier"
+
+#: includes/facets/date_range.php:300
+msgid "Clear"
+msgstr "Limpiar"
+
+#: includes/facets/proximity.php:243
+msgid "Clear location"
+msgstr "Borrar Ubicación"
+
+#: includes/facets/date_range.php:21
+msgid "Date"
+msgstr "Fecha"
+
+#: includes/facets/sort.php:180
+msgid "Date (Newest)"
+msgstr "Fecha (más reciente)"
+
+#: includes/facets/sort.php:187
+msgid "Date (Oldest)"
+msgstr "Fecha (más antigua)"
+
+#: includes/facets/proximity.php:357
+msgid "Distance"
+msgstr "Distancia"
+
+#: includes/facets/map.php:397
+msgid "Enable map filtering"
+msgstr ""
+
+#: includes/facets/date_range.php:23
+msgid "End date"
+msgstr "Último día"
+
+#: includes/facets/search.php:23
+msgid "Enter keywords"
+msgstr "Ingrese palabras claves"
+
+#: includes/facets/proximity.php:52
+msgid "Enter location"
+msgstr "Introducir ubicación"
+
+#: includes/facets/autocomplete.php:165
+msgid "Enter {n} or more characters"
+msgstr ""
+
+#. Plugin Name of the plugin
+#: index.php
+msgid "FacetWP"
+msgstr ""
+
+#. Author of the plugin
+#: index.php
+msgid "FacetWP, LLC"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:532
+msgid "Featured"
+msgstr ""
+
+#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32
+msgid "Go"
+msgstr "Ir"
+
+#: includes/class-display.php:208
+msgid "Go to next page"
+msgstr ""
+
+#: includes/class-display.php:207
+msgid "Go to page"
+msgstr ""
+
+#: includes/class-display.php:209
+msgid "Go to previous page"
+msgstr ""
+
+#. Author URI of the plugin
+#: index.php
+msgid "https://facetwp.com/"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:522
+msgid "In Stock"
+msgstr "En Stock"
+
+#: includes/facets/proximity.php:51
+msgid "km"
+msgstr ""
+
+#: includes/facets/autocomplete.php:164
+msgid "Loading"
+msgstr ""
+
+#: includes/facets/number_range.php:29
+msgid "Max"
+msgstr "Max"
+
+#: includes/facets/proximity.php:51
+msgid "mi"
+msgstr ""
+
+#: includes/facets/number_range.php:26
+msgid "Min"
+msgstr "Minutos"
+
+#: includes/integrations/acf/acf.php:325
+msgid "No"
+msgstr "No"
+
+#: includes/facets/date_range.php:25
+msgid "No dates"
+msgstr ""
+
+#: includes/facets/date_range.php:27
+msgid "No end dates"
+msgstr ""
+
+#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166
+msgid "No results"
+msgstr "No hay resultados"
+
+#: includes/class-display.php:227 includes/facets/fselect.php:87
+msgid "No results found"
+msgstr "No se encontraron resultados"
+
+#: includes/facets/date_range.php:26
+msgid "No start dates"
+msgstr ""
+
+#: includes/facets/number_range.php:23
+msgid "Number"
+msgstr "Número"
+
+#: includes/class-renderer.php:535
+msgid "of"
+msgstr "de"
+
+#: includes/integrations/woocommerce/woocommerce.php:529
+msgid "On Sale"
+msgstr "A la venta"
+
+#: includes/integrations/woocommerce/woocommerce.php:525
+msgid "Out of Stock"
+msgstr "Fuera de Stock"
+
+#: includes/class-renderer.php:592
+msgid "Per page"
+msgstr "Por página"
+
+#: includes/facets/map.php:400 includes/facets/reset.php:15
+#: includes/facets/slider.php:20
+msgid "Reset"
+msgstr "Reiniciar"
+
+#: includes/facets/fselect.php:86
+msgid "Search"
+msgstr "Buscar"
+
+#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164
+msgid "See less"
+msgstr "Ver menos"
+
+#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163
+msgid "See {num} more"
+msgstr "Ver {num} más"
+
+#: includes/facets/sort.php:162
+msgid "Sort by"
+msgstr "Ordenar por"
+
+#: includes/facets/date_range.php:22
+msgid "Start date"
+msgstr "Fecha de Comienzo"
+
+#: includes/facets/autocomplete.php:63
+msgid "Start typing"
+msgstr "Comience a escribir"
+
+#: includes/facets/sort.php:166
+msgid "Title (A-Z)"
+msgstr "Título (A-Z)"
+
+#: includes/facets/sort.php:173
+msgid "Title (Z-A)"
+msgstr "Título (Z-A)"
+
+#: includes/facets/rating.php:166
+msgid "Undo"
+msgstr "Deshacer"
+
+#: includes/integrations/acf/acf.php:325
+msgid "Yes"
+msgstr "Sí"
+
+#: includes/facets/fselect.php:85
+msgid "{n} selected"
+msgstr "{n} selecciones"
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-es_ES.mo b/wp-content/plugins/facetwp/languages/fwp-front-es_ES.mo
new file mode 100644
index 000000000..8b509b618
Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-es_ES.mo differ
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-es_ES.po b/wp-content/plugins/facetwp/languages/fwp-front-es_ES.po
new file mode 100644
index 000000000..2616aa6fe
--- /dev/null
+++ b/wp-content/plugins/facetwp/languages/fwp-front-es_ES.po
@@ -0,0 +1,226 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: FacetWP (front)\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-02-21 14:01+0000\n"
+"PO-Revision-Date: 2025-04-18 10:47+0000\n"
+"Last-Translator: \n"
+"Language-Team: Spanish (Spain)\n"
+"Language: es-ES\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Loco https://localise.biz/\n"
+"X-Loco-Version: 2.7.1; wp-6.7.1\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: includes/facets/rating.php:165
+msgid "& up"
+msgstr "& up"
+
+#. Description of the plugin
+#: index.php
+msgid "Advanced Filtering for WordPress"
+msgstr ""
+
+#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37
+#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26
+msgid "Any"
+msgstr "Cualquiera"
+
+#: includes/facets/date_range.php:300
+msgid "Clear"
+msgstr "Limpiar"
+
+#: includes/facets/proximity.php:243
+msgid "Clear location"
+msgstr "Borrar ubicación"
+
+#: includes/facets/date_range.php:21
+msgid "Date"
+msgstr "Fecha"
+
+#: includes/facets/sort.php:180
+msgid "Date (Newest)"
+msgstr "Fecha (más reciente)"
+
+#: includes/facets/sort.php:187
+msgid "Date (Oldest)"
+msgstr "Fecha (más antigua)"
+
+#: includes/facets/proximity.php:357
+msgid "Distance"
+msgstr "Distancia"
+
+#: includes/facets/map.php:397
+msgid "Enable map filtering"
+msgstr ""
+
+#: includes/facets/date_range.php:23
+msgid "End date"
+msgstr "Fecha de finalización"
+
+#: includes/facets/search.php:23
+msgid "Enter keywords"
+msgstr "¿Qué estás buscando?"
+
+#: includes/facets/proximity.php:52
+msgid "Enter location"
+msgstr "Introducir ubicación"
+
+#: includes/facets/autocomplete.php:165
+msgid "Enter {n} or more characters"
+msgstr ""
+
+#. Plugin Name of the plugin
+#: index.php
+msgid "FacetWP"
+msgstr ""
+
+#. Author of the plugin
+#: index.php
+msgid "FacetWP, LLC"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:532
+msgid "Featured"
+msgstr ""
+
+#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32
+msgid "Go"
+msgstr "Ir"
+
+#: includes/class-display.php:208
+msgid "Go to next page"
+msgstr ""
+
+#: includes/class-display.php:207
+msgid "Go to page"
+msgstr ""
+
+#: includes/class-display.php:209
+msgid "Go to previous page"
+msgstr ""
+
+#. Author URI of the plugin
+#: index.php
+msgid "https://facetwp.com/"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:522
+msgid "In Stock"
+msgstr "En Stock"
+
+#: includes/facets/proximity.php:51
+msgid "km"
+msgstr ""
+
+#: includes/facets/autocomplete.php:164
+msgid "Loading"
+msgstr ""
+
+#: includes/facets/number_range.php:29
+msgid "Max"
+msgstr "Máx"
+
+#: includes/facets/proximity.php:51
+msgid "mi"
+msgstr ""
+
+#: includes/facets/number_range.php:26
+msgid "Min"
+msgstr "Mín"
+
+#: includes/integrations/acf/acf.php:325
+msgid "No"
+msgstr "No"
+
+#: includes/facets/date_range.php:25
+msgid "No dates"
+msgstr ""
+
+#: includes/facets/date_range.php:27
+msgid "No end dates"
+msgstr ""
+
+#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166
+msgid "No results"
+msgstr "No hay resultados"
+
+#: includes/class-display.php:227 includes/facets/fselect.php:87
+msgid "No results found"
+msgstr "No se han encontrado resultados"
+
+#: includes/facets/date_range.php:26
+msgid "No start dates"
+msgstr ""
+
+#: includes/facets/number_range.php:23
+msgid "Number"
+msgstr "Número"
+
+#: includes/class-renderer.php:535
+msgid "of"
+msgstr "de"
+
+#: includes/integrations/woocommerce/woocommerce.php:529
+msgid "On Sale"
+msgstr "En oferta"
+
+#: includes/integrations/woocommerce/woocommerce.php:525
+msgid "Out of Stock"
+msgstr "Agotado"
+
+#: includes/class-renderer.php:592
+msgid "Per page"
+msgstr "Por página"
+
+#: includes/facets/map.php:400 includes/facets/reset.php:15
+#: includes/facets/slider.php:20
+msgid "Reset"
+msgstr "Reiniciar"
+
+#: includes/facets/fselect.php:86
+msgid "Search"
+msgstr "Buscar"
+
+#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164
+msgid "See less"
+msgstr "Ver menos"
+
+#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163
+msgid "See {num} more"
+msgstr "Ver {num} más"
+
+#: includes/facets/sort.php:162
+msgid "Sort by"
+msgstr "Ordenar por"
+
+#: includes/facets/date_range.php:22
+msgid "Start date"
+msgstr "Fecha de inicio"
+
+#: includes/facets/autocomplete.php:63
+msgid "Start typing"
+msgstr "Escribe algo"
+
+#: includes/facets/sort.php:166
+msgid "Title (A-Z)"
+msgstr "Título (A-Z)"
+
+#: includes/facets/sort.php:173
+msgid "Title (Z-A)"
+msgstr "Título (Z-A)"
+
+#: includes/facets/rating.php:166
+msgid "Undo"
+msgstr "Deshacer"
+
+#: includes/integrations/acf/acf.php:325
+msgid "Yes"
+msgstr "Sí"
+
+#: includes/facets/fselect.php:85
+msgid "{n} selected"
+msgstr "{n} selecciones"
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-fr_FR.mo b/wp-content/plugins/facetwp/languages/fwp-front-fr_FR.mo
new file mode 100644
index 000000000..41f266dbf
Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-fr_FR.mo differ
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-fr_FR.po b/wp-content/plugins/facetwp/languages/fwp-front-fr_FR.po
new file mode 100644
index 000000000..62af9f58c
--- /dev/null
+++ b/wp-content/plugins/facetwp/languages/fwp-front-fr_FR.po
@@ -0,0 +1,226 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: FacetWP (front)\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-02-21 14:01+0000\n"
+"PO-Revision-Date: 2025-04-18 10:47+0000\n"
+"Last-Translator: \n"
+"Language-Team: French (France)\n"
+"Language: fr-FR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Loco https://localise.biz/\n"
+"X-Loco-Version: 2.7.1; wp-6.7.1\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: includes/facets/rating.php:165
+msgid "& up"
+msgstr "& up"
+
+#. Description of the plugin
+#: index.php
+msgid "Advanced Filtering for WordPress"
+msgstr ""
+
+#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37
+#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26
+msgid "Any"
+msgstr "Tous"
+
+#: includes/facets/date_range.php:300
+msgid "Clear"
+msgstr "Effacer"
+
+#: includes/facets/proximity.php:243
+msgid "Clear location"
+msgstr "Effacer l’emplacement"
+
+#: includes/facets/date_range.php:21
+msgid "Date"
+msgstr "Date"
+
+#: includes/facets/sort.php:180
+msgid "Date (Newest)"
+msgstr "Date (plus récent)"
+
+#: includes/facets/sort.php:187
+msgid "Date (Oldest)"
+msgstr "Date (la plus ancienne)"
+
+#: includes/facets/proximity.php:357
+msgid "Distance"
+msgstr "Distance"
+
+#: includes/facets/map.php:397
+msgid "Enable map filtering"
+msgstr ""
+
+#: includes/facets/date_range.php:23
+msgid "End date"
+msgstr "Date de fin"
+
+#: includes/facets/search.php:23
+msgid "Enter keywords"
+msgstr "Rechercher par nom"
+
+#: includes/facets/proximity.php:52
+msgid "Enter location"
+msgstr "Entrez la localisation"
+
+#: includes/facets/autocomplete.php:165
+msgid "Enter {n} or more characters"
+msgstr ""
+
+#. Plugin Name of the plugin
+#: index.php
+msgid "FacetWP"
+msgstr ""
+
+#. Author of the plugin
+#: index.php
+msgid "FacetWP, LLC"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:532
+msgid "Featured"
+msgstr ""
+
+#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32
+msgid "Go"
+msgstr "Aller"
+
+#: includes/class-display.php:208
+msgid "Go to next page"
+msgstr ""
+
+#: includes/class-display.php:207
+msgid "Go to page"
+msgstr ""
+
+#: includes/class-display.php:209
+msgid "Go to previous page"
+msgstr ""
+
+#. Author URI of the plugin
+#: index.php
+msgid "https://facetwp.com/"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:522
+msgid "In Stock"
+msgstr "En stock"
+
+#: includes/facets/proximity.php:51
+msgid "km"
+msgstr ""
+
+#: includes/facets/autocomplete.php:164
+msgid "Loading"
+msgstr ""
+
+#: includes/facets/number_range.php:29
+msgid "Max"
+msgstr "Max"
+
+#: includes/facets/proximity.php:51
+msgid "mi"
+msgstr ""
+
+#: includes/facets/number_range.php:26
+msgid "Min"
+msgstr "Min"
+
+#: includes/integrations/acf/acf.php:325
+msgid "No"
+msgstr "Non"
+
+#: includes/facets/date_range.php:25
+msgid "No dates"
+msgstr ""
+
+#: includes/facets/date_range.php:27
+msgid "No end dates"
+msgstr ""
+
+#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166
+msgid "No results"
+msgstr "Aucun résultat"
+
+#: includes/class-display.php:227 includes/facets/fselect.php:87
+msgid "No results found"
+msgstr "Aucun résultat trouvé"
+
+#: includes/facets/date_range.php:26
+msgid "No start dates"
+msgstr ""
+
+#: includes/facets/number_range.php:23
+msgid "Number"
+msgstr "Nombre"
+
+#: includes/class-renderer.php:535
+msgid "of"
+msgstr "de"
+
+#: includes/integrations/woocommerce/woocommerce.php:529
+msgid "On Sale"
+msgstr "Promo"
+
+#: includes/integrations/woocommerce/woocommerce.php:525
+msgid "Out of Stock"
+msgstr "Rupture de stock"
+
+#: includes/class-renderer.php:592
+msgid "Per page"
+msgstr "Par page"
+
+#: includes/facets/map.php:400 includes/facets/reset.php:15
+#: includes/facets/slider.php:20
+msgid "Reset"
+msgstr "Réinitialiser"
+
+#: includes/facets/fselect.php:86
+msgid "Search"
+msgstr "Rechercher"
+
+#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164
+msgid "See less"
+msgstr "Voir moins"
+
+#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163
+msgid "See {num} more"
+msgstr "Voir {num} plus"
+
+#: includes/facets/sort.php:162
+msgid "Sort by"
+msgstr "Trier par"
+
+#: includes/facets/date_range.php:22
+msgid "Start date"
+msgstr "Date de début"
+
+#: includes/facets/autocomplete.php:63
+msgid "Start typing"
+msgstr "Commencer à taper"
+
+#: includes/facets/sort.php:166
+msgid "Title (A-Z)"
+msgstr "Titre (A-Z)"
+
+#: includes/facets/sort.php:173
+msgid "Title (Z-A)"
+msgstr "Titre (Z-A)"
+
+#: includes/facets/rating.php:166
+msgid "Undo"
+msgstr "Annuler"
+
+#: includes/integrations/acf/acf.php:325
+msgid "Yes"
+msgstr "Oui"
+
+#: includes/facets/fselect.php:85
+msgid "{n} selected"
+msgstr "{n} sélectionné"
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-it_IT.mo b/wp-content/plugins/facetwp/languages/fwp-front-it_IT.mo
new file mode 100644
index 000000000..7907c7398
Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-it_IT.mo differ
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-it_IT.po b/wp-content/plugins/facetwp/languages/fwp-front-it_IT.po
new file mode 100644
index 000000000..23e3d83a3
--- /dev/null
+++ b/wp-content/plugins/facetwp/languages/fwp-front-it_IT.po
@@ -0,0 +1,226 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: FacetWP (front)\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-02-21 14:01+0000\n"
+"PO-Revision-Date: 2025-04-18 10:48+0000\n"
+"Last-Translator: \n"
+"Language-Team: Italian\n"
+"Language: it-IT\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Loco https://localise.biz/\n"
+"X-Loco-Version: 2.7.1; wp-6.7.1\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: includes/facets/rating.php:165
+msgid "& up"
+msgstr "& up"
+
+#. Description of the plugin
+#: index.php
+msgid "Advanced Filtering for WordPress"
+msgstr ""
+
+#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37
+#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26
+msgid "Any"
+msgstr "Qualsiasi"
+
+#: includes/facets/date_range.php:300
+msgid "Clear"
+msgstr "Cancella"
+
+#: includes/facets/proximity.php:243
+msgid "Clear location"
+msgstr "Cancella località"
+
+#: includes/facets/date_range.php:21
+msgid "Date"
+msgstr "Data"
+
+#: includes/facets/sort.php:180
+msgid "Date (Newest)"
+msgstr "Data (più recente)"
+
+#: includes/facets/sort.php:187
+msgid "Date (Oldest)"
+msgstr "Data (più vecchia)"
+
+#: includes/facets/proximity.php:357
+msgid "Distance"
+msgstr "Distanza"
+
+#: includes/facets/map.php:397
+msgid "Enable map filtering"
+msgstr ""
+
+#: includes/facets/date_range.php:23
+msgid "End date"
+msgstr "Data Fine"
+
+#: includes/facets/search.php:23
+msgid "Enter keywords"
+msgstr "Inserisci parole chiave"
+
+#: includes/facets/proximity.php:52
+msgid "Enter location"
+msgstr "Inserisci posizione"
+
+#: includes/facets/autocomplete.php:165
+msgid "Enter {n} or more characters"
+msgstr ""
+
+#. Plugin Name of the plugin
+#: index.php
+msgid "FacetWP"
+msgstr ""
+
+#. Author of the plugin
+#: index.php
+msgid "FacetWP, LLC"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:532
+msgid "Featured"
+msgstr ""
+
+#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32
+msgid "Go"
+msgstr "Vai"
+
+#: includes/class-display.php:208
+msgid "Go to next page"
+msgstr ""
+
+#: includes/class-display.php:207
+msgid "Go to page"
+msgstr ""
+
+#: includes/class-display.php:209
+msgid "Go to previous page"
+msgstr ""
+
+#. Author URI of the plugin
+#: index.php
+msgid "https://facetwp.com/"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:522
+msgid "In Stock"
+msgstr "Disponibile"
+
+#: includes/facets/proximity.php:51
+msgid "km"
+msgstr ""
+
+#: includes/facets/autocomplete.php:164
+msgid "Loading"
+msgstr ""
+
+#: includes/facets/number_range.php:29
+msgid "Max"
+msgstr "Max"
+
+#: includes/facets/proximity.php:51
+msgid "mi"
+msgstr ""
+
+#: includes/facets/number_range.php:26
+msgid "Min"
+msgstr "Min"
+
+#: includes/integrations/acf/acf.php:325
+msgid "No"
+msgstr "No"
+
+#: includes/facets/date_range.php:25
+msgid "No dates"
+msgstr ""
+
+#: includes/facets/date_range.php:27
+msgid "No end dates"
+msgstr ""
+
+#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166
+msgid "No results"
+msgstr "Nessun risultato"
+
+#: includes/class-display.php:227 includes/facets/fselect.php:87
+msgid "No results found"
+msgstr "Nessun risultato trovato"
+
+#: includes/facets/date_range.php:26
+msgid "No start dates"
+msgstr ""
+
+#: includes/facets/number_range.php:23
+msgid "Number"
+msgstr "Numero"
+
+#: includes/class-renderer.php:535
+msgid "of"
+msgstr "di"
+
+#: includes/integrations/woocommerce/woocommerce.php:529
+msgid "On Sale"
+msgstr "Offerta"
+
+#: includes/integrations/woocommerce/woocommerce.php:525
+msgid "Out of Stock"
+msgstr "Esaurito"
+
+#: includes/class-renderer.php:592
+msgid "Per page"
+msgstr "Per pagina"
+
+#: includes/facets/map.php:400 includes/facets/reset.php:15
+#: includes/facets/slider.php:20
+msgid "Reset"
+msgstr "Reset"
+
+#: includes/facets/fselect.php:86
+msgid "Search"
+msgstr "Cerca"
+
+#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164
+msgid "See less"
+msgstr "Vedi di meno"
+
+#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163
+msgid "See {num} more"
+msgstr "Vedi altri {num}"
+
+#: includes/facets/sort.php:162
+msgid "Sort by"
+msgstr "Ordina per"
+
+#: includes/facets/date_range.php:22
+msgid "Start date"
+msgstr "Data Inizio"
+
+#: includes/facets/autocomplete.php:63
+msgid "Start typing"
+msgstr "Scrivi l'indirizzo o la città qui"
+
+#: includes/facets/sort.php:166
+msgid "Title (A-Z)"
+msgstr "Titoli (A-Z)"
+
+#: includes/facets/sort.php:173
+msgid "Title (Z-A)"
+msgstr "Titoli (Z-A)"
+
+#: includes/facets/rating.php:166
+msgid "Undo"
+msgstr "Annulla"
+
+#: includes/integrations/acf/acf.php:325
+msgid "Yes"
+msgstr "Sì"
+
+#: includes/facets/fselect.php:85
+msgid "{n} selected"
+msgstr "{n} selezionato"
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-nb_NO.mo b/wp-content/plugins/facetwp/languages/fwp-front-nb_NO.mo
new file mode 100644
index 000000000..9b99ea848
Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-nb_NO.mo differ
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-nb_NO.po b/wp-content/plugins/facetwp/languages/fwp-front-nb_NO.po
new file mode 100644
index 000000000..7bcde227e
--- /dev/null
+++ b/wp-content/plugins/facetwp/languages/fwp-front-nb_NO.po
@@ -0,0 +1,227 @@
+# Translation of Plugin: FacetWP - FacetWP frontend in Norwegian (Bokmål)
+# This file is distributed under the same license as the Plugin: FacetWP - FacetWP frontend package.
+msgid ""
+msgstr ""
+"PO-Revision-Date: 2025-04-18 10:49+0000\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Loco https://localise.biz/\n"
+"Language: nb-NO\n"
+"Project-Id-Version: Plugin: FacetWP - FacetWP frontend\n"
+"Language-Team: Norwegian (Bokmål)\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2025-04-18 10:49+0000\n"
+"Last-Translator: \n"
+"X-Loco-Version: 2.7.1; wp-6.7.1"
+
+#: includes/facets/rating.php:165
+msgid "& up"
+msgstr "& opp"
+
+#. Description of the plugin
+#: index.php
+msgid "Advanced Filtering for WordPress"
+msgstr ""
+
+#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37
+#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26
+msgid "Any"
+msgstr "Noen"
+
+#: includes/facets/date_range.php:300
+msgid "Clear"
+msgstr "Tøm"
+
+#: includes/facets/proximity.php:243
+msgid "Clear location"
+msgstr "Tøm sted"
+
+#: includes/facets/date_range.php:21
+msgid "Date"
+msgstr "Dato"
+
+#: includes/facets/sort.php:180
+msgid "Date (Newest)"
+msgstr "Dato (nyeste)"
+
+#: includes/facets/sort.php:187
+msgid "Date (Oldest)"
+msgstr "Dato (eldste)"
+
+#: includes/facets/proximity.php:357
+msgid "Distance"
+msgstr "Avstand"
+
+#: includes/facets/map.php:397
+msgid "Enable map filtering"
+msgstr ""
+
+#: includes/facets/date_range.php:23
+msgid "End date"
+msgstr "Sluttdato"
+
+#: includes/facets/search.php:23
+msgid "Enter keywords"
+msgstr "Skriv inn søkeord"
+
+#: includes/facets/proximity.php:52
+msgid "Enter location"
+msgstr "Angi plassering"
+
+#: includes/facets/autocomplete.php:165
+msgid "Enter {n} or more characters"
+msgstr "Skriv inn {n} eller flere tegn"
+
+#. Plugin Name of the plugin
+#: index.php
+msgid "FacetWP"
+msgstr ""
+
+#. Author of the plugin
+#: index.php
+msgid "FacetWP, LLC"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:532
+msgid "Featured"
+msgstr "Utvalgte"
+
+#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32
+msgid "Go"
+msgstr "Gå"
+
+#: includes/class-display.php:208
+msgid "Go to next page"
+msgstr ""
+
+#: includes/class-display.php:207
+msgid "Go to page"
+msgstr ""
+
+#: includes/class-display.php:209
+msgid "Go to previous page"
+msgstr ""
+
+#. Author URI of the plugin
+#: index.php
+msgid "https://facetwp.com/"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:522
+msgid "In Stock"
+msgstr "På lager"
+
+#: includes/facets/proximity.php:51
+msgid "km"
+msgstr ""
+
+#: includes/facets/autocomplete.php:164
+msgid "Loading"
+msgstr "Laster"
+
+#: includes/facets/number_range.php:29
+msgid "Max"
+msgstr "Maks"
+
+#: includes/facets/proximity.php:51
+msgid "mi"
+msgstr ""
+
+#: includes/facets/number_range.php:26
+msgid "Min"
+msgstr "Min"
+
+#: includes/integrations/acf/acf.php:325
+msgid "No"
+msgstr "Nei"
+
+#: includes/facets/date_range.php:25
+msgid "No dates"
+msgstr ""
+
+#: includes/facets/date_range.php:27
+msgid "No end dates"
+msgstr ""
+
+#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166
+msgid "No results"
+msgstr "Ingen resultater"
+
+#: includes/class-display.php:227 includes/facets/fselect.php:87
+msgid "No results found"
+msgstr "Ingen resultater funnet"
+
+#: includes/facets/date_range.php:26
+msgid "No start dates"
+msgstr ""
+
+#: includes/facets/number_range.php:23
+msgid "Number"
+msgstr "Nummer"
+
+#: includes/class-renderer.php:535
+msgid "of"
+msgstr "av"
+
+#: includes/integrations/woocommerce/woocommerce.php:529
+msgid "On Sale"
+msgstr "På salg"
+
+#: includes/integrations/woocommerce/woocommerce.php:525
+msgid "Out of Stock"
+msgstr "Ikke på lager"
+
+#: includes/class-renderer.php:592
+msgid "Per page"
+msgstr "Per side"
+
+#: includes/facets/map.php:400 includes/facets/reset.php:15
+#: includes/facets/slider.php:20
+msgid "Reset"
+msgstr "Tilbakestill"
+
+#: includes/facets/fselect.php:86
+msgid "Search"
+msgstr "Søk"
+
+#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164
+msgid "See less"
+msgstr "Se mindre"
+
+#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163
+msgid "See {num} more"
+msgstr "Se {num} mer"
+
+#: includes/facets/sort.php:162
+msgid "Sort by"
+msgstr "Sorter etter"
+
+#: includes/facets/date_range.php:22
+msgid "Start date"
+msgstr "Startdato"
+
+#: includes/facets/autocomplete.php:63
+msgid "Start typing"
+msgstr "Begynner å skrive"
+
+#: includes/facets/sort.php:166
+msgid "Title (A-Z)"
+msgstr "Tittel (A-Z)"
+
+#: includes/facets/sort.php:173
+msgid "Title (Z-A)"
+msgstr "Tittel (Z-A)"
+
+#: includes/facets/rating.php:166
+msgid "Undo"
+msgstr "Angre"
+
+#: includes/integrations/acf/acf.php:325
+msgid "Yes"
+msgstr "Ja"
+
+#: includes/facets/fselect.php:85
+msgid "{n} selected"
+msgstr "{n} valgt"
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-nl_NL.mo b/wp-content/plugins/facetwp/languages/fwp-front-nl_NL.mo
new file mode 100644
index 000000000..812dd4f11
Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-nl_NL.mo differ
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-nl_NL.po b/wp-content/plugins/facetwp/languages/fwp-front-nl_NL.po
new file mode 100644
index 000000000..3c85b6e06
--- /dev/null
+++ b/wp-content/plugins/facetwp/languages/fwp-front-nl_NL.po
@@ -0,0 +1,226 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: FacetWP (front)\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-02-21 14:01+0000\n"
+"PO-Revision-Date: 2025-04-18 10:22+0000\n"
+"Last-Translator: \n"
+"Language-Team: Dutch\n"
+"Language: nl-NL\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Loco https://localise.biz/\n"
+"X-Loco-Version: 2.7.1; wp-6.7.1\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: includes/facets/rating.php:165
+msgid "& up"
+msgstr "& hoger"
+
+#. Description of the plugin
+#: index.php
+msgid "Advanced Filtering for WordPress"
+msgstr ""
+
+#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37
+#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26
+msgid "Any"
+msgstr "Alle"
+
+#: includes/facets/date_range.php:300
+msgid "Clear"
+msgstr "Wissen"
+
+#: includes/facets/proximity.php:243
+msgid "Clear location"
+msgstr "Verwijder locatie"
+
+#: includes/facets/date_range.php:21
+msgid "Date"
+msgstr "Datum"
+
+#: includes/facets/sort.php:180
+msgid "Date (Newest)"
+msgstr "Datum (nieuwste)"
+
+#: includes/facets/sort.php:187
+msgid "Date (Oldest)"
+msgstr "Datum (oudste)"
+
+#: includes/facets/proximity.php:357
+msgid "Distance"
+msgstr "Afstand"
+
+#: includes/facets/map.php:397
+msgid "Enable map filtering"
+msgstr "Filter met kaart"
+
+#: includes/facets/date_range.php:23
+msgid "End date"
+msgstr "Einddatum"
+
+#: includes/facets/search.php:23
+msgid "Enter keywords"
+msgstr "Voer zoektermen in"
+
+#: includes/facets/proximity.php:52
+msgid "Enter location"
+msgstr "Vul locatie in"
+
+#: includes/facets/autocomplete.php:165
+msgid "Enter {n} or more characters"
+msgstr "Vul {n} of meer letters in"
+
+#. Plugin Name of the plugin
+#: index.php
+msgid "FacetWP"
+msgstr ""
+
+#. Author of the plugin
+#: index.php
+msgid "FacetWP, LLC"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:532
+msgid "Featured"
+msgstr ""
+
+#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32
+msgid "Go"
+msgstr "Ga"
+
+#: includes/class-display.php:208
+msgid "Go to next page"
+msgstr "Naar volgende pagina"
+
+#: includes/class-display.php:207
+msgid "Go to page"
+msgstr "Naar pagina"
+
+#: includes/class-display.php:209
+msgid "Go to previous page"
+msgstr "Naar vorige pagina"
+
+#. Author URI of the plugin
+#: index.php
+msgid "https://facetwp.com/"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:522
+msgid "In Stock"
+msgstr "Op voorraad"
+
+#: includes/facets/proximity.php:51
+msgid "km"
+msgstr "km"
+
+#: includes/facets/autocomplete.php:164
+msgid "Loading"
+msgstr "Laden"
+
+#: includes/facets/number_range.php:29
+msgid "Max"
+msgstr "Max"
+
+#: includes/facets/proximity.php:51
+msgid "mi"
+msgstr "mi"
+
+#: includes/facets/number_range.php:26
+msgid "Min"
+msgstr "Min"
+
+#: includes/integrations/acf/acf.php:325
+msgid "No"
+msgstr "Nee"
+
+#: includes/facets/date_range.php:25
+msgid "No dates"
+msgstr "Geen data"
+
+#: includes/facets/date_range.php:27
+msgid "No end dates"
+msgstr "Geen einddata"
+
+#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166
+msgid "No results"
+msgstr "Niets gevonden"
+
+#: includes/class-display.php:227 includes/facets/fselect.php:87
+msgid "No results found"
+msgstr "Niets gevonden"
+
+#: includes/facets/date_range.php:26
+msgid "No start dates"
+msgstr "Geen start data"
+
+#: includes/facets/number_range.php:23
+msgid "Number"
+msgstr "Nummer"
+
+#: includes/class-renderer.php:535
+msgid "of"
+msgstr "van"
+
+#: includes/integrations/woocommerce/woocommerce.php:529
+msgid "On Sale"
+msgstr "Aanbieding"
+
+#: includes/integrations/woocommerce/woocommerce.php:525
+msgid "Out of Stock"
+msgstr "Niet op voorraad"
+
+#: includes/class-renderer.php:592
+msgid "Per page"
+msgstr "Per pagina"
+
+#: includes/facets/map.php:400 includes/facets/reset.php:15
+#: includes/facets/slider.php:20
+msgid "Reset"
+msgstr "Reset"
+
+#: includes/facets/fselect.php:86
+msgid "Search"
+msgstr "Zoeken"
+
+#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164
+msgid "See less"
+msgstr "Toon minder"
+
+#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163
+msgid "See {num} more"
+msgstr "Toon {num} meer"
+
+#: includes/facets/sort.php:162
+msgid "Sort by"
+msgstr "Sorteer op"
+
+#: includes/facets/date_range.php:22
+msgid "Start date"
+msgstr "Start datum"
+
+#: includes/facets/autocomplete.php:63
+msgid "Start typing"
+msgstr "Start met typen"
+
+#: includes/facets/sort.php:166
+msgid "Title (A-Z)"
+msgstr "Titel (A-Z)"
+
+#: includes/facets/sort.php:173
+msgid "Title (Z-A)"
+msgstr "Titel (Z-A)"
+
+#: includes/facets/rating.php:166
+msgid "Undo"
+msgstr "Undo"
+
+#: includes/integrations/acf/acf.php:325
+msgid "Yes"
+msgstr "Ja"
+
+#: includes/facets/fselect.php:85
+msgid "{n} selected"
+msgstr "{n} geselecteerd"
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-pl_PL.mo b/wp-content/plugins/facetwp/languages/fwp-front-pl_PL.mo
new file mode 100644
index 000000000..494a55e08
Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-pl_PL.mo differ
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-pl_PL.po b/wp-content/plugins/facetwp/languages/fwp-front-pl_PL.po
new file mode 100644
index 000000000..c699452c9
--- /dev/null
+++ b/wp-content/plugins/facetwp/languages/fwp-front-pl_PL.po
@@ -0,0 +1,227 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: FacetWP (front)\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-02-21 14:01+0000\n"
+"PO-Revision-Date: 2025-04-18 10:50+0000\n"
+"Last-Translator: \n"
+"Language-Team: Polish\n"
+"Language: pl-PL\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 "
+"|| n%100>14) ? 1 : 2);\n"
+"X-Generator: Loco https://localise.biz/\n"
+"X-Loco-Version: 2.7.1; wp-6.7.1\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: includes/facets/rating.php:165
+msgid "& up"
+msgstr "& się"
+
+#. Description of the plugin
+#: index.php
+msgid "Advanced Filtering for WordPress"
+msgstr ""
+
+#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37
+#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26
+msgid "Any"
+msgstr "Dowolny"
+
+#: includes/facets/date_range.php:300
+msgid "Clear"
+msgstr "Wyczyść"
+
+#: includes/facets/proximity.php:243
+msgid "Clear location"
+msgstr "Wyczyść lokalizację"
+
+#: includes/facets/date_range.php:21
+msgid "Date"
+msgstr "Data"
+
+#: includes/facets/sort.php:180
+msgid "Date (Newest)"
+msgstr "Daty (Najnowsze)"
+
+#: includes/facets/sort.php:187
+msgid "Date (Oldest)"
+msgstr "Daty (Najstarsze)"
+
+#: includes/facets/proximity.php:357
+msgid "Distance"
+msgstr "Odległość"
+
+#: includes/facets/map.php:397
+msgid "Enable map filtering"
+msgstr ""
+
+#: includes/facets/date_range.php:23
+msgid "End date"
+msgstr "Data zakończenia"
+
+#: includes/facets/search.php:23
+msgid "Enter keywords"
+msgstr "Wprowadź słowa kluczowe"
+
+#: includes/facets/proximity.php:52
+msgid "Enter location"
+msgstr "Wprowadź lokalizację"
+
+#: includes/facets/autocomplete.php:165
+msgid "Enter {n} or more characters"
+msgstr ""
+
+#. Plugin Name of the plugin
+#: index.php
+msgid "FacetWP"
+msgstr ""
+
+#. Author of the plugin
+#: index.php
+msgid "FacetWP, LLC"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:532
+msgid "Featured"
+msgstr ""
+
+#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32
+msgid "Go"
+msgstr "Idź"
+
+#: includes/class-display.php:208
+msgid "Go to next page"
+msgstr ""
+
+#: includes/class-display.php:207
+msgid "Go to page"
+msgstr ""
+
+#: includes/class-display.php:209
+msgid "Go to previous page"
+msgstr ""
+
+#. Author URI of the plugin
+#: index.php
+msgid "https://facetwp.com/"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:522
+msgid "In Stock"
+msgstr "W magazynie"
+
+#: includes/facets/proximity.php:51
+msgid "km"
+msgstr ""
+
+#: includes/facets/autocomplete.php:164
+msgid "Loading"
+msgstr ""
+
+#: includes/facets/number_range.php:29
+msgid "Max"
+msgstr "Max"
+
+#: includes/facets/proximity.php:51
+msgid "mi"
+msgstr ""
+
+#: includes/facets/number_range.php:26
+msgid "Min"
+msgstr "Min"
+
+#: includes/integrations/acf/acf.php:325
+msgid "No"
+msgstr "Nie"
+
+#: includes/facets/date_range.php:25
+msgid "No dates"
+msgstr ""
+
+#: includes/facets/date_range.php:27
+msgid "No end dates"
+msgstr ""
+
+#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166
+msgid "No results"
+msgstr "Brak wyników"
+
+#: includes/class-display.php:227 includes/facets/fselect.php:87
+msgid "No results found"
+msgstr "Nie znaleziono żadnych wyników"
+
+#: includes/facets/date_range.php:26
+msgid "No start dates"
+msgstr ""
+
+#: includes/facets/number_range.php:23
+msgid "Number"
+msgstr "Numer"
+
+#: includes/class-renderer.php:535
+msgid "of"
+msgstr "z"
+
+#: includes/integrations/woocommerce/woocommerce.php:529
+msgid "On Sale"
+msgstr "W sprzedaży"
+
+#: includes/integrations/woocommerce/woocommerce.php:525
+msgid "Out of Stock"
+msgstr "Brak w magazynie"
+
+#: includes/class-renderer.php:592
+msgid "Per page"
+msgstr "Na stronę"
+
+#: includes/facets/map.php:400 includes/facets/reset.php:15
+#: includes/facets/slider.php:20
+msgid "Reset"
+msgstr "Resetuj"
+
+#: includes/facets/fselect.php:86
+msgid "Search"
+msgstr "Szukaj"
+
+#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164
+msgid "See less"
+msgstr "Zobacz mniej"
+
+#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163
+msgid "See {num} more"
+msgstr "Zobacz {num} więcej"
+
+#: includes/facets/sort.php:162
+msgid "Sort by"
+msgstr "Sortuj wg"
+
+#: includes/facets/date_range.php:22
+msgid "Start date"
+msgstr "Data rozpoczęcia"
+
+#: includes/facets/autocomplete.php:63
+msgid "Start typing"
+msgstr "Zacznij wpisywać"
+
+#: includes/facets/sort.php:166
+msgid "Title (A-Z)"
+msgstr "Nazwy (A-Z)"
+
+#: includes/facets/sort.php:173
+msgid "Title (Z-A)"
+msgstr "Nazwy (Z-A)"
+
+#: includes/facets/rating.php:166
+msgid "Undo"
+msgstr "Cofnij"
+
+#: includes/integrations/acf/acf.php:325
+msgid "Yes"
+msgstr "Tak"
+
+#: includes/facets/fselect.php:85
+msgid "{n} selected"
+msgstr "wybrano {n}"
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-pt_BR.mo b/wp-content/plugins/facetwp/languages/fwp-front-pt_BR.mo
new file mode 100644
index 000000000..b4134fc86
Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-pt_BR.mo differ
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-pt_BR.po b/wp-content/plugins/facetwp/languages/fwp-front-pt_BR.po
new file mode 100644
index 000000000..329496b6c
--- /dev/null
+++ b/wp-content/plugins/facetwp/languages/fwp-front-pt_BR.po
@@ -0,0 +1,226 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: FacetWP (front)\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-02-21 14:01+0000\n"
+"PO-Revision-Date: 2025-04-18 10:50+0000\n"
+"Last-Translator: \n"
+"Language-Team: Portuguese (Brazil)\n"
+"Language: pt-BR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Loco https://localise.biz/\n"
+"X-Loco-Version: 2.7.1; wp-6.7.1\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: includes/facets/rating.php:165
+msgid "& up"
+msgstr "& up"
+
+#. Description of the plugin
+#: index.php
+msgid "Advanced Filtering for WordPress"
+msgstr ""
+
+#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37
+#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26
+msgid "Any"
+msgstr "Qualquer"
+
+#: includes/facets/date_range.php:300
+msgid "Clear"
+msgstr "Limpar"
+
+#: includes/facets/proximity.php:243
+msgid "Clear location"
+msgstr "Limpar localização"
+
+#: includes/facets/date_range.php:21
+msgid "Date"
+msgstr "Data"
+
+#: includes/facets/sort.php:180
+msgid "Date (Newest)"
+msgstr "Data (mais recente)"
+
+#: includes/facets/sort.php:187
+msgid "Date (Oldest)"
+msgstr "Data (mais antiga)"
+
+#: includes/facets/proximity.php:357
+msgid "Distance"
+msgstr "Distância"
+
+#: includes/facets/map.php:397
+msgid "Enable map filtering"
+msgstr ""
+
+#: includes/facets/date_range.php:23
+msgid "End date"
+msgstr "Data Final"
+
+#: includes/facets/search.php:23
+msgid "Enter keywords"
+msgstr "Digitar palavras-chave"
+
+#: includes/facets/proximity.php:52
+msgid "Enter location"
+msgstr "Digitar local"
+
+#: includes/facets/autocomplete.php:165
+msgid "Enter {n} or more characters"
+msgstr ""
+
+#. Plugin Name of the plugin
+#: index.php
+msgid "FacetWP"
+msgstr ""
+
+#. Author of the plugin
+#: index.php
+msgid "FacetWP, LLC"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:532
+msgid "Featured"
+msgstr ""
+
+#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32
+msgid "Go"
+msgstr "Ir"
+
+#: includes/class-display.php:208
+msgid "Go to next page"
+msgstr ""
+
+#: includes/class-display.php:207
+msgid "Go to page"
+msgstr ""
+
+#: includes/class-display.php:209
+msgid "Go to previous page"
+msgstr ""
+
+#. Author URI of the plugin
+#: index.php
+msgid "https://facetwp.com/"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:522
+msgid "In Stock"
+msgstr "Em estoque"
+
+#: includes/facets/proximity.php:51
+msgid "km"
+msgstr ""
+
+#: includes/facets/autocomplete.php:164
+msgid "Loading"
+msgstr ""
+
+#: includes/facets/number_range.php:29
+msgid "Max"
+msgstr "Máx"
+
+#: includes/facets/proximity.php:51
+msgid "mi"
+msgstr ""
+
+#: includes/facets/number_range.php:26
+msgid "Min"
+msgstr "Min"
+
+#: includes/integrations/acf/acf.php:325
+msgid "No"
+msgstr "Não"
+
+#: includes/facets/date_range.php:25
+msgid "No dates"
+msgstr ""
+
+#: includes/facets/date_range.php:27
+msgid "No end dates"
+msgstr ""
+
+#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166
+msgid "No results"
+msgstr "Sem resultados"
+
+#: includes/class-display.php:227 includes/facets/fselect.php:87
+msgid "No results found"
+msgstr "Nenhum resultado encontrado"
+
+#: includes/facets/date_range.php:26
+msgid "No start dates"
+msgstr ""
+
+#: includes/facets/number_range.php:23
+msgid "Number"
+msgstr "Número"
+
+#: includes/class-renderer.php:535
+msgid "of"
+msgstr "de"
+
+#: includes/integrations/woocommerce/woocommerce.php:529
+msgid "On Sale"
+msgstr "À venda"
+
+#: includes/integrations/woocommerce/woocommerce.php:525
+msgid "Out of Stock"
+msgstr "Fora de Estoque"
+
+#: includes/class-renderer.php:592
+msgid "Per page"
+msgstr "Por página"
+
+#: includes/facets/map.php:400 includes/facets/reset.php:15
+#: includes/facets/slider.php:20
+msgid "Reset"
+msgstr "Redefinir"
+
+#: includes/facets/fselect.php:86
+msgid "Search"
+msgstr "Pesquisar"
+
+#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164
+msgid "See less"
+msgstr "Ver menos"
+
+#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163
+msgid "See {num} more"
+msgstr "Ver {num} mais"
+
+#: includes/facets/sort.php:162
+msgid "Sort by"
+msgstr "Ordenar por"
+
+#: includes/facets/date_range.php:22
+msgid "Start date"
+msgstr "Data de Início"
+
+#: includes/facets/autocomplete.php:63
+msgid "Start typing"
+msgstr "Comece a digitar"
+
+#: includes/facets/sort.php:166
+msgid "Title (A-Z)"
+msgstr "Título (A-Z)"
+
+#: includes/facets/sort.php:173
+msgid "Title (Z-A)"
+msgstr "Título (Z-A)"
+
+#: includes/facets/rating.php:166
+msgid "Undo"
+msgstr "Desfazer"
+
+#: includes/integrations/acf/acf.php:325
+msgid "Yes"
+msgstr "Sim"
+
+#: includes/facets/fselect.php:85
+msgid "{n} selected"
+msgstr "{n} seleccionado"
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-ro_RO.mo b/wp-content/plugins/facetwp/languages/fwp-front-ro_RO.mo
new file mode 100644
index 000000000..93392b23c
Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-ro_RO.mo differ
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-ro_RO.po b/wp-content/plugins/facetwp/languages/fwp-front-ro_RO.po
new file mode 100644
index 000000000..36af0af55
--- /dev/null
+++ b/wp-content/plugins/facetwp/languages/fwp-front-ro_RO.po
@@ -0,0 +1,227 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: FacetWP (front)\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-02-21 14:01+0000\n"
+"PO-Revision-Date: 2025-04-18 10:51+0000\n"
+"Last-Translator: \n"
+"Language-Team: Romanian\n"
+"Language: ro-RO\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n==0 || (n!=1 && n%100>=1 && "
+"n%100<=19) ? 1 : 2);\n"
+"X-Generator: Loco https://localise.biz/\n"
+"X-Loco-Version: 2.7.1; wp-6.7.1\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: includes/facets/rating.php:165
+msgid "& up"
+msgstr "& up"
+
+#. Description of the plugin
+#: index.php
+msgid "Advanced Filtering for WordPress"
+msgstr ""
+
+#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37
+#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26
+msgid "Any"
+msgstr "Orice"
+
+#: includes/facets/date_range.php:300
+msgid "Clear"
+msgstr "Sterge"
+
+#: includes/facets/proximity.php:243
+msgid "Clear location"
+msgstr "Ștergeți locația"
+
+#: includes/facets/date_range.php:21
+msgid "Date"
+msgstr "Data"
+
+#: includes/facets/sort.php:180
+msgid "Date (Newest)"
+msgstr "Data (cele mai noi)"
+
+#: includes/facets/sort.php:187
+msgid "Date (Oldest)"
+msgstr "Data (cea mai veche)"
+
+#: includes/facets/proximity.php:357
+msgid "Distance"
+msgstr "Distanță"
+
+#: includes/facets/map.php:397
+msgid "Enable map filtering"
+msgstr ""
+
+#: includes/facets/date_range.php:23
+msgid "End date"
+msgstr "Data de sfârșit"
+
+#: includes/facets/search.php:23
+msgid "Enter keywords"
+msgstr "Introdu cuvinte cheie"
+
+#: includes/facets/proximity.php:52
+msgid "Enter location"
+msgstr "Vă rugăm să introduceți ID-ul Locatiei"
+
+#: includes/facets/autocomplete.php:165
+msgid "Enter {n} or more characters"
+msgstr ""
+
+#. Plugin Name of the plugin
+#: index.php
+msgid "FacetWP"
+msgstr ""
+
+#. Author of the plugin
+#: index.php
+msgid "FacetWP, LLC"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:532
+msgid "Featured"
+msgstr ""
+
+#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32
+msgid "Go"
+msgstr "Mergi"
+
+#: includes/class-display.php:208
+msgid "Go to next page"
+msgstr ""
+
+#: includes/class-display.php:207
+msgid "Go to page"
+msgstr ""
+
+#: includes/class-display.php:209
+msgid "Go to previous page"
+msgstr ""
+
+#. Author URI of the plugin
+#: index.php
+msgid "https://facetwp.com/"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:522
+msgid "In Stock"
+msgstr "În stoc"
+
+#: includes/facets/proximity.php:51
+msgid "km"
+msgstr ""
+
+#: includes/facets/autocomplete.php:164
+msgid "Loading"
+msgstr ""
+
+#: includes/facets/number_range.php:29
+msgid "Max"
+msgstr "Max"
+
+#: includes/facets/proximity.php:51
+msgid "mi"
+msgstr ""
+
+#: includes/facets/number_range.php:26
+msgid "Min"
+msgstr "Min"
+
+#: includes/integrations/acf/acf.php:325
+msgid "No"
+msgstr "Nu"
+
+#: includes/facets/date_range.php:25
+msgid "No dates"
+msgstr ""
+
+#: includes/facets/date_range.php:27
+msgid "No end dates"
+msgstr ""
+
+#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166
+msgid "No results"
+msgstr "Nici un rezultat"
+
+#: includes/class-display.php:227 includes/facets/fselect.php:87
+msgid "No results found"
+msgstr "Niciun rezultat găsit"
+
+#: includes/facets/date_range.php:26
+msgid "No start dates"
+msgstr ""
+
+#: includes/facets/number_range.php:23
+msgid "Number"
+msgstr "Număr"
+
+#: includes/class-renderer.php:535
+msgid "of"
+msgstr "din"
+
+#: includes/integrations/woocommerce/woocommerce.php:529
+msgid "On Sale"
+msgstr "La reducere"
+
+#: includes/integrations/woocommerce/woocommerce.php:525
+msgid "Out of Stock"
+msgstr "Stoc epuizat"
+
+#: includes/class-renderer.php:592
+msgid "Per page"
+msgstr "Per pagină"
+
+#: includes/facets/map.php:400 includes/facets/reset.php:15
+#: includes/facets/slider.php:20
+msgid "Reset"
+msgstr "Reset"
+
+#: includes/facets/fselect.php:86
+msgid "Search"
+msgstr "Caută"
+
+#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164
+msgid "See less"
+msgstr "Vedea mai puțin"
+
+#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163
+msgid "See {num} more"
+msgstr "Vezi {num} mai multe"
+
+#: includes/facets/sort.php:162
+msgid "Sort by"
+msgstr "Sortare după"
+
+#: includes/facets/date_range.php:22
+msgid "Start date"
+msgstr "Data de început"
+
+#: includes/facets/autocomplete.php:63
+msgid "Start typing"
+msgstr "Începeți să tastați"
+
+#: includes/facets/sort.php:166
+msgid "Title (A-Z)"
+msgstr "Titlu (A-Z)"
+
+#: includes/facets/sort.php:173
+msgid "Title (Z-A)"
+msgstr "Titlu (Z-A)"
+
+#: includes/facets/rating.php:166
+msgid "Undo"
+msgstr "Anulare"
+
+#: includes/integrations/acf/acf.php:325
+msgid "Yes"
+msgstr "Da"
+
+#: includes/facets/fselect.php:85
+msgid "{n} selected"
+msgstr "{n} selectat"
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-ru_RU.mo b/wp-content/plugins/facetwp/languages/fwp-front-ru_RU.mo
new file mode 100644
index 000000000..7edf8a178
Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-ru_RU.mo differ
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-ru_RU.po b/wp-content/plugins/facetwp/languages/fwp-front-ru_RU.po
new file mode 100644
index 000000000..08ba1706d
--- /dev/null
+++ b/wp-content/plugins/facetwp/languages/fwp-front-ru_RU.po
@@ -0,0 +1,227 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: FacetWP (front)\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-02-21 14:01+0000\n"
+"PO-Revision-Date: 2025-04-18 10:52+0000\n"
+"Last-Translator: \n"
+"Language-Team: Russian\n"
+"Language: ru-RU\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
+"X-Generator: Loco https://localise.biz/\n"
+"X-Loco-Version: 2.7.1; wp-6.7.1\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: includes/facets/rating.php:165
+msgid "& up"
+msgstr "и вверх"
+
+#. Description of the plugin
+#: index.php
+msgid "Advanced Filtering for WordPress"
+msgstr ""
+
+#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37
+#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26
+msgid "Any"
+msgstr "Любой"
+
+#: includes/facets/date_range.php:300
+msgid "Clear"
+msgstr "Очистить"
+
+#: includes/facets/proximity.php:243
+msgid "Clear location"
+msgstr "Очистить местоположение"
+
+#: includes/facets/date_range.php:21
+msgid "Date"
+msgstr "Дата"
+
+#: includes/facets/sort.php:180
+msgid "Date (Newest)"
+msgstr "Дата (новые)"
+
+#: includes/facets/sort.php:187
+msgid "Date (Oldest)"
+msgstr "Дата (самая старая)"
+
+#: includes/facets/proximity.php:357
+msgid "Distance"
+msgstr "Расстояние"
+
+#: includes/facets/map.php:397
+msgid "Enable map filtering"
+msgstr ""
+
+#: includes/facets/date_range.php:23
+msgid "End date"
+msgstr "Дата окончания"
+
+#: includes/facets/search.php:23
+msgid "Enter keywords"
+msgstr "Введите ключевые слова"
+
+#: includes/facets/proximity.php:52
+msgid "Enter location"
+msgstr "Введите местоположение"
+
+#: includes/facets/autocomplete.php:165
+msgid "Enter {n} or more characters"
+msgstr ""
+
+#. Plugin Name of the plugin
+#: index.php
+msgid "FacetWP"
+msgstr ""
+
+#. Author of the plugin
+#: index.php
+msgid "FacetWP, LLC"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:532
+msgid "Featured"
+msgstr ""
+
+#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32
+msgid "Go"
+msgstr "Перейти"
+
+#: includes/class-display.php:208
+msgid "Go to next page"
+msgstr ""
+
+#: includes/class-display.php:207
+msgid "Go to page"
+msgstr ""
+
+#: includes/class-display.php:209
+msgid "Go to previous page"
+msgstr ""
+
+#. Author URI of the plugin
+#: index.php
+msgid "https://facetwp.com/"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:522
+msgid "In Stock"
+msgstr "В наличии"
+
+#: includes/facets/proximity.php:51
+msgid "km"
+msgstr ""
+
+#: includes/facets/autocomplete.php:164
+msgid "Loading"
+msgstr ""
+
+#: includes/facets/number_range.php:29
+msgid "Max"
+msgstr "Макс"
+
+#: includes/facets/proximity.php:51
+msgid "mi"
+msgstr ""
+
+#: includes/facets/number_range.php:26
+msgid "Min"
+msgstr "Мин"
+
+#: includes/integrations/acf/acf.php:325
+msgid "No"
+msgstr "Нет"
+
+#: includes/facets/date_range.php:25
+msgid "No dates"
+msgstr ""
+
+#: includes/facets/date_range.php:27
+msgid "No end dates"
+msgstr ""
+
+#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166
+msgid "No results"
+msgstr "Нет результатов"
+
+#: includes/class-display.php:227 includes/facets/fselect.php:87
+msgid "No results found"
+msgstr "Результатов не найдено"
+
+#: includes/facets/date_range.php:26
+msgid "No start dates"
+msgstr ""
+
+#: includes/facets/number_range.php:23
+msgid "Number"
+msgstr "Номер"
+
+#: includes/class-renderer.php:535
+msgid "of"
+msgstr "из"
+
+#: includes/integrations/woocommerce/woocommerce.php:529
+msgid "On Sale"
+msgstr "Распродажа"
+
+#: includes/integrations/woocommerce/woocommerce.php:525
+msgid "Out of Stock"
+msgstr "Нет в наличии"
+
+#: includes/class-renderer.php:592
+msgid "Per page"
+msgstr "На страницу"
+
+#: includes/facets/map.php:400 includes/facets/reset.php:15
+#: includes/facets/slider.php:20
+msgid "Reset"
+msgstr "Сброс"
+
+#: includes/facets/fselect.php:86
+msgid "Search"
+msgstr "Поиск"
+
+#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164
+msgid "See less"
+msgstr "Свернуть"
+
+#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163
+msgid "See {num} more"
+msgstr "Посмотреть {num} Подробнее"
+
+#: includes/facets/sort.php:162
+msgid "Sort by"
+msgstr "Сортировать по"
+
+#: includes/facets/date_range.php:22
+msgid "Start date"
+msgstr "Дата начала"
+
+#: includes/facets/autocomplete.php:63
+msgid "Start typing"
+msgstr "Напишите "
+
+#: includes/facets/sort.php:166
+msgid "Title (A-Z)"
+msgstr "Имя (А-Я)"
+
+#: includes/facets/sort.php:173
+msgid "Title (Z-A)"
+msgstr "Имя (Я-А)"
+
+#: includes/facets/rating.php:166
+msgid "Undo"
+msgstr "Отменить"
+
+#: includes/integrations/acf/acf.php:325
+msgid "Yes"
+msgstr "Да"
+
+#: includes/facets/fselect.php:85
+msgid "{n} selected"
+msgstr "{n} выбрано"
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-sv_SE.mo b/wp-content/plugins/facetwp/languages/fwp-front-sv_SE.mo
new file mode 100644
index 000000000..050548940
Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-sv_SE.mo differ
diff --git a/wp-content/plugins/facetwp/languages/fwp-front-sv_SE.po b/wp-content/plugins/facetwp/languages/fwp-front-sv_SE.po
new file mode 100644
index 000000000..fae2d3205
--- /dev/null
+++ b/wp-content/plugins/facetwp/languages/fwp-front-sv_SE.po
@@ -0,0 +1,226 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: FacetWP (front)\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-02-21 14:01+0000\n"
+"PO-Revision-Date: 2025-04-18 10:53+0000\n"
+"Last-Translator: \n"
+"Language-Team: Swedish\n"
+"Language: sv-SE\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Loco https://localise.biz/\n"
+"X-Loco-Version: 2.7.1; wp-6.7.1\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+#: includes/facets/rating.php:165
+msgid "& up"
+msgstr "& upp"
+
+#. Description of the plugin
+#: index.php
+msgid "Advanced Filtering for WordPress"
+msgstr ""
+
+#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37
+#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26
+msgid "Any"
+msgstr "Någon"
+
+#: includes/facets/date_range.php:300
+msgid "Clear"
+msgstr "Rensa"
+
+#: includes/facets/proximity.php:243
+msgid "Clear location"
+msgstr "Rensa plats"
+
+#: includes/facets/date_range.php:21
+msgid "Date"
+msgstr "Datum"
+
+#: includes/facets/sort.php:180
+msgid "Date (Newest)"
+msgstr "Datum (nyaste)"
+
+#: includes/facets/sort.php:187
+msgid "Date (Oldest)"
+msgstr "Datum (äldsta)"
+
+#: includes/facets/proximity.php:357
+msgid "Distance"
+msgstr "Avstånd"
+
+#: includes/facets/map.php:397
+msgid "Enable map filtering"
+msgstr ""
+
+#: includes/facets/date_range.php:23
+msgid "End date"
+msgstr "Slutdatum"
+
+#: includes/facets/search.php:23
+msgid "Enter keywords"
+msgstr "Ange nyckelord"
+
+#: includes/facets/proximity.php:52
+msgid "Enter location"
+msgstr "Ange plats"
+
+#: includes/facets/autocomplete.php:165
+msgid "Enter {n} or more characters"
+msgstr ""
+
+#. Plugin Name of the plugin
+#: index.php
+msgid "FacetWP"
+msgstr ""
+
+#. Author of the plugin
+#: index.php
+msgid "FacetWP, LLC"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:532
+msgid "Featured"
+msgstr ""
+
+#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32
+msgid "Go"
+msgstr "Start"
+
+#: includes/class-display.php:208
+msgid "Go to next page"
+msgstr ""
+
+#: includes/class-display.php:207
+msgid "Go to page"
+msgstr ""
+
+#: includes/class-display.php:209
+msgid "Go to previous page"
+msgstr ""
+
+#. Author URI of the plugin
+#: index.php
+msgid "https://facetwp.com/"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:522
+msgid "In Stock"
+msgstr "I lager"
+
+#: includes/facets/proximity.php:51
+msgid "km"
+msgstr ""
+
+#: includes/facets/autocomplete.php:164
+msgid "Loading"
+msgstr ""
+
+#: includes/facets/number_range.php:29
+msgid "Max"
+msgstr "Max"
+
+#: includes/facets/proximity.php:51
+msgid "mi"
+msgstr ""
+
+#: includes/facets/number_range.php:26
+msgid "Min"
+msgstr "Min"
+
+#: includes/integrations/acf/acf.php:325
+msgid "No"
+msgstr "Nej"
+
+#: includes/facets/date_range.php:25
+msgid "No dates"
+msgstr ""
+
+#: includes/facets/date_range.php:27
+msgid "No end dates"
+msgstr ""
+
+#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166
+msgid "No results"
+msgstr "Inga resultat"
+
+#: includes/class-display.php:227 includes/facets/fselect.php:87
+msgid "No results found"
+msgstr "Inga resultat hittades"
+
+#: includes/facets/date_range.php:26
+msgid "No start dates"
+msgstr ""
+
+#: includes/facets/number_range.php:23
+msgid "Number"
+msgstr "Nummer"
+
+#: includes/class-renderer.php:535
+msgid "of"
+msgstr "av"
+
+#: includes/integrations/woocommerce/woocommerce.php:529
+msgid "On Sale"
+msgstr "Rea"
+
+#: includes/integrations/woocommerce/woocommerce.php:525
+msgid "Out of Stock"
+msgstr "Slut i lager"
+
+#: includes/class-renderer.php:592
+msgid "Per page"
+msgstr "Per sida"
+
+#: includes/facets/map.php:400 includes/facets/reset.php:15
+#: includes/facets/slider.php:20
+msgid "Reset"
+msgstr "Återställ"
+
+#: includes/facets/fselect.php:86
+msgid "Search"
+msgstr "Sök"
+
+#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164
+msgid "See less"
+msgstr "Se mindre"
+
+#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163
+msgid "See {num} more"
+msgstr "Se {num} mer"
+
+#: includes/facets/sort.php:162
+msgid "Sort by"
+msgstr "Sortera efter"
+
+#: includes/facets/date_range.php:22
+msgid "Start date"
+msgstr "Startdatum"
+
+#: includes/facets/autocomplete.php:63
+msgid "Start typing"
+msgstr "Börja skriv"
+
+#: includes/facets/sort.php:166
+msgid "Title (A-Z)"
+msgstr "Titel (A-Z)"
+
+#: includes/facets/sort.php:173
+msgid "Title (Z-A)"
+msgstr "Titel (Z-A)"
+
+#: includes/facets/rating.php:166
+msgid "Undo"
+msgstr "Ångra"
+
+#: includes/integrations/acf/acf.php:325
+msgid "Yes"
+msgstr "Ja"
+
+#: includes/facets/fselect.php:85
+msgid "{n} selected"
+msgstr "{n} markerat"
diff --git a/wp-content/plugins/facetwp/languages/fwp-front.pot b/wp-content/plugins/facetwp/languages/fwp-front.pot
new file mode 100644
index 000000000..a3d89da7b
--- /dev/null
+++ b/wp-content/plugins/facetwp/languages/fwp-front.pot
@@ -0,0 +1,234 @@
+# Copyright (C) 2025 FacetWP, LLC
+# This file is distributed under the same license as the FacetWP plugin.
+# Generated with WP-CLI: wp i18n make-pot . languages/fwp-front.pot --domain=fwp-front
+msgid ""
+msgstr ""
+"Project-Id-Version: FacetWP (front)\n"
+"Report-Msgid-Bugs-To: https://facetwp.com/help-center/get-support/\n"
+"Last-Translator: Matt Gibbs \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"POT-Creation-Date: 2025-04-18T09:05:51+00:00\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"X-Generator: WP-CLI 2.11.0\n"
+"X-Domain: fwp-front\n"
+
+#. Plugin Name of the plugin
+#: index.php
+msgid "FacetWP"
+msgstr ""
+
+#. Description of the plugin
+#: index.php
+msgid "Advanced Filtering for WordPress"
+msgstr ""
+
+#. Author of the plugin
+#: index.php
+msgid "FacetWP, LLC"
+msgstr ""
+
+#. Author URI of the plugin
+#: index.php
+msgid "https://facetwp.com/"
+msgstr ""
+
+#: includes/class-display.php:207
+msgid "Go to page"
+msgstr ""
+
+#: includes/class-display.php:208
+msgid "Go to next page"
+msgstr ""
+
+#: includes/class-display.php:209
+msgid "Go to previous page"
+msgstr ""
+
+#: includes/class-display.php:227
+#: includes/facets/fselect.php:87
+msgid "No results found"
+msgstr ""
+
+#: includes/class-renderer.php:535
+msgid "of"
+msgstr ""
+
+#: includes/class-renderer.php:592
+msgid "Per page"
+msgstr ""
+
+#: includes/facets/autocomplete.php:63
+msgid "Start typing"
+msgstr ""
+
+#: includes/facets/autocomplete.php:66
+#: includes/facets/number_range.php:32
+msgid "Go"
+msgstr ""
+
+#: includes/facets/autocomplete.php:99
+#: includes/facets/autocomplete.php:166
+msgid "No results"
+msgstr ""
+
+#: includes/facets/autocomplete.php:164
+msgid "Loading"
+msgstr ""
+
+#: includes/facets/autocomplete.php:165
+msgid "Enter {n} or more characters"
+msgstr ""
+
+#: includes/facets/checkboxes.php:133
+#: includes/facets/hierarchy.php:163
+msgid "See {num} more"
+msgstr ""
+
+#: includes/facets/checkboxes.php:134
+#: includes/facets/hierarchy.php:164
+msgid "See less"
+msgstr ""
+
+#: includes/facets/date_range.php:21
+msgid "Date"
+msgstr ""
+
+#: includes/facets/date_range.php:22
+msgid "Start date"
+msgstr ""
+
+#: includes/facets/date_range.php:23
+msgid "End date"
+msgstr ""
+
+#: includes/facets/date_range.php:25
+msgid "No dates"
+msgstr ""
+
+#: includes/facets/date_range.php:26
+msgid "No start dates"
+msgstr ""
+
+#: includes/facets/date_range.php:27
+msgid "No end dates"
+msgstr ""
+
+#: includes/facets/date_range.php:300
+msgid "Clear"
+msgstr ""
+
+#: includes/facets/dropdown.php:35
+#: includes/facets/fselect.php:37
+#: includes/facets/fselect.php:80
+#: includes/facets/hierarchy.php:26
+msgid "Any"
+msgstr ""
+
+#: includes/facets/fselect.php:85
+msgid "{n} selected"
+msgstr ""
+
+#: includes/facets/fselect.php:86
+msgid "Search"
+msgstr ""
+
+#: includes/facets/map.php:397
+msgid "Enable map filtering"
+msgstr ""
+
+#: includes/facets/map.php:400
+#: includes/facets/reset.php:15
+#: includes/facets/slider.php:20
+msgid "Reset"
+msgstr ""
+
+#: includes/facets/number_range.php:23
+msgid "Number"
+msgstr ""
+
+#: includes/facets/number_range.php:26
+msgid "Min"
+msgstr ""
+
+#: includes/facets/number_range.php:29
+msgid "Max"
+msgstr ""
+
+#: includes/facets/proximity.php:51
+msgid "km"
+msgstr ""
+
+#: includes/facets/proximity.php:51
+msgid "mi"
+msgstr ""
+
+#: includes/facets/proximity.php:52
+msgid "Enter location"
+msgstr ""
+
+#: includes/facets/proximity.php:243
+msgid "Clear location"
+msgstr ""
+
+#: includes/facets/proximity.php:357
+msgid "Distance"
+msgstr ""
+
+#: includes/facets/rating.php:165
+msgid "& up"
+msgstr ""
+
+#: includes/facets/rating.php:166
+msgid "Undo"
+msgstr ""
+
+#: includes/facets/search.php:23
+msgid "Enter keywords"
+msgstr ""
+
+#: includes/facets/sort.php:162
+msgid "Sort by"
+msgstr ""
+
+#: includes/facets/sort.php:166
+msgid "Title (A-Z)"
+msgstr ""
+
+#: includes/facets/sort.php:173
+msgid "Title (Z-A)"
+msgstr ""
+
+#: includes/facets/sort.php:180
+msgid "Date (Newest)"
+msgstr ""
+
+#: includes/facets/sort.php:187
+msgid "Date (Oldest)"
+msgstr ""
+
+#: includes/integrations/acf/acf.php:325
+msgid "Yes"
+msgstr ""
+
+#: includes/integrations/acf/acf.php:325
+msgid "No"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:522
+msgid "In Stock"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:525
+msgid "Out of Stock"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:529
+msgid "On Sale"
+msgstr ""
+
+#: includes/integrations/woocommerce/woocommerce.php:532
+msgid "Featured"
+msgstr ""
diff --git a/wp-content/plugins/facetwp/languages/fwp.pot b/wp-content/plugins/facetwp/languages/fwp.pot
new file mode 100644
index 000000000..80756f0e5
--- /dev/null
+++ b/wp-content/plugins/facetwp/languages/fwp.pot
@@ -0,0 +1,1279 @@
+# Copyright (C) 2025 FacetWP, LLC
+# This file is distributed under the same license as the FacetWP plugin.
+# Generated with WP-CLI: wp i18n make-pot . languages/fwp.pot --domain=fwp
+msgid ""
+msgstr ""
+"Project-Id-Version: FacetWP\n"
+"Report-Msgid-Bugs-To: https://facetwp.com/help-center/get-support/\n"
+"Last-Translator: Matt Gibbs \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"POT-Creation-Date: 2025-04-18T08:59:58+00:00\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"X-Generator: WP-CLI 2.11.0\n"
+"X-Domain: fwp\n"
+
+#. Plugin Name of the plugin
+#: index.php
+msgid "FacetWP"
+msgstr ""
+
+#. Description of the plugin
+#: index.php
+msgid "Advanced Filtering for WordPress"
+msgstr ""
+
+#. Author of the plugin
+#: index.php
+msgid "FacetWP, LLC"
+msgstr ""
+
+#. Author URI of the plugin
+#: index.php
+msgid "https://facetwp.com/"
+msgstr ""
+
+#: includes/class-ajax.php:72
+msgid "Settings saved, please re-index"
+msgstr ""
+
+#: includes/class-ajax.php:78
+msgid "Settings saved"
+msgstr ""
+
+#: includes/class-ajax.php:85
+msgid "Error: invalid JSON"
+msgstr ""
+
+#: includes/class-ajax.php:143
+msgid "Done, please re-index"
+msgstr ""
+
+#: includes/class-ajax.php:205
+msgid "Error"
+msgstr ""
+
+#: includes/class-ajax.php:243
+msgid "Nothing to import"
+msgstr ""
+
+#: includes/class-ajax.php:279
+msgid "Imported"
+msgstr ""
+
+#: includes/class-ajax.php:282
+msgid "Skipped"
+msgstr ""
+
+#: includes/class-builder.php:281
+msgid "In Stock"
+msgstr ""
+
+#: includes/class-builder.php:281
+msgid "Out of Stock"
+msgstr ""
+
+#: includes/class-builder.php:286
+msgid "On Sale"
+msgstr ""
+
+#: includes/class-helper.php:523
+#: includes/class-settings.php:608
+msgid "Posts"
+msgstr ""
+
+#: includes/class-helper.php:525
+#: includes/class-settings.php:611
+msgid "Post Type"
+msgstr ""
+
+#: includes/class-helper.php:526
+#: includes/class-settings.php:612
+msgid "Post Date"
+msgstr ""
+
+#: includes/class-helper.php:527
+#: includes/class-settings.php:613
+msgid "Post Modified"
+msgstr ""
+
+#: includes/class-helper.php:528
+#: includes/class-settings.php:609
+msgid "Post Title"
+msgstr ""
+
+#: includes/class-helper.php:529
+msgid "Post Author"
+msgstr ""
+
+#: includes/class-helper.php:534
+msgid "Taxonomies"
+msgstr ""
+
+#: includes/class-helper.php:539
+#: includes/class-settings.php:616
+msgid "Custom Fields"
+msgstr ""
+
+#: includes/class-init.php:200
+#: templates/page-settings.php:109
+msgid "Settings"
+msgstr ""
+
+#: includes/class-request.php:372
+msgid "FacetWP was unable to auto-detect the post listing"
+msgstr ""
+
+#: includes/class-settings.php:13
+msgid "General"
+msgstr ""
+
+#: includes/class-settings.php:16
+msgid "License key"
+msgstr ""
+
+#: includes/class-settings.php:20
+msgid "Google Maps API key"
+msgstr ""
+
+#: includes/class-settings.php:24
+msgid "Test Google APIs"
+msgstr ""
+
+#: includes/class-settings.php:29
+msgid "Use Legacy Proximity facet"
+msgstr ""
+
+#: includes/class-settings.php:38
+msgid "Separators"
+msgstr ""
+
+#: includes/class-settings.php:43
+msgid "URL prefix"
+msgstr ""
+
+#: includes/class-settings.php:49
+msgid "Load jQuery"
+msgstr ""
+
+#: includes/class-settings.php:57
+msgid "Load a11y support"
+msgstr ""
+
+#: includes/class-settings.php:65
+msgid "Strict query detection"
+msgstr ""
+
+#: includes/class-settings.php:73
+msgid "Debug mode"
+msgstr ""
+
+#: includes/class-settings.php:82
+msgid "Enable automatic indexing"
+msgstr ""
+
+#: includes/class-settings.php:92
+#: includes/integrations/woocommerce/woocommerce.php:69
+msgid "WooCommerce"
+msgstr ""
+
+#: includes/class-settings.php:95
+msgid "Support product variations?"
+msgstr ""
+
+#: includes/class-settings.php:96
+msgid "Enable if your store uses variable products."
+msgstr ""
+
+#: includes/class-settings.php:100
+msgid "Index out-of-stock products?"
+msgstr ""
+
+#: includes/class-settings.php:101
+msgid "Show facet choices for out-of-stock products?"
+msgstr ""
+
+#: includes/class-settings.php:107
+msgid "Import / Export"
+msgstr ""
+
+#: includes/class-settings.php:110
+#: includes/class-settings.php:460
+msgid "Export"
+msgstr ""
+
+#: includes/class-settings.php:114
+#: includes/class-settings.php:468
+msgid "Import"
+msgstr ""
+
+#: includes/class-settings.php:160
+#: includes/facets/pager.php:285
+#: includes/facets/sort.php:55
+msgid "Default label"
+msgstr ""
+
+#: includes/class-settings.php:162
+msgid "Any"
+msgstr ""
+
+#: includes/class-settings.php:166
+msgid "Placeholder text"
+msgstr ""
+
+#: includes/class-settings.php:169
+msgid "Parent term"
+msgstr ""
+
+#: includes/class-settings.php:175
+msgid "Hierarchical"
+msgstr ""
+
+#: includes/class-settings.php:181
+msgid "Show expanded"
+msgstr ""
+
+#: includes/class-settings.php:187
+msgid "Multi-select"
+msgstr ""
+
+#: includes/class-settings.php:196
+#: includes/class-settings.php:288
+msgid "Show ghosts"
+msgstr ""
+
+#: includes/class-settings.php:201
+msgid "Preserve ghost order"
+msgstr ""
+
+#: includes/class-settings.php:212
+msgid "Value modifiers"
+msgstr ""
+
+#: includes/class-settings.php:215
+msgid "Off"
+msgstr ""
+
+#: includes/class-settings.php:216
+msgid "Exclude these values"
+msgstr ""
+
+#: includes/class-settings.php:217
+msgid "Show only these values"
+msgstr ""
+
+#: includes/class-settings.php:230
+msgid "Facet logic"
+msgstr ""
+
+#: includes/class-settings.php:233
+msgid "AND (match all)"
+msgstr ""
+
+#: includes/class-settings.php:234
+msgid "OR (match any)"
+msgstr ""
+
+#: includes/class-settings.php:240
+#: includes/class-settings.php:607
+#: includes/facets/sort.php:57
+msgid "Sort by"
+msgstr ""
+
+#: includes/class-settings.php:243
+msgid "Highest count"
+msgstr ""
+
+#: includes/class-settings.php:244
+msgid "Display value"
+msgstr ""
+
+#: includes/class-settings.php:245
+msgid "Raw value"
+msgstr ""
+
+#: includes/class-settings.php:246
+msgid "Term order"
+msgstr ""
+
+#: includes/class-settings.php:250
+msgid "Count"
+msgstr ""
+
+#: includes/class-settings.php:255
+msgid "Soft limit"
+msgstr ""
+
+#: includes/class-settings.php:261
+msgid "Other data source"
+msgstr ""
+
+#: includes/class-settings.php:267
+msgid "Compare type"
+msgstr ""
+
+#: includes/class-settings.php:270
+msgid "Basic"
+msgstr ""
+
+#: includes/class-settings.php:271
+msgid "Enclose"
+msgstr ""
+
+#: includes/class-settings.php:272
+msgid "Intersect"
+msgstr ""
+
+#: includes/class-settings.php:277
+msgid "UI type"
+msgstr ""
+
+#: includes/class-settings.php:280
+#: includes/facets/checkboxes.php:7
+msgid "Checkboxes"
+msgstr ""
+
+#: includes/class-settings.php:281
+#: includes/facets/radio.php:7
+msgid "Radio"
+msgstr ""
+
+#: includes/class-settings.php:282
+#: includes/facets/dropdown.php:7
+#: includes/facets/proximity.php:273
+msgid "Dropdown"
+msgstr ""
+
+#: includes/class-settings.php:283
+#: includes/facets/fselect.php:7
+msgid "fSelect"
+msgstr ""
+
+#: includes/class-settings.php:292
+msgid "Reset text"
+msgstr ""
+
+#: includes/class-settings.php:405
+msgid "Activate"
+msgstr ""
+
+#: includes/class-settings.php:411
+msgid "Get an API key"
+msgstr ""
+
+#: includes/class-settings.php:418
+#: includes/class-settings.php:428
+msgid "Maps JavaScript API"
+msgstr ""
+
+#: includes/class-settings.php:419
+#: includes/class-settings.php:429
+msgid "Geocoding API"
+msgstr ""
+
+#: includes/class-settings.php:420
+#: includes/class-settings.php:430
+msgid "Places API (Legacy)"
+msgstr ""
+
+#: includes/class-settings.php:421
+#: includes/class-settings.php:431
+msgid "Places API (New)"
+msgstr ""
+
+#: includes/class-settings.php:422
+#: includes/class-settings.php:432
+msgid "Retest"
+msgstr ""
+
+#: includes/class-settings.php:426
+msgid "Enter a Google Maps API key above to test."
+msgstr ""
+
+#: includes/class-settings.php:458
+msgid "All"
+msgstr ""
+
+#: includes/class-settings.php:458
+msgid "All facet"
+msgstr ""
+
+#: includes/class-settings.php:458
+msgid "All listings"
+msgstr ""
+
+#: includes/class-settings.php:458
+#: includes/facets/map.php:437
+#: includes/facets/reset.php:7
+msgid "Reset"
+msgstr ""
+
+#: includes/class-settings.php:465
+msgid "Paste the import code here"
+msgstr ""
+
+#: includes/class-settings.php:466
+msgid "Overwrite existing items?"
+msgstr ""
+
+#: includes/class-settings.php:533
+msgid "Not yet activated"
+msgstr ""
+
+#: includes/class-settings.php:540
+msgid "Valid until"
+msgstr ""
+
+#: includes/class-settings.php:556
+msgid "Number of grid columns"
+msgstr ""
+
+#: includes/class-settings.php:557
+msgid "Spacing between results"
+msgstr ""
+
+#: includes/class-settings.php:558
+msgid "No results text"
+msgstr ""
+
+#: includes/class-settings.php:559
+msgid "Text style"
+msgstr ""
+
+#: includes/class-settings.php:560
+msgid "Text color"
+msgstr ""
+
+#: includes/class-settings.php:561
+msgid "Font size"
+msgstr ""
+
+#: includes/class-settings.php:562
+msgid "Background color"
+msgstr ""
+
+#: includes/class-settings.php:563
+msgid "Border"
+msgstr ""
+
+#: includes/class-settings.php:564
+msgid "Border style"
+msgstr ""
+
+#: includes/class-settings.php:565
+#: includes/facets/proximity.php:275
+msgid "None"
+msgstr ""
+
+#: includes/class-settings.php:566
+msgid "Solid"
+msgstr ""
+
+#: includes/class-settings.php:567
+msgid "Dashed"
+msgstr ""
+
+#: includes/class-settings.php:568
+msgid "Dotted"
+msgstr ""
+
+#: includes/class-settings.php:569
+msgid "Double"
+msgstr ""
+
+#: includes/class-settings.php:570
+msgid "Border color"
+msgstr ""
+
+#: includes/class-settings.php:571
+msgid "Border width"
+msgstr ""
+
+#: includes/class-settings.php:572
+msgid "Button text"
+msgstr ""
+
+#: includes/class-settings.php:573
+msgid "Button text color"
+msgstr ""
+
+#: includes/class-settings.php:574
+msgid "Button padding"
+msgstr ""
+
+#: includes/class-settings.php:575
+msgid "Separator"
+msgstr ""
+
+#: includes/class-settings.php:576
+msgid "Custom CSS"
+msgstr ""
+
+#: includes/class-settings.php:577
+msgid "Column widths"
+msgstr ""
+
+#: includes/class-settings.php:578
+#: includes/class-settings.php:625
+msgid "Content"
+msgstr ""
+
+#: includes/class-settings.php:579
+msgid "Image size"
+msgstr ""
+
+#: includes/class-settings.php:580
+msgid "Author field"
+msgstr ""
+
+#: includes/class-settings.php:581
+msgid "Display name"
+msgstr ""
+
+#: includes/class-settings.php:582
+msgid "User login"
+msgstr ""
+
+#: includes/class-settings.php:583
+msgid "User ID"
+msgstr ""
+
+#: includes/class-settings.php:584
+msgid "Field type"
+msgstr ""
+
+#: includes/class-settings.php:585
+msgid "Text"
+msgstr ""
+
+#: includes/class-settings.php:586
+msgid "Date"
+msgstr ""
+
+#: includes/class-settings.php:587
+msgid "Number"
+msgstr ""
+
+#: includes/class-settings.php:588
+msgid "Date format"
+msgstr ""
+
+#: includes/class-settings.php:589
+msgid "Input format"
+msgstr ""
+
+#: includes/class-settings.php:590
+msgid "Number format"
+msgstr ""
+
+#: includes/class-settings.php:591
+#: includes/facets/reset.php:60
+msgid "Link"
+msgstr ""
+
+#: includes/class-settings.php:592
+msgid "Link type"
+msgstr ""
+
+#: includes/class-settings.php:593
+msgid "Post URL"
+msgstr ""
+
+#: includes/class-settings.php:594
+msgid "Custom URL"
+msgstr ""
+
+#: includes/class-settings.php:595
+msgid "Open in new tab?"
+msgstr ""
+
+#: includes/class-settings.php:596
+#: includes/facets/slider.php:139
+msgid "Prefix"
+msgstr ""
+
+#: includes/class-settings.php:597
+#: includes/facets/slider.php:143
+msgid "Suffix"
+msgstr ""
+
+#: includes/class-settings.php:598
+msgid "Hide item?"
+msgstr ""
+
+#: includes/class-settings.php:599
+msgid "Padding"
+msgstr ""
+
+#: includes/class-settings.php:600
+msgid "CSS class"
+msgstr ""
+
+#: includes/class-settings.php:601
+msgid "Button border"
+msgstr ""
+
+#: includes/class-settings.php:602
+msgid "Term URL"
+msgstr ""
+
+#: includes/class-settings.php:603
+msgid "Fetch"
+msgstr ""
+
+#: includes/class-settings.php:604
+msgid "All post types"
+msgstr ""
+
+#: includes/class-settings.php:605
+msgid "and show"
+msgstr ""
+
+#: includes/class-settings.php:606
+msgid "per page"
+msgstr ""
+
+#: includes/class-settings.php:610
+msgid "Post Name"
+msgstr ""
+
+#: includes/class-settings.php:614
+msgid "Comment Count"
+msgstr ""
+
+#: includes/class-settings.php:615
+msgid "Menu Order"
+msgstr ""
+
+#: includes/class-settings.php:617
+msgid "Narrow results by"
+msgstr ""
+
+#: includes/class-settings.php:618
+msgid "Hit Enter"
+msgstr ""
+
+#: includes/class-settings.php:619
+msgid "Add query sort"
+msgstr ""
+
+#: includes/class-settings.php:620
+msgid "Add query filter"
+msgstr ""
+
+#: includes/class-settings.php:621
+msgid "Clear"
+msgstr ""
+
+#: includes/class-settings.php:622
+msgid "Enter term slugs"
+msgstr ""
+
+#: includes/class-settings.php:623
+msgid "Enter values"
+msgstr ""
+
+#: includes/class-settings.php:624
+msgid "Layout"
+msgstr ""
+
+#: includes/class-settings.php:626
+msgid "Style"
+msgstr ""
+
+#: includes/class-settings.php:627
+msgid "Row"
+msgstr ""
+
+#: includes/class-settings.php:628
+msgid "Column"
+msgstr ""
+
+#: includes/class-settings.php:629
+msgid "Start typing"
+msgstr ""
+
+#: includes/class-settings.php:630
+#: templates/page-settings.php:158
+#: templates/page-settings.php:186
+msgid "Label"
+msgstr ""
+
+#: includes/class-settings.php:631
+msgid "Unique name"
+msgstr ""
+
+#: includes/class-settings.php:632
+msgid "Facet type"
+msgstr ""
+
+#: includes/class-settings.php:633
+msgid "Copy shortcode"
+msgstr ""
+
+#: includes/class-settings.php:634
+msgid "Data source"
+msgstr ""
+
+#: includes/class-settings.php:635
+msgid "Switch to advanced mode"
+msgstr ""
+
+#: includes/class-settings.php:636
+msgid "Switch to visual mode"
+msgstr ""
+
+#: includes/class-settings.php:637
+msgid "Display"
+msgstr ""
+
+#: includes/class-settings.php:638
+msgid "Query"
+msgstr ""
+
+#: includes/class-settings.php:639
+msgid "Help"
+msgstr ""
+
+#: includes/class-settings.php:640
+msgid "Display Code"
+msgstr ""
+
+#: includes/class-settings.php:641
+msgid "Query Arguments"
+msgstr ""
+
+#: includes/class-settings.php:642
+msgid "Saving"
+msgstr ""
+
+#: includes/class-settings.php:643
+msgid "Indexing"
+msgstr ""
+
+#: includes/class-settings.php:644
+msgid "The index table is empty"
+msgstr ""
+
+#: includes/class-settings.php:645
+msgid "Indexing complete"
+msgstr ""
+
+#: includes/class-settings.php:646
+msgid "Looking"
+msgstr ""
+
+#: includes/class-settings.php:647
+msgid "Purging"
+msgstr ""
+
+#: includes/class-settings.php:648
+msgid "Copied!"
+msgstr ""
+
+#: includes/class-settings.php:649
+msgid "Press CTRL+C to copy"
+msgstr ""
+
+#: includes/class-settings.php:650
+msgid "Activating"
+msgstr ""
+
+#: includes/class-settings.php:651
+#: templates/page-settings.php:115
+msgid "Re-index"
+msgstr ""
+
+#: includes/class-settings.php:652
+msgid "Stop indexer"
+msgstr ""
+
+#: includes/class-settings.php:653
+msgid "Loading"
+msgstr ""
+
+#: includes/class-settings.php:654
+msgid "Importing"
+msgstr ""
+
+#: includes/class-settings.php:655
+msgid "Convert to query args"
+msgstr ""
+
+#: includes/class-settings.php:699
+msgid "full"
+msgstr ""
+
+#: includes/class-updater.php:163
+msgid "Please renew your license for automatic updates."
+msgstr ""
+
+#: includes/class-updater.php:168
+msgid "Please activate your FacetWP license for automatic updates."
+msgstr ""
+
+#: includes/facets/autocomplete.php:11
+msgid "Autocomplete"
+msgstr ""
+
+#: includes/facets/date_range.php:7
+msgid "Date Range"
+msgstr ""
+
+#: includes/facets/date_range.php:137
+#: includes/facets/number_range.php:112
+msgid "Fields to show"
+msgstr ""
+
+#: includes/facets/date_range.php:139
+msgid "Start + End Dates"
+msgstr ""
+
+#: includes/facets/date_range.php:140
+msgid "Exact Date"
+msgstr ""
+
+#: includes/facets/date_range.php:141
+msgid "Start Date"
+msgstr ""
+
+#: includes/facets/date_range.php:142
+msgid "End Date"
+msgstr ""
+
+#: includes/facets/date_range.php:151
+msgid "Display format"
+msgstr ""
+
+#: includes/facets/hierarchy.php:7
+msgid "Hierarchy"
+msgstr ""
+
+#: includes/facets/map.php:24
+msgid "Map"
+msgstr ""
+
+#: includes/facets/map.php:413
+#: includes/facets/map.php:474
+#: includes/facets/proximity.php:255
+msgid "Longitude"
+msgstr ""
+
+#: includes/facets/map.php:414
+msgid "(Optional) use a separate longitude field."
+msgstr ""
+
+#: includes/facets/map.php:420
+msgid "Map ID"
+msgstr ""
+
+#: includes/facets/map.php:422
+msgid "In the Google Cloud Console's Map Management section, create a Map ID and enter it here. Choose a map rendering type (raster or vector) in the Map ID settings. To change your map styling, create a Map Style in the Map Styles section and attach it to the Map ID."
+msgstr ""
+
+#: includes/facets/map.php:425
+msgid "Map design"
+msgstr ""
+
+#: includes/facets/map.php:426
+msgid "In the Google Cloud Console's Map Management section, create a Map ID and enter it in the above Map ID setting. Then create a Map Style in the Map Styles section and attach it to the Map ID."
+msgstr ""
+
+#: includes/facets/map.php:432
+msgid "Enable filtering button label"
+msgstr ""
+
+#: includes/facets/map.php:433
+msgid "Enable map filtering"
+msgstr ""
+
+#: includes/facets/map.php:436
+msgid "Reset button label"
+msgstr ""
+
+#: includes/facets/map.php:441
+msgid "Marker clustering"
+msgstr ""
+
+#: includes/facets/map.php:442
+msgid "Group markers into clusters?"
+msgstr ""
+
+#: includes/facets/map.php:446
+msgid "Marker limit"
+msgstr ""
+
+#: includes/facets/map.php:448
+msgid "Show all results"
+msgstr ""
+
+#: includes/facets/map.php:449
+msgid "Show current page results"
+msgstr ""
+
+#: includes/facets/map.php:454
+msgid "Width"
+msgstr ""
+
+#: includes/facets/map.php:454
+msgid "Height"
+msgstr ""
+
+#: includes/facets/map.php:455
+msgid "Map width / height"
+msgstr ""
+
+#: includes/facets/map.php:456
+msgid "Set width and height of the map. Without units, px is assumed. Use other CSS units if needed, e.g. 100% for responsive full width of the parent container. Don't use 100% for the height if the map's container does not have a fixed height, else the map will have no height and will not show."
+msgstr ""
+
+#: includes/facets/map.php:463
+#: includes/facets/number_range.php:116
+msgid "Min"
+msgstr ""
+
+#: includes/facets/map.php:464
+#: includes/facets/number_range.php:117
+msgid "Max"
+msgstr ""
+
+#: includes/facets/map.php:465
+msgid "Zoom min / max"
+msgstr ""
+
+#: includes/facets/map.php:466
+msgid "Set zoom bounds (between 1 and 20)."
+msgstr ""
+
+#: includes/facets/map.php:473
+msgid "Latitude"
+msgstr ""
+
+#: includes/facets/map.php:475
+msgid "Zoom (1-20)"
+msgstr ""
+
+#: includes/facets/map.php:476
+msgid "Fallback lat / lng / zoom"
+msgstr ""
+
+#: includes/facets/map.php:477
+msgid "Center the map here, and set a custom zoom level, if there are no results."
+msgstr ""
+
+#: includes/facets/map.php:487
+msgid "Info window ajax loading"
+msgstr ""
+
+#: includes/facets/map.php:488
+msgid "Dynamically load marker info window content on click of markers, which could improve load times for pages with many markers."
+msgstr ""
+
+#: includes/facets/map.php:492
+msgid "Info window content"
+msgstr ""
+
+#: includes/facets/map.php:495
+msgid "To search your code, click in the editor, then use Ctrl+F (Windows/Linux) or Cmd+F (Mac)."
+msgstr ""
+
+#: includes/facets/number_range.php:7
+msgid "Number Range"
+msgstr ""
+
+#: includes/facets/number_range.php:114
+msgid "Min + Max"
+msgstr ""
+
+#: includes/facets/number_range.php:115
+msgid "Exact"
+msgstr ""
+
+#: includes/facets/pager.php:10
+msgid "Pager"
+msgstr ""
+
+#: includes/facets/pager.php:213
+msgid "Pager type"
+msgstr ""
+
+#: includes/facets/pager.php:215
+msgid "Page numbers"
+msgstr ""
+
+#: includes/facets/pager.php:216
+msgid "Result counts"
+msgstr ""
+
+#: includes/facets/pager.php:217
+msgid "Load more"
+msgstr ""
+
+#: includes/facets/pager.php:218
+msgid "Per page"
+msgstr ""
+
+#: includes/facets/pager.php:222
+msgid "Inner size"
+msgstr ""
+
+#: includes/facets/pager.php:228
+msgid "Dots label"
+msgstr ""
+
+#: includes/facets/pager.php:234
+msgid "Prev button label"
+msgstr ""
+
+#: includes/facets/pager.php:240
+msgid "Next button label"
+msgstr ""
+
+#: includes/facets/pager.php:246
+msgid "Scroll target"
+msgstr ""
+
+#: includes/facets/pager.php:253
+msgid "Scroll offset"
+msgstr ""
+
+#: includes/facets/pager.php:259
+msgid "Count text (plural)"
+msgstr ""
+
+#: includes/facets/pager.php:265
+msgid "Count text (singular)"
+msgstr ""
+
+#: includes/facets/pager.php:270
+msgid "Count text (no results)"
+msgstr ""
+
+#: includes/facets/pager.php:275
+msgid "Load more text"
+msgstr ""
+
+#: includes/facets/pager.php:280
+msgid "Loading text"
+msgstr ""
+
+#: includes/facets/pager.php:290
+msgid "Per page options"
+msgstr ""
+
+#: includes/facets/proximity.php:13
+msgid "Proximity"
+msgstr ""
+
+#: includes/facets/proximity.php:263
+msgid "Unit of measurement"
+msgstr ""
+
+#: includes/facets/proximity.php:265
+msgid "Miles"
+msgstr ""
+
+#: includes/facets/proximity.php:266
+msgid "Kilometers"
+msgstr ""
+
+#: includes/facets/proximity.php:271
+msgid "Radius UI"
+msgstr ""
+
+#: includes/facets/proximity.php:274
+#: includes/facets/slider.php:7
+msgid "Slider"
+msgstr ""
+
+#: includes/facets/proximity.php:279
+msgid "Radius options"
+msgstr ""
+
+#: includes/facets/proximity.php:285
+msgid "Range (min)"
+msgstr ""
+
+#: includes/facets/proximity.php:290
+msgid "Range (max)"
+msgstr ""
+
+#: includes/facets/proximity.php:295
+msgid "Default radius"
+msgstr ""
+
+#: includes/facets/rating.php:7
+msgid "Star Rating"
+msgstr ""
+
+#: includes/facets/rating.php:118
+msgid "Rating icon"
+msgstr ""
+
+#: includes/facets/rating.php:121
+msgid "Stars"
+msgstr ""
+
+#: includes/facets/rating.php:122
+msgid "Star Outlines"
+msgstr ""
+
+#: includes/facets/rating.php:123
+msgid "Hearts"
+msgstr ""
+
+#: includes/facets/rating.php:128
+msgid "Show ghost ratings"
+msgstr ""
+
+#: includes/facets/rating.php:133
+msgid "Color"
+msgstr ""
+
+#: includes/facets/rating.php:140
+msgid "Selected color"
+msgstr ""
+
+#: includes/facets/rating.php:146
+msgid "Undo color"
+msgstr ""
+
+#: includes/facets/rating.php:152
+msgid "Ghost color"
+msgstr ""
+
+#: includes/facets/reset.php:57
+msgid "Reset UI"
+msgstr ""
+
+#: includes/facets/reset.php:59
+msgid "Button"
+msgstr ""
+
+#: includes/facets/reset.php:65
+msgid "Include / exclude"
+msgstr ""
+
+#: includes/facets/reset.php:68
+msgid "Reset everything"
+msgstr ""
+
+#: includes/facets/reset.php:69
+msgid "Reset only these facets"
+msgstr ""
+
+#: includes/facets/reset.php:70
+msgid "Reset all except these facets"
+msgstr ""
+
+#: includes/facets/reset.php:80
+msgid "Auto-hide"
+msgstr ""
+
+#: includes/facets/search.php:8
+msgid "Search"
+msgstr ""
+
+#: includes/facets/search.php:68
+msgid "WP Default"
+msgstr ""
+
+#: includes/facets/search.php:77
+msgid "Search engine"
+msgstr ""
+
+#: includes/facets/search.php:82
+msgid "Auto refresh"
+msgstr ""
+
+#: includes/facets/search.php:87
+msgid "Order by relevance"
+msgstr ""
+
+#: includes/facets/slider.php:151
+msgid "Format"
+msgstr ""
+
+#: includes/facets/slider.php:158
+msgid "Step"
+msgstr ""
+
+#: includes/facets/sort.php:10
+msgid "Sort"
+msgstr ""
+
+#: includes/facets/sort.php:62
+msgid "Sort options"
+msgstr ""
+
+#: includes/integrations/searchwp/searchwp.php:209
+msgid "Default"
+msgstr ""
+
+#: index.php:86
+msgid "FacetWP requires PHP %s or above. Please contact your host and request a PHP upgrade."
+msgstr ""
+
+#: templates/page-settings.php:107
+#: templates/page-settings.php:147
+#: templates/page-settings.php:151
+msgid "Facets"
+msgstr ""
+
+#: templates/page-settings.php:108
+#: templates/page-settings.php:175
+#: templates/page-settings.php:179
+msgid "Listings"
+msgstr ""
+
+#: templates/page-settings.php:110
+msgid "Support"
+msgstr ""
+
+#: templates/page-settings.php:119
+msgid "Show indexer stats"
+msgstr ""
+
+#: templates/page-settings.php:120
+msgid "Show indexable post types"
+msgstr ""
+
+#: templates/page-settings.php:121
+msgid "Purge the index table"
+msgstr ""
+
+#: templates/page-settings.php:126
+msgid "Save changes"
+msgstr ""
+
+#: templates/page-settings.php:152
+#: templates/page-settings.php:180
+msgid "Add new"
+msgstr ""
+
+#: templates/page-settings.php:159
+#: templates/page-settings.php:187
+msgid "Name"
+msgstr ""
+
+#: templates/page-settings.php:160
+msgid "Type"
+msgstr ""
+
+#: templates/page-settings.php:161
+msgid "Source"
+msgstr ""
+
+#: templates/page-settings.php:162
+msgid "Rows"
+msgstr ""
+
+#: templates/page-settings.php:188
+msgid "Display mode"
+msgstr ""
+
+#: templates/page-settings.php:189
+msgid "Post types"
+msgstr ""
diff --git a/wp-content/plugins/facetwp/package.json b/wp-content/plugins/facetwp/package.json
new file mode 100644
index 000000000..e3a815db3
--- /dev/null
+++ b/wp-content/plugins/facetwp/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "facetwp",
+ "type": "module",
+ "devDependencies": {
+ "@rollup/plugin-buble": "^1.0.3",
+ "@rollup/plugin-multi-entry": "^6.0.1",
+ "@rollup/plugin-terser": "^0.4.4",
+ "rollup": "^2.79.1"
+ },
+ "scripts": {
+ "build": "rollup -c",
+ "watch": "rollup -c -w"
+ }
+}
diff --git a/wp-content/plugins/facetwp/rollup.config.js b/wp-content/plugins/facetwp/rollup.config.js
new file mode 100644
index 000000000..a9a6c1daf
--- /dev/null
+++ b/wp-content/plugins/facetwp/rollup.config.js
@@ -0,0 +1,52 @@
+import terser from '@rollup/plugin-terser';
+import multiEntry from '@rollup/plugin-multi-entry';
+import buble from '@rollup/plugin-buble';
+
+export default [{
+ input: [
+ 'assets/vendor/fUtil/fUtil.js',
+ 'assets/js/src/event-manager.js',
+ 'assets/js/src/front.js',
+ 'assets/js/src/front-facets.js'
+ ],
+ output: {
+ file: 'assets/js/dist/front.min.js',
+ format: 'iife'
+ },
+ plugins: [
+ multiEntry(),
+ terser()
+ ]
+},
+{
+ input: 'assets/js/src/admin.js',
+ output: {
+ file: 'assets/js/dist/admin.min.js',
+ format: 'iife'
+ },
+ plugins: [
+ buble( { transforms: { asyncAwait: false } } )
+ ]
+},
+{
+ input: 'assets/vendor/fDate/fDate.js',
+ output: {
+ file: 'assets/vendor/fDate/fDate.min.js',
+ format: 'iife'
+ },
+ plugins: [
+ buble(),
+ terser()
+ ]
+},
+{
+ input: 'assets/vendor/nummy/nummy.js',
+ output: {
+ file: 'assets/vendor/nummy/nummy.min.js',
+ format: 'iife'
+ },
+ plugins: [
+ buble(),
+ terser()
+ ]
+}]
diff --git a/wp-content/plugins/facetwp/templates/page-settings.php b/wp-content/plugins/facetwp/templates/page-settings.php
new file mode 100755
index 000000000..409a6a37d
--- /dev/null
+++ b/wp-content/plugins/facetwp/templates/page-settings.php
@@ -0,0 +1,239 @@
+get_html();
+
+// Settings
+$settings = FWP()->settings->get_registered_settings();
+$i18n = FWP()->settings->get_i18n_strings();
+$image_sizes = FWP()->settings->get_image_size_labels();
+
+// Useful data
+$data = FWP()->helper->settings;
+$facet_types = FWP()->helper->facet_types;
+$data_sources = FWP()->helper->get_data_sources();
+$layout_data = FWP()->builder->get_layout_data();
+$query_data = FWP()->builder->get_query_data();
+
+// Generate fields and handle aliases
+$facet_fields = [];
+$fields = FWP()->settings->get_registered_facet_fields();
+
+foreach ( $fields as $name => $field ) {
+ $output = FWP()->settings->get_facet_field_html( $name );
+ $output = preg_replace( '/[ ]+/s', ' ', $output );
+ $facet_fields[ $name ] = [
+ 'names' => isset( $field['items'] ) ? array_keys( $field['items'] ) : [ $name ],
+ 'html' => trim( $output )
+ ];
+}
+
+// Get SVG icons
+$svg_icons = FWP()->settings->get_svg();
+
+// Clone facet settings HTML
+$facet_clone = [];
+$admin_scripts = '';
+
+foreach ( $facet_types as $name => $class ) {
+ if (method_exists( $class, 'admin_scripts' ) ) {
+ ob_start();
+ $class->admin_scripts();
+ $admin_scripts .= ob_get_clean();
+ }
+
+ if ( method_exists( $class, 'settings_html' ) ) {
+ ob_start();
+ $class->settings_html();
+ $facet_clone[ $name ] = trim( ob_get_clean() );
+ }
+}
+
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Check your browser console if this text doesn't disappear
+
+
+
+
+
+
+ »
+ {{ getItemLabel() }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ »
+ {{ getItemLabel() }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wp-content/plugins/facetwp/templates/page-support.php b/wp-content/plugins/facetwp/templates/page-support.php
new file mode 100644
index 000000000..d2c9c8998
--- /dev/null
+++ b/wp-content/plugins/facetwp/templates/page-support.php
@@ -0,0 +1,121 @@
+payment_id = (int) FWP()->helper->get_license_meta( 'payment_id' );
+ }
+
+
+ function get_html() {
+ if ( 0 < $this->payment_id ) {
+ $output = '';
+ }
+ else {
+ $output = 'Active License Required
';
+ $output .= 'Please activate or renew your license to access support.
';
+ }
+
+ return $output;
+ }
+
+
+ function get_sysinfo() {
+ $plugins = get_plugins();
+ $active_plugins = get_option( 'active_plugins', [] );
+ $theme = wp_get_theme();
+ $parent = $theme->parent();
+
+ ob_start();
+
+?>
+Home URL:
+
+Payment ID: payment_id; ?>
+
+WordPress Version:
+
+Theme: get( 'Name' ) . ' ' . $theme->get( 'Version' ); ?>
+
+Parent Theme: get( 'Name' ) . ' ' . $parent->get( 'Version' ); ?>
+
+
+Debug Mode: helper->get_setting( 'debug_mode' ) ) ? 'ON' : 'OFF'; ?>
+
+PHP Version:
+
+MySQL Version: db_version() ); ?>
+
+Web Server Info:
+
+PHP Memory Limit:
+
+WP_MEMORY_LIMIT:
+
+WP_MAX_MEMORY_LIMIT:
+
+WP_DEBUG:
+
+WP_DEBUG_LOG:
+
+
+ $plugin_data ) {
+ $network_plugin = get_plugin_data(WP_PLUGIN_DIR . '/' . $plugin_path);
+ echo $network_plugin['Name'] . ' ' . $network_plugin['Version'] . '\n';
+ }
+
+ $sites = get_sites();
+ $current_subsite_id = get_current_blog_id();
+
+ foreach ( $sites as $subsite ) {
+
+ $subsite_id = get_object_vars( $subsite )['blog_id'];
+ $subsite_name = get_blog_details( $subsite_id )->blogname;
+ $active_plugins = get_blog_option( $subsite->blog_id, 'active_plugins' );
+
+ if ( intval( $subsite_id ) === $current_subsite_id ) {
+ echo '\n### Sub-site #' . $subsite_id . ' - ' . $subsite_name . ' (Current):\n';
+ } else {
+ echo '\n### Sub-site #' . $subsite_id . ' - ' . $subsite_name . ':\n';
+ }
+
+ if ( ! empty( $active_plugins ) ) {
+ foreach ( $plugins as $plugin_path => $plugin ) {
+ if ( in_array( $plugin_path, $active_plugins ) ) {
+ echo $plugin['Name'] . ' ' . $plugin['Version'] . '\n';
+ }
+ }
+ } else {
+ echo 'No active plugins' . '\n';
+ }
+ }
+
+ } else {
+ foreach ( $plugins as $plugin_path => $plugin ) {
+ if ( in_array( $plugin_path, $active_plugins ) ) {
+ echo $plugin['Name'] . ' ' . $plugin['Version'] . '\n';
+ }
+ }
+ }
+
+ $output = ob_get_clean();
+ $output = str_replace( '.php', '-php', $output );
+ $output = preg_replace( "/[ ]{2,}/", ' ', trim( $output ) );
+ $output = str_replace( '\n', '{n}', $output );
+ $output = urlencode( $output );
+ return $output;
+ }
+}
\ No newline at end of file