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
filter-everything / src / wpc-helpers.php

wpc-helpers.php in Filter Everything — WordPress & WooCommerce Filters 1.9.7, at src/wpc-helpers.php

1,285 lines 39.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 use \FilterEverything\Filter\Container;
8 use \FilterEverything\Filter\FilterSet;
9 use \FilterEverything\Filter\FilterFields;
10 use \FilterEverything\Filter\PostMetaNumEntity;
11 use \FilterEverything\Filter\PostDateEntity;
12 use \FilterEverything\Filter\PostMetaDateEntity;
13 /**
14 * Returns user's caps level that allows to use the plugin.
15 * Developers can modify this level via hook 'wpc_plugin_user_caps' ot their own risk.
16 * @return string
17 */
18 function flrt_plugin_user_caps()
19 {
20 return apply_filters('wpc_plugin_user_caps', 'manage_options');
21 }
22
23 function flrt_the_set($set_id = 0)
24 {
25 if (function_exists('brizy_load')) {
26 if (!did_action('wp_print_scripts')) {
27 return false;
28 }
29 }
30
31 return Container::instance()->getFilterContext()->takeSet($set_id);
32 }
33
34 function flrt_print_filters_for($hook = '')
35 {
36 global $wp_filter;
37 if (empty($hook) || !isset($wp_filter[$hook]))
38 return;
39 return $wp_filter[$hook];
40 }
41
42 function flrt_is_filter_request()
43 {
44 $wpManager = Container::instance()->getWpManager();
45 return $wpManager->getQueryVar('wpc_is_filter_request');
46 }
47
48 /**
49 * Whether filters travel as pretty URL path segments.
50 *
51 * FLRT_PERMALINKS_ENABLED is the switch; it can be overridden in wp-config.php
52 * or a theme's functions.php (Permalink Manager compatibility mode). When it is
53 * not defined yet, compute the default it would get: PRO with non-empty rewrite
54 * rules. WpManager::init() defines the constant from this on `init`, but code
55 * running in a request where the plugin was loaded after `init` — plugin
56 * activation — must not fall back to the query-string mode.
57 *
58 * @return bool
59 */
60 function flrt_permalinks_enabled()
61 {
62 if ( defined( 'FLRT_PERMALINKS_ENABLED' ) ) {
63 return (bool) FLRT_PERMALINKS_ENABLED;
64 }
65
66 global $wp_rewrite;
67 $rewrite = ( $wp_rewrite instanceof \WP_Rewrite ) ? $wp_rewrite->wp_rewrite_rules() : [];
68
69 return defined( 'FLRT_FILTERS_PRO' ) && ! empty( $rewrite );
70 }
71
72 function flrt_include($filename)
73 {
74 $path = flrt_get_path($filename);
75
76 if (file_exists($path)) {
77 include_once($path);
78 }
79 }
80
81 function flrt_get_path($path = '')
82 {
83 return FLRT_PLUGIN_DIR_PATH . ltrim($path, '/');
84 }
85
86 function flrt_ucfirst($text)
87 {
88 if (!is_string($text)) {
89 return $text;
90 }
91 return mb_strtoupper(mb_substr($text, 0, 1)) . mb_substr($text, 1);
92 }
93
94 function flrt_lcfirst( $text )
95 {
96 if( ! is_string( $text ) ){
97 return $text;
98 }
99 return mb_strtolower( mb_substr( $text, 0, 1 ) ) . mb_substr( $text, 1 );
100 }
101
102 function flrt_add_query_arg(...$args)
103 {
104 if (is_array($args[0])) {
105 if (count($args) < 2 || false === $args[1]) {
106 $uri = $_SERVER['REQUEST_URI'];
107 } else {
108 $uri = $args[1];
109 }
110 } else {
111 if (count($args) < 3 || false === $args[2]) {
112 $uri = $_SERVER['REQUEST_URI'];
113 } else {
114 $uri = $args[2];
115 }
116 }
117
118 $frag = strstr($uri, '#');
119 if ($frag) {
120 $uri = substr($uri, 0, -strlen($frag));
121 } else {
122 $frag = '';
123 }
124
125 if (0 === stripos($uri, 'http://')) {
126 $protocol = 'http://';
127 $uri = substr($uri, 7);
128 } elseif (0 === stripos($uri, 'https://')) {
129 $protocol = 'https://';
130 $uri = substr($uri, 8);
131 } else {
132 $protocol = '';
133 }
134
135 if (strpos($uri, '?') !== false) {
136 list($base, $query) = explode('?', $uri, 2);
137 $base .= '?';
138 } elseif ($protocol || strpos($uri, '=') === false) {
139 $base = $uri . '?';
140 $query = '';
141 } else {
142 $base = '';
143 $query = $uri;
144 }
145
146 wp_parse_str($query, $qs);
147
148 if (is_array($args[0])) {
149 foreach ($args[0] as $k => $v) {
150 $qs[$k] = $v;
151 }
152 } else {
153 $qs[$args[0]] = $args[1];
154 }
155
156 foreach ($qs as $k => $v) {
157 if (false === $v) {
158 unset($qs[$k]);
159 }
160 }
161
162 $ret = build_query($qs);
163 $ret = trim($ret, '?');
164 $ret = preg_replace('#=(&|$)#', '$1', $ret);
165 $ret = $protocol . $base . $ret . $frag;
166 $ret = rtrim($ret, '?');
167 return $ret;
168 }
169
170 /**
171 * @param $terms array
172 * @param $keys array
173 *
174 * @return array Array of objects with required keys
175 */
176 function flrt_extract_objects_vars($terms, $keys = [])
177 {
178 $required = [];
179
180 foreach ($terms as $i => $term) {
181 $new_object = new \stdClass();
182
183 foreach ($keys as $key) {
184 if (isset($term->$key)) {
185 $new_object->$key = $term->$key;
186 $required[$term->term_id] = $new_object;
187 }
188 }
189 }
190
191 return $required;
192 }
193
194 add_filter('wpc_check_broken_query_vars', function ($query_vars, $query) {
195 if (is_admin()) {
196 return $query_vars;
197 }
198
199 $detector_class = 'FilterEverything\\Filter\\WP_Query_Source_Detector';
200 $is_allowed_method = 'is_allowed';
201 $source_var = 'flrt_detected_source';
202
203 if (defined('FLRT_FILTERS_PRO')) {
204 if (defined('FLRT_PRO_BUILDER_KEY')) {
205 $builder_key = apply_filters('wpc_builder_key_pro', $query->get($source_var), constant('FLRT_PRO_BUILDER_KEY'));
206
207 if ($detector_class::$is_allowed_method($builder_key)) {
208 return $query_vars;
209 }
210 }
211 }
212
213 if (!defined('FLRT_FILTERS_PRO')) {
214 $builder_key = apply_filters('wpc_builder_key', $query->get($source_var), $detector_class::$builder_key);
215
216 if ($detector_class::$is_allowed_method($builder_key)) {
217 return $query_vars;
218 }
219 }
220
221 return [];
222 }, 10, 2);
223
224 add_filter('wpc_builder_key', function ($source, $builder_id) {
225 return (int) sprintf("%u", crc32($source . $builder_id));
226 }, 10, 2);
227
228 /**
229 * Compatibility with membership / access-control plugins.
230 *
231 * Such plugins (e.g. Ultimate Membership Pro — indeed-membership-pro) inject
232 * post__in / post__not_in into front-end queries to hide protected content from
233 * non-authorised or logged-out visitors. That flips is_post__in / is_post__not_in
234 * in the query-identity hash FE uses to match a Filter Set to a page query — so a
235 * page that filters correctly for the (unrestricted) admin who saved the set stops
236 * matching for restricted visitors, and filtering silently does nothing for them.
237 *
238 * We normalise those two flags back to their save-time value (false), but ONLY when
239 * such a plugin is actually active. This keeps every saved query hash untouched on
240 * sites that don't have this conflict. The per-page order counter still distinguishes
241 * genuinely different same-shape queries, so filtering stays correct.
242 */
243 add_filter('wpc_check_broken_query_vars', function ($query_vars, $query) {
244 // Add markers of other post-hiding plugins here if needed; overridable via the filter.
245 $context_hides_posts = defined('IHC_PATH') // Ultimate Membership Pro
246 || defined('WCB2B_PLUGIN_FILE'); // WooCommerce B2B — hides "unallowed" products from guests/other groups via post__not_in
247
248 if ( ! apply_filters('wpc_context_hides_posts', $context_hides_posts, $query) ) {
249 return $query_vars;
250 }
251
252 // Only touch queries FE still considers filterable (its own callback empties the
253 // rest at priority 10); never re-populate an emptied set.
254 if ( is_array($query_vars) && array_key_exists('is_post__not_in', $query_vars) ) {
255 $query_vars['is_post__in'] = false;
256 $query_vars['is_post__not_in'] = false;
257 }
258
259 return $query_vars;
260 }, 20, 2);
261
262 function flrt_remove_level_array($array)
263 {
264 /**
265 * @feature maybe rewrite this full of shame code
266 */
267 if (!is_array($array)) {
268 return [];
269 }
270
271 $flatten = [];
272
273 array_map(function ($a) use (&$flatten) {
274 if (is_array($a)) {
275 $flatten = array_merge($flatten, $a);
276 }
277 },
278 $array);
279
280 return $flatten;
281 }
282
283 add_filter('wpc_check_errors_ids', function ($error_ids, $query) {
284 $source_key = 'flrt_detected_source';
285 if (empty($query->query_vars[$source_key])) {
286 return $error_ids;
287 }
288
289 $source = $query->query_vars[$source_key];
290 $detector_class = 'FilterEverything\\Filter\\WP_Query_Source_Detector';
291 $is_allowed_method = 'is_allowed';
292
293 if (defined('FLRT_FILTERS_PRO')) {
294 if (defined('FLRT_PRO_BUILDER_KEY')) {
295 $builder_key = apply_filters('wpc_builder_key_pro', $source, constant('FLRT_PRO_BUILDER_KEY'));
296 if ($detector_class::$is_allowed_method($builder_key)) {
297 return $error_ids;
298 }
299 }
300 }
301
302 if (!defined('FLRT_FILTERS_PRO')) {
303 $builder_key = apply_filters('wpc_builder_key', $source, $detector_class::$builder_key);
304
305 if ($detector_class::$is_allowed_method($builder_key)) {
306 return $error_ids;
307 }
308 }
309
310 return [];
311 }, 10, 2);
312
313 function flrt_get_forbidden_prefixes()
314 {
315 //@todo it seems all existing tax prefixes should be there
316 // All them actual only when permalinks off
317 $forbidden_prefixes = ['srch'];
318 $permalinksEnabled = defined('FLRT_PERMALINKS_ENABLED') ? FLRT_PERMALINKS_ENABLED : false;
319 if (!$permalinksEnabled) {
320 $forbidden_prefixes = array_merge($forbidden_prefixes, array('cat', 'tag', 'page', 'author'));
321 }
322
323 if (flrt_wpml_active()) {
324 $wpml_url_format = apply_filters('wpml_setting', 0, 'language_negotiation_type');
325 if ($wpml_url_format === '3') {
326 $forbidden_prefixes[] = 'lang';
327 }
328 }
329
330 return apply_filters('wpc_forbidden_prefixes', $forbidden_prefixes);
331 }
332
333 function flrt_get_forbidden_meta_keys()
334 {
335 $forbidden_meta_keys = array('wpc_filter_set_post_type', 'wpc_seo_rule_post_type');
336 return apply_filters('wpc_forbidden_meta_keys', $forbidden_meta_keys);
337 }
338
339 function flrt_array_contains_duplicate($array)
340 {
341 return count($array) != count(array_unique($array));
342 }
343
344 function flrt_create_filters_nonce()
345 {
346 return FilterSet::createNonce();
347 }
348
349 function flrt_get_filter_fields_mapping()
350 {
351 return Container::instance()->getFilterFieldsService()->getFieldsMapping();
352 }
353
354 function flrt_get_configured_filters($post_id)
355 {
356 $filterFields = Container::instance()->getFilterFieldsService();
357 return $filterFields->getFiltersInputs($post_id);
358 }
359
360 function flrt_get_filter_view_name($view_key)
361 {
362 $view_options = FilterFields::getViewOptions();
363 if (isset($view_options[$view_key])) {
364 return esc_html($view_options[$view_key]);
365 }
366
367 return esc_html($view_key);
368 }
369
370 function flrt_get_filter_entity_name($entity_key)
371 {
372 $em = Container::instance()->getEntityManager();
373 $entities = $em->getPossibleEntities();
374
375 foreach ($entities as $key => $entity_array) {
376 if (isset($entity_array['entities'][$entity_key])) {
377 return esc_html($entity_array['entities'][$entity_key]);
378 }
379 }
380
381 if ($entity_key === 'post_meta_exists' && !defined('FLRT_FILTERS_PRO')) {
382 return esc_html__('Available in PRO', 'filter-everything');
383 }
384
385 return esc_html($entity_key);
386 }
387
388 function flrt_get_set_settings_fields($post_id)
389 {
390 $filterSet = Container::instance()->getFilterSetService();
391 return $filterSet->getSettingsTypeFields($post_id);
392 }
393
394 function flrt_get_set_settings_location_fields($post_id)
395 {
396 $filterSet = Container::instance()->getFilterSetService();
397 return $filterSet->getSettingsLocationTypeFields($post_id);
398 }
399
400 function flrt_extract_vars(&$array, $keys)
401 {
402 $r = [];
403 foreach ($keys as $key) {
404 $var = flrt_extract_var($array, $key);
405 if ($var) {
406 $r[$key] = $var;
407 }
408 }
409 return $r;
410 }
411
412 function flrt_extract_var(&$array, $key, $default = null)
413 {
414 // check if exists
415 // - uses array_key_exists to extract NULL values (isset will fail)
416 if (is_array($array) && array_key_exists($key, $array)) {
417 $v = $array[$key];
418 unset($array[$key]);
419 return $v;
420 }
421 return $default;
422 }
423
424 function flrt_get_empty_filter($set_id)
425 {
426 $filterFields = Container::instance()->getFilterFieldsService();
427 return $filterFields->getEmptyFilterObject($set_id);
428 }
429
430 function flrt_excluded_taxonomies()
431 {
432 $excluded_taxonomies = array(
433 'nav_menu',
434 'link_category',
435 'post_format',
436 'template_category',
437 'element_category',
438 'fusion_tb_category',
439 'slide-page',
440 'elementor_font_type',
441 'post_translations',
442 'term_language',
443 'term_translations',
444 'wp_theme',
445 'wp_template_part_area',
446 'wp_pattern_category',
447 'elementor_library_type',
448 'elementor_library_category',
449 );
450
451 return apply_filters('wpc_excluded_taxonomies', $excluded_taxonomies);
452 }
453
454 function flrt_force_non_unique_slug($notNull, $originalSlug)
455 {
456 return $originalSlug;
457 }
458
459 function flrt_redirect_to_error($post_id, $errors)
460 {
461 $redirect = get_edit_post_link($post_id, 'url');
462 $error_code = 20; // Default error code
463
464 if (!empty($errors) && is_array($errors)) {
465 $error_code = reset($errors);
466 }
467
468 $redirect = add_query_arg('message', $error_code, $redirect);
469 wp_redirect($redirect);
470 exit;
471 }
472
473 function flrt_sanitize_int($var)
474 {
475 return preg_replace('/[^\d]+/', '', $var);
476 }
477
478 function flrt_get_query_string_parameters()
479 {
480 $container = Container::instance();
481 $get = $container->getTheGet();
482 $post = $container->getThePost();
483
484 // For compatibility with some Nginx configurations
485 unset($get['q']);
486
487 if (isset($post['flrt_ajax_link'])) {
488 $parts = parse_url($post['flrt_ajax_link']);
489 if (isset($parts['query'])) {
490 parse_str($parts['query'], $output);
491 return $output;
492 }
493 }
494
495 return $get;
496 }
497
498 function flrt_get_option($key, $default = false)
499 {
500 $settings = get_option('wpc_filter_settings');
501
502 if (isset($settings[$key])) {
503 return apply_filters('wpc_get_option', $settings[$key], $key);
504 }
505
506 if ($default) {
507 return $default;
508 }
509
510 return false;
511
512 }
513
514 function flrt_remove_option($key)
515 {
516 $settings = get_option('wpc_filter_settings');
517
518 if (isset($settings[$key]) && $settings[$key]) {
519 unset($settings[$key]);
520 return update_option('wpc_filter_settings', $settings);
521 }
522
523 return false;
524 }
525
526 function flrt_change_option($option_key, $key, $value, $autoload = false)
527 {
528 $settings = get_option($option_key);
529
530 if ($settings[$key] === $value) {
531 return true;
532 }
533
534 $settings = is_array($settings) ? $settings : [];
535 $settings[$key] = $value;
536
537 return update_option($option_key, $settings, $autoload);
538 }
539
540 function flrt_get_experimental_option($key, $default = false)
541 {
542 /**
543 * @todo This should be rewritten
544 */
545 $settings = get_option('wpc_filter_experimental');
546
547 if (isset($settings[$key])) {
548 return apply_filters('wpc_get_option', $settings[$key], $key);
549 }
550
551 if ($default !== false) {
552 return apply_filters('wpc_get_option', $default, $key);
553 }
554
555 return apply_filters('wpc_get_option', false, $key);
556
557 }
558
559 function flrt_term_id($name, $filter, $id, $echo = true)
560 {
561 $attr = esc_attr("wpc-" . $name . "-" . $filter['entity'] . "-" . esc_attr($filter['e_name']) . "-" . $id);
562 if ($echo) {
563 echo $attr;
564 } else {
565 return $attr;
566 }
567 }
568
569 function flrt_get_plugin_name()
570 {
571 if (defined('FLRT_FILTERS_PRO')) {
572 return esc_html__('Filter Everything Pro', 'filter-everything');
573 } else {
574 return esc_html__('Filter Everything', 'filter-everything');
575 }
576 }
577
578 function flrt_get_plugin_url($type = 'about', $full = false)
579 {
580 if ($full) {
581 return esc_url($full);
582 }
583
584 return esc_url(FLRT_PLUGIN_URL . '/' . $type);
585 }
586
587 function flrt_get_term_by_slug($prefix)
588 {
589 global $wpdb;
590
591 $sql = "SELECT {$wpdb->terms}.slug FROM {$wpdb->terms} WHERE {$wpdb->terms}.slug = '%s'";
592 $sql = $wpdb->prepare($sql, $prefix);
593 $result = $wpdb->get_row($sql);
594
595 if (isset($result->slug) && $result->slug) {
596 return $result->slug;
597 }
598
599 return false;
600 }
601
602 function flrt_walk_terms_tree($terms, $args)
603 {
604 _deprecated_function('flrt_walk_terms_tree', '1.7.6', 'flrt_filter_walk_terms_tree()');
605 flrt_filter_walk_terms_tree($terms, $args);
606 }
607
608 function flrt_filter_walk_terms_tree($terms, $args)
609 {
610 $walker = new \FilterEverything\Filter\WalkerCheckbox();
611
612 $depth = -1;
613 if (isset($args['filter']['hierarchy']) && $args['filter']['hierarchy'] === 'yes') {
614 $depth = 10;
615 }
616
617 return $walker->walk($terms, $depth, $args);
618 }
619
620 function flrt_get_all_parents($elements, $parent_id, &$ids)
621 {
622 if (isset($elements[$parent_id]->parent) && $elements[$parent_id]->parent > 0) {
623 $id = $elements[$parent_id]->parent;
624 $ids_flipped = array_flip($ids);
625
626 if (!isset($ids_flipped[$id])) {
627 $ids[] = $id;
628 }
629
630 flrt_get_all_parents($elements, $id, $ids);
631 } else {
632 return $ids;
633 }
634 }
635
636 function flrt_get_parents_with_not_empty_children($elements, $key = 'cross_count')
637 {
638 $has_posts_in_children = [];
639
640 if (empty($elements) || !is_array($elements)) {
641 return $has_posts_in_children;
642 }
643
644 $new_elements = [];
645
646 foreach ($elements as $k => $e) {
647 $new_elements[$e->term_id] = $e;
648 }
649
650 $has_posts_in_children_flipped = array_flip($has_posts_in_children);
651
652 foreach ($new_elements as $e) {
653 if (isset($e->parent) && !empty($e->parent) && $e->$key > 0) {
654 // Find all parents for term that contains posts
655 if (!isset($has_posts_in_children_flipped[$e->parent])) {
656 $has_posts_in_children[] = $e->parent;
657 }
658
659 flrt_get_all_parents($new_elements, $e->parent, $has_posts_in_children);
660 }
661 }
662
663 return $has_posts_in_children;
664 }
665
666 function flrt_find_all_descendants($arr)
667 {
668 $all_results = [];
669
670 if (empty($arr) || !is_array($arr)) {
671 return $all_results;
672 }
673
674 foreach ($arr as $k => $v) {
675 $curr_result = [];
676
677 for ($stack = [$k]; count($stack);) {
678 $el = array_pop($stack);
679
680 if (array_key_exists($el, $arr) && is_array($arr[$el])) {
681 foreach ($arr[$el] as $child) {
682 $curr_result [] = $child;
683 $stack [] = $child;
684 }
685 }
686 }
687
688 if (count($curr_result)) {
689 $all_results[$k] = $curr_result;
690 }
691 }
692
693 return $all_results;
694 }
695
696 function flrt_debug_title()
697 {
698
699 echo '<div class="wpc-debug-title">' . esc_html__('Filter Everything debug', 'filter-everything');
700 echo '&nbsp;' . flrt_help_tip(
701 sprintf(
702 __('Debug messages are visible for logged in administrators only. You can disable them in Filters -> <a href="%s">Settings</a> -> Debug mode.', 'filter-everything'),
703 admin_url('edit.php?post_type=filter-set&page=filters-settings')
704 ), true) . '</div>';
705 }
706
707 function flrt_is_debug_mode()
708 {
709 $debug_mode = false;
710 if (flrt_get_option('widget_debug_messages') === 'on') {
711 if (current_user_can(flrt_plugin_user_caps())) {
712 $debug_mode = true;
713 }
714 }
715
716 return $debug_mode;
717 }
718
719 function flrt_clean($var)
720 {
721 if (is_array($var)) {
722 return array_map('flrt_clean', $var);
723 } else {
724 return is_scalar($var) ? sanitize_text_field($var) : $var;
725 }
726 }
727
728 function flrt_sorting_option_value($order_by_value, $meta_keys, $orders, $i)
729 {
730 $meta_key = isset($meta_keys[$i]) ? $meta_keys[$i] : '';
731 $order = isset($orders[$i]) ? $orders[$i] : '';
732
733 $option_value = $order_by_value;
734
735 if (in_array($order_by_value, ['m', 'n'], true)) {
736 $option_value .= $meta_key;
737 }
738
739 $option_value .= ($order === 'desc') ? '-' . $order : '';
740
741 return $option_value;
742 }
743
744 function flrt_get_active_plugins()
745 {
746
747 if (is_multisite()) {
748 $active_plugins = get_site_option('active_sitewide_plugins');
749 if (is_array($active_plugins)) {
750 $active_plugins = array_keys($active_plugins);
751 }
752
753 $site_active_plugins = apply_filters('active_plugins', get_option('active_plugins'));
754 $active_plugins = array_merge($active_plugins, $site_active_plugins);
755 } else {
756 $active_plugins = apply_filters('active_plugins', get_option('active_plugins'));
757 }
758
759 return $active_plugins;
760 }
761
762 /**
763 * Version tag for FE transient cache NAMES. Bump it whenever the cached data
764 * SHAPE changes (v2 = compact aggregated maps instead of raw SQL rows).
765 * Versioned key names keep branches/versions with different cache formats
766 * from ever reading each other's transients (e.g. when switching git branches
767 * on a dev site) - each format lives under its own keys and stale ones simply
768 * expire.
769 */
770 if ( ! defined( 'FLRT_CACHE_FORMAT_SUFFIX' ) ) {
771 define( 'FLRT_CACHE_FORMAT_SUFFIX', '_v2' );
772 }
773
774 function flrt_remove_empty_terms($checkTerms, $filter, $has_not_empty_children_flipped = [], $use_apply_button = false)
775 {
776
777 if (!$use_apply_button) {
778 foreach ($checkTerms as $index => $term) {
779 if ($filter['hierarchy'] === 'yes') {
780
781 if ($term->cross_count === 0
782 && !isset($has_not_empty_children_flipped[$term->term_id])) {
783 unset($checkTerms[$index]);
784 }
785
786 } else {
787 if ($term->cross_count === 0) {
788 unset($checkTerms[$index]);
789 }
790 }
791 }
792 }
793
794 return $checkTerms;
795 }
796
797 function flrt_get_wp_queried_term($terms)
798 {
799 $wp_queried_terms = false;
800
801 foreach ($terms as $term) {
802 if ($term->wp_queried === true) {
803 $wp_queried_terms = $term;
804 break;
805 }
806 }
807
808 return $wp_queried_terms;
809 }
810
811 function flrt_get_filter_terms($filter, $posType, $em = false)
812 {
813 if (!$em) {
814 $em = Container::instance()->getEntityManager();
815 }
816
817 $entityObj = $em->getEntityByFilter($filter, $posType);
818
819 // The filter may lack entity/e_name (e.g. a parent_filter reference
820 // to a filter that is absent from the rendered set)
821 if (!$entityObj) {
822 return [];
823 }
824
825 // Exclude or include terms
826 $isInclude = (isset($filter['include']) && $filter['include'] === 'yes');
827 $entityObj->setExcludedTerms($filter['exclude'], $isInclude);
828
829 $terms = $entityObj->getTerms();
830
831 return apply_filters('wpc_items_after_calc_term_count', $terms);
832 }
833
834 function flrt_str_replace($string = '', $search_replace = array())
835 {
836 $ignore = array();
837 unset($search_replace['']);
838
839 foreach ($search_replace as $search => $replace) {
840 if (in_array($search, $ignore)) {
841 continue;
842 }
843 if (strpos($string, $search) === false) {
844 continue;
845 }
846 $string = str_replace($search, $replace, $string);
847 $ignore[] = $replace;
848 }
849
850 return $string;
851 }
852
853 function flrt_string_polyfill($data) {
854 return map_deep( $data,'flrt_string_polyfill_body');
855 }
856
857 function flrt_string_polyfill_body( $string ){
858
859 if ( ! is_string( $string ) ) {
860 return $string;
861 }
862
863 $str = preg_replace('/\x00|<[^>]*>?/', '', $string );
864 return str_replace( ["'", '"'], ['&#39;', '&#34;'], $str );
865 }
866
867 if (!function_exists('flrt_set_transient')) {
868 function flrt_set_transient($transient, $value, $expiration = 0)
869 {
870 if (defined('FLRT_SET_TRANSIENT_ENABLED') && FLRT_SET_TRANSIENT_ENABLED) {
871 set_transient($transient, $value, $expiration);
872 }
873 }
874 }
875
876 if (!function_exists('flrt_get_transient')) {
877 function flrt_get_transient($transient)
878 {
879 if (defined('FLRT_SET_TRANSIENT_ENABLED') && FLRT_SET_TRANSIENT_ENABLED) {
880 return get_transient($transient);
881 }
882 return false;
883 }
884 }
885
886 if (!class_exists('FlrtWooDiscountRules')) {
887 class FlrtWooDiscountRules
888 {
889
890 protected $rules;
891
892 protected $base;
893 protected $rule_helper;
894 protected $discount_calculator;
895 protected $manage_discount;
896
897 //public $filter;
898 public function __construct()
899 {
900 $this->base = new Wdr\App\Controllers\Base();
901 $this->rule_helper = new Wdr\App\Helpers\Rule();
902 $this->manage_discount = new Wdr\App\Controllers\ManageDiscount();
903 $this->rules = $this->manage_discount->getDiscountRules();
904 $this->discount_calculator = new Wdr\App\Controllers\DiscountCalculator($this->rule_helper->getAvailableRules($this->base->getAvailableConditions()));
905 }
906
907 public function getProductPriceToDisplay($product)
908 {
909 return $this->discount_calculator->getProductPriceToDisplay($product, 1);
910 }
911 }
912
913 function flrt_woo_discount_rules_class()
914 {
915 return new FlrtWooDiscountRules();
916 }
917 }
918
919
920 if (!function_exists('flrt_is_sitemap_exists')) {
921 function flrt_is_sitemap_exists()
922 {
923 $filepath = rtrim(FLRT_XML_PATH, '/\\') . '/filter-sitemap-index.xml';
924 return file_exists($filepath);
925 }
926 }
927 if (!function_exists('flrt_get_index_sitemap')) {
928 function flrt_get_index_sitemap()
929 {
930 return fltr_get_url_from_absolute_path(FLRT_XML_PATH . '/filter-sitemap-index.xml');
931 }
932 }
933
934 if (!function_exists('fltr_get_url_from_absolute_path')) {
935 function fltr_get_url_from_absolute_path($absolute_path)
936 {
937 $wp_root_path = realpath(ABSPATH);
938 $wp_url = site_url();
939
940 $file_path = realpath($absolute_path);
941
942 if (!$file_path || strpos($file_path, $wp_root_path) !== 0) {
943 return false;
944 }
945
946
947 $relative_path = str_replace($wp_root_path, '', $file_path);
948 $relative_path = str_replace('\\', '/', $relative_path);
949
950 return rtrim($wp_url, '/') . $relative_path;
951 }
952 }
953
954 if (!function_exists('flrt_has_filter_seo_rules')) {
955 function flrt_has_filter_seo_rules()
956 {
957 $args = [
958 'post_type' => 'filter-seo-rule',
959 'post_status' => 'publish',
960 'posts_per_page' => 1,
961 'fields' => 'ids',
962 'no_found_rows' => true,
963 ];
964
965 $query = new WP_Query($args);
966 return $query->have_posts();
967 }
968 }
969
970 if (!function_exists('flrt_get_last_modified_filter_seo_rule')) {
971 function flrt_get_last_modified_filter_post_type($post_type)
972 {
973 global $wpdb;
974 $sql = "
975 SELECT post_modified
976 FROM {$wpdb->posts}
977 WHERE post_type = '%s'
978 AND post_status IN ('publish', 'trash')
979 ORDER BY post_modified DESC
980 LIMIT 1";
981 $query = $wpdb->prepare($sql, $post_type);
982 $last_modified = $wpdb->get_var($query);
983
984 if (!$last_modified) {
985 return false;
986 }
987 return $last_modified;
988 }
989 }
990
991 if (!function_exists('flrt_check_to_update_xml')) {
992 function flrt_check_to_update_xml()
993 {
994 $wpc_xml_write_date = get_option('wpc_xml_write_date');
995 if (!$wpc_xml_write_date) return false;
996
997 $last_modified_filter_seo_rule = flrt_get_last_modified_filter_post_type(FLRT_SEO_RULES_POST_TYPE);
998 $last_modified_filter_set = flrt_get_last_modified_filter_post_type(FLRT_FILTERS_SET_POST_TYPE);
999
1000 if (!$last_modified_filter_seo_rule && !$last_modified_filter_set) return false;
1001
1002 $wpc_xml_write_date = strtotime($wpc_xml_write_date);
1003 $last_modified_filter_seo_rule = strtotime($last_modified_filter_seo_rule);
1004 $last_modified_filter_set = strtotime($last_modified_filter_set);
1005
1006 if ($last_modified_filter_seo_rule > $wpc_xml_write_date || $last_modified_filter_set > $wpc_xml_write_date) {
1007 return true;
1008 }
1009 if ($wpc_xml_write_date !== false && !flrt_has_filter_seo_rules()) {
1010 return true;
1011 }
1012 return false;
1013 }
1014 }
1015
1016
1017 if (!function_exists('flrt_post_type_underline_transform')) {
1018 function flrt_post_type_underline_transform($post_type)
1019 {
1020 if (mb_strpos($post_type, '-') !== false) {
1021 return str_replace('-', '_', $post_type);
1022 }
1023 return $post_type;
1024 }
1025 }
1026
1027
1028 if (!function_exists('flrt_generate_unique_copy_title')) {
1029
1030 /**
1031 * Generates a unique copy title in the format:
1032 * "Base Title – {copy_text} {N}".
1033 *
1034 * @param string $original_title The original post title to derive the base title from.
1035 * If it already ends with "– {copy_text} N", that suffix is stripped.
1036 * @param string $post_type The WordPress post type within which to check for duplicate titles.
1037 * By default expects the FLRT_FILTERS_SET_POST_TYPE constant.
1038 * @param string $copy_text The suffix text for copies (e.g., 'copy', 'duplicate').
1039 * @param int $start_number Numbering threshold:
1040 * - if 0 (default), the first copy gets number "1";
1041 * - if 1, the first copy has no number; numbering appears only when
1042 * a "… – {copy_text} 1" already exists.
1043 *
1044 * @return string The generated unique copy title.
1045 */
1046
1047 function flrt_generate_unique_copy_title($original_title, $post_type = FLRT_FILTERS_SET_POST_TYPE, $copy_text = 'copy', $number_position = true)
1048 {
1049 global $wpdb;
1050 if ($number_position) {
1051 $preg_match_pattern = '/^(.*) – ' . preg_quote($copy_text, '/') . ' \d+$/';
1052 }
1053
1054 if (!$number_position) {
1055 $preg_match_pattern = '/^(.*) \d+ – ' . preg_quote($copy_text, '/') . '$/';
1056 }
1057
1058 if (preg_match($preg_match_pattern, $original_title, $matches)) {
1059 $base_title = $matches[1];
1060 } else {
1061 $base_title = $original_title;
1062 }
1063
1064 if ($number_position) {
1065 $copy_pattern = $wpdb->esc_like($base_title) . '' . $wpdb->esc_like($copy_text) . '%';
1066 $titles = $wpdb->get_col(
1067 $wpdb->prepare(
1068 "SELECT post_title FROM $wpdb->posts
1069 WHERE (post_title = %s OR post_title LIKE %s)
1070 AND post_type = %s",
1071 $base_title,
1072 $copy_pattern,
1073 $post_type
1074 )
1075 );
1076 }
1077 if (!$number_position) {
1078 $copy_pattern = $wpdb->esc_like($base_title) . ' % – ' . $wpdb->esc_like($copy_text);
1079 $titles = $wpdb->get_col(
1080 $wpdb->prepare(
1081 "SELECT post_title FROM $wpdb->posts
1082 WHERE post_title LIKE %s
1083 AND post_type = %s",
1084 $copy_pattern,
1085 $post_type
1086 )
1087 );
1088 }
1089
1090 $max_copy_number = 0;
1091
1092 if ($number_position) {
1093 $preg_match_pattern_title = '/^' . preg_quote($base_title, '/') . '' . preg_quote($copy_text, '/') . ' (\d+)$/';
1094
1095 }
1096 if (!$number_position) {
1097 $preg_match_pattern_title = '/^' . preg_quote($base_title, '/') . ' (\d+) – ' . preg_quote($copy_text, '/') . '$/';
1098 }
1099
1100 foreach ($titles as $title) {
1101 if (preg_match($preg_match_pattern_title, $title, $m)) {
1102 if (isset($m[1]) && is_numeric($m[1])) {
1103 $num = intval($m[1]);
1104 if ($num > $max_copy_number) {
1105 $max_copy_number = $num;
1106 }
1107 }
1108 }
1109 }
1110
1111 $new_number = $max_copy_number + 1;
1112
1113 if ($number_position) {
1114 $text = $base_title . '' . $copy_text . ' ' . $new_number;
1115 }
1116 if (!$number_position) {
1117 $text = $base_title . ' ' . $new_number . '' . $copy_text;
1118 }
1119 return $text;
1120 }
1121 }
1122
1123 if (!function_exists('flrt_get_delete_set_transient')) {
1124 function flrt_refresh_temp_transient($set_name, $data)
1125 {
1126 if (get_transient($set_name) !== false) {
1127 delete_transient($set_name);
1128 }
1129 set_transient($set_name, $data, 300);
1130 }
1131 }
1132
1133 if (!function_exists('flrt_view_admin_error')) {
1134 function flrt_view_admin_error($text)
1135 {
1136 $error_str = '<div class="notice notice-error is-dismissible"><p>%s</p></div>';
1137 printf(
1138 $error_str,
1139 $text
1140 );
1141 }
1142 }
1143 if (!function_exists('flrt_export_setting_icon')) {
1144 function flrt_export_setting_icon()
1145 {
1146 return '<svg xmlns="http://www.w3.org/2000/svg" fill="#99a2b2" width="19px" height="19px" viewBox="0 0 24 20"><polyline id="primary" points="15 3 21 3 21 9" style="fill: none; stroke: rgb(153,162,178); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"/><line id="primary-2" data-name="primary" x1="11" y1="13" x2="21" y2="3" style="fill: none; stroke: rgb(153,162,178); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"/><path id="primary-3" data-name="primary" d="M21,13v7a1,1,0,0,1-1,1H4a1,1,0,0,1-1-1V4A1,1,0,0,1,4,3h7" style="fill: none; stroke: rgb(153,162,178); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"/></svg>';
1147 }
1148 }
1149
1150 if (!function_exists('flrt_import_setting_icon')) {
1151 function flrt_import_setting_icon()
1152 {
1153 return '<svg xmlns="http://www.w3.org/2000/svg" fill="#000000" width="19px" height="19px" viewBox="0 0 24 23" id="wpc-import-icon"><polyline id="primary" points="17 13 11 13 11 7" style="fill: none; stroke: rgb(153,162,178); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"/><line id="primary-2" data-name="primary" x1="21" y1="3" x2="11" y2="13" style="fill: none; stroke: rgb(153,162,178); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"/><path id="primary-3" data-name="primary" d="M21,13v7a1,1,0,0,1-1,1H4a1,1,0,0,1-1-1V4A1,1,0,0,1,4,3h7" style="fill: none; stroke: rgb(153,162,178); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"/></svg>';
1154 }
1155 }
1156
1157 if (!function_exists('flrt_open_in_new_tab_icon')) {
1158 function flrt_open_in_new_tab_icon()
1159 {
1160 $icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 21" width="18" height="18" aria-hidden="true" focusable="false">
1161 <path d="M19.5 4.5h-7V6h4.44l-5.97 5.97 1.06 1.06L18 7.06v4.44h1.5v-7Zm-13 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-3H17v3a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h3V5.5h-3Z"></path></svg>';
1162 return wp_kses(
1163 $icon,
1164 array(
1165 'svg' => array(
1166 'xmlns' => true,
1167 'viewbox' => true,
1168 'width' => true,
1169 'height' => true,
1170 'aria-hidden' => true,
1171 'focusable' => true,
1172 ),
1173 'path' => array(
1174 'd' => true
1175 ),
1176 )
1177 );
1178 }
1179 }
1180
1181 if (!function_exists('flrt_vailable_in_pro_attr_link')) {
1182 function flrt_vailable_in_pro_attr_link($target_blank = false): string
1183 {
1184 $link = 'edit.php?post_type=' . FLRT_FILTERS_SET_POST_TYPE . '&page=flrt-pro';
1185 if ($target_blank) {
1186 $link .= '_target=blank';
1187 }
1188 return $link;
1189 }
1190 }
1191 if (!function_exists('flrt_pro_promo_label')) {
1192 function flrt_pro_promo_label($replace_class = false): string
1193 {
1194 $class = $replace_class ? 'wpc-pro-badge' : 'wpc-pro-badge-transparent';
1195 $label = ' <span class="' . $class . '">' . esc_html__('PRO', 'filter-everything') . '</span>';
1196
1197 return wp_kses($label, [
1198 'span' => [
1199 'class' => []
1200 ]
1201 ]);
1202 }
1203 }
1204
1205 if(!function_exists( 'flrt_unlock_icon')){
1206 function flrt_unlock_icon($width = '20px', $height = '20px', $color = '#3858E9')
1207 {
1208 return '<svg xmlns="http://www.w3.org/2000/svg" height="' . $height . '" viewBox="0 -960 960 960" width="' . $width . '" fill="' . $color . '"><path d="M264-624h336v-96q0-50-35-85t-85-35q-50 0-85 35t-35 85h-72q0-80 56.23-136 56.22-56 136-56Q560-912 616-855.84q56 56.16 56 135.84v96h24q29.7 0 50.85 21.15Q768-581.7 768-552v384q0 29.7-21.16 50.85Q725.68-96 695.96-96H263.72Q234-96 213-117.15T192-168v-384q0-29.7 21.15-50.85Q234.3-624 264-624Zm0 456h432v-384H264v384Zm216.21-120Q510-288 531-309.21t21-51Q552-390 530.79-411t-51-21Q450-432 429-410.79t-21 51Q408-330 429.21-309t51 21ZM264-168v-384 384Z"/></svg>';
1209 }
1210 }
1211
1212 if(!function_exists( 'flrt_crown_icon')){
1213 function flrt_crown_icon($width = '16px', $height = '16px', $color = '#FFFFFF')
1214 {
1215 return '<svg xmlns="http://www.w3.org/2000/svg" fill="' . $color . '" viewBox="0 0 16 16" width="' . $width . '" height="' . $height . '" class="wpc-crown-icon"><path fill="' . $color . '" d="M8.687 1.932c-.238-.634-1.136-.634-1.374 0l-1.642 4.38-3.167-2.11a.733.733 0 0 0-1.126.753L3.333 12h9.334l1.955-7.045a.733.733 0 0 0-1.126-.754L10.33 6.313zM6.654 7.49 8 3.899l1.346 3.59c.166.442.7.614 1.094.352l2.59-1.727-1.363 4.553H4.333L2.97 6.114 5.56 7.841a.733.733 0 0 0 1.094-.352m6.013 5.844H3.333v1.334h9.334z"></path></svg>';
1216 }
1217 }
1218
1219 if(!function_exists( 'flrt_unlock_in_pro')){
1220 function flrt_unlock_in_pro($button_text = '')
1221 {
1222 $string = '<a class="wpc-available-in-pro-button" href="' . admin_url(flrt_vailable_in_pro_attr_link()) . '">';
1223 $string .= !empty($button_text) ? $button_text . ' - ': '';
1224 $string .= flrt_unlock_icon();
1225 $string .= '<span>' . esc_html__('Unlock with PRO', 'filter-everything') . '</span></a>';
1226 return $string;
1227 }
1228 }
1229
1230
1231 add_filter('wpc_filter_default_fields', 'flrt_add_pro_promo_fields', 10, 2);
1232
1233
1234 add_action('wp_footer', function () {
1235 if (current_user_can(flrt_plugin_user_caps()))
1236 if (!wp_script_is('wpc-filter-everything')) {
1237 {
1238 ?>
1239 <script>
1240 jQuery(function ($) {
1241 $(document).on('click', '.wpc-open-close-filters-button', function (e) {
1242 e.preventDefault();
1243 let openCloseButton = $(this);
1244 let wpcButtonFilterSetError = openCloseButton.data('wpcButtonFilterSetError');
1245 let wpcButtonWidgetError = openCloseButton.data('wpcButtonWidgetError');
1246
1247 if (typeof wpcButtonFilterSetError !== 'undefined') {
1248 alert(wpcButtonFilterSetError);
1249 }
1250 if (typeof wpcButtonWidgetError !== 'undefined') {
1251 if (typeof window.wpcFilterWidgetActive === 'undefined') {
1252 alert(wpcButtonWidgetError);
1253 }
1254 }
1255 });
1256 });
1257 </script>
1258 <?php
1259 }
1260 }
1261 });
1262
1263 function flrt_log( $message, $data = null ) {
1264 if ( ! defined('WP_DEBUG_LOG') || ! WP_DEBUG_LOG ) {
1265 return;
1266 }
1267
1268 $log = '[Filter Everything] ' . $message;
1269
1270 if ( $data !== null ) {
1271 $log .= ' | ' . wp_json_encode( $data );
1272 }
1273
1274 error_log( $log );
1275 }
1276
1277 function prepare_wpc_filter_set_json_data($postData)
1278 {
1279 if(!empty($postData['wpc_filter_set_json_data'])){
1280 $postData = json_decode(stripslashes($postData['wpc_filter_set_json_data']), true);
1281 $postData['wpc_set_fields']['wp_filter_query_vars'] = wp_slash($postData['wpc_set_fields']['wp_filter_query_vars']);
1282 }
1283 return $postData;
1284 }
1285