| @@ -30,8 +30,12 @@ | ||
| 30 | 30 | add_action('wpc_after_filter_input', 'flrt_after_filter_input'); |
| 31 | 31 | add_filter('wpc_filters_checkbox_term_html', 'wpc_term_brand_logo', 5, 4); |
| 32 | 32 | add_filter('wpc_filters_radio_term_html', 'wpc_term_brand_logo', 5, 4); |
| 33 | 33 | add_filter('wpc_filters_label_term_html', 'wpc_term_brand_logo', 5, 4); |
| 34 | + add_filter('wpc_filters_checkbox_term_html', 'wpc_replace_links_with_spans', 11, 4); | |
| 35 | + add_filter('wpc_filters_radio_term_html', 'wpc_replace_links_with_spans', 11, 4); | |
| 36 | + add_filter('wpc_filters_label_term_html', 'wpc_replace_links_with_spans', 11, 4); | |
| 37 | + add_filter('wpc_filters_rating_term_html', 'wpc_replace_links_with_spans', 11, 4); | |
| 34 | 38 | add_filter('wpc_taxonomy_location_terms', 'flrt_remove_default_category_location', 10, 2); |
| 35 | 39 | add_filter( 'wpc_set_num_shift', 'flrt_round_numeric_values', 10, 3 ); |
| 36 | 40 | |
| 37 | 41 | if (!function_exists('flrt_ucfirst_term_slug_name')) { |
| @@ -180,9 +184,8 @@ | ||
| 180 | 184 | { |
| 181 | 185 | if (isset($attributes['class']) && $attributes['class'] === 'wpc-field-slug' && $attributes['value'] === '') { |
| 182 | 186 | echo '<p class="description">' . esc_html__('a-z, 0-9, "_" and "-" symbols supported only', 'filter-everything') . '</p>'; |
| 183 | 187 | } |
| 184 | - | |
| 185 | 188 | } |
| 186 | 189 | } |
| 187 | 190 | |
| 188 | 191 | if (!function_exists('wpc_term_brand_logo')) { |
| @@ -195,13 +198,13 @@ | ||
| 195 | 198 | return $html; |
| 196 | 199 | } |
| 197 | 200 | $src = flrt_get_term_brand_image($term->term_id, $filter); |
| 198 | 201 | |
| 199 | - $link_attributes .= ' title="' . $term->name . '"'; | |
| 202 | + $link_attributes .= ' title="' . esc_attr( $term->name ) . '"'; | |
| 200 | 203 | |
| 201 | 204 | if ($src) { |
| 202 | - $img = '<span class="wpc-term-image-wrapper"><img src="' . $src . '" alt="' . $term->name . '" /></span>'; | |
| 203 | - $html = '<a ' . $link_attributes . '>' . $img . '<span class="wpc-term-name">' . $term->name . '</span></a>'; | |
| 205 | + $img = '<span class="wpc-term-image-wrapper"><img src="' . esc_url( $src ) . '" alt="' . esc_attr( $term->name ) . '" /></span>'; | |
| 206 | + $html = '<a ' . $link_attributes . '>' . $img . '<span class="wpc-term-name">' . esc_html( $term->name ) . '</span></a>'; | |
| 204 | 207 | } |
| 205 | 208 | |
| 206 | 209 | return $html; |
| 207 | 210 | } |
| @@ -208,8 +211,14 @@ | ||
| 208 | 211 | } |
| 209 | 212 | |
| 210 | 213 | if( !function_exists( 'flrt_remove_default_category_location' ) ) { |
| 211 | 214 | function flrt_remove_default_category_location( $terms, $taxonomy ){ |
| 215 | + // Bail on anything that is not a terms array (e.g. WP_Error when the | |
| 216 | + // taxonomy is unavailable) so we never index into a non-array. | |
| 217 | + if ( is_wp_error( $terms ) || ! is_array( $terms ) ) { | |
| 218 | + return $terms; | |
| 219 | + } | |
| 220 | + | |
| 212 | 221 | $default_category = 0; |
| 213 | 222 | |
| 214 | 223 | if ( $taxonomy === 'product_cat' ) { |
| 215 | 224 | $default_category = get_option( 'default_product_cat' ); |
| @@ -266,11 +275,13 @@ | ||
| 266 | 275 | return $value; |
| 267 | 276 | } |
| 268 | 277 | |
| 269 | 278 | } |
| 279 | + | |
| 270 | 280 | function flrt_chips( $showReset = false, $setIds = [] ) { |
| 271 | 281 | $templateManager = \FilterEverything\Filter\Container::instance()->getTemplateManager(); |
| 272 | 282 | $wpManager = \FilterEverything\Filter\Container::instance()->getWpManager(); |
| 283 | + $filterSet = Container::instance()->getFilterSetService(); | |
| 273 | 284 | |
| 274 | 285 | if( empty( $setIds ) || ! $setIds || ! is_array( $setIds ) ){ |
| 275 | 286 | $relatedSetIds = $wpManager->getQueryVar('wpc_page_related_set_ids'); |
| 276 | 287 | |
| @@ -424,25 +435,8 @@ | ||
| 424 | 435 | |
| 425 | 436 | return $classes; |
| 426 | 437 | } |
| 427 | 438 | |
| 428 | -//add_filter( 'wpc_filter_classes', 'flrt_same_swatches_width', 20, 4 ); | |
| 429 | -//function flrt_same_swatches_width( $classes, $filter, $default_classes, $terms, $args ){ | |
| 430 | -// if( in_array( 'wpc-filter-has-swatches', $classes ) ){ | |
| 431 | -// if( ! empty( $terms ) ) { | |
| 432 | -// $counters = []; | |
| 433 | -// foreach ( $terms as $single_term ) { | |
| 434 | -// $counters[] = $single_term->cross_count; | |
| 435 | -// } | |
| 436 | -// | |
| 437 | -// $max = max( $counters ); | |
| 438 | -// $classes[] = 'wpc-counter-width-'. strlen( (string)$max ); | |
| 439 | -// } | |
| 440 | -// } | |
| 441 | -// return $classes; | |
| 442 | -//} | |
| 443 | - | |
| 444 | - | |
| 445 | 439 | // Bricks Builder fix for Any Category Filter Set |
| 446 | 440 | add_action( 'wpc_all_set_wp_queried_posts', 'flrt_bricks_builder_category_compat', 10, 2 ); |
| 447 | 441 | function flrt_bricks_builder_category_compat( $set_wp_query, $setId ){ |
| 448 | 442 | |
| @@ -473,12 +467,13 @@ | ||
| 473 | 467 | // 'max_num_label' |
| 474 | 468 | |
| 475 | 469 | if( $filter['min_num_label'] !== '' ){ |
| 476 | 470 | if( ! is_null( $term ) && property_exists( $term, 'slug' ) && in_array( $term->slug, ['min', 'from'] ) ){ |
| 471 | + $value = flrt_chips_label_value( $filter, $term->slug ); | |
| 477 | 472 | if( strpos( $filter['min_num_label'], '{value}' ) !== false ){ |
| 478 | - $term_name = str_replace( '{value}', $filter['values'][$term->slug], $filter['min_num_label'] ); | |
| 473 | + $term_name = str_replace( '{value}', $value, $filter['min_num_label'] ); | |
| 479 | 474 | }else { |
| 480 | - $term_name = $filter['min_num_label'] .' '.$filter['values'][$term->slug]; | |
| 475 | + $term_name = $filter['min_num_label'] .' '.$value; | |
| 481 | 476 | } |
| 482 | 477 | |
| 483 | 478 | } |
| 484 | 479 | } |
| @@ -484,12 +479,13 @@ | ||
| 484 | 479 | } |
| 485 | 480 | |
| 486 | 481 | if( $filter['max_num_label'] !== '' ){ |
| 487 | 482 | if( ! is_null( $term ) && property_exists( $term, 'slug' ) && in_array( $term->slug, ['max', 'to'] ) ){ |
| 483 | + $value = flrt_chips_label_value( $filter, $term->slug ); | |
| 488 | 484 | if( strpos( $filter['max_num_label'], '{value}' ) !== false ){ |
| 489 | - $term_name = str_replace( '{value}', $filter['values'][$term->slug], $filter['max_num_label'] ); | |
| 485 | + $term_name = str_replace( '{value}', $value, $filter['max_num_label'] ); | |
| 490 | 486 | }else { |
| 491 | - $term_name = $filter['max_num_label'] .' '.$filter['values'][$term->slug]; | |
| 487 | + $term_name = $filter['max_num_label'] .' '.$value; | |
| 492 | 488 | } |
| 493 | 489 | } |
| 494 | 490 | } |
| 495 | 491 | |
| @@ -497,8 +493,38 @@ | ||
| 497 | 493 | |
| 498 | 494 | return $term_name; |
| 499 | 495 | } |
| 500 | 496 | |
| 497 | +/** | |
| 498 | + * Queried value for a {value} chip label. Date values must respect the | |
| 499 | + * filter's Date Format option, like the default term names built in | |
| 500 | + * PostDateEntity::createTermName() do (raw URL values otherwise leak | |
| 501 | + * into the chips). | |
| 502 | + */ | |
| 503 | +function flrt_chips_label_value( $filter, $edge ) { | |
| 504 | + $value = isset( $filter['values'][ $edge ] ) ? $filter['values'][ $edge ] : ''; | |
| 505 | + | |
| 506 | + if ( in_array( $filter['entity'], [ 'post_date', 'post_meta_date' ] ) && ! empty( $filter['date_format'] ) ) { | |
| 507 | + $format = $filter['date_format']; | |
| 508 | + $date_type = flrt_detect_date_type( $value ); | |
| 509 | + | |
| 510 | + // In case if we have several date filters on the same page | |
| 511 | + if ( in_array( $date_type, [ 'date', 'time' ] ) ) { | |
| 512 | + $maybe_split_format = flrt_split_date_time( $format ); | |
| 513 | + if ( isset( $maybe_split_format[ $date_type ] ) ) { | |
| 514 | + $format = $maybe_split_format[ $date_type ]; | |
| 515 | + } | |
| 516 | + } | |
| 517 | + | |
| 518 | + $formatted = flrt_apply_date_format( $value, $format ); | |
| 519 | + if ( $formatted ) { | |
| 520 | + $value = $formatted; | |
| 521 | + } | |
| 522 | + } | |
| 523 | + | |
| 524 | + return $value; | |
| 525 | +} | |
| 526 | + | |
| 501 | 527 | add_filter( 'query_loop_block_query_vars', 'flrt_query_loop_block_query_vars', 10, 2 ); |
| 502 | 528 | |
| 503 | 529 | function flrt_query_loop_block_query_vars( $query, $block ){ |
| 504 | 530 | global $xd; |
| @@ -762,9 +788,9 @@ | ||
| 762 | 788 | $include_custom_meta_keys, |
| 763 | 789 | static fn(string $key) => strncmp($key, $q, strlen($q)) === 0 |
| 764 | 790 | )); |
| 765 | 791 | } |
| 766 | - $default_items = array_map(static function ($k) { | |
| 792 | + $default_items = array_map(static function ($k) use ($include_custom_meta_keys) { | |
| 767 | 793 | return [ |
| 768 | 794 | 'id' => $k, |
| 769 | 795 | 'text' => $include_custom_meta_keys[$k], |
| 770 | 796 | ]; |
| @@ -850,8 +876,140 @@ | ||
| 850 | 876 | }); |
| 851 | 877 | } |
| 852 | 878 | |
| 853 | 879 | |
| 880 | +/** | |
| 881 | + * PRO-only fields have no inputs in the free admin UI, so saving in the free | |
| 882 | + * version would silently wipe their values from the DB. Instead, carry the | |
| 883 | + * previously saved values over — they must survive free-mode saves and start | |
| 884 | + * working again after switching back to PRO. In the free version their | |
| 885 | + * runtime effect is disabled at read time (see the hooks below), not in the DB. | |
| 886 | + */ | |
| 887 | +add_filter('wpc_pre_save_filter', function($filter) { | |
| 888 | + if( !defined('FLRT_FILTERS_PRO')) { | |
| 889 | + $proFields = array('show_range_list', 'range_list_input', 'used_for_variations'); | |
| 890 | + | |
| 891 | + // Values for these fields can not come from the free UI — drop any posted ones | |
| 892 | + foreach ($proFields as $proField) { | |
| 893 | + unset($filter[$proField]); | |
| 894 | + } | |
| 895 | + | |
| 896 | + if (!empty($filter['ID']) && is_numeric($filter['ID'])) { | |
| 897 | + $savedPost = get_post((int) $filter['ID']); | |
| 898 | + | |
| 899 | + if ($savedPost && $savedPost->post_type === FLRT_FILTERS_POST_TYPE) { | |
| 900 | + $savedFields = (array) maybe_unserialize($savedPost->post_content); | |
| 901 | + | |
| 902 | + foreach ($proFields as $proField) { | |
| 903 | + if (isset($savedFields[$proField])) { | |
| 904 | + $filter[$proField] = $savedFields[$proField]; | |
| 905 | + } | |
| 906 | + } | |
| 907 | + } | |
| 908 | + } | |
| 909 | + } | |
| 910 | + return $filter; | |
| 911 | +}); | |
| 912 | + | |
| 913 | +add_filter('wpc_pre_save_set_fields', function($setFields) { | |
| 914 | + if( !defined('FLRT_FILTERS_PRO')) { | |
| 915 | + $proFields = array('hide_empty_filter', 'custom_posts_container', 'apply_button_page_type', 'apply_button_post_name'); | |
| 916 | + | |
| 917 | + foreach ($proFields as $proField) { | |
| 918 | + unset($setFields[$proField]); | |
| 919 | + } | |
| 920 | + | |
| 921 | + if (!empty($setFields['ID']) && is_numeric($setFields['ID'])) { | |
| 922 | + $savedPost = get_post((int) $setFields['ID']); | |
| 923 | + | |
| 924 | + if ($savedPost && $savedPost->post_type === FLRT_FILTERS_SET_POST_TYPE) { | |
| 925 | + $savedFields = (array) maybe_unserialize($savedPost->post_content); | |
| 926 | + | |
| 927 | + foreach ($proFields as $proField) { | |
| 928 | + if (isset($savedFields[$proField])) { | |
| 929 | + $setFields[$proField] = $savedFields[$proField]; | |
| 930 | + } | |
| 931 | + } | |
| 932 | + } | |
| 933 | + } | |
| 934 | + } | |
| 935 | + return $setFields; | |
| 936 | +}); | |
| 937 | + | |
| 938 | +/** | |
| 939 | + * PRO-only plugin settings have no inputs in the free Settings UI (they render | |
| 940 | + * as "Unlock with PRO"), so saving the settings form in the free version would | |
| 941 | + * silently drop their values from the option. Carry the previously saved | |
| 942 | + * values over — they must survive free-mode saves and start working again | |
| 943 | + * after switching back to PRO. Their runtime effect is disabled at read time | |
| 944 | + * (flrt_instant_recount). Since 1.9.6 «Disable filter links for crawlers» is a | |
| 945 | + * regular option in both builds and no longer belongs here. | |
| 946 | + */ | |
| 947 | +add_filter('pre_update_option_wpc_filter_settings', function($value, $old_value) { | |
| 948 | + if( !defined('FLRT_FILTERS_PRO')) { | |
| 949 | + $proOptions = array('apply_button_instant_recount'); | |
| 950 | + | |
| 951 | + foreach ($proOptions as $proOption) { | |
| 952 | + // Values for these keys can not come from the free UI — drop any posted ones | |
| 953 | + if (is_array($value)) { | |
| 954 | + unset($value[$proOption]); | |
| 955 | + } | |
| 956 | + | |
| 957 | + if (is_array($old_value) && isset($old_value[$proOption])) { | |
| 958 | + if (!is_array($value)) { | |
| 959 | + $value = array(); | |
| 960 | + } | |
| 961 | + $value[$proOption] = $old_value[$proOption]; | |
| 962 | + } | |
| 963 | + } | |
| 964 | + } | |
| 965 | + return $value; | |
| 966 | +}, 10, 2); | |
| 967 | + | |
| 968 | +/** | |
| 969 | + * Runtime gate for the free version: the PRO-only values preserved in the DB | |
| 970 | + * must not activate PRO features while the plugin runs as free. | |
| 971 | + */ | |
| 972 | +add_filter('wpc_after_get_filter', function($filter) { | |
| 973 | + if( !defined('FLRT_FILTERS_PRO')) { | |
| 974 | + $filter['show_range_list'] = ''; | |
| 975 | + $filter['range_list_input'] = ''; // '' matches getEmptyFilter() and disables the JS range-list branch | |
| 976 | + $filter['used_for_variations'] = ''; | |
| 977 | + } | |
| 978 | + return $filter; | |
| 979 | +}); | |
| 980 | + | |
| 981 | +add_filter('wpc_filter_before_make_default_set_values', function($parsed) { | |
| 982 | + if( !defined('FLRT_FILTERS_PRO')) { | |
| 983 | + foreach (array('hide_empty_filter', 'custom_posts_container', 'apply_button_page_type', 'apply_button_post_name') as $proField) { | |
| 984 | + unset($parsed[$proField]); | |
| 985 | + } | |
| 986 | + } | |
| 987 | + return $parsed; | |
| 988 | +}); | |
| 989 | + | |
| 990 | +if (!function_exists('wpc_replace_links_with_spans')) { | |
| 991 | + function wpc_replace_links_with_spans($term_name, $attributes = '', $term = false, $filter = false) | |
| 992 | + { | |
| 993 | + // Available in both builds since 1.9.6: in free every filter link becomes | |
| 994 | + // a <span> (filter pages are noindex there by design); PRO keeps a real | |
| 995 | + // <a> for the targets its SEO Rules index (smart spans) | |
| 996 | + if( flrt_get_option('disable_filter_links_for_bots') === 'on' ) { | |
| 997 | + // Keep a real <a> when the target page is indexable (within the | |
| 998 | + // Indexing Depth) — hiding pages the SEO layer indexes would | |
| 999 | + // starve them of internal links; only the noise stays hidden (PRO; | |
| 1000 | + // the helper does not exist in free, so nothing is exempt there) | |
| 1001 | + if ( $term && $filter && function_exists('flrt_indexable_link_target') | |
| 1002 | + && flrt_indexable_link_target( $term, $filter ) ) { | |
| 1003 | + return $term_name; | |
| 1004 | + } | |
| 1005 | + $term_name = str_replace('<a', '<span', $term_name); | |
| 1006 | + $term_name = str_replace('href', 'data-wpc-span-link', $term_name); | |
| 1007 | + return str_replace('</a>', '</span>', $term_name); | |
| 1008 | + } | |
| 1009 | + return $term_name; | |
| 1010 | + } | |
| 1011 | +} | |
| 854 | 1012 | |
| 855 | 1013 | //add_filter( 'stackable/posts/post_query', 'flrt_stackable_block_query_vars', 10, 3 ); |
| 856 | 1014 | //function flrt_stackable_block_query_vars( $post_query, $context, $query_string ){ |
| 857 | 1015 | // if( is_user_logged_in() ) { |