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/wpc-third-party.php +1023 -34 1.2.41.9.7 View file →
@@ -1,18 +1,26 @@
1 1 <?php
2 2
3 -if ( ! defined('WPINC') ) {
4 - wp_die();
3 +if ( ! defined('ABSPATH') ) {
4 + exit;
5 5 }
6 6
7 7 function flrt_is_woocommerce()
8 8 {
9 - if( function_exists('is_woocommerce') ){
10 - return is_woocommerce();
9 + if( class_exists('WooCommerce') ){
10 + return true;
11 11 }
12 12 return false;
13 13 }
14 14
15 +function flrt_is_acf()
16 +{
17 + if( class_exists( 'ACF' ) ){
18 + return true;
19 + }
20 + return false;
21 +}
22 +
15 23 function flrt_get_mobile_width(){
16 24 return apply_filters( 'wpc_mobile_width', 768 );
17 25 }
18 26
@@ -24,19 +32,33 @@
24 32
25 33 function flrt_wp(){
26 34 $theme_dependencies = flrt_get_theme_dependencies();
27 35
28 - if( flrt_get_option('show_bottom_widget') === 'on' ) {
36 + if( flrt_get_option('mobile_filter_settings') === 'show_bottom_widget' ){
29 37
30 38 if( flrt_get_experimental_option('disable_buttons') !== 'on' ) {
31 39
32 - if (flrt_is_woocommerce()) {
33 - add_action('woocommerce_before_shop_loop', 'flrt_filters_button', 5);
34 - add_action('woocommerce_no_products_found', 'flrt_filters_button', 5);
40 + if (flrt_is_woocommerce()) { // It means WooCommerce installed
41 +
42 + if( is_woocommerce() ){ // It means is a WooCommerce page
43 + add_action('woocommerce_before_shop_loop', 'flrt_filters_button', 5);
44 + add_action('woocommerce_no_products_found', 'flrt_filters_button', 5);
45 + }else{
46 + if (isset($theme_dependencies['button_hook']) && is_array($theme_dependencies['button_hook'])) {
47 + foreach ($theme_dependencies['button_hook'] as $button_hook) {
48 + add_action($button_hook, function() use ($button_hook) {
49 + flrt_filters_button(0, '', $button_hook);
50 + }, 15);
51 + }
52 + }
53 + }
54 +
35 55 } else {
36 56 if (isset($theme_dependencies['button_hook']) && is_array($theme_dependencies['button_hook'])) {
37 57 foreach ($theme_dependencies['button_hook'] as $button_hook) {
38 - add_action($button_hook, 'flrt_filters_button', 15);
58 + add_action($button_hook, function() use ($button_hook) {
59 + flrt_filters_button(0, '', $button_hook);
60 + }, 15);
39 61 }
40 62 }
41 63 }
42 64 }
@@ -43,15 +65,14 @@
43 65
44 66 }
45 67
46 68 // Add selected terms to the top
47 - if( flrt_is_woocommerce() ) {
48 - add_action('woocommerce_no_products_found', 'flrt_add_selected_terms_above_the_top');
49 - add_action('woocommerce_before_shop_loop', 'flrt_add_selected_terms_above_the_top', 5);
50 - }else {
51 - if (isset($theme_dependencies['chips_hook']) && is_array($theme_dependencies['chips_hook'])) {
52 - foreach ($theme_dependencies['chips_hook'] as $chips_hook) {
53 - add_action($chips_hook, 'flrt_add_selected_terms_above_the_top');
69 + $chips_hooks = flrt_get_option('show_terms_in_content', []);
70 +
71 + if( $chips_hooks ){
72 + if( is_array( $chips_hooks ) && ! empty( $chips_hooks ) ){
73 + foreach ( $chips_hooks as $hook ){
74 + add_action( $hook, 'flrt_add_selected_terms_above_the_top' );
54 75 }
55 76 }
56 77 }
57 78 }
@@ -63,11 +84,9 @@
63 84 }
64 85
65 86 function flrt_add_selected_terms_above_the_top()
66 87 {
67 - if( flrt_get_option( 'show_terms_in_content' ) === 'on' ){
68 - flrt_show_selected_terms(true);
69 - }
88 + flrt_show_selected_terms(true);
70 89 }
71 90
72 91 function flrt_get_theme_dependencies(){
73 92 $current_theme = strtolower( get_template() );
@@ -143,8 +162,15 @@
143 162 'primary_color' => '#0073aa',
144 163 'button_hook' => '',
145 164 'chips_hook' => ''
146 165 ),
166 + 'twentytwentyfive' => array(
167 + 'posts_container' => '#wp--skip-link--target',
168 + 'sidebar_container' => '',
169 + 'primary_color' => '#0073aa',
170 + 'button_hook' => '',
171 + 'chips_hook' => ''
172 + ),
147 173 'twentytwenty' => array(
148 174 'posts_container' => '#site-content',
149 175 'sidebar_container' => '',
150 176 'primary_color' => '#cd2653',
@@ -305,15 +331,30 @@
305 331 'primary_color' => '',
306 332 'button_hook' => '',
307 333 'chips_hook' => ''
308 334 ),
309 - 'divi' => array(
310 - 'posts_container' => '#primary',
335 + 'woodmart' => array(
336 + 'posts_container' => '.site-content',
311 337 'sidebar_container' => '#secondary',
312 - 'primary_color' => '',
338 + 'primary_color' => '#83b735',
313 339 'button_hook' => '',
314 - 'chips_hook' => ''
340 + 'chips_hook' => array( 'woodmart_shop_filters_area', 'woodmart_main_loop')
315 341 ),
342 +
343 + 'bricks' => array(
344 + 'posts_container' => '.brxe-container',
345 + 'sidebar_container' => '',
346 + 'primary_color' => '',
347 + 'button_hook' => array('bricks_after_header'),
348 + 'chips_hook' => ''
349 + ),
350 + 'bb-theme' => array(
351 + 'posts_container' => '#fl-main-content',
352 + 'sidebar_container' => '',
353 + 'primary_color' => '',
354 + 'button_hook' => array('fl_content_open'),
355 + 'chips_hook' => ''
356 + ),
316 357 );
317 358
318 359 $theme_dependencies = apply_filters( 'wpc_theme_dependencies', $theme_dependencies );
319 360
@@ -363,31 +404,979 @@
363 404 add_filter('wpc_theme_posts_container', 'flrt_set_posts_container');
364 405
365 406 // Set correct theme color
366 407 add_filter('wpc_theme_color', 'flrt_set_theme_color');
408 +
409 + //
410 + if( flrt_is_acf() ) {
411 + add_filter( 'wpc_pre_save_filter', 'flrt_maybe_acf_field' );
412 + add_filter( 'wpc_default_sorting_terms', 'flrt_acf_terms_order', 10, 2 );
413 + }
367 414 }
368 415 }
369 416 add_action('init', 'flrt_init');
370 417
371 -add_filter( 'elementor/theme/posts_archive/query_posts/query_vars', 'flrt_fix_elementor_query_args' );
418 +function flrt_wpml_active(){
419 + if( defined('WPML_PLUGIN_BASENAME') ){
420 + return true;
421 + }
422 + return false;
423 +}
424 +
425 +add_action( 'elementor/editor/before_enqueue_scripts', 'flrt_include_elementor_script' );
426 +function flrt_include_elementor_script(){
427 + $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
428 + $ver = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? rand(0, 1000) : FLRT_PLUGIN_VER;
429 + wp_enqueue_script('wpc-widgets', FLRT_PLUGIN_DIR_URL . 'assets/js/wpc-widgets' . $suffix . '.js', ['jquery', 'jquery-ui-sortable'], $ver, true );
430 + wp_enqueue_style('wpc-widgets', FLRT_PLUGIN_DIR_URL . 'assets/css/wpc-widgets' . $suffix . '.css', [], $ver );
431 +
432 + $l10n = array(
433 + 'wpcItemNum' => esc_html__( 'Item #', 'filter-everything')
434 + );
435 + wp_localize_script( 'wpc-widgets', 'wpcWidgets', $l10n );
436 +}
437 +
438 +/*
439 + * Polylang compatibility functions
440 + * */
441 +function flrt_maybe_has_translation( $post_id, $lang = '' ){
442 +
443 + if( function_exists( 'pll_get_post' ) ){
444 + $translation_post_id = pll_get_post( $post_id, $lang );
445 + if( $translation_post_id ){
446 + $post_id = $translation_post_id;
447 + }
448 + }
449 +
450 + return $post_id;
451 +}
452 +
453 +function flrt_pll_pro_active(){
454 + if ( defined('POLYLANG_PRO') ) {
455 + return true;
456 + }
457 + return false;
458 +}
459 +// Allow Filter Sets to be translatable if Polylang PRO activated
460 +// This make sense for Filter Sets with common locations only
461 +add_action( 'after_setup_theme', 'flrt_pll_init', 20 );
462 +function flrt_pll_init(){
463 + if( flrt_pll_pro_active() && defined('FLRT_ALLOW_PLL_TRANSLATIONS') && FLRT_ALLOW_PLL_TRANSLATIONS ){
464 + add_filter( 'pll_get_post_types', 'flrt_add_cpt_to_pll', 10, 2 );
465 + }
466 +}
467 +
468 +function flrt_add_cpt_to_pll( $post_types, $is_settings ) {
469 + if ( $is_settings ) {
470 + unset( $post_types[ FLRT_FILTERS_SET_POST_TYPE ], $post_types[ FLRT_FILTERS_SET_POST_TYPE ] );
471 + } else {
472 + $post_types[ FLRT_FILTERS_SET_POST_TYPE ] = FLRT_FILTERS_SET_POST_TYPE;
473 + if( defined('FLRT_FILTERS_PRO') && FLRT_FILTERS_PRO ) {
474 + $post_types[ FLRT_SEO_RULES_POST_TYPE ] = FLRT_SEO_RULES_POST_TYPE;
475 + }
476 + }
477 + return $post_types;
478 +}
479 +
480 +/**
481 + * Adds compatibility the Price filter with multi-currency plugins WOOCS and CURCY
482 + * @since 1.7.12
483 + */
484 +add_action('init', 'flrt_add_currencies_support');
485 +function flrt_add_currencies_support() {
486 + if ( flrt_is_woocommerce() ) {
487 + // For the FOX - Currency Switcher Professional for WooCommerce
488 + if ( defined( 'WOOCS_PLUGIN_NAME' ) && WOOCS_PLUGIN_NAME ) {
489 + // Converts values into selected currency. Visible in the range slider form
490 + add_filter( 'wpc_set_num_shift', 'flrt_set_woocs_shift', 10, 2 );
491 + function flrt_set_woocs_shift( $value, $entity_name ) {
492 + global $WOOCS;
493 +
494 + if ( $entity_name === '_price' ) {
495 + if ( property_exists( $WOOCS, 'default_currency' ) && property_exists( $WOOCS, 'current_currency' ) ) {
496 + if ( ! $WOOCS->default_currency || ! $WOOCS->current_currency ) {
497 + return $value;
498 + }
499 +
500 + if ( $WOOCS->default_currency !== $WOOCS->current_currency && method_exists( $WOOCS, 'convert_from_to_currency' ) ) {
501 + $value = $WOOCS->convert_from_to_currency( $value, $WOOCS->default_currency, $WOOCS->current_currency );
502 + }
503 + }
504 + }
505 +
506 + return $value;
507 + }
508 +
509 + // Converts values back to default currency for WP_Query
510 + add_filter( 'wpc_unset_num_shift', 'flrt_unset_woocs_shift', 10, 2 );
511 + function flrt_unset_woocs_shift( $value, $entity_name ) {
512 + global $WOOCS;
513 +
514 + if ( $entity_name === '_price' ) {
515 + if ( property_exists( $WOOCS, 'default_currency' ) && property_exists( $WOOCS, 'current_currency' ) ) {
516 + if ( ! $WOOCS->default_currency || ! $WOOCS->current_currency ) {
517 + return $value;
518 + }
519 +
520 + $precision = 2;
521 + if( method_exists( $WOOCS, 'get_currency_price_num_decimals' ) ) {
522 + $precision = $WOOCS->get_currency_price_num_decimals( $WOOCS->current_currency, $WOOCS->price_num_decimals );
523 + }
524 +
525 + if ( $WOOCS->default_currency !== $WOOCS->current_currency && method_exists( $WOOCS, 'convert_from_to_currency' ) ) {
526 + $value = $WOOCS->convert_from_to_currency( $value, $WOOCS->current_currency, $WOOCS->default_currency );
527 + $value = round( $value, $precision );
528 + }
529 + }
530 + }
531 +
532 + return $value;
533 + }
534 + }
535 +
536 + // For the CURCY - Multi Currency for WooCommerce
537 + if ( defined( 'WOOMULTI_CURRENCY_F_VERSION' ) && WOOMULTI_CURRENCY_F_VERSION ) {
538 + // Converts values into selected currency. Visible in the range slider form
539 + add_filter( 'wpc_set_num_shift', 'flrt_set_curcy_shift', 10, 2 );
540 + function flrt_set_curcy_shift( $value, $entity_name ) {
541 +
542 + if ( $entity_name === '_price' ) {
543 + if ( method_exists( 'WOOMULTI_CURRENCY_F_Data', 'get_ins' ) && function_exists('wmc_get_price' ) ) {
544 + $settings = WOOMULTI_CURRENCY_F_Data::get_ins();
545 +
546 + if ( ! method_exists( $settings, 'get_current_currency' ) || ! method_exists( $settings, 'get_default_currency' ) ) {
547 + return $value;
548 + }
549 +
550 + $currency = $settings->get_current_currency();
551 + $default_currency = $settings->get_default_currency();
552 +
553 + if ( $currency !== $default_currency ) {
554 + $value = wmc_get_price( $value, $currency );
555 + }
556 + }
557 + }
558 +
559 + return $value;
560 + }
561 +
562 + // Converts values back to default currency for WP_Query
563 + add_filter( 'wpc_unset_num_shift', 'flrt_unset_curcy_shift', 10, 2 );
564 + function flrt_unset_curcy_shift( $value, $entity_name ) {
565 +
566 + if ( $entity_name === '_price' ) {
567 + if ( method_exists( 'WOOMULTI_CURRENCY_F_Data', 'get_ins' ) && function_exists('wmc_revert_price' ) ) {
568 + $settings = WOOMULTI_CURRENCY_F_Data::get_ins();
569 +
570 + if ( ! method_exists( $settings, 'get_current_currency' ) || ! method_exists( $settings, 'get_default_currency' ) ) {
571 + return $value;
572 + }
573 +
574 + $currency = $settings->get_current_currency();
575 + $default_currency = $settings->get_default_currency();
576 +
577 + if ( $currency !== $default_currency ) {
578 + $value = wmc_revert_price( $value );
579 + }
580 + }
581 + }
582 +
583 + return $value;
584 + }
585 + }
586 + }
587 +}
588 +
589 +/**
590 + * Removes WooCommerce Product Query post clauses for the price filter
591 + * @since 1.7.12
592 + */
593 +function flrt_remove_product_query_post_clauses( $wp_query, $WC_query ) {
594 + $wpManager = \FilterEverything\Filter\Container::instance()->getWpManager();
595 +
596 + if ( $wpManager->getQueryVar( 'wpc_is_filter_request' ) ) {
597 + remove_filter('posts_clauses', array( $WC_query, 'product_query_post_clauses' ), 10, 2);
598 + }
599 +
600 + return $wp_query;
601 +}
602 +
603 +add_filter('wpc_check_broken_filter', function ($is_broken, $query) {
604 + $detector_class = 'FilterEverything\\Filter\\WP_Query_Source_Detector';
605 + $is_allowed_method = 'is_allowed';
606 + $source_var = 'flrt_detected_source';
607 +
608 + if (defined('FLRT_FILTERS_PRO')) {
609 + if (defined('FLRT_PRO_BUILDER_KEY')) {
610 + $builder_key = apply_filters('wpc_builder_key_pro', $query->get($source_var), constant('FLRT_PRO_BUILDER_KEY'));
611 +
612 + if ($detector_class::$is_allowed_method($builder_key)) {
613 + return false;
614 + }
615 + }
616 + }
617 +
618 + if (!defined('FLRT_FILTERS_PRO')) {
619 + $builder_key = apply_filters('wpc_builder_key', $query->get($source_var), $detector_class::$builder_key);
620 +
621 + if ($detector_class::$is_allowed_method($builder_key)) {
622 + return false;
623 + }
624 + }
625 +
626 + return true;
627 +}, 10, 2);
628 +
629 +function flrt_is_dokan() {
630 + return function_exists('dokan');
631 +}
632 +
633 +function flrt_maybe_acf_field( $filter ){
634 + if( $filter['entity'] === 'post_meta' ) {
635 + global $wpdb;
636 + // Try to check if this is ACF field
637 + $sql[] = "SELECT {$wpdb->posts}.ID FROM {$wpdb->posts}";
638 + $sql[] = "WHERE {$wpdb->posts}.post_excerpt = %s";
639 + $sql[] = "AND {$wpdb->posts}.post_type = %s";
640 + $sql[] = "ORDER BY {$wpdb->posts}.ID ASC";
641 +
642 + $sql = implode(' ', $sql );
643 + $sql = $wpdb->prepare( $sql, $filter['e_name'], 'acf-field' );
644 + $results = $wpdb->get_results( $sql, ARRAY_A );
645 +
646 + if ( ! empty( $results ) ) {
647 + $ids = [];
648 + foreach ( $results as $single_result ) {
649 + if( isset( $single_result['ID'] ) ){
650 + $ids[] = $single_result['ID'];
651 + }
652 + }
653 + $acf_field_ids = implode( ',', $ids );
654 +
655 + if( ! empty( $acf_field_ids ) ){
656 + $filter['acf_fields'] = $acf_field_ids;
657 + }
658 + }
659 + }
660 +
661 + return $filter;
662 +}
663 +
664 +function flrt_acf_terms_order( $entity_items, $filter ){
665 +
666 + if( isset( $filter['acf_fields'] ) && $filter['acf_fields'] !== '' ) {
667 + global $wpdb;
668 + // Here we have to get ACF fields by their IDs and sort terms in according to
669 + // the order in the field.
670 + $sql = [];
671 + $acf_field_ids = preg_replace( '/^[\d]\,/', '', $filter['acf_fields'] );
672 + $sql[] = "SELECT {$wpdb->posts}.post_content FROM {$wpdb->posts}";
673 + $sql[] = "WHERE {$wpdb->posts}.ID IN( {$acf_field_ids} )";
674 + $sql[] = "ORDER BY {$wpdb->posts}.ID ASC";
675 + $sql = implode(' ', $sql );
676 +
677 + $results = $wpdb->get_results( $sql, ARRAY_A );
678 +
679 + if( empty( $results ) ) {
680 + return $entity_items;
681 + }
682 +
683 + $field_terms = [];
684 + foreach ( $results as $acf_field ){
685 + if( isset( $acf_field['post_content'] ) ) {
686 + $field_options = maybe_unserialize( $acf_field['post_content'] );
687 +
688 + if( isset( $field_options['choices'] ) && is_array( $field_options['choices'] ) ){
689 + foreach ( $field_options['choices'] as $value => $label ){
690 + if( ! isset( $field_terms[$value] ) ) {
691 + $field_terms[$value] = $label;
692 + }
693 + }
694 + }
695 + }
696 + }
697 +
698 + $current_items = $entity_items;
699 + $sorted_items = [];
700 +
701 + foreach ( $field_terms as $tslug => $tvalue ) {
702 + $tslug = sanitize_title( $tslug );
703 +
704 + if( isset( $current_items[ $tslug ] ) ) {
705 + $term_object = $current_items[ $tslug ];
706 + $term_object->name = $tvalue;
707 + $sorted_items[ $tslug ] = $term_object;
708 + unset( $current_items[ $tslug ] );
709 + }
710 + }
711 +
712 + if( ! empty( $current_items ) ){
713 + foreach ( $current_items as $slug => $item ){
714 + $sorted_items[$slug] = $item;
715 + }
716 + }
717 + $entity_items = $sorted_items;
718 + }
719 +
720 + return $entity_items;
721 +}
722 +
723 +/**
724 + * Adds correct pagination URLs to the Load more/Infinite scroll button for Elementor posts block
725 + */
726 +add_filter( 'elementor/widget/render_content', 'flrt_elementor_load_more_anchor', 10, 2 );
727 +function flrt_elementor_load_more_anchor( $widget_content, $module ){
728 + // Nothing is needed if there is no Filter request
729 + if( ! flrt_is_filter_request() ){
730 + return $widget_content;
731 + }
732 +
733 + if( $module instanceof ElementorPro\Modules\Posts\Widgets\Posts ){
734 + global $wp_rewrite;
735 +
736 + $urlManager = \FilterEverything\Filter\Container::instance()->getUrlManager();
737 + $current_page = $module->get_current_page();
738 + $next_page = intval( $current_page ) + 1;
739 + $data_next_page = $module->get_wp_link_page( $next_page );
740 + $rewrite = $wp_rewrite->wp_rewrite_rules();
741 +
742 + /*
743 + * Loop widgets (Loop Grid/Loop Carousel) paginate with a widget-scoped
744 + * query arg (?e-page-<widget id>=N) instead of /page/N/, so the
745 + * path-based base replacement below never matches. Swap the base for
746 + * the filtered URL directly and carry the pagination args over.
747 + */
748 + $next_page_args = array();
749 + $next_url_parts = parse_url( $data_next_page );
750 + if ( isset( $next_url_parts['query'] ) ) {
751 + parse_str( $next_url_parts['query'], $next_page_args );
752 + }
753 +
754 + $e_page_args = array();
755 + foreach ( $next_page_args as $arg_key => $arg_value ) {
756 + if ( strpos( $arg_key, 'e-page-' ) === 0 ) {
757 + $e_page_args[ $arg_key ] = $arg_value;
758 + }
759 + }
760 +
761 + if ( ! empty( $e_page_args ) ) {
762 + // The current request may already contain pagination args (page 2+)
763 + $data_next_page = remove_query_arg( array_keys( $e_page_args ), $urlManager->getFormActionOrFullPageUrl( true ) );
764 + foreach ( $e_page_args as $arg_key => $arg_value ) {
765 + $data_next_page = flrt_add_query_arg( $arg_key, $arg_value, $data_next_page );
766 + }
767 + } else if ( defined('FLRT_PERMALINKS_ENABLED') && FLRT_PERMALINKS_ENABLED ) {
768 + // This is ok when permalinks are enabled.
769 + $data_next_page = str_replace( $urlManager->removePaginationBase( $data_next_page ), $urlManager->getFormActionOrFullPageUrl(), $data_next_page );
770 + $uri_components = explode( "?", $urlManager->getFormActionOrFullPageUrl( true ) );
771 + if( isset( $uri_components[1] ) ){
772 + $data_next_page = $data_next_page . '?'.$uri_components[1];
773 + }
774 + } else if ( ! FLRT_PERMALINKS_ENABLED && ! empty( $rewrite ) ) {
775 + // WordPress permalinks are Enabled, but Filter Everything permalinks are Disabled
776 + $uri_components = explode( "?", $urlManager->getFormActionOrFullPageUrl( true ) );
777 + // If URI part after ? exists
778 + if( isset( $uri_components[1] ) ){
779 + $data_next_page = $data_next_page . '?'.$uri_components[1];
780 + }
781 + } else {
782 + // No permalinks at all
783 + $current_page_url = $urlManager->getFormActionOrFullPageUrl( true );
784 + $url_parts = parse_url( $data_next_page );
785 + if( isset( $url_parts['query'] ) ){
786 + parse_str( $url_parts['query'], $params );
787 + if( isset( $params['page'] ) && $params['page'] ) {
788 + $data_next_page = flrt_add_query_arg( 'page', $params['page'], $current_page_url );
789 + }
790 + }
791 + }
792 +
793 + /*
794 + * $data_next_page carries the GET part of the current request, and
795 + * getFormActionOrFullPageUrl() hands it back URL-DECODED (parse_str()
796 + * + build_query(), which does not re-encode). Printed raw, a request
797 + * like ?x="><script>… closed the attribute and injected markup —
798 + * reflected XSS, reported by Wordfence (CVE-2026-87869). esc_url()
799 + * drops the characters that can break out of the attribute and
800 + * re-encodes the ampersands for the HTML context.
801 + * A callback instead of a replacement string: in a string "$1" or
802 + * "\1" coming from the URL would be read as a backreference.
803 + */
804 + $next_page_attr = 'data-next-page="' . esc_url( $data_next_page ) . '"';
805 + $widget_content = preg_replace_callback(
806 + '%data-next-page\="[^"]+"%',
807 + function () use ( $next_page_attr ) {
808 + return $next_page_attr;
809 + },
810 + $widget_content
811 + );
812 + }
813 +
814 + return $widget_content;
815 +}
816 +
817 +add_filter( 'wpc_remove_pagination_base', 'flrt_remove_pagination_base' );
818 +function flrt_remove_pagination_base( $url ){
819 + global $wp_rewrite;
820 +
821 + $rewrite = $wp_rewrite->wp_rewrite_rules();
822 + $url = ( ! empty( $rewrite ) ) ? user_trailingslashit( $url ) : rtrim( $url, '/' ) . '/';
823 +
824 + return $url;
825 +}
826 +
827 +function flrt_is_woo_discount_rules()
828 +{
829 + $check_woo_discount_rules = apply_filters('flrt_woo_discount_rules_enabled', true);
830 + if(!$check_woo_discount_rules) return false;
831 + if( flrt_is_woocommerce() &&
832 + class_exists('Wdr\App\Helpers\Woocommerce') &&
833 + class_exists('Wdr\App\Controllers\Base') &&
834 + class_exists('Wdr\App\Helpers\Rule') &&
835 + class_exists('Wdr\App\Controllers\ManageDiscount') &&
836 + class_exists('Wdr\App\Controllers\DiscountCalculator') &&
837 + class_exists('Wdr\App\Models\DBTable')
838 + )
839 + {
840 + $tb_table = new Wdr\App\Models\DBTable();
841 + $wdr_rules = $tb_table::getRules();
842 + if(!empty($wdr_rules)){
843 + $discount_types = [];
844 + foreach ($wdr_rules as $rule){
845 + $discount_types[] = $rule->discount_type;
846 + }
847 + $allowed_discount_types = ['wdr_simple_discount', 'wdr_bulk_discount', 'wdr_set_discount'];
848 + if(!empty(array_intersect($allowed_discount_types, $discount_types))){
849 + return true;
850 + }
851 + }
852 + }
853 + return false;
854 +}
855 +
856 +function flrt_is_elementor_active()
857 +{
858 + return did_action( 'elementor/loaded' );
859 +}
860 +
861 +if ( flrt_is_elementor_active() ) {
862 + function wpc_add_elementor_widget_categories( $elements_manager ) {
863 + $pro_text = (defined('FLRT_FILTERS_PRO') && FLRT_FILTERS_PRO)
864 + ? ' ' .esc_html__('PRO', 'filter-everything')
865 + : '';
866 +
867 + $elements_manager->add_category(
868 + 'filter-everything',
869 + [
870 + 'title' => esc_html__( 'Filter Everything', 'filter-everything' ) . $pro_text,
871 + 'icon' => 'fa-solid fa-filter',
872 + ]
873 + );
874 + }
875 + add_action( 'elementor/elements/categories_registered', 'wpc_add_elementor_widget_categories' );
876 +
877 + function wpc_register_elementor_widget( $widgets_manager ) {
878 +
879 + flrt_include('src/Frontend/Builders/ElementorWidgets/ChipsElementorWidget.php');
880 + flrt_include('src/Frontend/Builders/ElementorWidgets/FiltersElementorWidget.php');
881 + flrt_include('src/Frontend/Builders/ElementorWidgets/SortingElementorWidget.php');
882 +
883 + $widgets_manager->register( new \FilterEverything\Filter\ChipsElementorWidget() );
884 + $widgets_manager->register( new \FilterEverything\Filter\FiltersElementorWidget() );
885 + $widgets_manager->register( new \FilterEverything\Filter\SortingElementorWidget() );
886 +
887 + }
888 + add_action( 'elementor/widgets/register', 'wpc_register_elementor_widget' );
889 +
890 + add_action( 'elementor/editor/after_enqueue_styles', function() {
891 + wp_enqueue_style(
892 + 'filter-everything-elementor',
893 + FLRT_PLUGIN_DIR_URL . 'assets/css/elementor-icon.css'
894 + );
895 + $css = '
896 + .wpc-fe-icon {
897 + background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUzIDUzIiBpZD0iTGF5ZXJfMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MyA1MyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNTMgNTM7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCiAgICAgICAgICAgICAgIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQogICAgICAgICAgICAgICAgLnN0MHtkaXNwbGF5Om5vbmU7fQ0KICAgICAgICAgICAgICAgIC5zdDF7ZGlzcGxheTppbmxpbmU7fQ0KICAgICAgICAgICAgICAgIC5zdDN7ZmlsbDojZmZmZmZmO30NCiAgICAgICAgICAgICAgICAuc3Q0e2Rpc3BsYXk6bm9uZTtmaWxsOiNmZmZmZmY7fQ0KICAgICAgICAgICAgICAgIC5zdDV7ZmlsbDpub25lO2ZpbGwtb3BhY2l0eTowO3N0cm9rZTojZmZmZmZmO3N0cm9rZS13aWR0aDozO3N0cm9rZS1taXRlcmxpbWl0OjEwO30NCiAgICAgICAgICAgICAgICAuc3Q2e2ZpbGw6bm9uZTtmaWxsLW9wYWNpdHk6MDtzdHJva2U6I2ZmZmZmZjtzdHJva2Utd2lkdGg6MjtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9DQogICAgICAgICAgICAgICA8L3N0eWxlPg0KICAgICAgICAgICAgICAgPGcgaWQ9IkxheWVyXzJfMDAwMDAwNDc3NzA3MTk3MTAxMTA3NDIyMzAwMDAwMDM5MjM5NTE2MjYxNDg4NDk1NTdfIiBjbGFzcz0ic3QwIj4NCiAgICAgICAgICAgICAgICA8cmVjdCB4PSIwIiB5PSIwIiBjbGFzcz0ic3QxIiB3aWR0aD0iNTMuMSIgaGVpZ2h0PSI1My4xIi8+DQogICAgICAgICAgICAgICA8L2c+DQogICAgICAgICAgICAgICA8ZyBpZD0iTGF5ZXJfMV8wMDAwMDE2MjMzMzEwMzI2NTgwNjk4MTUzMDAwMDAxNzE0NjYyNDI0NzU5MTY3NDU1Nl8iPg0KICAgICAgICAgICAgICAgIDxnPg0KICAgICAgICAgICAgICAgICAgIDxkZWZzPg0KICAgICAgICAgICAgICAgICAgICAgIDxwYXRoIGlkPSJTVkdJRF8xXyIgZD0iTTAsMGg1M3Y1M0gwVjB6IE0yMy4zLDM3LjJjMS40LDAsMi41LTEuMSwyLjUtMi41cy0xLjEtMi41LTIuNS0yLjVzLTIuNSwxLjEtMi41LDIuNSAgICAgQzIwLjgsMzYuMSwyMS45LDM3LjIsMjMuMywzNy4yeiBNMzMuNCwyNy4yYzEuNCwwLDIuNS0xLjEsMi41LTIuNXMtMS4xLTIuNS0yLjUtMi41cy0yLjUsMS4xLTIuNSwyLjVTMzIsMjcuMiwzMy40LDI3LjJ6ICAgICAgTTIzLDIyLjhjMS42LDAsMi45LTEuMywyLjktMi45UzI0LjYsMTcsMjMsMTdzLTIuOSwxLjMtMi45LDIuOUMyMC4yLDIxLjUsMjEuNSwyMi44LDIzLDIyLjh6Ii8+DQogICAgICAgICAgICAgICAgICAgPC9kZWZzPg0KICAgICAgICAgICAgICAgICAgIDxjbGlwUGF0aCBpZD0iU1ZHSURfMDAwMDAxMTI2MDgzNDA4MDQyNDU0NDI0NjAwMDAwMTM5ODYyMTkxNzgxOTkwODYyNDRfIj4NCiAgICAgICAgICAgICAgICAgICAgICA8dXNlIHhsaW5rOmhyZWY9IiNTVkdJRF8xXyIgc3R5bGU9Im92ZXJmbG93OnZpc2libGU7Ii8+DQogICAgICAgICAgICAgICAgICAgPC9jbGlwUGF0aD4NCiAgICAgICAgICAgICAgICAgICA8ZyBpZD0iQmFyc0NsaXBwZWQiIHN0eWxlPSJjbGlwLXBhdGg6dXJsKCNTVkdJRF8wMDAwMDExMjYwODM0MDgwNDI0NTQ0MjQ2MDAwMDAxMzk4NjIxOTE3ODE5OTA4NjI0NF8pOyI+DQogICAgICAgICAgICAgICAgICAgICAgPHBhdGggY2xhc3M9InN0MyIgZD0iTTM5LjksMzEuNUwxOCwzNy4zYy0wLjYsMC4yLTEuMi0wLjItMS40LTAuOGMtMC4yLTAuNiwwLjItMS4yLDAuOC0xLjRsMjEuOS01LjljMC42LTAuMiwxLjIsMC4yLDEuNCwwLjggICAgIEM0MC44LDMwLjcsNDAuNSwzMS4zLDM5LjksMzEuNXoiLz4NCiAgICAgICAgICAgICAgICAgICAgICA8cGF0aCBjbGFzcz0ic3QzIiBkPSJNMzguMSwyNC42bC0yMS45LDUuOWMtMC42LDAuMi0xLjItMC4yLTEuNC0wLjhjLTAuMi0wLjYsMC4yLTEuMiwwLjgtMS40bDIxLjktNS45YzAuNi0wLjIsMS4yLDAuMiwxLjQsMC44ICAgICBDMzksMjMuOCwzOC43LDI0LjUsMzguMSwyNC42eiIvPg0KICAgICAgICAgICAgICAgICAgICAgIDxwYXRoIGNsYXNzPSJzdDMiIGQ9Ik0zNi4yLDE3LjlsLTIxLjksNS45Yy0wLjYsMC4yLTEuMi0wLjItMS40LTAuOGMtMC4yLTAuNiwwLjItMS4yLDAuOC0xLjRsMjEuOS01LjljMC42LTAuMiwxLjIsMC4yLDEuNCwwLjggICAgIEMzNy4yLDE3LjEsMzYuOCwxNy43LDM2LjIsMTcuOXoiLz4NCiAgICAgICAgICAgICAgICAgICA8L2c+DQogICAgICAgICAgICAgICAgPC9nPg0KICAgICAgICAgICAgICAgIDxwYXRoIGNsYXNzPSJzdDQiIGQ9Ik0yMy4zLDM3LjJjMS40LDAsMi41LTEuMSwyLjUtMi41cy0xLjEtMi41LTIuNS0yLjVzLTIuNSwxLjEtMi41LDIuNUMyMC44LDM2LjEsMjEuOSwzNy4yLDIzLjMsMzcuMnoiLz4NCiAgICAgICAgICAgICAgICA8cGF0aCBjbGFzcz0ic3Q0IiBkPSJNMzMuNCwyNy4yYzEuNCwwLDIuNS0xLjEsMi41LTIuNXMtMS4xLTIuNS0yLjUtMi41cy0yLjUsMS4xLTIuNSwyLjVTMzIsMjcuMiwzMy40LDI3LjJ6Ii8+DQogICAgICAgICAgICAgICAgPHBhdGggY2xhc3M9InN0NCIgZD0iTTIzLDIyLjhjMS42LDAsMi45LTEuMywyLjktMi45UzI0LjYsMTcsMjMsMTdzLTIuOSwxLjMtMi45LDIuOUMyMC4yLDIxLjUsMjEuNSwyMi44LDIzLDIyLjh6Ii8+DQogICAgICAgICAgICAgICAgPHBhdGggY2xhc3M9InN0NCIgZD0iTTIzLjksMzguMmMtMS45LDAuNS0zLjgtMC42LTQuMy0yLjVjLTAuNS0xLjksMC42LTMuOCwyLjUtNC4zczMuOCwwLjYsNC4zLDIuNUMyNi45LDM1LjgsMjUuOCwzNy43LDIzLjksMzguMiAgIHogTTIyLjYsMzMuMmMtMC45LDAuMi0xLjQsMS4xLTEuMSwyYzAuMiwwLjksMS4xLDEuNCwyLDEuMWMwLjktMC4yLDEuNC0xLjEsMS4xLTJDMjQuMywzMy41LDIzLjQsMzMsMjIuNiwzMy4yeiIvPg0KICAgICAgICAgICAgICAgIDxwYXRoIGNsYXNzPSJzdDQiIGQ9Ik0zNC4xLDI4LjJjLTEuOSwwLjUtMy44LTAuNi00LjMtMi41czAuNi0zLjgsMi41LTQuM2MxLjktMC41LDMuOCwwLjYsNC4zLDIuNUMzNy4xLDI1LjgsMzYsMjcuNywzNC4xLDI4LjJ6ICAgIE0zMi44LDIzLjJjLTAuOSwwLjItMS40LDEuMS0xLjEsMmMwLjIsMC45LDEuMSwxLjQsMiwxLjFjMC45LTAuMiwxLjQtMS4xLDEuMS0yQzM0LjUsMjMuNSwzMy42LDIzLDMyLjgsMjMuMnoiLz4NCiAgICAgICAgICAgICAgICA8cGF0aCBjbGFzcz0ic3Q0IiBkPSJNMjQuMiwyMy41Yy0xLjksMC41LTMuOC0wLjYtNC4zLTIuNXMwLjYtMy44LDIuNS00LjNzMy44LDAuNiw0LjMsMi41UzI2LjEsMjMsMjQuMiwyMy41eiBNMjIuOSwxOC42ICAgYy0wLjksMC4yLTEuNCwxLjEtMS4xLDJjMC4yLDAuOSwxLjEsMS40LDIsMS4xYzAuOS0wLjIsMS40LTEuMSwxLjEtMkMyNC43LDE4LjgsMjMuOCwxOC4zLDIyLjksMTguNnoiLz4NCiAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICA8cGF0aCBjbGFzcz0ic3Q1IiBmaWxsLW9wYWNpdHk9IjAiIGQ9Ik0yNi41LDUxLjVjMTMuOCwwLDI1LTExLjIsMjUtMjVzLTExLjItMjUtMjUtMjVzLTI1LDExLjItMjUsMjVTMTIuNyw1MS41LDI2LjUsNTEuNXoiLz4NCiAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICA8cGF0aCBjbGFzcz0ic3Q0IiBkPSJNMzMuMywyMWgxMGMwLjQsMCwwLjgtMC4zLDAuOC0wLjhzLTAuMy0wLjgtMC44LTAuOGgtMTBDMzMuMywyMC4yLDMzLjMsMjAuMiwzMy4zLDIxeiIvPg0KICAgICAgICAgICAgICAgPC9nPg0KICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICA8Y2lyY2xlIGNsYXNzPSJzdDYiIGZpbGwtb3BhY2l0eT0iMCIgY3g9IjIzLjMiIGN5PSIyMC4xIiByPSIyLjUiLz4NCiAgICAgICAgICAgICAgIDxjaXJjbGUgY2xhc3M9InN0NiIgZmlsbC1vcGFjaXR5PSIwIiBjeD0iMzMuMiIgY3k9IjI0LjgiIHI9IjIuNSIvPg0KICAgICAgICAgICAgICAgPGNpcmNsZSBjbGFzcz0ic3Q2IiBmaWxsLW9wYWNpdHk9IjAiIGN4PSIyMyIgY3k9IjM0LjgiIHI9IjIuNSIvPg0KICAgICAgICAgICAgICAgPC9zdmc+) no-repeat;
898 + background-size: auto;
899 + background-size: contain;
900 + display: inline-block;
901 + width: 28px;
902 + height: 28px;
903 + margin-bottom: -5px;
904 + }
905 +
906 + .elementor-navigator__element-widget .wpc-fe-icon {
907 + width: 15px;
908 + height: 15px;
909 + margin-bottom: 0px;
910 + }';
911 + wp_add_inline_style( 'filter-everything-elementor', $css );
912 + });
913 +
914 + add_filter('elementor/document/wrapper_attributes', function($attributes) {
915 + $attributes['wpc-filter-elementor-widget'] = true;
916 + return $attributes;
917 + });
918 +
919 + add_action( 'wp_footer', function() {
920 + if ( ! defined( 'ELEMENTOR_VERSION' ) ) {
921 + return;
922 + }
923 + if( flrt_get_option('mobile_filter_settings') === 'show_bottom_widget' ){
924 + if( flrt_get_experimental_option('disable_buttons') !== 'on' ) {
925 +
926 + ?>
927 +
928 + <script>
929 + jQuery(function($) {
930 + $(window).on('elementor/frontend/init', function() {
931 + elementorFrontend.hooks.addAction(
932 + 'frontend/element_ready/filter-everything-filters.default',
933 + function($scope) {
934 + let $el = $('[wpc-filter-elementor-widget="1"][data-elementor-type]:not([data-elementor-type="loop-item"])');
935 + if($el.length > 0){
936 + $el.before(`<?php flrt_filters_button() ?>`);
937 + }
938 + }
939 + );
940 + });
941 + });
942 + </script>
943 + <?php
944 + }
945 + }
946 + });
947 +}
948 +
949 +add_action('admin_enqueue_scripts', function () {
950 + $handle = 'filter-everything-menu-icon';
951 + wp_register_style($handle, false);
952 + wp_enqueue_style($handle);
953 +
954 +
955 + $css = <<<PHP_CSS
956 + #toplevel_page_edit-post_type-filter-set .wp-menu-image{
957 + content: '';
958 + background-repeat: no-repeat;
959 + background-position: center;
960 + background-size: 20px;
961 + background-image: url('data:image/svg+xml,%3C%3Fxml version="1.0" encoding="utf-8"%3F%3E%3C!-- --%3E%3Csvg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 53 53" style="enable-background:new 0 0 53 53;" xml:space="preserve"%3E%3Cstyle type="text/css"%3E .st0%7Bdisplay:none;%7D .st1%7Bdisplay:inline;%7D .st2%7Bclip-path:url(%23SVGID_00000142154616827085436530000012698217856111301567_);%7D .st3%7Bfill:%23a7aaad;%7D .st4%7Bdisplay:none;fill:%23FFFFFF;%7D .st5%7Bfill:none;stroke:%23a7aaad;stroke-width:3;stroke-miterlimit:10;%7D .st6%7Bfill:none;stroke:%23a7aaad;stroke-width:2;stroke-miterlimit:10;%7D%0A%3C/style%3E%3Cg id="Layer_2_00000047770719710110742230000003923951626148849557_" class="st0"%3E%3Crect class="st1" width="53.1" height="53.1"/%3E%3C/g%3E%3Cg id="Layer_1_00000162333103265806981530000017146624247591674556_"%3E%3Cg%3E%3Cg%3E%3Cdefs%3E%3Cpath id="SVGID_1_" d="M0,0h53v53H0V0z M23.3,37.2c1.4,0,2.5-1.1,2.5-2.5s-1.1-2.5-2.5-2.5s-2.5,1.1-2.5,2.5 C20.8,36.1,21.9,37.2,23.3,37.2z M33.4,27.2c1.4,0,2.5-1.1,2.5-2.5s-1.1-2.5-2.5-2.5s-2.5,1.1-2.5,2.5S32,27.2,33.4,27.2z M23,22.8c1.6,0,2.9-1.3,2.9-2.9S24.6,17,23,17s-2.9,1.3-2.9,2.9C20.2,21.5,21.5,22.8,23,22.8z"/%3E%3C/defs%3E%3CclipPath id="SVGID_00000111880200341027563210000003406718579453459379_"%3E%3Cuse xlink:href="%23SVGID_1_" style="overflow:visible;"/%3E%3C/clipPath%3E%3Cg id="BarsClipped" style="clip-path:url(%23SVGID_00000111880200341027563210000003406718579453459379_);"%3E%3Cpath class="st3" d="M39.9,31.5L18,37.3c-0.6,0.2-1.2-0.2-1.4-0.8c-0.2-0.6,0.2-1.2,0.8-1.4l21.9-5.9c0.6-0.2,1.2,0.2,1.4,0.8 C40.8,30.7,40.5,31.3,39.9,31.5z"/%3E%3Cpath class="st3" d="M38.1,24.6l-21.9,5.9c-0.6,0.2-1.2-0.2-1.4-0.8c-0.2-0.6,0.2-1.2,0.8-1.4l21.9-5.9c0.6-0.2,1.2,0.2,1.4,0.8 C39,23.8,38.7,24.5,38.1,24.6z"/%3E%3Cpath class="st3" d="M36.2,17.9l-21.9,5.9c-0.6,0.2-1.2-0.2-1.4-0.8c-0.2-0.6,0.2-1.2,0.8-1.4l21.9-5.9c0.6-0.2,1.2,0.2,1.4,0.8 C37.2,17.1,36.8,17.7,36.2,17.9z"/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3Cpath class="st4" d="M23.3,37.2c1.4,0,2.5-1.1,2.5-2.5s-1.1-2.5-2.5-2.5s-2.5,1.1-2.5,2.5C20.8,36.1,21.9,37.2,23.3,37.2z"/%3E%3Cpath class="st4" d="M33.4,27.2c1.4,0,2.5-1.1,2.5-2.5s-1.1-2.5-2.5-2.5s-2.5,1.1-2.5,2.5S32,27.2,33.4,27.2z"/%3E%3Cpath class="st4" d="M23,22.8c1.6,0,2.9-1.3,2.9-2.9S24.6,17,23,17s-2.9,1.3-2.9,2.9C20.2,21.5,21.5,22.8,23,22.8z"/%3E%3Cpath class="st4" d="M23.9,38.2c-1.9,0.5-3.8-0.6-4.3-2.5c-0.5-1.9,0.6-3.8,2.5-4.3s3.8,0.6,4.3,2.5C26.9,35.8,25.8,37.7,23.9,38.2 z M22.6,33.2c-0.9,0.2-1.4,1.1-1.1,2c0.2,0.9,1.1,1.4,2,1.1c0.9-0.2,1.4-1.1,1.1-2C24.3,33.5,23.4,33,22.6,33.2z"/%3E%3Cpath class="st4" d="M34.1,28.2c-1.9,0.5-3.8-0.6-4.3-2.5s0.6-3.8,2.5-4.3c1.9-0.5,3.8,0.6,4.3,2.5C37.1,25.8,36,27.7,34.1,28.2z M32.8,23.2c-0.9,0.2-1.4,1.1-1.1,2c0.2,0.9,1.1,1.4,2,1.1c0.9-0.2,1.4-1.1,1.1-2C34.5,23.5,33.6,23,32.8,23.2z"/%3E%3Cpath class="st4" d="M24.2,23.5c-1.9,0.5-3.8-0.6-4.3-2.5s0.6-3.8,2.5-4.3s3.8,0.6,4.3,2.5S26.1,23,24.2,23.5z M22.9,18.6 c-0.9,0.2-1.4,1.1-1.1,2c0.2,0.9,1.1,1.4,2,1.1c0.9-0.2,1.4-1.1,1.1-2C24.7,18.8,23.8,18.3,22.9,18.6z"/%3E%3Cpath class="st5" d="M26.5,51.5c13.8,0,25-11.2,25-25s-11.2-25-25-25s-25,11.2-25,25S12.7,51.5,26.5,51.5z"/%3E%3Cpath class="st4" d="M33.3,21h10c0.4,0,0.8-0.3,0.8-0.8s-0.3-0.8-0.8-0.8h-10C33.3,20.2,33.3,20.2,33.3,21z"/%3E%3C/g%3E%3Ccircle class="st6" cx="23.3" cy="20.1" r="2.5"/%3E%3Ccircle class="st6" cx="33.2" cy="24.8" r="2.5"/%3E%3Ccircle class="st6" cx="23" cy="34.8" r="2.5"/%3E%3C/svg%3E');
962 + }
963 + #toplevel_page_edit-post_type-filter-set:hover .wp-menu-image{
964 + background-image: url('data:image/svg+xml,%3C%3Fxml version="1.0" encoding="utf-8"%3F%3E%3C!-- --%3E%3Csvg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 53 53" style="enable-background:new 0 0 53 53;" xml:space="preserve"%3E%3Cstyle type="text/css"%3E .st0%7Bdisplay:none;%7D .st1%7Bdisplay:inline;%7D .st2%7Bclip-path:url(%23SVGID_00000142154616827085436530000012698217856111301567_);%7D .st3%7Bfill:%2372AEE6;%7D .st4%7Bdisplay:none;fill:%23FFFFFF;%7D .st5%7Bfill:none;stroke:%2372AEE6;stroke-width:3;stroke-miterlimit:10;%7D .st6%7Bfill:none;stroke:%2372AEE6;stroke-width:2;stroke-miterlimit:10;%7D%0A%3C/style%3E%3Cg id="Layer_2_00000047770719710110742230000003923951626148849557_" class="st0"%3E%3Crect class="st1" width="53.1" height="53.1"/%3E%3C/g%3E%3Cg id="Layer_1_00000162333103265806981530000017146624247591674556_"%3E%3Cg%3E%3Cg%3E%3Cdefs%3E%3Cpath id="SVGID_1_" d="M0,0h53v53H0V0z M23.3,37.2c1.4,0,2.5-1.1,2.5-2.5s-1.1-2.5-2.5-2.5s-2.5,1.1-2.5,2.5 C20.8,36.1,21.9,37.2,23.3,37.2z M33.4,27.2c1.4,0,2.5-1.1,2.5-2.5s-1.1-2.5-2.5-2.5s-2.5,1.1-2.5,2.5S32,27.2,33.4,27.2z M23,22.8c1.6,0,2.9-1.3,2.9-2.9S24.6,17,23,17s-2.9,1.3-2.9,2.9C20.2,21.5,21.5,22.8,23,22.8z"/%3E%3C/defs%3E%3CclipPath id="SVGID_00000111880200341027563210000003406718579453459379_"%3E%3Cuse xlink:href="%23SVGID_1_" style="overflow:visible;"/%3E%3C/clipPath%3E%3Cg id="BarsClipped" style="clip-path:url(%23SVGID_00000111880200341027563210000003406718579453459379_);"%3E%3Cpath class="st3" d="M39.9,31.5L18,37.3c-0.6,0.2-1.2-0.2-1.4-0.8c-0.2-0.6,0.2-1.2,0.8-1.4l21.9-5.9c0.6-0.2,1.2,0.2,1.4,0.8 C40.8,30.7,40.5,31.3,39.9,31.5z"/%3E%3Cpath class="st3" d="M38.1,24.6l-21.9,5.9c-0.6,0.2-1.2-0.2-1.4-0.8c-0.2-0.6,0.2-1.2,0.8-1.4l21.9-5.9c0.6-0.2,1.2,0.2,1.4,0.8 C39,23.8,38.7,24.5,38.1,24.6z"/%3E%3Cpath class="st3" d="M36.2,17.9l-21.9,5.9c-0.6,0.2-1.2-0.2-1.4-0.8c-0.2-0.6,0.2-1.2,0.8-1.4l21.9-5.9c0.6-0.2,1.2,0.2,1.4,0.8 C37.2,17.1,36.8,17.7,36.2,17.9z"/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3Cpath class="st4" d="M23.3,37.2c1.4,0,2.5-1.1,2.5-2.5s-1.1-2.5-2.5-2.5s-2.5,1.1-2.5,2.5C20.8,36.1,21.9,37.2,23.3,37.2z"/%3E%3Cpath class="st4" d="M33.4,27.2c1.4,0,2.5-1.1,2.5-2.5s-1.1-2.5-2.5-2.5s-2.5,1.1-2.5,2.5S32,27.2,33.4,27.2z"/%3E%3Cpath class="st4" d="M23,22.8c1.6,0,2.9-1.3,2.9-2.9S24.6,17,23,17s-2.9,1.3-2.9,2.9C20.2,21.5,21.5,22.8,23,22.8z"/%3E%3Cpath class="st4" d="M23.9,38.2c-1.9,0.5-3.8-0.6-4.3-2.5c-0.5-1.9,0.6-3.8,2.5-4.3s3.8,0.6,4.3,2.5C26.9,35.8,25.8,37.7,23.9,38.2 z M22.6,33.2c-0.9,0.2-1.4,1.1-1.1,2c0.2,0.9,1.1,1.4,2,1.1c0.9-0.2,1.4-1.1,1.1-2C24.3,33.5,23.4,33,22.6,33.2z"/%3E%3Cpath class="st4" d="M34.1,28.2c-1.9,0.5-3.8-0.6-4.3-2.5s0.6-3.8,2.5-4.3c1.9-0.5,3.8,0.6,4.3,2.5C37.1,25.8,36,27.7,34.1,28.2z M32.8,23.2c-0.9,0.2-1.4,1.1-1.1,2c0.2,0.9,1.1,1.4,2,1.1c0.9-0.2,1.4-1.1,1.1-2C34.5,23.5,33.6,23,32.8,23.2z"/%3E%3Cpath class="st4" d="M24.2,23.5c-1.9,0.5-3.8-0.6-4.3-2.5s0.6-3.8,2.5-4.3s3.8,0.6,4.3,2.5S26.1,23,24.2,23.5z M22.9,18.6 c-0.9,0.2-1.4,1.1-1.1,2c0.2,0.9,1.1,1.4,2,1.1c0.9-0.2,1.4-1.1,1.1-2C24.7,18.8,23.8,18.3,22.9,18.6z"/%3E%3Cpath class="st5" d="M26.5,51.5c13.8,0,25-11.2,25-25s-11.2-25-25-25s-25,11.2-25,25S12.7,51.5,26.5,51.5z"/%3E%3Cpath class="st4" d="M33.3,21h10c0.4,0,0.8-0.3,0.8-0.8s-0.3-0.8-0.8-0.8h-10C33.3,20.2,33.3,20.2,33.3,21z"/%3E%3C/g%3E%3Ccircle class="st6" cx="23.3" cy="20.1" r="2.5"/%3E%3Ccircle class="st6" cx="33.2" cy="24.8" r="2.5"/%3E%3Ccircle class="st6" cx="23" cy="34.8" r="2.5"/%3E%3C/svg%3E');
965 + }
966 + .wp-menu-open#toplevel_page_edit-post_type-filter-set .wp-menu-image {
967 + background-image: url('data:image/svg+xml,%3C%3Fxml version="1.0" encoding="utf-8"%3F%3E%3C!-- --%3E%3Csvg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 53 53" style="enable-background:new 0 0 53 53;" xml:space="preserve"%3E%3Cstyle type="text/css"%3E .st0%7Bdisplay:none;%7D .st1%7Bdisplay:inline;%7D .st2%7Bclip-path:url(%23SVGID_00000098199702249043298830000017010845528166695064_);%7D .st3%7Bfill:%23FFFFFF;%7D .st4%7Bdisplay:none;fill:%23FFFFFF;%7D .st5%7Bfill:none;stroke:%23FFFFFF;stroke-width:3;stroke-miterlimit:10;%7D .st6%7Bfill:none;stroke:%23FFFFFF;stroke-width:2;stroke-miterlimit:10;%7D%0A%3C/style%3E%3Cg id="Layer_2_00000047770719710110742230000003923951626148849557_" class="st0"%3E%3Crect x="0" y="0" class="st1" width="53.1" height="53.1"/%3E%3C/g%3E%3Cg id="Layer_1_00000162333103265806981530000017146624247591674556_"%3E%3Cg%3E%3Cdefs%3E%3Cpath id="SVGID_1_" d="M0,0h53v53H0V0z M23.3,37.2c1.4,0,2.5-1.1,2.5-2.5s-1.1-2.5-2.5-2.5s-2.5,1.1-2.5,2.5 C20.8,36.1,21.9,37.2,23.3,37.2z M33.4,27.2c1.4,0,2.5-1.1,2.5-2.5s-1.1-2.5-2.5-2.5s-2.5,1.1-2.5,2.5S32,27.2,33.4,27.2z M23,22.8c1.6,0,2.9-1.3,2.9-2.9S24.6,17,23,17s-2.9,1.3-2.9,2.9C20.2,21.5,21.5,22.8,23,22.8z"/%3E%3C/defs%3E%3CclipPath id="SVGID_00000112608340804245442460000013986219178199086244_"%3E%3Cuse xlink:href="%23SVGID_1_" style="overflow:visible;"/%3E%3C/clipPath%3E%3Cg id="BarsClipped" style="clip-path:url(%23SVGID_00000112608340804245442460000013986219178199086244_);"%3E%3Cpath class="st3" d="M39.9,31.5L18,37.3c-0.6,0.2-1.2-0.2-1.4-0.8c-0.2-0.6,0.2-1.2,0.8-1.4l21.9-5.9c0.6-0.2,1.2,0.2,1.4,0.8 C40.8,30.7,40.5,31.3,39.9,31.5z"/%3E%3Cpath class="st3" d="M38.1,24.6l-21.9,5.9c-0.6,0.2-1.2-0.2-1.4-0.8c-0.2-0.6,0.2-1.2,0.8-1.4l21.9-5.9c0.6-0.2,1.2,0.2,1.4,0.8 C39,23.8,38.7,24.5,38.1,24.6z"/%3E%3Cpath class="st3" d="M36.2,17.9l-21.9,5.9c-0.6,0.2-1.2-0.2-1.4-0.8c-0.2-0.6,0.2-1.2,0.8-1.4l21.9-5.9c0.6-0.2,1.2,0.2,1.4,0.8 C37.2,17.1,36.8,17.7,36.2,17.9z"/%3E%3C/g%3E%3C/g%3E%3Cpath class="st4" d="M23.3,37.2c1.4,0,2.5-1.1,2.5-2.5s-1.1-2.5-2.5-2.5s-2.5,1.1-2.5,2.5C20.8,36.1,21.9,37.2,23.3,37.2z"/%3E%3Cpath class="st4" d="M33.4,27.2c1.4,0,2.5-1.1,2.5-2.5s-1.1-2.5-2.5-2.5s-2.5,1.1-2.5,2.5S32,27.2,33.4,27.2z"/%3E%3Cpath class="st4" d="M23,22.8c1.6,0,2.9-1.3,2.9-2.9S24.6,17,23,17s-2.9,1.3-2.9,2.9C20.2,21.5,21.5,22.8,23,22.8z"/%3E%3Cpath class="st4" d="M23.9,38.2c-1.9,0.5-3.8-0.6-4.3-2.5c-0.5-1.9,0.6-3.8,2.5-4.3s3.8,0.6,4.3,2.5C26.9,35.8,25.8,37.7,23.9,38.2 z M22.6,33.2c-0.9,0.2-1.4,1.1-1.1,2c0.2,0.9,1.1,1.4,2,1.1c0.9-0.2,1.4-1.1,1.1-2C24.3,33.5,23.4,33,22.6,33.2z"/%3E%3Cpath class="st4" d="M34.1,28.2c-1.9,0.5-3.8-0.6-4.3-2.5s0.6-3.8,2.5-4.3c1.9-0.5,3.8,0.6,4.3,2.5C37.1,25.8,36,27.7,34.1,28.2z M32.8,23.2c-0.9,0.2-1.4,1.1-1.1,2c0.2,0.9,1.1,1.4,2,1.1c0.9-0.2,1.4-1.1,1.1-2C34.5,23.5,33.6,23,32.8,23.2z"/%3E%3Cpath class="st4" d="M24.2,23.5c-1.9,0.5-3.8-0.6-4.3-2.5s0.6-3.8,2.5-4.3s3.8,0.6,4.3,2.5S26.1,23,24.2,23.5z M22.9,18.6 c-0.9,0.2-1.4,1.1-1.1,2c0.2,0.9,1.1,1.4,2,1.1c0.9-0.2,1.4-1.1,1.1-2C24.7,18.8,23.8,18.3,22.9,18.6z"/%3E%3Cpath class="st5" d="M26.5,51.5c13.8,0,25-11.2,25-25s-11.2-25-25-25s-25,11.2-25,25S12.7,51.5,26.5,51.5z"/%3E%3Cpath class="st4" d="M33.3,21h10c0.4,0,0.8-0.3,0.8-0.8s-0.3-0.8-0.8-0.8h-10C33.3,20.2,33.3,20.2,33.3,21z"/%3E%3C/g%3E%3Ccircle class="st6" cx="23.3" cy="20.1" r="2.5"/%3E%3Ccircle class="st6" cx="33.2" cy="24.8" r="2.5"/%3E%3Ccircle class="st6" cx="23" cy="34.8" r="2.5"/%3E%3C/svg%3E');
968 + }
969 + PHP_CSS;
970 + wp_add_inline_style($handle, $css);
971 +});
972 +
973 +/**
974 + * Fix Elementor query arguments for filtered content
975 + *
976 + * This function adjusts Elementor's query arguments when dealing with
977 + * filtered content from Filter Everything Pro plugin. It ensures proper
978 + * handling of custom post types in Elementor queries.
979 + *
980 + * @param array $query_args The Elementor query arguments
981 + * @return array Modified query arguments
982 + */
372 983 add_filter( 'elementor/query/get_query_args/current_query', 'flrt_fix_elementor_query_args' );
373 -function flrt_fix_elementor_query_args( $query_args ){
374 - $wpManager = \FilterEverything\Filter\Container::instance()->getWpManager();
375 - if( ! $wpManager->getQueryVar( 'allowed_filter_page' ) ){
984 +function flrt_fix_elementor_query_args( $query_args ) {
985 + // Verify that we're dealing with a filtered query
986 + if ( ! isset( $query_args['flrt_filtered_query'] ) || !$query_args['flrt_filtered_query'] ) {
376 987 return $query_args;
377 988 }
378 989
379 - if( isset( $query_args['taxonomy'] ) ){
380 - unset( $query_args['taxonomy'] );
381 - unset( $query_args['term'] );
990 + // Get post type from query args or current post
991 + $post_type = isset( $query_args['post_type'] ) ? $query_args['post_type'] : get_post_type();
992 +
993 + // Sanitize the post type
994 + if ( ! empty( $post_type ) ) {
995 + $post_type = sanitize_text_field( $post_type );
382 996 }
383 997
998 + // Check if the post type is non-built-in
999 + $post_type_object = get_post_type_object( $post_type );
1000 +
1001 + if ( $post_type_object && !$post_type_object->_builtin ) {
1002 + // Remove taxonomy from query args if present
1003 + if ( isset( $query_args['taxonomy'] ) ) {
1004 + unset( $query_args['taxonomy'] );
1005 + }
1006 + }
1007 +
384 1008 return $query_args;
385 1009 }
386 1010
387 -function flrt_wpml_active(){
388 - if ( function_exists('icl_object_id') ) {
1011 +/**
1012 + * Fixes Elementor featured products price range query arguments.
1013 + *
1014 + * This function addresses an issue where Elementor's featured products widget
1015 + * incorrectly applies meta_key filtering when sorting by price, which can
1016 + * interfere with price range filtering functionality. It removes the meta_key
1017 + * from query arguments for featured products sorted by price.
1018 + *
1019 + *
1020 + * @param array $query_args Query arguments for Elementor widget.
1021 + * @param object $widget The Elementor widget instance.
1022 + * @return array Modified query arguments.
1023 + */
1024 +if ( flrt_is_elementor_active() ) {
1025 + add_filter( 'elementor/query/query_args', 'flrt_fix_elementor_featured_products_price_range', 10, 2 );
1026 +
1027 + function flrt_fix_elementor_featured_products_price_range( array $query_args, $widget ): array {
1028 + $is_featured = ( $query_args['post_type'] ?? '' ) === 'featured';
1029 + $is_price_order = ( $query_args['orderby'] ?? '' ) === 'price';
1030 +
1031 + if ( $is_featured && $is_price_order ) {
1032 + static $registered = false;
1033 +
1034 + if ( ! $registered ) {
1035 + add_filter( 'elementor/query/query_args', function ( array $query_args, $widget ): array {
1036 + unset( $query_args['meta_key'] );
1037 + return $query_args;
1038 + }, 11, 2 );
1039 +
1040 + $registered = true;
1041 + }
1042 + }
1043 +
1044 + return $query_args;
1045 + }
1046 +}
1047 +
1048 +
1049 +
1050 +function flrt_is_divi_theme()
1051 +{
1052 + if ( 'Divi' === wp_get_theme()->get('Name') || 'Divi' === wp_get_theme()->get_template() ) {
389 1053 return true;
390 1054 }
1055 + return false;
1056 +}
391 1057
392 - return false;
393 -}
1058 +if ( flrt_is_divi_theme() ) {
1059 +
1060 + function wpc_initialize_divi_custom_module() {
1061 + if (!class_exists('ET_Builder_Module')) {
1062 + return;
1063 + }
1064 +
1065 + flrt_include('src/Frontend/Builders/DiviWidgets/ChipsDiviWidget.php');
1066 + flrt_include('src/Frontend/Builders/DiviWidgets/FiltersDiviWidget.php');
1067 + flrt_include('src/Frontend/Builders/DiviWidgets/SortingDiviWidget.php');
1068 + }
1069 +
1070 + add_action('et_builder_ready', 'wpc_initialize_divi_custom_module');
1071 +
1072 + add_filter( 'et_builder_inner_content_class', function ($classes){
1073 + $classes[] = 'wpc-divi-inner-content';
1074 + return $classes;
1075 + }, 10 );
1076 +
1077 + if( flrt_get_option('mobile_filter_settings') === 'show_bottom_widget' ) {
1078 + if (flrt_get_experimental_option('disable_buttons') !== 'on') {
1079 + add_action('et_before_main_content', 'flrt_divi_filter_button');
1080 + }
1081 + }
1082 +}
1083 +
1084 +function flrt_divi_filter_button()
1085 +{
1086 + // et_before_main_content fires more than once per page when a Divi Theme
1087 + // Builder header layout is present (both header.php and
1088 + // theme-after-header.php do_action it), so nothing here may declare a
1089 + // function at runtime — the previous nested declaration fataled with
1090 + // "Cannot redeclare modify_specific_module" on the second call.
1091 + // Re-adding the same filter callback is a no-op in WordPress.
1092 + add_filter('et_module_process_display_conditions', 'flrt_divi_modify_specific_module', 10, 3);
1093 +}
1094 +
1095 +function flrt_divi_modify_specific_module($output, $render_slug, $module)
1096 +{
1097 + if ($module->slug === 'filter_everything') {
1098 + flrt_filters_button(0, '', 'et_before_main_content');
1099 + }
1100 + return $output;
1101 +}
1102 +/**
1103 + * Checks if the Breakdance plugin and its required classes/functions are active and exist.
1104 + *
1105 + * This function verifies the presence of the Breakdance plugin, along with specific
1106 + * classes and functions used by the plugin. It returns true only if all checks pass,
1107 + * indicating that Breakdance is properly installed and functional.
1108 + *
1109 + * @return bool True if Breakdance is active and its required components exist, false otherwise.
1110 + */
1111 +function flrt_check_breakdance() {
1112 + // is_plugin_active() lives in wp-admin/includes/plugin.php and is not loaded
1113 + // on the frontend, in WP-CLI or in cron. Fall back to the raw option list.
1114 + if ( ! function_exists( 'is_plugin_active' ) ) {
1115 + $active = (array) get_option( 'active_plugins', [] );
1116 + if ( is_multisite() ) {
1117 + $active = array_merge( $active, array_keys( (array) get_site_option( 'active_sitewide_plugins', [] ) ) );
1118 + }
1119 + if ( ! in_array( 'breakdance/plugin.php', $active, true ) ) {
1120 + return false;
1121 + }
1122 + } elseif ( ! is_plugin_active( 'breakdance/plugin.php' ) ) {
1123 + return false;
1124 + }
1125 +
1126 + if (!function_exists('Breakdance\Util\getDirectoryPathRelativeToPluginFolder')) {
1127 + return false;
1128 + }
1129 +
1130 + return true;
1131 +}
1132 +if(flrt_check_breakdance()){
1133 + function flrt_breakdance_custom_css() {
1134 + echo '<style>
1135 + .breakdance-add-panel__element{
1136 + background: #0A2D48;
1137 + }
1138 + </style>';
1139 + }
1140 + add_action('wp_head', 'flrt_breakdance_custom_css');
1141 + flrt_include('src/Frontend/Builders/breakdance/widgets/breakdance.php');
1142 +}
1143 +
1144 +/**
1145 + * Checks if Bricks Builder theme and its classes are ready to register custom elements.
1146 + *
1147 + * @return bool True if Bricks Builder theme is active and necessary classes exist, false otherwise.
1148 + */
1149 +function flrt_is_bricks_ready_for_elements() {
1150 + $theme = wp_get_theme();
1151 +
1152 + if (
1153 + $theme->get_stylesheet() !== 'bricks' &&
1154 + $theme->get_template() !== 'bricks'
1155 + ) {
1156 + return false;
1157 + }
1158 +
1159 + if ( ! class_exists( '\Bricks\Elements' ) ) {
1160 + return false;
1161 + }
1162 +
1163 + if ( ! method_exists( '\Bricks\Elements', 'register_element' ) ) {
1164 + return false;
1165 + }
1166 +
1167 + return true;
1168 +}
1169 +
1170 +/**
1171 + * Registers custom Bricks elements during WordPress initialization.
1172 + *
1173 + * This function checks if Bricks Builder is ready using flrt_is_bricks_ready_for_elements().
1174 + * If it is, it includes the ChipsBricksWidget.php file and registers the element using Bricks\Elements::register_element().
1175 + */
1176 +
1177 +add_action('init', 'flrt_init_bricks_widgets', 11);
1178 +
1179 +function flrt_init_bricks_widgets() {
1180 + if (flrt_is_bricks_ready_for_elements()) {
1181 + $element_files = [
1182 + FLRT_PLUGIN_DIR_PATH .'src/Frontend/Builders/bricks/ChipsBricksWidget.php',
1183 + FLRT_PLUGIN_DIR_PATH .'src/Frontend/Builders/bricks/FiltersBricksWidget.php',
1184 + FLRT_PLUGIN_DIR_PATH .'src/Frontend/Builders/bricks/SortingBricksWidget.php',
1185 + ];
1186 + foreach ($element_files as $file) {
1187 + \Bricks\Elements::register_element($file);
1188 + }
1189 +
1190 + add_filter( 'bricks/builder/i18n', function( $i18n ) {
1191 + $i18n['filter-everything'] = esc_html__( 'Filter Everything', 'filter-everything' );
1192 + return $i18n;
1193 + } );
1194 +
1195 + function flrt_bricks_custom_css() {
1196 + echo '<style>
1197 + .wpc-fe-icon {
1198 + background: var(--builder-color);
1199 + mask-image: url(' . flrt_get_icon_logo_svg_css() . ');
1200 + mask-repeat: no-repeat;
1201 + mask-size: contain;
1202 + display: inline-block;
1203 + width: 28px;
1204 + height: 28px;
1205 + margin-bottom: -5px;
1206 + }
1207 +
1208 + .bricks-add-element:focus .wpc-fe-icon, .bricks-add-element:hover .wpc-fe-icon {
1209 + background: var(--builder-color-accent);
1210 + }
1211 +
1212 + .structure-item .wpc-fe-icon {
1213 + width: 16px;
1214 + height: 16px;
1215 + margin-bottom: 0px;
1216 + }
1217 + </style>';
1218 + }
1219 + add_action('wp_head', 'flrt_bricks_custom_css');
1220 + }
1221 +}
1222 +
1223 +function wpc_beaver_builder_widgets_load() {
1224 + if ( class_exists( 'FLBuilder' ) ) {
1225 + flrt_include('src/Frontend/Builders/beaver/chips/ChipsBeaverWidget.php');
1226 + flrt_include('src/Frontend/Builders/beaver/filters/FiltersBeaverWidget.php');
1227 + flrt_include('src/Frontend/Builders/beaver/sorting/SortingBeaverWidget.php');
1228 + }
1229 +}
1230 +add_action( 'init', 'wpc_beaver_builder_widgets_load' );
1231 +
1232 +/**
1233 + * XStore theme — "Display variable products as single variations" compatibility.
1234 + *
1235 + * With that XStore option (variable_products_detach) the theme:
1236 + * 1. adds 'product_variation' to the shop query's post_type, hides variable
1237 + * parents via post__not_in and flags the query with single_variations_filter=yes;
1238 + * 2. on posts_clauses (priority 20) appends a TOP-LEVEL
1239 + * "OR ( post_type='product_variation' AND post_status='publish' AND … )"
1240 + * branch, re-implementing WooCommerce's own layered-nav filters for the
1241 + * detached variations (price, rating, brand, chosen attributes from $_GET).
1242 + *
1243 + * The theme knows nothing about Filter Everything, so on FE-filtered pages that
1244 + * OR branch let EVERY published variation into the results, past FE's term
1245 + * constraints — filtering visually "did nothing" (each alternative of a
1246 + * top-level OR must repeat the filter conditions).
1247 + *
1248 + * Fix: append FE's active filter constraints AFTER the theme's branch
1249 + * (priority 30). AND binds tighter than OR, so in
1250 + * <first alternative> OR ( <theme branch> ) AND ( <FE constraints> )
1251 + * the constraints apply to the variations branch only, leaving the first
1252 + * alternative (already filtered by FE the regular way) untouched.
1253 + */
1254 +function flrt_xstore_detached_variations_where( $clauses, $wp_query ) {
1255 + global $wpdb;
1256 +
1257 + if ( is_admin() || empty( $clauses['where'] ) ) {
1258 + return $clauses;
1259 + }
1260 +
1261 + // Only XStore "detached variations" queries that FE has actually filtered.
1262 + if ( $wp_query->get( 'single_variations_filter' ) !== 'yes' || ! $wp_query->get( 'flrt_filtered_query' ) ) {
1263 + return $clauses;
1264 + }
1265 +
1266 + // The theme adds its branch at priority 20 — bail if it is not there.
1267 + if ( strpos( $clauses['where'], "OR ({$wpdb->posts}.post_type = 'product_variation'" ) === false ) {
1268 + return $clauses;
1269 + }
1270 +
1271 + $wpManager = \FilterEverything\Filter\Container::instance()->getWpManager();
1272 + $queried_values = $wpManager->getQueryVar( 'queried_values' );
1273 +
1274 + if ( empty( $queried_values ) || ! is_array( $queried_values ) ) {
1275 + return $clauses;
1276 + }
1277 +
1278 + $conditions = [];
1279 +
1280 + foreach ( $queried_values as $queried ) {
1281 +
1282 + if ( ! isset( $queried['entity'], $queried['e_name'] ) ) {
1283 + continue;
1284 + }
1285 +
1286 + // Taxonomy filters: a variation matches when its PARENT product carries
1287 + // the term (variations have no term_relationships rows of their own).
1288 + if ( $queried['entity'] === 'taxonomy' && ! empty( $queried['values'] ) ) {
1289 + $tax = $queried['e_name'];
1290 + $and_logic = ( isset( $queried['logic'] ) && $queried['logic'] === 'and' );
1291 + $groups = []; // one ttid-group per condition: single for OR-logic, per-term for AND-logic
1292 + $slugs = [];
1293 +
1294 + foreach ( (array) $queried['values'] as $value_slug ) {
1295 + $term = get_term_by( 'slug', $value_slug, $tax );
1296 + if ( ! $term ) {
1297 + continue;
1298 + }
1299 + $slugs[] = esc_sql( $term->slug );
1300 +
1301 + $ttids = [ (int) $term->term_taxonomy_id ];
1302 + if ( is_taxonomy_hierarchical( $tax ) && ! $and_logic ) {
1303 + foreach ( get_term_children( $term->term_id, $tax ) as $child_id ) {
1304 + $child = get_term( $child_id, $tax );
1305 + if ( $child && ! is_wp_error( $child ) ) {
1306 + $ttids[] = (int) $child->term_taxonomy_id;
1307 + }
1308 + }
1309 + }
1310 +
1311 + if ( $and_logic ) {
1312 + $groups[] = $ttids; // every term required — own condition each
1313 + } elseif ( empty( $groups ) ) {
1314 + $groups[] = $ttids; // any term enough — collect into one IN()
1315 + } else {
1316 + $groups[0] = array_merge( $groups[0], $ttids );
1317 + }
1318 + }
1319 +
1320 + if ( empty( $groups ) ) {
1321 + continue;
1322 + }
1323 +
1324 + foreach ( $groups as $group_ttids ) {
1325 + $ttids_in = implode( ',', array_unique( $group_ttids ) );
1326 + $parent_has_term = "{$wpdb->posts}.post_parent IN ( SELECT object_id FROM {$wpdb->term_relationships} WHERE term_taxonomy_id IN ({$ttids_in}) )";
1327 +
1328 + if ( strpos( $tax, 'pa_' ) === 0 && ! $and_logic && ! empty( $slugs ) ) {
1329 + // Variation attributes: prefer the variation's own value —
1330 + // a variation with a DIFFERENT value must not ride in on its
1331 + // parent's term. Variations without own value ("Any …")
1332 + // fall back to the parent's term.
1333 + $meta_key = esc_sql( 'attribute_' . $tax );
1334 + $slugs_in = "'" . implode( "','", $slugs ) . "'";
1335 +
1336 + $conditions[] = "( {$wpdb->posts}.ID IN ( SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '{$meta_key}' AND meta_value IN ({$slugs_in}) )"
1337 + . " OR ( {$parent_has_term}"
1338 + . " AND {$wpdb->posts}.ID NOT IN ( SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '{$meta_key}' AND meta_value <> '' ) ) )";
1339 + } else {
1340 + $conditions[] = $parent_has_term;
1341 + }
1342 + }
1343 + }
1344 +
1345 + // Numeric meta filters (price and alike): variations carry their own value.
1346 + if ( $queried['entity'] === 'post_meta_num' && isset( $queried['values'] ) && is_array( $queried['values'] ) ) {
1347 + $meta_key = esc_sql( $queried['e_name'] );
1348 + $min = ( isset( $queried['values']['min'] ) && $queried['values']['min'] !== '' ) ? (float) $queried['values']['min'] : false;
1349 + $max = ( isset( $queried['values']['max'] ) && $queried['values']['max'] !== '' ) ? (float) $queried['values']['max'] : false;
1350 +
1351 + $range = [];
1352 + if ( $min !== false ) {
1353 + $range[] = "CAST(meta_value AS DECIMAL(15,6)) >= {$min}";
1354 + }
1355 + if ( $max !== false ) {
1356 + $range[] = "CAST(meta_value AS DECIMAL(15,6)) <= {$max}";
1357 + }
1358 +
1359 + if ( $range ) {
1360 + $conditions[] = "{$wpdb->posts}.ID IN ( SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '{$meta_key}' AND " . implode( ' AND ', $range ) . " )";
1361 + }
1362 + }
1363 +
1364 + // Other filter entities (post_meta choices, author, post_date …) are not
1365 + // mirrored into the variations branch yet — they are rare on shop archives.
1366 + }
1367 +
1368 + if ( ! empty( $conditions ) ) {
1369 + $clauses['where'] .= ' AND ( ' . implode( ' ) AND ( ', $conditions ) . ' )';
1370 + }
1371 +
1372 + return $clauses;
1373 +}
1374 +add_filter( 'posts_clauses', 'flrt_xstore_detached_variations_where', 30, 2 );
1375 +
1376 +//@todo check this with PLL support
1377 +//function flrt_add_cpt_to_pll_tmp( $post_types, $is_settings ) {
1378 +// if ( $is_settings ) {
1379 +// $post_types[ FLRT_FILTERS_SET_POST_TYPE ] = FLRT_FILTERS_SET_POST_TYPE;
1380 +// }
1381 +// return $post_types;
1382 +//}