PluginProbe
Filter Everything — WordPress & WooCommerce Filters / 1.9.7
Filter Everything — WordPress & WooCommerce Filters v1.9.7
1.9.7 1.9.6 1.9.5 1.9.4 1.9.3 1.9.2.2 1.9.2.1 trunk 1.2.1 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.4.1 1.4.4 1.4.5 1.4.8 1.4.9 1.5.0 1.5.1 1.6.0 1.6.1 1.6.2 All 52 releases
← All changes | src/Plugin.php +513 -622 1.9.2.21.9.7 View file →
@@ -31,8 +31,11 @@
31 31
32 32 if( is_admin() ){
33 33 new Admin();
34 34 }
35 +
36 + // «Plugin notifications» channel. Not admin-only: its daily fetch runs in WP-Cron.
37 + new Messages();
35 38 }
36 39
37 40 public function register_hooks(){
38 41 /**
@@ -40,17 +43,14 @@
40 43 */
41 44 $getData = Container::instance()->getTheGet();
42 45
43 46 if( ! is_admin() ){
44 - global $wp_version;
47 + // Priority 1000: run AFTER third-party routers (e.g. Brain\Cortex inside
48 + // WP User Manager, priority 100). When Cortex ran after us, it received
49 + // our "false", found no route of its own and re-ran $wp->query_posts(),
50 + // wiping the filtered main query with an unfiltered one.
51 + add_filter( 'do_parse_request', array( $this->wpManager, 'customParseRequest' ), 1000, 3 );
45 52
46 - // Different ParseRequest methods for WP version 6.0 or prior
47 - if( version_compare( $wp_version, '5.9.3', '>' ) ){
48 - add_filter( 'do_parse_request', array( $this->wpManager, 'customParseRequest' ), 10, 3 );
49 - }else{
50 - add_filter( 'do_parse_request', array( $this->wpManager, 'customParseRequestBefore60' ), 10, 3 );
51 - }
52 -
53 53 add_action( 'parse_request', array( $this->wpManager, 'parseRequest' ) );
54 54 add_action( 'pre_get_posts', array( $this->wpManager, 'addFilterQueryToWpQuery' ), 9999 );
55 55
56 56 add_filter( 'posts_where', [ $this->wpManager, 'fixPostsWhereForSearch' ], 10, 2 );
@@ -101,8 +101,13 @@
101 101
102 102 add_action( 'save_post', [$this, 'resetTransitions'] );
103 103 add_action( 'delete_post', [$this, 'resetTransitions'] );
104 104 add_action( 'woocommerce_ajax_save_product_variations', [$this, 'resetTransitions'] );
105 + // Term changes bypass save_post but alter the filter data (term names,
106 + // slugs, product assignments via wp-cli/imports) — same invalidation
107 + add_action( 'created_term', [$this, 'resetTransitions'] );
108 + add_action( 'edited_term', [$this, 'resetTransitions'] );
109 + add_action( 'delete_term', [$this, 'resetTransitions'] );
105 110
106 111 if( isset( $getData['reset_filters_cache'] ) && $getData['reset_filters_cache'] == true ){
107 112 $this->resetTransitions();
108 113 }
@@ -170,37 +175,21 @@
170 175 $parts = explode( '#', $entityEname, 2 );
171 176 $e_name = isset( $parts[1] ) ? $parts[1] : '';
172 177 $type = isset( $parts[0] ) ? $parts[0] : '';
173 178
174 - $terms_transient_key = '';
175 -
176 179 if ( in_array( $type, [ 'post_meta_num', 'tax_numeric' ] ) ) {
177 - global $wpdb;
178 - $key = flrt_get_terms_transient_key( $type . '_'. $e_name, false );
179 -
180 - $result = $wpdb->get_results( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE '%{$key}%'", ARRAY_A );
181 -
182 - if ( isset( $result[0]['option_name'] ) ) {
183 - $terms_transient_key = str_replace( '_transient_', '', str_replace( '_transient_timeout_', '', $result[0]['option_name'] ) );
184 - }
180 + // Entity code appends the queried post types between the e_name and
181 + // the format suffix (wpc_terms_post_meta_num__price_product_product_variation_v2),
182 + // so the exact key can not be rebuilt here - match on the unsuffixed base
183 + $this->deleteTermsTransientsLike( 'wpc_terms_' . $type . '_' . $e_name );
185 184 }
186 185
187 186 if ( in_array( $type, [ 'post_date' ] ) ) {
188 - global $wpdb;
189 - $key = 'wpc_terms_post_date_';
190 - $result = $wpdb->get_results( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE '%{$key}%'", ARRAY_A );
191 - if ( isset( $result[0]['option_name'] ) ) {
192 - $terms_transient_key = str_replace( '_transient_', '', str_replace( '_transient_timeout_', '', $result[0]['option_name'] ) );
193 - }
187 + $this->deleteTermsTransientsLike( 'wpc_terms_post_date_' );
194 188 }
195 189
196 190 if ( in_array( $type, [ 'post_meta_date' ] ) ) {
197 - global $wpdb;
198 - $key = 'wpc_terms_post_meta_date_';
199 - $result = $wpdb->get_results( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE '%{$key}%'", ARRAY_A );
200 - if ( isset( $result[0]['option_name'] ) ) {
201 - $terms_transient_key = str_replace( '_transient_', '', str_replace( '_transient_timeout_', '', $result[0]['option_name'] ) );
202 - }
191 + $this->deleteTermsTransientsLike( 'wpc_terms_post_meta_date_' );
203 192 }
204 193
205 194 if ( $type === 'post_meta_exists' ) {
206 195 delete_transient( flrt_get_post_ids_transient_key( $e_name .'_yes' ) );
@@ -206,12 +195,12 @@
206 195 delete_transient( flrt_get_post_ids_transient_key( $e_name .'_yes' ) );
207 196 delete_transient( flrt_get_post_ids_transient_key( $e_name .'_no' ) );
208 197 }
209 198
210 - if( ! $terms_transient_key ){
211 - $terms_transient_key = flrt_get_terms_transient_key( $type . '_'. $e_name );
212 - }
213 -
199 + // With an external object cache transients are not in the options
200 + // table and the LIKE lookups above find nothing, so always delete
201 + // the post-type-less key variant directly as well
202 + $terms_transient_key = flrt_get_terms_transient_key( $type . '_'. $e_name );
214 203 $post_ids_transient_key = flrt_get_post_ids_transient_key( $slug );
215 204 $var_meta_transient_key = flrt_get_variations_transient_key( 'attribute_'. $e_name );
216 205
217 206 delete_transient( $terms_transient_key );
@@ -219,17 +208,56 @@
219 208 delete_transient( $var_meta_transient_key );
220 209 }
221 210 }
222 211
223 - $variations_key = 'wpc_posts_variations';
212 + $variations_key = 'wpc_posts_variations' . FLRT_CACHE_FORMAT_SUFFIX;
224 213 $filter_key = 'wpc_filters_query';
225 214
226 215 delete_transient($variations_key);
227 216 delete_transient($filter_key);
228 217
218 + // Static filter-data files (see maybeWriteJsonBlobFile): bump the
219 + // version so new URLs are minted, and remove the now-unused files
220 + update_option( 'flrt_json_blob_ver', time(), false );
221 + $uploads = wp_upload_dir();
222 + if ( empty( $uploads['error'] ) ) {
223 + $blob_files = glob( trailingslashit( $uploads['basedir'] ) . 'flrt-cache/filters-*.json' );
224 + if ( is_array( $blob_files ) ) {
225 + foreach ( $blob_files as $blob_file ) {
226 + @unlink( $blob_file );
227 + }
228 + }
229 + }
230 +
229 231 unset( $terms_transient_key, $post_ids_transient_key, $var_meta_transient_key, $all_filters, $em );
230 232 }
231 233
234 + /**
235 + * Deletes all DB-stored transients whose name contains the given base key.
236 + * Used by resetTransitions() for term caches whose full key includes parts
237 + * unknown at reset time (queried post types, language code, format suffix).
238 + */
239 + private function deleteTermsTransientsLike( $base_key )
240 + {
241 + global $wpdb;
242 +
243 + $like = '%' . $wpdb->esc_like( $base_key ) . '%';
244 + $names = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE %s", $like ) );
245 +
246 + if ( ! is_array( $names ) ) {
247 + return;
248 + }
249 +
250 + $keys = [];
251 + foreach ( $names as $name ) {
252 + $keys[] = str_replace( [ '_transient_timeout_', '_transient_' ], '', $name );
253 + }
254 +
255 + foreach ( array_unique( $keys ) as $key ) {
256 + delete_transient( $key );
257 + }
258 + }
259 +
232 260 public function prepareEntities()
233 261 {
234 262 $wpManager = Container::instance()->getWpManager();
235 263 $em = Container::instance()->getEntityManager();
@@ -252,14 +280,13 @@
252 280 if( $key === 'show_count' ){
253 281
254 282 $new_fields['instead_custom_posts_container'] = array(
255 283 'type' => 'inProButton',
256 - 'label' => esc_html__('HTML id or class of the Posts Container', 'filter-everything'),
284 + 'label' => esc_html__('Results container', 'filter-everything'),
257 285 'pro_label' => flrt_pro_promo_label(),
258 286 'name' => $filterSet->generateFieldName('instead_custom_posts_container'),
259 287 'id' => $filterSet->generateFieldId('instead_custom_posts_container'),
260 288 'default' => '',
261 - 'placeholder' => esc_html__('Available in PRO', 'filter-everything'),
262 289 'settings' => true
263 290 );
264 291
265 292 }
@@ -285,9 +312,12 @@
285 312
286 313 public function addSetTailFields( $fields, $filterSet )
287 314 {
288 315 $new_fields = [];
289 - $insert_after = defined('FLRT_FILTERS_PRO') ? 'custom_posts_container' : 'show_count';
316 + // In PRO the tail block goes right after hide_empty_filter, which pushes
317 + // custom_posts_container below the horizontal filters — the same position
318 + // its free-mode teaser stub has
319 + $insert_after = defined('FLRT_FILTERS_PRO') ? 'hide_empty_filter' : 'show_count';
290 320
291 321 foreach ( $fields as $key => $attributes ){
292 322 // Always insert regular 'old' field
293 323 $new_fields[$key] = $attributes;
@@ -295,9 +325,9 @@
295 325 if( $key === $insert_after ){
296 326
297 327 $new_fields['use_search_field'] = array(
298 328 'type' => 'Checkbox',
299 - 'label' => esc_html__('Search Field', 'filter-everything'),
329 + 'label' => esc_html__('Enable Search Field', 'filter-everything'),
300 330 'name' => $filterSet->generateFieldName('use_search_field'),
301 331 'id' => $filterSet->generateFieldId('use_search_field'),
302 332 'class' => 'wpc-field-use-search-field',
303 333 'default' => 'no',
@@ -438,593 +468,22 @@
438 468 echo sprintf('<meta name="robots" content="%s">', $content)."\r\n";
439 469 }
440 470 }
441 471
472 + /**
473 + * @see DynamicCss::printInlineCss() (kept as the wp_head callback name)
474 + */
442 475 public function inlineFrontCss()
443 476 {
444 - $inline = '<style type="text/css" id="filter-everything-inline-css">.wpc-orderby-select{width:100%}.wpc-filters-open-button-container{display:none}.wpc-debug-message{padding:16px;font-size:14px;border:1px dashed #ccc;margin-bottom:20px}.wpc-debug-title{visibility:hidden}.wpc-button-inner,.wpc-chip-content{display:flex;align-items:center}.wpc-icon-html-wrapper{position:relative;margin-right:10px;top:2px}.wpc-icon-html-wrapper span{display:block;height:1px;width:18px;border-radius:3px;background:#2c2d33;margin-bottom:4px;position:relative}span.wpc-icon-line-1:after,span.wpc-icon-line-2:after,span.wpc-icon-line-3:after{content:"";display:block;width:3px;height:3px;border:1px solid #2c2d33;background-color:#fff;position:absolute;top:-2px;box-sizing:content-box}span.wpc-icon-line-3:after{border-radius:50%;left:2px}span.wpc-icon-line-1:after{border-radius:50%;left:5px}span.wpc-icon-line-2:after{border-radius:50%;left:12px}body .wpc-filters-open-button-container a.wpc-filters-open-widget,body .wpc-filters-open-button-container a.wpc-open-close-filters-button{display:inline-block;text-align:left;border:1px solid #2c2d33;border-radius:2px;line-height:1.5;padding:7px 12px;background-color:transparent;color:#2c2d33;box-sizing:border-box;text-decoration:none!important;font-weight:400;transition:none;position:relative}@media screen and (max-width:768px){.wpc_show_bottom_widget .wpc-filters-open-button-container,.wpc_show_open_close_button .wpc-filters-open-button-container{display:block}.wpc_show_bottom_widget .wpc-filters-open-button-container{margin-top:1em;margin-bottom:1em}}</style>'."\r\n";
445 - echo $inline;
477 + ( new DynamicCss() )->printInlineCss();
446 478 }
447 479
480 + /**
481 + * @see DynamicCss::enqueueDynamicCss() (kept as the wp_print_styles callback name)
482 + */
448 483 public function dynamicFrontCss()
449 484 {
450 - // Do not include plugin CSS if there are no Filter Sets on the page
451 - $sets = $this->wpManager->getQueryVar( 'wpc_page_related_set_ids', [] );
452 - if( empty( $sets ) ) {
453 - return false;
454 - }
455 -
456 - $maxHeight = flrt_get_option( 'container_height' );
457 - $color = flrt_get_option( 'primary_color', flrt_default_theme_color() );
458 - $move_to_top = flrt_get_option( 'try_move_to_top_sidebar' );
459 - $wpc_mobile_width = flrt_get_mobile_width();
460 -
461 - // Experimental Options
462 - $custom_css = flrt_get_experimental_option('custom_css');
463 - $use_loader = flrt_get_experimental_option('use_loader');
464 - $dark_overlay = flrt_get_experimental_option('dark_overlay');
465 - $styled_inputs = flrt_get_experimental_option('styled_inputs');
466 - $use_select2 = flrt_get_experimental_option('select2_dropdowns');
467 - $rounded_swatch = flrt_get_experimental_option('rounded_swatches');
468 - $contrastColor = false;
469 -
470 - $swatches_measurements = [ 'width' => 32, 'height' => 32 ];
471 - if( $rounded_swatch ) {
472 - $swatches_measurements = [ 'width' => 32, 'height' => 32 ];
473 - }
474 -
475 - $swatches = apply_filters( 'wpc_swatches_width_height', $swatches_measurements );
476 - $brands = apply_filters( 'wpc_brands_width_height', [ 'width' => 70, 'height' => 40 ] );
477 -
478 - $css = '';
479 - $css .= '@media screen and (min-width:'.($wpc_mobile_width+1).'px){.wpc_show_bottom_widget .wpc-filters-widget-content{height:auto!important}body.wpc_show_open_close_button .wpc-filters-widget-content.wpc-closed,body.wpc_show_open_close_button .wpc-filters-widget-content.wpc-opened,body.wpc_show_open_close_button .wpc-filters-widget-content:not(.wpc-opened){display:block!important}}@media screen and (min-width:'.$wpc_mobile_width.'px){.wpc-custom-selected-terms{clear:both;width:100%}.wpc-custom-selected-terms ul.wpc-filter-chips-list{display:flex;overflow-x:auto;padding-left:0}.wpc-filters-main-wrap .wpc-custom-selected-terms ul.wpc-filter-chips-list{display:block;overflow:visible}html.is-active .wpc-filters-overlay{top:0;opacity:.3;background:#fff}.wpc-filters-main-wrap input.wpc-label-input+label:hover{border:1px solid rgba(0,0,0,.25);border-radius:5px}.wpc-filters-main-wrap input.wpc-label-input+label:hover span.wpc-filter-label-wrapper{color:#333;background-color:rgba(0,0,0,.25)}.wpc-filters-main-wrap .wpc-filters-labels li.wpc-term-item input+label:hover a{color:#333}.theme-storefront #primary .storefront-sorting .wpc-custom-selected-terms{font-size:inherit}.theme-storefront #primary .wpc-custom-selected-terms{font-size:.875em}}@media screen and (max-width:'.$wpc_mobile_width.'px){.wpc-filters-labels li.wpc-term-item label:hover .wpc-term-swatch-wrapper:after,.wpc-filters-labels li.wpc-term-item label:hover .wpc-term-swatch-wrapper:before{display:none;}.wpc_show_bottom_widget .wpc-filters-widget-top-container,.wpc_show_open_close_button .wpc-filters-widget-top-container{text-align:center}.wpc_show_bottom_widget .wpc-filters-widget-top-container{position:sticky;top:0;z-index:99999;border-bottom:1px solid #f7f7f7}.wpc-custom-selected-terms:not(.wpc-show-on-mobile),.wpc-edit-filter-set,.wpc_show_bottom_widget .widget_wpc_selected_filters_widget,.wpc_show_bottom_widget .wpc-filters-widget-content .wpc-filter-set-widget-title,.wpc_show_bottom_widget .wpc-filters-main-wrap .widget-title,.wpc_show_bottom_widget .wpc-filters-widget-wrapper .wpc-filter-layout-submit-button,.wpc_show_bottom_widget .wpc-posts-found,body.wpc_show_bottom_widget .wpc-open-close-filters-button,body.wpc_show_open_close_button .wpc-filters-widget-content:not(.wpc-opened){display:none}.wpc_show_bottom_widget .wpc-filters-widget-top-container:not(.wpc-show-on-desktop),.wpc_show_bottom_widget .wpc-spinner.is-active,.wpc_show_bottom_widget .wpc-widget-close-container,html.is-active body:not(.wpc_show_bottom_widget) .wpc-spinner{display:block}body .wpc-filters-main-wrap li.wpc-term-item{padding:2px 0}.wpc-chip-empty{width:0;display:list-item;visibility:hidden;margin-right:0!important}.wpc-overlay-visible #secondary{z-index:auto}html.is-active:not(.wpc-overlay-visible) .wpc-filters-overlay{top:0;opacity:.2;background:#fff}.wpc-custom-selected-terms.wpc-show-on-mobile ul.wpc-filter-chips-list{display:flex;overflow-x:auto;padding-left:0}html.is-active body:not(.wpc_show_bottom_widget) .wpc-filters-overlay{top:0;opacity:.3;background:#fff}body.wpc_show_bottom_widget .wpc-filters-widget-content.wpc-closed,body.wpc_show_bottom_widget .wpc-filters-widget-content.wpc-opened,body.wpc_show_bottom_widget .wpc-filters-widget-content:not(.wpc-opened){display:block!important}.wpc-open-close-filters-button{display:block;margin-bottom:20px}.wpc-overlay-visible body,html.wpc-overlay-visible{overflow:hidden!important}.wpc_show_bottom_widget .widget_wpc_filters_widget,.wpc_show_bottom_widget .wpc-filters-main-wrap{padding:0!important;margin:0!important}.wpc_show_bottom_widget .wpc-filters-range-column{width:48%;max-width:none}.wpc_show_bottom_widget .wpc-filters-toolbar{display:flex;margin:1em 0}.wpc_show_bottom_widget .wpc-inner-widget-chips-wrapper{display:block;padding-left:20px;padding-right:20px}.wpc_show_bottom_widget .wpc-filters-main-wrap .widget-title.wpc-filter-title{display:flex}.wpc_show_bottom_widget .wpc-inner-widget-chips-wrapper .wpc-filter-chips-list,.wpc_show_open_close_button .wpc-inner-widget-chips-wrapper .wpc-filter-chips-list{display:flex;-webkit-box-pack:start;place-content:center flex-start;overflow-x:auto;padding-top:5px;padding-bottom:5px;margin-left:0;padding-left:0}.wpc-overlay-visible .wpc_show_bottom_widget .wpc-filters-overlay{top:0;opacity:.4}.wpc_show_bottom_widget .wpc-filters-main-wrap .wpc-spinner.is-active+.wpc-filters-widget-content .wpc-filters-scroll-container .wpc-filters-widget-wrapper{opacity:.6;pointer-events:none}.wpc_show_bottom_widget .wpc-filters-open-button-container{margin-top:1em;margin-bottom:1em}.wpc_show_bottom_widget .wpc-filters-widget-content{position:fixed;bottom:0;right:0;left:0;top:5%;z-index:999999;padding:0;background-color:#fff;margin:0;box-sizing:border-box;border-radius:7px 7px 0 0;transition:transform .25s;transform:translate3d(0,120%,0);-webkit-overflow-scrolling:touch;height:auto}.wpc_show_bottom_widget .wpc-filters-widget-containers-wrapper{padding:0;margin:0;overflow-y:scroll;box-sizing:border-box;position:fixed;top:56px;left:0;right:0;bottom:0}.wpc_show_bottom_widget .wpc-filters-widget-content.wpc-filters-widget-opened{transform:translate3d(0,0,0)}.theme-twentyfourteen .wpc_show_bottom_widget .wpc-filters-widget-content,.theme-twentyfourteen.wpc_show_bottom_widget .wpc-filters-scroll-container{background-color:#000}.wpc_show_bottom_widget .wpc-filters-section:not(.wpc-filter-post_meta_num):not(.wpc-filter-tax_numeric) .wpc-filter-content ul.wpc-filters-ul-list,.wpc_show_open_close_button .wpc-filters-section:not(.wpc-filter-post_meta_num):not(.wpc-filter-tax_numeric) .wpc-filter-content ul.wpc-filters-ul-list{max-height:none}.wpc_show_bottom_widget .wpc-filters-scroll-container{background:#fff;min-height:100%}.wpc_show_bottom_widget .wpc-filters-widget-wrapper{padding:20px 20px 15px}.wpc-filter-everything-dropdown .select2-search--dropdown .select2-search__field,.wpc-sorting-form select,.wpc_show_bottom_widget .wpc-filters-main-wrap input[type=number],.wpc_show_bottom_widget .wpc-filters-main-wrap input[type=text],.wpc_show_bottom_widget .wpc-filters-main-wrap select,.wpc_show_bottom_widget .wpc-filters-main-wrap textarea,.wpc_show_bottom_widget .wpc-search-field,.wpc_show_open_close_button .wpc-search-field,.wpc_show_open_close_button .wpc-filter-search-field{font-size:16px}.wpc-filter-layout-dropdown .select2-container .select2-selection--single,.wpc-sorting-form .select2-container .select2-selection--single{height:auto;padding:6px}.wpc_show_bottom_widget .wpc-filters-section:not(.wpc-filter-post_meta_num):not(.wpc-filter-tax_numeric) .wpc-filter-content ul.wpc-filters-ul-list{overflow-y:visible}.theme-twentyeleven #primary,.theme-twentyeleven #secondary{margin-left:0;margin-right:0;clear:both;float:none}#main>.fusion-row{max-width:100%}.wpc_show_bottom_widget .wpc-filters-open-button-container,.wpc_show_bottom_widget .wpc-filters-widget-controls-container,.wpc_show_bottom_widget .wpc-filters-widget-top-container,.wpc_show_open_close_button .wpc-filters-open-button-container{display:block}}'."\r\n";
480 - $css .= '.wpc-preload-img{display:none;}';
481 - // Number of items visible by default in More/Less filters
482 - $css .= '.wpc-filter-more-less:not(.wpc-search-active) .wpc-filters-ul-list > li:nth-child(-n+'.flrt_more_less_count().'){display: list-item;}'."\r\n";
483 -
484 - $css .= 'li.wpc-term-item label span.wpc-term-swatch,.wpc-term-swatch-wrapper{width:'.$swatches['width'].'px;min-width:'.$swatches['width'].'px;';
485 -
486 - if ( $rounded_swatch ) {
487 - $css .= 'border-radius:'.$swatches['height'].'px;';
488 - }
489 -
490 - $css .= 'height:'.$swatches['height'].'px;}'."\r\n";
491 - $css .= '.wpc-term-swatch-wrapper:after{width:'.($swatches['width']/2.5).'px;height:'.($swatches['width']/5).'px;left:'.($swatches['width']/3.5).'px;top:'.($swatches['height']/3.5).'px;}';
492 - $css .= '.wpc-term-image-wrapper{width:'.$brands['width'].'px;min-width:'.$brands['width'].'px;height:'.$brands['height'].'px;}';
493 -
494 - if( $maxHeight ){
495 - $css .= '.wpc-filters-section:not(.wpc-filter-more-less):not(.wpc-filter-post_meta_num):not(.wpc-filter-tax_numeric):not(.wpc-filter-layout-dropdown):not(.wpc-filter-terms-count-0) .wpc-filter-content:not(.wpc-filter-has-hierarchy) ul.wpc-filters-ul-list{
496 - max-height: '.$maxHeight.'px;
497 - overflow-y: auto;
498 - }'."\r\n";
499 - }
500 -
501 - if( $color ){
502 - $contrastColor = flrt_get_contrast_color($color);
503 - $css .= '.wpc-filters-range-inputs .ui-slider-horizontal .ui-slider-range{
504 - background-color: '.$color.';
505 - }
506 - '."\r\n";
507 -
508 - $css .= '.wpc-spinner:after {
509 - border-top-color: '.$color.';
510 - }'."\r\n";
511 -
512 - $css .= '.theme-Avada .wpc-filter-product_visibility .star-rating:before,
513 - .wpc-filter-product_visibility .star-rating span:before{
514 - color: '.$color.';
515 - }'."\r\n";
516 -
517 - $css .= 'body .wpc-filters-main-wrap input.wpc-label-input:checked+label span.wpc-filter-label-wrapper{
518 - background-color: '.$color.';
519 - }'."\r\n";
520 -
521 - $css .= 'body .wpc-filters-main-wrap input.wpc-label-input:checked+label{
522 - border-color: '.$color.';
523 - }'."\r\n";
524 -
525 - // Disabled label
526 - $css .= 'body .wpc-filters-main-wrap .wpc-term-disabled input.wpc-label-input:checked+label span.wpc-filter-label-wrapper{
527 - background-color: #d8d8d8;
528 - }'."\r\n";
529 -
530 - $css .= 'body .wpc-filters-main-wrap .wpc-term-disabled input.wpc-label-input:checked+label{
531 - border-color: #d8d8d8;
532 - }'."\r\n";
533 -
534 - $css .= 'body .wpc-filters-main-wrap .wpc-term-disabled input.wpc-label-input+label:hover{
535 - border-color: #d8d8d8;
536 - }'."\r\n";
537 -
538 - $css .= 'body .wpc-filters-main-wrap .wpc-term-disabled input.wpc-label-input:checked+label span.wpc-filter-label-wrapper,
539 - body .wpc-filters-main-wrap .wpc-filters-labels li.wpc-term-item.wpc-term-disabled input:checked+label a{
540 - color: #333333;
541 - }'."\r\n";
542 - // End of disabled label
543 -
544 - $css .= 'body .wpc-filters-main-wrap input.wpc-label-input:checked+label span.wpc-filter-label-wrapper,
545 - body .wpc-filters-main-wrap .wpc-filters-labels li.wpc-term-item input:checked+label a{
546 - color: '.$contrastColor.';
547 - }'."\r\n";
548 -
549 - $css .= 'body .wpc-filter-chips-list li.wpc-filter-chip:not(.wpc-chip-reset-all) a{
550 - border-color: '.$color.';
551 - }'."\r\n";
552 -
553 - $css .= 'body .wpc-filters-main-wrap .wpc-filters-widget-controls-container a.wpc-filters-apply-button,
554 - body .wpc-filters-main-wrap a.wpc-filters-submit-button{
555 - border-color: '.$color.';
556 - background-color: '.$color.';
557 - color: '.$contrastColor.';
558 - }'."\r\n";
559 -
560 - $css .= 'body .wpc-filter-chips-list li.wpc-filter-chip a:hover{
561 - opacity: 0.9;
562 - }'."\r\n";
563 -
564 - $css .= 'body .wpc-filter-chips-list li.wpc-filter-chip a:active{
565 - opacity: 0.75;
566 - }'."\r\n";
567 -
568 - $css .= '.star-rating span,
569 - .star-rating span:before{
570 - color: '.$color.';
571 - }'."\r\n";
572 -
573 - $css .= 'body a.wpc-filters-open-widget:active, a.wpc-filters-open-widget:active,
574 - .wpc-filters-open-widget:active{
575 - border-color: '.$color.';
576 - background-color: '.$color.';
577 - color: '.$contrastColor.';
578 - }'."\r\n";
579 -
580 - $css .= 'a.wpc-filters-open-widget:active span.wpc-icon-line-1:after,
581 - a.wpc-filters-open-widget:active span.wpc-icon-line-2:after,
582 - a.wpc-filters-open-widget:active span.wpc-icon-line-3:after{
583 - background-color: '.$color.';
584 - border-color: '.$contrastColor.';
585 - }'."\r\n";
586 -
587 - $css .= 'a.wpc-filters-open-widget:active .wpc-icon-html-wrapper span{
588 - background-color: '.$contrastColor.';
589 - }'."\r\n";
590 -
591 - //$css .= '@media screen and (min-width: '.$wpc_mobile_width.'px) {'."\r\n";
592 - $css .= 'body .wpc-filters-main-wrap input.wpc-label-input+label:hover span.wpc-filter-label-wrapper{
593 - color: '.$contrastColor.';
594 - background-color: '.$color.';
595 - }'."\r\n";
596 -
597 - $css .= 'body .wpc-filters-main-wrap .wpc-filters-labels li.wpc-term-item input+label:hover a{
598 - color: '.$contrastColor.';
599 - }'."\r\n";
600 - $css .= 'body .wpc-filters-main-wrap input.wpc-label-input+label:hover{
601 - border-color: '.$color.';
602 - }'."\r\n";
603 -
604 -
605 - $css .= '#ui-datepicker-div.wpc-filter-datepicker .ui-state-active,
606 - #ui-datepicker-div.ui-widget-content.wpc-filter-datepicker .ui-state-active,
607 - #ui-datepicker-div.wpc-filter-datepicker .ui-widget-header .ui-state-active{
608 - border-color: '.$color.';
609 - background: '.$color.';
610 - opacity: 0.95;
611 - }'."\r\n";
612 -
613 - $css .= '#ui-datepicker-div.wpc-filter-datepicker .ui-state-hover,
614 - #ui-datepicker-div.ui-widget-content.wpc-filter-datepicker .ui-state-hover,
615 - #ui-datepicker-div.wpc-filter-datepicker .ui-widget-header .ui-state-hover,
616 - #ui-datepicker-div.wpc-filter-datepicker .ui-state-focus,
617 - #ui-datepicker-div.ui-widget-content.wpc-filter-datepicker .ui-state-focus,
618 - #ui-datepicker-div.wpc-filter-datepicker .ui-widget-header .ui-state-focus{
619 - border-color: '.$color.';
620 - background: '.$color.';
621 - opacity: 0.6;
622 - }';
623 -
624 - $css .= '#ui-datepicker-div.wpc-filter-datepicker .ui-datepicker-close.ui-state-default{
625 - background: '.$color.';
626 - color: '.$contrastColor.';
627 - }'."\r\n";
628 -
629 - //$css .= '}'."\r\n";
630 - $css .= '.flrt-star-label svg{
631 - stroke: '.$color.';
632 - }'."\r\n";
633 -
634 - $css .= '.flrt-star-label-hover svg, .wpc-chip-stars svg{
635 - fill: '.$color.';
636 - }'."\r\n";
637 -
638 - $css .= '.wpc-filter-label-stars-wrapper{
639 - padding: 4px 5px !important;
640 - }'."\r\n";
641 -
642 - $css .= '.wpc-filter-label-stars-wrapper .flrt-star-label svg{
643 - height: 17px;
644 - width: 17px;
645 - }'."\r\n";
646 -
647 - $css .= 'body .wpc-filters-main-wrap input.wpc-label-input:checked+label span.wpc-filter-label-stars-wrapper .flrt-star-label svg,
648 - span.wpc-filter-label-stars-wrapper:hover .flrt-star-label svg{
649 - fill: ' . flrt_get_contrast_color($color) . ';
650 - }'."\r\n";
651 - }
652 - if( $styled_inputs ){
653 - $styled_color = $color ? $color : '#0570e2';
654 - $contrastColor = $contrastColor ? $contrastColor : flrt_get_contrast_color($styled_color);
655 - $hoverColor = flrt_add_color_opacity($color);
656 - $no_hex_color = substr( $color, 1, 6 );
657 - $css .= '.wpc-filters-main-wrap input[type=checkbox],
658 - .wpc-filters-main-wrap input[type=radio]{
659 - -webkit-appearance: none;
660 - -moz-appearance: none;
661 - position: relative;
662 - width: 20px;
663 - height: 20px;
664 - border: 1px solid #c9d1e0;
665 - background: #ffffff;
666 - border-radius: 5px;
667 - min-width: 20px;
668 - }
669 - i.wpc-toggle-children-list:after,
670 - i.wpc-toggle-children-list:before{
671 - background-color: #b8bcc8;
672 - }
673 - i.wpc-toggle-children-list:hover:after,
674 - i.wpc-toggle-children-list:hover:before{
675 - background-color: '.$color.';
676 - }
677 - .wpc-filters-widget-content input[type=email],
678 - .wpc-filters-widget-content input[type=number],
679 - .wpc-filters-widget-content input[type=password],
680 - .wpc-filters-widget-content input[type=search],
681 - .wpc-filters-widget-content input[type=tel],
682 - .wpc-filters-widget-content input[type=text],
683 - .wpc-filters-widget-content input[type=url]{
684 - height: 44px;
685 - }
686 - .wpc-filters-widget-content .wpc-filters-section input[type="number"],
687 - .wpc-filters-widget-content .wpc-filters-section input[type="text"]{
688 - border: 1px solid #ccd0dc;
689 - border-radius: 6px;
690 - background: transparent;
691 - box-shadow: none;
692 - padding: 8px 16px;
693 - }
694 - .wpc-filters-main-wrap input[type=checkbox]:after {
695 - content: "";
696 - opacity: 0;
697 - display: block;
698 - left: 3px;
699 - top: 3px;
700 - position: absolute;
701 - width: 12px;
702 - height: 12px;
703 - border: navajowhite;
704 - box-sizing: content-box;
705 - background-color: '.$styled_color.';
706 - border-radius: 2px;
707 - }
708 - .wpc-filters-main-wrap input[type=radio]:after {
709 - content: "";
710 - opacity: 0;
711 - display: block;
712 - left: 3px;
713 - top: 3px;
714 - position: absolute;
715 - width: 12px;
716 - height: 12px;
717 - border-radius: 50%;
718 - background: '.$styled_color.';
719 - box-sizing: content-box;
720 - }
721 - .wpc-filters-main-wrap input[type=radio]:checked,
722 - .wpc-filters-main-wrap input[type=checkbox]:checked {
723 - border-color: '.$styled_color.';
724 - }
725 - .wpc-filters-main-wrap .wpc-radio-item.wpc-term-disabled input[type=radio],
726 - .wpc-filters-main-wrap .wpc-checkbox-item.wpc-term-disabled > div > input[type=checkbox],
727 - .wpc-filters-main-wrap .wpc-checkbox-item.wpc-term-disabled > div > input[type=checkbox]:after,
728 - .wpc-filters-main-wrap .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=checkbox]:after,
729 - .wpc-filters-main-wrap .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=checkbox],
730 - .wpc-filters-main-wrap .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=radio],
731 - .wpc-term-swatch-no-image{
732 - border-color: #d8d8d8;
733 - }
734 - .wpc-filters-main-wrap .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=checkbox]:after,
735 - .wpc-filters-main-wrap .wpc-checkbox-item.wpc-term-disabled > div > input[type=checkbox]:after {
736 - background-color: #d8d8d8;
737 - }
738 - .wpc-filters-main-wrap .wpc-radio-item.wpc-term-disabled input[type=radio]:after,
739 - .wpc-filters-main-wrap .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=radio]:after{
740 - background-color: #d8d8d8;
741 - }
742 - .wpc-filters-main-wrap input[type=radio]:checked:after,
743 - .wpc-filters-main-wrap input[type=checkbox]:checked:after {
744 - opacity: 1;
745 - }
746 - .wpc-filters-main-wrap input[type=radio] {
747 - border-radius: 50%;
748 - }
749 - .wpc-filters-widget-content .wpc-filters-section .wpc-filter-search-wrapper .wpc-filter-search-field {
750 - padding: 8px 16px 8px 48px;
751 - }
752 - .wpc-filters-widget-content .wpc-filters-date-range-wrapper input[type="text"]{
753 - padding-right: 48px;
754 - background-image: url("data:image/svg+xml,%3Csvg width=\'24\' height=\'24\' viewBox=\'0 0 16 16\' fill=\'none\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Crect x=\'2\' y=\'4\' width=\'12\' height=\'10\' rx=\'1.33333\' stroke=\'%23b8bcc8\' stroke-width=\'1.33333\'/%3E%3Cpath d=\'M2.66699 7.3335H13.3337\' stroke=\'%23b8bcc8\' stroke-width=\'1.33333\' stroke-linecap=\'round\'/%3E%3Cpath d=\'M6 10.6667H10\' stroke=\'%23b8bcc8\' stroke-width=\'1.33333\' stroke-linecap=\'round\'/%3E%3Cpath d=\'M5.33301 2L5.33301 4.66667\' stroke=\'%23b8bcc8\' stroke-width=\'1.33333\' stroke-linecap=\'round\'/%3E%3Cpath d=\'M10.667 2L10.667 4.66667\' stroke=\'%23b8bcc8\' stroke-width=\'1.33333\' stroke-linecap=\'round\'/%3E%3C/svg%3E%0A");
755 - background-repeat: no-repeat;
756 - background-position: right 16px bottom 50%;
757 - background-size: 16px;
758 - }
759 - .wpc-filters-widget-content .wpc-filters-date-range-wrapper input[type="text"]:focus,
760 - .wpc-filters-widget-content .wpc-filters-date-range-wrapper input[type="text"]:hover{
761 - background-image: url("data:image/svg+xml,%3Csvg width=\'24\' height=\'24\' viewBox=\'0 0 16 16\' fill=\'none\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Crect x=\'2\' y=\'4\' width=\'12\' height=\'10\' rx=\'1.33333\' stroke=\'%23'.$no_hex_color.'\' stroke-width=\'1.33333\'/%3E%3Cpath d=\'M2.66699 7.3335H13.3337\' stroke=\'%23'.$no_hex_color.'\' stroke-width=\'1.33333\' stroke-linecap=\'round\'/%3E%3Cpath d=\'M6 10.6667H10\' stroke=\'%23b8bcc8\' stroke-width=\'1.33333\' stroke-linecap=\'round\'/%3E%3Cpath d=\'M5.33301 2L5.33301 4.66667\' stroke=\'%23'.$no_hex_color.'\' stroke-width=\'1.33333\' stroke-linecap=\'round\'/%3E%3Cpath d=\'M10.667 2L10.667 4.66667\' stroke=\'%23'.$no_hex_color.'\' stroke-width=\'1.33333\' stroke-linecap=\'round\'/%3E%3C/svg%3E%0A");
762 - }
763 - .wpc-filter-layout-dropdown .select2-container--default .select2-selection--single .select2-selection__arrow b,
764 - .wpc-sorting-form .select2-container--default .select2-selection--single .select2-selection__arrow b{
765 - border-left: 1px solid #b8bcc8;
766 - border-top: 1px solid #b8bcc8;
767 - }
768 - .wpc-filter-layout-dropdown .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b,
769 - .wpc-sorting-form .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{
770 - border-left: 1px solid #b8bcc8;
771 - border-top: 1px solid #b8bcc8;
772 - }
773 - .wpc-filter-collapsible .wpc-filter-title .wpc-open-icon,
774 - .wpc-filter-collapsible-reverse.wpc-filter-collapsible.wpc-closed .wpc-filter-title .wpc-open-icon,
775 - .wpc-filter-collapsible.wpc-closed .wpc-filter-title .wpc-open-icon,
776 - .wpc-filter-has-selected.wpc-closed .wpc-filter-title .wpc-open-icon{
777 - border-left: 1px solid #b8bcc8;
778 - border-top: 1px solid #b8bcc8;
779 - }
780 - .wpc-sorting-form .select2-container--default .select2-selection--single:hover,
781 - .wpc-filters-widget-content input[type=email]:hover,
782 - .wpc-filters-widget-content input[type=number]:hover,
783 - .wpc-filters-widget-content input[type=password]:hover,
784 - .wpc-filters-widget-content input[type=search]:hover,
785 - .wpc-filters-widget-content input[type=tel]:hover,
786 - .wpc-filters-widget-content input[type=text]:hover,
787 - .wpc-filters-widget-content input[type=url]:hover{
788 - border-color: '. $hoverColor.';
789 - }
790 - .wpc-filter-layout-dropdown .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b,
791 - .wpc-sorting-form .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b,
792 - .widget_wpc_sorting_widget .select2-container--default .select2-selection--single:hover .select2-selection__arrow b,
793 - .wpc-filter-layout-dropdown .select2-container--default .select2-selection--single:hover .select2-selection__arrow b,
794 - .wpc-filter-layout-dropdown .select2-container--default .select2-selection--single:hover,
795 - .select2-container--default.select2-container--open .wpc-filter-everything-dropdown.select2-dropdown,
796 - .wpc-sorting-form .select2-container--open .select2-selection--single:hover,
797 - .widget_wpc_sorting_widget .select2-container--open .select2-selection--single,
798 - .wpc-filter-layout-dropdown .select2-container--open .select2-selection--single,
799 - .wpc-filters-widget-content input[type=email]:focus,
800 - .wpc-filters-widget-content input[type=number]:focus,
801 - .wpc-filters-widget-content input[type=password]:focus,
802 - .wpc-filters-widget-content input[type=search]:focus,
803 - .wpc-filters-widget-content input[type=tel]:focus,
804 - .wpc-filters-widget-content input[type=text]:focus,
805 - .wpc-filters-widget-content input[type=url]:focus,
806 - .wpc-filters-widget-content input[type=email]:active,
807 - .wpc-filters-widget-content input[type=number]:active,
808 - .wpc-filters-widget-content input[type=password]:active,
809 - .wpc-filters-widget-content input[type=search]:active,
810 - .wpc-filters-widget-content input[type=tel]:active,
811 - .wpc-filters-widget-content input[type=text]:active,
812 - .wpc-filters-widget-content input[type=url]:active,
813 - .wpc-filter-collapsible .wpc-filter-title button:hover .wpc-open-icon,
814 - .wpc-filter-collapsible-reverse.wpc-filter-collapsible.wpc-closed .wpc-filter-title button:hover .wpc-open-icon,
815 - .wpc-filter-collapsible.wpc-closed .wpc-filter-title button:hover .wpc-open-icon,
816 - .wpc-filter-has-selected.wpc-closed .wpc-filter-title button:hover .wpc-open-icon{
817 - border-color: '.$color.';
818 - }
819 -
820 - .wpc-filters-main-wrap a.wpc-toggle-a:hover {
821 - color: '.$color.';
822 - }
823 - .wpc-sorting-form .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple,
824 - .wpc-filter-layout-dropdown .select2-container--default.select2-container--open.select2-container--above .select2-selection--single,
825 - .wpc-sorting-form .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple,
826 - .wpc-filter-layout-dropdown .select2-container--default.select2-container--open.select2-container--above .select2-selection--single{
827 - border-top: 1px solid transparent;
828 - }
829 - .wpc-search-field-wrapper .wpc-search-clear-icon-wrapper,
830 - .wpc-filter-search-wrapper button.wpc-search-clear{
831 - color: #b5bed2;
832 - }
833 - .wpc-filters-main-wrap .wpc-filters-labels li.wpc-term-item label {
834 - border-color: #ccd0dc;
835 - }
836 - .wpc-filters-main-wrap .wpc-filters-labels li.wpc-term-item label span.wpc-filter-label-wrapper{
837 - padding: 8px 7px;
838 - }
839 - .wpc-filters-labels li.wpc-term-has-image label:hover .wpc-term-image-wrapper,
840 - .wpc-filters-labels li.wpc-term-has-image input[type=checkbox]:checked + label .wpc-term-image-wrapper{
841 - border-color: '.$color.';
842 - }
843 - @media screen and (min-width: '.$wpc_mobile_width.'px) {
844 - .wpc-filters-main-wrap input[type=radio]:hover,
845 - .wpc-filters-main-wrap input[type=checkbox]:hover{
846 - border-color: '.$styled_color.';
847 - }
848 - .wpc-filter-label-wrapper .wpc-term-swatch-no-image:hover,
849 - .wpc-filters-main-wrap .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=radio]:hover,
850 - .wpc-filters-main-wrap .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=checkbox]:hover{
851 - border-color: #c3c3c3;
852 - }
853 - }';
854 - }
855 -
856 - if( $use_select2 ){
857 - $styled_color = $color ? $color : '#0570e2';
858 - $contrastColor = $contrastColor ? $contrastColor : flrt_get_contrast_color($styled_color);
859 -
860 - $css .= '.wpc-sorting-form select,
861 - .wpc-filter-content select{
862 - padding: 2px 8px 2px 10px;
863 - border-color: #c9d1e0;
864 - border-radius: 3px;
865 - color: inherit;
866 - -webkit-appearance: none;
867 - }
868 - .select2-container--default .wpc-filter-everything-dropdown .select2-results__option--highlighted::after,
869 - .select2-container--default .wpc-filter-everything-dropdown .select2-results__option[aria-selected="true"]::after,
870 - .select2-container--default .wpc-filter-everything-dropdown .select2-results__option[data-selected="true"]::after {
871 - border: 2px solid '.$color.';
872 - content: "";
873 - position: absolute !important;
874 - right: 13px !important;
875 - width: 18px;
876 - height: 10px;
877 - top: 30% !important;
878 - font-size: 16px !important;
879 - color: #000 !important;
880 - transform: rotate(137deg) !important;
881 - border-bottom: none;
882 - border-left: none;
883 - box-sizing: border-box;
884 - }
885 - .select2-container--default .wpc-filter-everything-dropdown .select2-results__option--highlighted::after {
886 - border-top: 2px solid #C7D1E2;
887 - border-right: 2px solid #C7D1E2;
888 - }
889 - .select2-container--default .wpc-filter-everything-dropdown .select2-results__option--highlighted[aria-selected],
890 - .select2-container--default .wpc-filter-everything-dropdown .select2-results__option--highlighted[data-selected]{
891 - background-color: rgba(0,0,0,0.05);
892 - color: inherit;
893 - }
894 - ';
895 - $css .= '@media screen and (max-width: '.$wpc_mobile_width.'px) {'."\r\n";
896 - $css .= '.wpc-sorting-form select,
897 - .wpc-filter-content select{
898 - padding: 6px 12px 6px 14px;
899 - }';
900 -
901 - $css .= '}'."\r\n";
902 - }
903 -
904 - if( $move_to_top ){
905 - $css .= '@media screen and (max-width: '.$wpc_mobile_width.'px) {'."\r\n";
906 -
907 - $css .= 'body #main,
908 - body #content .col-full,
909 - .woocommerce-page .content-has-sidebar,
910 - .woocommerce-page .has-one-sidebar,
911 - .woocommerce-page #main-sidebar-container,
912 - .woocommerce-page .theme-page-wrapper,
913 - .woocommerce-page #content-area,
914 - .theme-jevelin.woocommerce-page .woocomerce-styling,
915 - .woocommerce-page .content_wrapper,
916 - .woocommerce-page #col-mask,
917 - body #main-content .content-area {
918 - -js-display: flex;
919 - display: -webkit-box;
920 - display: -webkit-flex;
921 - display: -moz-box;
922 - display: -ms-flexbox;
923 - display: flex;
924 - -webkit-box-orient: vertical;
925 - -moz-box-orient: vertical;
926 - -webkit-flex-direction: column;
927 - -ms-flex-direction: column;
928 - flex-direction: column;
929 - }
930 - body #primary,
931 - .woocommerce-page .has-one-sidebar > section,
932 - .woocommerce-page .theme-content,
933 - .woocommerce-page #left-area,
934 - .woocommerce-page #content,
935 - .woocommerce-page .sections_group,
936 - .woocommerce-page .content-box,
937 - body #main-sidebar-container #main {
938 - -webkit-box-ordinal-group: 2;
939 - -moz-box-ordinal-group: 2;
940 - -ms-flex-order: 2;
941 - -webkit-order: 2;
942 - order: 2;
943 - }
944 - body #secondary,
945 - .woocommerce-page .has-one-sidebar > aside,
946 - body aside#mk-sidebar,
947 - .woocommerce-page #sidebar,
948 - .woocommerce-page .sidebar,
949 - body #main-sidebar-container #sidebar {
950 - -webkit-box-ordinal-group: 1;
951 - -moz-box-ordinal-group: 1;
952 - -ms-flex-order: 1;
953 - -webkit-order: 1;
954 - order: 1;
955 - }
956 -
957 - /*second method first method solve issue theme specific*/
958 - .woocommerce-page:not(.single,.page) .btWithSidebar.btSidebarLeft .btContentHolder,
959 - body .theme-generatepress.woocommerce #content {
960 - display: table;
961 - }
962 - body .btContent,
963 - body .theme-generatepress.woocommerce #primary {
964 - display: table-footer-group;
965 - }
966 - body .btSidebar,
967 - body .theme-generatepress.woocommerce #left-sidebar {
968 - display: table-header-group;
969 - }'."\r\n";
970 - $css .= '#ui-datepicker-div.wpc-filter-datepicker .ui-datepicker-close.ui-state-default{
971 - background: '.$color.';
972 - color: '.$contrastColor.';
973 - }'."\r\n";
974 - $css .= '.wpc-filters-date-range-column{
975 - justify-content: left;
976 - }'."\r\n";
977 - $css .= '}'."\r\n";
978 - }
979 -
980 - if( $use_loader ){
981 - $css .= '@media screen and (min-width: '.$wpc_mobile_width.'px) {'."\r\n";
982 - $css .= 'html.is-active .wpc-spinner{
983 - display: block;
984 - }';
985 - $css .= '}'."\r\n";
986 - }
987 -
988 - if( $dark_overlay ){
989 - $css .= '@media screen and (min-width: '.$wpc_mobile_width.'px) {'."\r\n";
990 - $css .= 'html.is-active .wpc-filters-overlay{
991 - opacity: .15;
992 - background: #000000;
993 - }';
994 - $css .= '}'."\r\n";
995 - }
996 -
997 - if( $custom_css ){
998 - $css .= $custom_css."\r\n";
999 - }
1000 -
1001 - $wp_upload_dir = wp_upload_dir();
1002 - $upload_dir_baseurl = $wp_upload_dir['baseurl'];
1003 - $upload_dir_basepath = $wp_upload_dir['basedir'];
1004 -
1005 - $cache_dir = $upload_dir_basepath . '/cache/filter-everything/';
1006 -
1007 - if ( ! file_exists( $cache_dir ) ) {
1008 - mkdir($cache_dir, 0777, true);
1009 - chmod($cache_dir, 0777);
1010 - }
1011 -
1012 - $filename = md5( $css ) . '.css';
1013 -
1014 - $fileurl = $upload_dir_baseurl .'/cache/filter-everything/' . $filename;
1015 - $filepath = $cache_dir . $filename;
1016 -
1017 - if ( $css !== '' ) {
1018 - if ( ! file_exists( $filepath ) ) {
1019 - file_put_contents( $filepath, $css );
1020 - }
1021 -
1022 - if( file_exists( $filepath ) ){
1023 - wp_enqueue_style('wpc-filter-everything-custom', $fileurl );
1024 - }
1025 - }
1026 -
485 + ( new DynamicCss() )->enqueueDynamicCss();
1027 486 }
1028 487
1029 488 public function bodyClass( $classes )
1030 489 {
@@ -1055,13 +514,80 @@
1055 514 // later updated, never on a brand-new installation.
1056 515 if ( ! get_option( 'flrt_version' ) ) {
1057 516 add_option( 'flrt_version', FLRT_PLUGIN_VER );
1058 517 }
518 + // When the plugin arrived — Messages keeps promos quiet for a day after it.
519 + if ( ! get_option( Messages::INSTALLED_AT_OPTION ) ) {
520 + add_option( Messages::INSTALLED_AT_OPTION, time(), '', false );
521 + }
522 +
523 + // PRO: put the managed block back into a physical robots.txt right away
524 + // (deactivate() removed it) instead of waiting for the next trigger.
525 + if ( function_exists( 'flrt_robots_file_sync' ) ) {
526 + flrt_robots_file_sync( 'activate' );
527 + }
1059 528 }
1060 529
1061 530 /**
531 + * Deactivation: PRO takes its managed block out of the physical robots.txt
532 + * and stops the daily sync cron. Options and posts are kept for the
533 + * next activation — that is uninstall()'s job.
534 + */
535 + public static function deactivate()
536 + {
537 + if ( function_exists( 'flrt_robots_file_teardown' ) ) {
538 + flrt_robots_file_teardown();
539 + }
540 +
541 + Messages::teardown();
542 + }
543 +
544 + /**
1062 545 * Clears all plugin data: options and posts
1063 546 */
547 + /**
548 + * Removes the static filter-data cache (uploads/flrt-cache) with its
549 + * folder. Defensive on purpose: the path must look exactly like our own
550 + * folder, only known file patterns are deleted, every step is
551 + * error-suppressed and any unexpected failure is swallowed — the cleanup
552 + * is best-effort and must never break the uninstall process.
553 + */
554 + private static function deleteJsonBlobCache()
555 + {
556 + try {
557 + $uploads = wp_upload_dir();
558 + if ( ! empty( $uploads['error'] ) || empty( $uploads['basedir'] ) ) {
559 + return;
560 + }
561 +
562 + $dir = trailingslashit( $uploads['basedir'] ) . 'flrt-cache';
563 +
564 + if ( substr( $dir, -strlen( '/flrt-cache' ) ) !== '/flrt-cache' || ! is_dir( $dir ) ) {
565 + return;
566 + }
567 +
568 + $blob_files = glob( $dir . '/filters-*.json' );
569 + $tmp_files = glob( $dir . '/filters-*.json.*.tmp' );
570 +
571 + $to_delete = array_merge(
572 + is_array( $blob_files ) ? $blob_files : [],
573 + is_array( $tmp_files ) ? $tmp_files : []
574 + );
575 + $to_delete[] = $dir . '/index.php';
576 +
577 + foreach ( $to_delete as $file ) {
578 + if ( is_file( $file ) ) {
579 + @unlink( $file );
580 + }
581 + }
582 +
583 + // Removed only when empty — anything unexpected inside stays put
584 + @rmdir( $dir );
585 + } catch ( \Throwable $e ) {
586 + // Best-effort cleanup: never break the uninstall
587 + }
588 + }
589 +
1064 590 public static function uninstall( $force = false )
1065 591 {
1066 592 $allow_to_delete = true;
1067 593
@@ -1097,9 +623,14 @@
1097 623 }
1098 624
1099 625 if( $allow_to_delete ){
1100 626
627 + if ( function_exists( 'flrt_robots_file_teardown' ) ) {
628 + flrt_robots_file_teardown();
629 + }
630 +
1101 631 $options = [
632 + 'wpc_robots_file_state',
1102 633 'wpc_filter_settings',
1103 634 'wpc_indexing_deep_settings',
1104 635 'wpc_filter_permalinks',
1105 636 'wpc_seo_rules_settings',
@@ -1104,8 +635,9 @@
1104 635 'wpc_filter_permalinks',
1105 636 'wpc_seo_rules_settings',
1106 637 'wpc_xml_write_date',
1107 638 'wpc_filter_experimental',
639 + 'flrt_json_blob_ver',
1108 640 'widget_wpc_filters_widget',
1109 641 'widget_wpc_sorting_widget',
1110 642 'widget_wpc_chips_widget',
1111 643 ];
@@ -1113,8 +645,16 @@
1113 645 foreach ( $options as $option_name ){
1114 646 delete_option( $option_name );
1115 647 }
1116 648
649 + self::deleteJsonBlobCache();
650 +
651 + // «Plugin notifications»: cron event, cached feed, consent, per-user dismissals
652 + Messages::uninstall();
653 + if ( class_exists( __NAMESPACE__ . '\\ReviewRequest' ) ) {
654 + ReviewRequest::uninstall();
655 + }
656 +
1117 657 // Deactivate and erase license if exists
1118 658 if ( defined( 'FLRT_FILTERS_PRO' ) && FLRT_FILTERS_PRO ) {
1119 659
1120 660 wpc_clear_folder(FLRT_XML_PATH);
@@ -1232,8 +772,9 @@
1232 772 $l10n = array(
1233 773 'prefixesOrderAvailableInPro' => esc_html__( 'Editing the order of URL prefixes is available in the PRO version', 'filter-everything' ),
1234 774 'chipsPlaceholder' => esc_html__( 'Select or enter hooks', 'filter-everything' ),
1235 775 'colorSwatchesPlaceholder' => esc_html__( 'Click to select taxonomies', 'filter-everything' ),
776 + 'chooseElementHelpText' => esc_html__( 'Hover over the Results container and click to select it', 'filter-everything' ),
1236 777 );
1237 778 wp_localize_script( 'wpc-filters-admin', 'wpcFiltersAdminCommon', $l10n );
1238 779
1239 780 wp_enqueue_script( 'select2', FLRT_PLUGIN_DIR_URL . "assets/js/select2/select2".$suffix.".js", array('jquery'), $select2ver );
@@ -1257,11 +798,14 @@
1257 798 /**
1258 799 * string
1259 800 */
1260 801 $getData = Container::instance()->getTheGet();
1261 - if( isset( $getData[FLRT_BEAVER_BUILDER_VAR] ) ){
802 + if( isset( $getData['fl_builder'] ) ){
1262 803 wp_enqueue_style('wpc-widgets', FLRT_PLUGIN_DIR_URL . 'assets/css/wpc-widgets' . $suffix . '.css', [], $ver );
1263 804 }
805 + if ( isset( $getData['flrt_get_html_selector'] ) && $getData['flrt_get_html_selector'] === '1' && current_user_can('manage_options') ) {
806 + wp_enqueue_style('wpc-element-picker', FLRT_PLUGIN_DIR_URL . 'assets/css/element-picker' . $suffix . '.css', [], $ver );
807 + }
1264 808
1265 809 // Do not include plugin CSS if there are no Filter Sets on the page
1266 810 $sets = $this->wpManager->getQueryVar('wpc_page_related_set_ids', []);
1267 811 if( empty( $sets ) ) {
@@ -1287,9 +831,15 @@
1287 831 $em = Container::instance()->getEntityManager();
1288 832
1289 833 $wpcFrontJsVariables = [];
1290 834
1291 - if( isset( $getData[FLRT_BEAVER_BUILDER_VAR] ) ){
835 + if ( isset( $getData['flrt_get_html_selector'] ) && $getData['flrt_get_html_selector'] === '1' && current_user_can('manage_options') ) {
836 + wp_enqueue_script( 'wpc-element-picker-bundle', FLRT_PLUGIN_DIR_URL . 'assets/js/js-element-picker/element-picker.bundle' . $suffix . '.js', array( 'jquery' ), $ver, true );
837 + wp_enqueue_script( 'wpc-element-picker', FLRT_PLUGIN_DIR_URL . 'assets/js/element-picker' . $suffix . '.js', array( 'jquery', 'wpc-element-picker-bundle' ), $ver, true );
838 + add_action( 'wp_footer', [$this, 'showElementPickerPanel'] );
839 + }
840 +
841 + if( isset( $getData['fl_builder'] ) ){
1292 842 wp_enqueue_script('wpc-widgets', FLRT_PLUGIN_DIR_URL . 'assets/js/wpc-widgets' . $suffix . '.js', array('jquery'), $ver );
1293 843 $l10n = array(
1294 844 'wpcItemNum' => esc_html__( 'Item #', 'filter-everything' )
1295 845 );
@@ -1391,10 +941,16 @@
1391 941 $per_page = apply_filters( 'wpc_filter_sets_posts_per_page', $per_page );
1392 942
1393 943 $wpcPostContainers = apply_filters( 'wpc_posts_containers', flrt_get_option( 'posts_container', flrt_default_posts_container() ) );
1394 944
945 + if ( is_array( $wpcPostContainers ) ) {
946 + $wpcPostContainers = array_map( 'wp_specialchars_decode', $wpcPostContainers );
947 + }
948 +
1395 949 wp_register_script( 'wc-jquery-ui-touchpunch', FLRT_PLUGIN_DIR_URL . 'assets/js/jquery-ui-touch-punch/jquery-ui-touch-punch'.$suffix.'.js', [], $ver, true );
1396 950
951 + $isPro = defined('FLRT_FILTERS_PRO') && FLRT_FILTERS_PRO;
952 +
1397 953 $wpcFrontJsVariables = array(
1398 954 'ajaxUrl' => admin_url('admin-ajax.php'),
1399 955 'wpcAjaxEnabled' => $ajaxEnabled,
1400 956 'wpcStatusCookieName' => FLRT_FOLDING_COOKIE_NAME,
@@ -1409,13 +965,19 @@
1409 965 'wpcAutoScrollOffset' => $autoScrollOffset,
1410 966 'wpcWaitCursor' => $waitCursor,
1411 967 'wpcPostsPerPage' => $per_page,
1412 968 'wpcUseSelect2' => $wpcUseSelect2,
1413 - 'wpcDateFilters' => false,
969 + 'wpcDateFilters' => false,
1414 970 'wpcPopupCompatMode' => $wpcPopupCompatMode,
1415 971 'wpcApplyButtonSets' => $applyButtonSets,
1416 972 'wpcQueryOnThePageSets' => $queryOnThePageSets,
1417 - 'wpcNoPostsContainerMsg' => esc_html__('It appears that this page does not contain a container with the specified «HTML id or class of the Posts Container». Try to specify the correct one in the Filter Set settings or the common plugin Settings.', 'filter-everything'),
973 + 'wpcNoPostsContainerMsg' => esc_html__('It appears that this page does not contain the container specified in the «Results container» option. Try to specify the correct one in the Filter Set settings or the common plugin Settings.', 'filter-everything'),
974 + 'wpcIsPro' => $isPro,
975 + 'permalinksEnabled' => FLRT_PERMALINKS_ENABLED,
976 + 'wpcMoreLessCount' => flrt_more_less_count(),
977 + 'wpcSearchChipsText' => esc_html__('search: %s', 'filter-everything' ),
978 + 'chipsTitle' => esc_html__('Remove %s from results', 'filter-everything'),
979 + 'chipsReset' => esc_html__('Reset all', 'filter-everything'),
1418 980 );
1419 981
1420 982 /**
1421 983 * We includes Flatpickr.js only on pages, where date filter is used.
@@ -1459,8 +1021,9 @@
1459 1021 'dayNames' => array_values( $wp_locale->weekday ),
1460 1022 'dayNamesMin' => array_values( $wp_locale->weekday_initial ),
1461 1023 'dayNamesShort' => array_values( $wp_locale->weekday_abbrev ),
1462 1024 'firstDay' => get_option( 'start_of_week' ),
1025 + 'applyText' => _x( 'Apply', 'Date Picker applyText', 'filter-everything' ),
1463 1026 );
1464 1027 }
1465 1028
1466 1029 /**
@@ -1478,11 +1041,295 @@
1478 1041 $wpcFrontJsVariables['wpcUseSelect2'] = $wpcUseSelect2;
1479 1042
1480 1043 wp_localize_script( 'wpc-filter-everything', 'wpcFilterFront', $wpcFrontJsVariables );
1481 1044
1045 + if(!empty($applyButtonSets) && flrt_instant_recount()){
1046 + $this->inlineScriptJsonData();
1047 + }
1048 +
1482 1049 unset( $filterSetService, $wpcFrontJsVariables );
1483 1050 }
1484 1051
1052 + private function inlineScriptJsonData()
1053 + {
1054 + global $wp, $wp_rewrite;
1055 + $flrt_json_data = Container::instance()->getFilterContext()->jsonData();
1056 + if(!empty($flrt_json_data)){
1057 + $wpc_filter_permalinks = get_option( 'wpc_filter_permalinks', [] );
1058 + $permalinksTab = new PermalinksTab();
1059 + $wpc_filter_permalinks_numbering = [];
1060 + $wpc_filter_permalinks_entities = [];
1061 + $permalinks_result = [];
1062 + $includeEntities = ['post_meta_num', 'tax_numeric', 'post_date', 'post_meta_date'];
1063 + $i = 1;
1064 + // Maps are keyed by the full "entity#e_name" pair: two filters may share
1065 + // an e_name with different entities (post_meta_num#_sale_price "Sale Price"
1066 + // vs post_meta_exists#_sale_price "On Sale") and keying by e_name alone
1067 + // made the last one win, sending On Sale into the no-slug GET branch (?yes=on)
1068 + foreach ($permalinksTab->movePostMetaNumInTheEnd($wpc_filter_permalinks) as $key => $value) {
1069 + $parts = explode('#', $key);
1070 + $permalinks_result[$key] = $value;
1071 + if(in_array($parts[0], $includeEntities)){
1072 + $wpc_filter_permalinks_entities[$key] = $parts[0];
1073 + }
1074 + $wpc_filter_permalinks_numbering[$i++] = $key;
1075 + }
1076 + $flrt_json_data['wpcFilterPermalinks'] = $permalinks_result;
1077 + $flrt_json_data['wpcFilterPermalinksNum'] = $wpc_filter_permalinks_numbering;
1078 + $flrt_json_data['wpcFilterEntitiesWithoutSlug'] = $wpc_filter_permalinks_entities;
1079 + // Pagination must never leak into the Apply URL base: applying a new
1080 + // selection restarts the results from page 1 (deeper pages may not exist)
1081 + $request = (string) $wp->request;
1082 + $pagination_base = ! empty( $wp_rewrite->pagination_base ) ? $wp_rewrite->pagination_base : 'page';
1083 + $request = preg_replace( '#(^|/)' . preg_quote( $pagination_base, '#' ) . '/\d+$#', '', $request );
1084 +
1085 + $flrt_json_data['domain'] = trailingslashit(home_url(add_query_arg([], $request)));
1086 + // The site's permalink convention (mirrors user_trailingslashit) —
1087 + // the client-side URL builder must terminate paths the same way
1088 + $flrt_json_data['trailingSlash'] = ( user_trailingslashit( 'wpc' ) === 'wpc/' );
1089 + // On shops that list variations as standalone catalog items (XStore's
1090 + // variable_products_detach) the client-side recount must keep displayed
1091 + // counts in variation space — mirrors the server-side
1092 + // wpc_from_variations_to_products counting gate.
1093 + $flrt_json_data['variationsAsProducts'] = function_exists( 'flrt_variations_listed_as_products' )
1094 + ? (bool) flrt_variations_listed_as_products() : false;
1095 +
1096 + /**
1097 + * The selection-independent bulk of the data (term post lists,
1098 + * meta values, the set universe — ~95% of the payload, tens of
1099 + * MB on large catalogs) is served as a hash-versioned STATIC
1100 + * FILE: cacheable across pages and visits, fetched off the
1101 + * critical path. Only the page-scoped part stays inline. When
1102 + * the file cannot be written, everything falls back inline —
1103 + * still as an inert JSON block, never as a JS object literal
1104 + * (the full JS parser needed ~28 s for 32 MB; JSON.parse of the
1105 + * same payload takes ~60 ms).
1106 + */
1107 + $blobUrl = $this->maybeWriteJsonBlobFile( $flrt_json_data );
1108 + $pagePart = [];
1109 +
1110 + if ( $blobUrl ) {
1111 + foreach ( [ 'wpcFilterPermalinks', 'wpcFilterPermalinksNum', 'wpcFilterEntitiesWithoutSlug', 'domain', 'trailingSlash', 'variationsAsProducts' ] as $rootKey ) {
1112 + if ( array_key_exists( $rootKey, $flrt_json_data ) ) {
1113 + $pagePart[ $rootKey ] = $flrt_json_data[ $rootKey ];
1114 + }
1115 + }
1116 + foreach ( $flrt_json_data as $key => $setData ) {
1117 + if ( ! is_numeric( $key ) ) {
1118 + continue;
1119 + }
1120 + $pagePart[ $key ] = [
1121 + 'filteredAllPostsIds' => isset( $setData['filteredAllPostsIds'] ) ? $setData['filteredAllPostsIds'] : [],
1122 + 'totalFilteredCount' => isset( $setData['totalFilteredCount'] ) ? $setData['totalFilteredCount'] : 0,
1123 + ];
1124 + }
1125 + $pagePart['blobUrl'] = $blobUrl;
1126 + $json = wp_json_encode( $pagePart );
1127 + } else {
1128 + $json = wp_json_encode( $flrt_json_data );
1129 + }
1130 +
1131 + add_action( 'wp_print_footer_scripts', function () use ( $json ) {
1132 + echo '<script type="application/json" id="wpc-filter-json-data">' . $json . '</script>' . "\n";
1133 + }, 1 );
1134 +
1135 + $bootstrap = 'try { var wpcFilterJsonDataEl = document.getElementById("wpc-filter-json-data");';
1136 + $bootstrap .= ' if ( wpcFilterJsonDataEl ) { var wpcFilterPagePart = JSON.parse( wpcFilterJsonDataEl.textContent );';
1137 + $bootstrap .= ' wpcFilterJsonDataEl.parentNode.removeChild( wpcFilterJsonDataEl );';
1138 + $bootstrap .= ' if ( wpcFilterPagePart.blobUrl ) {';
1139 + $bootstrap .= ' window.wpcFilterJsonBlobUrl = wpcFilterPagePart.blobUrl;';
1140 + $bootstrap .= ' window.wpcFilterJsonDataPromise = fetch( wpcFilterPagePart.blobUrl, { credentials: "same-origin" } )';
1141 + $bootstrap .= '.then( function (r) { if ( ! r.ok ) { throw new Error( "HTTP " + r.status ); } return r.json(); } )';
1142 + $bootstrap .= '.then( function (blob) { for ( var k in wpcFilterPagePart ) { if ( k === "blobUrl" ) { continue; }';
1143 + $bootstrap .= ' if ( blob[k] && typeof blob[k] === "object" && ! Array.isArray( blob[k] ) && wpcFilterPagePart[k] && typeof wpcFilterPagePart[k] === "object" && ! Array.isArray( wpcFilterPagePart[k] ) ) {';
1144 + $bootstrap .= ' for ( var kk in wpcFilterPagePart[k] ) { blob[k][kk] = wpcFilterPagePart[k][kk]; } } else { blob[k] = wpcFilterPagePart[k]; } }';
1145 + $bootstrap .= ' window.wpcFilterJsonData = blob; return blob; } )';
1146 + $bootstrap .= '.catch( function (e) { if ( window.console ) { console.error( "Filter Everything: filter data fetch failed", e ); } } );';
1147 + $bootstrap .= ' } else { window.wpcFilterJsonData = wpcFilterPagePart; } }';
1148 + $bootstrap .= ' } catch (e) { if ( window.console ) { console.error( "Filter Everything: filter data JSON parse failed", e ); } }';
1149 +
1150 + wp_add_inline_script( 'wpc-filter-everything', $bootstrap, 'before' );
1151 + }
1152 + }
1153 +
1154 + /**
1155 + * Writes the selection-independent part of $flrt_json_data to a static
1156 + * JSON file in uploads/flrt-cache/ and returns its URL (false = keep
1157 + * everything inline). The file name carries a hash of the inputs that
1158 + * define the content (plugin/cache versions, blob version bumped in
1159 + * resetTransitions(), set group, language, universe), so URLs are
1160 + * immutable: a stale file is impossible, only unused ones — those are
1161 + * garbage-collected in resetTransitions().
1162 + */
1163 + private function maybeWriteJsonBlobFile( $flrt_json_data )
1164 + {
1165 + if ( apply_filters( 'wpc_json_static_file', true ) === false ) {
1166 + return false;
1167 + }
1168 +
1169 + $setIds = array_filter( array_keys( $flrt_json_data ), 'is_numeric' );
1170 + if ( empty( $setIds ) ) {
1171 + return false;
1172 + }
1173 +
1174 + $firstSet = $flrt_json_data[ reset( $setIds ) ];
1175 + $group = isset( $firstSet['relatedSets'] ) && $firstSet['relatedSets'] !== '' ? $firstSet['relatedSets'] : implode( '_', $setIds );
1176 + $group = preg_replace( '/[^0-9_]/', '', (string) $group );
1177 + $universe = ( isset( $firstSet['allPostsIds'] ) && is_array( $firstSet['allPostsIds'] ) ) ? array_keys( $firstSet['allPostsIds'] ) : [];
1178 + $lang = defined( 'ICL_LANGUAGE_CODE' ) ? ICL_LANGUAGE_CODE : '';
1179 +
1180 + // The filter CONFIGS (view, More/Less, parent, labels, ...) shape the
1181 + // stored blob, but no hook is guaranteed to bump flrt_json_blob_ver for
1182 + // every way a config can change — a missed bump used to freeze a stale
1183 + // config into the immutable file forever (e.g. a filter switched to
1184 + // More/Less kept more_less='no' in the blob and the recount hid every
1185 + // term of it). Fingerprint the configs directly: a config change now
1186 + // mints a new file name by construction. 'values' is page-scoped and
1187 + // normalized out of the stored blob — it must not vary the name.
1188 + $configFingerprint = [];
1189 + foreach ( $setIds as $sid ) {
1190 + if ( empty( $flrt_json_data[ $sid ]['allEntities'] ) ) {
1191 + continue;
1192 + }
1193 + foreach ( $flrt_json_data[ $sid ]['allEntities'] as $eName => $entity ) {
1194 + $filterConf = is_object( $entity )
1195 + ? ( isset( $entity->filter ) ? (array) $entity->filter : [] )
1196 + : ( isset( $entity['filter'] ) ? (array) $entity['filter'] : [] );
1197 + unset( $filterConf['values'] );
1198 + $configFingerprint[ $sid ][ $eName ] = $filterConf;
1199 + }
1200 + }
1201 +
1202 + $hash = md5( implode( '|', [
1203 + FLRT_PLUGIN_VER,
1204 + FLRT_CACHE_FORMAT_SUFFIX,
1205 + (string) get_option( 'flrt_json_blob_ver', 1 ),
1206 + // The same DB serves different filter data in free and PRO (PRO-only
1207 + // fields are gated at read time) — the modes must not share a file
1208 + defined( 'FLRT_FILTERS_PRO' ) ? 'pro' : 'free',
1209 + $group,
1210 + $lang,
1211 + count( $universe ),
1212 + md5( implode( ',', $universe ) ),
1213 + md5( (string) wp_json_encode( $configFingerprint ) ),
1214 + ] ) );
1215 +
1216 + $uploads = wp_upload_dir();
1217 + if ( ! empty( $uploads['error'] ) ) {
1218 + return false;
1219 + }
1220 +
1221 + $fileName = 'filters-' . $group . '-' . $hash . '.json';
1222 + $dir = trailingslashit( $uploads['basedir'] ) . 'flrt-cache';
1223 + $file = $dir . '/' . $fileName;
1224 + $url = trailingslashit( $uploads['baseurl'] ) . 'flrt-cache/' . $fileName;
1225 +
1226 + if ( file_exists( $file ) ) {
1227 + return $url;
1228 + }
1229 +
1230 + if ( ! wp_mkdir_p( $dir ) ) {
1231 + return false;
1232 + }
1233 +
1234 + if ( ! file_exists( $dir . '/index.php' ) ) {
1235 + @file_put_contents( $dir . '/index.php', "<?php // Silence is golden.\n" );
1236 + }
1237 +
1238 + $encoded = wp_json_encode( $this->normalizeJsonBlob( $flrt_json_data ) );
1239 + if ( ! $encoded ) {
1240 + return false;
1241 + }
1242 +
1243 + // Write to a temp name first: a concurrent request must never fetch
1244 + // a half-written multi-MB file
1245 + $tmp = $file . '.' . wp_generate_password( 8, false ) . '.tmp';
1246 + if ( @file_put_contents( $tmp, $encoded ) === false ) {
1247 + return false;
1248 + }
1249 + if ( ! @rename( $tmp, $file ) ) {
1250 + @unlink( $tmp );
1251 + return false;
1252 + }
1253 +
1254 + return $url;
1255 + }
1256 +
1257 + /**
1258 + * Strips/zeroes the page-scoped leaves so that every page of the set
1259 + * group produces an identical blob: filteredAllPostsIds and the current
1260 + * result counts live inline; cross_count, current range bounds and
1261 + * per-bucket counts are recomputed client-side on the first recount and
1262 + * are never read from the JSON before that.
1263 + */
1264 + private function normalizeJsonBlob( $flrt_json_data )
1265 + {
1266 + // Entities are objects — one encode/decode round-trip detaches a
1267 + // plain array tree we can normalize without touching the originals
1268 + $data = json_decode( wp_json_encode( $flrt_json_data ), true );
1269 +
1270 + $rangeLeaves = [ 'min', 'max', 'from', 'to', 'time_from', 'time_to' ];
1271 +
1272 + foreach ( $data as $key => &$setData ) {
1273 + if ( ! is_numeric( $key ) ) {
1274 + continue;
1275 + }
1276 +
1277 + unset( $setData['filteredAllPostsIds'], $setData['totalFilteredCount'] );
1278 +
1279 + if ( empty( $setData['allEntities'] ) || ! is_array( $setData['allEntities'] ) ) {
1280 + continue;
1281 + }
1282 +
1283 + foreach ( $setData['allEntities'] as &$entity ) {
1284 + unset( $entity['filter']['values'] );
1285 +
1286 + if ( array_key_exists( 'new_date_query', $entity ) ) {
1287 + $entity['new_date_query'] = [];
1288 + }
1289 +
1290 + foreach ( $rangeLeaves as $leaf ) {
1291 + if ( array_key_exists( $leaf, $entity ) ) {
1292 + $entity[ $leaf ] = false;
1293 + }
1294 + }
1295 +
1296 + if ( empty( $entity['items'] ) || ! is_array( $entity['items'] ) ) {
1297 + continue;
1298 + }
1299 +
1300 + foreach ( $entity['items'] as &$item ) {
1301 + if ( array_key_exists( 'cross_count', $item ) ) {
1302 + $item['cross_count'] = 0;
1303 + }
1304 + if ( array_key_exists( 'wp_queried', $item ) ) {
1305 + $item['wp_queried'] = false;
1306 + }
1307 + foreach ( $rangeLeaves as $leaf ) {
1308 + if ( array_key_exists( $leaf, $item ) ) {
1309 + $item[ $leaf ] = false;
1310 + }
1311 + }
1312 + if ( ! empty( $item['range_list_input'] ) && is_array( $item['range_list_input'] ) ) {
1313 + foreach ( $item['range_list_input'] as $bucket => $count ) {
1314 + if ( is_numeric( $count ) ) {
1315 + $item['range_list_input'][ $bucket ] = 0;
1316 + }
1317 + }
1318 + }
1319 + }
1320 + unset( $item );
1321 + }
1322 + unset( $entity );
1323 + }
1324 + unset( $setData );
1325 +
1326 + // These stay inline (page-scoped or tiny site config)
1327 + unset( $data['domain'], $data['trailingSlash'], $data['variationsAsProducts'], $data['wpcFilterPermalinks'], $data['wpcFilterPermalinksNum'], $data['wpcFilterEntitiesWithoutSlug'] );
1328 +
1329 + return $data;
1330 + }
1331 +
1485 1332 public function removeApplyButtonOrderField( &$set_settings_fields )
1486 1333 {
1487 1334 unset( $set_settings_fields['apply_button_menu_order'], $set_settings_fields['search_field_menu_order'] );
1488 1335 }
@@ -1661,6 +1508,50 @@
1661 1508 $where = implode( ' ', $sql ) . $where;
1662 1509 }
1663 1510
1664 1511 return $where;
1512 + }
1513 +
1514 + public function showElementPickerPanel()
1515 + {
1516 + $getData = Container::instance()->getTheGet();
1517 +
1518 + $html = sprintf(
1519 + '<div id="wpc-choose-selector" data-set-id="%1$s">
1520 + <div id="wpc-choose-id-css" class="wpc-choose-block">
1521 + <span>%2$s</span>
1522 + <input id="wpc-chooses-html-id">
1523 + </div>
1524 + <div id="wpc-choose-class-css" class="wpc-choose-block">
1525 + <span>%3$s</span>
1526 + <input id="wpc-chooses-html-css">
1527 + </div>
1528 + <div id="wpc-choose-empty" class="wpc-choose-block">
1529 +
1530 + <span id="wpc-chooses-empty-element">%4$s</span>
1531 + </div>
1532 + <div id="wpc-choose-selector-buttons">
1533 + <button id="wpc-choose-another-selector">%5$s</button>
1534 + <button id="wpc-choose-save-btn">%6$s</button>
1535 + </div>
1536 + </div>',
1537 + !(empty($getData['flrt_set_id'])) ? esc_attr($getData['flrt_set_id']) : 'global_posts_container',
1538 + esc_html__( 'Chosen ID: ', 'filter-everything' ),
1539 + esc_html__( 'Chosen Class: ', 'filter-everything' ),
1540 + esc_html__( 'The element does not have any CSS selectors', 'filter-everything' ),
1541 + esc_html__( 'Choose another', 'filter-everything' ),
1542 + esc_html__( 'Save', 'filter-everything' )
1543 + );
1544 +
1545 + // Localize script with nonce and AJAX URL
1546 + wp_localize_script('wpc-element-picker', 'wpcElementPickerData', array(
1547 + 'ajax_url' => admin_url('admin-ajax.php'),
1548 + 'error_saving_selector' => esc_html__('Error saving selector', 'filter-everything'),
1549 + 'no_element_selected' => esc_html__('Error: No element selected', 'filter-everything'),
1550 + 'failed_to_save_selector' => esc_html__('Error: Failed to save selector. Please try again.', 'filter-everything'),
1551 + 'saving' => esc_html__('Saving...', 'filter-everything'),
1552 + 'save' => esc_html__('Save', 'filter-everything'),
1553 + ));
1554 +
1555 + echo $html;
1665 1556 }
1666 1557 }