PluginProbe
Filter Everything — WordPress & WooCommerce Filters / 1.9.7
Filter Everything — WordPress & WooCommerce Filters v1.9.7
1.9.7 1.9.6 1.9.5 1.9.4 1.9.3 1.9.2.2 1.9.2.1 trunk 1.2.1 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.4.1 1.4.4 1.4.5 1.4.8 1.4.9 1.5.0 1.5.1 1.6.0 1.6.1 1.6.2 All 52 releases
← All changes | src/Plugin.php +1298 -444 1.2.41.9.7 View file →
@@ -1,17 +1,21 @@
1 1 <?php
2 2
3 3 namespace FilterEverything\Filter;
4 4
5 -if ( ! defined('WPINC') ) {
6 - wp_die();
5 +if ( ! defined('ABSPATH') ) {
6 + exit;
7 7 }
8 8
9 +use FilterEverything\Filter\Pro\Api\ApiRequests;
9 10 use FilterEverything\Filter\Shortcodes;
10 11 use FilterEverything\Filter\Pro\PluginPro;
12 +use FilterEverything\Filter\DefaultSettings;
13 +use FilterEverything\Filter\PluginHelpers;
11 14
12 15 class Plugin
13 16 {
17 + use PluginHelpers;
14 18 private $wpManager;
15 19
16 20 public function __construct()
17 21 {
@@ -27,22 +31,47 @@
27 31
28 32 if( is_admin() ){
29 33 new Admin();
30 34 }
35 +
36 + // «Plugin notifications» channel. Not admin-only: its daily fetch runs in WP-Cron.
37 + new Messages();
31 38 }
32 39
33 40 public function register_hooks(){
34 - $postData = Container::instance()->getThePost();
41 + /**
42 + * string
43 + */
44 + $getData = Container::instance()->getTheGet();
35 45
36 46 if( ! is_admin() ){
37 - add_filter( 'do_parse_request', array( $this->wpManager, 'customParseRequest' ), 10, 3 );
47 + // Priority 1000: run AFTER third-party routers (e.g. Brain\Cortex inside
48 + // WP User Manager, priority 100). When Cortex ran after us, it received
49 + // our "false", found no route of its own and re-ran $wp->query_posts(),
50 + // wiping the filtered main query with an unfiltered one.
51 + add_filter( 'do_parse_request', array( $this->wpManager, 'customParseRequest' ), 1000, 3 );
52 +
38 53 add_action( 'parse_request', array( $this->wpManager, 'parseRequest' ) );
39 54 add_action( 'pre_get_posts', array( $this->wpManager, 'addFilterQueryToWpQuery' ), 9999 );
40 55
41 56 add_filter( 'posts_where', [ $this->wpManager, 'fixPostsWhereForSearch' ], 10, 2 );
57 + add_filter( 'post_limits_request', [ $this->wpManager, 'addSQlComment' ], 10, 2 );
42 58 add_action( 'pre_get_posts', array( $this->wpManager, 'fixSearchPostType' ), 9999 );
43 59
44 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();
45 74 }
46 75
47 76 add_action( 'body_class', array( $this, 'bodyClass' ) );
48 77
@@ -49,12 +78,13 @@
49 78 add_action( 'admin_print_styles', array( $this, 'includeAdminCss' ) );
50 79 add_action( 'admin_print_scripts', array( $this, 'includeAdminJs' ) );
51 80
52 81 // Do not include JS, if this page is admin or can't contain filters
53 - if( ! is_admin() ){
82 + if( ! is_admin() && ! is_login() ){
83 + add_action( 'wp_head', [ $this, 'inlineFrontCss' ] );
54 84 add_action( 'wp_print_styles', array( $this, 'includeFrontCss' ) );
55 85 add_action( 'wp_print_scripts', array( $this, 'includeFrontJs' ) );
56 - add_action( 'wp_print_styles', array( $this, 'inlineFrontCss' ) );
86 + add_action( 'wp_print_styles', array( $this, 'dynamicFrontCss' ) );
57 87 }
58 88
59 89 add_action( 'wp_footer', [$this, 'footerHtml'] );
60 90
@@ -63,13 +93,171 @@
63 93 add_filter( 'wpc_filter_set_default_fields', [ $this, 'addAvailableInProFields' ], 10, 2 );
64 94 add_filter( 'wpc_pre_save_set_fields', [ $this, 'unsetAvailableInProFields' ] );
65 95 }
66 96
97 + add_filter( 'wpc_filter_set_default_fields', [ $this, 'addSetTailFields' ], 20, 2 );
98 +
67 99 // Disable single search result redirect
68 100 add_filter( 'woocommerce_redirect_single_search_result', '__return_false' );
69 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 + }
70 165 }
71 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 +
72 260 public function prepareEntities()
73 261 {
74 262 $wpManager = Container::instance()->getWpManager();
75 263 $em = Container::instance()->getEntityManager();
@@ -88,56 +276,178 @@
88 276 foreach ( $fields as $key => $attributes ){
89 277 // Always insert regular 'old' field
90 278 $new_fields[$key] = $attributes;
91 279
92 - if( $key === 'post_name' ){
280 + if( $key === 'show_count' ){
93 281
94 - $new_fields['instead_post_name'] = array(
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(
95 349 'type' => 'Text',
96 - 'label' => esc_html__('Location', 'filter-everything'),
97 - 'name' => $filterSet->generateFieldName('instead_post_name'),
98 - 'id' => $filterSet->generateFieldId('instead_post_name'),
99 - 'class' => 'wpc-field-instead-of-location',
100 - 'default' => '',
101 - 'readonly' => 'readonly',
102 - 'placeholder' => esc_html__('Available in PRO', 'filter-everything'),
103 - 'instructions' => esc_html__('Specify page(s) where to show this Filter Set', 'filter-everything'),
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'),
104 356 'settings' => true
105 357 );
106 358
107 - $new_fields['instead_wp_filter_query'] = array(
359 + $new_fields['search_field_placeholder'] = array(
108 360 'type' => 'Text',
109 - 'label' => esc_html__('Query', 'filter-everything'),
110 - 'name' => $filterSet->generateFieldName('instead_wp_filter_query'),
111 - 'id' => $filterSet->generateFieldId('instead_wp_filter_query'),
112 - 'class' => 'wpc-field-instead-wp-filter-query',
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' ),
113 366 'default' => '',
114 - 'readonly' => 'readonly',
115 - 'placeholder' => esc_html__('Available in PRO', 'filter-everything'),
116 - 'instructions' => esc_html__('Select WP Query, that should be filtered', 'filter-everything'),
117 - 'tooltip' => esc_html__( 'The page selected in the Location field can contain multiple WP Queries associated with the desired Post type. They can be responsible for the work of widgets, posts and even nav menus. Please, try experimentally determining which WP Query is responsible for displaying the posts you want to filter.', 'filter-everything' ),
118 - 'settings' => true
367 + 'settings' => true
119 368 );
120 369
121 - }
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 + );
122 380
123 - if( $key === 'show_count' ){
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 + );
124 390
125 - $new_fields['instead_custom_posts_container'] = array(
391 + $new_fields['apply_button_text'] = array(
126 392 'type' => 'Text',
127 - 'label' => esc_html__('CSS ID or Class of Posts Container', 'filter-everything'),
128 - 'name' => $filterSet->generateFieldName('instead_custom_posts_container'),
129 - 'id' => $filterSet->generateFieldId('instead_custom_posts_container'),
130 - 'class' => 'wpc-field-instead-custom-posts-container',
131 - 'default' => '',
132 - 'readonly' => 'readonly',
133 - 'placeholder' => esc_html__('Available in PRO', 'filter-everything'),
134 - 'instructions' => esc_html__('Specify individual CSS selector of Posts Container for AJAX', 'filter-everything'),
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'),
135 398 'settings' => true
136 399 );
137 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 + );
138 449 }
139 -
140 450 }
141 451
142 452 return $new_fields;
143 453 }
@@ -158,417 +468,267 @@
158 468 echo sprintf('<meta name="robots" content="%s">', $content)."\r\n";
159 469 }
160 470 }
161 471
472 + /**
473 + * @see DynamicCss::printInlineCss() (kept as the wp_head callback name)
474 + */
162 475 public function inlineFrontCss()
163 476 {
164 - if( ! $this->wpManager->getQueryVar('allowed_filter_page') ) {
165 - return false;
477 + ( new DynamicCss() )->printInlineCss();
478 + }
479 +
480 + /**
481 + * @see DynamicCss::enqueueDynamicCss() (kept as the wp_print_styles callback name)
482 + */
483 + public function dynamicFrontCss()
484 + {
485 + ( new DynamicCss() )->enqueueDynamicCss();
486 + }
487 +
488 + public function bodyClass( $classes )
489 + {
490 + if( flrt_get_option('mobile_filter_settings') === 'show_open_close_button' ){
491 + $classes[] = 'wpc_show_open_close_button';
166 492 }
167 493
168 - $maxHeight = flrt_get_option('container_height');
169 - $color = flrt_get_option( 'primary_color', flrt_default_theme_color() );
170 - $move_to_top = flrt_get_option('try_move_to_top_sidebar');
171 - $wpc_mobile_width = flrt_get_mobile_width();
494 + if( flrt_is_filter_request() ){
495 + $classes[] = 'wpc_is_filter_request';
496 + }
172 497
173 - // Experimental Options
174 - $custom_css = flrt_get_experimental_option('custom_css');
175 - $use_loader = flrt_get_experimental_option('use_loader');
176 - $dark_overlay = flrt_get_experimental_option('dark_overlay');
177 - $styled_inputs = flrt_get_experimental_option('styled_inputs');
178 - $use_select2 = flrt_get_experimental_option('select2_dropdowns');
179 - $contrastColor = false;
498 + return $classes;
499 + }
180 500
181 - $css = '';
501 + public static function activate()
502 + {
503 + $defaultSettings = new DefaultSettings();
504 + if ( ! get_option('wpc_filter_settings') ) {
505 + add_option('wpc_filter_settings', $defaultSettings->wpc_filter_settings() );
506 + }
182 507
183 - if( $maxHeight ){
184 - $css .= '.wpc-filters-section:not(.wpc-filter-post_meta_num,.wpc-filter-layout-dropdown) .wpc-filter-content:not(.wpc-filter-has-hierarchy){
185 - max-height: '.$maxHeight.'px;
186 - overflow-y: auto;
187 - }'."\r\n";
188 - }
508 + if( ! get_option( 'wpc_filter_experimental' ) ){
509 + add_option('wpc_filter_experimental', $defaultSettings->wpc_filter_experimental() );
510 + }
189 511
190 - if( $color ){
191 - $contrastColor = flrt_get_contrast_color($color);
192 - $css .= '.ui-slider-horizontal .ui-slider-range{
193 - background-color: '.$color.';
194 - }
195 - '."\r\n";
512 + // Stamp the current version on fresh installs so 'update'-triggered
513 + // admin notices (see AdminNotices) fire only when an existing install is
514 + // later updated, never on a brand-new installation.
515 + if ( ! get_option( 'flrt_version' ) ) {
516 + add_option( 'flrt_version', FLRT_PLUGIN_VER );
517 + }
518 + // When the plugin arrived — Messages keeps promos quiet for a day after it.
519 + if ( ! get_option( Messages::INSTALLED_AT_OPTION ) ) {
520 + add_option( Messages::INSTALLED_AT_OPTION, time(), '', false );
521 + }
196 522
197 - $css .= '.wpc-spinner:after {
198 - border-top-color: '.$color.';
199 - }'."\r\n";
523 + // PRO: put the managed block back into a physical robots.txt right away
524 + // (deactivate() removed it) instead of waiting for the next trigger.
525 + if ( function_exists( 'flrt_robots_file_sync' ) ) {
526 + flrt_robots_file_sync( 'activate' );
527 + }
528 + }
200 529
201 - $css .= '.theme-Avada .wpc-filter-product_visibility .star-rating:before,
202 - .wpc-filter-product_visibility .star-rating span:before{
203 - color: '.$color.';
204 - }'."\r\n";
530 + /**
531 + * Deactivation: PRO takes its managed block out of the physical robots.txt
532 + * and stops the daily sync cron. Options and posts are kept for the
533 + * next activation — that is uninstall()'s job.
534 + */
535 + public static function deactivate()
536 + {
537 + if ( function_exists( 'flrt_robots_file_teardown' ) ) {
538 + flrt_robots_file_teardown();
539 + }
205 540
206 - $css .= '.theme-twentyfourteen .widget-area input.wpc-label-input:checked+label span.wpc-filter-label-wrapper,
207 - .widget-area input.wpc-label-input:checked+label span.wpc-filter-label-wrapper,
208 - .wpc-filters-widget-main-wrapper input.wpc-label-input:checked+label span.wpc-filter-label-wrapper{
209 - background-color: '.$color.';
210 - }'."\r\n";
541 + Messages::teardown();
542 + }
211 543
212 - $css .= '.widget-area input.wpc-label-input:checked+label,
213 - input.wpc-label-input:checked+label{
214 - border-color: '.$color.';
215 - }'."\r\n";
544 + /**
545 + * Clears all plugin data: options and posts
546 + */
547 + /**
548 + * Removes the static filter-data cache (uploads/flrt-cache) with its
549 + * folder. Defensive on purpose: the path must look exactly like our own
550 + * folder, only known file patterns are deleted, every step is
551 + * error-suppressed and any unexpected failure is swallowed — the cleanup
552 + * is best-effort and must never break the uninstall process.
553 + */
554 + private static function deleteJsonBlobCache()
555 + {
556 + try {
557 + $uploads = wp_upload_dir();
558 + if ( ! empty( $uploads['error'] ) || empty( $uploads['basedir'] ) ) {
559 + return;
560 + }
216 561
217 - $css .= '#secondary .wpc-filters-labels li.wpc-term-item input:checked+label a,
218 - #secondary .widget-area input.wpc-label-input:checked+label span.wpc-filter-label-wrapper,
219 - .widget-area input.wpc-label-input:checked+label span.wpc-filter-label-wrapper,
220 - .wpc-filters-widget-main-wrapper input.wpc-label-input:checked+label span.wpc-filter-label-wrapper,
221 - .widget-area .wpc-filters-labels li.wpc-term-item input:checked+label a,
222 - .wpc-filters-widget-main-wrapper .wpc-filters-labels li.wpc-term-item input:checked+label a,
223 - body .wpc-filters-labels li.wpc-term-item input:checked+label a,
224 - body#colibri .wpc-filters-labels li.wpc-term-item input:checked+label a,
225 - body#colibri .widget-area input.wpc-label-input:checked+label span.wpc-filter-label-wrapper{
226 - color: '.$contrastColor.';
227 - }'."\r\n";
562 + $dir = trailingslashit( $uploads['basedir'] ) . 'flrt-cache';
228 563
229 - $css .= '#secondary .wpc-filter-chips-list li.wpc-filter-chip:not(.wpc-chip-reset-all) a,
230 - .widget-area .widget .wpc-filter-chips-list li.wpc-filter-chip:not(.wpc-chip-reset-all) a,
231 - body .wpc-filter-chips-list li.wpc-filter-chip:not(.wpc-chip-reset-all) a,
232 - body#colibri .wpc-filter-chips-list li.wpc-filter-chip:not(.wpc-chip-reset-all) a,
233 - .wpc-filter-chips-list li.wpc-filter-chip:not(.wpc-chip-reset-all) a{
234 - background-color: '.$color.';
235 - color: '.$contrastColor.';
236 - border-color: '.$color.';
237 - }'."\r\n";
564 + if ( substr( $dir, -strlen( '/flrt-cache' ) ) !== '/flrt-cache' || ! is_dir( $dir ) ) {
565 + return;
566 + }
238 567
239 - $css .= '.widget-area .widget .wpc-filter-chips-list li.wpc-filter-chip:not(.wpc-chip-reset-all) a:hover,
240 - .wpc-filter-chips-list li.wpc-filter-chip:not(.wpc-chip-reset-all) a:hover{
241 - opacity: 0.9;
242 - }'."\r\n";
568 + $blob_files = glob( $dir . '/filters-*.json' );
569 + $tmp_files = glob( $dir . '/filters-*.json.*.tmp' );
243 570
244 - $css .= '.widget-area .widget .wpc-filter-chips-list li.wpc-filter-chip:not(.wpc-chip-reset-all) a:active,
245 - .wpc-filter-chips-list li.wpc-filter-chip:not(.wpc-chip-reset-all) a:active{
246 - opacity: 0.75;
247 - }'."\r\n";
571 + $to_delete = array_merge(
572 + is_array( $blob_files ) ? $blob_files : [],
573 + is_array( $tmp_files ) ? $tmp_files : []
574 + );
575 + $to_delete[] = $dir . '/index.php';
248 576
249 - $css .= '.wpc-filter-chips-list a:hover .wpc-chip-remove-icon,
250 - .widget-area .widget .wpc-filter-chips-list a:hover .wpc-chip-remove-icon{
251 - color: '.$contrastColor.';
252 - }'."\r\n";
577 + foreach ( $to_delete as $file ) {
578 + if ( is_file( $file ) ) {
579 + @unlink( $file );
580 + }
581 + }
253 582
254 - $css .= '.star-rating span,
255 - .star-rating span:before{
256 - color: '.$color.';
257 - }'."\r\n";
583 + // Removed only when empty — anything unexpected inside stays put
584 + @rmdir( $dir );
585 + } catch ( \Throwable $e ) {
586 + // Best-effort cleanup: never break the uninstall
587 + }
588 + }
258 589
259 - $css .= '@media screen and (min-width: '.$wpc_mobile_width.'px) {'."\r\n";
260 - $css .= '.theme-twentyfourteen .widget-area input.wpc-label-input+label:hover span.wpc-filter-label-wrapper,
261 - .widget-area input.wpc-label-input+label:hover span.wpc-filter-label-wrapper,
262 - .wpc-filters-widget-main-wrapper input.wpc-label-input+label:hover span.wpc-filter-label-wrapper{
263 - color: '.$contrastColor.';
264 - background-color: '.$color.';
265 - }'."\r\n";
266 - $css .= '#secondary .wpc-filters-labels li.wpc-term-item input+label:hover a,
267 - body .wpc-filters-labels li.wpc-term-item input+label:hover a,
268 - body#colibri .wpc-filters-labels li.wpc-term-item input+label:hover a,
269 - .widget-area .wpc-filters-labels li.wpc-term-item input+label:hover a,
270 - .wpc-filters-widget-main-wrapper .wpc-filters-labels li.wpc-term-item input+label:hover a{
271 - color: '.$contrastColor.';
272 - }'."\r\n";
273 - $css .= '.widget-area input.wpc-label-input+label:hover,
274 - .wpc-filters-widget-main-wrapper input.wpc-label-input+label:hover{
275 - border-color: '.$color.';
276 - }'."\r\n";
277 - $css .= '}'."\r\n";
278 - }
279 - if( $styled_inputs ){
280 - $styled_color = $color ? $color : '#0570e2';
281 - $contrastColor = $contrastColor ? $contrastColor : flrt_get_contrast_color($styled_color);
590 + public static function uninstall( $force = false )
591 + {
592 + $allow_to_delete = true;
282 593
283 - $css .= '.wpc-filters-widget-main-wrapper input[type=checkbox],
284 - .wpc-filters-widget-main-wrapper input[type=radio]{
285 - -webkit-appearance: none;
286 - -moz-appearance: none;
287 - position: relative;
288 - width: 20px;
289 - height: 20px;
290 - border: 2px solid #bdbdbd;
291 - border: 2px solid #ccd0dc;
292 - background: #ffffff;
293 - border-radius: 5px;
294 - min-width: 20px;
295 - }
296 - .wpc-filters-widget-main-wrapper input[type=checkbox]:after {
297 - content: "";
298 - opacity: 0;
299 - display: block;
300 - left: 5px;
301 - top: 2px;
302 - position: absolute;
303 - width: 4px;
304 - height: 8px;
305 - border: 2px solid '.$styled_color.';
306 - border-top: 0;
307 - border-left: 0;
308 - transform: rotate(45deg);
309 - box-sizing: content-box;
310 - }
311 - .wpc-filters-widget-main-wrapper input[type=radio]:after {
312 - content: "";
313 - opacity: 0;
314 - display: block;
315 - left: 4px;
316 - top: 4px;
317 - position: absolute;
318 - width: 8px;
319 - height: 8px;
320 - border-radius: 50%;
321 - background: '.$styled_color.';
322 - box-sizing: content-box;
323 - }
324 - .wpc-filters-widget-main-wrapper input[type=radio]:checked,
325 - .wpc-filters-widget-main-wrapper input[type=checkbox]:checked {
326 - border-color: '.$styled_color.';
327 - }
328 - .wpc-filters-widget-main-wrapper .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=checkbox]:after,
329 - .wpc-filters-widget-main-wrapper .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=checkbox],
330 - .wpc-filters-widget-main-wrapper .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=radio]{
331 - border-color: #d8d8d8;
332 - }
333 - .wpc-filters-widget-main-wrapper .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=radio]:after{
334 - background-color: #d8d8d8;
335 - }
336 - .wpc-filters-widget-main-wrapper input[type=radio]:checked:after,
337 - .wpc-filters-widget-main-wrapper input[type=checkbox]:checked:after {
338 - opacity: 1;
339 - }
340 - .wpc-filters-widget-main-wrapper input[type=radio] {
341 - border-radius: 50%;
342 - }
343 -
344 - @media screen and (min-width: 768px) {
345 - .wpc-filters-widget-main-wrapper input[type=radio]:hover,
346 - .wpc-filters-widget-main-wrapper input[type=checkbox]:hover{
347 - border-color: '.$styled_color.';
348 - }
349 - .wpc-filters-widget-main-wrapper .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=radio]:hover,
350 - .wpc-filters-widget-main-wrapper .wpc-term-count-0:not(.wpc-has-not-empty-children) input[type=checkbox]:hover{
351 - border-color: #c3c3c3;
352 - }
353 - }';
594 + if( is_multisite() ){
595 + $active_plugins = get_site_option('active_sitewide_plugins');
596 + if( is_array( $active_plugins ) ){
597 + $active_plugins = array_keys( $active_plugins );
354 598 }
599 + }else{
600 + $active_plugins = apply_filters('active_plugins', get_option('active_plugins'));
601 + }
355 602
356 - if( $use_select2 ){
357 - $styled_color = $color ? $color : '#0570e2';
358 - $contrastColor = $contrastColor ? $contrastColor : flrt_get_contrast_color($styled_color);
603 + $fe_active = [];
604 + $to_compare = [
605 + 'filter-everything-pro/filter-everything.php',
606 + 'filter-everything/filter-everything.php'
607 + ];
359 608
360 - $css .= '.wpc-filter-content select{
361 - padding: 6px 12px 6px 14px;
362 - border-color: #ccd0dc;
363 - border-radius: 3px;
364 - color: inherit;
365 - -webkit-appearance: none;
366 - }
367 - .select2-container--default .wpc-filter-everything-dropdown .select2-results__option--highlighted[aria-selected],
368 - .select2-container--default .wpc-filter-everything-dropdown .select2-results__option--highlighted[data-selected]{
369 - background-color: '.$styled_color.';
370 - color: '.$contrastColor.';
371 - }
372 - ';
609 + if( ! empty( $active_plugins ) ){
610 + foreach ( $active_plugins as $plugin_path ){
611 + if( in_array( $plugin_path, $to_compare ) ){
612 + $fe_active[] = $plugin_path;
613 + }
373 614 }
615 + }
374 616
375 - if( $move_to_top ){
376 - $css .= '@media screen and (max-width: '.$wpc_mobile_width.'px) {'."\r\n";
617 + if( count( $fe_active ) > 0 ){
618 + $allow_to_delete = false;
619 + }
377 620
378 - $css .= 'body #main,
379 - body #content .col-full,
380 - .woocommerce-page .content-has-sidebar,
381 - .woocommerce-page .has-one-sidebar,
382 - .woocommerce-page #main-sidebar-container,
383 - .woocommerce-page .theme-page-wrapper,
384 - .woocommerce-page #content-area,
385 - .theme-jevelin.woocommerce-page .woocomerce-styling,
386 - .woocommerce-page .content_wrapper,
387 - .woocommerce-page #col-mask,
388 - body #main-content .content-area {
389 - -js-display: flex;
390 - display: -webkit-box;
391 - display: -webkit-flex;
392 - display: -moz-box;
393 - display: -ms-flexbox;
394 - display: flex;
395 - -webkit-box-orient: vertical;
396 - -moz-box-orient: vertical;
397 - -webkit-flex-direction: column;
398 - -ms-flex-direction: column;
399 - flex-direction: column;
400 - }
401 - body #primary,
402 - .woocommerce-page .has-one-sidebar > section,
403 - .woocommerce-page .theme-content,
404 - .woocommerce-page #left-area,
405 - .woocommerce-page #content,
406 - .woocommerce-page .sections_group,
407 - .woocommerce-page .content-box,
408 - body #main-sidebar-container #main {
409 - -webkit-box-ordinal-group: 2;
410 - -moz-box-ordinal-group: 2;
411 - -ms-flex-order: 2;
412 - -webkit-order: 2;
413 - order: 2;
414 - }
415 - body #secondary,
416 - .woocommerce-page .has-one-sidebar > aside,
417 - body aside#mk-sidebar,
418 - .woocommerce-page #sidebar,
419 - .woocommerce-page .sidebar,
420 - body #main-sidebar-container #sidebar {
421 - -webkit-box-ordinal-group: 1;
422 - -moz-box-ordinal-group: 1;
423 - -ms-flex-order: 1;
424 - -webkit-order: 1;
425 - order: 1;
426 - }
427 -
428 - /*second method first method solve issue theme specific*/
429 - .woocommerce-page:not(.single,.page) .btWithSidebar.btSidebarLeft .btContentHolder,
430 - body .theme-generatepress.woocommerce #content {
431 - display: table;
432 - }
433 - body .btContent,
434 - body .theme-generatepress.woocommerce #primary {
435 - display: table-footer-group;
436 - }
437 - body .btSidebar,
438 - body .theme-generatepress.woocommerce #left-sidebar {
439 - display: table-header-group;
440 - }'."\r\n";
441 - $css .= '}'."\r\n";
442 - }
621 + if ( $force == true ) {
622 + $allow_to_delete = true;
623 + }
443 624
444 - if( $use_loader ){
445 - $css .= '@media screen and (min-width: '.$wpc_mobile_width.'px) {'."\r\n";
446 - $css .= 'html.is-active .wpc-spinner{
447 - display: block;
448 - }';
449 - $css .= '}'."\r\n";
625 + if( $allow_to_delete ){
626 +
627 + if ( function_exists( 'flrt_robots_file_teardown' ) ) {
628 + flrt_robots_file_teardown();
450 629 }
451 630
452 - if( $dark_overlay ){
453 - $css .= '@media screen and (min-width: '.$wpc_mobile_width.'px) {'."\r\n";
454 - $css .= 'html.is-active .wpc-filters-overlay{
455 - opacity: .15;
456 - background: #000000;
457 - }';
458 - $css .= '}'."\r\n";
459 - }
631 + $options = [
632 + 'wpc_robots_file_state',
633 + 'wpc_filter_settings',
634 + 'wpc_indexing_deep_settings',
635 + 'wpc_filter_permalinks',
636 + 'wpc_seo_rules_settings',
637 + 'wpc_xml_write_date',
638 + 'wpc_filter_experimental',
639 + 'flrt_json_blob_ver',
640 + 'widget_wpc_filters_widget',
641 + 'widget_wpc_sorting_widget',
642 + 'widget_wpc_chips_widget',
643 + ];
460 644
461 - if( $custom_css ){
462 - $css .= $custom_css."\r\n";
645 + foreach ( $options as $option_name ){
646 + delete_option( $option_name );
463 647 }
464 648
649 + self::deleteJsonBlobCache();
465 650
466 - $wp_upload_dir = wp_upload_dir();
467 - $upload_dir_baseurl = $wp_upload_dir['baseurl'];
468 - $upload_dir_basepath = $wp_upload_dir['basedir'];
651 + // «Plugin notifications»: cron event, cached feed, consent, per-user dismissals
652 + Messages::uninstall();
653 + if ( class_exists( __NAMESPACE__ . '\\ReviewRequest' ) ) {
654 + ReviewRequest::uninstall();
655 + }
469 656
470 - $cache_dir = $upload_dir_basepath . '/cache/filter-everything/';
657 + // Deactivate and erase license if exists
658 + if ( defined( 'FLRT_FILTERS_PRO' ) && FLRT_FILTERS_PRO ) {
471 659
472 - if ( ! file_exists( $cache_dir ) ) {
473 - mkdir($cache_dir, 0777, true);
474 - chmod($cache_dir, 0777);
475 - }
660 + wpc_clear_folder(FLRT_XML_PATH);
476 661
477 - $filename = md5( $css ) . '.css';
662 + $to_send = false;
663 + $saved_value = get_option( FLRT_LICENSE_KEY );
478 664
479 - $fileurl = $upload_dir_baseurl .'/cache/filter-everything/' . $filename;
480 - $filepath = $cache_dir . $filename;
665 + if( isset( $saved_value['license_key'] ) ) {
666 + $saved_value_arr = maybe_unserialize( base64_decode( $saved_value['license_key'] ) );
667 + $to_send = $saved_value_arr;
668 + }
481 669
482 - if ( $css !== '' ) {
483 - if ( ! file_exists( $filepath ) ) {
484 - file_put_contents( $filepath, $css );
485 - }
670 + if ( is_array( $to_send ) ){
671 + $to_send['home_url'] = home_url();
486 672
487 - if( file_exists( $filepath ) ){
488 - wp_enqueue_style('wpc-filter-everything-custom', $fileurl );
489 - }
490 - }
673 + // Make data suitable to send as GET variables
674 + $to_send = array_map( 'urlencode', $to_send );
491 675
492 - }
676 + if ( isset( $saved_value_arr['id'] ) && $saved_value_arr['id'] ) {
677 + $apiRequest = new ApiRequests();
678 + $result = $apiRequest->sendRequest('DELETE', 'license', $to_send );
493 679
494 - public function bodyClass( $classes )
495 - {
496 - if( flrt_get_option('show_open_close_button') === 'on' ){
497 - $classes[] = 'wpc_show_open_close_button';
498 - }
680 + // If license was deactivated, we have to refresh updates info
681 + delete_transient(FLRT_VERSION_TRANSIENT );
682 + delete_option( FLRT_LICENSE_KEY );
683 + }
684 + }
499 685
500 - return $classes;
501 - }
686 + }
502 687
503 - public static function activate()
504 - {
505 - if ( ! get_option('wpc_filter_settings') ) {
506 - $defaultOptions = array(
507 - 'primary_color' => '#0570e2',
508 - 'container_height' => '350',
509 - 'show_open_close_button' => '',
510 - 'show_terms_in_content' => 'on',
511 - 'widget_debug_messages' => 'on'
688 + $postTypes = array(
689 + FLRT_FILTERS_SET_POST_TYPE,
690 + FLRT_FILTERS_POST_TYPE
512 691 );
513 692
514 - // PRO default options
515 - if( defined('FLRT_FILTERS_PRO') && FLRT_FILTERS_PRO ){
516 - $defaultOptions['show_bottom_widget'] = 'on';
693 + if( defined( 'FLRT_SEO_RULES_POST_TYPE' ) ){
694 + $postTypes[] = FLRT_SEO_RULES_POST_TYPE;
517 695 }
518 696
519 - add_option('wpc_filter_settings', $defaultOptions );
520 - }
521 -
522 - if( ! get_option( 'wpc_filter_experimental' ) ){
523 -
524 - $defaultExperimentalOptions = array(
525 - 'use_loader' => 'on',
526 - 'use_wait_cursor' => 'on',
527 - 'dark_overlay' => 'on',
528 - 'auto_scroll' => '',
529 - 'styled_inputs' => '',
530 - 'select2_dropdowns' => ''
697 + $filterPosts = new \WP_Query(
698 + array(
699 + 'posts_per_page' => -1,
700 + 'post_status' => array('any'),
701 + 'post_type' => $postTypes,
702 + 'fields' => 'ids',
703 + 'suppress_filters' => true
704 + )
531 705 );
532 706
533 - add_option('wpc_filter_experimental', $defaultExperimentalOptions );
534 - }
535 - }
707 + $filterPostsIds = $filterPosts->get_posts();
536 708
537 - /**
538 - * Clears all plugin data: options and posts
539 - */
540 - public static function uninstall()
541 - {
542 - delete_option('wpc_filter_settings' );
543 - delete_option('wpc_indexing_deep_settings' );
544 - delete_option('wpc_filter_permalinks' );
545 - delete_option('wpc_seo_rules_settings' );
546 - delete_option('wpc_filter_experimental' );
709 + if( ! empty( $filterPostsIds ) ){
710 + foreach ($filterPostsIds as $post_id) {
711 + wp_delete_post( $post_id, true );
712 + }
713 + }
547 714
548 - $postTypes = array(
549 - FLRT_FILTERS_SET_POST_TYPE,
550 - FLRT_FILTERS_POST_TYPE
551 - );
715 + $filterTrashPosts = new \WP_Query(
716 + array(
717 + 'posts_per_page' => -1,
718 + 'post_status' => array('trash'),
719 + 'post_type' => $postTypes,
720 + 'fields' => 'ids',
721 + 'suppress_filters' => true
722 + )
723 + );
552 724
553 - if( defined( 'FLRT_SEO_RULES_POST_TYPE' ) ){
554 - $postTypes[] = FLRT_SEO_RULES_POST_TYPE;
555 - }
725 + $filterTrashPostsIds = $filterTrashPosts->get_posts();
556 726
557 - $filterPosts = new \WP_Query(
558 - array(
559 - 'posts_per_page' => -1,
560 - 'post_status' => array('any'),
561 - 'post_type' => $postTypes,
562 - 'fields' => 'ids'
563 - )
564 - );
565 -
566 - $filterPostsIds = $filterPosts->get_posts();
567 -
568 - if( ! empty( $filterPostsIds ) ){
569 - foreach ($filterPostsIds as $post_id) {
570 - wp_delete_post( $post_id, true );
727 + if( ! empty( $filterTrashPostsIds ) ){
728 + foreach ($filterTrashPostsIds as $post_id) {
729 + wp_delete_post( $post_id, true );
730 + }
571 731 }
572 732 }
573 733 }
574 734
@@ -578,11 +738,26 @@
578 738 }
579 739
580 740 public function includeAdminCss()
581 741 {
582 - $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
583 - $ver = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? rand(0, 1000) : FLRT_PLUGIN_VER;
584 - wp_enqueue_style( 'wpc-filter-everything-admin', FLRT_PLUGIN_URL . 'assets/css/filter-everything-admin'.$suffix.'.css', ['wp-color-picker'], $ver );
742 + $screen = get_current_screen();
743 + if ( ! is_null( $screen ) && property_exists( $screen, 'base' ) ) {
744 + $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
745 + $ver = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? rand(0, 1000) : FLRT_PLUGIN_VER;
746 + $is_about_pro_tab = isset($_GET['tab']) && $_GET['tab'] === 'aboutpro';
747 +
748 + if ( in_array( $screen->base, [ 'edit', 'post', 'edit-tags', 'term' ] ) || ( strpos( $screen->base, 'filters-settings' ) !== false ) ) {
749 + wp_enqueue_style( 'wpc-filter-everything-admin', FLRT_PLUGIN_DIR_URL . 'assets/css/filter-everything-admin'.$suffix.'.css', ['wp-color-picker'], $ver );
750 + if($is_about_pro_tab){
751 + wp_enqueue_style( 'wpc-filter-everything-pro-benefits', FLRT_PLUGIN_DIR_URL . 'assets/css/pro-benefits-page'.$suffix.'.css', ['wpc-filter-everything-admin'], $ver );
752 + }
753 + }
754 +
755 + if ( $screen->base === 'widgets' ) {
756 + wp_enqueue_style('wpc-widgets', FLRT_PLUGIN_DIR_URL . 'assets/css/wpc-widgets' . $suffix . '.css', [], $ver );
757 + }
758 +
759 + }
585 760 }
586 761
587 762 public function includeAdminJs()
588 763 {
@@ -587,78 +762,160 @@
587 762 public function includeAdminJs()
588 763 {
589 764 $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
590 765 $ver = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? rand(0, 1000) : FLRT_PLUGIN_VER;
766 + $select2ver = '4.1.0';
591 767
592 - wp_register_script( 'jquery-tiptip', FLRT_PLUGIN_URL . 'assets/js/jquery-tiptip/jquery.tipTip' . $suffix . '.js', array( 'jquery' ), $ver, true );
768 + wp_register_script( 'jquery-tiptip', FLRT_PLUGIN_DIR_URL . 'assets/js/jquery-tiptip/jquery.tipTip' . $suffix . '.js', array( 'jquery' ), $ver, true );
593 769 wp_enqueue_script('jquery-tiptip');
770 + 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 );
594 771
595 - wp_enqueue_script('wpc-filters-admin', FLRT_PLUGIN_URL . 'assets/js/wpc-filters-common-admin' . $suffix . '.js', array('jquery', 'jquery-ui-sortable', 'wp-color-picker'), $ver );
596 -
597 772 $l10n = array(
598 - 'prefixesOrderAvailableInPro' => __( 'Editing the order of URL prefixes is available in PRO version', 'filter-everything' ),
773 + 'prefixesOrderAvailableInPro' => esc_html__( 'Editing the order of URL prefixes is available in the PRO version', 'filter-everything' ),
774 + 'chipsPlaceholder' => esc_html__( 'Select or enter hooks', 'filter-everything' ),
775 + 'colorSwatchesPlaceholder' => esc_html__( 'Click to select taxonomies', 'filter-everything' ),
776 + 'chooseElementHelpText' => esc_html__( 'Hover over the Results container and click to select it', 'filter-everything' ),
599 777 );
600 778 wp_localize_script( 'wpc-filters-admin', 'wpcFiltersAdminCommon', $l10n );
779 +
780 + wp_enqueue_script( 'select2', FLRT_PLUGIN_DIR_URL . "assets/js/select2/select2".$suffix.".js", array('jquery'), $select2ver );
781 + wp_enqueue_style('select2', FLRT_PLUGIN_DIR_URL . "assets/css/select2/select2".$suffix.".css", '', $select2ver );
782 +
783 + $screen = get_current_screen();
784 +
785 + if( ! is_null( $screen ) && property_exists( $screen, 'base' ) && $screen->base === 'widgets' ){
786 + wp_enqueue_script('wpc-widgets', FLRT_PLUGIN_DIR_URL . 'assets/js/wpc-widgets' . $suffix . '.js', array('jquery'), $ver );
787 + $l10n = array(
788 + 'wpcItemNum' => esc_html__( 'Item #', 'filter-everything')
789 + );
790 + wp_localize_script( 'wpc-widgets', 'wpcWidgets', $l10n );
791 + }
601 792 }
602 793
603 794 public function includeFrontCss()
604 795 {
605 - if( ! $this->wpManager->getQueryVar('allowed_filter_page') ) {
606 - if( flrt_get_option( 'widget_debug_messages' ) !== 'on' ){
607 - return false;
608 - }
796 + $suffix = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '.min';
797 + $ver = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? rand(0, 1000) : FLRT_PLUGIN_VER;
798 + /**
799 + * string
800 + */
801 + $getData = Container::instance()->getTheGet();
802 + if( isset( $getData['fl_builder'] ) ){
803 + wp_enqueue_style('wpc-widgets', FLRT_PLUGIN_DIR_URL . 'assets/css/wpc-widgets' . $suffix . '.css', [], $ver );
609 804 }
805 + if ( isset( $getData['flrt_get_html_selector'] ) && $getData['flrt_get_html_selector'] === '1' && current_user_can('manage_options') ) {
806 + wp_enqueue_style('wpc-element-picker', FLRT_PLUGIN_DIR_URL . 'assets/css/element-picker' . $suffix . '.css', [], $ver );
807 + }
610 808
611 - $suffix = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? '' : '.min';
612 - $ver = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? rand(0, 1000) : FLRT_PLUGIN_VER;
613 - wp_enqueue_style('wpc-filter-everything', FLRT_PLUGIN_URL . 'assets/css/filter-everything' . $suffix . '.css', [], $ver);
809 + // Do not include plugin CSS if there are no Filter Sets on the page
810 + $sets = $this->wpManager->getQueryVar('wpc_page_related_set_ids', []);
811 + if( empty( $sets ) ) {
812 + return false;
813 + }
614 814
815 + wp_enqueue_style('wpc-filter-everything', FLRT_PLUGIN_DIR_URL . 'assets/css/filter-everything' . $suffix . '.css', [], $ver);
615 816 }
616 817
617 818 public function footerHtml()
618 819 {
619 - if( $this->wpManager->getQueryVar( 'allowed_filter_page' ) ){
620 - echo '<div class="wpc-filters-overlay"></div>'."\r\n";
621 - }
820 + echo '<div class="wpc-filters-overlay"></div>'."\r\n";
622 821 }
623 822
624 823 public function includeFrontJs()
625 824 {
626 - if( ! $this->wpManager->getQueryVar('allowed_filter_page') ) {
627 - if( flrt_get_option( 'widget_debug_messages' ) !== 'on' ){
628 - return false;
825 + $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
826 + $ver = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? rand(0, 1000) : FLRT_PLUGIN_VER;
827 + /**
828 + * string
829 + */
830 + $getData = Container::instance()->getTheGet();
831 + $em = Container::instance()->getEntityManager();
832 +
833 + $wpcFrontJsVariables = [];
834 +
835 + if ( isset( $getData['flrt_get_html_selector'] ) && $getData['flrt_get_html_selector'] === '1' && current_user_can('manage_options') ) {
836 + wp_enqueue_script( 'wpc-element-picker-bundle', FLRT_PLUGIN_DIR_URL . 'assets/js/js-element-picker/element-picker.bundle' . $suffix . '.js', array( 'jquery' ), $ver, true );
837 + wp_enqueue_script( 'wpc-element-picker', FLRT_PLUGIN_DIR_URL . 'assets/js/element-picker' . $suffix . '.js', array( 'jquery', 'wpc-element-picker-bundle' ), $ver, true );
838 + add_action( 'wp_footer', [$this, 'showElementPickerPanel'] );
839 + }
840 +
841 + if( isset( $getData['fl_builder'] ) ){
842 + wp_enqueue_script('wpc-widgets', FLRT_PLUGIN_DIR_URL . 'assets/js/wpc-widgets' . $suffix . '.js', array('jquery'), $ver );
843 + $l10n = array(
844 + 'wpcItemNum' => esc_html__( 'Item #', 'filter-everything' )
845 + );
846 + wp_localize_script( 'wpc-widgets', 'wpcWidgets', $l10n );
847 + }
848 +
849 + // Do not include plugin JS if there are no Filter Sets on the page
850 + $sets = $this->wpManager->getQueryVar( 'wpc_page_related_set_ids', [] );
851 + $related_filters = $em->getSetsRelatedFilters( $sets );
852 + $date_filters = [];
853 + $include_timepicker = false;
854 +
855 + if ( ! empty( $related_filters ) ) {
856 + foreach ( $related_filters as $filter ) {
857 + if ( in_array( $filter['entity'], [ 'post_date', 'post_meta_date' ] ) ) {
858 +
859 + $date_time = flrt_split_date_time( $filter['date_format'] );
860 +
861 + global $wp_locale;
862 + $date_filters[ $filter['ID'] ] = [
863 + 'date_type' => $filter['date_type'],
864 + 'date_format' => flrt_convert_date_to_js( $date_time['date'] ),
865 + 'time_format' => flrt_convert_time_to_js( $date_time['time'] ),
866 + ];
867 +
868 + if ( in_array( $filter['date_type'], [ 'time', 'datetime' ] ) ) {
869 + $include_timepicker = true;
870 + }
871 +
872 + }
629 873 }
630 874 }
631 875
876 + /**
877 + * Do not continue and do not include any assets
878 + * if this page does not contain Filter Sets
879 + */
880 + if ( empty( $sets ) ) {
881 + return false;
882 + }
883 +
632 884 $showBottomWidget = 'no';
633 885 $ajaxEnabled = false;
634 886 $autoScroll = false;
635 887 $waitCursor = false;
636 888 $wpcUseSelect2 = false;
889 + $wpcPopupCompatMode = false;
637 890 $autoScrollOffset = apply_filters( 'wpc_auto_scroll_offset', 150 );
638 891 $wpc_mobile_width = flrt_get_mobile_width();
639 892 $per_page = [];
640 - $sets = $this->wpManager->getQueryVar('wpc_page_related_set_ids');
893 + $applyButtonSets = [];
894 + $queryOnThePageSets = [];
895 + $filterSetService = Container::instance()->getFilterSetService();
641 896
642 - $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
643 - $ver = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? rand(0, 1000) : FLRT_PLUGIN_VER;
644 -
645 - if( flrt_get_option('show_bottom_widget') === 'on' ) {
897 + if ( flrt_get_option('mobile_filter_settings') === 'show_bottom_widget' ) {
646 898 $showBottomWidget = 'yes';
647 899 }
648 900
649 - if( flrt_get_option('enable_ajax') === 'on' ){
901 + if ( flrt_get_option('enable_ajax') === 'on' ) {
650 902 $ajaxEnabled = true;
651 903 }
652 904
653 - if( flrt_get_experimental_option('auto_scroll') === 'on' ){
905 + if ( flrt_get_experimental_option('auto_scroll') === 'on' ) {
654 906 $autoScroll = true;
655 907 }
656 908
657 - if( flrt_get_experimental_option( 'use_wait_cursor' ) === 'on' ){
909 + if ( flrt_get_experimental_option( 'use_wait_cursor' ) === 'on' ) {
658 910 $waitCursor = true;
659 911 }
660 912
913 + if ( flrt_get_option('bottom_widget_compatibility') ) {
914 + $wpcPopupCompatMode = true;
915 + }
916 +
917 + //@todo This appears on login page and produce not an array error
661 918 foreach( $sets as $set ){
662 919 if( $set['filtered_post_type'] === 'product' && function_exists('wc_get_default_products_per_row') ){
663 920 $numberposts = apply_filters( 'loop_shop_per_page', wc_get_default_products_per_row() * wc_get_default_product_rows_per_page() );
664 921 }else{
@@ -665,39 +922,636 @@
665 922 $numberposts = get_option( 'posts_per_page' );
666 923 }
667 924
668 925 $per_page[ $set['ID'] ] = intval($numberposts);
926 + $theSet = $filterSetService->getSet( $set['ID'] );
927 +
928 + if( isset( $set['query_on_the_page'] ) && $set['query_on_the_page'] ){
929 + if( (int) $set['ID'] > 0 ) {
930 + $queryOnThePageSets[] = (int) $set['ID'];
931 + }
932 + }
933 +
934 + if( isset( $theSet['use_apply_button']['value'] ) && $theSet['use_apply_button']['value'] === 'yes' ){
935 + if( (int) $set['ID'] > 0 ){
936 + $applyButtonSets[] = (int) $set['ID'];
937 + }
938 + }
669 939 }
670 940
941 + $per_page = apply_filters( 'wpc_filter_sets_posts_per_page', $per_page );
942 +
671 943 $wpcPostContainers = apply_filters( 'wpc_posts_containers', flrt_get_option( 'posts_container', flrt_default_posts_container() ) );
672 944
673 - wp_register_script( 'wc-jquery-ui-touchpunch', FLRT_PLUGIN_URL . 'assets/js/jquery-ui-touch-punch/jquery-ui-touch-punch'.$suffix.'.js', [], $ver, true );
674 - wp_enqueue_script('wpc-filter-everything', FLRT_PLUGIN_URL . 'assets/js/filter-everything'.$suffix.'.js', array('jquery', 'jquery-ui-slider', 'wc-jquery-ui-touchpunch'), $ver, true );
945 + if ( is_array( $wpcPostContainers ) ) {
946 + $wpcPostContainers = array_map( 'wp_specialchars_decode', $wpcPostContainers );
947 + }
675 948
949 + wp_register_script( 'wc-jquery-ui-touchpunch', FLRT_PLUGIN_DIR_URL . 'assets/js/jquery-ui-touch-punch/jquery-ui-touch-punch'.$suffix.'.js', [], $ver, true );
950 +
951 + $isPro = defined('FLRT_FILTERS_PRO') && FLRT_FILTERS_PRO;
952 +
953 + $wpcFrontJsVariables = array(
954 + 'ajaxUrl' => admin_url('admin-ajax.php'),
955 + 'wpcAjaxEnabled' => $ajaxEnabled,
956 + 'wpcStatusCookieName' => FLRT_FOLDING_COOKIE_NAME,
957 + 'wpcMoreLessCookieName' => FLRT_MORELESS_COOKIE_NAME,
958 + 'wpcHierarchyListCookieName' => FLRT_HIERARCHY_LIST_COOKIE_NAME,
959 + 'wpcWidgetStatusCookieName' => FLRT_OPEN_CLOSE_BUTTON_COOKIE_NAME,
960 + 'wpcMobileWidth' => $wpc_mobile_width,
961 + 'showBottomWidget' => $showBottomWidget,
962 + '_nonce' => wp_create_nonce('wpcNonceFront'),
963 + 'wpcPostContainers' => $wpcPostContainers,
964 + 'wpcAutoScroll' => $autoScroll,
965 + 'wpcAutoScrollOffset' => $autoScrollOffset,
966 + 'wpcWaitCursor' => $waitCursor,
967 + 'wpcPostsPerPage' => $per_page,
968 + 'wpcUseSelect2' => $wpcUseSelect2,
969 + 'wpcDateFilters' => false,
970 + 'wpcPopupCompatMode' => $wpcPopupCompatMode,
971 + 'wpcApplyButtonSets' => $applyButtonSets,
972 + 'wpcQueryOnThePageSets' => $queryOnThePageSets,
973 + 'wpcNoPostsContainerMsg' => esc_html__('It appears that this page does not contain the container specified in the «Results container» option. Try to specify the correct one in the Filter Set settings or the common plugin Settings.', 'filter-everything'),
974 + 'wpcIsPro' => $isPro,
975 + 'permalinksEnabled' => FLRT_PERMALINKS_ENABLED,
976 + 'wpcMoreLessCount' => flrt_more_less_count(),
977 + 'wpcSearchChipsText' => esc_html__('search: %s', 'filter-everything' ),
978 + 'chipsTitle' => esc_html__('Remove %s from results', 'filter-everything'),
979 + 'chipsReset' => esc_html__('Reset all', 'filter-everything'),
980 + );
981 +
982 + /**
983 + * We includes Flatpickr.js only on pages, where date filter is used.
984 + */
985 + if ( ! empty( $date_filters ) ) {
986 +
987 + wp_enqueue_script( 'jquery-ui-datepicker' );
988 + wp_enqueue_style( 'wpc-datepicker', FLRT_PLUGIN_DIR_URL . 'assets/css/datepicker/jquery-ui'.$suffix.'.css', array(), '1.11.4' );
989 +
990 + if ( $include_timepicker ) {
991 + 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' );
992 + wp_enqueue_style( 'wpc-timepicker', FLRT_PLUGIN_DIR_URL . "assets/css/timepicker/jquery-ui-timepicker-addon".$suffix.".css", array(), '1.6.3' );
993 + }
994 +
995 + $wpcFrontJsVariables['wpcDateFilters'] = $date_filters;
996 + $wpcFrontJsVariables['wpcDateFiltersLocale'] = determine_locale();
997 + $wpcFrontJsVariables['wpcDateFiltersL10n'] = array(
998 + 'closeText' => _x( 'Filter', 'Date Picker closeText', 'filter-everything' ),
999 + 'currentText' => _x( 'Today', 'Date Picker currentText', 'filter-everything' ),
1000 + 'nextText' => _x( 'Next', 'Date Picker nextText', 'filter-everything' ),
1001 + 'prevText' => _x( 'Prev', 'Date Picker prevText', 'filter-everything' ),
1002 + 'weekHeader' => _x( 'Wk', 'Date Picker weekHeader', 'filter-everything' ),
1003 + 'timeOnlyTitle' => _x( 'Choose Time', 'Date Time Picker timeOnlyTitle', 'filter-everything' ),
1004 + 'timeText' => _x( 'Time', 'Date Time Picker timeText', 'filter-everything' ),
1005 + 'hourText' => _x( 'Hour', 'Date Time Picker hourText', 'filter-everything' ),
1006 + 'minuteText' => _x( 'Minute', 'Date Time Picker minuteText', 'filter-everything' ),
1007 + 'secondText' => _x( 'Second', 'Date Time Picker secondText', 'filter-everything' ),
1008 + 'timezoneText' => _x( 'Time Zone', 'Date Time Picker timezoneText', 'filter-everything' ),
1009 + 'selectText' => _x( 'Select', 'Date Time Picker selectText', 'filter-everything' ),
1010 + 'amNames' => array(
1011 + _x( 'AM', 'Date Time Picker amText', 'filter-everything' ),
1012 + _x( 'A', 'Date Time Picker amTextShort', 'filter-everything' ),
1013 + ),
1014 + 'pmNames' => array(
1015 + _x( 'PM', 'Date Time Picker pmText', 'filter-everything' ),
1016 + _x( 'P', 'Date Time Picker pmTextShort', 'filter-everything' ),
1017 + ),
1018 +
1019 + 'monthNames' => array_values( $wp_locale->month ),
1020 + 'monthNamesShort' => array_values( $wp_locale->month_abbrev ),
1021 + 'dayNames' => array_values( $wp_locale->weekday ),
1022 + 'dayNamesMin' => array_values( $wp_locale->weekday_initial ),
1023 + 'dayNamesShort' => array_values( $wp_locale->weekday_abbrev ),
1024 + 'firstDay' => get_option( 'start_of_week' ),
1025 + 'applyText' => _x( 'Apply', 'Date Picker applyText', 'filter-everything' ),
1026 + );
1027 + }
1028 +
1029 + /**
1030 + * Include Main Front filters javascript
1031 + */
1032 + 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 );
1033 +
676 1034 if( flrt_get_experimental_option('select2_dropdowns') === 'on' ){
677 1035 $select2ver = '4.1.0';
678 1036 $wpcUseSelect2 = 'yes';
679 - wp_enqueue_script( 'select2', FLRT_PLUGIN_URL . "assets/js/select2/select2".$suffix.".js", array('jquery'), $select2ver );
680 - wp_enqueue_style('select2', FLRT_PLUGIN_URL . "assets/css/select2/select2".$suffix.".css", '', $select2ver );
1037 + wp_enqueue_script( 'select2', FLRT_PLUGIN_DIR_URL . "assets/js/select2/select2".$suffix.".js", array('jquery'), $select2ver );
1038 + wp_enqueue_style('select2', FLRT_PLUGIN_DIR_URL . "assets/css/select2/select2".$suffix.".css", '', $select2ver );
1039 + }
681 1040
1041 + $wpcFrontJsVariables['wpcUseSelect2'] = $wpcUseSelect2;
1042 +
1043 + wp_localize_script( 'wpc-filter-everything', 'wpcFilterFront', $wpcFrontJsVariables );
1044 +
1045 + if(!empty($applyButtonSets) && flrt_instant_recount()){
1046 + $this->inlineScriptJsonData();
682 1047 }
683 1048
684 - wp_localize_script( 'wpc-filter-everything', 'wpcFilterFront',
685 - array(
686 - 'ajaxUrl' => admin_url('admin-ajax.php'),
687 - 'wpcAjaxEnabled' => $ajaxEnabled,
688 - 'wpcStatusCookieName' => FLRT_STATUS_COOKIE_NAME,
689 - 'wpcHierarchyListCookieName' => FLRT_HIERARCHY_LIST_COOKIE_NAME,
690 - 'wpcWidgetStatusCookieName' => FLRT_OPEN_CLOSE_BUTTON_COOKIE_NAME,
691 - 'wpcMobileWidth' => $wpc_mobile_width,
692 - 'showBottomWidget' => $showBottomWidget,
693 - '_nonce' => wp_create_nonce('wpcNonceFront'),
694 - 'wpcPostContainers' => $wpcPostContainers,
695 - 'wpcAutoScroll' => $autoScroll,
696 - 'wpcAutoScrollOffset' => $autoScrollOffset,
697 - 'wpcWaitCursor' => $waitCursor,
698 - 'wpcPostsPerPage' => $per_page,
699 - 'wpcUseSelect2' => $wpcUseSelect2
700 - )
1049 + unset( $filterSetService, $wpcFrontJsVariables );
1050 + }
1051 +
1052 + private function inlineScriptJsonData()
1053 + {
1054 + global $wp, $wp_rewrite;
1055 + $flrt_json_data = Container::instance()->getFilterContext()->jsonData();
1056 + if(!empty($flrt_json_data)){
1057 + $wpc_filter_permalinks = get_option( 'wpc_filter_permalinks', [] );
1058 + $permalinksTab = new PermalinksTab();
1059 + $wpc_filter_permalinks_numbering = [];
1060 + $wpc_filter_permalinks_entities = [];
1061 + $permalinks_result = [];
1062 + $includeEntities = ['post_meta_num', 'tax_numeric', 'post_date', 'post_meta_date'];
1063 + $i = 1;
1064 + // Maps are keyed by the full "entity#e_name" pair: two filters may share
1065 + // an e_name with different entities (post_meta_num#_sale_price "Sale Price"
1066 + // vs post_meta_exists#_sale_price "On Sale") and keying by e_name alone
1067 + // made the last one win, sending On Sale into the no-slug GET branch (?yes=on)
1068 + foreach ($permalinksTab->movePostMetaNumInTheEnd($wpc_filter_permalinks) as $key => $value) {
1069 + $parts = explode('#', $key);
1070 + $permalinks_result[$key] = $value;
1071 + if(in_array($parts[0], $includeEntities)){
1072 + $wpc_filter_permalinks_entities[$key] = $parts[0];
1073 + }
1074 + $wpc_filter_permalinks_numbering[$i++] = $key;
1075 + }
1076 + $flrt_json_data['wpcFilterPermalinks'] = $permalinks_result;
1077 + $flrt_json_data['wpcFilterPermalinksNum'] = $wpc_filter_permalinks_numbering;
1078 + $flrt_json_data['wpcFilterEntitiesWithoutSlug'] = $wpc_filter_permalinks_entities;
1079 + // Pagination must never leak into the Apply URL base: applying a new
1080 + // selection restarts the results from page 1 (deeper pages may not exist)
1081 + $request = (string) $wp->request;
1082 + $pagination_base = ! empty( $wp_rewrite->pagination_base ) ? $wp_rewrite->pagination_base : 'page';
1083 + $request = preg_replace( '#(^|/)' . preg_quote( $pagination_base, '#' ) . '/\d+$#', '', $request );
1084 +
1085 + $flrt_json_data['domain'] = trailingslashit(home_url(add_query_arg([], $request)));
1086 + // The site's permalink convention (mirrors user_trailingslashit) —
1087 + // the client-side URL builder must terminate paths the same way
1088 + $flrt_json_data['trailingSlash'] = ( user_trailingslashit( 'wpc' ) === 'wpc/' );
1089 + // On shops that list variations as standalone catalog items (XStore's
1090 + // variable_products_detach) the client-side recount must keep displayed
1091 + // counts in variation space — mirrors the server-side
1092 + // wpc_from_variations_to_products counting gate.
1093 + $flrt_json_data['variationsAsProducts'] = function_exists( 'flrt_variations_listed_as_products' )
1094 + ? (bool) flrt_variations_listed_as_products() : false;
1095 +
1096 + /**
1097 + * The selection-independent bulk of the data (term post lists,
1098 + * meta values, the set universe — ~95% of the payload, tens of
1099 + * MB on large catalogs) is served as a hash-versioned STATIC
1100 + * FILE: cacheable across pages and visits, fetched off the
1101 + * critical path. Only the page-scoped part stays inline. When
1102 + * the file cannot be written, everything falls back inline —
1103 + * still as an inert JSON block, never as a JS object literal
1104 + * (the full JS parser needed ~28 s for 32 MB; JSON.parse of the
1105 + * same payload takes ~60 ms).
1106 + */
1107 + $blobUrl = $this->maybeWriteJsonBlobFile( $flrt_json_data );
1108 + $pagePart = [];
1109 +
1110 + if ( $blobUrl ) {
1111 + foreach ( [ 'wpcFilterPermalinks', 'wpcFilterPermalinksNum', 'wpcFilterEntitiesWithoutSlug', 'domain', 'trailingSlash', 'variationsAsProducts' ] as $rootKey ) {
1112 + if ( array_key_exists( $rootKey, $flrt_json_data ) ) {
1113 + $pagePart[ $rootKey ] = $flrt_json_data[ $rootKey ];
1114 + }
1115 + }
1116 + foreach ( $flrt_json_data as $key => $setData ) {
1117 + if ( ! is_numeric( $key ) ) {
1118 + continue;
1119 + }
1120 + $pagePart[ $key ] = [
1121 + 'filteredAllPostsIds' => isset( $setData['filteredAllPostsIds'] ) ? $setData['filteredAllPostsIds'] : [],
1122 + 'totalFilteredCount' => isset( $setData['totalFilteredCount'] ) ? $setData['totalFilteredCount'] : 0,
1123 + ];
1124 + }
1125 + $pagePart['blobUrl'] = $blobUrl;
1126 + $json = wp_json_encode( $pagePart );
1127 + } else {
1128 + $json = wp_json_encode( $flrt_json_data );
1129 + }
1130 +
1131 + add_action( 'wp_print_footer_scripts', function () use ( $json ) {
1132 + echo '<script type="application/json" id="wpc-filter-json-data">' . $json . '</script>' . "\n";
1133 + }, 1 );
1134 +
1135 + $bootstrap = 'try { var wpcFilterJsonDataEl = document.getElementById("wpc-filter-json-data");';
1136 + $bootstrap .= ' if ( wpcFilterJsonDataEl ) { var wpcFilterPagePart = JSON.parse( wpcFilterJsonDataEl.textContent );';
1137 + $bootstrap .= ' wpcFilterJsonDataEl.parentNode.removeChild( wpcFilterJsonDataEl );';
1138 + $bootstrap .= ' if ( wpcFilterPagePart.blobUrl ) {';
1139 + $bootstrap .= ' window.wpcFilterJsonBlobUrl = wpcFilterPagePart.blobUrl;';
1140 + $bootstrap .= ' window.wpcFilterJsonDataPromise = fetch( wpcFilterPagePart.blobUrl, { credentials: "same-origin" } )';
1141 + $bootstrap .= '.then( function (r) { if ( ! r.ok ) { throw new Error( "HTTP " + r.status ); } return r.json(); } )';
1142 + $bootstrap .= '.then( function (blob) { for ( var k in wpcFilterPagePart ) { if ( k === "blobUrl" ) { continue; }';
1143 + $bootstrap .= ' if ( blob[k] && typeof blob[k] === "object" && ! Array.isArray( blob[k] ) && wpcFilterPagePart[k] && typeof wpcFilterPagePart[k] === "object" && ! Array.isArray( wpcFilterPagePart[k] ) ) {';
1144 + $bootstrap .= ' for ( var kk in wpcFilterPagePart[k] ) { blob[k][kk] = wpcFilterPagePart[k][kk]; } } else { blob[k] = wpcFilterPagePart[k]; } }';
1145 + $bootstrap .= ' window.wpcFilterJsonData = blob; return blob; } )';
1146 + $bootstrap .= '.catch( function (e) { if ( window.console ) { console.error( "Filter Everything: filter data fetch failed", e ); } } );';
1147 + $bootstrap .= ' } else { window.wpcFilterJsonData = wpcFilterPagePart; } }';
1148 + $bootstrap .= ' } catch (e) { if ( window.console ) { console.error( "Filter Everything: filter data JSON parse failed", e ); } }';
1149 +
1150 + wp_add_inline_script( 'wpc-filter-everything', $bootstrap, 'before' );
1151 + }
1152 + }
1153 +
1154 + /**
1155 + * Writes the selection-independent part of $flrt_json_data to a static
1156 + * JSON file in uploads/flrt-cache/ and returns its URL (false = keep
1157 + * everything inline). The file name carries a hash of the inputs that
1158 + * define the content (plugin/cache versions, blob version bumped in
1159 + * resetTransitions(), set group, language, universe), so URLs are
1160 + * immutable: a stale file is impossible, only unused ones — those are
1161 + * garbage-collected in resetTransitions().
1162 + */
1163 + private function maybeWriteJsonBlobFile( $flrt_json_data )
1164 + {
1165 + if ( apply_filters( 'wpc_json_static_file', true ) === false ) {
1166 + return false;
1167 + }
1168 +
1169 + $setIds = array_filter( array_keys( $flrt_json_data ), 'is_numeric' );
1170 + if ( empty( $setIds ) ) {
1171 + return false;
1172 + }
1173 +
1174 + $firstSet = $flrt_json_data[ reset( $setIds ) ];
1175 + $group = isset( $firstSet['relatedSets'] ) && $firstSet['relatedSets'] !== '' ? $firstSet['relatedSets'] : implode( '_', $setIds );
1176 + $group = preg_replace( '/[^0-9_]/', '', (string) $group );
1177 + $universe = ( isset( $firstSet['allPostsIds'] ) && is_array( $firstSet['allPostsIds'] ) ) ? array_keys( $firstSet['allPostsIds'] ) : [];
1178 + $lang = defined( 'ICL_LANGUAGE_CODE' ) ? ICL_LANGUAGE_CODE : '';
1179 +
1180 + // The filter CONFIGS (view, More/Less, parent, labels, ...) shape the
1181 + // stored blob, but no hook is guaranteed to bump flrt_json_blob_ver for
1182 + // every way a config can change — a missed bump used to freeze a stale
1183 + // config into the immutable file forever (e.g. a filter switched to
1184 + // More/Less kept more_less='no' in the blob and the recount hid every
1185 + // term of it). Fingerprint the configs directly: a config change now
1186 + // mints a new file name by construction. 'values' is page-scoped and
1187 + // normalized out of the stored blob — it must not vary the name.
1188 + $configFingerprint = [];
1189 + foreach ( $setIds as $sid ) {
1190 + if ( empty( $flrt_json_data[ $sid ]['allEntities'] ) ) {
1191 + continue;
1192 + }
1193 + foreach ( $flrt_json_data[ $sid ]['allEntities'] as $eName => $entity ) {
1194 + $filterConf = is_object( $entity )
1195 + ? ( isset( $entity->filter ) ? (array) $entity->filter : [] )
1196 + : ( isset( $entity['filter'] ) ? (array) $entity['filter'] : [] );
1197 + unset( $filterConf['values'] );
1198 + $configFingerprint[ $sid ][ $eName ] = $filterConf;
1199 + }
1200 + }
1201 +
1202 + $hash = md5( implode( '|', [
1203 + FLRT_PLUGIN_VER,
1204 + FLRT_CACHE_FORMAT_SUFFIX,
1205 + (string) get_option( 'flrt_json_blob_ver', 1 ),
1206 + // The same DB serves different filter data in free and PRO (PRO-only
1207 + // fields are gated at read time) — the modes must not share a file
1208 + defined( 'FLRT_FILTERS_PRO' ) ? 'pro' : 'free',
1209 + $group,
1210 + $lang,
1211 + count( $universe ),
1212 + md5( implode( ',', $universe ) ),
1213 + md5( (string) wp_json_encode( $configFingerprint ) ),
1214 + ] ) );
1215 +
1216 + $uploads = wp_upload_dir();
1217 + if ( ! empty( $uploads['error'] ) ) {
1218 + return false;
1219 + }
1220 +
1221 + $fileName = 'filters-' . $group . '-' . $hash . '.json';
1222 + $dir = trailingslashit( $uploads['basedir'] ) . 'flrt-cache';
1223 + $file = $dir . '/' . $fileName;
1224 + $url = trailingslashit( $uploads['baseurl'] ) . 'flrt-cache/' . $fileName;
1225 +
1226 + if ( file_exists( $file ) ) {
1227 + return $url;
1228 + }
1229 +
1230 + if ( ! wp_mkdir_p( $dir ) ) {
1231 + return false;
1232 + }
1233 +
1234 + if ( ! file_exists( $dir . '/index.php' ) ) {
1235 + @file_put_contents( $dir . '/index.php', "<?php // Silence is golden.\n" );
1236 + }
1237 +
1238 + $encoded = wp_json_encode( $this->normalizeJsonBlob( $flrt_json_data ) );
1239 + if ( ! $encoded ) {
1240 + return false;
1241 + }
1242 +
1243 + // Write to a temp name first: a concurrent request must never fetch
1244 + // a half-written multi-MB file
1245 + $tmp = $file . '.' . wp_generate_password( 8, false ) . '.tmp';
1246 + if ( @file_put_contents( $tmp, $encoded ) === false ) {
1247 + return false;
1248 + }
1249 + if ( ! @rename( $tmp, $file ) ) {
1250 + @unlink( $tmp );
1251 + return false;
1252 + }
1253 +
1254 + return $url;
1255 + }
1256 +
1257 + /**
1258 + * Strips/zeroes the page-scoped leaves so that every page of the set
1259 + * group produces an identical blob: filteredAllPostsIds and the current
1260 + * result counts live inline; cross_count, current range bounds and
1261 + * per-bucket counts are recomputed client-side on the first recount and
1262 + * are never read from the JSON before that.
1263 + */
1264 + private function normalizeJsonBlob( $flrt_json_data )
1265 + {
1266 + // Entities are objects — one encode/decode round-trip detaches a
1267 + // plain array tree we can normalize without touching the originals
1268 + $data = json_decode( wp_json_encode( $flrt_json_data ), true );
1269 +
1270 + $rangeLeaves = [ 'min', 'max', 'from', 'to', 'time_from', 'time_to' ];
1271 +
1272 + foreach ( $data as $key => &$setData ) {
1273 + if ( ! is_numeric( $key ) ) {
1274 + continue;
1275 + }
1276 +
1277 + unset( $setData['filteredAllPostsIds'], $setData['totalFilteredCount'] );
1278 +
1279 + if ( empty( $setData['allEntities'] ) || ! is_array( $setData['allEntities'] ) ) {
1280 + continue;
1281 + }
1282 +
1283 + foreach ( $setData['allEntities'] as &$entity ) {
1284 + unset( $entity['filter']['values'] );
1285 +
1286 + if ( array_key_exists( 'new_date_query', $entity ) ) {
1287 + $entity['new_date_query'] = [];
1288 + }
1289 +
1290 + foreach ( $rangeLeaves as $leaf ) {
1291 + if ( array_key_exists( $leaf, $entity ) ) {
1292 + $entity[ $leaf ] = false;
1293 + }
1294 + }
1295 +
1296 + if ( empty( $entity['items'] ) || ! is_array( $entity['items'] ) ) {
1297 + continue;
1298 + }
1299 +
1300 + foreach ( $entity['items'] as &$item ) {
1301 + if ( array_key_exists( 'cross_count', $item ) ) {
1302 + $item['cross_count'] = 0;
1303 + }
1304 + if ( array_key_exists( 'wp_queried', $item ) ) {
1305 + $item['wp_queried'] = false;
1306 + }
1307 + foreach ( $rangeLeaves as $leaf ) {
1308 + if ( array_key_exists( $leaf, $item ) ) {
1309 + $item[ $leaf ] = false;
1310 + }
1311 + }
1312 + if ( ! empty( $item['range_list_input'] ) && is_array( $item['range_list_input'] ) ) {
1313 + foreach ( $item['range_list_input'] as $bucket => $count ) {
1314 + if ( is_numeric( $count ) ) {
1315 + $item['range_list_input'][ $bucket ] = 0;
1316 + }
1317 + }
1318 + }
1319 + }
1320 + unset( $item );
1321 + }
1322 + unset( $entity );
1323 + }
1324 + unset( $setData );
1325 +
1326 + // These stay inline (page-scoped or tiny site config)
1327 + unset( $data['domain'], $data['trailingSlash'], $data['variationsAsProducts'], $data['wpcFilterPermalinks'], $data['wpcFilterPermalinksNum'], $data['wpcFilterEntitiesWithoutSlug'] );
1328 +
1329 + return $data;
1330 + }
1331 +
1332 + public function removeApplyButtonOrderField( &$set_settings_fields )
1333 + {
1334 + unset( $set_settings_fields['apply_button_menu_order'], $set_settings_fields['search_field_menu_order'] );
1335 + }
1336 +
1337 + public function handleFilterSetFieldsVisibility( $filterSetFields )
1338 + {
1339 + if( isset( $filterSetFields['use_apply_button']['value'] ) && $filterSetFields['use_apply_button']['value'] === 'yes' ) {
1340 + $filterSetFields['apply_button_text']['additional_class'] = 'wpc-opened';
1341 + $filterSetFields['reset_button_text']['additional_class'] = 'wpc-opened';
1342 + }
1343 +
1344 + if ( isset( $filterSetFields['use_search_field']['value'] ) && $filterSetFields['use_search_field']['value'] === 'yes' ) {
1345 + $filterSetFields['search_field_placeholder']['additional_class'] = 'wpc-opened';
1346 + $filterSetFields['search_field_label']['additional_class'] = 'wpc-opened';
1347 + }
1348 +
1349 + if (isset( $filterSetFields['horizontal_view']['value'] ) && $filterSetFields['horizontal_view']['value'] === 'yes'){
1350 + $filterSetFields['horizontal_view_column']['additional_class'] = 'wpc-opened';
1351 + }
1352 +
1353 + return $filterSetFields;
1354 + }
1355 +
1356 + public function disableCacheProductsShortcode( $out )
1357 + {
1358 + $wpManager = Container::instance()->getWpManager();
1359 + $is_filter_request = $wpManager->getQueryVar('wpc_is_filter_request');
1360 + $thePost = Container::instance()->getThePost();
1361 + $action = isset( $thePost['action'] ) ? $thePost['action'] : false;
1362 +
1363 + // wpc_get_wp_queries - action to get WP_Queries on a page
1364 + if( isset( $out['cache'] ) && ( $is_filter_request || $action === 'wpc_get_wp_queries' ) ){
1365 + $out['cache'] = false;
1366 + }
1367 +
1368 + return $out;
1369 + }
1370 +
1371 + public function showCombinedFields( &$filter, $field_key )
1372 + {
1373 + $includeExclude = flrt_extract_vars( $filter, array('exclude', 'include') );
1374 + if( $includeExclude ):
1375 +
1376 + ?><tr class="<?php echo esc_attr( flrt_filter_row_class( $includeExclude['exclude'] ) ); ?>"<?php flrt_maybe_hide_row( $includeExclude['exclude'] ); ?>><?php
1377 +
1378 + flrt_include_admin_view('filter-field-label', array(
1379 + 'field_key' => 'exclude',
1380 + 'attributes' => $includeExclude['exclude']
1381 + )
1382 + );
1383 + ?>
1384 + <td class="wpc-filter-field-td wpc-filter-field-include-exclude-td">
1385 + <div class="wpc-filter-field-include-exclude-wrap">
1386 + <div class="wpc-field-wrap wpc-field-exclude-wrap <?php if( isset( $includeExclude['exclude']['id'] ) ){ echo esc_attr( $includeExclude['exclude']['id'] ); } ?>-wrap">
1387 + <?php echo flrt_render_input( $includeExclude['exclude'] ); // Already escaped in function ?>
1388 + <?php do_action('wpc_after_filter_input', $includeExclude['exclude'] ); ?>
1389 + </div>
1390 + <div class="wpc-field-wrap wpc-field-include-wrap <?php if( isset( $includeExclude['include']['id'] ) ){ echo esc_attr( $includeExclude['include']['id'] ); } ?>-wrap">
1391 + <?php echo flrt_render_input( $includeExclude['include'] ); // Already escaped in function ?>
1392 + <?php do_action('wpc_after_filter_input', $includeExclude['include'] ); ?>
1393 + </div>
1394 + </div>
1395 + </td>
1396 + </tr><?php
1397 +
1398 + endif;
1399 +
1400 + if ( $field_key === 'min_num_label' ) {
1401 + $min_max_num_labels = flrt_extract_vars( $filter, array( 'min_num_label', 'max_num_label' ) );
1402 +
1403 + if( $min_max_num_labels ) :
1404 + $min_field_id = $max_field_id = '';
1405 +
1406 + if (isset($min_max_num_labels['min_num_label']['id'])) {
1407 + $min_field_id = esc_attr($min_max_num_labels['min_num_label']['id']);
1408 + }
1409 + if( isset( $min_max_num_labels['max_num_label']['id'] ) ){
1410 + $max_field_id = esc_attr( $min_max_num_labels['max_num_label']['id'] );
1411 + }
1412 +
1413 + if( isset($min_max_num_labels['min_num_label']['value']) ){
1414 + $min_max_num_labels['min_num_label']['data-caret'] = mb_strlen( $min_max_num_labels['min_num_label']['value'] ) + 1;
1415 + }
1416 +
1417 + if( isset($min_max_num_labels['max_num_label']['value']) ){
1418 + $min_max_num_labels['max_num_label']['data-caret'] = mb_strlen( $min_max_num_labels['max_num_label']['value'] ) + 1;
1419 + }
1420 +
1421 + ?><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
1422 +
1423 + flrt_include_admin_view('filter-field-label', array(
1424 + 'field_key' => 'min_num_label',
1425 + 'attributes' => $min_max_num_labels['min_num_label']
1426 + )
1427 + );
1428 + ?>
1429 + <td class="wpc-filter-field-td wpc-filter-field-min-max-labels-td">
1430 + <div class="wpc-filter-field-min-max-labels-wrap">
1431 + <div class="wpc-field-wrap wpc-field-min_num_label-wrap <?php echo $min_field_id; ?>-wrap">
1432 + <?php echo flrt_render_input( $min_max_num_labels['min_num_label'] ); // Already escaped in function ?>
1433 + <?php do_action('wpc_after_filter_input', $min_max_num_labels['min_num_label'] ); ?>
1434 + </div>
1435 + <div class="wpc-field-wrap wpc-field-max_num_label-wrap <?php echo $max_field_id; ?>-wrap">
1436 + <?php echo flrt_render_input( $min_max_num_labels['max_num_label'] ); // Already escaped in function ?>
1437 + <?php do_action('wpc_after_filter_input', $min_max_num_labels['max_num_label'] ); ?>
1438 + </div>
1439 + <p class="description"><?php echo wp_kses(
1440 + 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 ),
1441 + array( 'span' => array(
1442 + 'class' => true,
1443 + 'data-field' => true,
1444 + 'title' => true,
1445 + ) )
1446 + ) ?></p>
1447 + </div>
1448 + </td>
1449 + </tr><?php
1450 +
1451 + endif;
1452 + }
1453 + }
1454 +
1455 + public function addSearchArgsToWpQuery( $wp_query )
1456 + {
1457 + if ( isset( $_GET['srch'] ) && $_GET['srch'] ) {
1458 + $keyword = filter_input( INPUT_GET, 'srch', FILTER_DEFAULT );
1459 + $wp_query->set( 's', $keyword );
1460 + }
1461 +
1462 + return $wp_query;
1463 + }
1464 +
1465 + public function addSkuSearchSql( $search, $wp_query )
1466 + {
1467 + if( $wp_query->get('flrt_query_hash') || $wp_query->get('flrt_query_clone') ){
1468 +
1469 + if ( $wp_query->get('wc_query') === 'product_query' || $wp_query->get('post_type') === 'product' /* || $wp_query->get('post_type') === 'product_variation' */ ) {
1470 + global $wpdb;
1471 +
1472 + $product_id = wc_get_product_id_by_sku( $wp_query->get('s') );
1473 + if ( ! $product_id ) {
1474 + return $search;
1475 + }
1476 +
1477 + $product = wc_get_product( $product_id );
1478 + if ( $product->is_type( 'variation' ) ) {
1479 + $product_id = $product->get_parent_id();
1480 + }
1481 +
1482 + $search = str_replace( 'AND (((', "AND (({$wpdb->posts}.ID IN (" . $product_id . ")) OR ((", $search );
1483 + return $search;
1484 + }
1485 +
1486 + }
1487 + return $search;
1488 + }
1489 +
1490 + public function postDateWhere( $where, $wp_query )
1491 + { global $wpdb;
1492 + $sql = [];
1493 + $operator = '';
1494 + $wpc_date_query = $wp_query->get( 'wpc_date_query' );
1495 +
1496 + if( ! empty( $wpc_date_query ) && is_array( $wpc_date_query ) ) {
1497 + foreach ( $wpc_date_query as $edge => $value ) {
1498 + if( $edge === 'from' ) {
1499 + $operator = '>=';
1500 + } elseif ( $edge === 'to' ) {
1501 + $operator = '<=';
1502 + }
1503 +
1504 + $sql[] = $wpdb->prepare( "AND {$wpdb->posts}.post_date {$operator} %s ", $value );
1505 +
1506 + }
1507 +
1508 + $where = implode( ' ', $sql ) . $where;
1509 + }
1510 +
1511 + return $where;
1512 + }
1513 +
1514 + public function showElementPickerPanel()
1515 + {
1516 + $getData = Container::instance()->getTheGet();
1517 +
1518 + $html = sprintf(
1519 + '<div id="wpc-choose-selector" data-set-id="%1$s">
1520 + <div id="wpc-choose-id-css" class="wpc-choose-block">
1521 + <span>%2$s</span>
1522 + <input id="wpc-chooses-html-id">
1523 + </div>
1524 + <div id="wpc-choose-class-css" class="wpc-choose-block">
1525 + <span>%3$s</span>
1526 + <input id="wpc-chooses-html-css">
1527 + </div>
1528 + <div id="wpc-choose-empty" class="wpc-choose-block">
1529 +
1530 + <span id="wpc-chooses-empty-element">%4$s</span>
1531 + </div>
1532 + <div id="wpc-choose-selector-buttons">
1533 + <button id="wpc-choose-another-selector">%5$s</button>
1534 + <button id="wpc-choose-save-btn">%6$s</button>
1535 + </div>
1536 + </div>',
1537 + !(empty($getData['flrt_set_id'])) ? esc_attr($getData['flrt_set_id']) : 'global_posts_container',
1538 + esc_html__( 'Chosen ID: ', 'filter-everything' ),
1539 + esc_html__( 'Chosen Class: ', 'filter-everything' ),
1540 + esc_html__( 'The element does not have any CSS selectors', 'filter-everything' ),
1541 + esc_html__( 'Choose another', 'filter-everything' ),
1542 + esc_html__( 'Save', 'filter-everything' )
701 1543 );
1544 +
1545 + // Localize script with nonce and AJAX URL
1546 + wp_localize_script('wpc-element-picker', 'wpcElementPickerData', array(
1547 + 'ajax_url' => admin_url('admin-ajax.php'),
1548 + 'error_saving_selector' => esc_html__('Error saving selector', 'filter-everything'),
1549 + 'no_element_selected' => esc_html__('Error: No element selected', 'filter-everything'),
1550 + 'failed_to_save_selector' => esc_html__('Error: Failed to save selector. Please try again.', 'filter-everything'),
1551 + 'saving' => esc_html__('Saving...', 'filter-everything'),
1552 + 'save' => esc_html__('Save', 'filter-everything'),
1553 + ));
1554 +
1555 + echo $html;
702 1556 }
703 -}
1557 +}