PluginProbe
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets / 4.5.6
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets v4.5.6
4.5.6 4.5.5 4.5.4 4.2.1 4.2.2 4.2.3 4.5.0 4.5.2 4.5.3 4.2.0 4.1.18 4.1.17 4.1.16 4.1.15 4.1.14 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.0.9 4.1.0 4.1.1 4.1.2 All 148 releases
← All changes | includes/helper.php +163 -80 4.1.13 → 4.5.6 View file →
@@ -2,8 +2,12 @@
2 2
3 3 use UltimatePostKit\Ultimate_Post_Kit_Loader;
4 4 use Elementor\Plugin;
5 5
6 +if (!defined('ABSPATH')) {
7 + exit; // Exit if accessed directly.
8 +}
9 +
6 10 /**
7 11 * You can easily add white label branding for for extended license or multi site license.
8 12 * Don't try for regular license otherwise your license will be invalid.
9 13 * return white label
@@ -165,10 +169,25 @@
165 169 $tax_output = 'objects'; // or objects
166 170 $taxonomies = get_taxonomies( $args, $tax_output );
167 171 if ( $taxonomies ) {
168 172 foreach ( $taxonomies as $taxonomy ) {
169 - $post_type_obj = get_post_type_object( $taxonomy->object_type[0] );
170 - $output[ $taxonomy->name ] = ( $taxonomy->label ? $taxonomy->label : '' ) . ' (' . isset( $post_type_obj->label ) . ')';
173 + $taxonomy_label = $taxonomy->label ? $taxonomy->label : $taxonomy->name;
174 + $term_count = wp_count_terms(
175 + [
176 + 'taxonomy' => $taxonomy->name,
177 + 'hide_empty' => false,
178 + ]
179 + );
180 +
181 + if ( is_wp_error( $term_count ) ) {
182 + $term_count = 0;
183 + }
184 +
185 + $output[ $taxonomy->name ] = sprintf(
186 + '%s (%d)',
187 + $taxonomy_label,
188 + (int) $term_count
189 + );
171 190 }
172 191 }
173 192
174 193 return $output;
@@ -173,8 +192,9 @@
173 192
174 193 return $output;
175 194 }
176 195
196 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- established function name relied on across the plugin family / feedback SDK; renaming would break integration.
177 197 function upk_get_category( $post_type ) {
178 198 switch ( $post_type ) {
179 199 case 'campaign':
180 200 $taxonomy = 'campaign_category';
@@ -335,8 +355,9 @@
335 355
336 356 /**
337 357 * HexColor
338 358 */
359 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- established function name relied on across the plugin family / feedback SDK; renaming would break integration.
339 360 function strToHex( $string, $steps = -10 ) {
340 361
341 362 if ( empty( $string ) ) {
342 363 return false;
@@ -359,8 +380,52 @@
359 380
360 381 return strToUpper( $output );
361 382 }
362 383
384 +/**
385 + * Get the current paged number for a custom WP_Query instance.
386 + *
387 + * @param \WP_Query $wp_query Query object.
388 + * @return int Current page number.
389 + */
390 +function ultimate_post_kit_get_query_paged( $wp_query ) {
391 + if ( ! $wp_query instanceof \WP_Query ) {
392 + return 1;
393 + }
394 +
395 + $paged_from_query = isset( $wp_query->query_vars['paged'] ) ? (int) $wp_query->query_vars['paged'] : 0;
396 + $page_from_query = isset( $wp_query->query_vars['page'] ) ? (int) $wp_query->query_vars['page'] : 0;
397 +
398 + if ( is_front_page() ) {
399 + $paged = max( get_query_var( 'page' ), get_query_var( 'paged' ), $paged_from_query, $page_from_query );
400 + } else {
401 + $paged = max( get_query_var( 'paged' ), $paged_from_query );
402 + }
403 +
404 + return max( 1, (int) $paged );
405 +}
406 +
407 +/**
408 + * Get item counter offset for paginated query loops.
409 + *
410 + * @param \WP_Query $wp_query Query object.
411 + * @return int Zero-based offset for the first item on the current page.
412 + */
413 +function ultimate_post_kit_get_query_counter_offset( $wp_query ) {
414 + if ( ! $wp_query instanceof \WP_Query ) {
415 + return 0;
416 + }
417 +
418 + $paged = ultimate_post_kit_get_query_paged( $wp_query );
419 + $posts_per_page = (int) $wp_query->get( 'posts_per_page' );
420 +
421 + if ( 1 >= $paged || 0 >= $posts_per_page ) {
422 + return 0;
423 + }
424 +
425 + return ( $paged - 1 ) * $posts_per_page;
426 +}
427 +
363 428 function ultimate_post_kit_post_pagination( $wp_query, $widget_id = '' ) {
364 429
365 430 /** Stop execution if there's only 1 page */
366 431 if ( $wp_query->max_num_pages <= 1 ) {
@@ -366,24 +431,10 @@
366 431 if ( $wp_query->max_num_pages <= 1 ) {
367 432 return;
368 433 }
369 434
370 - // Get current page from multiple sources for reliability
371 - $paged_from_query = isset( $wp_query->query_vars['paged'] ) ? $wp_query->query_vars['paged'] : 0;
372 - $page_from_query = isset( $wp_query->query_vars['page'] ) ? $wp_query->query_vars['page'] : 0;
373 -
374 - if ( is_front_page() ) {
375 - // On front page, WordPress can use either 'page' or 'paged' depending on permalink structure
376 - $paged = max( get_query_var( 'page' ), get_query_var( 'paged' ), $paged_from_query, $page_from_query );
377 - $paged = $paged ? $paged : 1;
378 - $page_var = 'page';
379 - } else {
380 - $paged = max( get_query_var( 'paged' ), $paged_from_query );
381 - $paged = $paged ? $paged : 1;
382 - $page_var = 'paged';
383 - }
384 -
385 - $max = intval( $wp_query->max_num_pages );
435 + $paged = ultimate_post_kit_get_query_paged( $wp_query );
436 + $max = (int) $wp_query->max_num_pages;
386 437
387 438 /** Add current page to the array */
388 439 if ( $paged >= 1 ) {
389 440 $links[] = $paged;
@@ -768,11 +819,11 @@
768 819 'h3' => 'H3',
769 820 'h4' => 'H4',
770 821 'h5' => 'H5',
771 822 'h6' => 'H6',
772 - 'div' => 'div',
773 - 'span' => 'span',
774 - 'p' => 'p',
823 + 'div' => 'Div',
824 + 'span' => 'Span',
825 + 'p' => 'P',
775 826 ];
776 827
777 828 return $title_tags;
778 829 }
@@ -852,13 +903,24 @@
852 903
853 904 return wpautop( $output );
854 905 }
855 906
907 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- established function name relied on across the plugin family / feedback SDK; renaming would break integration.
856 908 function get_user_role( $id ) {
909 + $user = new WP_User( $id );
910 + $role = array_shift( $user->roles );
857 911
858 - $user = new WP_User( $id );
912 + if ( empty( $role ) ) {
913 + return '';
914 + }
859 915
860 - return array_shift( $user->roles );
916 + $wp_roles = wp_roles();
917 +
918 + if ( ! isset( $wp_roles->roles[ $role ]['name'] ) ) {
919 + return '';
920 + }
921 +
922 + return translate_user_role( $wp_roles->roles[ $role ]['name'] );
861 923 }
862 924
863 925
864 926 /**
@@ -871,9 +933,12 @@
871 933 */
872 934
873 935 if ( _is_upk_pro_activated() ) {
874 936 function ultimate_post_kit_reading_time( $content, $avg_reading_speed, $hide_seconds = 'no', $hide_minutes = 'no' ) {
875 - $total_word = str_word_count( strip_tags( $content ) );
937 + $avg_reading_speed = is_scalar( $avg_reading_speed ) ? (int) $avg_reading_speed : 0;
938 + $avg_reading_speed = $avg_reading_speed > 0 ? $avg_reading_speed : 200;
939 +
940 + $total_word = str_word_count( wp_strip_all_tags( $content ) );
876 941 $reading_minute = floor( $total_word / $avg_reading_speed );
877 942 $reading_seconds = floor( $total_word % $avg_reading_speed / ( $avg_reading_speed / 60 ) );
878 943
879 944 $hide_seconds = ( $hide_seconds === 'yes' );
@@ -923,8 +988,9 @@
923 988 /**
924 989 * License Validation
925 990 */
926 991 if ( ! function_exists( 'upk_license_validation' ) ) {
992 + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- established function name relied on across the plugin family / feedback SDK; renaming would break integration.
927 993 function upk_license_validation() {
928 994
929 995 if ( function_exists( '_is_upk_pro_activated' ) && false === _is_upk_pro_activated() ) {
930 996 return false;
@@ -939,90 +1005,107 @@
939 1005 return false;
940 1006 }
941 1007 }
942 1008
1009 +
943 1010 /**
944 - * Inject custom CSS and JS into the header
1011 + * Restrict a request-supplied post type to the ones this site already exposes to
1012 + * anonymous visitors.
1013 + *
1014 + * The load-more handlers are registered on wp_ajax_nopriv_* and rebuild their WP_Query
1015 + * from $_POST, so the post type they query is attacker-controlled. Post types that are
1016 + * public but flagged exclude_from_search (Elementor's elementor_library, for example)
1017 + * are deliberately hidden from anonymous visitors elsewhere, so they must not be
1018 + * reachable here either.
1019 + *
1020 + * @param string|array $post_type Requested post type(s).
1021 + * @param string $fallback Post type to fall back to when nothing is allowed.
1022 + * @return string|array Sanitized post type(s).
945 1023 */
946 -if ( ! function_exists( 'upk_inject_header_custom_code' ) ) {
947 - function upk_inject_header_custom_code() {
948 - if ( upk_is_page_excluded() ) {
949 - return;
950 - }
1024 +if ( ! function_exists( 'ultimate_post_kit_sanitize_public_post_type' ) ) {
1025 + function ultimate_post_kit_sanitize_public_post_type( $post_type, $fallback = 'post' ) {
951 1026
952 - $custom_css = get_option( 'upk_custom_css', '' );
953 - $custom_js = get_option( 'upk_custom_js', '' );
1027 + $is_allowed = static function ( $type ) {
1028 + $object = get_post_type_object( $type );
954 1029
955 - if ( ! empty( $custom_css ) ) {
956 - echo "\n<!-- Ultimate Post Kit Custom Header CSS -->\n";
957 - echo '<style type="text/css">' . "\n";
958 - echo $custom_css . "\n";
959 - echo '</style>' . "\n";
1030 + return $object && is_post_type_viewable( $type ) && empty( $object->exclude_from_search );
1031 + };
1032 +
1033 + if ( is_array( $post_type ) ) {
1034 + $requested = array_filter( $post_type, 'is_scalar' );
1035 + $requested = array_values( array_filter( array_map( 'strval', $requested ), $is_allowed ) );
1036 +
1037 + return empty( $requested ) ? $fallback : $requested;
960 1038 }
961 1039
962 - if ( ! empty( $custom_js ) ) {
963 - echo "\n<!-- Ultimate Post Kit Custom Header JS -->\n";
964 - echo '<script type="text/javascript">' . "\n";
965 - echo $custom_js . "\n";
966 - echo '</script>' . "\n";
1040 + if ( ! is_scalar( $post_type ) ) {
1041 + return $fallback;
967 1042 }
1043 +
1044 + $post_type = (string) $post_type;
1045 +
1046 + return $is_allowed( $post_type ) ? $post_type : $fallback;
968 1047 }
969 1048 }
970 1049
971 1050 /**
972 - * Inject custom CSS and JS into the footer
1051 + * Clamp a request-supplied excerpt word count.
1052 + *
1053 + * excerpt_length arrives from $_POST on the unauthenticated load-more handlers and is
1054 + * passed straight to wp_trim_words(), so an unbounded value returns effectively the
1055 + * whole post_content instead of a teaser.
1056 + *
1057 + * @param mixed $length Requested word count.
1058 + * @param int $default Value to use when the request supplies nothing usable.
1059 + * @return int Clamped word count.
973 1060 */
974 -if ( ! function_exists( 'upk_inject_footer_custom_code' ) ) {
975 - function upk_inject_footer_custom_code() {
976 - if ( upk_is_page_excluded() ) {
977 - return;
978 - }
1061 +if ( ! function_exists( 'ultimate_post_kit_clamp_excerpt_length' ) ) {
1062 + function ultimate_post_kit_clamp_excerpt_length( $length, $default = 20 ) {
979 1063
980 - $custom_css_2 = get_option( 'upk_custom_css_2', '' );
981 - $custom_js_2 = get_option( 'upk_custom_js_2', '' );
1064 + $length = is_scalar( $length ) ? (int) $length : 0;
982 1065
983 - if ( ! empty( $custom_css_2 ) ) {
984 - echo "\n<!-- Ultimate Post Kit Custom Footer CSS -->\n";
985 - echo '<style type="text/css">' . "\n";
986 - echo $custom_css_2 . "\n";
987 - echo '</style>' . "\n";
1066 + if ( $length < 1 ) {
1067 + $length = (int) $default;
988 1068 }
989 1069
990 - if ( ! empty( $custom_js_2 ) ) {
991 - echo "\n<!-- Ultimate Post Kit Custom Footer JS -->\n";
992 - echo '<script type="text/javascript">' . "\n";
993 - echo $custom_js_2 . "\n";
994 - echo '</script>' . "\n";
995 - }
1070 + return max( 1, min( 200, $length ) );
996 1071 }
997 1072 }
998 1073
999 1074 /**
1000 - * Check if current page should be excluded from custom code injection
1075 + * Make a request-supplied Elementor icon array safe to render.
1076 + *
1077 + * The load-more handlers run on wp_ajax_nopriv_* and rebuild their settings from $_POST.
1078 + * map_deep() preserves nested arrays, so an icon array reaches
1079 + * Elementor\Icons_Manager::render_icon() exactly as the caller shaped it. The 'svg'
1080 + * library branch resolves to Svg::get_inline_svg( $value['id'] ), which reads an
1081 + * attachment by id with no capability or post-status check, so an icon coming from a
1082 + * request must never be allowed to select it.
1083 + *
1084 + * @param mixed $icon Icon array as supplied by the request.
1085 + * @return array|false Safe icon array, or false when nothing renderable remains.
1001 1086 */
1002 -if ( ! function_exists( 'upk_is_page_excluded' ) ) {
1003 - function upk_is_page_excluded() {
1004 - $excluded_pages = get_option( 'upk_excluded_pages', array() );
1005 -
1006 - if ( empty( $excluded_pages ) || ! is_array( $excluded_pages ) ) {
1087 +if ( ! function_exists( 'ultimate_post_kit_sanitize_request_icon' ) ) {
1088 + function ultimate_post_kit_sanitize_request_icon( $icon ) {
1089 +
1090 + if ( ! is_array( $icon ) || empty( $icon['library'] ) || ! is_scalar( $icon['library'] ) ) {
1007 1091 return false;
1008 1092 }
1009 1093
1010 - $current_id = 0;
1011 -
1012 - if ( is_home() && ! is_front_page() ) {
1013 - $current_id = get_option( 'page_for_posts' );
1014 - } elseif ( is_front_page() ) {
1015 - $current_id = get_option( 'page_on_front' );
1016 - } elseif ( is_singular() ) {
1017 - $current_id = get_queried_object_id();
1018 - } elseif ( is_category() || is_tag() || is_tax() ) {
1094 + $library = (string) $icon['library'];
1095 +
1096 + // Uploaded-SVG icons are addressed by attachment id; never resolve one from a request.
1097 + if ( 'svg' === $library ) {
1019 1098 return false;
1020 - } elseif ( is_author() ) {
1099 + }
1100 +
1101 + // Font icons are rendered as a CSS class, so the value must stay a scalar.
1102 + if ( ! isset( $icon['value'] ) || ! is_scalar( $icon['value'] ) ) {
1021 1103 return false;
1022 - } elseif ( is_archive() ) {
1023 - return false;
1024 1104 }
1025 1105
1026 - return in_array( $current_id, $excluded_pages );
1106 + return [
1107 + 'library' => $library,
1108 + 'value' => (string) $icon['value'],
1109 + ];
1027 1110 }
1028 1111 }