PluginProbe
Filter Everything — WordPress & WooCommerce Filters / 1.9.2.1
Filter Everything — WordPress & WooCommerce Filters v1.9.2.1
1.9.7 1.9.6 1.9.5 1.9.4 1.9.3 1.9.2.2 1.9.2.1 trunk 1.2.1 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.4.1 1.4.4 1.4.5 1.4.8 1.4.9 1.5.0 1.5.1 1.6.0 1.6.1 1.6.2 All 52 releases
filter-everything / src / wpc-helpers.php

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

2,297 lines 74.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if ( ! defined('ABSPATH') ) {
4 exit;
5 }
6
7 use \FilterEverything\Filter\Container;
8 use \FilterEverything\Filter\FilterSet;
9 use \FilterEverything\Filter\FilterFields;
10 use \FilterEverything\Filter\PostMetaNumEntity;
11 use \FilterEverything\Filter\PostDateEntity;
12 use \FilterEverything\Filter\PostMetaDateEntity;
13 /**
14 * Returns user's caps level that allows to use the plugin.
15 * Developers can modify this level via hook 'wpc_plugin_user_caps' ot their own risk.
16 * @return string
17 */
18 function flrt_plugin_user_caps(){
19 return apply_filters( 'wpc_plugin_user_caps', 'manage_options' );
20 }
21
22 function flrt_the_set( $set_id = 0 ){
23 global $flrt_sets;
24
25 if( function_exists('brizy_load' ) ){
26 if( ! did_action('wp_print_scripts') ) {
27 return false;
28 }
29 }
30
31 if( $set_id ){
32 foreach ( $flrt_sets as $k => $set ){
33 if( $set['ID'] === $set_id ){
34 unset( $flrt_sets[$k] );
35 return $set;
36 }
37 }
38 }
39
40 return array_shift( $flrt_sets );
41 }
42
43 function flrt_print_filters_for( $hook = '' ) {
44 global $wp_filter;
45 if( empty( $hook ) || !isset( $wp_filter[$hook] ) )
46 return;
47 return $wp_filter[$hook];
48 }
49
50 function flrt_is_filter_request()
51 {
52 $wpManager = Container::instance()->getWpManager();
53 return $wpManager->getQueryVar('wpc_is_filter_request');
54 }
55
56 function flrt_include($filename )
57 {
58 $path = flrt_get_path( $filename );
59
60 if( file_exists($path) ) {
61 include_once( $path );
62 }
63 }
64
65 function flrt_get_path($path = '' )
66 {
67 return FLRT_PLUGIN_DIR_PATH . ltrim($path, '/');
68 }
69
70 function flrt_ucfirst( $text )
71 {
72 if( ! is_string( $text ) ){
73 return $text;
74 }
75 return mb_strtoupper( mb_substr( $text, 0, 1 ) ) . mb_substr( $text, 1 );
76 }
77
78 function flrt_lcfirst( $text )
79 {
80 if( ! is_string( $text ) ){
81 return $text;
82 }
83 return mb_strtolower( mb_substr( $text, 0, 1 ) ) . mb_substr( $text, 1 );
84 }
85
86 function flrt_sanitize_tooltip($var )
87 {
88 return htmlspecialchars(
89 wp_kses(
90 html_entity_decode( $var ),
91 array(
92 'br' => array(),
93 'em' => array(),
94 'strong' => array(),
95 'small' => array(),
96 'span' => array(),
97 'ul' => array(),
98 'li' => array(),
99 'ol' => array(),
100 'p' => array(),
101 'a' => array('href'=>true)
102 )
103 )
104 );
105 }
106
107 function flrt_help_tip($tip, $allow_html = false )
108 {
109 if ( $allow_html ) {
110 $tip = flrt_sanitize_tooltip( $tip );
111 } else {
112 $tip = esc_attr( $tip );
113 }
114
115 return '<span class="wpc-help-tip" data-tip="' . $tip . '"></span>';
116 }
117
118 function flrt_tooltip($attr )
119 {
120 if( ! isset( $attr['tooltip'] ) || ! $attr['tooltip'] ){
121 return false;
122 }
123
124 return flrt_help_tip($attr['tooltip'], true);
125 }
126
127 function flrt_field_instructions($attr)
128 {
129 if( ! isset( $attr['instructions'] ) || ! $attr['instructions'] ){
130 return false;
131 }
132 $instructions = wp_kses(
133 $attr['instructions'],
134 array(
135 'br' => array(),
136 'span' => array('class'=>true),
137 'strong' => array(),
138 'a' => array('href'=>true, 'title'=>true)
139 )
140 );
141 return '<p class="wpc-field-description">'.$instructions.'</p>';
142 }
143
144 function flrt_add_query_arg(...$args ) {
145 if ( is_array( $args[0] ) ) {
146 if ( count( $args ) < 2 || false === $args[1] ) {
147 $uri = $_SERVER['REQUEST_URI'];
148 } else {
149 $uri = $args[1];
150 }
151 } else {
152 if ( count( $args ) < 3 || false === $args[2] ) {
153 $uri = $_SERVER['REQUEST_URI'];
154 } else {
155 $uri = $args[2];
156 }
157 }
158
159 $frag = strstr( $uri, '#' );
160 if ( $frag ) {
161 $uri = substr( $uri, 0, -strlen( $frag ) );
162 } else {
163 $frag = '';
164 }
165
166 if ( 0 === stripos( $uri, 'http://' ) ) {
167 $protocol = 'http://';
168 $uri = substr( $uri, 7 );
169 } elseif ( 0 === stripos( $uri, 'https://' ) ) {
170 $protocol = 'https://';
171 $uri = substr( $uri, 8 );
172 } else {
173 $protocol = '';
174 }
175
176 if ( strpos( $uri, '?' ) !== false ) {
177 list( $base, $query ) = explode( '?', $uri, 2 );
178 $base .= '?';
179 } elseif ( $protocol || strpos( $uri, '=' ) === false ) {
180 $base = $uri . '?';
181 $query = '';
182 } else {
183 $base = '';
184 $query = $uri;
185 }
186
187 wp_parse_str( $query, $qs );
188
189 if ( is_array( $args[0] ) ) {
190 foreach ( $args[0] as $k => $v ) {
191 $qs[ $k ] = $v;
192 }
193 } else {
194 $qs[ $args[0] ] = $args[1];
195 }
196
197 foreach ( $qs as $k => $v ) {
198 if ( false === $v ) {
199 unset( $qs[ $k ] );
200 }
201 }
202
203 $ret = build_query( $qs );
204 $ret = trim( $ret, '?' );
205 $ret = preg_replace( '#=(&|$)#', '$1', $ret );
206 $ret = $protocol . $base . $ret . $frag;
207 $ret = rtrim( $ret, '?' );
208 return $ret;
209 }
210
211 /**
212 * @param $terms array
213 * @param $keys array
214 *
215 * @return array Array of objects with required keys
216 */
217 function flrt_extract_objects_vars( $terms, $keys = [] )
218 {
219 $required = [];
220
221 foreach ( $terms as $i => $term ) {
222 $new_object = new \stdClass();
223
224 foreach( $keys as $key ) {
225 if( isset( $term->$key ) ){
226 $new_object->$key = $term->$key;
227 $required[$term->term_id] = $new_object;
228 }
229 }
230 }
231
232 return $required;
233 }
234
235 add_filter('wpc_check_broken_query_vars', function ($query_vars, $query) {
236 if (is_admin()) {
237 return $query_vars;
238 }
239
240 $detector_class = 'FilterEverything\\Filter\\WP_Query_Source_Detector';
241 $is_allowed_method = 'is_allowed';
242 $source_var = 'flrt_detected_source';
243
244 if (defined('FLRT_FILTERS_PRO')) {
245 if (defined('FLRT_PRO_BUILDER_KEY')) {
246 $builder_key = apply_filters('wpc_builder_key_pro', $query->get($source_var), constant('FLRT_PRO_BUILDER_KEY'));
247
248 if ($detector_class::$is_allowed_method($builder_key)) {
249 return $query_vars;
250 }
251 }
252 }
253
254 if (!defined('FLRT_FILTERS_PRO')) {
255 $builder_key = apply_filters('wpc_builder_key', $query->get($source_var), $detector_class::$builder_key);
256
257 if ($detector_class::$is_allowed_method($builder_key)) {
258 return $query_vars;
259 }
260 }
261
262 return [];
263 }, 10, 2);
264
265 add_filter('wpc_builder_key', function ($source, $builder_id) {
266 return (int) sprintf("%u", crc32($source . $builder_id));
267 }, 10, 2);
268
269 function flrt_remove_level_array( $array )
270 {
271 /**
272 * @feature maybe rewrite this full of shame code
273 */
274 if( ! is_array( $array ) ){
275 return [];
276 }
277
278 $flatten = [];
279
280 array_map( function ($a) use(&$flatten){
281 if( is_array( $a ) ){
282 $flatten = array_merge($flatten, $a);
283 }
284 },
285 $array );
286
287 return $flatten;
288 }
289
290 add_filter('wpc_check_errors_ids', function ($error_ids, $query) {
291 $source_key = 'flrt_detected_source';
292 if (empty($query->query_vars[$source_key])) {
293 return $error_ids;
294 }
295
296 $source = $query->query_vars[$source_key];
297 $detector_class = 'FilterEverything\\Filter\\WP_Query_Source_Detector';
298 $is_allowed_method = 'is_allowed';
299
300 if (defined('FLRT_FILTERS_PRO')) {
301 if (defined('FLRT_PRO_BUILDER_KEY')) {
302 $builder_key = apply_filters('wpc_builder_key_pro', $source, constant('FLRT_PRO_BUILDER_KEY'));
303 if ($detector_class::$is_allowed_method($builder_key)) {
304 return $error_ids;
305 }
306 }
307 }
308
309 if (!defined('FLRT_FILTERS_PRO')) {
310 $builder_key = apply_filters('wpc_builder_key', $source, $detector_class::$builder_key);
311
312 if ($detector_class::$is_allowed_method($builder_key)) {
313 return $error_ids;
314 }
315 }
316
317 return [];
318 }, 10, 2);
319
320 function flrt_get_forbidden_prefixes()
321 {
322 //@todo it seems all existing tax prefixes should be there
323 // All them actual only when permalinks off
324 $forbidden_prefixes = [ 'srch' ];
325 $permalinksEnabled = defined('FLRT_PERMALINKS_ENABLED') ? FLRT_PERMALINKS_ENABLED : false;
326 if( ! $permalinksEnabled ) {
327 $forbidden_prefixes = array_merge( $forbidden_prefixes, array('cat', 'tag', 'page', 'author') );
328 }
329
330 if( flrt_wpml_active() ){
331 $wpml_url_format = apply_filters( 'wpml_setting', 0, 'language_negotiation_type' );
332 if( $wpml_url_format === '3' ){
333 $forbidden_prefixes[] = 'lang';
334 }
335 }
336
337 return apply_filters( 'wpc_forbidden_prefixes', $forbidden_prefixes );
338 }
339
340 function flrt_get_forbidden_meta_keys()
341 {
342 $forbidden_meta_keys = array('wpc_filter_set_post_type', 'wpc_seo_rule_post_type');
343 return apply_filters( 'wpc_forbidden_meta_keys', $forbidden_meta_keys );
344 }
345
346 function flrt_array_contains_duplicate($array )
347 {
348 return count($array) != count( array_unique($array) );
349 }
350
351 function flrt_maybe_hide_row( $atts )
352 {
353 if( $atts['type'] === 'Hidden' ){
354 echo ' style="display:none;"';
355 }
356 }
357 function flrt_filter_row_class( $field_atts )
358 {
359 $classes = [ 'wpc-filter-tr' ];
360
361 if( isset( $field_atts['class'] ) ){
362 $classes[] = $field_atts['class'] . '-tr';
363 }
364
365 if( isset( $field_atts['additional_class'] ) ){
366 $classes[] = $field_atts['additional_class'];
367 }
368
369 return implode(" ", $classes);
370 }
371
372
373 function flrt_include_admin_view( $path, $args = [] )
374 {
375 $templateManager = Container::instance()->getTemplateManager();
376 $templateManager->includeAdminView( $path, $args );
377 }
378
379 function flrt_include_front_view( $path, $args = [] )
380 {
381 $templateManager = Container::instance()->getTemplateManager();
382 $templateManager->includeFrontView( $path, $args );
383 }
384
385 function flrt_create_filters_nonce()
386 {
387 return FilterSet::createNonce();
388 }
389
390 function flrt_get_filter_fields_mapping()
391 {
392 return Container::instance()->getFilterFieldsService()->getFieldsMapping();
393 }
394
395 function flrt_get_configured_filters($post_id )
396 {
397 $filterFields = Container::instance()->getFilterFieldsService();
398 return $filterFields->getFiltersInputs( $post_id );
399 }
400
401 function flrt_get_filter_view_name($view_key )
402 {
403 $view_options = FilterFields::getViewOptions();
404 if( isset( $view_options[ $view_key ] ) ){
405 return esc_html($view_options[ $view_key ]);
406 }
407
408 return esc_html($view_key);
409 }
410
411 function flrt_get_filter_entity_name($entity_key )
412 {
413 $em = Container::instance()->getEntityManager();
414 $entities = $em->getPossibleEntities();
415
416 foreach( $entities as $key => $entity_array ){
417 if( isset( $entity_array['entities'][ $entity_key ] ) ){
418 return esc_html($entity_array['entities'][ $entity_key ]);
419 }
420 }
421
422 if( $entity_key === 'post_meta_exists' && ! defined('FLRT_FILTERS_PRO') ){
423 return esc_html__('Available in PRO', 'filter-everything');
424 }
425
426 return esc_html($entity_key);
427 }
428
429 function flrt_get_set_settings_fields($post_id)
430 {
431 $filterSet = Container::instance()->getFilterSetService();
432 return $filterSet->getSettingsTypeFields( $post_id );
433 }
434
435 function flrt_get_set_settings_location_fields($post_id)
436 {
437 $filterSet = Container::instance()->getFilterSetService();
438 return $filterSet->getSettingsLocationTypeFields( $post_id );
439 }
440
441 function flrt_render_input( $atts )
442 {
443 $className = isset( $atts['type'] ) ? '\FilterEverything\Filter\\' . $atts['type'] : '\FilterEverything\Filter\Text';
444
445 if( class_exists( $className ) ){
446 $input = new $className( $atts );
447 return $input->render();
448 }
449
450 return false;
451 }
452
453 function flrt_extract_vars(&$array, $keys )
454 {
455 $r = [];
456 foreach( $keys as $key ) {
457 $var = flrt_extract_var( $array, $key );
458 if( $var ){
459 $r[ $key ] = $var;
460 }
461 }
462 return $r;
463 }
464
465 function flrt_extract_var(&$array, $key, $default = null )
466 {
467 // check if exists
468 // - uses array_key_exists to extract NULL values (isset will fail)
469 if( is_array($array) && array_key_exists($key, $array) ) {
470 $v = $array[ $key ];
471 unset( $array[ $key ] );
472 return $v;
473 }
474 return $default;
475 }
476
477 function flrt_get_empty_filter( $set_id )
478 {
479 $filterFields = Container::instance()->getFilterFieldsService();
480 return $filterFields->getEmptyFilterObject( $set_id );
481 }
482
483 function flrt_excluded_taxonomies()
484 {
485 $excluded_taxonomies = array(
486 'nav_menu',
487 'link_category',
488 'post_format',
489 'template_category',
490 'element_category',
491 'fusion_tb_category',
492 'slide-page',
493 'elementor_font_type',
494 'post_translations',
495 'term_language',
496 'term_translations',
497 'wp_theme',
498 'wp_template_part_area',
499 'wp_pattern_category',
500 'elementor_library_type',
501 'elementor_library_category',
502 );
503
504 return apply_filters( 'wpc_excluded_taxonomies', $excluded_taxonomies );
505 }
506
507 function flrt_force_non_unique_slug($notNull, $originalSlug )
508 {
509 return $originalSlug;
510 }
511
512 function flrt_redirect_to_error($post_id, $errors )
513 {
514 $redirect = get_edit_post_link( $post_id, 'url' );
515 $error_code = 20; // Default error code
516
517 if( !empty( $errors ) && is_array( $errors ) ){
518 $error_code = reset( $errors );
519 }
520
521 $redirect = add_query_arg( 'message', $error_code, $redirect );
522 wp_redirect( $redirect );
523 exit;
524 }
525
526 function flrt_sanitize_int( $var )
527 {
528 return preg_replace('/[^\d]+/', '', $var );
529 }
530
531 function flrt_range_input_name( $slug, $edge = 'min', $type = 'num' )
532 {
533 if ( $type === 'date' ) {
534 return PostDateEntity::inputName( $slug, $edge );
535 }
536
537 return PostMetaNumEntity::inputName( $slug, $edge );
538 }
539
540 function flrt_query_string_form_fields( $values = null, $exclude = [], $current_key = '', $return = false ) {
541
542 $filter_everything_exclude = array_keys( apply_filters( 'wpc_unnecessary_get_parameters', [] ) );
543 $exclude = array_merge( $exclude, $filter_everything_exclude );
544
545 if ( is_null( $values ) ) {
546 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
547 $values = Container::instance()->getTheGet();
548 // For compatibility with some Nginx configurations
549 unset($values['q']);
550 } elseif ( is_string( $values ) ) {
551 $url_parts = wp_parse_url( $values );
552 $values = [];
553
554 if ( ! empty( $url_parts['query'] ) ) {
555 // This is to preserve full-stops, pluses and spaces in the query string when ran through parse_str.
556 $replace_chars = array(
557 '.' => '{dot}',
558 '+' => '{plus}',
559 );
560
561 $query_string = str_replace( array_keys( $replace_chars ), array_values( $replace_chars ), $url_parts['query'] );
562
563 // Parse the string.
564 parse_str( $query_string, $parsed_query_string );
565
566 // Convert the full-stops, pluses and spaces back and add to values array.
567 foreach ( $parsed_query_string as $key => $value ) {
568 $new_key = str_replace( array_values( $replace_chars ), array_keys( $replace_chars ), $key );
569 $new_value = str_replace( array_values( $replace_chars ), array_keys( $replace_chars ), $value );
570 $values[ $new_key ] = $new_value;
571 }
572 }
573 }
574 $html = '';
575
576 foreach ( $values as $key => $value ) {
577 if ( in_array( $key, $exclude, true ) ) {
578 continue;
579 }
580 if ( $current_key ) {
581 $key = $current_key . '[' . $key . ']';
582 }
583 if ( is_array( $value ) ) {
584 $html .= flrt_query_string_form_fields( $value, $exclude, $key, true );
585 } else {
586 $html .= '<input type="hidden" name="' . esc_attr( $key ) . '" value="' . esc_attr( wp_unslash( $value ) ) . '" />';
587 }
588 }
589
590 if ( $return ) {
591 return $html;
592 }
593
594 echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
595 }
596
597 function flrt_get_query_string_parameters()
598 {
599 $container = Container::instance();
600 $get = $container->getTheGet();
601 $post = $container->getThePost();
602
603 // For compatibility with some Nginx configurations
604 unset($get['q']);
605
606 if( isset( $post['flrt_ajax_link'] ) ){
607 $parts = parse_url( $post['flrt_ajax_link'] );
608 if( isset( $parts['query'] ) ){
609 parse_str( $parts['query'], $output );
610 return $output;
611 }
612 }
613
614 return $get;
615 }
616
617
618
619 function flrt_count( $term, $show = 'yes' )
620 {
621 _deprecated_function( 'flrt_count', '1.7.6', 'flrt_filter_count()' );
622 flrt_filter_count( $term, $show );
623 }
624
625 if ( ! function_exists( 'flrt_filter_count' ) ){
626 function flrt_filter_count( $term, $show = 'yes' )
627 {
628 if( $show === 'yes' ) :
629 echo flrt_filter_get_count( $term );
630 endif;
631 }
632 }
633
634 /**
635 * @since 1.0.5
636 * @param $term
637 * @return string
638 */
639 function flrt_filter_get_count( $term ){
640 return '<span class="wpc-term-count"><span class="wpc-term-count-brackets-open">(</span><span class="wpc-term-count-value">'.esc_html( $term->cross_count ).'</span><span class="wpc-term-count-brackets-close">)</span></span>&nbsp;';
641 }
642
643 if ( ! function_exists( 'flrt_spinner_html' ) ) {
644 function flrt_spinner_html()
645 {
646 return '<div class="wpc-spinner"></div>';
647 }
648 }
649
650 function flrt_filters_widget_content_class( $setId )
651 {
652 if ( isset( $_COOKIE[ FLRT_OPEN_CLOSE_BUTTON_COOKIE_NAME ] ) ) {
653
654 if ( $_COOKIE[ FLRT_OPEN_CLOSE_BUTTON_COOKIE_NAME ] === $setId ) {
655 return ' wpc-opened';
656 }else{
657 return ' wpc-closed';
658 }
659 }
660 }
661
662 function flrt_filters_button( $setId = 0, $class = '' )
663 {
664 /**
665 * @feature add nice wrapper to this functions to allow users put it into themes.
666 */
667 $classes = [];
668 $sets = [];
669 $wpManager = \FilterEverything\Filter\Container::instance()->getWpManager();
670 $templateManager = \FilterEverything\Filter\Container::instance()->getTemplateManager();
671
672 $draft_sets = $wpManager->getQueryVar('wpc_page_related_set_ids');
673
674 if ( ! is_array( $draft_sets ) ) {
675 $draft_sets = [];
676 }
677
678 foreach ( $draft_sets as $set ){
679 if( isset( $set['show_on_the_page'] ) && $set['show_on_the_page'] ){
680 $sets[] = $set;
681 }
682 }
683
684 if( ! $setId && isset( $sets[0]['ID'] ) ){
685 $setId = $sets[0]['ID'];
686 }
687
688 foreach ( $sets as $set ){
689 if( $set['ID'] === $setId ){
690 $theSet = $set;
691 break;
692 }
693 }
694
695 if( flrt_get_option('mobile_filter_settings') === 'show_bottom_widget' ){
696 $classes[] = 'wpc-filters-open-widget';
697 }else{
698 $classes[] = 'wpc-open-close-filters-button';
699 }
700
701 if( $class ){
702 $classes[] = trim($class);
703 }
704
705 $attrClass = implode(" ", $classes);
706 $setId = preg_replace('/[^\d]+/', '', $setId);
707
708 $wpc_found_posts = NULL;
709 $srch = isset( $_GET['srch'] ) ? filter_input( INPUT_GET, 'srch', FILTER_SANITIZE_SPECIAL_CHARS ) : '';
710 $all = false;
711 if ( $srch ) {
712 $all = true;
713 }
714
715 if( $wpManager->getQueryVar('wpc_is_filter_request' ) || $srch ){
716 $wpc_found_posts = flrt_posts_found_quantity( $setId, $all );
717 }
718
719 $templateManager->includeFrontView( 'filters-button', array( 'wpc_found_posts' => $wpc_found_posts, 'class' => $attrClass, 'set_id' => $setId ) );
720 }
721
722 function flrt_posts_found( $setid = 0, $all = false, $hidden = false )
723 {
724 $templateManager = \FilterEverything\Filter\Container::instance()->getTemplateManager();
725 $fss = \FilterEverything\Filter\Container::instance()->getFilterSetService();
726
727 if ( isset( $_GET['srch'] ) && $_GET['srch'] ) {
728 $all = true;
729 }
730 $count = flrt_posts_found_quantity( $setid, $all );
731
732 $theSet = $fss->getSet( $setid );
733 $postType = isset( $theSet['post_type']['value'] ) ? $theSet['post_type']['value'] : '';
734
735 $obj = get_post_type_object($postType);
736 $pluralLabel = isset( $obj->label ) ? apply_filters( 'wpc_label_singular_posts_found_msg', $obj->label ) : esc_html__('items', 'filter-everything');
737 $singularLabel = isset( $obj->labels->singular_name ) ? apply_filters( 'wpc_label_plural_posts_found_msg', $obj->labels->singular_name ) : esc_html__('item', 'filter-everything');
738
739 $templateManager->includeFrontView( 'posts-found', array( 'posts_found_count' => $count, 'singular_label' => $singularLabel, 'plural_label' => $pluralLabel, 'hidden' => $hidden) );
740 }
741
742 function flrt_get_option( $key, $default = false )
743 {
744 $settings = get_option('wpc_filter_settings');
745
746 if( isset( $settings[$key] ) ){
747 return apply_filters( 'wpc_get_option', $settings[$key], $key);
748 }
749
750 if( $default ){
751 return $default;
752 }
753
754 return false;
755
756 }
757
758 function flrt_remove_option($key )
759 {
760 $settings = get_option('wpc_filter_settings');
761
762 if (isset($settings[$key]) && $settings[$key]) {
763 unset($settings[$key]);
764 return update_option('wpc_filter_settings', $settings);
765 }
766
767 return false;
768 }
769
770 function flrt_get_experimental_option($key, $default = false )
771 {
772 /**
773 * @todo This should be rewritten
774 */
775 $settings = get_option('wpc_filter_experimental');
776
777 if( isset( $settings[$key] ) ){
778 return apply_filters( 'wpc_get_option', $settings[$key], $key);
779 }
780
781 if( $default !== false ){
782 return apply_filters( 'wpc_get_option', $default, $key);
783 }
784
785 return apply_filters( 'wpc_get_option', false, $key );
786
787 }
788
789 function flrt_get_status_css_class( $id, $cookieName, $classes = [ 'opened' => 'wpc-opened', 'closed' => 'wpc-closed' ] ){
790
791 if ( isset( $_COOKIE[ $cookieName ] ) ) {
792 $openediDs = explode(",", $_COOKIE[ $cookieName ] );
793
794 if ( in_array( $id, $openediDs ) ) {
795 return $classes['opened'];
796 } elseif ( in_array( -$id, $openediDs ) ) {
797 return $classes['closed'];
798 } else {
799 return '';
800 }
801 }
802
803 return '';
804 }
805
806 if ( ! function_exists('flrt_filter_header') ) {
807 function flrt_filter_header( $filter, $terms )
808 {
809 $openButton = ($filter['collapse'] === 'yes') ? '<button><span class="wpc-wrap-icons">' : '';
810 $closeButton = ($filter['collapse'] === 'yes') ? '</span><span class="wpc-open-icon"></span></button>' : '';
811 $tooltip = '';
812
813 if ( $filter['collapse'] === 'yes' && !empty($filter['values']) && !empty($terms) ) {
814 $selected = [];
815 $list = '<span class="wpc-filter-selected-values">&mdash; ';
816 // Does not work for numeric filters
817 // @todo
818 foreach ( $terms as $id => $term_object ) {
819
820 if ( in_array( $term_object->slug, $filter['values'] ) ) {
821 $selected[] = $term_object->name;
822 }
823 }
824
825 $list .= implode(", ", $selected) . '</span>';
826
827 $closeButton = $list . $closeButton;
828 }
829
830 if ( isset( $filter['tooltip'] ) && $filter['tooltip'] ) {
831 $tooltip = flrt_help_tip( $filter['tooltip'], true );
832 }
833
834 $filter_label = apply_filters( 'wpc_filter_title', $filter['label'], $filter );
835
836 ?>
837 <div class="wpc-filter-header"><div class="widget-title wpc-filter-title"><?php
838 echo $openButton . esc_html( $filter_label ) . $tooltip . $closeButton;
839 ?></div></div><?php
840 }
841 }
842
843 function flrt_filter_class( $filter, $default_classes = [], $terms = [], $args = [] )
844 {
845 $digits = [];
846 $length = 1;
847 if( ! empty( $terms ) ) {
848 foreach ( $terms as $term ) {
849 $digits[] = $term->cross_count;
850 }
851 }
852
853 if( ! empty( $digits ) ){
854 $length = strlen( (string) max( $digits ) );
855 }
856
857 $classes = array(
858 'wpc-filters-section',
859 'wpc-filters-section-'.esc_attr( $filter['ID'] ),
860 'wpc-filter-'.esc_attr( $filter['e_name'] ),
861 'wpc-filter-'.esc_attr( $filter['entity'] ),
862 'wpc-filter-layout-'.esc_attr( $filter['view'] ),
863 'wpc-counter-length-'.esc_attr( $length )
864 );
865
866 if ( isset( $filter['values'] ) && ! empty( $filter['values'] ) ) {
867 $classes[] = 'wpc-filter-has-selected';
868 }
869
870 // Set correct more/less class for specific views
871 if ( in_array( $filter['view'], [ 'checkboxes', 'radio', 'labels' ] ) ) {
872 if ( isset( $filter['more_less'] ) && $filter['more_less'] === 'yes' ) {
873
874 $classes[] = 'wpc-filter-more-less';
875
876 if ( in_array( $filter['ID'], flrt_more_less_opened() ) ) {
877 $classes[] = 'wpc-show-more-reverse';
878 }
879
880 $classes[] = flrt_get_status_css_class( $filter['ID'], FLRT_MORELESS_COOKIE_NAME, [ 'opened' => 'wpc-show-more', 'closed' => 'wpc-show-less'] );
881
882 // We have to count only first-level terms if hierarchy is enabled
883 if( isset( $filter['hierarchy'] ) && $filter['hierarchy'] === 'yes' ) {
884 if ( ! empty( $terms ) ) {
885 $only_parents = [];
886 foreach ( $terms as $term_id => $term ) {
887 if ( $term->parent == 0 ) {
888 $only_parents[ $term_id ] = $term;
889 }
890 }
891
892 $terms = $only_parents;
893 unset( $only_parents );
894 }
895 }
896
897 if ( count( $terms ) <= flrt_more_less_count() || $args['hide'] ) {
898 $classes[] = 'wpc-filter-few-terms';
899 }
900
901 } else {
902 $classes[] = 'wpc-filter-full-height';
903 }
904 }
905
906 if ( isset( $filter['collapse'] ) && $filter['collapse'] === 'yes' ) {
907 if ( in_array( $filter['ID'], flrt_folding_opened() ) ) {
908 $classes[] = 'wpc-filter-collapsible-reverse';
909 }
910
911 $classes[] = 'wpc-filter-collapsible';
912
913 $classes[] = flrt_get_status_css_class( $filter['ID'], FLRT_FOLDING_COOKIE_NAME );
914 }
915
916 if ( in_array( $filter['ID'], flrt_hierarchy_opened() ) ) {
917 if( isset( $filter['hierarchy'] ) && $filter['hierarchy'] === 'yes' ){
918 $classes[] = 'wpc-filter-hierarchy-reverse';
919 }
920 }
921
922 if ( in_array( $filter['entity'], [ 'post_date', 'post_meta_date' ] ) ) {
923 $classes[] = 'wpc-datetype-'.$filter['date_type'];
924 }
925
926 if ( ! empty( $default_classes ) ) {
927 $classes = array_merge( $classes, $default_classes );
928 }
929
930 $classes[] = 'wpc-filter-terms-count-'.count( $terms );
931
932 $classes = apply_filters( 'wpc_filter_classes', $classes, $filter, $default_classes, $terms, $args );
933
934 return implode( " ", $classes );
935 }
936
937 function flrt_filter_content_class( $filter, $default_classes = [] )
938 {
939 $classes = array(
940 'wpc-filter-content'
941 );
942
943 if( isset( $filter['e_name'] ) ){
944 $classes[] = 'wpc-filter-'.$filter['e_name'];
945 }
946
947 if( isset( $filter['hierarchy'] ) && $filter['hierarchy'] === 'yes' ){
948 $classes[] = 'wpc-filter-has-hierarchy';
949 }
950
951 if( ! empty( $default_classes ) ){
952 $classes = array_merge( $classes, $default_classes );
953 }
954
955 $classes = apply_filters( 'wpc_filter_content_classes', $classes, $default_classes );
956
957 return implode( " ", $classes );
958
959 }
960
961 if ( ! function_exists( 'flrt_filter_no_terms_message' ) ) {
962 /**
963 * Outputs "No terms" message
964 * @param string $tag HTML tag name for the message wrapper
965 * @since 1.7.6
966 */
967 function flrt_filter_no_terms_message( $tag = 'li' ) {
968 if ( ! $tag || $tag === '' ) {
969 $tag = 'li';
970 }
971
972 $srch = isset( $_GET['srch'] ) ? filter_input( INPUT_GET, 'srch', FILTER_SANITIZE_SPECIAL_CHARS ) : '';
973
974 echo '<'.$tag.' class="wpc-no-filter-terms">';
975 if ( ! flrt_is_filter_request() && ! $srch ) {
976 esc_html_e('There are no filter terms yet', 'filter-everything' );
977 if( flrt_is_debug_mode() ){
978 echo '&nbsp;'.flrt_help_tip(
979 esc_html__('Possible reasons: 1) Filter\'s criterion doesn\'t contain any terms yet, and you have to add them 2) Terms may be created, but no one post that should be filtered attached to these terms 3) You excluded all possible terms in Filter\'s options.', 'filter-everything')
980 );
981 }
982 } else {
983 esc_html_e('N/A', 'filter-everything' );
984 }
985 echo '</'.$tag.'>';
986 }
987 }
988
989 if ( ! function_exists( 'flrt_filter_more_less' ) ) {
990 /**
991 * Outputs More/Less toggle link
992 * @param array $filter Filter array
993 * @since 1.7.6
994 */
995 function flrt_filter_more_less( $filter ) {
996 if ( isset( $filter['more_less'] ) && $filter['more_less'] === 'yes' ): ?>
997 <a class="wpc-see-more-control wpc-toggle-a" href="javascript:void(0);" data-fid="<?php echo esc_attr( $filter['ID'] ); ?>"><?php esc_html_e('See more', 'filter-everything' ); ?></a>
998 <a class="wpc-see-less-control wpc-toggle-a" href="javascript:void(0);" data-fid="<?php echo esc_attr( $filter['ID'] ); ?>"><?php esc_html_e('See less', 'filter-everything' ); ?></a>
999 <?php endif;
1000 }
1001 }
1002
1003 if ( ! function_exists( 'flrt_filter_search_field' ) ) {
1004 /**
1005 * Outputs filter search field
1006 * @since 1.7.6
1007 */
1008 function flrt_filter_search_field( $filter, $view_args, $terms ) {
1009 if ( empty( $terms ) ) {
1010 return false;
1011 }
1012
1013 if( $filter['search'] === 'yes' && $view_args['ask_to_select_parent'] === false ): ?>
1014 <div class="wpc-filter-search-wrapper wpc-filter-search-wrapper-<?php echo esc_attr( $filter['ID'] ); ?>">
1015 <span class="wpc-search-icon"></span>
1016 <input class="wpc-filter-search-field" type="text" value="" placeholder="<?php esc_html_e('Search', 'filter-everything' ) ?>" />
1017 <button class="wpc-search-clear" type="button" title="<?php esc_html_e('Clear search', 'filter-everything' ) ?>"><span class="wpc-search-clear-icon">&#215;</span></button>
1018 </div>
1019 <?php endif;
1020 }
1021 }
1022
1023 function flrt_get_contrast_ratio($hexColor){
1024 // hexColor RGB
1025 $R1 = hexdec(substr($hexColor, 1, 2));
1026 $G1 = hexdec(substr($hexColor, 3, 2));
1027 $B1 = hexdec(substr($hexColor, 5, 2));
1028
1029 // Black RGB
1030 $blackColor = "#000000";
1031 $R2BlackColor = hexdec(substr($blackColor, 1, 2));
1032 $G2BlackColor = hexdec(substr($blackColor, 3, 2));
1033 $B2BlackColor = hexdec(substr($blackColor, 5, 2));
1034
1035 // Calc contrast ratio
1036 $L1 = 0.2126 * pow($R1 / 255, 2.2) +
1037 0.7152 * pow($G1 / 255, 2.2) +
1038 0.0722 * pow($B1 / 255, 2.2);
1039
1040 $L2 = 0.2126 * pow($R2BlackColor / 255, 2.2) +
1041 0.7152 * pow($G2BlackColor / 255, 2.2) +
1042 0.0722 * pow($B2BlackColor / 255, 2.2);
1043
1044 $contrastRatio = 0;
1045 if ($L1 > $L2) {
1046 $contrastRatio = (int)(($L1 + 0.05) / ($L2 + 0.05));
1047 } else {
1048 $contrastRatio = (int)(($L2 + 0.05) / ($L1 + 0.05));
1049 }
1050 return round($contrastRatio);
1051 }
1052 function flrt_get_contrast_color($hexColor)
1053 {
1054
1055 $contrastRatio = flrt_get_contrast_ratio($hexColor);
1056 // If contrast is more than 5, return black color
1057 if ($contrastRatio > 10) {
1058 return '#333333';
1059 } else {
1060 // if not, return white color.
1061 return '#f5f5f5';
1062 }
1063 }
1064 function flrt_hex_to_rgb($hexColor, $opacity = 100) {
1065 $hexColor = ltrim($hexColor, '#');
1066
1067 $r = hexdec(substr($hexColor, 0, 2));
1068 $g = hexdec(substr($hexColor, 2, 2));
1069 $b = hexdec(substr($hexColor, 4, 2));
1070 $opacity = $opacity/100;
1071 return "rgb($r $g $b / $opacity)";
1072 }
1073
1074 function flrt_add_color_opacity($hexColor, $opacity = 50){
1075 $contrastRatio = flrt_get_contrast_ratio($hexColor);
1076
1077 if ($contrastRatio <= 15) {
1078 return flrt_hex_to_rgb($hexColor, $opacity);
1079 } else {
1080 return $hexColor;
1081 }
1082 }
1083
1084
1085 function flrt_default_posts_container()
1086 {
1087 return apply_filters( 'wpc_theme_posts_container', '#primary' );
1088 }
1089
1090 function flrt_default_theme_color()
1091 {
1092 return apply_filters( 'wpc_theme_color', '#0570e2' );
1093 }
1094
1095 function flrt_term_id($name, $filter, $id, $echo = true )
1096 {
1097 $attr = esc_attr( "wpc-" . $name . "-" . $filter['entity'] . "-" . esc_attr( $filter['e_name'] ) . "-" . $id );
1098 if( $echo ){
1099 echo $attr;
1100 } else {
1101 return $attr;
1102 }
1103 }
1104
1105 function flrt_get_icon_svg($color = '#ffffff' )
1106 {
1107 $svg = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" enable-background="new 0 0 53 53" id="Layer_1" x="0px" y="0px" viewBox="0 0 53 53" style="enable-background:new 0 0 53 53;" xml:space="preserve">
1108 <style type="text/css">
1109 .st0{display:none;}
1110 .st1{display:inline;}
1111 .st3{fill:'.$color.';}
1112 .st4{display:none;fill:'.$color.';}
1113 .st5{fill:none;fill-opacity:0;stroke:'.$color.';stroke-width:3;stroke-miterlimit:10;}
1114 .st6{fill:none;fill-opacity:0;stroke:'.$color.';stroke-width:2;stroke-miterlimit:10;}
1115 </style>
1116 <g id="Layer_2_00000047770719710110742230000003923951626148849557_" class="st0">
1117 <rect x="0" y="0" class="st1" width="53.1" height="53.1"/>
1118 </g>
1119 <g id="Layer_1_00000162333103265806981530000017146624247591674556_">
1120 <g>
1121 <defs>
1122 <path 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"/>
1123 </defs>
1124 <clipPath id="SVGID_00000112608340804245442460000013986219178199086244_">
1125 <use xlink:href="#SVGID_1_" style="overflow:visible;"/>
1126 </clipPath>
1127 <g id="BarsClipped" style="clip-path:url(#SVGID_00000112608340804245442460000013986219178199086244_);">
1128 <path 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"/>
1129 <path 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"/>
1130 <path 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"/>
1131 </g>
1132 </g>
1133 <path 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"/>
1134 <path 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"/>
1135 <path 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"/>
1136 <path 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"/>
1137 <path 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"/>
1138 <path 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"/>
1139
1140 <path class="st5" fill-opacity="0" 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"/>
1141
1142 <path 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"/>
1143 </g>
1144
1145 <circle class="st6" fill-opacity="0" cx="23.3" cy="20.1" r="2.5"/>
1146 <circle class="st6" fill-opacity="0" cx="33.2" cy="24.8" r="2.5"/>
1147 <circle class="st6" fill-opacity="0" cx="23" cy="34.8" r="2.5"/>
1148 </svg>';
1149
1150 return 'data:image/svg+xml;base64,' . base64_encode( $svg );
1151 }
1152
1153 function flrt_get_icon_html()
1154 {
1155 ?>
1156 <span class="wpc-icon-html-wrapper">
1157 <span class="wpc-icon-line-1"></span>
1158 <span class="wpc-icon-line-2"></span>
1159 <span class="wpc-icon-line-3"></span>
1160 </span>
1161 <?php
1162 }
1163
1164 function flrt_get_plugin_name()
1165 {
1166 if( defined('FLRT_FILTERS_PRO')){
1167 return esc_html__( 'Filter Everything Pro', 'filter-everything' );
1168 }else{
1169 return esc_html__( 'Filter Everything', 'filter-everything' );
1170 }
1171 }
1172
1173 function flrt_get_plugin_url($type = 'about', $full = false )
1174 {
1175 if( $full ){
1176 return esc_url($full);
1177 }
1178
1179 return esc_url(FLRT_PLUGIN_URL . '/' . $type );
1180 }
1181
1182 function flrt_get_term_by_slug($prefix ){
1183 global $wpdb;
1184
1185 $sql = "SELECT {$wpdb->terms}.slug FROM {$wpdb->terms} WHERE {$wpdb->terms}.slug = '%s'";
1186 $sql = $wpdb->prepare( $sql, $prefix );
1187 $result = $wpdb->get_row( $sql );
1188
1189 if( isset($result->slug) && $result->slug ){
1190 return $result->slug;
1191 }
1192
1193 return false;
1194 }
1195
1196 function flrt_walk_terms_tree( $terms, $args ) {
1197 _deprecated_function( 'flrt_walk_terms_tree', '1.7.6', 'flrt_filter_walk_terms_tree()' );
1198 flrt_filter_walk_terms_tree( $terms, $args );
1199 }
1200
1201 function flrt_filter_walk_terms_tree( $terms, $args ) {
1202 $walker = new \FilterEverything\Filter\WalkerCheckbox();
1203
1204 $depth = -1;
1205 if ( isset( $args['filter']['hierarchy'] ) && $args['filter']['hierarchy'] === 'yes' ) {
1206 $depth = 10;
1207 }
1208
1209 return $walker->walk( $terms, $depth, $args );
1210 }
1211
1212 function flrt_get_all_parents($elements, $parent_id, &$ids ){
1213 if( isset( $elements[$parent_id]->parent ) && $elements[$parent_id]->parent > 0 ){
1214 $id = $elements[$parent_id]->parent;
1215 $ids_flipped = array_flip($ids);
1216
1217 if( ! isset( $ids_flipped[$id] ) ){
1218 $ids[] = $id;
1219 }
1220
1221 flrt_get_all_parents( $elements, $id, $ids );
1222 }else{
1223 return $ids;
1224 }
1225 }
1226
1227 function flrt_get_parents_with_not_empty_children($elements, $key = 'cross_count' ){
1228 $has_posts_in_children = [];
1229
1230 if( empty( $elements ) || ! is_array( $elements ) ){
1231 return $has_posts_in_children;
1232 }
1233
1234 $new_elements = [];
1235
1236 foreach ( $elements as $k => $e ) {
1237 $new_elements[$e->term_id] = $e;
1238 }
1239
1240 $has_posts_in_children_flipped = array_flip( $has_posts_in_children );
1241
1242 foreach ( $new_elements as $e ) {
1243 if ( isset( $e->parent ) && ! empty( $e->parent ) && $e->$key > 0 ) {
1244 // Find all parents for term that contains posts
1245 if( ! isset( $has_posts_in_children_flipped[ $e->parent ] ) ){
1246 $has_posts_in_children[] = $e->parent;
1247 }
1248
1249 flrt_get_all_parents( $new_elements, $e->parent, $has_posts_in_children );
1250 }
1251 }
1252
1253 return $has_posts_in_children;
1254 }
1255
1256 /**
1257 * Combines all filter sets for the same WP_Query
1258 *
1259 * @param array $all_sets - list of all page related sets
1260 * @param $current_set
1261 * @return array $queryRelatedSets IDs of all query related sets
1262 */
1263 function flrt_get_sets_with_the_same_query( $all_sets, $current_set ){
1264 $queryRelatedSets = [];
1265 // First detect desired query index;
1266 $query = '';
1267 $post_type = '';
1268 $location = '';
1269 $set_id = $current_set['ID'];
1270
1271 foreach( $all_sets as $set ){
1272 if( $set['ID'] === $set_id ){
1273 // Current Set values
1274 $query = $set['query'];
1275 $post_type = $set['filtered_post_type'];
1276 $location = $set['query_location'];
1277 break;
1278 }
1279 }
1280
1281 // Then find all sets with such query
1282 foreach( $all_sets as $set ){
1283 if( $set['query'] === $query && $post_type === $set['filtered_post_type'] && $location === $set['query_location'] ){
1284 $queryRelatedSets[] = $set['ID'];
1285 }
1286 }
1287
1288 if( empty( $queryRelatedSets ) ){
1289 $queryRelatedSets[] = $set_id;
1290 }
1291
1292 return $queryRelatedSets;
1293 }
1294
1295 function flrt_find_all_descendants($arr) {
1296 $all_results = [];
1297
1298 if( empty( $arr ) || ! is_array( $arr ) ){
1299 return $all_results;
1300 }
1301
1302 foreach ($arr as $k => $v) {
1303 $curr_result = [];
1304
1305 for ($stack = [$k]; count($stack);) {
1306 $el = array_pop($stack);
1307
1308 if (array_key_exists($el, $arr) && is_array($arr[$el])) {
1309 foreach ($arr[$el] as $child) {
1310 $curr_result []= $child;
1311 $stack []= $child;
1312 }
1313 }
1314 }
1315
1316 if (count($curr_result)) {
1317 $all_results[$k] = $curr_result;
1318 }
1319 }
1320
1321 return $all_results;
1322 }
1323
1324 function flrt_debug_title(){
1325
1326 echo '<div class="wpc-debug-title">'.esc_html__('Filter Everything debug', 'filter-everything');
1327 echo '&nbsp;'.flrt_help_tip(
1328 sprintf(
1329 __('Debug messages are visible for logged in administrators only. You can disable them in Filters -> <a href="%s">Settings</a> -> Debug mode.', 'filter-everything'),
1330 admin_url( 'edit.php?post_type=filter-set&page=filters-settings' )
1331 ), true ).'</div>';
1332 }
1333
1334 function flrt_is_debug_mode(){
1335 $debug_mode = false;
1336 if( flrt_get_option( 'widget_debug_messages' ) === 'on' ) {
1337 if( current_user_can( flrt_plugin_user_caps() ) ){
1338 $debug_mode = true;
1339 }
1340 }
1341
1342 return $debug_mode;
1343 }
1344
1345 function flrt_clean( $var ) {
1346 if ( is_array( $var ) ) {
1347 return array_map( 'flrt_clean', $var );
1348 } else {
1349 return is_scalar( $var ) ? sanitize_text_field( $var ) : $var;
1350 }
1351 }
1352
1353 function flrt_sorting_option_value( $order_by_value, $meta_keys, $orders, $i ){
1354 $meta_key = isset( $meta_keys[$i] ) ? $meta_keys[$i] : '';
1355 $order = isset( $orders[$i] ) ? $orders[$i] : '';
1356
1357 $option_value = $order_by_value;
1358
1359 if( in_array( $order_by_value, ['m', 'n'], true ) ){
1360 $option_value .= $meta_key;
1361 }
1362
1363 $option_value .= ( $order === 'desc' ) ? '-'.$order : '';
1364
1365 return $option_value;
1366 }
1367
1368 function flrt_get_active_plugins(){
1369
1370 if( is_multisite() ){
1371 $active_plugins = get_site_option('active_sitewide_plugins');
1372 if( is_array( $active_plugins ) ){
1373 $active_plugins = array_keys( $active_plugins );
1374 }
1375
1376 $site_active_plugins = apply_filters( 'active_plugins', get_option('active_plugins') );
1377 $active_plugins = array_merge( $active_plugins, $site_active_plugins );
1378 }else{
1379 $active_plugins = apply_filters( 'active_plugins', get_option('active_plugins') );
1380 }
1381
1382 return $active_plugins;
1383 }
1384
1385 function flrt_get_terms_transient_key( $salt, $include_lang = true ){
1386 $key = 'wpc_terms_' . $salt;
1387 if ( flrt_wpml_active() && defined( 'ICL_LANGUAGE_CODE' ) && $include_lang ) {
1388 $key .= '_'.ICL_LANGUAGE_CODE;
1389 }
1390
1391 if( function_exists('pll_current_language') && $include_lang ){
1392 $pll_lang = pll_current_language();
1393 if( $pll_lang ){
1394 $key .= '_'.$pll_lang;
1395 }
1396 }
1397
1398 return $key;
1399 }
1400
1401 function flrt_get_post_ids_transient_key( $salt ){
1402 $key = 'wpc_posts_' . $salt;
1403 if (flrt_wpml_active() && defined('ICL_LANGUAGE_CODE')) {
1404 $key .= '_'.ICL_LANGUAGE_CODE;
1405 }
1406
1407 if( function_exists('pll_current_language') ){
1408 $pll_lang = pll_current_language();
1409 if( $pll_lang ){
1410 $key .= '_'.$pll_lang;
1411 }
1412 }
1413
1414 return $key;
1415 }
1416
1417 function flrt_get_variations_transient_key( $salt ){
1418 $key = 'wpc_variations_' . $salt;
1419 if (flrt_wpml_active() && defined('ICL_LANGUAGE_CODE')) {
1420 $key .= '_'.ICL_LANGUAGE_CODE;
1421 }
1422
1423 if( function_exists('pll_current_language') ){
1424 $pll_lang = pll_current_language();
1425 if( $pll_lang ){
1426 $key .= '_'.$pll_lang;
1427 }
1428 }
1429
1430 return $key;
1431 }
1432
1433 function flrt_is_query_on_page( $setPosts, $searchKey ){
1434 $filterSet = Container::instance()->getFilterSetService();
1435 $sets = [];
1436 if( ! is_array( $setPosts ) ){
1437 return $sets;
1438 }
1439
1440 foreach ( $setPosts as $set ){
1441
1442 $parameters = maybe_unserialize( $set->post_content );
1443 $query = isset( $parameters['wp_filter_query'] ) ? $parameters['wp_filter_query']: '-1';
1444 if($filterSet->under_limit_filter_set($set->ID)){
1445 continue;
1446 }
1447
1448 if( isset( $parameters['use_apply_button'] ) && $parameters['use_apply_button'] === 'yes' ){
1449
1450 $query_on_the_page = false;
1451 $show_on_the_page = false;
1452
1453 if( defined('FLRT_FILTERS_PRO') && FLRT_FILTERS_PRO ) {
1454 if ( isset( $parameters['apply_button_post_name'] ) ){
1455
1456 if( $parameters['apply_button_post_name'] === $set->post_name ||
1457 $parameters['apply_button_post_name'] === 'no_page___no_page' ){
1458 $query_on_the_page = true;
1459 }
1460
1461 if( in_array( $parameters['apply_button_post_name'], $searchKey ) || ( $parameters['apply_button_post_name'] === 'no_page___no_page' ) ){
1462 $show_on_the_page = true;
1463 }
1464 }
1465
1466 }else{
1467 $query_on_the_page = true;
1468 $show_on_the_page = true;
1469 }
1470
1471 $sets[] = array(
1472 'ID' => (string) $set->ID,
1473 'filtered_post_type' => $set->post_excerpt,
1474 'query' => $query, // query hash
1475 'query_location' => $set->post_name,
1476 'query_on_the_page' => $query_on_the_page,
1477 'page_search_keys' => $searchKey,
1478 'show_on_the_page' => $show_on_the_page
1479 );
1480
1481 }else{
1482 if( in_array( $set->post_name, $searchKey ) ){
1483 $sets[] = array(
1484 'ID' => (string) $set->ID,
1485 'filtered_post_type' => $set->post_excerpt,
1486 'query' => $query, // query hash
1487 'query_location' => $set->post_name,
1488 'query_on_the_page' => true,
1489 'page_search_keys' => $searchKey,
1490 'show_on_the_page' => true
1491 );
1492 }else{
1493 // This set is for another page and was selected by Apply button location but the button disabled
1494 continue;
1495 }
1496 }
1497
1498 }
1499
1500 return $sets;
1501 }
1502
1503 function flrt_remove_empty_terms( $checkTerms, $filter, $has_not_empty_children_flipped = [] ){
1504
1505 foreach ($checkTerms as $index => $term) {
1506 if( $filter['hierarchy'] === 'yes' ){
1507
1508 if( $term->cross_count === 0
1509 && ! isset( $has_not_empty_children_flipped[$term->term_id] ) ){
1510 unset($checkTerms[$index]);
1511 }
1512
1513 }else{
1514 if( $term->cross_count === 0 ){
1515 unset($checkTerms[$index]);
1516 }
1517 }
1518 }
1519
1520 return $checkTerms;
1521 }
1522
1523 function flrt_get_wp_queried_term($terms ){
1524 $wp_queried_terms = false;
1525
1526 foreach ( $terms as $term ) {
1527 if ( $term->wp_queried === true ){
1528 $wp_queried_terms = $term;
1529 break;
1530 }
1531 }
1532
1533 return $wp_queried_terms;
1534 }
1535
1536 function flrt_get_filter_terms( $filter, $posType, $em = false ) {
1537 if( ! $em ){
1538 $em = Container::instance()->getEntityManager();
1539 }
1540
1541 $entityObj = $em->getEntityByFilter( $filter, $posType );
1542 // Exclude or include terms
1543 $isInclude = ( isset( $filter['include'] ) && $filter['include'] === 'yes' );
1544 $entityObj->setExcludedTerms( $filter['exclude'], $isInclude );
1545
1546 $terms = $entityObj->getTerms();
1547
1548 return apply_filters( 'wpc_items_after_calc_term_count', $terms );
1549 }
1550
1551 function flrt_get_term_brand_image( $term_id, $filter ) {
1552 $src = false;
1553
1554 if ( $filter['e_name'] === 'pwb-brand' ) {
1555 $attachment_id = get_term_meta($term_id, 'pwb_brand_image', true);
1556 $attachment_props = wp_get_attachment_image_src($attachment_id, 'small');
1557 $src = isset($attachment_props[0]) ? $attachment_props[0] : false;
1558 } elseif ( in_array( $filter['e_name'], ['yith_product_brand', 'product_brand'] ) ) {
1559 $attachment_id = get_term_meta($term_id, 'thumbnail_id', true);
1560 $attachment_props = wp_get_attachment_image_src($attachment_id, 'small');
1561 $src = isset($attachment_props[0]) ? $attachment_props[0] : false;
1562 } else {
1563 // pa_brand
1564 $src = get_term_meta( $term_id, 'image', true );
1565
1566 if( intval( $src ) > 0 ){
1567 $src = wp_get_attachment_image_url( $src,'full' );
1568 }
1569
1570 if ( isset( $src['id'] ) && $src['id'] ) {
1571 $src = wp_get_attachment_image_url( $src['id'],'full' );
1572 }
1573 }
1574
1575 return $src;
1576 }
1577
1578 function flrt_get_term_swatch_image( $term_id, $filter ) {
1579 $src = false;
1580 $image_key = 'image';
1581
1582 if ( strpos( $filter['e_name'], 'pa_' ) === 0 ) {
1583 $image_key = 'product_attribute_' . $image_key;
1584 }
1585
1586 if ( $filter['e_name'] === 'product_cat' ) {
1587 $image_key = 'thumbnail_id';
1588 }
1589
1590 $image_key = apply_filters( 'wpc_image_term_meta_key', $image_key, $filter );
1591
1592 $image_id = get_term_meta( $term_id, $image_key, true );
1593 $swatch_image_size = apply_filters( 'wpc_swatch_image_size', 'thumbnail' );
1594
1595 if ( $image_id ) {
1596 $src = wp_get_attachment_image_url( $image_id, $swatch_image_size );
1597 }
1598
1599 return $src;
1600 }
1601
1602 function flrt_get_term_swatch_color( $term_id, $filter ) {
1603 $color = false;
1604 $color_key = 'color';
1605
1606 if ( strpos( $filter['e_name'], 'pa_' ) === 0 ) {
1607 $color_key = 'product_attribute_' . $color_key;
1608 }
1609
1610 $color_key = apply_filters( 'wpc_color_term_meta_key', $color_key, $filter );
1611 $color = get_term_meta( $term_id, $color_key, true );
1612
1613 return $color;
1614 }
1615
1616 /**
1617 * Checks and returns date format.
1618 * Does not check if date is valid
1619 * @param $date
1620 * @return string|false date, time format or false
1621 */
1622 function flrt_detect_date_type( $date_or_time )
1623 {
1624 if ( ! $date_or_time ) {
1625 return false;
1626 }
1627 $format = false;
1628 $date = false;
1629 $time = false;
1630
1631 $date_or_time = str_replace( FLRT_DATE_TIME_SEPARATOR, ' ', $date_or_time );
1632
1633 $pcs = date_parse( $date_or_time );
1634 if ( $pcs['year'] !== false && $pcs['month'] !== false && $pcs['day'] !== false ) {
1635 $date = true;
1636 }
1637
1638 if ( $pcs['hour'] !== false && $pcs['minute'] !== false && $pcs['second'] !== false ) {
1639 $time = true;
1640 }
1641
1642 if ( $date && $time ) {
1643 $format = 'datetime';
1644 } else {
1645 if ( $date ) {
1646 $format = 'date';
1647 }
1648 if ( $time ) {
1649 $format = 'time';
1650 }
1651 }
1652
1653 return $format;
1654 }
1655
1656 /**
1657 * Modifies datetime to the human format
1658 * @param $datetime
1659 * @param $date_type
1660 * @param string $sep
1661 * @return mixed|string
1662 */
1663 function flrt_clean_date_time( $datetime, $date_type, $sep = " " )
1664 {
1665 if ( $date_type === 'date' || $date_type === 'DATE' ) {
1666 $pieces = explode( $sep, $datetime );
1667 return $pieces[0]; //date e.g. 2021-05-14
1668 } else if ( $date_type === 'time' || $date_type === 'TIME') {
1669 $pieces = explode( $sep, $datetime );
1670 if ( isset( $pieces[1] ) ) {
1671 return $pieces[1]; //time e.g. 14:15:47
1672 }
1673 } else {
1674 return $datetime; // str_replace( $sep, ' ', $datetime ); //datetime e.g. 2021-05-14 14:15:47
1675 }
1676 }
1677
1678 function flrt_apply_date_format( $income_date, $format = "Y-m-d H:i:s" )
1679 {
1680 $timestamp = strtotime( $income_date );
1681 return flrt_date( $format, $timestamp );
1682 }
1683
1684 function flrt_date( $format, $timestamp = null ) {
1685 global $wp_locale;
1686
1687 if ( null === $timestamp ) {
1688 $timestamp = time();
1689 } elseif ( ! is_numeric( $timestamp ) ) {
1690 return false;
1691 }
1692
1693 $datetime = date_create( '@' . $timestamp );
1694
1695 if ( empty( $wp_locale->month ) || empty( $wp_locale->weekday ) ) {
1696 $date = $datetime->format( $format );
1697 } else {
1698 // We need to unpack shorthand `r` format because it has parts that might be localized.
1699 $format = preg_replace( '/(?<!\\\\)r/', DATE_RFC2822, $format );
1700
1701 $new_format = '';
1702 $format_length = strlen( $format );
1703 $month = $wp_locale->get_month( $datetime->format( 'm' ) );
1704 $weekday = $wp_locale->get_weekday( $datetime->format( 'w' ) );
1705
1706 for ( $i = 0; $i < $format_length; $i++ ) {
1707 switch ( $format[ $i ] ) {
1708 case 'D':
1709 $new_format .= addcslashes( $wp_locale->get_weekday_abbrev( $weekday ), '\\A..Za..z' );
1710 break;
1711 case 'F':
1712 $new_format .= addcslashes( $month, '\\A..Za..z' );
1713 break;
1714 case 'l':
1715 $new_format .= addcslashes( $weekday, '\\A..Za..z' );
1716 break;
1717 case 'M':
1718 $new_format .= addcslashes( $wp_locale->get_month_abbrev( $month ), '\\A..Za..z' );
1719 break;
1720 case 'a':
1721 $new_format .= addcslashes( $wp_locale->get_meridiem( $datetime->format( 'a' ) ), '\\A..Za..z' );
1722 break;
1723 case 'A':
1724 $new_format .= addcslashes( $wp_locale->get_meridiem( $datetime->format( 'A' ) ), '\\A..Za..z' );
1725 break;
1726 case '\\':
1727 $new_format .= $format[ $i ];
1728
1729 // If character follows a slash, we add it without translating.
1730 if ( $i < $format_length ) {
1731 $new_format .= $format[ ++$i ];
1732 }
1733 break;
1734 default:
1735 $new_format .= $format[ $i ];
1736 break;
1737 }
1738 }
1739
1740 $date = date_format( $datetime, $new_format );
1741 }
1742
1743 return $date;
1744 }
1745
1746 function flrt_default_date_format( $date_type = 'date' )
1747 {
1748 /**
1749 * @todo date format depend from localization and geo settings
1750 * we have to relate them here
1751 */
1752 $date_format = __('F j, Y');
1753
1754 switch ( $date_type ) {
1755 case 'date':
1756 $date_format = __('F j, Y'); //'d-m-Y';
1757 break;
1758 case 'datetime':
1759 $date_format = __('F j, Y g:i a'); //'d-m-Y H:i:s';
1760 break;
1761 case 'time':
1762 $date_format = __('g:i a'); // 'H:i:s';
1763 break;
1764 }
1765
1766 return $date_format;
1767 }
1768
1769 function flrt_convert_date_to_js( $date_or_time ){
1770 $date_php_to_js = Container::instance()->getParam('php_to_js_date_formats');
1771 return flrt_str_replace( $date_or_time, $date_php_to_js );
1772 }
1773
1774 function flrt_convert_time_to_js( $date_or_time ){
1775 $time_php_to_js = Container::instance()->getParam('php_to_js_time_formats');
1776 return flrt_str_replace( $date_or_time, $time_php_to_js );
1777 }
1778
1779 function flrt_str_replace( $string = '', $search_replace = array() ) {
1780 $ignore = array();
1781 unset( $search_replace[''] );
1782
1783 foreach ( $search_replace as $search => $replace ) {
1784 if ( in_array( $search, $ignore ) ) {
1785 continue;
1786 }
1787 if ( strpos( $string, $search ) === false ) {
1788 continue;
1789 }
1790 $string = str_replace( $search, $replace, $string );
1791 $ignore[] = $replace;
1792 }
1793
1794 return $string;
1795 }
1796
1797 function flrt_split_date_time( $date_time = '' ) {
1798 $php_date = Container::instance()->getParam('php_to_js_date_formats');
1799 $php_time = Container::instance()->getParam('php_to_js_time_formats');
1800 $chars = str_split( $date_time );
1801 $type = 'date';
1802
1803 $data = array(
1804 'date' => '',
1805 'time' => '',
1806 );
1807
1808 foreach ( $chars as $i => $c ) {
1809 if ( isset( $php_date[ $c ] ) ) {
1810 $type = 'date';
1811 } elseif ( isset( $php_time[ $c ] ) ) {
1812 $type = 'time';
1813 }
1814 $data[ $type ] .= $c;
1815 }
1816
1817 $data['date'] = trim( $data['date'] );
1818 $data['time'] = trim( $data['time'] );
1819
1820 return $data;
1821 }
1822
1823 function flrt_string_polyfill( $data ) {
1824 return map_deep( $data, 'flrt_string_polyfill_body' );
1825 }
1826
1827 function flrt_string_polyfill_body( $string ){
1828
1829 if ( ! is_string( $string ) ) {
1830 return $string;
1831 }
1832
1833 $str = preg_replace('/\x00|<[^>]*>?/', '', $string );
1834 return str_replace( ["'", '"'], ['&#39;', '&#34;'], $str );
1835 }
1836
1837 function flrt_rating_star(){
1838 return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 25">
1839 <polygon class="cls-1" points="19.89 24.5 12.48 19.8 5.06 24.48 7.03 15.62 0.5 9.64 9.12 8.87 12.51 0.5 15.88 8.88 24.5 9.68 17.96 15.63 19.89 24.5"/>
1840 </svg>';
1841 }
1842
1843 function flrt_check_update_mobile_settings(){
1844
1845 $settings = get_option('wpc_filter_settings', false);
1846 if ($settings !== false){
1847 if(!flrt_get_option('mobile_filter_settings')){
1848
1849 $mobile_filter_settings = 'nothing';
1850
1851 if ( (flrt_get_option('show_bottom_widget') === 'on' && flrt_get_option('show_open_close_button') === 'on')
1852 || (flrt_get_option('show_bottom_widget') === 'on' && !flrt_get_option('show_open_close_button'))){
1853 $mobile_filter_settings = 'show_bottom_widget';
1854 } elseif (flrt_get_option('show_open_close_button') == 'on' && !flrt_get_option('show_bottom_widget')){
1855 $mobile_filter_settings = 'show_open_close_button';
1856 }
1857
1858 $settings = get_option('wpc_filter_settings');
1859
1860 if (isset($settings['show_bottom_widget']) && $settings['show_bottom_widget']) {
1861 unset($settings['show_bottom_widget']);
1862 }
1863
1864 if (isset($settings['show_open_close_button']) && $settings['show_open_close_button']) {
1865 unset($settings['show_open_close_button']);
1866 }
1867
1868 $settings['mobile_filter_settings'] = $mobile_filter_settings;
1869 update_option('wpc_filter_settings', $settings);
1870 }
1871 }
1872 }
1873
1874 if(!function_exists('flrt_set_transient')){
1875 function flrt_set_transient($transient, $value, $expiration = 0){
1876 if(defined('FLRT_SET_TRANSIENT_ENABLED') && FLRT_SET_TRANSIENT_ENABLED){
1877 set_transient( $transient, $value, $expiration);
1878 }
1879 }
1880 }
1881
1882 if(!function_exists('flrt_get_transient')){
1883 function flrt_get_transient($transient){
1884 if(defined('FLRT_SET_TRANSIENT_ENABLED') && FLRT_SET_TRANSIENT_ENABLED){
1885 return get_transient( $transient );
1886 }
1887 return false;
1888 }
1889 }
1890
1891 if(!class_exists('FlrtWooDiscountRules')) {
1892 class FlrtWooDiscountRules{
1893
1894 protected $rules;
1895
1896 protected $base;
1897 protected $rule_helper;
1898 protected $discount_calculator;
1899 protected $manage_discount;
1900
1901 //public $filter;
1902 public function __construct()
1903 {
1904 $this->base = new Wdr\App\Controllers\Base();
1905 $this->rule_helper = new Wdr\App\Helpers\Rule();
1906 $this->manage_discount = new Wdr\App\Controllers\ManageDiscount();
1907 $this->rules = $this->manage_discount->getDiscountRules();
1908 $this->discount_calculator = new Wdr\App\Controllers\DiscountCalculator($this->rule_helper->getAvailableRules($this->base->getAvailableConditions()));
1909 }
1910
1911 public function getProductPriceToDisplay($product){
1912 return $this->discount_calculator->getProductPriceToDisplay($product, 1);
1913 }
1914 }
1915 function flrt_woo_discount_rules_class(){
1916 return new FlrtWooDiscountRules();
1917 }
1918 }
1919
1920
1921 if(!function_exists('flrt_is_sitemap_exists')) {
1922 function flrt_is_sitemap_exists()
1923 {
1924 $filepath = rtrim(FLRT_XML_PATH, '/\\') . '/filter-sitemap-index.xml';
1925 return file_exists($filepath);
1926 }
1927 }
1928 if(!function_exists('flrt_get_index_sitemap')) {
1929 function flrt_get_index_sitemap()
1930 {
1931 return fltr_get_url_from_absolute_path(FLRT_XML_PATH . '/filter-sitemap-index.xml');
1932 }
1933 }
1934
1935 if(!function_exists('fltr_get_url_from_absolute_path')) {
1936 function fltr_get_url_from_absolute_path($absolute_path)
1937 {
1938 $wp_root_path = realpath(ABSPATH);
1939 $wp_url = site_url();
1940
1941 $file_path = realpath($absolute_path);
1942
1943 if (!$file_path || strpos($file_path, $wp_root_path) !== 0) {
1944 return false;
1945 }
1946
1947
1948 $relative_path = str_replace($wp_root_path, '', $file_path);
1949 $relative_path = str_replace('\\', '/', $relative_path);
1950
1951 return rtrim($wp_url, '/') . $relative_path;
1952 }
1953 }
1954
1955 if(!function_exists('flrt_has_filter_seo_rules')) {
1956 function flrt_has_filter_seo_rules()
1957 {
1958 $args = [
1959 'post_type' => 'filter-seo-rule',
1960 'post_status' => 'publish',
1961 'posts_per_page' => 1,
1962 'fields' => 'ids',
1963 'no_found_rows' => true,
1964 ];
1965
1966 $query = new WP_Query($args);
1967 return $query->have_posts();
1968 }
1969 }
1970
1971 if(!function_exists('flrt_get_last_modified_filter_seo_rule')) {
1972 function flrt_get_last_modified_filter_post_type($post_type)
1973 {
1974 global $wpdb;
1975 $sql = "
1976 SELECT post_modified
1977 FROM {$wpdb->posts}
1978 WHERE post_type = '%s'
1979 AND post_status IN ('publish', 'trash')
1980 ORDER BY post_modified DESC
1981 LIMIT 1";
1982 $query = $wpdb->prepare($sql, $post_type);
1983 $last_modified = $wpdb->get_var($query);
1984
1985 if (!$last_modified) {
1986 return false;
1987 }
1988 return $last_modified;
1989 }
1990 }
1991
1992 if(!function_exists('flrt_check_to_update_xml')){
1993 function flrt_check_to_update_xml(){
1994 $wpc_xml_write_date = get_option('wpc_xml_write_date');
1995 if(!$wpc_xml_write_date) return false;
1996
1997 $last_modified_filter_seo_rule = flrt_get_last_modified_filter_post_type(FLRT_SEO_RULES_POST_TYPE);
1998 $last_modified_filter_set = flrt_get_last_modified_filter_post_type(FLRT_FILTERS_SET_POST_TYPE);
1999
2000 if(!$last_modified_filter_seo_rule && !$last_modified_filter_set) return false;
2001
2002 $wpc_xml_write_date = strtotime($wpc_xml_write_date);
2003 $last_modified_filter_seo_rule = strtotime($last_modified_filter_seo_rule);
2004 $last_modified_filter_set = strtotime($last_modified_filter_set);
2005
2006 if ($last_modified_filter_seo_rule > $wpc_xml_write_date || $last_modified_filter_set > $wpc_xml_write_date) {
2007 return true;
2008 }
2009 if($wpc_xml_write_date !== false && !flrt_has_filter_seo_rules()){
2010 return true;
2011 }
2012 return false;
2013 }
2014 }
2015
2016
2017 if(!function_exists('flrt_post_type_underline_transform')){
2018 function flrt_post_type_underline_transform($post_type){
2019 if(mb_strpos($post_type, '-') !== false){
2020 return str_replace('-', '_', $post_type);
2021 }
2022 return $post_type;
2023 }
2024 }
2025
2026
2027 if(!function_exists('flrt_generate_unique_copy_title')) {
2028
2029 /**
2030 * Generates a unique copy title in the format:
2031 * "Base Title – {copy_text} {N}".
2032 *
2033 * @param string $original_title The original post title to derive the base title from.
2034 * If it already ends with "– {copy_text} N", that suffix is stripped.
2035 * @param string $post_type The WordPress post type within which to check for duplicate titles.
2036 * By default expects the FLRT_FILTERS_SET_POST_TYPE constant.
2037 * @param string $copy_text The suffix text for copies (e.g., 'copy', 'duplicate').
2038 * @param int $start_number Numbering threshold:
2039 * - if 0 (default), the first copy gets number "1";
2040 * - if 1, the first copy has no number; numbering appears only when
2041 * a "… – {copy_text} 1" already exists.
2042 *
2043 * @return string The generated unique copy title.
2044 */
2045
2046 function flrt_generate_unique_copy_title($original_title, $post_type = FLRT_FILTERS_SET_POST_TYPE, $copy_text = 'copy', $number_position = true)
2047 {
2048 global $wpdb;
2049 if ($number_position){
2050 $preg_match_pattern = '/^(.*) – ' . preg_quote($copy_text, '/') . ' \d+$/';
2051 }
2052
2053 if (!$number_position){
2054 $preg_match_pattern = '/^(.*) \d+ – ' . preg_quote($copy_text, '/') . '$/';
2055 }
2056
2057 if (preg_match($preg_match_pattern, $original_title, $matches)) {
2058 $base_title = $matches[1];
2059 } else {
2060 $base_title = $original_title;
2061 }
2062
2063 if ($number_position){
2064 $copy_pattern = $wpdb->esc_like($base_title) . '' . $wpdb->esc_like($copy_text) . '%';
2065 $titles = $wpdb->get_col(
2066 $wpdb->prepare(
2067 "SELECT post_title FROM $wpdb->posts
2068 WHERE (post_title = %s OR post_title LIKE %s)
2069 AND post_type = %s",
2070 $base_title,
2071 $copy_pattern,
2072 $post_type
2073 )
2074 );
2075 }
2076 if (!$number_position) {
2077 $copy_pattern = $wpdb->esc_like($base_title) . ' % – ' . $wpdb->esc_like($copy_text);
2078 $titles = $wpdb->get_col(
2079 $wpdb->prepare(
2080 "SELECT post_title FROM $wpdb->posts
2081 WHERE post_title LIKE %s
2082 AND post_type = %s",
2083 $copy_pattern,
2084 $post_type
2085 )
2086 );
2087 }
2088
2089 $max_copy_number = 0;
2090
2091 if ($number_position){
2092 $preg_match_pattern_title = '/^' . preg_quote($base_title, '/') . '' . preg_quote($copy_text, '/') . ' (\d+)$/';
2093
2094 }
2095 if (!$number_position){
2096 $preg_match_pattern_title = '/^' . preg_quote($base_title, '/') . ' (\d+) – ' . preg_quote($copy_text, '/') . '$/';
2097 }
2098
2099 foreach ($titles as $title) {
2100 if (preg_match($preg_match_pattern_title, $title, $m)) {
2101 if (isset($m[1]) && is_numeric($m[1])) {
2102 $num = intval($m[1]);
2103 if ($num > $max_copy_number) {
2104 $max_copy_number = $num;
2105 }
2106 }
2107 }
2108 }
2109
2110 $new_number = $max_copy_number + 1;
2111
2112 if ($number_position){
2113 $text = $base_title . '' . $copy_text . ' ' . $new_number;
2114 }
2115 if (!$number_position){
2116 $text = $base_title . ' ' . $new_number .'' . $copy_text;
2117 }
2118 return $text;
2119 }
2120 }
2121
2122 if(!function_exists('flrt_get_delete_set_transient')){
2123 function flrt_refresh_temp_transient($set_name, $data)
2124 {
2125 if(get_transient($set_name) !== false){
2126 delete_transient($set_name);
2127 }
2128 set_transient($set_name, $data, 300);
2129 }
2130 }
2131
2132 if(!function_exists('flrt_view_admin_error')){
2133 function flrt_view_admin_error($text){
2134 $error_str = '<div class="notice notice-error is-dismissible"><p>%s</p></div>';
2135 printf(
2136 $error_str,
2137 $text
2138 );
2139 }
2140 }
2141 if (!function_exists('flrt_export_setting_icon')) {
2142 function flrt_export_setting_icon()
2143 {
2144 return '<svg xmlns="http://www.w3.org/2000/svg" fill="#99a2b2" width="19px" height="19px" viewBox="0 0 24 20"><polyline id="primary" points="15 3 21 3 21 9" style="fill: none; stroke: rgb(153,162,178); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"/><line id="primary-2" data-name="primary" x1="11" y1="13" x2="21" y2="3" style="fill: none; stroke: rgb(153,162,178); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"/><path id="primary-3" data-name="primary" d="M21,13v7a1,1,0,0,1-1,1H4a1,1,0,0,1-1-1V4A1,1,0,0,1,4,3h7" style="fill: none; stroke: rgb(153,162,178); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"/></svg>';
2145 }
2146 }
2147
2148 if (!function_exists('flrt_import_setting_icon')) {
2149 function flrt_import_setting_icon()
2150 {
2151 return '<svg xmlns="http://www.w3.org/2000/svg" fill="#000000" width="19px" height="19px" viewBox="0 0 24 23" id="wpc-import-icon"><polyline id="primary" points="17 13 11 13 11 7" style="fill: none; stroke: rgb(153,162,178); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"/><line id="primary-2" data-name="primary" x1="21" y1="3" x2="11" y2="13" style="fill: none; stroke: rgb(153,162,178); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"/><path id="primary-3" data-name="primary" d="M21,13v7a1,1,0,0,1-1,1H4a1,1,0,0,1-1-1V4A1,1,0,0,1,4,3h7" style="fill: none; stroke: rgb(153,162,178); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"/></svg>';
2152 }
2153 }
2154
2155 if (!function_exists('flrt_open_in_new_tab_icon')) {
2156 function flrt_open_in_new_tab_icon()
2157 {
2158 $icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 21" width="18" height="18" aria-hidden="true" focusable="false">
2159 <path d="M19.5 4.5h-7V6h4.44l-5.97 5.97 1.06 1.06L18 7.06v4.44h1.5v-7Zm-13 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-3H17v3a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h3V5.5h-3Z"></path></svg>';
2160 return wp_kses(
2161 $icon,
2162 array(
2163 'svg' => array(
2164 'xmlns' => true,
2165 'viewbox' => true,
2166 'width' => true,
2167 'height' => true,
2168 'aria-hidden' => true,
2169 'focusable' => true,
2170 ),
2171 'path' => array(
2172 'd' => true
2173 ),
2174 )
2175 );
2176 }
2177 }
2178
2179 if (!function_exists('flrt_vailable_in_pro_attr_link')) {
2180 function flrt_vailable_in_pro_attr_link($target_blank = false) : string
2181 {
2182 $link = 'edit.php?post_type=' . FLRT_FILTERS_SET_POST_TYPE . '&page=flrt-pro';
2183 if ($target_blank) {
2184 $link .= '_target=blank';
2185 }
2186 return $link;
2187 }
2188 }
2189 if (!function_exists('flrt_pro_promo_label')) {
2190 function flrt_pro_promo_label($replace_class = false) : string
2191 {
2192 $class = $replace_class ? 'wpc-pro-badge' : 'wpc-pro-badge-transparent';
2193 $label = ' <span class="' . $class .'">' . esc_html__('PRO', 'filter-everything') . '</span>';
2194
2195 return wp_kses($label, [
2196 'span' => [
2197 'class' => []
2198 ]
2199 ]);
2200 }
2201 }
2202
2203 if(!function_exists( 'flrt_unlock_icon')){
2204 function flrt_unlock_icon($width = '20px', $height = '20px', $color = '#3858E9')
2205 {
2206 return '<svg xmlns="http://www.w3.org/2000/svg" height="' . $height . '" viewBox="0 -960 960 960" width="' . $width . '" fill="' . $color . '"><path d="M264-624h336v-96q0-50-35-85t-85-35q-50 0-85 35t-35 85h-72q0-80 56.23-136 56.22-56 136-56Q560-912 616-855.84q56 56.16 56 135.84v96h24q29.7 0 50.85 21.15Q768-581.7 768-552v384q0 29.7-21.16 50.85Q725.68-96 695.96-96H263.72Q234-96 213-117.15T192-168v-384q0-29.7 21.15-50.85Q234.3-624 264-624Zm0 456h432v-384H264v384Zm216.21-120Q510-288 531-309.21t21-51Q552-390 530.79-411t-51-21Q450-432 429-410.79t-21 51Q408-330 429.21-309t51 21ZM264-168v-384 384Z"/></svg>';
2207 }
2208 }
2209
2210 if(!function_exists( 'flrt_crown_icon')){
2211 function flrt_crown_icon($width = '16px', $height = '16px', $color = '#FFFFFF')
2212 {
2213 return '<svg xmlns="http://www.w3.org/2000/svg" fill="' . $color . '" viewBox="0 0 16 16" width="' . $width . '" height="' . $height . '" class="wpc-crown-icon"><path fill="' . $color . '" d="M8.687 1.932c-.238-.634-1.136-.634-1.374 0l-1.642 4.38-3.167-2.11a.733.733 0 0 0-1.126.753L3.333 12h9.334l1.955-7.045a.733.733 0 0 0-1.126-.754L10.33 6.313zM6.654 7.49 8 3.899l1.346 3.59c.166.442.7.614 1.094.352l2.59-1.727-1.363 4.553H4.333L2.97 6.114 5.56 7.841a.733.733 0 0 0 1.094-.352m6.013 5.844H3.333v1.334h9.334z"></path></svg>';
2214 }
2215 }
2216
2217 if(!function_exists( 'flrt_unlock_in_pro')){
2218 function flrt_unlock_in_pro($button_text = '')
2219 {
2220 $string = '<a class="wpc-available-in-pro-button" href="' . admin_url(flrt_vailable_in_pro_attr_link()) . '">';
2221 $string .= !empty($button_text) ? $button_text . ' - ': '';
2222 $string .= flrt_unlock_icon();
2223 $string .= '<span>' . esc_html__('Unlock with PRO', 'filter-everything') . '</span></a>';
2224 return $string;
2225 }
2226 }
2227
2228
2229 add_filter('wpc_filter_default_fields', 'flrt_add_pro_promo_fields', 10, 2);
2230 function flrt_add_pro_promo_fields( $defaultFields, $filterFields )
2231 {
2232
2233 if (!defined('FLRT_FILTERS_PRO')) {
2234 if(flrt_is_woocommerce()){
2235 $updatedFields = [];
2236 foreach ( $defaultFields as $key => $field ){
2237 $updatedFields[$key] = $field;
2238
2239 if( $key === 'hierarchy' ){
2240 $updatedFields['used_for_variations'] = array(
2241 'type' => 'inProButton',
2242 'pro_label' => flrt_pro_promo_label(),
2243 'label' => esc_html__('Use for Variations', 'filter-everything'),
2244 'class' => 'wpc-field-for-variations',
2245 'default' => 'no',
2246 'instructions' => esc_html__('If checked, filtering will take into account variations with this attribute or meta key', 'filter-everything'),
2247 );
2248 }
2249 }
2250 return $updatedFields;
2251 }
2252 }
2253
2254 return $defaultFields;
2255
2256 }
2257
2258 function flrt_pro_features_link()
2259 {
2260 return 'https://layout.filtereverything.pro/#why-choose-pro';
2261 }
2262
2263 function flrt_unlock_pro_link( $utm_content = '' )
2264 {
2265 return 'https://filtereverything.pro/pricing/?utm_source=free_plugin&utm_medium=internal&utm_campaign=free_plugin_upgrade&utm_content=' . $utm_content;
2266 }
2267
2268
2269 function wpc_clear_folder($directory)
2270 {
2271
2272 if (!is_dir($directory)) {
2273 return false;
2274 }
2275
2276 $files = glob(rtrim($directory, '/\\') . DIRECTORY_SEPARATOR . '*');
2277
2278 if(!empty($files)){
2279 foreach ($files as $file) {
2280 if (is_file($file)) {
2281 unlink($file);
2282 }
2283 }
2284 }
2285 }
2286
2287 function flrt_diamond_icon($svg_fill = "var(--wpc-pro-color, #3858E9)")
2288 {
2289 return '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="' . $svg_fill. '" version="1.1" id="Layer_1" width="16px" height="16px" viewBox="0 0 70 70" enable-background="new 0 0 70 70" xml:space="preserve">
2290 <g>
2291 <path d="M67.142,23.641L55.405,10.456c-0.379-0.423-0.92-0.873-1.488-0.873h-37.98c-0.568,0-1.109,0.45-1.489,0.874L2.711,23.752 c-0.691,0.771-0.68,1.94,0.025,2.697L33.462,59.46c0.378,0.407,0.909,0.638,1.464,0.638s1.086-0.257,1.464-0.664l30.728-33.042 C67.822,25.634,67.833,24.411,67.142,23.641z M46.555,25.583L34.902,53.414L22.608,25.583H46.555z M21.725,23.583l-4.417-10h34.272 l-4.188,10H21.725z M32.231,52.152L7.586,25.583h12.879L32.231,52.152z M48.702,25.583H62c0.094,0,0.179-0.029,0.265-0.054 L37.462,52.318L48.702,25.583z M61.871,23.583H49.543l3.971-9.447L61.871,23.583z M15.714,14.851l3.867,8.732H8.027L15.714,14.851z "/>
2292 <path d="M35,14.583H23c-0.552,0-1,0.447-1,1s0.448,1,1,1h12c0.552,0,1-0.447,1-1S35.552,14.583,35,14.583z"/>
2293 <path d="M45,14.583h-5c-0.552,0-1,0.447-1,1s0.448,1,1,1h5c0.552,0,1-0.447,1-1S45.552,14.583,45,14.583z"/>
2294 </g>
2295 </svg>';
2296 }
2297