PluginProbe
Filter Everything — WordPress & WooCommerce Filters / trunk
Filter Everything — WordPress & WooCommerce Filters vtrunk
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 1.6.3 All 51 releases
filter-everything / src / Plugin.php

Plugin.php in Filter Everything — WordPress & WooCommerce Filters trunk, at src/Plugin.php

2,119 lines 110.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FilterEverything\Filter;
4
5 if ( ! defined('ABSPATH') ) {
6 exit;
7 }
8
9 use FilterEverything\Filter\Pro\Api\ApiRequests;
10 use FilterEverything\Filter\Shortcodes;
11 use FilterEverything\Filter\Pro\PluginPro;
12 use FilterEverything\Filter\DefaultSettings;
13 use FilterEverything\Filter\PluginHelpers;
14
15 class Plugin
16 {
17 use PluginHelpers;
18 private $wpManager;
19
20 public function __construct()
21 {
22 $this->wpManager = Container::instance()->getWpManager();
23 $this->wpManager->init();
24 $this->register_hooks();
25
26 new Shortcodes();
27
28 if( defined('FLRT_FILTERS_PRO') && FLRT_FILTERS_PRO ){
29 new PluginPro();
30 }
31
32 if( is_admin() ){
33 new Admin();
34 }
35 }
36
37 public function register_hooks(){
38 /**
39 * string
40 */
41 $getData = Container::instance()->getTheGet();
42
43 if( ! is_admin() ){
44 global $wp_version;
45
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 add_action( 'parse_request', array( $this->wpManager, 'parseRequest' ) );
54 add_action( 'pre_get_posts', array( $this->wpManager, 'addFilterQueryToWpQuery' ), 9999 );
55
56 add_filter( 'posts_where', [ $this->wpManager, 'fixPostsWhereForSearch' ], 10, 2 );
57 add_filter( 'post_limits_request', [ $this->wpManager, 'addSQlComment' ], 10, 2 );
58 add_action( 'pre_get_posts', array( $this->wpManager, 'fixSearchPostType' ), 9999 );
59
60 add_action( 'template_redirect', [ $this, 'prepareEntities' ] );
61
62 add_action( 'wpc_filtered_query_end', [ $this, 'addSearchArgsToWpQuery' ] );
63 add_action( 'wpc_all_set_wp_queried_posts', [ $this, 'addSearchArgsToWpQuery' ] );
64
65 add_filter( 'posts_where', [ $this, 'postDateWhere' ], 10000, 2 );
66
67 if ( flrt_is_woocommerce() ){
68 add_action( 'woocommerce_product_query', 'flrt_remove_product_query_post_clauses', 10, 2 );
69 add_filter( 'posts_search', [$this, 'addSkuSearchSql'], 10000, 2 );
70 }
71
72 $sorting = new Sorting();
73 $sorting->registerHooks();
74 }
75
76 add_action( 'body_class', array( $this, 'bodyClass' ) );
77
78 add_action( 'admin_print_styles', array( $this, 'includeAdminCss' ) );
79 add_action( 'admin_print_scripts', array( $this, 'includeAdminJs' ) );
80
81 // Do not include JS, if this page is admin or can't contain filters
82 if( ! is_admin() && ! is_login() ){
83 add_action( 'wp_head', [ $this, 'inlineFrontCss' ] );
84 add_action( 'wp_print_styles', array( $this, 'includeFrontCss' ) );
85 add_action( 'wp_print_scripts', array( $this, 'includeFrontJs' ) );
86 add_action( 'wp_print_styles', array( $this, 'dynamicFrontCss' ) );
87 }
88
89 add_action( 'wp_footer', [$this, 'footerHtml'] );
90
91 if( ! defined('FLRT_FILTERS_PRO') ) {
92 add_action( 'wp_head', array($this, 'noIndexFilterPages'), 1 );
93 add_filter( 'wpc_filter_set_default_fields', [ $this, 'addAvailableInProFields' ], 10, 2 );
94 add_filter( 'wpc_pre_save_set_fields', [ $this, 'unsetAvailableInProFields' ] );
95 }
96
97 add_filter( 'wpc_filter_set_default_fields', [ $this, 'addSetTailFields' ], 20, 2 );
98
99 // Disable single search result redirect
100 add_filter( 'woocommerce_redirect_single_search_result', '__return_false' );
101
102 add_action( 'save_post', [$this, 'resetTransitions'] );
103 add_action( 'delete_post', [$this, 'resetTransitions'] );
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'] );
110
111 if( isset( $getData['reset_filters_cache'] ) && $getData['reset_filters_cache'] == true ){
112 $this->resetTransitions();
113 }
114
115 add_action( 'wpc_before_filter_set_settings_fields', [$this, 'removeApplyButtonOrderField'] );
116 add_filter( 'wpc_filter_set_prepared_values', [$this, 'handleFilterSetFieldsVisibility'] );
117
118 add_action( 'wpc_cycle_filter_fields', [$this, 'showCombinedFields'], 10, 2 );
119
120 add_action( 'pre_get_posts', [$this, 'burpOutAllWpQueries'], 9999 );
121
122 add_action('wp_ajax_wpc-get-set-location-terms', [$this, 'sendSetLocationTerms']);
123
124 add_filter('wpc_relevant_set_ids', [$this, 'findRelevantSets'], 10, 2);
125 add_filter('wpc_is_filtered_query_free', [$this, 'isFilteredQuery'], 10, 2);
126
127
128 add_filter('wpc_prepare_filter_set_parameters', [$this, 'prepareSetParameters'], 10, 2);
129
130 add_filter('wpc_filter_before_make_default_set_values', [$this, 'legacyPrepareWpPageTypeValue'] );
131
132 add_filter('wpc_validation_wp_page_type_entities', [$this, 'validationWpPageTypeEntities'] );
133
134 add_filter('wpc_validation_location_entities', [$this, 'validationLocationEntities'], 10, 2);
135
136 add_action( 'wpc_before_filter_set_settings_location_fields', [$this, 'showLocationFields'] );
137
138 add_filter('manage_edit-' . FLRT_FILTERS_SET_POST_TYPE . '_columns', array($this, 'filterSetPostTypeCol'));
139 add_action('manage_' . FLRT_FILTERS_SET_POST_TYPE . '_posts_custom_column', array($this, 'filterSetPostTypeColContent'), 10, 2);
140
141 $woo_shortcodes = array(
142 'products',
143 'featured_products',
144 'sale_products',
145 'best_selling_products',
146 'recent_products',
147 'product_attribute',
148 'top_rated_products'
149 );
150
151 // Set first install timestamp
152 $first_install = get_option( 'wpc_first_install' );
153 if ( ! $first_install ){
154 $first_install = [];
155 $first_install['install_time'] = time();
156 $first_install['rate_disabled'] = false;
157 $first_install['rate_delayed'] = false;
158 update_option( 'wpc_first_install', $first_install );
159 }
160
161 // Fix caching problem for products queried by shortcode
162 foreach ( $woo_shortcodes as $woo_shortcode ){
163 add_filter( "shortcode_atts_{$woo_shortcode}", [$this, 'disableCacheProductsShortcode'] );
164 }
165 }
166
167 public function resetTransitions()
168 {
169 $em = Container::instance()->getEntityManager();
170 $all_filters = $em->getGlobalConfiguredSlugs();
171
172 if( is_array( $all_filters ) ){
173 foreach ( $all_filters as $entityEname => $slug ){
174 // For terms it should be entity name
175 $parts = explode( '#', $entityEname, 2 );
176 $e_name = isset( $parts[1] ) ? $parts[1] : '';
177 $type = isset( $parts[0] ) ? $parts[0] : '';
178
179 if ( in_array( $type, [ 'post_meta_num', 'tax_numeric' ] ) ) {
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 );
184 }
185
186 if ( in_array( $type, [ 'post_date' ] ) ) {
187 $this->deleteTermsTransientsLike( 'wpc_terms_post_date_' );
188 }
189
190 if ( in_array( $type, [ 'post_meta_date' ] ) ) {
191 $this->deleteTermsTransientsLike( 'wpc_terms_post_meta_date_' );
192 }
193
194 if ( $type === 'post_meta_exists' ) {
195 delete_transient( flrt_get_post_ids_transient_key( $e_name .'_yes' ) );
196 delete_transient( flrt_get_post_ids_transient_key( $e_name .'_no' ) );
197 }
198
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 );
203 $post_ids_transient_key = flrt_get_post_ids_transient_key( $slug );
204 $var_meta_transient_key = flrt_get_variations_transient_key( 'attribute_'. $e_name );
205
206 delete_transient( $terms_transient_key );
207 delete_transient( $post_ids_transient_key );
208 delete_transient( $var_meta_transient_key );
209 }
210 }
211
212 $variations_key = 'wpc_posts_variations' . FLRT_CACHE_FORMAT_SUFFIX;
213 $filter_key = 'wpc_filters_query';
214
215 delete_transient($variations_key);
216 delete_transient($filter_key);
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
231 unset( $terms_transient_key, $post_ids_transient_key, $var_meta_transient_key, $all_filters, $em );
232 }
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
260 public function prepareEntities()
261 {
262 $wpManager = Container::instance()->getWpManager();
263 $em = Container::instance()->getEntityManager();
264 $sets = $wpManager->getQueryVar('wpc_page_related_set_ids');
265
266 if( $sets ){
267 foreach( $sets as $set ){
268 $em->prepareEntitiesToDisplay( array( $set ) );
269 }
270 }
271 }
272
273
274 public function addAvailableInProFields( $fields, $filterSet )
275 {
276 foreach ( $fields as $key => $attributes ){
277 // Always insert regular 'old' field
278 $new_fields[$key] = $attributes;
279
280 if( $key === 'show_count' ){
281
282 $new_fields['instead_custom_posts_container'] = array(
283 'type' => 'inProButton',
284 'label' => esc_html__('Results container', 'filter-everything'),
285 'pro_label' => flrt_pro_promo_label(),
286 'name' => $filterSet->generateFieldName('instead_custom_posts_container'),
287 'id' => $filterSet->generateFieldId('instead_custom_posts_container'),
288 'default' => '',
289 'settings' => true
290 );
291
292 }
293
294 if( $key === 'hide_empty' ){
295 $new_fields['instead_hide_empty_filter_container'] = array(
296 'type' => 'inProButton',
297 'label' => esc_html__('Hide empty Filters', 'filter-everything'),
298 'pro_label' => flrt_pro_promo_label(),
299 'name' => $filterSet->generateFieldName('instead_custom_posts_container'),
300 'id' => $filterSet->generateFieldId('instead_custom_posts_container'),
301 'default' => '',
302 'instructions' => esc_html__('Hide the Entire Filter if no one term contains posts', 'filter-everything'),
303 'settings' => true
304 );
305
306 }
307
308 }
309
310 return $new_fields;
311 }
312
313 public function addSetTailFields( $fields, $filterSet )
314 {
315 $new_fields = [];
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';
320
321 foreach ( $fields as $key => $attributes ){
322 // Always insert regular 'old' field
323 $new_fields[$key] = $attributes;
324
325 if( $key === $insert_after ){
326
327 $new_fields['use_search_field'] = array(
328 'type' => 'Checkbox',
329 'label' => esc_html__('Enable Search Field', 'filter-everything'),
330 'name' => $filterSet->generateFieldName('use_search_field'),
331 'id' => $filterSet->generateFieldId('use_search_field'),
332 'class' => 'wpc-field-use-search-field',
333 'default' => 'no',
334 'instructions' => esc_html__('Allows you to search by text among filtered posts', 'filter-everything'),
335 'settings' => true
336 );
337
338 $new_fields['search_field_menu_order'] = array(
339 'type' => 'Hidden',
340 'label' => '',
341 'class' => 'wpc-menu-order-field',
342 'id' => $filterSet->generateFieldId('search_field_menu_order'),
343 'name' => $filterSet->generateFieldName('search_field_menu_order'),
344 'default' => -1,
345 'settings' => true
346 );
347
348 $new_fields['search_field_label'] = array(
349 'type' => 'Text',
350 'label' => esc_html__('Search Field Title', 'filter-everything'),
351 'name' => $filterSet->generateFieldName('search_field_label'),
352 'id' => $filterSet->generateFieldId('search_field_label'),
353 'class' => 'wpc-search-field-label',
354 'default' => esc_html__('Search', 'filter-everything'),
355 'instructions' => esc_html__('Specify if needed or leave empty', 'filter-everything'),
356 'settings' => true
357 );
358
359 $new_fields['search_field_placeholder'] = array(
360 'type' => 'Text',
361 'label' => esc_html__('Search Field Placeholder', 'filter-everything'),
362 'name' => $filterSet->generateFieldName('search_field_placeholder'),
363 'id' => $filterSet->generateFieldId('search_field_placeholder'),
364 'class' => 'wpc-search-field-placeholder',
365 'placeholder' => esc_html__( 'e.g. Search products, Search posts', 'filter-everything' ),
366 'default' => '',
367 'settings' => true
368 );
369
370 $new_fields['use_apply_button'] = array(
371 'type' => 'Checkbox',
372 'label' => esc_html__('«Apply Button» mode', 'filter-everything'),
373 'name' => $filterSet->generateFieldName('use_apply_button'),
374 'id' => $filterSet->generateFieldId('use_apply_button'),
375 'class' => 'wpc-field-use-apply-button',
376 'default' => 'no',
377 'instructions' => esc_html__('Enables filtering by clicking the Apply button', 'filter-everything'),
378 'settings' => true
379 );
380
381 $new_fields['apply_button_menu_order'] = array(
382 'type' => 'Hidden',
383 'label' => '',
384 'class' => 'wpc-menu-order-field',
385 'id' => $filterSet->generateFieldId('apply_button_menu_order'),
386 'name' => $filterSet->generateFieldName('apply_button_menu_order'),
387 'default' => -1,
388 'settings' => true
389 );
390
391 $new_fields['apply_button_text'] = array(
392 'type' => 'Text',
393 'label' => esc_html__('Apply Button label', 'filter-everything'),
394 'name' => $filterSet->generateFieldName('apply_button_text'),
395 'id' => $filterSet->generateFieldId('apply_button_text'),
396 'class' => 'wpc-field-apply-button-text',
397 'default' => esc_html__('Apply', 'filter-everything'),
398 'settings' => true
399 );
400
401 $new_fields['reset_button_text'] = array(
402 'type' => 'Text',
403 'label' => esc_html__('Reset Button label', 'filter-everything'),
404 'name' => $filterSet->generateFieldName('reset_button_text'),
405 'id' => $filterSet->generateFieldId('reset_button_text'),
406 'class' => 'wpc-field-reset-button-text',
407 'default' => esc_html__('Reset', 'filter-everything'),
408 'settings' => true
409 );
410
411 $new_fields['horizontal_view'] = array(
412 'type' => 'Checkbox',
413 'label' => esc_html__('Horizontal filters', 'filter-everything'),
414 'name' => $filterSet->generateFieldName('horizontal_view'),
415 'id' => $filterSet->generateFieldId('horizontal_view'),
416 'class' => 'wpc-field-horizontal-view-text',
417 'default' => 'no',
418 'instructions' => esc_html__('Display filters side by side instead of one per row', 'filter-everything'),
419 'settings' => true
420 );
421 $columns_options = [];
422 for ( $i = 2; $i <= 5; $i++ ){
423 $columns_options[(string)$i] = (string)$i;
424 }
425 $new_fields['horizontal_view_column'] = array(
426 'type' => 'Select',
427 'label' => esc_html__('Number of columns', 'filter-everything'),
428 'class' => 'wpc-field-horizontal-view-column',
429 'id' => $filterSet->generateFieldId('horizontal_view_column'),
430 'name' => $filterSet->generateFieldName('horizontal_view_column'),
431 'options' => $columns_options,
432 'default' => '3',
433 'instructions' => esc_html__('How many columns to use in horizontal mode', 'filter-everything'),
434 'settings' => true
435 );
436
437 $screen = function_exists('get_current_screen') ? get_current_screen() : null;
438 $new_fields['horizontal_view_priority'] = array(
439 'type' => 'Select',
440 'label' => esc_html__('Horizontal priority', 'filter-everything'),
441 'class' => 'wpc-field-horizontal-view-priority',
442 'id' => $filterSet->generateFieldId('horizontal_view_priority'),
443 'name' => $filterSet->generateFieldName('horizontal_view_priority'),
444 'options' => ['widget' => 'widget', 'filter_set' => 'filter_set'],
445 'default' => ($screen && $screen->base === 'post' && $screen->action === 'add') ? 'filter_set' : 'widget',
446 'instructions' => esc_html__('How many columns to use in horizontal mode', 'filter-everything'),
447 'settings' => true,
448 );
449 }
450 }
451
452 return $new_fields;
453 }
454
455 public function unsetAvailableInProFields( $setFields )
456 {
457 unset( $setFields['instead_post_name'], $setFields['instead_custom_posts_container'] );
458
459 return $setFields;
460 }
461
462 public function noIndexFilterPages()
463 {
464 if( flrt_is_filter_request() ){
465 $robots['index'] = 'noindex';
466 $robots['follow'] = 'nofollow';
467 $content = implode(', ', $robots );
468 echo sprintf('<meta name="robots" content="%s">', $content)."\r\n";
469 }
470 }
471
472 public function inlineFrontCss()
473 {
474 $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";
475 echo $inline;
476 }
477
478 public function dynamicFrontCss()
479 {
480 // Do not include plugin CSS if there are no Filter Sets on the page
481 $sets = $this->wpManager->getQueryVar( 'wpc_page_related_set_ids', [] );
482 if( empty( $sets ) ) {
483 return false;
484 }
485
486 $maxHeight = flrt_get_option( 'container_height' );
487 $color = flrt_get_option( 'primary_color', flrt_default_theme_color() );
488 $move_to_top = flrt_get_option( 'try_move_to_top_sidebar' );
489 $wpc_mobile_width = flrt_get_mobile_width();
490
491 // Experimental Options
492 $custom_css = flrt_get_experimental_option('custom_css');
493 $use_loader = flrt_get_experimental_option('use_loader');
494 $dark_overlay = flrt_get_experimental_option('dark_overlay');
495 $styled_inputs = flrt_get_experimental_option('styled_inputs');
496 $use_select2 = flrt_get_experimental_option('select2_dropdowns');
497 $rounded_swatch = flrt_get_experimental_option('rounded_swatches');
498 $contrastColor = false;
499
500 $swatches_measurements = [ 'width' => 32, 'height' => 32 ];
501 if( $rounded_swatch ) {
502 $swatches_measurements = [ 'width' => 32, 'height' => 32 ];
503 }
504
505 $swatches = apply_filters( 'wpc_swatches_width_height', $swatches_measurements );
506 $brands = apply_filters( 'wpc_brands_width_height', [ 'width' => 70, 'height' => 40 ] );
507
508 $css = '';
509 $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;overflow-y:clip;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),.widget_wpc_chips_widget .widget-title:not(.wpc-show-on-mobile-widget-title), .widget_wpc_chips_widget .widgettitle:not(.wpc-show-on-mobile-widget-title) {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";
510 $css .= '.wpc-preload-img{display:none;}';
511 // Number of items visible by default in More/Less filters
512
513 $css .= 'li.wpc-term-item label span.wpc-term-swatch,.wpc-term-swatch-wrapper{width:'.$swatches['width'].'px;min-width:'.$swatches['width'].'px;';
514
515 if ( $rounded_swatch ) {
516 $css .= 'border-radius:'.$swatches['height'].'px;';
517 }
518
519 $css .= 'height:'.$swatches['height'].'px;}'."\r\n";
520 $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;}';
521 $css .= '.wpc-term-image-wrapper{width:'.$brands['width'].'px;min-width:'.$brands['width'].'px;height:'.$brands['height'].'px;}';
522
523 if( $maxHeight ){
524 $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{
525 max-height: '.$maxHeight.'px;
526 overflow-y: auto;
527 }'."\r\n";
528 }
529
530 if( $color ){
531 $contrastColor = flrt_get_contrast_color($color);
532 $css .= '.wpc-filters-range-inputs .ui-slider-horizontal .ui-slider-range{
533 background-color: '.$color.';
534 }
535 '."\r\n";
536
537 $css .= '.wpc-spinner:after {
538 border-top-color: '.$color.';
539 }'."\r\n";
540
541 $css .= '.theme-Avada .wpc-filter-product_visibility .star-rating:before,
542 .wpc-filter-product_visibility .star-rating span:before{
543 color: '.$color.';
544 }'."\r\n";
545
546 $css .= 'body .wpc-filters-main-wrap input.wpc-label-input:checked+label span.wpc-filter-label-wrapper{
547 background-color: '.$color.';
548 }'."\r\n";
549
550 $css .= 'body .wpc-filters-main-wrap input.wpc-label-input:checked+label{
551 border-color: '.$color.';
552 }'."\r\n";
553
554 // Disabled label
555 $css .= 'body .wpc-filters-main-wrap .wpc-term-disabled input.wpc-label-input:checked+label span.wpc-filter-label-wrapper{
556 background-color: #d8d8d8;
557 }'."\r\n";
558
559 $css .= 'body .wpc-filters-main-wrap .wpc-term-disabled input.wpc-label-input:checked+label{
560 border-color: #d8d8d8;
561 }'."\r\n";
562
563 $css .= 'body .wpc-filters-main-wrap .wpc-term-disabled input.wpc-label-input+label:hover{
564 border-color: #d8d8d8;
565 }'."\r\n";
566
567 $css .= 'body .wpc-filters-main-wrap .wpc-term-disabled input.wpc-label-input:checked+label span.wpc-filter-label-wrapper,
568 body .wpc-filters-main-wrap .wpc-filters-labels li.wpc-term-item.wpc-term-disabled input:checked+label a,
569 body .wpc-filters-main-wrap .wpc-filters-labels li.wpc-term-item.wpc-term-disabled input:checked+label span
570 {
571 color: #333333;
572 }'."\r\n";
573 // End of disabled label
574
575 $css .= 'body .wpc-filters-main-wrap input.wpc-label-input:checked+label span.wpc-filter-label-wrapper,
576 body .wpc-filters-main-wrap .wpc-filters-labels li.wpc-term-item input:checked+label a{
577 color: '.$contrastColor.';
578 }'."\r\n";
579
580 $css .= 'body .wpc-filter-chips-list li.wpc-filter-chip:not(.wpc-chip-reset-all) a,
581 body .wpc-filter-chips-list li.wpc-filter-chip:not(.wpc-chip-reset-all) span[data-wpc-span-link],
582 body .wpc-filter-chips-list li.wpc-filter-chip:not(.wpc-chip-reset-all) span.wpc-apply-button-chip{
583 border-color: '.$color.';
584 }'."\r\n";
585
586 $css .= 'body .wpc-filters-main-wrap .wpc-filters-widget-controls-container a.wpc-filters-apply-button,
587 body .wpc-filters-main-wrap a.wpc-filters-submit-button{
588 border-color: '.$color.';
589 background-color: '.$color.';
590 color: '.$contrastColor.';
591 }'."\r\n";
592
593 $css .= 'body .wpc-filter-chips-list li.wpc-filter-chip a:hover,
594 body .wpc-filter-chips-list li.wpc-filter-chip span[data-wpc-span-link]:hover,
595 body .wpc-filter-chips-list li.wpc-filter-chip span.wpc-apply-button-chip:hover{
596 opacity: 0.9;
597 }'."\r\n";
598
599 $css .= 'body .wpc-filter-chips-list li.wpc-filter-chip a:active,
600 body .wpc-filter-chips-list li.wpc-filter-chip span[data-wpc-span-link]:active,
601 body .wpc-filter-chips-list li.wpc-filter-chip span.wpc-apply-button-chip:active{
602 opacity: 0.75;
603 }'."\r\n";
604
605 $css .= '.star-rating span,
606 .star-rating span:before{
607 color: '.$color.';
608 }'."\r\n";
609
610 $css .= 'body a.wpc-filters-open-widget:active, a.wpc-filters-open-widget:active,
611 .wpc-filters-open-widget:active{
612 border-color: '.$color.';
613 background-color: '.$color.';
614 color: '.$contrastColor.';
615 }'."\r\n";
616
617 $css .= 'a.wpc-filters-open-widget:active span.wpc-icon-line-1:after,
618 a.wpc-filters-open-widget:active span.wpc-icon-line-2:after,
619 a.wpc-filters-open-widget:active span.wpc-icon-line-3:after{
620 background-color: '.$color.';
621 border-color: '.$contrastColor.';
622 }'."\r\n";
623
624 $css .= 'a.wpc-filters-open-widget:active .wpc-icon-html-wrapper span{
625 background-color: '.$contrastColor.';
626 }'."\r\n";
627
628 //$css .= '@media screen and (min-width: '.$wpc_mobile_width.'px) {'."\r\n";
629 $css .= 'body .wpc-filters-main-wrap input.wpc-label-input+label:hover span.wpc-filter-label-wrapper{
630 color: '.$contrastColor.';
631 background-color: '.$color.';
632 }'."\r\n";
633
634 $css .= 'body .wpc-filters-main-wrap .wpc-filters-labels li.wpc-term-item input+label:hover a{
635 color: '.$contrastColor.';
636 }'."\r\n";
637 $css .= 'body .wpc-filters-main-wrap input.wpc-label-input+label:hover{
638 border-color: '.$color.';
639 }'."\r\n";
640
641
642 $css .= '#ui-datepicker-div.wpc-filter-datepicker .ui-state-active,
643 #ui-datepicker-div.ui-widget-content.wpc-filter-datepicker .ui-state-active,
644 #ui-datepicker-div.wpc-filter-datepicker .ui-widget-header .ui-state-active{
645 border-color: '.$color.';
646 background: '.$color.';
647 opacity: 0.95;
648 }'."\r\n";
649
650 $css .= '#ui-datepicker-div.wpc-filter-datepicker .ui-state-hover,
651 #ui-datepicker-div.ui-widget-content.wpc-filter-datepicker .ui-state-hover,
652 #ui-datepicker-div.wpc-filter-datepicker .ui-widget-header .ui-state-hover,
653 #ui-datepicker-div.wpc-filter-datepicker .ui-state-focus,
654 #ui-datepicker-div.ui-widget-content.wpc-filter-datepicker .ui-state-focus,
655 #ui-datepicker-div.wpc-filter-datepicker .ui-widget-header .ui-state-focus{
656 border-color: '.$color.';
657 background: '.$color.';
658 opacity: 0.6;
659 }';
660
661 $css .= '#ui-datepicker-div.wpc-filter-datepicker .ui-datepicker-close.ui-state-default{
662 background: '.$color.';
663 color: '.$contrastColor.';
664 }'."\r\n";
665
666 //$css .= '}'."\r\n";
667 $css .= '.flrt-star-label svg{
668 stroke: '.$color.';
669 }'."\r\n";
670
671 $css .= '.flrt-star-label-hover svg, .wpc-chip-stars svg{
672 fill: '.$color.';
673 }'."\r\n";
674
675 $css .= '.wpc-filter-label-stars-wrapper{
676 padding: 4px 5px !important;
677 }'."\r\n";
678
679 $css .= '.wpc-filter-label-stars-wrapper .flrt-star-label svg{
680 height: 17px;
681 width: 17px;
682 }'."\r\n";
683
684 $css .= 'body .wpc-filters-main-wrap input.wpc-label-input:checked+label span.wpc-filter-label-stars-wrapper .flrt-star-label svg,
685 span.wpc-filter-label-stars-wrapper:hover .flrt-star-label svg{
686 fill: ' . flrt_get_contrast_color($color) . ';
687 }'."\r\n";
688 }
689 if( $styled_inputs ){
690 $styled_color = $color ? $color : '#0570e2';
691 $contrastColor = $contrastColor ? $contrastColor : flrt_get_contrast_color($styled_color);
692 $hoverColor = flrt_add_color_opacity($color);
693 $no_hex_color = substr( $color, 1, 6 );
694 $css .= '.wpc-filters-main-wrap input[type=checkbox],
695 .wpc-filters-main-wrap input[type=radio]{
696 -webkit-appearance: none;
697 -moz-appearance: none;
698 position: relative;
699 width: 20px;
700 height: 20px;
701 border: 1px solid #c9d1e0;
702 background: #ffffff;
703 border-radius: 5px;
704 min-width: 20px;
705 }
706 i.wpc-toggle-children-list:after,
707 i.wpc-toggle-children-list:before{
708 background-color: #b8bcc8;
709 }
710 i.wpc-toggle-children-list:hover:after,
711 i.wpc-toggle-children-list:hover:before{
712 background-color: '.$color.';
713 }
714 .wpc-filters-widget-content input[type=email],
715 .wpc-filters-widget-content input[type=number],
716 .wpc-filters-widget-content input[type=password],
717 .wpc-filters-widget-content input[type=search],
718 .wpc-filters-widget-content input[type=tel],
719 .wpc-filters-widget-content input[type=text],
720 .wpc-filters-widget-content input[type=url]{
721 height: 44px;
722 }
723 .wpc-filters-widget-content .wpc-filters-section input[type="number"],
724 .wpc-filters-widget-content .wpc-filters-section input[type="text"]{
725 border: 1px solid #ccd0dc;
726 border-radius: 6px;
727 background: transparent;
728 box-shadow: none;
729 padding: 8px 16px;
730 }
731 .wpc-filters-main-wrap input[type=checkbox]:after {
732 content: "";
733 opacity: 0;
734 display: block;
735 left: 3px;
736 top: 3px;
737 position: absolute;
738 width: 12px;
739 height: 12px;
740 border: navajowhite;
741 box-sizing: content-box;
742 background-color: '.$styled_color.';
743 border-radius: 2px;
744 }
745 .wpc-filters-main-wrap input[type=radio]:after {
746 content: "";
747 opacity: 0;
748 display: block;
749 left: 3px;
750 top: 3px;
751 position: absolute;
752 width: 12px;
753 height: 12px;
754 border-radius: 50%;
755 background: '.$styled_color.';
756 box-sizing: content-box;
757 }
758 .wpc-filters-main-wrap input[type=radio]:checked,
759 .wpc-filters-main-wrap input[type=checkbox]:checked {
760 border-color: '.$styled_color.';
761 }
762 .wpc-filters-main-wrap .wpc-radio-item.wpc-term-disabled input[type=radio],
763 .wpc-filters-main-wrap .wpc-checkbox-item.wpc-term-disabled > div > input[type=checkbox],
764 .wpc-filters-main-wrap .wpc-checkbox-item.wpc-term-disabled > div > input[type=checkbox]:after,
765 .wpc-filters-main-wrap .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=checkbox]:after,
766 .wpc-filters-main-wrap .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=checkbox],
767 .wpc-filters-main-wrap .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=radio],
768 .wpc-term-swatch-no-image{
769 border-color: #d8d8d8;
770 }
771 .wpc-filters-main-wrap .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=checkbox]:after,
772 .wpc-filters-main-wrap .wpc-checkbox-item.wpc-term-disabled > div > input[type=checkbox]:after {
773 background-color: #d8d8d8;
774 }
775 .wpc-filters-main-wrap .wpc-radio-item.wpc-term-disabled input[type=radio]:after,
776 .wpc-filters-main-wrap .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=radio]:after{
777 background-color: #d8d8d8;
778 }
779 .wpc-filters-main-wrap input[type=radio]:checked:after,
780 .wpc-filters-main-wrap input[type=checkbox]:checked:after {
781 opacity: 1;
782 }
783 .wpc-filters-main-wrap input[type=radio] {
784 border-radius: 50%;
785 }
786 .wpc-filters-widget-content .wpc-filters-section .wpc-filter-search-wrapper .wpc-filter-search-field {
787 padding: 8px 16px 8px 48px;
788 }
789 .wpc-filters-widget-content .wpc-filters-date-range-wrapper input[type="text"]{
790 padding-right: 48px;
791 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");
792 background-repeat: no-repeat;
793 background-position: right 16px bottom 50%;
794 background-size: 16px;
795 }
796 .wpc-filters-widget-content .wpc-filters-date-range-wrapper input[type="text"]:focus,
797 .wpc-filters-widget-content .wpc-filters-date-range-wrapper input[type="text"]:hover{
798 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");
799 }
800 .wpc-filter-layout-dropdown .select2-container--default .select2-selection--single .select2-selection__arrow b,
801 .wpc-sorting-form .select2-container--default .select2-selection--single .select2-selection__arrow b{
802 border-left: 1px solid #b8bcc8;
803 border-top: 1px solid #b8bcc8;
804 }
805 .wpc-filter-layout-dropdown .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b,
806 .wpc-sorting-form .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{
807 border-left: 1px solid #b8bcc8;
808 border-top: 1px solid #b8bcc8;
809 }
810 .wpc-filter-collapsible .wpc-filter-title .wpc-open-icon,
811 .wpc-filter-collapsible-reverse.wpc-filter-collapsible.wpc-closed .wpc-filter-title .wpc-open-icon,
812 .wpc-filter-collapsible.wpc-closed .wpc-filter-title .wpc-open-icon,
813 .wpc-filter-has-selected.wpc-closed .wpc-filter-title .wpc-open-icon{
814 border-left: 1px solid #b8bcc8;
815 border-top: 1px solid #b8bcc8;
816 }
817 .wpc-sorting-form .select2-container--default .select2-selection--single:hover,
818 .wpc-filters-widget-content input[type=email]:hover,
819 .wpc-filters-widget-content input[type=number]:hover,
820 .wpc-filters-widget-content input[type=password]:hover,
821 .wpc-filters-widget-content input[type=search]:hover,
822 .wpc-filters-widget-content input[type=tel]:hover,
823 .wpc-filters-widget-content input[type=text]:hover,
824 .wpc-filters-widget-content input[type=url]:hover{
825 border-color: '. $hoverColor.';
826 }
827 .wpc-filter-layout-dropdown .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b,
828 .wpc-sorting-form .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b,
829 .widget_wpc_sorting_widget .select2-container--default .select2-selection--single:hover .select2-selection__arrow b,
830 .wpc-filter-layout-dropdown .select2-container--default .select2-selection--single:hover .select2-selection__arrow b,
831 .wpc-filter-layout-dropdown .select2-container--default .select2-selection--single:hover,
832 .select2-container--default.select2-container--open .wpc-filter-everything-dropdown.select2-dropdown,
833 .wpc-sorting-form .select2-container--open .select2-selection--single:hover,
834 .widget_wpc_sorting_widget .select2-container--open .select2-selection--single,
835 .wpc-filter-layout-dropdown .select2-container--open .select2-selection--single,
836 .wpc-filters-widget-content input[type=email]:focus,
837 .wpc-filters-widget-content input[type=number]:focus,
838 .wpc-filters-widget-content input[type=password]:focus,
839 .wpc-filters-widget-content input[type=search]:focus,
840 .wpc-filters-widget-content input[type=tel]:focus,
841 .wpc-filters-widget-content input[type=text]:focus,
842 .wpc-filters-widget-content input[type=url]:focus,
843 .wpc-filters-widget-content input[type=email]:active,
844 .wpc-filters-widget-content input[type=number]:active,
845 .wpc-filters-widget-content input[type=password]:active,
846 .wpc-filters-widget-content input[type=search]:active,
847 .wpc-filters-widget-content input[type=tel]:active,
848 .wpc-filters-widget-content input[type=text]:active,
849 .wpc-filters-widget-content input[type=url]:active,
850 .wpc-filter-collapsible .wpc-filter-title button:hover .wpc-open-icon,
851 .wpc-filter-collapsible-reverse.wpc-filter-collapsible.wpc-closed .wpc-filter-title button:hover .wpc-open-icon,
852 .wpc-filter-collapsible.wpc-closed .wpc-filter-title button:hover .wpc-open-icon,
853 .wpc-filter-has-selected.wpc-closed .wpc-filter-title button:hover .wpc-open-icon{
854 border-color: '.$color.';
855 }
856
857 .wpc-filters-main-wrap a.wpc-toggle-a:hover {
858 color: '.$color.';
859 }
860 .wpc-sorting-form .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple,
861 .wpc-filter-layout-dropdown .select2-container--default.select2-container--open.select2-container--above .select2-selection--single,
862 .wpc-sorting-form .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple,
863 .wpc-filter-layout-dropdown .select2-container--default.select2-container--open.select2-container--above .select2-selection--single{
864 border-top: 1px solid transparent;
865 }
866 .wpc-search-field-wrapper .wpc-search-clear-icon-wrapper,
867 .wpc-filter-search-wrapper button.wpc-search-clear{
868 color: #b5bed2;
869 }
870 .wpc-filters-main-wrap .wpc-filters-labels li.wpc-term-item label {
871 border-color: #ccd0dc;
872 }
873 .wpc-filters-main-wrap .wpc-filters-labels li.wpc-term-item label span.wpc-filter-label-wrapper{
874 padding: 8px 7px;
875 }
876 .wpc-filters-labels li.wpc-term-has-image label:hover .wpc-term-image-wrapper,
877 .wpc-filters-labels li.wpc-term-has-image input[type=checkbox]:checked + label .wpc-term-image-wrapper{
878 border-color: '.$color.';
879 }
880 @media screen and (min-width: '.$wpc_mobile_width.'px) {
881 .wpc-filters-main-wrap input[type=radio]:hover,
882 .wpc-filters-main-wrap input[type=checkbox]:hover{
883 border-color: '.$styled_color.';
884 }
885 .wpc-filter-label-wrapper .wpc-term-swatch-no-image:hover,
886 .wpc-filters-main-wrap .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=radio]:hover,
887 .wpc-filters-main-wrap .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=checkbox]:hover{
888 border-color: #c3c3c3;
889 }
890 }';
891 }
892
893 if( $use_select2 ){
894 $styled_color = $color ? $color : '#0570e2';
895 $contrastColor = $contrastColor ? $contrastColor : flrt_get_contrast_color($styled_color);
896
897 $css .= '.wpc-sorting-form select,
898 .wpc-filter-content select{
899 padding: 2px 8px 2px 10px;
900 border-color: #c9d1e0;
901 border-radius: 3px;
902 color: inherit;
903 -webkit-appearance: none;
904 }
905 .select2-container--default .wpc-filter-everything-dropdown .select2-results__option--highlighted::after,
906 .select2-container--default .wpc-filter-everything-dropdown .select2-results__option[aria-selected="true"]::after,
907 .select2-container--default .wpc-filter-everything-dropdown .select2-results__option[data-selected="true"]::after {
908 border: 2px solid '.$color.';
909 content: "";
910 position: absolute !important;
911 right: 13px !important;
912 width: 18px;
913 height: 10px;
914 top: 30% !important;
915 font-size: 16px !important;
916 color: #000 !important;
917 transform: rotate(137deg) !important;
918 border-bottom: none;
919 border-left: none;
920 box-sizing: border-box;
921 }
922 .select2-container--default .wpc-filter-everything-dropdown .select2-results__option--highlighted::after {
923 border-top: 2px solid #C7D1E2;
924 border-right: 2px solid #C7D1E2;
925 }
926 .select2-container--default .wpc-filter-everything-dropdown .select2-results__option--highlighted[aria-selected],
927 .select2-container--default .wpc-filter-everything-dropdown .select2-results__option--highlighted[data-selected]{
928 background-color: rgba(0,0,0,0.05);
929 color: inherit;
930 }
931 ';
932 $css .= '@media screen and (max-width: '.$wpc_mobile_width.'px) {'."\r\n";
933 $css .= '.wpc-sorting-form select,
934 .wpc-filter-content select{
935 padding: 6px 12px 6px 14px;
936 }';
937
938 $css .= '}'."\r\n";
939 }
940
941 if( $move_to_top ){
942 $css .= '@media screen and (max-width: '.$wpc_mobile_width.'px) {'."\r\n";
943
944 $css .= 'body #main,
945 body #content .col-full,
946 .woocommerce-page .content-has-sidebar,
947 .woocommerce-page .has-one-sidebar,
948 .woocommerce-page #main-sidebar-container,
949 .woocommerce-page .theme-page-wrapper,
950 .woocommerce-page #content-area,
951 .theme-jevelin.woocommerce-page .woocomerce-styling,
952 .woocommerce-page .content_wrapper,
953 .woocommerce-page #col-mask,
954 body #main-content .content-area {
955 -js-display: flex;
956 display: -webkit-box;
957 display: -webkit-flex;
958 display: -moz-box;
959 display: -ms-flexbox;
960 display: flex;
961 -webkit-box-orient: vertical;
962 -moz-box-orient: vertical;
963 -webkit-flex-direction: column;
964 -ms-flex-direction: column;
965 flex-direction: column;
966 }
967 body #primary,
968 .woocommerce-page .has-one-sidebar > section,
969 .woocommerce-page .theme-content,
970 .woocommerce-page #left-area,
971 .woocommerce-page #content,
972 .woocommerce-page .sections_group,
973 .woocommerce-page .content-box,
974 body #main-sidebar-container #main {
975 -webkit-box-ordinal-group: 2;
976 -moz-box-ordinal-group: 2;
977 -ms-flex-order: 2;
978 -webkit-order: 2;
979 order: 2;
980 }
981 body #secondary,
982 .woocommerce-page .has-one-sidebar > aside,
983 body aside#mk-sidebar,
984 .woocommerce-page #sidebar,
985 .woocommerce-page .sidebar,
986 body #main-sidebar-container #sidebar {
987 -webkit-box-ordinal-group: 1;
988 -moz-box-ordinal-group: 1;
989 -ms-flex-order: 1;
990 -webkit-order: 1;
991 order: 1;
992 }
993
994 /*second method first method solve issue theme specific*/
995 .woocommerce-page:not(.single,.page) .btWithSidebar.btSidebarLeft .btContentHolder,
996 body .theme-generatepress.woocommerce #content {
997 display: table;
998 }
999 body .btContent,
1000 body .theme-generatepress.woocommerce #primary {
1001 display: table-footer-group;
1002 }
1003 body .btSidebar,
1004 body .theme-generatepress.woocommerce #left-sidebar {
1005 display: table-header-group;
1006 }'."\r\n";
1007 $css .= '#ui-datepicker-div.wpc-filter-datepicker .ui-datepicker-close.ui-state-default{
1008 background: '.$color.';
1009 color: '.$contrastColor.';
1010 }'."\r\n";
1011 $css .= '.wpc-filters-date-range-column{
1012 justify-content: left;
1013 }'."\r\n";
1014 $css .= '}'."\r\n";
1015 }
1016
1017 if( $use_loader ){
1018 $css .= '@media screen and (min-width: '.$wpc_mobile_width.'px) {'."\r\n";
1019 $css .= 'html.is-active .wpc-spinner{
1020 display: block;
1021 }';
1022 $css .= '}'."\r\n";
1023 }
1024
1025 if( $dark_overlay ){
1026 $css .= '@media screen and (min-width: '.$wpc_mobile_width.'px) {'."\r\n";
1027 $css .= 'html.is-active .wpc-filters-overlay{
1028 opacity: .15;
1029 background: #000000;
1030 }';
1031 $css .= '}'."\r\n";
1032 }
1033
1034 if(flrt_is_divi_theme()){
1035 $css .= <<<PHP_CSS
1036 .wp-theme-Divi .et_builder_inner_content .widget_wpc_sorting_widget,
1037 .wp-theme-Divi .et_builder_inner_content .widget_wpc_filters_widget,
1038 .wp-theme-Divi .et_builder_inner_content .widget_wpc_chips_widget
1039 {
1040 margin: 0 0 3.7em;
1041 }
1042 .wp-theme-Divi .et_builder_inner_content .widget_wpc_chips_widget ul.wpc-filter-chips-list{
1043 padding: 0;
1044 }
1045 .wpc-divi-filter-wrap .wpc-filters-open-button-container{
1046 display: block !important;
1047 }
1048 @media screen and (max-width: 768px) {
1049 .wpc-filters-open-button-container.et_before_main_content{
1050 display: block !important;
1051 }
1052 }
1053 PHP_CSS;
1054 $css .= "\r\n";
1055 }
1056
1057 if( $custom_css ){
1058 $css .= $custom_css."\r\n";
1059 }
1060
1061 $wp_upload_dir = wp_upload_dir();
1062 $upload_dir_baseurl = $wp_upload_dir['baseurl'];
1063 $upload_dir_basepath = $wp_upload_dir['basedir'];
1064
1065 $cache_dir = $upload_dir_basepath . '/cache/filter-everything/';
1066
1067 if ( ! file_exists( $cache_dir ) ) {
1068 mkdir($cache_dir, 0777, true);
1069 chmod($cache_dir, 0777);
1070 }
1071
1072 $filename = md5( $css ) . '.css';
1073
1074 $fileurl = $upload_dir_baseurl .'/cache/filter-everything/' . $filename;
1075 $filepath = $cache_dir . $filename;
1076
1077 if ( $css !== '' ) {
1078 if ( ! file_exists( $filepath ) ) {
1079 file_put_contents( $filepath, $css );
1080 }
1081
1082 if( file_exists( $filepath ) ){
1083 wp_enqueue_style('wpc-filter-everything-custom', $fileurl );
1084 }
1085 }
1086
1087 }
1088
1089 public function bodyClass( $classes )
1090 {
1091 if( flrt_get_option('mobile_filter_settings') === 'show_open_close_button' ){
1092 $classes[] = 'wpc_show_open_close_button';
1093 }
1094
1095 if( flrt_is_filter_request() ){
1096 $classes[] = 'wpc_is_filter_request';
1097 }
1098
1099 return $classes;
1100 }
1101
1102 public static function activate()
1103 {
1104 $defaultSettings = new DefaultSettings();
1105 if ( ! get_option('wpc_filter_settings') ) {
1106 add_option('wpc_filter_settings', $defaultSettings->wpc_filter_settings() );
1107 }
1108
1109 if( ! get_option( 'wpc_filter_experimental' ) ){
1110 add_option('wpc_filter_experimental', $defaultSettings->wpc_filter_experimental() );
1111 }
1112
1113 // Stamp the current version on fresh installs so 'update'-triggered
1114 // admin notices (see AdminNotices) fire only when an existing install is
1115 // later updated, never on a brand-new installation.
1116 if ( ! get_option( 'flrt_version' ) ) {
1117 add_option( 'flrt_version', FLRT_PLUGIN_VER );
1118 }
1119 }
1120
1121 /**
1122 * Clears all plugin data: options and posts
1123 */
1124 /**
1125 * Removes the static filter-data cache (uploads/flrt-cache) with its
1126 * folder. Defensive on purpose: the path must look exactly like our own
1127 * folder, only known file patterns are deleted, every step is
1128 * error-suppressed and any unexpected failure is swallowed — the cleanup
1129 * is best-effort and must never break the uninstall process.
1130 */
1131 private static function deleteJsonBlobCache()
1132 {
1133 try {
1134 $uploads = wp_upload_dir();
1135 if ( ! empty( $uploads['error'] ) || empty( $uploads['basedir'] ) ) {
1136 return;
1137 }
1138
1139 $dir = trailingslashit( $uploads['basedir'] ) . 'flrt-cache';
1140
1141 if ( substr( $dir, -strlen( '/flrt-cache' ) ) !== '/flrt-cache' || ! is_dir( $dir ) ) {
1142 return;
1143 }
1144
1145 $blob_files = glob( $dir . '/filters-*.json' );
1146 $tmp_files = glob( $dir . '/filters-*.json.*.tmp' );
1147
1148 $to_delete = array_merge(
1149 is_array( $blob_files ) ? $blob_files : [],
1150 is_array( $tmp_files ) ? $tmp_files : []
1151 );
1152 $to_delete[] = $dir . '/index.php';
1153
1154 foreach ( $to_delete as $file ) {
1155 if ( is_file( $file ) ) {
1156 @unlink( $file );
1157 }
1158 }
1159
1160 // Removed only when empty — anything unexpected inside stays put
1161 @rmdir( $dir );
1162 } catch ( \Throwable $e ) {
1163 // Best-effort cleanup: never break the uninstall
1164 }
1165 }
1166
1167 public static function uninstall( $force = false )
1168 {
1169 $allow_to_delete = true;
1170
1171 if( is_multisite() ){
1172 $active_plugins = get_site_option('active_sitewide_plugins');
1173 if( is_array( $active_plugins ) ){
1174 $active_plugins = array_keys( $active_plugins );
1175 }
1176 }else{
1177 $active_plugins = apply_filters('active_plugins', get_option('active_plugins'));
1178 }
1179
1180 $fe_active = [];
1181 $to_compare = [
1182 'filter-everything-pro/filter-everything.php',
1183 'filter-everything/filter-everything.php'
1184 ];
1185
1186 if( ! empty( $active_plugins ) ){
1187 foreach ( $active_plugins as $plugin_path ){
1188 if( in_array( $plugin_path, $to_compare ) ){
1189 $fe_active[] = $plugin_path;
1190 }
1191 }
1192 }
1193
1194 if( count( $fe_active ) > 0 ){
1195 $allow_to_delete = false;
1196 }
1197
1198 if ( $force == true ) {
1199 $allow_to_delete = true;
1200 }
1201
1202 if( $allow_to_delete ){
1203
1204 $options = [
1205 'wpc_filter_settings',
1206 'wpc_indexing_deep_settings',
1207 'wpc_filter_permalinks',
1208 'wpc_seo_rules_settings',
1209 'wpc_xml_write_date',
1210 'wpc_filter_experimental',
1211 'flrt_json_blob_ver',
1212 'widget_wpc_filters_widget',
1213 'widget_wpc_sorting_widget',
1214 'widget_wpc_chips_widget',
1215 ];
1216
1217 foreach ( $options as $option_name ){
1218 delete_option( $option_name );
1219 }
1220
1221 self::deleteJsonBlobCache();
1222
1223 // Deactivate and erase license if exists
1224 if ( defined( 'FLRT_FILTERS_PRO' ) && FLRT_FILTERS_PRO ) {
1225
1226 wpc_clear_folder(FLRT_XML_PATH);
1227
1228 $to_send = false;
1229 $saved_value = get_option( FLRT_LICENSE_KEY );
1230
1231 if( isset( $saved_value['license_key'] ) ) {
1232 $saved_value_arr = maybe_unserialize( base64_decode( $saved_value['license_key'] ) );
1233 $to_send = $saved_value_arr;
1234 }
1235
1236 if ( is_array( $to_send ) ){
1237 $to_send['home_url'] = home_url();
1238
1239 // Make data suitable to send as GET variables
1240 $to_send = array_map( 'urlencode', $to_send );
1241
1242 if ( isset( $saved_value_arr['id'] ) && $saved_value_arr['id'] ) {
1243 $apiRequest = new ApiRequests();
1244 $result = $apiRequest->sendRequest('DELETE', 'license', $to_send );
1245
1246 // If license was deactivated, we have to refresh updates info
1247 delete_transient(FLRT_VERSION_TRANSIENT );
1248 delete_option( FLRT_LICENSE_KEY );
1249 }
1250 }
1251
1252 }
1253
1254 $postTypes = array(
1255 FLRT_FILTERS_SET_POST_TYPE,
1256 FLRT_FILTERS_POST_TYPE
1257 );
1258
1259 if( defined( 'FLRT_SEO_RULES_POST_TYPE' ) ){
1260 $postTypes[] = FLRT_SEO_RULES_POST_TYPE;
1261 }
1262
1263 $filterPosts = new \WP_Query(
1264 array(
1265 'posts_per_page' => -1,
1266 'post_status' => array('any'),
1267 'post_type' => $postTypes,
1268 'fields' => 'ids',
1269 'suppress_filters' => true
1270 )
1271 );
1272
1273 $filterPostsIds = $filterPosts->get_posts();
1274
1275 if( ! empty( $filterPostsIds ) ){
1276 foreach ($filterPostsIds as $post_id) {
1277 wp_delete_post( $post_id, true );
1278 }
1279 }
1280
1281 $filterTrashPosts = new \WP_Query(
1282 array(
1283 'posts_per_page' => -1,
1284 'post_status' => array('trash'),
1285 'post_type' => $postTypes,
1286 'fields' => 'ids',
1287 'suppress_filters' => true
1288 )
1289 );
1290
1291 $filterTrashPostsIds = $filterTrashPosts->get_posts();
1292
1293 if( ! empty( $filterTrashPostsIds ) ){
1294 foreach ($filterTrashPostsIds as $post_id) {
1295 wp_delete_post( $post_id, true );
1296 }
1297 }
1298 }
1299 }
1300
1301 public static function switchTheme() {
1302 flrt_remove_option('posts_container');
1303 flrt_remove_option('primary_color');
1304 }
1305
1306 public function includeAdminCss()
1307 {
1308 $screen = get_current_screen();
1309 if ( ! is_null( $screen ) && property_exists( $screen, 'base' ) ) {
1310 $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
1311 $ver = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? rand(0, 1000) : FLRT_PLUGIN_VER;
1312 $is_about_pro_tab = isset($_GET['tab']) && $_GET['tab'] === 'aboutpro';
1313
1314 if ( in_array( $screen->base, [ 'edit', 'post', 'edit-tags', 'term' ] ) || ( strpos( $screen->base, 'filters-settings' ) !== false ) || ( strpos( $screen->base, 'filters-whats-new' ) !== false ) ) {
1315 wp_enqueue_style( 'wpc-filter-everything-admin', FLRT_PLUGIN_DIR_URL . 'assets/css/filter-everything-admin'.$suffix.'.css', ['wp-color-picker'], $ver );
1316 if($is_about_pro_tab){
1317 wp_enqueue_style( 'wpc-filter-everything-pro-benefits', FLRT_PLUGIN_DIR_URL . 'assets/css/pro-benefits-page'.$suffix.'.css', ['wpc-filter-everything-admin'], $ver );
1318 }
1319 }
1320
1321 if ( $screen->base === 'widgets' ) {
1322 wp_enqueue_style('wpc-widgets', FLRT_PLUGIN_DIR_URL . 'assets/css/wpc-widgets' . $suffix . '.css', [], $ver );
1323 }
1324
1325 }
1326 }
1327
1328 public function includeAdminJs()
1329 {
1330 $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
1331 $ver = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? rand(0, 1000) : FLRT_PLUGIN_VER;
1332 $select2ver = '4.1.0';
1333
1334 wp_register_script( 'jquery-tiptip', FLRT_PLUGIN_DIR_URL . 'assets/js/jquery-tiptip/jquery.tipTip' . $suffix . '.js', array( 'jquery' ), $ver, true );
1335 wp_enqueue_script('jquery-tiptip');
1336 wp_enqueue_script('wpc-filters-admin', FLRT_PLUGIN_DIR_URL . 'assets/js/wpc-filters-common-admin' . $suffix . '.js', array( 'jquery', 'jquery-ui-sortable', 'wp-color-picker', 'select2'), $ver, true );
1337
1338 $l10n = array(
1339 'prefixesOrderAvailableInPro' => esc_html__( 'Editing the order of URL prefixes is available in the PRO version', 'filter-everything' ),
1340 'chipsPlaceholder' => esc_html__( 'Select or enter hooks', 'filter-everything' ),
1341 'colorSwatchesPlaceholder' => esc_html__( 'Click to select taxonomies', 'filter-everything' ),
1342 'chooseElementHelpText' => esc_html__( 'Hover over the Results container and click to select it', 'filter-everything' ),
1343 );
1344 wp_localize_script( 'wpc-filters-admin', 'wpcFiltersAdminCommon', $l10n );
1345
1346 wp_enqueue_script( 'select2', FLRT_PLUGIN_DIR_URL . "assets/js/select2/select2".$suffix.".js", array('jquery'), $select2ver );
1347 wp_enqueue_style('select2', FLRT_PLUGIN_DIR_URL . "assets/css/select2/select2".$suffix.".css", '', $select2ver );
1348
1349 $screen = get_current_screen();
1350
1351 if( ! is_null( $screen ) && property_exists( $screen, 'base' ) && $screen->base === 'widgets' ){
1352 wp_enqueue_script('wpc-widgets', FLRT_PLUGIN_DIR_URL . 'assets/js/wpc-widgets' . $suffix . '.js', array('jquery'), $ver );
1353 $l10n = array(
1354 'wpcItemNum' => esc_html__( 'Item #', 'filter-everything')
1355 );
1356 wp_localize_script( 'wpc-widgets', 'wpcWidgets', $l10n );
1357 }
1358 }
1359
1360 public function includeFrontCss()
1361 {
1362 $suffix = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '.min';
1363 $ver = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? rand(0, 1000) : FLRT_PLUGIN_VER;
1364 /**
1365 * string
1366 */
1367 $getData = Container::instance()->getTheGet();
1368 if( isset( $getData['fl_builder'] ) ){
1369 wp_enqueue_style('wpc-widgets', FLRT_PLUGIN_DIR_URL . 'assets/css/wpc-widgets' . $suffix . '.css', [], $ver );
1370 }
1371 if ( isset( $getData['flrt_get_html_selector'] ) && $getData['flrt_get_html_selector'] === '1' && current_user_can('manage_options') ) {
1372 wp_enqueue_style('wpc-element-picker', FLRT_PLUGIN_DIR_URL . 'assets/css/element-picker' . $suffix . '.css', [], $ver );
1373 }
1374
1375 // Do not include plugin CSS if there are no Filter Sets on the page
1376 $sets = $this->wpManager->getQueryVar('wpc_page_related_set_ids', []);
1377 if( empty( $sets ) ) {
1378 return false;
1379 }
1380
1381 wp_enqueue_style('wpc-filter-everything', FLRT_PLUGIN_DIR_URL . 'assets/css/filter-everything' . $suffix . '.css', [], $ver);
1382 }
1383
1384 public function footerHtml()
1385 {
1386 echo '<div class="wpc-filters-overlay"></div>'."\r\n";
1387 }
1388
1389 public function includeFrontJs()
1390 {
1391 $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
1392 $ver = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? rand(0, 1000) : FLRT_PLUGIN_VER;
1393 /**
1394 * string
1395 */
1396 $getData = Container::instance()->getTheGet();
1397 $em = Container::instance()->getEntityManager();
1398
1399 $wpcFrontJsVariables = [];
1400
1401 if ( isset( $getData['flrt_get_html_selector'] ) && $getData['flrt_get_html_selector'] === '1' && current_user_can('manage_options') ) {
1402 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 );
1403 wp_enqueue_script( 'wpc-element-picker', FLRT_PLUGIN_DIR_URL . 'assets/js/element-picker' . $suffix . '.js', array( 'jquery', 'wpc-element-picker-bundle' ), $ver, true );
1404 add_action( 'wp_footer', [$this, 'showElementPickerPanel'] );
1405 }
1406
1407 if( isset( $getData['fl_builder'] ) ){
1408 wp_enqueue_script('wpc-widgets', FLRT_PLUGIN_DIR_URL . 'assets/js/wpc-widgets' . $suffix . '.js', array('jquery'), $ver );
1409 $l10n = array(
1410 'wpcItemNum' => esc_html__( 'Item #', 'filter-everything' )
1411 );
1412 wp_localize_script( 'wpc-widgets', 'wpcWidgets', $l10n );
1413 }
1414
1415 // Do not include plugin JS if there are no Filter Sets on the page
1416 $sets = $this->wpManager->getQueryVar( 'wpc_page_related_set_ids', [] );
1417 $related_filters = $em->getSetsRelatedFilters( $sets );
1418 $date_filters = [];
1419 $include_timepicker = false;
1420
1421 if ( ! empty( $related_filters ) ) {
1422 foreach ( $related_filters as $filter ) {
1423 if ( in_array( $filter['entity'], [ 'post_date', 'post_meta_date' ] ) ) {
1424
1425 $date_time = flrt_split_date_time( $filter['date_format'] );
1426
1427 global $wp_locale;
1428 $date_filters[ $filter['ID'] ] = [
1429 'date_type' => $filter['date_type'],
1430 'date_format' => flrt_convert_date_to_js( $date_time['date'] ),
1431 'time_format' => flrt_convert_time_to_js( $date_time['time'] ),
1432 ];
1433
1434 if ( in_array( $filter['date_type'], [ 'time', 'datetime' ] ) ) {
1435 $include_timepicker = true;
1436 }
1437
1438 }
1439 }
1440 }
1441
1442 /**
1443 * Do not continue and do not include any assets
1444 * if this page does not contain Filter Sets
1445 */
1446 if ( empty( $sets ) ) {
1447 return false;
1448 }
1449
1450 $showBottomWidget = 'no';
1451 $ajaxEnabled = false;
1452 $autoScroll = false;
1453 $waitCursor = false;
1454 $wpcUseSelect2 = false;
1455 $wpcPopupCompatMode = false;
1456 $autoScrollOffset = apply_filters( 'wpc_auto_scroll_offset', 150 );
1457 $wpc_mobile_width = flrt_get_mobile_width();
1458 $per_page = [];
1459 $applyButtonSets = [];
1460 $queryOnThePageSets = [];
1461 $filterSetService = Container::instance()->getFilterSetService();
1462
1463 if ( flrt_get_option('mobile_filter_settings') === 'show_bottom_widget' ) {
1464 $showBottomWidget = 'yes';
1465 }
1466
1467 if ( flrt_get_option('enable_ajax') === 'on' ) {
1468 $ajaxEnabled = true;
1469 }
1470
1471 if ( flrt_get_experimental_option('auto_scroll') === 'on' ) {
1472 $autoScroll = true;
1473 }
1474
1475 if ( flrt_get_experimental_option( 'use_wait_cursor' ) === 'on' ) {
1476 $waitCursor = true;
1477 }
1478
1479 if ( flrt_get_option('bottom_widget_compatibility') ) {
1480 $wpcPopupCompatMode = true;
1481 }
1482
1483 //@todo This appears on login page and produce not an array error
1484 foreach( $sets as $set ){
1485 if( $set['filtered_post_type'] === 'product' && function_exists('wc_get_default_products_per_row') ){
1486 $numberposts = apply_filters( 'loop_shop_per_page', wc_get_default_products_per_row() * wc_get_default_product_rows_per_page() );
1487 }else{
1488 $numberposts = get_option( 'posts_per_page' );
1489 }
1490
1491 $per_page[ $set['ID'] ] = intval($numberposts);
1492 $theSet = $filterSetService->getSet( $set['ID'] );
1493
1494 if( isset( $set['query_on_the_page'] ) && $set['query_on_the_page'] ){
1495 if( (int) $set['ID'] > 0 ) {
1496 $queryOnThePageSets[] = (int) $set['ID'];
1497 }
1498 }
1499
1500 if( isset( $theSet['use_apply_button']['value'] ) && $theSet['use_apply_button']['value'] === 'yes' ){
1501 if( (int) $set['ID'] > 0 ){
1502 $applyButtonSets[] = (int) $set['ID'];
1503 }
1504 }
1505 }
1506
1507 $per_page = apply_filters( 'wpc_filter_sets_posts_per_page', $per_page );
1508
1509 $wpcPostContainers = apply_filters( 'wpc_posts_containers', flrt_get_option( 'posts_container', flrt_default_posts_container() ) );
1510
1511 if ( is_array( $wpcPostContainers ) ) {
1512 $wpcPostContainers = array_map( 'wp_specialchars_decode', $wpcPostContainers );
1513 }
1514
1515 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 );
1516
1517 $isPro = defined('FLRT_FILTERS_PRO') && FLRT_FILTERS_PRO;
1518
1519 $wpcFrontJsVariables = array(
1520 'ajaxUrl' => admin_url('admin-ajax.php'),
1521 'wpcAjaxEnabled' => $ajaxEnabled,
1522 'wpcStatusCookieName' => FLRT_FOLDING_COOKIE_NAME,
1523 'wpcMoreLessCookieName' => FLRT_MORELESS_COOKIE_NAME,
1524 'wpcHierarchyListCookieName' => FLRT_HIERARCHY_LIST_COOKIE_NAME,
1525 'wpcWidgetStatusCookieName' => FLRT_OPEN_CLOSE_BUTTON_COOKIE_NAME,
1526 'wpcMobileWidth' => $wpc_mobile_width,
1527 'showBottomWidget' => $showBottomWidget,
1528 '_nonce' => wp_create_nonce('wpcNonceFront'),
1529 'wpcPostContainers' => $wpcPostContainers,
1530 'wpcAutoScroll' => $autoScroll,
1531 'wpcAutoScrollOffset' => $autoScrollOffset,
1532 'wpcWaitCursor' => $waitCursor,
1533 'wpcPostsPerPage' => $per_page,
1534 'wpcUseSelect2' => $wpcUseSelect2,
1535 'wpcDateFilters' => false,
1536 'wpcPopupCompatMode' => $wpcPopupCompatMode,
1537 'wpcApplyButtonSets' => $applyButtonSets,
1538 'wpcQueryOnThePageSets' => $queryOnThePageSets,
1539 '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'),
1540 'wpcIsPro' => $isPro,
1541 'permalinksEnabled' => FLRT_PERMALINKS_ENABLED,
1542 'wpcMoreLessCount' => flrt_more_less_count(),
1543 'wpcSearchChipsText' => esc_html__('search: %s', 'filter-everything' ),
1544 'chipsTitle' => esc_html__('Remove %s from results', 'filter-everything'),
1545 'chipsReset' => esc_html__('Reset all', 'filter-everything'),
1546 );
1547
1548 /**
1549 * We includes Flatpickr.js only on pages, where date filter is used.
1550 */
1551 if ( ! empty( $date_filters ) ) {
1552
1553 wp_enqueue_script( 'jquery-ui-datepicker' );
1554 wp_enqueue_style( 'wpc-datepicker', FLRT_PLUGIN_DIR_URL . 'assets/css/datepicker/jquery-ui'.$suffix.'.css', array(), '1.11.4' );
1555
1556 if ( $include_timepicker ) {
1557 wp_enqueue_script( 'wpc-timepicker', FLRT_PLUGIN_DIR_URL . "assets/js/timepicker/jquery-ui-timepicker-addon".$suffix.".js", array( 'jquery-ui-datepicker' ), '1.6.3' );
1558 wp_enqueue_style( 'wpc-timepicker', FLRT_PLUGIN_DIR_URL . "assets/css/timepicker/jquery-ui-timepicker-addon".$suffix.".css", array(), '1.6.3' );
1559 }
1560
1561 $wpcFrontJsVariables['wpcDateFilters'] = $date_filters;
1562 $wpcFrontJsVariables['wpcDateFiltersLocale'] = determine_locale();
1563 $wpcFrontJsVariables['wpcDateFiltersL10n'] = array(
1564 'closeText' => _x( 'Filter', 'Date Picker closeText', 'filter-everything' ),
1565 'currentText' => _x( 'Today', 'Date Picker currentText', 'filter-everything' ),
1566 'nextText' => _x( 'Next', 'Date Picker nextText', 'filter-everything' ),
1567 'prevText' => _x( 'Prev', 'Date Picker prevText', 'filter-everything' ),
1568 'weekHeader' => _x( 'Wk', 'Date Picker weekHeader', 'filter-everything' ),
1569 'timeOnlyTitle' => _x( 'Choose Time', 'Date Time Picker timeOnlyTitle', 'filter-everything' ),
1570 'timeText' => _x( 'Time', 'Date Time Picker timeText', 'filter-everything' ),
1571 'hourText' => _x( 'Hour', 'Date Time Picker hourText', 'filter-everything' ),
1572 'minuteText' => _x( 'Minute', 'Date Time Picker minuteText', 'filter-everything' ),
1573 'secondText' => _x( 'Second', 'Date Time Picker secondText', 'filter-everything' ),
1574 'timezoneText' => _x( 'Time Zone', 'Date Time Picker timezoneText', 'filter-everything' ),
1575 'selectText' => _x( 'Select', 'Date Time Picker selectText', 'filter-everything' ),
1576 'amNames' => array(
1577 _x( 'AM', 'Date Time Picker amText', 'filter-everything' ),
1578 _x( 'A', 'Date Time Picker amTextShort', 'filter-everything' ),
1579 ),
1580 'pmNames' => array(
1581 _x( 'PM', 'Date Time Picker pmText', 'filter-everything' ),
1582 _x( 'P', 'Date Time Picker pmTextShort', 'filter-everything' ),
1583 ),
1584
1585 'monthNames' => array_values( $wp_locale->month ),
1586 'monthNamesShort' => array_values( $wp_locale->month_abbrev ),
1587 'dayNames' => array_values( $wp_locale->weekday ),
1588 'dayNamesMin' => array_values( $wp_locale->weekday_initial ),
1589 'dayNamesShort' => array_values( $wp_locale->weekday_abbrev ),
1590 'firstDay' => get_option( 'start_of_week' ),
1591 'applyText' => _x( 'Apply', 'Date Picker applyText', 'filter-everything' ),
1592 );
1593 }
1594
1595 /**
1596 * Include Main Front filters javascript
1597 */
1598 wp_enqueue_script('wpc-filter-everything', FLRT_PLUGIN_DIR_URL . 'assets/js/filter-everything'.$suffix.'.js', array('jquery', 'jquery-ui-slider', 'wc-jquery-ui-touchpunch'), $ver, true );
1599
1600 if( flrt_get_experimental_option('select2_dropdowns') === 'on' ){
1601 $select2ver = '4.1.0';
1602 $wpcUseSelect2 = 'yes';
1603 wp_enqueue_script( 'select2', FLRT_PLUGIN_DIR_URL . "assets/js/select2/select2".$suffix.".js", array('jquery'), $select2ver );
1604 wp_enqueue_style('select2', FLRT_PLUGIN_DIR_URL . "assets/css/select2/select2".$suffix.".css", '', $select2ver );
1605 }
1606
1607 $wpcFrontJsVariables['wpcUseSelect2'] = $wpcUseSelect2;
1608
1609 wp_localize_script( 'wpc-filter-everything', 'wpcFilterFront', $wpcFrontJsVariables );
1610
1611 if(!empty($applyButtonSets) && flrt_instant_recount()){
1612 $this->inlineScriptJsonData();
1613 }
1614
1615 unset( $filterSetService, $wpcFrontJsVariables );
1616 }
1617
1618 private function inlineScriptJsonData()
1619 {
1620 global $wp, $wp_rewrite, $flrt_json_data;
1621 if(!empty($flrt_json_data)){
1622 $wpc_filter_permalinks = get_option( 'wpc_filter_permalinks', [] );
1623 $permalinksTab = new PermalinksTab();
1624 $wpc_filter_permalinks_numbering = [];
1625 $wpc_filter_permalinks_entities = [];
1626 $permalinks_result = [];
1627 $includeEntities = ['post_meta_num', 'tax_numeric', 'post_date', 'post_meta_date'];
1628 $i = 1;
1629 foreach ($permalinksTab->movePostMetaNumInTheEnd($wpc_filter_permalinks) as $key => $value) {
1630 $parts = explode('#', $key);
1631 $permalinks_result[$parts[1]] = $value;
1632 if(in_array($parts[0], $includeEntities)){
1633 $wpc_filter_permalinks_entities[$parts[1]] = $parts[0];
1634 }
1635 $wpc_filter_permalinks_numbering[$i++] = $parts[1];
1636 }
1637 $flrt_json_data['wpcFilterPermalinks'] = $permalinks_result;
1638 $flrt_json_data['wpcFilterPermalinksNum'] = $wpc_filter_permalinks_numbering;
1639 $flrt_json_data['wpcFilterEntitiesWithoutSlug'] = $wpc_filter_permalinks_entities;
1640 // Pagination must never leak into the Apply URL base: applying a new
1641 // selection restarts the results from page 1 (deeper pages may not exist)
1642 $request = (string) $wp->request;
1643 $pagination_base = ! empty( $wp_rewrite->pagination_base ) ? $wp_rewrite->pagination_base : 'page';
1644 $request = preg_replace( '#(^|/)' . preg_quote( $pagination_base, '#' ) . '/\d+$#', '', $request );
1645
1646 $flrt_json_data['domain'] = trailingslashit(home_url(add_query_arg([], $request)));
1647 // The site's permalink convention (mirrors user_trailingslashit) —
1648 // the client-side URL builder must terminate paths the same way
1649 $flrt_json_data['trailingSlash'] = ( user_trailingslashit( 'wpc' ) === 'wpc/' );
1650 // On shops that list variations as standalone catalog items (XStore's
1651 // variable_products_detach) the client-side recount must keep displayed
1652 // counts in variation space — mirrors the server-side
1653 // wpc_from_variations_to_products counting gate.
1654 $flrt_json_data['variationsAsProducts'] = function_exists( 'flrt_variations_listed_as_products' )
1655 ? (bool) flrt_variations_listed_as_products() : false;
1656
1657 /**
1658 * The selection-independent bulk of the data (term post lists,
1659 * meta values, the set universe — ~95% of the payload, tens of
1660 * MB on large catalogs) is served as a hash-versioned STATIC
1661 * FILE: cacheable across pages and visits, fetched off the
1662 * critical path. Only the page-scoped part stays inline. When
1663 * the file cannot be written, everything falls back inline —
1664 * still as an inert JSON block, never as a JS object literal
1665 * (the full JS parser needed ~28 s for 32 MB; JSON.parse of the
1666 * same payload takes ~60 ms).
1667 */
1668 $blobUrl = $this->maybeWriteJsonBlobFile( $flrt_json_data );
1669 $pagePart = [];
1670
1671 if ( $blobUrl ) {
1672 foreach ( [ 'wpcFilterPermalinks', 'wpcFilterPermalinksNum', 'wpcFilterEntitiesWithoutSlug', 'domain', 'trailingSlash', 'variationsAsProducts' ] as $rootKey ) {
1673 if ( array_key_exists( $rootKey, $flrt_json_data ) ) {
1674 $pagePart[ $rootKey ] = $flrt_json_data[ $rootKey ];
1675 }
1676 }
1677 foreach ( $flrt_json_data as $key => $setData ) {
1678 if ( ! is_numeric( $key ) ) {
1679 continue;
1680 }
1681 $pagePart[ $key ] = [
1682 'filteredAllPostsIds' => isset( $setData['filteredAllPostsIds'] ) ? $setData['filteredAllPostsIds'] : [],
1683 'totalFilteredCount' => isset( $setData['totalFilteredCount'] ) ? $setData['totalFilteredCount'] : 0,
1684 ];
1685 }
1686 $pagePart['blobUrl'] = $blobUrl;
1687 $json = wp_json_encode( $pagePart );
1688 } else {
1689 $json = wp_json_encode( $flrt_json_data );
1690 }
1691
1692 add_action( 'wp_print_footer_scripts', function () use ( $json ) {
1693 echo '<script type="application/json" id="wpc-filter-json-data">' . $json . '</script>' . "\n";
1694 }, 1 );
1695
1696 $bootstrap = 'try { var wpcFilterJsonDataEl = document.getElementById("wpc-filter-json-data");';
1697 $bootstrap .= ' if ( wpcFilterJsonDataEl ) { var wpcFilterPagePart = JSON.parse( wpcFilterJsonDataEl.textContent );';
1698 $bootstrap .= ' wpcFilterJsonDataEl.parentNode.removeChild( wpcFilterJsonDataEl );';
1699 $bootstrap .= ' if ( wpcFilterPagePart.blobUrl ) {';
1700 $bootstrap .= ' window.wpcFilterJsonBlobUrl = wpcFilterPagePart.blobUrl;';
1701 $bootstrap .= ' window.wpcFilterJsonDataPromise = fetch( wpcFilterPagePart.blobUrl, { credentials: "same-origin" } )';
1702 $bootstrap .= '.then( function (r) { if ( ! r.ok ) { throw new Error( "HTTP " + r.status ); } return r.json(); } )';
1703 $bootstrap .= '.then( function (blob) { for ( var k in wpcFilterPagePart ) { if ( k === "blobUrl" ) { continue; }';
1704 $bootstrap .= ' if ( blob[k] && typeof blob[k] === "object" && ! Array.isArray( blob[k] ) && wpcFilterPagePart[k] && typeof wpcFilterPagePart[k] === "object" && ! Array.isArray( wpcFilterPagePart[k] ) ) {';
1705 $bootstrap .= ' for ( var kk in wpcFilterPagePart[k] ) { blob[k][kk] = wpcFilterPagePart[k][kk]; } } else { blob[k] = wpcFilterPagePart[k]; } }';
1706 $bootstrap .= ' window.wpcFilterJsonData = blob; return blob; } )';
1707 $bootstrap .= '.catch( function (e) { if ( window.console ) { console.error( "Filter Everything: filter data fetch failed", e ); } } );';
1708 $bootstrap .= ' } else { window.wpcFilterJsonData = wpcFilterPagePart; } }';
1709 $bootstrap .= ' } catch (e) { if ( window.console ) { console.error( "Filter Everything: filter data JSON parse failed", e ); } }';
1710
1711 wp_add_inline_script( 'wpc-filter-everything', $bootstrap, 'before' );
1712 }
1713 }
1714
1715 /**
1716 * Writes the selection-independent part of $flrt_json_data to a static
1717 * JSON file in uploads/flrt-cache/ and returns its URL (false = keep
1718 * everything inline). The file name carries a hash of the inputs that
1719 * define the content (plugin/cache versions, blob version bumped in
1720 * resetTransitions(), set group, language, universe), so URLs are
1721 * immutable: a stale file is impossible, only unused ones — those are
1722 * garbage-collected in resetTransitions().
1723 */
1724 private function maybeWriteJsonBlobFile( $flrt_json_data )
1725 {
1726 if ( apply_filters( 'wpc_json_static_file', true ) === false ) {
1727 return false;
1728 }
1729
1730 $setIds = array_filter( array_keys( $flrt_json_data ), 'is_numeric' );
1731 if ( empty( $setIds ) ) {
1732 return false;
1733 }
1734
1735 $firstSet = $flrt_json_data[ reset( $setIds ) ];
1736 $group = isset( $firstSet['relatedSets'] ) && $firstSet['relatedSets'] !== '' ? $firstSet['relatedSets'] : implode( '_', $setIds );
1737 $group = preg_replace( '/[^0-9_]/', '', (string) $group );
1738 $universe = ( isset( $firstSet['allPostsIds'] ) && is_array( $firstSet['allPostsIds'] ) ) ? array_keys( $firstSet['allPostsIds'] ) : [];
1739 $lang = defined( 'ICL_LANGUAGE_CODE' ) ? ICL_LANGUAGE_CODE : '';
1740
1741 // The filter CONFIGS (view, More/Less, parent, labels, ...) shape the
1742 // stored blob, but no hook is guaranteed to bump flrt_json_blob_ver for
1743 // every way a config can change — a missed bump used to freeze a stale
1744 // config into the immutable file forever (e.g. a filter switched to
1745 // More/Less kept more_less='no' in the blob and the recount hid every
1746 // term of it). Fingerprint the configs directly: a config change now
1747 // mints a new file name by construction. 'values' is page-scoped and
1748 // normalized out of the stored blob — it must not vary the name.
1749 $configFingerprint = [];
1750 foreach ( $setIds as $sid ) {
1751 if ( empty( $flrt_json_data[ $sid ]['allEntities'] ) ) {
1752 continue;
1753 }
1754 foreach ( $flrt_json_data[ $sid ]['allEntities'] as $eName => $entity ) {
1755 $filterConf = is_object( $entity )
1756 ? ( isset( $entity->filter ) ? (array) $entity->filter : [] )
1757 : ( isset( $entity['filter'] ) ? (array) $entity['filter'] : [] );
1758 unset( $filterConf['values'] );
1759 $configFingerprint[ $sid ][ $eName ] = $filterConf;
1760 }
1761 }
1762
1763 $hash = md5( implode( '|', [
1764 FLRT_PLUGIN_VER,
1765 FLRT_CACHE_FORMAT_SUFFIX,
1766 (string) get_option( 'flrt_json_blob_ver', 1 ),
1767 // The same DB serves different filter data in free and PRO (PRO-only
1768 // fields are gated at read time) — the modes must not share a file
1769 defined( 'FLRT_FILTERS_PRO' ) ? 'pro' : 'free',
1770 $group,
1771 $lang,
1772 count( $universe ),
1773 md5( implode( ',', $universe ) ),
1774 md5( (string) wp_json_encode( $configFingerprint ) ),
1775 ] ) );
1776
1777 $uploads = wp_upload_dir();
1778 if ( ! empty( $uploads['error'] ) ) {
1779 return false;
1780 }
1781
1782 $fileName = 'filters-' . $group . '-' . $hash . '.json';
1783 $dir = trailingslashit( $uploads['basedir'] ) . 'flrt-cache';
1784 $file = $dir . '/' . $fileName;
1785 $url = trailingslashit( $uploads['baseurl'] ) . 'flrt-cache/' . $fileName;
1786
1787 if ( file_exists( $file ) ) {
1788 return $url;
1789 }
1790
1791 if ( ! wp_mkdir_p( $dir ) ) {
1792 return false;
1793 }
1794
1795 if ( ! file_exists( $dir . '/index.php' ) ) {
1796 @file_put_contents( $dir . '/index.php', "<?php // Silence is golden.\n" );
1797 }
1798
1799 $encoded = wp_json_encode( $this->normalizeJsonBlob( $flrt_json_data ) );
1800 if ( ! $encoded ) {
1801 return false;
1802 }
1803
1804 // Write to a temp name first: a concurrent request must never fetch
1805 // a half-written multi-MB file
1806 $tmp = $file . '.' . wp_generate_password( 8, false ) . '.tmp';
1807 if ( @file_put_contents( $tmp, $encoded ) === false ) {
1808 return false;
1809 }
1810 if ( ! @rename( $tmp, $file ) ) {
1811 @unlink( $tmp );
1812 return false;
1813 }
1814
1815 return $url;
1816 }
1817
1818 /**
1819 * Strips/zeroes the page-scoped leaves so that every page of the set
1820 * group produces an identical blob: filteredAllPostsIds and the current
1821 * result counts live inline; cross_count, current range bounds and
1822 * per-bucket counts are recomputed client-side on the first recount and
1823 * are never read from the JSON before that.
1824 */
1825 private function normalizeJsonBlob( $flrt_json_data )
1826 {
1827 // Entities are objects — one encode/decode round-trip detaches a
1828 // plain array tree we can normalize without touching the originals
1829 $data = json_decode( wp_json_encode( $flrt_json_data ), true );
1830
1831 $rangeLeaves = [ 'min', 'max', 'from', 'to', 'time_from', 'time_to' ];
1832
1833 foreach ( $data as $key => &$setData ) {
1834 if ( ! is_numeric( $key ) ) {
1835 continue;
1836 }
1837
1838 unset( $setData['filteredAllPostsIds'], $setData['totalFilteredCount'] );
1839
1840 if ( empty( $setData['allEntities'] ) || ! is_array( $setData['allEntities'] ) ) {
1841 continue;
1842 }
1843
1844 foreach ( $setData['allEntities'] as &$entity ) {
1845 unset( $entity['filter']['values'] );
1846
1847 if ( array_key_exists( 'new_date_query', $entity ) ) {
1848 $entity['new_date_query'] = [];
1849 }
1850
1851 foreach ( $rangeLeaves as $leaf ) {
1852 if ( array_key_exists( $leaf, $entity ) ) {
1853 $entity[ $leaf ] = false;
1854 }
1855 }
1856
1857 if ( empty( $entity['items'] ) || ! is_array( $entity['items'] ) ) {
1858 continue;
1859 }
1860
1861 foreach ( $entity['items'] as &$item ) {
1862 if ( array_key_exists( 'cross_count', $item ) ) {
1863 $item['cross_count'] = 0;
1864 }
1865 if ( array_key_exists( 'wp_queried', $item ) ) {
1866 $item['wp_queried'] = false;
1867 }
1868 foreach ( $rangeLeaves as $leaf ) {
1869 if ( array_key_exists( $leaf, $item ) ) {
1870 $item[ $leaf ] = false;
1871 }
1872 }
1873 if ( ! empty( $item['range_list_input'] ) && is_array( $item['range_list_input'] ) ) {
1874 foreach ( $item['range_list_input'] as $bucket => $count ) {
1875 if ( is_numeric( $count ) ) {
1876 $item['range_list_input'][ $bucket ] = 0;
1877 }
1878 }
1879 }
1880 }
1881 unset( $item );
1882 }
1883 unset( $entity );
1884 }
1885 unset( $setData );
1886
1887 // These stay inline (page-scoped or tiny site config)
1888 unset( $data['domain'], $data['trailingSlash'], $data['variationsAsProducts'], $data['wpcFilterPermalinks'], $data['wpcFilterPermalinksNum'], $data['wpcFilterEntitiesWithoutSlug'] );
1889
1890 return $data;
1891 }
1892
1893 public function removeApplyButtonOrderField( &$set_settings_fields )
1894 {
1895 unset( $set_settings_fields['apply_button_menu_order'], $set_settings_fields['search_field_menu_order'] );
1896 }
1897
1898 public function handleFilterSetFieldsVisibility( $filterSetFields )
1899 {
1900 if( isset( $filterSetFields['use_apply_button']['value'] ) && $filterSetFields['use_apply_button']['value'] === 'yes' ) {
1901 $filterSetFields['apply_button_text']['additional_class'] = 'wpc-opened';
1902 $filterSetFields['reset_button_text']['additional_class'] = 'wpc-opened';
1903 }
1904
1905 if ( isset( $filterSetFields['use_search_field']['value'] ) && $filterSetFields['use_search_field']['value'] === 'yes' ) {
1906 $filterSetFields['search_field_placeholder']['additional_class'] = 'wpc-opened';
1907 $filterSetFields['search_field_label']['additional_class'] = 'wpc-opened';
1908 }
1909
1910 if (isset( $filterSetFields['horizontal_view']['value'] ) && $filterSetFields['horizontal_view']['value'] === 'yes'){
1911 $filterSetFields['horizontal_view_column']['additional_class'] = 'wpc-opened';
1912 }
1913
1914 return $filterSetFields;
1915 }
1916
1917 public function disableCacheProductsShortcode( $out )
1918 {
1919 $wpManager = Container::instance()->getWpManager();
1920 $is_filter_request = $wpManager->getQueryVar('wpc_is_filter_request');
1921 $thePost = Container::instance()->getThePost();
1922 $action = isset( $thePost['action'] ) ? $thePost['action'] : false;
1923
1924 // wpc_get_wp_queries - action to get WP_Queries on a page
1925 if( isset( $out['cache'] ) && ( $is_filter_request || $action === 'wpc_get_wp_queries' ) ){
1926 $out['cache'] = false;
1927 }
1928
1929 return $out;
1930 }
1931
1932 public function showCombinedFields( &$filter, $field_key )
1933 {
1934 $includeExclude = flrt_extract_vars( $filter, array('exclude', 'include') );
1935 if( $includeExclude ):
1936
1937 ?><tr class="<?php echo esc_attr( flrt_filter_row_class( $includeExclude['exclude'] ) ); ?>"<?php flrt_maybe_hide_row( $includeExclude['exclude'] ); ?>><?php
1938
1939 flrt_include_admin_view('filter-field-label', array(
1940 'field_key' => 'exclude',
1941 'attributes' => $includeExclude['exclude']
1942 )
1943 );
1944 ?>
1945 <td class="wpc-filter-field-td wpc-filter-field-include-exclude-td">
1946 <div class="wpc-filter-field-include-exclude-wrap">
1947 <div class="wpc-field-wrap wpc-field-exclude-wrap <?php if( isset( $includeExclude['exclude']['id'] ) ){ echo esc_attr( $includeExclude['exclude']['id'] ); } ?>-wrap">
1948 <?php echo flrt_render_input( $includeExclude['exclude'] ); // Already escaped in function ?>
1949 <?php do_action('wpc_after_filter_input', $includeExclude['exclude'] ); ?>
1950 </div>
1951 <div class="wpc-field-wrap wpc-field-include-wrap <?php if( isset( $includeExclude['include']['id'] ) ){ echo esc_attr( $includeExclude['include']['id'] ); } ?>-wrap">
1952 <?php echo flrt_render_input( $includeExclude['include'] ); // Already escaped in function ?>
1953 <?php do_action('wpc_after_filter_input', $includeExclude['include'] ); ?>
1954 </div>
1955 </div>
1956 </td>
1957 </tr><?php
1958
1959 endif;
1960
1961 if ( $field_key === 'min_num_label' ) {
1962 $min_max_num_labels = flrt_extract_vars( $filter, array( 'min_num_label', 'max_num_label' ) );
1963
1964 if( $min_max_num_labels ) :
1965 $min_field_id = $max_field_id = '';
1966
1967 if (isset($min_max_num_labels['min_num_label']['id'])) {
1968 $min_field_id = esc_attr($min_max_num_labels['min_num_label']['id']);
1969 }
1970 if( isset( $min_max_num_labels['max_num_label']['id'] ) ){
1971 $max_field_id = esc_attr( $min_max_num_labels['max_num_label']['id'] );
1972 }
1973
1974 if( isset($min_max_num_labels['min_num_label']['value']) ){
1975 $min_max_num_labels['min_num_label']['data-caret'] = mb_strlen( $min_max_num_labels['min_num_label']['value'] ) + 1;
1976 }
1977
1978 if( isset($min_max_num_labels['max_num_label']['value']) ){
1979 $min_max_num_labels['max_num_label']['data-caret'] = mb_strlen( $min_max_num_labels['max_num_label']['value'] ) + 1;
1980 }
1981
1982 ?><tr class="<?php echo esc_attr( flrt_filter_row_class( $min_max_num_labels['min_num_label'] ) ); ?>"<?php flrt_maybe_hide_row( $min_max_num_labels['min_num_label'] ); ?>><?php
1983
1984 flrt_include_admin_view('filter-field-label', array(
1985 'field_key' => 'min_num_label',
1986 'attributes' => $min_max_num_labels['min_num_label']
1987 )
1988 );
1989 ?>
1990 <td class="wpc-filter-field-td wpc-filter-field-min-max-labels-td">
1991 <div class="wpc-filter-field-min-max-labels-wrap">
1992 <div class="wpc-field-wrap wpc-field-min_num_label-wrap <?php echo $min_field_id; ?>-wrap">
1993 <?php echo flrt_render_input( $min_max_num_labels['min_num_label'] ); // Already escaped in function ?>
1994 <?php do_action('wpc_after_filter_input', $min_max_num_labels['min_num_label'] ); ?>
1995 </div>
1996 <div class="wpc-field-wrap wpc-field-max_num_label-wrap <?php echo $max_field_id; ?>-wrap">
1997 <?php echo flrt_render_input( $min_max_num_labels['max_num_label'] ); // Already escaped in function ?>
1998 <?php do_action('wpc_after_filter_input', $min_max_num_labels['max_num_label'] ); ?>
1999 </div>
2000 <p class="description"><?php echo wp_kses(
2001 sprintf( __('For example, "Price from <span class="wpc-variable-inserter" data-field="%s" title="Click to insert the variable">{value}</span> $" will be displayed as "Price from 150 $"', 'filter-everything'), $min_field_id ),
2002 array( 'span' => array(
2003 'class' => true,
2004 'data-field' => true,
2005 'title' => true,
2006 ) )
2007 ) ?></p>
2008 </div>
2009 </td>
2010 </tr><?php
2011
2012 endif;
2013 }
2014 }
2015
2016 public function addSearchArgsToWpQuery( $wp_query )
2017 {
2018 if ( isset( $_GET['srch'] ) && $_GET['srch'] ) {
2019 $keyword = filter_input( INPUT_GET, 'srch', FILTER_DEFAULT );
2020 $wp_query->set( 's', $keyword );
2021 }
2022
2023 return $wp_query;
2024 }
2025
2026 public function addSkuSearchSql( $search, $wp_query )
2027 {
2028 if( $wp_query->get('flrt_query_hash') || $wp_query->get('flrt_query_clone') ){
2029
2030 if ( $wp_query->get('wc_query') === 'product_query' || $wp_query->get('post_type') === 'product' /* || $wp_query->get('post_type') === 'product_variation' */ ) {
2031 global $wpdb;
2032
2033 $product_id = wc_get_product_id_by_sku( $wp_query->get('s') );
2034 if ( ! $product_id ) {
2035 return $search;
2036 }
2037
2038 $product = wc_get_product( $product_id );
2039 if ( $product->is_type( 'variation' ) ) {
2040 $product_id = $product->get_parent_id();
2041 }
2042
2043 $search = str_replace( 'AND (((', "AND (({$wpdb->posts}.ID IN (" . $product_id . ")) OR ((", $search );
2044 return $search;
2045 }
2046
2047 }
2048 return $search;
2049 }
2050
2051 public function postDateWhere( $where, $wp_query )
2052 { global $wpdb;
2053 $sql = [];
2054 $operator = '';
2055 $wpc_date_query = $wp_query->get( 'wpc_date_query' );
2056
2057 if( ! empty( $wpc_date_query ) && is_array( $wpc_date_query ) ) {
2058 foreach ( $wpc_date_query as $edge => $value ) {
2059 if( $edge === 'from' ) {
2060 $operator = '>=';
2061 } elseif ( $edge === 'to' ) {
2062 $operator = '<=';
2063 }
2064
2065 $sql[] = $wpdb->prepare( "AND {$wpdb->posts}.post_date {$operator} %s ", $value );
2066
2067 }
2068
2069 $where = implode( ' ', $sql ) . $where;
2070 }
2071
2072 return $where;
2073 }
2074
2075 public function showElementPickerPanel()
2076 {
2077 $getData = Container::instance()->getTheGet();
2078
2079 $html = sprintf(
2080 '<div id="wpc-choose-selector" data-set-id="%1$s">
2081 <div id="wpc-choose-id-css" class="wpc-choose-block">
2082 <span>%2$s</span>
2083 <input id="wpc-chooses-html-id">
2084 </div>
2085 <div id="wpc-choose-class-css" class="wpc-choose-block">
2086 <span>%3$s</span>
2087 <input id="wpc-chooses-html-css">
2088 </div>
2089 <div id="wpc-choose-empty" class="wpc-choose-block">
2090
2091 <span id="wpc-chooses-empty-element">%4$s</span>
2092 </div>
2093 <div id="wpc-choose-selector-buttons">
2094 <button id="wpc-choose-another-selector">%5$s</button>
2095 <button id="wpc-choose-save-btn">%6$s</button>
2096 </div>
2097 </div>',
2098 !(empty($getData['flrt_set_id'])) ? esc_attr($getData['flrt_set_id']) : 'global_posts_container',
2099 esc_html__( 'Chosen ID: ', 'filter-everything' ),
2100 esc_html__( 'Chosen Class: ', 'filter-everything' ),
2101 esc_html__( 'The element does not have any CSS selectors', 'filter-everything' ),
2102 esc_html__( 'Choose another', 'filter-everything' ),
2103 esc_html__( 'Save', 'filter-everything' )
2104 );
2105
2106 // Localize script with nonce and AJAX URL
2107 wp_localize_script('wpc-element-picker', 'wpcElementPickerData', array(
2108 'ajax_url' => admin_url('admin-ajax.php'),
2109 'error_saving_selector' => esc_html__('Error saving selector', 'filter-everything'),
2110 'no_element_selected' => esc_html__('Error: No element selected', 'filter-everything'),
2111 'failed_to_save_selector' => esc_html__('Error: Failed to save selector. Please try again.', 'filter-everything'),
2112 'saving' => esc_html__('Saving...', 'filter-everything'),
2113 'save' => esc_html__('Save', 'filter-everything'),
2114 ));
2115
2116 echo $html;
2117 }
2118 }
2119