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 +146 -78 4.1.18 → 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
@@ -188,8 +192,9 @@
188 192
189 193 return $output;
190 194 }
191 195
196 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- established function name relied on across the plugin family / feedback SDK; renaming would break integration.
192 197 function upk_get_category( $post_type ) {
193 198 switch ( $post_type ) {
194 199 case 'campaign':
195 200 $taxonomy = 'campaign_category';
@@ -350,8 +355,9 @@
350 355
351 356 /**
352 357 * HexColor
353 358 */
359 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- established function name relied on across the plugin family / feedback SDK; renaming would break integration.
354 360 function strToHex( $string, $steps = -10 ) {
355 361
356 362 if ( empty( $string ) ) {
357 363 return false;
@@ -374,8 +380,52 @@
374 380
375 381 return strToUpper( $output );
376 382 }
377 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 +
378 428 function ultimate_post_kit_post_pagination( $wp_query, $widget_id = '' ) {
379 429
380 430 /** Stop execution if there's only 1 page */
381 431 if ( $wp_query->max_num_pages <= 1 ) {
@@ -381,24 +431,10 @@
381 431 if ( $wp_query->max_num_pages <= 1 ) {
382 432 return;
383 433 }
384 434
385 - // Get current page from multiple sources for reliability
386 - $paged_from_query = isset( $wp_query->query_vars['paged'] ) ? $wp_query->query_vars['paged'] : 0;
387 - $page_from_query = isset( $wp_query->query_vars['page'] ) ? $wp_query->query_vars['page'] : 0;
388 -
389 - if ( is_front_page() ) {
390 - // On front page, WordPress can use either 'page' or 'paged' depending on permalink structure
391 - $paged = max( get_query_var( 'page' ), get_query_var( 'paged' ), $paged_from_query, $page_from_query );
392 - $paged = $paged ? $paged : 1;
393 - $page_var = 'page';
394 - } else {
395 - $paged = max( get_query_var( 'paged' ), $paged_from_query );
396 - $paged = $paged ? $paged : 1;
397 - $page_var = 'paged';
398 - }
399 -
400 - $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;
401 437
402 438 /** Add current page to the array */
403 439 if ( $paged >= 1 ) {
404 440 $links[] = $paged;
@@ -783,11 +819,11 @@
783 819 'h3' => 'H3',
784 820 'h4' => 'H4',
785 821 'h5' => 'H5',
786 822 'h6' => 'H6',
787 - 'div' => 'div',
788 - 'span' => 'span',
789 - 'p' => 'p',
823 + 'div' => 'Div',
824 + 'span' => 'Span',
825 + 'p' => 'P',
790 826 ];
791 827
792 828 return $title_tags;
793 829 }
@@ -867,13 +903,24 @@
867 903
868 904 return wpautop( $output );
869 905 }
870 906
907 +// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- established function name relied on across the plugin family / feedback SDK; renaming would break integration.
871 908 function get_user_role( $id ) {
909 + $user = new WP_User( $id );
910 + $role = array_shift( $user->roles );
872 911
873 - $user = new WP_User( $id );
912 + if ( empty( $role ) ) {
913 + return '';
914 + }
874 915
875 - 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'] );
876 923 }
877 924
878 925
879 926 /**
@@ -886,9 +933,12 @@
886 933 */
887 934
888 935 if ( _is_upk_pro_activated() ) {
889 936 function ultimate_post_kit_reading_time( $content, $avg_reading_speed, $hide_seconds = 'no', $hide_minutes = 'no' ) {
890 - $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 ) );
891 941 $reading_minute = floor( $total_word / $avg_reading_speed );
892 942 $reading_seconds = floor( $total_word % $avg_reading_speed / ( $avg_reading_speed / 60 ) );
893 943
894 944 $hide_seconds = ( $hide_seconds === 'yes' );
@@ -938,8 +988,9 @@
938 988 /**
939 989 * License Validation
940 990 */
941 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.
942 993 function upk_license_validation() {
943 994
944 995 if ( function_exists( '_is_upk_pro_activated' ) && false === _is_upk_pro_activated() ) {
945 996 return false;
@@ -954,90 +1005,107 @@
954 1005 return false;
955 1006 }
956 1007 }
957 1008
1009 +
958 1010 /**
959 - * 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).
960 1023 */
961 -if ( ! function_exists( 'upk_inject_header_custom_code' ) ) {
962 - function upk_inject_header_custom_code() {
963 - if ( upk_is_page_excluded() ) {
964 - return;
965 - }
1024 +if ( ! function_exists( 'ultimate_post_kit_sanitize_public_post_type' ) ) {
1025 + function ultimate_post_kit_sanitize_public_post_type( $post_type, $fallback = 'post' ) {
966 1026
967 - $custom_css = get_option( 'upk_custom_css', '' );
968 - $custom_js = get_option( 'upk_custom_js', '' );
1027 + $is_allowed = static function ( $type ) {
1028 + $object = get_post_type_object( $type );
969 1029
970 - if ( ! empty( $custom_css ) ) {
971 - echo "\n<!-- Ultimate Post Kit Custom Header CSS -->\n";
972 - echo '<style type="text/css">' . "\n";
973 - echo $custom_css . "\n";
974 - 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;
975 1038 }
976 1039
977 - if ( ! empty( $custom_js ) ) {
978 - echo "\n<!-- Ultimate Post Kit Custom Header JS -->\n";
979 - echo '<script type="text/javascript">' . "\n";
980 - echo $custom_js . "\n";
981 - echo '</script>' . "\n";
1040 + if ( ! is_scalar( $post_type ) ) {
1041 + return $fallback;
982 1042 }
1043 +
1044 + $post_type = (string) $post_type;
1045 +
1046 + return $is_allowed( $post_type ) ? $post_type : $fallback;
983 1047 }
984 1048 }
985 1049
986 1050 /**
987 - * 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.
988 1060 */
989 -if ( ! function_exists( 'upk_inject_footer_custom_code' ) ) {
990 - function upk_inject_footer_custom_code() {
991 - if ( upk_is_page_excluded() ) {
992 - return;
993 - }
1061 +if ( ! function_exists( 'ultimate_post_kit_clamp_excerpt_length' ) ) {
1062 + function ultimate_post_kit_clamp_excerpt_length( $length, $default = 20 ) {
994 1063
995 - $custom_css_2 = get_option( 'upk_custom_css_2', '' );
996 - $custom_js_2 = get_option( 'upk_custom_js_2', '' );
1064 + $length = is_scalar( $length ) ? (int) $length : 0;
997 1065
998 - if ( ! empty( $custom_css_2 ) ) {
999 - echo "\n<!-- Ultimate Post Kit Custom Footer CSS -->\n";
1000 - echo '<style type="text/css">' . "\n";
1001 - echo $custom_css_2 . "\n";
1002 - echo '</style>' . "\n";
1066 + if ( $length < 1 ) {
1067 + $length = (int) $default;
1003 1068 }
1004 1069
1005 - if ( ! empty( $custom_js_2 ) ) {
1006 - echo "\n<!-- Ultimate Post Kit Custom Footer JS -->\n";
1007 - echo '<script type="text/javascript">' . "\n";
1008 - echo $custom_js_2 . "\n";
1009 - echo '</script>' . "\n";
1010 - }
1070 + return max( 1, min( 200, $length ) );
1011 1071 }
1012 1072 }
1013 1073
1014 1074 /**
1015 - * 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.
1016 1086 */
1017 -if ( ! function_exists( 'upk_is_page_excluded' ) ) {
1018 - function upk_is_page_excluded() {
1019 - $excluded_pages = get_option( 'upk_excluded_pages', array() );
1020 -
1021 - 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'] ) ) {
1022 1091 return false;
1023 1092 }
1024 1093
1025 - $current_id = 0;
1026 -
1027 - if ( is_home() && ! is_front_page() ) {
1028 - $current_id = get_option( 'page_for_posts' );
1029 - } elseif ( is_front_page() ) {
1030 - $current_id = get_option( 'page_on_front' );
1031 - } elseif ( is_singular() ) {
1032 - $current_id = get_queried_object_id();
1033 - } 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 ) {
1034 1098 return false;
1035 - } 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'] ) ) {
1036 1103 return false;
1037 - } elseif ( is_archive() ) {
1038 - return false;
1039 1104 }
1040 1105
1041 - return in_array( $current_id, $excluded_pages );
1106 + return [
1107 + 'library' => $library,
1108 + 'value' => (string) $icon['value'],
1109 + ];
1042 1110 }
1043 1111 }