array( 'posts_container' => '#primary', 'sidebar_container' => '#secondary', 'primary_color' => '#96588a', 'button_hook' => array('storefront_content_top'), 'chips_hook' => array('storefront_loop_before') ), 'hello-elementor' => array( 'posts_container' => '.page-content', 'sidebar_container' => '', 'primary_color' => '#CC3366', 'button_hook' => '', 'chips_hook' => '' ), 'astra' => array( 'posts_container' => '#primary', 'sidebar_container' => '#secondary', 'primary_color' => '#0274be', 'button_hook' => '', 'chips_hook' => '' ), 'twentyeleven' => array( 'posts_container' => '#primary', 'sidebar_container' => '#secondary', 'primary_color' => '#1982d1', 'button_hook' => '', 'chips_hook' => '' ), 'twentytwelve' => array( 'posts_container' => '#primary', 'sidebar_container' => '#secondary', 'primary_color' => '#21759b', 'button_hook' => '', 'chips_hook' => '' ), 'twentyfourteen' => array( 'posts_container' => '#primary', 'sidebar_container' => '#secondary', 'primary_color' => '#24890d', 'button_hook' => '', 'chips_hook' => '' ), 'twentyfifteen' => array( 'posts_container' => '#primary', 'sidebar_container' => '#secondary', // There are problems on mobile // because of sidebar is hidden on mobile until user open header menu. 'primary_color' => '#333333', 'button_hook' => '', 'chips_hook' => '' ), 'twentysixteen' => array( 'posts_container' => '#primary', 'sidebar_container' => '#secondary', 'primary_color' => '#007acc', 'button_hook' => '', 'chips_hook' => '' ), 'twentyseventeen' => array( 'posts_container' => '#primary', 'sidebar_container' => '#secondary', 'primary_color' => '#222222', 'button_hook' => '', 'chips_hook' => '' ), 'twentynineteen' => array( 'posts_container' => '#primary', 'sidebar_container' => '#secondary', 'primary_color' => '#0073aa', 'button_hook' => '', 'chips_hook' => '' ), 'twentytwentyfive' => array( 'posts_container' => '#wp--skip-link--target', 'sidebar_container' => '', 'primary_color' => '#0073aa', 'button_hook' => '', 'chips_hook' => '' ), 'twentytwenty' => array( 'posts_container' => '#site-content', 'sidebar_container' => '', 'primary_color' => '#cd2653', 'button_hook' => '', 'chips_hook' => '' ), 'twentytwentyone' => array( 'posts_container' => '#content', 'sidebar_container' => '.widget-area', 'primary_color' => '#28303d', 'button_hook' => '', 'chips_hook' => '' ), 'popularfx' => array( 'posts_container' => '#primary', 'sidebar_container' => '#secondary', 'primary_color' => '#0072b7', 'button_hook' => '', 'chips_hook' => '' ), 'oceanwp' => array( 'posts_container' => '#primary', 'sidebar_container' => '#right-sidebar', 'primary_color' => '#13aff0', 'button_hook' => array('ocean_before_content'), 'chips_hook' => array('ocean_before_content') ), 'kadence' => array( 'posts_container' => '#main', 'sidebar_container' => '#secondary', 'primary_color' => '#3182ce', 'button_hook' => array('kadence_before_main_content'), 'chips_hook' => array('kadence_before_main_content') ), 'zakra' => array( 'posts_container' => '#primary', 'sidebar_container' => '#secondary', 'primary_color' => '#269bd1', 'button_hook' => array('zakra_before_posts_the_loop'), 'chips_hook' => array('zakra_before_posts_the_loop') ), 'neve' => array( 'posts_container' => '.nv-index-posts', 'sidebar_container' => '#secondary', // '.nv-sidebar-wrap', 'primary_color' => '#393939', 'button_hook' => array('neve_before_loop'), 'chips_hook' => array('neve_before_loop') ), 'hestia' => array( 'posts_container' => '#woo-products-wrap', 'sidebar_container' => '#secondary', 'primary_color' => '#e91e63', 'button_hook' => array('hestia_before_index_posts_loop'), 'chips_hook' => array('hestia_before_index_posts_loop') ), 'colibri-wp' => array( 'posts_container' => '.main-row-inner .h-col:not(.colibri-sidebar)', 'sidebar_container' => '.colibri-sidebar', 'primary_color' => '#03a9f4', 'button_hook' => '', 'chips_hook' => '' ), 'teluro' => array( 'posts_container' => '.main-row-inner .h-col:not(.colibri-sidebar)', 'sidebar_container' => '.colibri-sidebar', 'primary_color' => '#f26559', 'button_hook' => '', 'chips_hook' => '' ), 'numinous' => array( 'posts_container' => '#primary', 'sidebar_container' => '#secondary', 'primary_color' => '#f4ab00', 'button_hook' => array('numinous_content'), 'chips_hook' => '' ), 'sydney' => array( 'posts_container' => '#primary', 'sidebar_container' => '#secondary', 'primary_color' => '#d65050', 'button_hook' => array('sydney_before_content'), 'chips_hook' => '' ), // Commercial themes 'avada' => array( 'posts_container' => '#content', 'sidebar_container' => '#sidebar', 'primary_color' => '#65bc7b', 'button_hook' => array('avada_before_main_container'), 'chips_hook' => '' ), 'generatepress' => array( 'posts_container' => '#primary', 'sidebar_container' => '.sidebar', 'primary_color' => '#1e73be', 'button_hook' => array('generate_before_main_content'), 'chips_hook' => array('generate_before_main_content') ), 'the7' => array( 'posts_container' => '#content', 'sidebar_container' => '#sidebar', 'primary_color' => '', 'button_hook' => '', 'chips_hook' => '' ), 'dt-the7' => array( 'posts_container' => '#content', 'sidebar_container' => '#sidebar', 'primary_color' => '', 'button_hook' => '', 'chips_hook' => '' ), 'flatsome' => array( 'posts_container' => '.shop-container', 'sidebar_container' => '#shop-sidebar', 'primary_color' => '#446084', 'button_hook' => '', 'chips_hook' => '' ), 'betheme' => array( 'posts_container' => '.sections_group', 'sidebar_container' => '.sidebar', 'primary_color' => '', 'button_hook' => '', 'chips_hook' => '' ), 'bridge' => array( 'posts_container' => '.container .column1', 'sidebar_container' => '', 'primary_color' => '', 'button_hook' => '', 'chips_hook' => '' ), 'impreza' => array( 'posts_container' => '#page-content .w-grid-list', 'sidebar_container' => '', 'primary_color' => '', 'button_hook' => '', 'chips_hook' => '' ), 'enfold' => array( 'posts_container' => 'main.content', 'sidebar_container' => '', 'primary_color' => '', 'button_hook' => '', 'chips_hook' => '' ), 'porto' => array( 'posts_container' => '#content', 'sidebar_container' => '', 'primary_color' => '', 'button_hook' => '', 'chips_hook' => '' ), 'genesis' => array( 'posts_container' => '#primary', 'sidebar_container' => '#secondary', 'primary_color' => '', 'button_hook' => '', 'chips_hook' => '' ), 'woodmart' => array( 'posts_container' => '.site-content', 'sidebar_container' => '#secondary', 'primary_color' => '#83b735', 'button_hook' => '', 'chips_hook' => array( 'woodmart_shop_filters_area', 'woodmart_main_loop') ), 'bricks' => array( 'posts_container' => '.brxe-container', 'sidebar_container' => '', 'primary_color' => '', 'button_hook' => array('bricks_after_header'), 'chips_hook' => '' ), 'bb-theme' => array( 'posts_container' => '#fl-main-content', 'sidebar_container' => '', 'primary_color' => '', 'button_hook' => array('fl_content_open'), 'chips_hook' => '' ), ); $theme_dependencies = apply_filters( 'wpc_theme_dependencies', $theme_dependencies ); if( isset( $theme_dependencies[ $current_theme ] ) ){ return $theme_dependencies[ $current_theme ]; } return array( 'posts_container' => false, 'sidebar_container' => false, 'primary_color' => false, 'button_hook' => array(), 'chips_hook' => array() ); } add_action( 'wp', 'flrt_wp' ); if( ! function_exists('flrt_set_posts_container') ){ function flrt_set_posts_container( $theme_posts_container ) { $theme_dependencies = flrt_get_theme_dependencies(); if( isset( $theme_dependencies[ 'posts_container' ] ) ){ return $theme_dependencies[ 'posts_container' ]; } return $theme_posts_container; } } function flrt_set_theme_color($color ){ $theme_dependencies = flrt_get_theme_dependencies(); if( $theme_dependencies['primary_color'] ){ $color = $theme_dependencies['primary_color']; } return $color; } if( ! function_exists('flrt_init') ){ function flrt_init() { // Set correct theme posts container add_filter('wpc_theme_posts_container', 'flrt_set_posts_container'); // Set correct theme color add_filter('wpc_theme_color', 'flrt_set_theme_color'); // if( flrt_is_acf() ) { add_filter( 'wpc_pre_save_filter', 'flrt_maybe_acf_field' ); add_filter( 'wpc_default_sorting_terms', 'flrt_acf_terms_order', 10, 2 ); } } } add_action('init', 'flrt_init'); function flrt_wpml_active(){ if( defined('WPML_PLUGIN_BASENAME') ){ return true; } return false; } add_action( 'elementor/editor/before_enqueue_scripts', 'flrt_include_elementor_script' ); function flrt_include_elementor_script(){ $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min'; $ver = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? rand(0, 1000) : FLRT_PLUGIN_VER; wp_enqueue_script('wpc-widgets', FLRT_PLUGIN_DIR_URL . 'assets/js/wpc-widgets' . $suffix . '.js', ['jquery', 'jquery-ui-sortable'], $ver, true ); wp_enqueue_style('wpc-widgets', FLRT_PLUGIN_DIR_URL . 'assets/css/wpc-widgets' . $suffix . '.css', [], $ver ); $l10n = array( 'wpcItemNum' => esc_html__( 'Item #', 'filter-everything') ); wp_localize_script( 'wpc-widgets', 'wpcWidgets', $l10n ); } /* * Polylang compatibility functions * */ function flrt_maybe_has_translation( $post_id, $lang = '' ){ if( function_exists( 'pll_get_post' ) ){ $translation_post_id = pll_get_post( $post_id, $lang ); if( $translation_post_id ){ $post_id = $translation_post_id; } } return $post_id; } function flrt_pll_pro_active(){ if ( defined('POLYLANG_PRO') ) { return true; } return false; } // Allow Filter Sets to be translatable if Polylang PRO activated // This make sense for Filter Sets with common locations only add_action( 'after_setup_theme', 'flrt_pll_init', 20 ); function flrt_pll_init(){ if( flrt_pll_pro_active() && defined('FLRT_ALLOW_PLL_TRANSLATIONS') && FLRT_ALLOW_PLL_TRANSLATIONS ){ add_filter( 'pll_get_post_types', 'flrt_add_cpt_to_pll', 10, 2 ); } } function flrt_add_cpt_to_pll( $post_types, $is_settings ) { if ( $is_settings ) { unset( $post_types[ FLRT_FILTERS_SET_POST_TYPE ], $post_types[ FLRT_FILTERS_SET_POST_TYPE ] ); } else { $post_types[ FLRT_FILTERS_SET_POST_TYPE ] = FLRT_FILTERS_SET_POST_TYPE; if( defined('FLRT_FILTERS_PRO') && FLRT_FILTERS_PRO ) { $post_types[ FLRT_SEO_RULES_POST_TYPE ] = FLRT_SEO_RULES_POST_TYPE; } } return $post_types; } /** * Adds compatibility the Price filter with multi-currency plugins WOOCS and CURCY * @since 1.7.12 */ add_action('init', 'flrt_add_currencies_support'); function flrt_add_currencies_support() { if ( flrt_is_woocommerce() ) { // For the FOX - Currency Switcher Professional for WooCommerce if ( defined( 'WOOCS_PLUGIN_NAME' ) && WOOCS_PLUGIN_NAME ) { // Converts values into selected currency. Visible in the range slider form add_filter( 'wpc_set_num_shift', 'flrt_set_woocs_shift', 10, 2 ); function flrt_set_woocs_shift( $value, $entity_name ) { global $WOOCS; if ( $entity_name === '_price' ) { if ( property_exists( $WOOCS, 'default_currency' ) && property_exists( $WOOCS, 'current_currency' ) ) { if ( ! $WOOCS->default_currency || ! $WOOCS->current_currency ) { return $value; } if ( $WOOCS->default_currency !== $WOOCS->current_currency && method_exists( $WOOCS, 'convert_from_to_currency' ) ) { $value = $WOOCS->convert_from_to_currency( $value, $WOOCS->default_currency, $WOOCS->current_currency ); } } } return $value; } // Converts values back to default currency for WP_Query add_filter( 'wpc_unset_num_shift', 'flrt_unset_woocs_shift', 10, 2 ); function flrt_unset_woocs_shift( $value, $entity_name ) { global $WOOCS; if ( $entity_name === '_price' ) { if ( property_exists( $WOOCS, 'default_currency' ) && property_exists( $WOOCS, 'current_currency' ) ) { if ( ! $WOOCS->default_currency || ! $WOOCS->current_currency ) { return $value; } $precision = 2; if( method_exists( $WOOCS, 'get_currency_price_num_decimals' ) ) { $precision = $WOOCS->get_currency_price_num_decimals( $WOOCS->current_currency, $WOOCS->price_num_decimals ); } if ( $WOOCS->default_currency !== $WOOCS->current_currency && method_exists( $WOOCS, 'convert_from_to_currency' ) ) { $value = $WOOCS->convert_from_to_currency( $value, $WOOCS->current_currency, $WOOCS->default_currency ); $value = round( $value, $precision ); } } } return $value; } } // For the CURCY - Multi Currency for WooCommerce if ( defined( 'WOOMULTI_CURRENCY_F_VERSION' ) && WOOMULTI_CURRENCY_F_VERSION ) { // Converts values into selected currency. Visible in the range slider form add_filter( 'wpc_set_num_shift', 'flrt_set_curcy_shift', 10, 2 ); function flrt_set_curcy_shift( $value, $entity_name ) { if ( $entity_name === '_price' ) { if ( method_exists( 'WOOMULTI_CURRENCY_F_Data', 'get_ins' ) && function_exists('wmc_get_price' ) ) { $settings = WOOMULTI_CURRENCY_F_Data::get_ins(); if ( ! method_exists( $settings, 'get_current_currency' ) || ! method_exists( $settings, 'get_default_currency' ) ) { return $value; } $currency = $settings->get_current_currency(); $default_currency = $settings->get_default_currency(); if ( $currency !== $default_currency ) { $value = wmc_get_price( $value, $currency ); } } } return $value; } // Converts values back to default currency for WP_Query add_filter( 'wpc_unset_num_shift', 'flrt_unset_curcy_shift', 10, 2 ); function flrt_unset_curcy_shift( $value, $entity_name ) { if ( $entity_name === '_price' ) { if ( method_exists( 'WOOMULTI_CURRENCY_F_Data', 'get_ins' ) && function_exists('wmc_revert_price' ) ) { $settings = WOOMULTI_CURRENCY_F_Data::get_ins(); if ( ! method_exists( $settings, 'get_current_currency' ) || ! method_exists( $settings, 'get_default_currency' ) ) { return $value; } $currency = $settings->get_current_currency(); $default_currency = $settings->get_default_currency(); if ( $currency !== $default_currency ) { $value = wmc_revert_price( $value ); } } } return $value; } } } } /** * Removes WooCommerce Product Query post clauses for the price filter * @since 1.7.12 */ function flrt_remove_product_query_post_clauses( $wp_query, $WC_query ) { $wpManager = \FilterEverything\Filter\Container::instance()->getWpManager(); if ( $wpManager->getQueryVar( 'wpc_is_filter_request' ) ) { remove_filter('posts_clauses', array( $WC_query, 'product_query_post_clauses' ), 10, 2); } return $wp_query; } add_filter('wpc_check_broken_filter', function ($is_broken, $query) { $detector_class = 'FilterEverything\\Filter\\WP_Query_Source_Detector'; $is_allowed_method = 'is_allowed'; $source_var = 'flrt_detected_source'; if (defined('FLRT_FILTERS_PRO')) { if (defined('FLRT_PRO_BUILDER_KEY')) { $builder_key = apply_filters('wpc_builder_key_pro', $query->get($source_var), constant('FLRT_PRO_BUILDER_KEY')); if ($detector_class::$is_allowed_method($builder_key)) { return false; } } } if (!defined('FLRT_FILTERS_PRO')) { $builder_key = apply_filters('wpc_builder_key', $query->get($source_var), $detector_class::$builder_key); if ($detector_class::$is_allowed_method($builder_key)) { return false; } } return true; }, 10, 2); function flrt_is_dokan() { return function_exists('dokan'); } function flrt_maybe_acf_field( $filter ){ if( $filter['entity'] === 'post_meta' ) { global $wpdb; // Try to check if this is ACF field $sql[] = "SELECT {$wpdb->posts}.ID FROM {$wpdb->posts}"; $sql[] = "WHERE {$wpdb->posts}.post_excerpt = %s"; $sql[] = "AND {$wpdb->posts}.post_type = %s"; $sql[] = "ORDER BY {$wpdb->posts}.ID ASC"; $sql = implode(' ', $sql ); $sql = $wpdb->prepare( $sql, $filter['e_name'], 'acf-field' ); $results = $wpdb->get_results( $sql, ARRAY_A ); if ( ! empty( $results ) ) { $ids = []; foreach ( $results as $single_result ) { if( isset( $single_result['ID'] ) ){ $ids[] = $single_result['ID']; } } $acf_field_ids = implode( ',', $ids ); if( ! empty( $acf_field_ids ) ){ $filter['acf_fields'] = $acf_field_ids; } } } return $filter; } function flrt_acf_terms_order( $entity_items, $filter ){ if( isset( $filter['acf_fields'] ) && $filter['acf_fields'] !== '' ) { global $wpdb; // Here we have to get ACF fields by their IDs and sort terms in according to // the order in the field. $sql = []; $acf_field_ids = preg_replace( '/^[\d]\,/', '', $filter['acf_fields'] ); $sql[] = "SELECT {$wpdb->posts}.post_content FROM {$wpdb->posts}"; $sql[] = "WHERE {$wpdb->posts}.ID IN( {$acf_field_ids} )"; $sql[] = "ORDER BY {$wpdb->posts}.ID ASC"; $sql = implode(' ', $sql ); $results = $wpdb->get_results( $sql, ARRAY_A ); if( empty( $results ) ) { return $entity_items; } $field_terms = []; foreach ( $results as $acf_field ){ if( isset( $acf_field['post_content'] ) ) { $field_options = maybe_unserialize( $acf_field['post_content'] ); if( isset( $field_options['choices'] ) && is_array( $field_options['choices'] ) ){ foreach ( $field_options['choices'] as $value => $label ){ if( ! isset( $field_terms[$value] ) ) { $field_terms[$value] = $label; } } } } } $current_items = $entity_items; $sorted_items = []; foreach ( $field_terms as $tslug => $tvalue ) { $tslug = sanitize_title( $tslug ); if( isset( $current_items[ $tslug ] ) ) { $term_object = $current_items[ $tslug ]; $term_object->name = $tvalue; $sorted_items[ $tslug ] = $term_object; unset( $current_items[ $tslug ] ); } } if( ! empty( $current_items ) ){ foreach ( $current_items as $slug => $item ){ $sorted_items[$slug] = $item; } } $entity_items = $sorted_items; } return $entity_items; } /** * Adds correct pagination URLs to the Load more/Infinite scroll button for Elementor posts block */ add_filter( 'elementor/widget/render_content', 'flrt_elementor_load_more_anchor', 10, 2 ); function flrt_elementor_load_more_anchor( $widget_content, $module ){ // Nothing is needed if there is no Filter request if( ! flrt_is_filter_request() ){ return $widget_content; } if( $module instanceof ElementorPro\Modules\Posts\Widgets\Posts ){ global $wp_rewrite; $urlManager = \FilterEverything\Filter\Container::instance()->getUrlManager(); $current_page = $module->get_current_page(); $next_page = intval( $current_page ) + 1; $data_next_page = $module->get_wp_link_page( $next_page ); $rewrite = $wp_rewrite->wp_rewrite_rules(); /* * Loop widgets (Loop Grid/Loop Carousel) paginate with a widget-scoped * query arg (?e-page-=N) instead of /page/N/, so the * path-based base replacement below never matches. Swap the base for * the filtered URL directly and carry the pagination args over. */ $next_page_args = array(); $next_url_parts = parse_url( $data_next_page ); if ( isset( $next_url_parts['query'] ) ) { parse_str( $next_url_parts['query'], $next_page_args ); } $e_page_args = array(); foreach ( $next_page_args as $arg_key => $arg_value ) { if ( strpos( $arg_key, 'e-page-' ) === 0 ) { $e_page_args[ $arg_key ] = $arg_value; } } if ( ! empty( $e_page_args ) ) { // The current request may already contain pagination args (page 2+) $data_next_page = remove_query_arg( array_keys( $e_page_args ), $urlManager->getFormActionOrFullPageUrl( true ) ); foreach ( $e_page_args as $arg_key => $arg_value ) { $data_next_page = flrt_add_query_arg( $arg_key, $arg_value, $data_next_page ); } } else if ( defined('FLRT_PERMALINKS_ENABLED') && FLRT_PERMALINKS_ENABLED ) { // This is ok when permalinks are enabled. $data_next_page = str_replace( $urlManager->removePaginationBase( $data_next_page ), $urlManager->getFormActionOrFullPageUrl(), $data_next_page ); $uri_components = explode( "?", $urlManager->getFormActionOrFullPageUrl( true ) ); if( isset( $uri_components[1] ) ){ $data_next_page = $data_next_page . '?'.$uri_components[1]; } } else if ( ! FLRT_PERMALINKS_ENABLED && ! empty( $rewrite ) ) { // WordPress permalinks are Enabled, but Filter Everything permalinks are Disabled $uri_components = explode( "?", $urlManager->getFormActionOrFullPageUrl( true ) ); // If URI part after ? exists if( isset( $uri_components[1] ) ){ $data_next_page = $data_next_page . '?'.$uri_components[1]; } } else { // No permalinks at all $current_page_url = $urlManager->getFormActionOrFullPageUrl( true ); $url_parts = parse_url( $data_next_page ); if( isset( $url_parts['query'] ) ){ parse_str( $url_parts['query'], $params ); if( isset( $params['page'] ) && $params['page'] ) { $data_next_page = flrt_add_query_arg( 'page', $params['page'], $current_page_url ); } } } /* * $data_next_page carries the GET part of the current request, and * getFormActionOrFullPageUrl() hands it back URL-DECODED (parse_str() * + build_query(), which does not re-encode). Printed raw, a request * like ?x="> _builtin ) { // Remove taxonomy from query args if present if ( isset( $query_args['taxonomy'] ) ) { unset( $query_args['taxonomy'] ); } } return $query_args; } /** * Fixes Elementor featured products price range query arguments. * * This function addresses an issue where Elementor's featured products widget * incorrectly applies meta_key filtering when sorting by price, which can * interfere with price range filtering functionality. It removes the meta_key * from query arguments for featured products sorted by price. * * * @param array $query_args Query arguments for Elementor widget. * @param object $widget The Elementor widget instance. * @return array Modified query arguments. */ if ( flrt_is_elementor_active() ) { add_filter( 'elementor/query/query_args', 'flrt_fix_elementor_featured_products_price_range', 10, 2 ); function flrt_fix_elementor_featured_products_price_range( array $query_args, $widget ): array { $is_featured = ( $query_args['post_type'] ?? '' ) === 'featured'; $is_price_order = ( $query_args['orderby'] ?? '' ) === 'price'; if ( $is_featured && $is_price_order ) { static $registered = false; if ( ! $registered ) { add_filter( 'elementor/query/query_args', function ( array $query_args, $widget ): array { unset( $query_args['meta_key'] ); return $query_args; }, 11, 2 ); $registered = true; } } return $query_args; } } function flrt_is_divi_theme() { if ( 'Divi' === wp_get_theme()->get('Name') || 'Divi' === wp_get_theme()->get_template() ) { return true; } return false; } if ( flrt_is_divi_theme() ) { function wpc_initialize_divi_custom_module() { if (!class_exists('ET_Builder_Module')) { return; } flrt_include('src/Frontend/Builders/DiviWidgets/ChipsDiviWidget.php'); flrt_include('src/Frontend/Builders/DiviWidgets/FiltersDiviWidget.php'); flrt_include('src/Frontend/Builders/DiviWidgets/SortingDiviWidget.php'); } add_action('et_builder_ready', 'wpc_initialize_divi_custom_module'); add_filter( 'et_builder_inner_content_class', function ($classes){ $classes[] = 'wpc-divi-inner-content'; return $classes; }, 10 ); if( flrt_get_option('mobile_filter_settings') === 'show_bottom_widget' ) { if (flrt_get_experimental_option('disable_buttons') !== 'on') { add_action('et_before_main_content', 'flrt_divi_filter_button'); } } } function flrt_divi_filter_button() { // et_before_main_content fires more than once per page when a Divi Theme // Builder header layout is present (both header.php and // theme-after-header.php do_action it), so nothing here may declare a // function at runtime — the previous nested declaration fataled with // "Cannot redeclare modify_specific_module" on the second call. // Re-adding the same filter callback is a no-op in WordPress. add_filter('et_module_process_display_conditions', 'flrt_divi_modify_specific_module', 10, 3); } function flrt_divi_modify_specific_module($output, $render_slug, $module) { if ($module->slug === 'filter_everything') { flrt_filters_button(0, '', 'et_before_main_content'); } return $output; } /** * Checks if the Breakdance plugin and its required classes/functions are active and exist. * * This function verifies the presence of the Breakdance plugin, along with specific * classes and functions used by the plugin. It returns true only if all checks pass, * indicating that Breakdance is properly installed and functional. * * @return bool True if Breakdance is active and its required components exist, false otherwise. */ function flrt_check_breakdance() { // is_plugin_active() lives in wp-admin/includes/plugin.php and is not loaded // on the frontend, in WP-CLI or in cron. Fall back to the raw option list. if ( ! function_exists( 'is_plugin_active' ) ) { $active = (array) get_option( 'active_plugins', [] ); if ( is_multisite() ) { $active = array_merge( $active, array_keys( (array) get_site_option( 'active_sitewide_plugins', [] ) ) ); } if ( ! in_array( 'breakdance/plugin.php', $active, true ) ) { return false; } } elseif ( ! is_plugin_active( 'breakdance/plugin.php' ) ) { return false; } if (!function_exists('Breakdance\Util\getDirectoryPathRelativeToPluginFolder')) { return false; } return true; } if(flrt_check_breakdance()){ function flrt_breakdance_custom_css() { echo ''; } add_action('wp_head', 'flrt_breakdance_custom_css'); flrt_include('src/Frontend/Builders/breakdance/widgets/breakdance.php'); } /** * Checks if Bricks Builder theme and its classes are ready to register custom elements. * * @return bool True if Bricks Builder theme is active and necessary classes exist, false otherwise. */ function flrt_is_bricks_ready_for_elements() { $theme = wp_get_theme(); if ( $theme->get_stylesheet() !== 'bricks' && $theme->get_template() !== 'bricks' ) { return false; } if ( ! class_exists( '\Bricks\Elements' ) ) { return false; } if ( ! method_exists( '\Bricks\Elements', 'register_element' ) ) { return false; } return true; } /** * Registers custom Bricks elements during WordPress initialization. * * This function checks if Bricks Builder is ready using flrt_is_bricks_ready_for_elements(). * If it is, it includes the ChipsBricksWidget.php file and registers the element using Bricks\Elements::register_element(). */ add_action('init', 'flrt_init_bricks_widgets', 11); function flrt_init_bricks_widgets() { if (flrt_is_bricks_ready_for_elements()) { $element_files = [ FLRT_PLUGIN_DIR_PATH .'src/Frontend/Builders/bricks/ChipsBricksWidget.php', FLRT_PLUGIN_DIR_PATH .'src/Frontend/Builders/bricks/FiltersBricksWidget.php', FLRT_PLUGIN_DIR_PATH .'src/Frontend/Builders/bricks/SortingBricksWidget.php', ]; foreach ($element_files as $file) { \Bricks\Elements::register_element($file); } add_filter( 'bricks/builder/i18n', function( $i18n ) { $i18n['filter-everything'] = esc_html__( 'Filter Everything', 'filter-everything' ); return $i18n; } ); function flrt_bricks_custom_css() { echo ''; } add_action('wp_head', 'flrt_bricks_custom_css'); } } function wpc_beaver_builder_widgets_load() { if ( class_exists( 'FLBuilder' ) ) { flrt_include('src/Frontend/Builders/beaver/chips/ChipsBeaverWidget.php'); flrt_include('src/Frontend/Builders/beaver/filters/FiltersBeaverWidget.php'); flrt_include('src/Frontend/Builders/beaver/sorting/SortingBeaverWidget.php'); } } add_action( 'init', 'wpc_beaver_builder_widgets_load' ); /** * XStore theme — "Display variable products as single variations" compatibility. * * With that XStore option (variable_products_detach) the theme: * 1. adds 'product_variation' to the shop query's post_type, hides variable * parents via post__not_in and flags the query with single_variations_filter=yes; * 2. on posts_clauses (priority 20) appends a TOP-LEVEL * "OR ( post_type='product_variation' AND post_status='publish' AND … )" * branch, re-implementing WooCommerce's own layered-nav filters for the * detached variations (price, rating, brand, chosen attributes from $_GET). * * The theme knows nothing about Filter Everything, so on FE-filtered pages that * OR branch let EVERY published variation into the results, past FE's term * constraints — filtering visually "did nothing" (each alternative of a * top-level OR must repeat the filter conditions). * * Fix: append FE's active filter constraints AFTER the theme's branch * (priority 30). AND binds tighter than OR, so in * OR ( ) AND ( ) * the constraints apply to the variations branch only, leaving the first * alternative (already filtered by FE the regular way) untouched. */ function flrt_xstore_detached_variations_where( $clauses, $wp_query ) { global $wpdb; if ( is_admin() || empty( $clauses['where'] ) ) { return $clauses; } // Only XStore "detached variations" queries that FE has actually filtered. if ( $wp_query->get( 'single_variations_filter' ) !== 'yes' || ! $wp_query->get( 'flrt_filtered_query' ) ) { return $clauses; } // The theme adds its branch at priority 20 — bail if it is not there. if ( strpos( $clauses['where'], "OR ({$wpdb->posts}.post_type = 'product_variation'" ) === false ) { return $clauses; } $wpManager = \FilterEverything\Filter\Container::instance()->getWpManager(); $queried_values = $wpManager->getQueryVar( 'queried_values' ); if ( empty( $queried_values ) || ! is_array( $queried_values ) ) { return $clauses; } $conditions = []; foreach ( $queried_values as $queried ) { if ( ! isset( $queried['entity'], $queried['e_name'] ) ) { continue; } // Taxonomy filters: a variation matches when its PARENT product carries // the term (variations have no term_relationships rows of their own). if ( $queried['entity'] === 'taxonomy' && ! empty( $queried['values'] ) ) { $tax = $queried['e_name']; $and_logic = ( isset( $queried['logic'] ) && $queried['logic'] === 'and' ); $groups = []; // one ttid-group per condition: single for OR-logic, per-term for AND-logic $slugs = []; foreach ( (array) $queried['values'] as $value_slug ) { $term = get_term_by( 'slug', $value_slug, $tax ); if ( ! $term ) { continue; } $slugs[] = esc_sql( $term->slug ); $ttids = [ (int) $term->term_taxonomy_id ]; if ( is_taxonomy_hierarchical( $tax ) && ! $and_logic ) { foreach ( get_term_children( $term->term_id, $tax ) as $child_id ) { $child = get_term( $child_id, $tax ); if ( $child && ! is_wp_error( $child ) ) { $ttids[] = (int) $child->term_taxonomy_id; } } } if ( $and_logic ) { $groups[] = $ttids; // every term required — own condition each } elseif ( empty( $groups ) ) { $groups[] = $ttids; // any term enough — collect into one IN() } else { $groups[0] = array_merge( $groups[0], $ttids ); } } if ( empty( $groups ) ) { continue; } foreach ( $groups as $group_ttids ) { $ttids_in = implode( ',', array_unique( $group_ttids ) ); $parent_has_term = "{$wpdb->posts}.post_parent IN ( SELECT object_id FROM {$wpdb->term_relationships} WHERE term_taxonomy_id IN ({$ttids_in}) )"; if ( strpos( $tax, 'pa_' ) === 0 && ! $and_logic && ! empty( $slugs ) ) { // Variation attributes: prefer the variation's own value — // a variation with a DIFFERENT value must not ride in on its // parent's term. Variations without own value ("Any …") // fall back to the parent's term. $meta_key = esc_sql( 'attribute_' . $tax ); $slugs_in = "'" . implode( "','", $slugs ) . "'"; $conditions[] = "( {$wpdb->posts}.ID IN ( SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '{$meta_key}' AND meta_value IN ({$slugs_in}) )" . " OR ( {$parent_has_term}" . " AND {$wpdb->posts}.ID NOT IN ( SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '{$meta_key}' AND meta_value <> '' ) ) )"; } else { $conditions[] = $parent_has_term; } } } // Numeric meta filters (price and alike): variations carry their own value. if ( $queried['entity'] === 'post_meta_num' && isset( $queried['values'] ) && is_array( $queried['values'] ) ) { $meta_key = esc_sql( $queried['e_name'] ); $min = ( isset( $queried['values']['min'] ) && $queried['values']['min'] !== '' ) ? (float) $queried['values']['min'] : false; $max = ( isset( $queried['values']['max'] ) && $queried['values']['max'] !== '' ) ? (float) $queried['values']['max'] : false; $range = []; if ( $min !== false ) { $range[] = "CAST(meta_value AS DECIMAL(15,6)) >= {$min}"; } if ( $max !== false ) { $range[] = "CAST(meta_value AS DECIMAL(15,6)) <= {$max}"; } if ( $range ) { $conditions[] = "{$wpdb->posts}.ID IN ( SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '{$meta_key}' AND " . implode( ' AND ', $range ) . " )"; } } // Other filter entities (post_meta choices, author, post_date …) are not // mirrored into the variations branch yet — they are rare on shop archives. } if ( ! empty( $conditions ) ) { $clauses['where'] .= ' AND ( ' . implode( ' ) AND ( ', $conditions ) . ' )'; } return $clauses; } add_filter( 'posts_clauses', 'flrt_xstore_detached_variations_where', 30, 2 ); //@todo check this with PLL support //function flrt_add_cpt_to_pll_tmp( $post_types, $is_settings ) { // if ( $is_settings ) { // $post_types[ FLRT_FILTERS_SET_POST_TYPE ] = FLRT_FILTERS_SET_POST_TYPE; // } // return $post_types; //}