PluginProbe
Post Grid Gutenberg Blocks – PostX / 5.1.1
Post Grid Gutenberg Blocks – PostX v5.1.1
5.1.1 5.1.0 5.0.41 5.0.39 5.0.38 5.0.37 5.0.36 5.0.35 5.0.34 5.0.33 5.0.32 5.0.31 5.0.30 5.0.29 5.0.28 5.0.27 5.0.26 5.0.25 5.0.23 5.0.24 5.0.22 5.0.21 5.0.20 5.0.19 5.0.18 All 240 releases
← All changes | classes/Functions.php +211 -85 5.0.19 → 5.1.1 View file →
@@ -110,18 +110,18 @@
110 110 public function get_quick_query( $prams, $args ) {
111 111 switch ( $prams['queryQuick'] ) {
112 112 case 'related_posts':
113 113 global $post;
114 - $p_id = isset( $post->ID ) && $post->ID ? $post->ID : ( isset( $_POST['postId'] ) ? sanitize_text_field( $_POST['postId'] ) : '' ); //phpcs:disable WordPress.Security.NonceVerification.Missing
114 + $p_id = isset( $post->ID ) && $post->ID ? $post->ID : ( isset( $_POST['postId'] ) ? sanitize_text_field( wp_unslash( $_POST['postId'] ) ) : '' ); //phpcs:disable WordPress.Security.NonceVerification.Missing
115 115 if ( $p_id ) {
116 - $args['post__not_in'] = array( $p_id );
116 + $args['post__not_in'] = array( $p_id ); // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Needed by the block query; the user picks these filters.
117 117 }
118 118 break;
119 119 case 'related_tag':
120 120 global $post;
121 - $p_id = isset( $post->ID ) && $post->ID ? $post->ID : ( isset( $prams['current_post'] ) && $prams['current_post'] ? $prams['current_post'] : ( isset( $_POST['postId'] ) ? sanitize_text_field( $_POST['postId'] ) : '' ) ); //phpcs:disable ordPress.Security.NonceVerification.Missing
121 + $p_id = isset( $post->ID ) && $post->ID ? $post->ID : ( isset( $prams['current_post'] ) && $prams['current_post'] ? $prams['current_post'] : ( isset( $_POST['postId'] ) ? sanitize_text_field( wp_unslash( $_POST['postId'] ) ) : '' ) ); //phpcs:disable ordPress.Security.NonceVerification.Missing
122 122 if ( $p_id ) {
123 - $args['tax_query'] = array(
123 + $args['tax_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Needed by the block query; the user picks these filters.
124 124 array(
125 125 'taxonomy' => 'post_tag',
126 126 'terms' => $this->get_terms_id( $p_id, 'post_tag' ),
127 127 'field' => 'term_id',
@@ -126,16 +126,16 @@
126 126 'terms' => $this->get_terms_id( $p_id, 'post_tag' ),
127 127 'field' => 'term_id',
128 128 ),
129 129 );
130 - $args['post__not_in'] = array( $p_id );
130 + $args['post__not_in'] = array( $p_id ); // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Needed by the block query; the user picks these filters.
131 131 }
132 132 break;
133 133 case 'related_category':
134 134 global $post;
135 - $p_id = isset( $post->ID ) && $post->ID ? $post->ID : ( isset( $prams['current_post'] ) && $prams['current_post'] ? $prams['current_post'] : ( isset( $_POST['postId'] ) ? sanitize_text_field( $_POST['postId'] ) : '' ) ); //phpcs:disable ordPress.Security.NonceVerification.Missing
135 + $p_id = isset( $post->ID ) && $post->ID ? $post->ID : ( isset( $prams['current_post'] ) && $prams['current_post'] ? $prams['current_post'] : ( isset( $_POST['postId'] ) ? sanitize_text_field( wp_unslash( $_POST['postId'] ) ) : '' ) ); //phpcs:disable ordPress.Security.NonceVerification.Missing
136 136 if ( $p_id ) {
137 - $args['tax_query'] = array(
137 + $args['tax_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Needed by the block query; the user picks these filters.
138 138 array(
139 139 'taxonomy' => 'category',
140 140 'terms' => $this->get_terms_id( $p_id, 'category' ),
141 141 'field' => 'term_id',
@@ -140,16 +140,16 @@
140 140 'terms' => $this->get_terms_id( $p_id, 'category' ),
141 141 'field' => 'term_id',
142 142 ),
143 143 );
144 - $args['post__not_in'] = array( $p_id );
144 + $args['post__not_in'] = array( $p_id ); // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Needed by the block query; the user picks these filters.
145 145 }
146 146 break;
147 147 case 'related_cat_tag':
148 148 global $post;
149 - $p_id = isset( $post->ID ) && $post->ID ? $post->ID : ( isset( $prams['current_post'] ) && $prams['current_post'] ? $prams['current_post'] : ( isset( $_POST['postId'] ) ? sanitize_text_field( $_POST['postId'] ) : '' ) ); //phpcs:disable ordPress.Security.NonceVerification.Missing
149 + $p_id = isset( $post->ID ) && $post->ID ? $post->ID : ( isset( $prams['current_post'] ) && $prams['current_post'] ? $prams['current_post'] : ( isset( $_POST['postId'] ) ? sanitize_text_field( wp_unslash( $_POST['postId'] ) ) : '' ) ); //phpcs:disable ordPress.Security.NonceVerification.Missing
150 150 if ( $p_id ) {
151 - $args['tax_query'] = array(
151 + $args['tax_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Needed by the block query; the user picks these filters.
152 152 array(
153 153 'taxonomy' => 'post_tag',
154 154 'terms' => $this->get_terms_id( $p_id, 'post_tag' ),
155 155 'field' => 'term_id',
@@ -159,9 +159,9 @@
159 159 'terms' => $this->get_terms_id( $p_id, 'category' ),
160 160 'field' => 'term_id',
161 161 ),
162 162 );
163 - $args['post__not_in'] = array( $p_id );
163 + $args['post__not_in'] = array( $p_id ); // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Needed by the block query; the user picks these filters.
164 164 }
165 165 break;
166 166 case 'sticky_posts':
167 167 $sticky = get_option( 'sticky_posts' );
@@ -231,27 +231,27 @@
231 231 $args['order'] = 'DESC';
232 232 $args['date_query'] = array( array( 'after' => '1 month ago' ) );
233 233 break;
234 234 case 'popular_post_1_day_view':
235 - $args['meta_key'] = '__post_views_count';
235 + $args['meta_key'] = '__post_views_count'; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Needed by the block query; the user picks these filters.
236 236 $args['orderby'] = 'meta_value_num';
237 237 $args['order'] = 'DESC';
238 238 $args['date_query'] = array( array( 'after' => '1 day ago' ) );
239 239 break;
240 240 case 'popular_post_7_days_view':
241 - $args['meta_key'] = '__post_views_count';
241 + $args['meta_key'] = '__post_views_count'; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Needed by the block query; the user picks these filters.
242 242 $args['orderby'] = 'meta_value_num';
243 243 $args['order'] = 'DESC';
244 244 $args['date_query'] = array( array( 'after' => '1 week ago' ) );
245 245 break;
246 246 case 'popular_post_30_days_view':
247 - $args['meta_key'] = '__post_views_count';
247 + $args['meta_key'] = '__post_views_count'; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Needed by the block query; the user picks these filters.
248 248 $args['orderby'] = 'meta_value_num';
249 249 $args['order'] = 'DESC';
250 250 $args['date_query'] = array( array( 'after' => '1 month ago' ) );
251 251 break;
252 252 case 'popular_post_all_times_view':
253 - $args['meta_key'] = '__post_views_count';
253 + $args['meta_key'] = '__post_views_count'; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Needed by the block query; the user picks these filters.
254 254 $args['orderby'] = 'meta_value_num';
255 255 $args['order'] = 'DESC';
256 256 break;
257 257 default:
@@ -341,8 +341,11 @@
341 341 * @param BOOLEAN $call_common .
342 342 * @return STRING|ARRAY.
343 343 */
344 344 public function build_css_for_inline_print( $post_id, $call_common = true ) {
345 + // Callers pass a template ID from builder settings or shortcode attributes; it goes into
346 + // a file path and the id attribute below, so only a positive integer is accepted.
347 + $post_id = absint( $post_id );
345 348 if ( $post_id ) {
346 349 $upload_dir_url = wp_get_upload_dir();
347 350 $upload_css_dir_url = trailingslashit( $upload_dir_url['basedir'] );
348 351
@@ -398,8 +401,28 @@
398 401 }
399 402
400 403
401 404 /**
405 + * Settings that are safe to send to the browser.
406 + *
407 + * Same as get_setting() without secrets: the OpenAI key is replaced by
408 + * `chatgpt_has_key`. Use this for anything localized to a script. get_setting()
409 + * itself is unchanged, because callers write its result back to the option.
410 + *
411 + * @return array
412 + */
413 + public function get_client_settings() {
414 + $data = $this->get_setting();
415 + if ( ! is_array( $data ) ) {
416 + return array();
417 + }
418 + $data['chatgpt_has_key'] = ! empty( $data['chatgpt_secret_key'] );
419 + unset( $data['chatgpt_secret_key'] );
420 + return $data;
421 + }
422 +
423 +
424 + /**
402 425 * Set Option Settings
403 426 *
404 427 * @since v.1.0.0
405 428 * @param STRING | $key of the Option (STRING) .
@@ -435,9 +458,9 @@
435 458 $hasDarkImage = ( $darkImg['enable'] && $darkImg['url'] ) ? ' ultp-light-image-block ' : '';
436 459 $alt = $alt ? ' alt="' . $alt . '" ' : '';
437 460 $lazy_data = $lazy ? ' loading="lazy"' : '';
438 461
439 - $dlMode = isset( $_COOKIE['ultplocalDLMode'] ) ? $_COOKIE['ultplocalDLMode'] : ultimate_post()->get_dl_mode();
462 + $dlMode = isset( $_COOKIE['ultplocalDLMode'] ) ? sanitize_key( wp_unslash( $_COOKIE['ultplocalDLMode'] ) ) : ultimate_post()->get_dl_mode();
440 463
441 464 $lightMode = $hasDarkImage ? ( $dlMode == 'ultplight' ? '' : 'inactive ' ) : '';
442 465 $darkMode = $hasDarkImage ? ( $hasDarkImage && $dlMode == 'ultpdark' ? '' : ' inactive ' ) : '';
443 466
@@ -482,9 +505,9 @@
482 505 * @return STRING
483 506 */
484 507 public function excerpt( $post_id, $limit = 55 ) {
485 508 $content = preg_replace( '/(\[postx_template[\s\w="]*)]/m', '', get_the_excerpt( $post_id ) ); // Remove postx_template shortcode form Content
486 - return apply_filters( 'the_excerpt', wp_trim_words( $content, $limit ) );
509 + return apply_filters( 'the_excerpt', wp_trim_words( $content, $limit ) ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Core hook.
487 510 }
488 511
489 512 /**
490 513 * Builder Attributes
@@ -690,9 +713,9 @@
690 713 $str = explode( '###', $builder );
691 714 if ( isset( $str[0] ) ) {
692 715 if ( $str[0] == 'taxonomy' ) {
693 716 if ( isset( $str[1] ) && isset( $str[2] ) ) {
694 - $archive_query['tax_query'] = array(
717 + $archive_query['tax_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Needed by the block query; the user picks these filters.
695 718 array(
696 719 'taxonomy' => $str[1],
697 720 'field' => 'slug',
698 721 'terms' => $str[2],
@@ -732,10 +755,10 @@
732 755 }
733 756 $archive_query['posts_per_page'] = isset( $attr['queryNumber'] ) ? $attr['queryNumber'] : 1;
734 757 $archive_query['paged'] = isset( $attr['paged'] ) ? $attr['paged'] : 1;
735 758
736 - $archive_query['paged'] = ! wp_doing_ajax() && isset( $_GET[ $attr['blockId'] . '_page' ] ) && is_numeric( $_GET[ $attr['blockId'] . '_page' ] ) ?
737 - intval( $_GET[ $attr['blockId'] . '_page' ] ) :
759 + $archive_query['paged'] = ! wp_doing_ajax() && isset( $_GET[ $attr['blockId'] . '_page' ] ) && is_numeric( $_GET[ $attr['blockId'] . '_page' ] ) ? // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only page number for block pagination.
760 + intval( $_GET[ $attr['blockId'] . '_page' ] ) : // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only page number for block pagination.
738 761 $archive_query['paged'];
739 762
740 763 if ( isset( $attr['queryOffset'] ) && $attr['queryOffset'] ) {
741 764 $offset = $this->get_offset( $attr['queryOffset'], $archive_query );
@@ -754,9 +777,9 @@
754 777
755 778 if ( isset( $attr['queryExclude'] ) && $attr['queryExclude'] ) {
756 779 $_exclude = ( substr( $attr['queryExclude'], 0, 1 ) === '[' ) ? $this->get_value( json_decode( $attr['queryExclude'] ) ) : explode( ',', $attr['queryExclude'] );
757 780 if ( is_array( $_exclude ) && count( $_exclude ) ) {
758 - $archive_query['post__not_in'] = isset( $archive_query['post__not_in'] ) ? array_merge( $archive_query['post__not_in'], $_exclude ) : $_exclude;
781 + $archive_query['post__not_in'] = isset( $archive_query['post__not_in'] ) ? array_merge( $archive_query['post__not_in'], $_exclude ) : $_exclude; // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Needed by the block query; the user picks these filters.
759 782 }
760 783 }
761 784
762 785 if ( isset( $attr['querySticky'] ) && $attr['querySticky'] ) {
@@ -761,16 +784,16 @@
761 784
762 785 if ( isset( $attr['querySticky'] ) && $attr['querySticky'] ) {
763 786 if ( filter_var( $attr['querySticky'], FILTER_VALIDATE_BOOLEAN ) ) {
764 787 $sticky = get_option( 'sticky_posts', array() );
765 - $archive_query['post__not_in'] = isset( $archive_query['post__not_in'] ) ? array_merge( $archive_query['post__not_in'], $sticky ) : $sticky;
788 + $archive_query['post__not_in'] = isset( $archive_query['post__not_in'] ) ? array_merge( $archive_query['post__not_in'], $sticky ) : $sticky; // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Needed by the block query; the user picks these filters.
766 789 }
767 790 }
768 791 // ===============
769 792 if ( isset( $attr['queryUnique'] ) && $attr['queryUnique'] ) {
770 - global $unique_ID;
771 - if ( isset( $unique_ID[ $attr['queryUnique'] ] ) ) {
772 - $archive_query['post__not_in'] = isset( $archive_query['post__not_in'] ) ? array_merge( $archive_query['post__not_in'], $unique_ID[ $attr['queryUnique'] ] ) : $unique_ID[ $attr['queryUnique'] ];
793 + global $ultp_unique_ID;
794 + if ( isset( $ultp_unique_ID[ $attr['queryUnique'] ] ) ) {
795 + $archive_query['post__not_in'] = isset( $archive_query['post__not_in'] ) ? array_merge( $archive_query['post__not_in'], $ultp_unique_ID[ $attr['queryUnique'] ] ) : $ultp_unique_ID[ $attr['queryUnique'] ]; // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Needed by the block query; the user picks these filters.
773 796 }
774 797 }
775 798 // ===============
776 799
@@ -808,9 +831,9 @@
808 831 'field' => 'slug',
809 832 'terms' => $val,
810 833 );
811 834 }
812 - $archive_query['tax_query'] = $final;
835 + $archive_query['tax_query'] = $final; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Needed by the block query; the user picks these filters.
813 836 }
814 837 }
815 838 }
816 839 }
@@ -816,10 +839,10 @@
816 839 }
817 840 // from v.3.0.7 - for search builder
818 841 if ( is_search() ) {
819 842 $archive_query['orderby'] = 'relevance';
820 - if ( isset( $_GET['ultp_exclude'] ) ) { // Added support for search block exclude
821 - $ultp_exclude = json_decode( stripslashes( $_GET['ultp_exclude'] ), true );
843 + if ( isset( $_GET['ultp_exclude'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public search filter (search block exclude).
844 + $ultp_exclude = json_decode( sanitize_text_field( wp_unslash( $_GET['ultp_exclude'] ) ), true ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only public search filter.
822 845 if ( is_array( $ultp_exclude ) ) {
823 846 $all_types = get_post_types( array( 'public' => true ), 'names' );
824 847 $post_type = array_diff( $all_types, $ultp_exclude );
825 848 $archive_query['post_type'] = $post_type;
@@ -848,11 +871,11 @@
848 871
849 872 $paged = isset( $attr['paged'] ) ? $attr['paged'] : 1;
850 873 $paged = ! wp_doing_ajax() &&
851 874 isset( $attr['blockId'] ) &&
852 - isset( $_GET[ $attr['blockId'] . '_page' ] ) &&
853 - is_numeric( $_GET[ $attr['blockId'] . '_page' ] ) ?
854 - intval( $_GET[ $attr['blockId'] . '_page' ] ) :
875 + isset( $_GET[ $attr['blockId'] . '_page' ] ) && // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only page number for block pagination.
876 + is_numeric( $_GET[ $attr['blockId'] . '_page' ] ) ? // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only page number for block pagination.
877 + intval( $_GET[ $attr['blockId'] . '_page' ] ) : // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only page number for block pagination.
855 878 $paged;
856 879
857 880 $query_args = array(
858 881 'posts_per_page' => $query_number,
@@ -905,9 +928,9 @@
905 928 }
906 929
907 930 if ( isset( $attr['queryOrderBy'] ) && isset( $attr['metaKey'] ) ) {
908 931 if ( $attr['queryOrderBy'] == 'meta_value_num' ) {
909 - $query_args['meta_key'] = $attr['metaKey'];
932 + $query_args['meta_key'] = $attr['metaKey']; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Needed by the block query; the user picks these filters.
910 933 }
911 934 }
912 935
913 936 // Include Remove from Version 2.5.4.
@@ -923,9 +946,9 @@
923 946 if ( isset( $attr['queryTax'] ) ) {
924 947 if ( isset( $attr['queryTaxValue'] ) ) {
925 948 $var = $this->getFilterQueryArgs( $attr );
926 949 if ( isset( $var ) && count( $var ) > 1 ) {
927 - $query_args['tax_query'] = $var;
950 + $query_args['tax_query'] = $var; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Needed by the block query; the user picks these filters.
928 951 }
929 952 }
930 953 }
931 954
@@ -953,15 +976,15 @@
953 976 );
954 977 }
955 978
956 979 if ( is_array( $query_args ) && array_key_exists( 'tax_query', $query_args ) ) {
957 - $query_args['tax_query'] = array(
980 + $query_args['tax_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Needed by the block query; the user picks these filters.
958 981 'relation' => 'AND',
959 982 $query_args['tax_query'],
960 983 );
961 984 $query_args['tax_query'][] = $final;
962 985 } else {
963 - $query_args['tax_query'] = $final;
986 + $query_args['tax_query'] = $final; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Needed by the block query; the user picks these filters.
964 987 }
965 988 }
966 989 }
967 990
@@ -967,21 +990,21 @@
967 990
968 991 if ( isset( $attr['queryExclude'] ) && $attr['queryExclude'] ) {
969 992 $_exclude = ( substr( $attr['queryExclude'], 0, 1 ) === '[' ) ? $this->get_value( json_decode( $attr['queryExclude'] ) ) : explode( ',', $attr['queryExclude'] );
970 993 if ( is_array( $_exclude ) && count( $_exclude ) ) {
971 - $query_args['post__not_in'] = isset( $query_args['post__not_in'] ) ? array_merge( $query_args['post__not_in'], $_exclude ) : $_exclude;
994 + $query_args['post__not_in'] = isset( $query_args['post__not_in'] ) ? array_merge( $query_args['post__not_in'], $_exclude ) : $_exclude; // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Needed by the block query; the user picks these filters.
972 995 }
973 996 }
974 997
975 998 if ( isset( $attr['queryUnique'] ) && $attr['queryUnique'] ) {
976 - global $unique_ID;
977 - if ( isset( $unique_ID[ $attr['queryUnique'] ] ) ) {
978 - $query_args['post__not_in'] = isset( $query_args['post__not_in'] ) ? array_merge( $query_args['post__not_in'], $unique_ID[ $attr['queryUnique'] ] ) : $unique_ID[ $attr['queryUnique'] ];
999 + global $ultp_unique_ID;
1000 + if ( isset( $ultp_unique_ID[ $attr['queryUnique'] ] ) ) {
1001 + $query_args['post__not_in'] = isset( $query_args['post__not_in'] ) ? array_merge( $query_args['post__not_in'], $ultp_unique_ID[ $attr['queryUnique'] ] ) : $ultp_unique_ID[ $attr['queryUnique'] ]; // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Needed by the block query; the user picks these filters.
979 1002 }
980 1003 }
981 1004 // exclude current post from Post blocks // v.2.9.6.
982 1005 if ( is_single() && get_the_ID() ) {
983 - $query_args['post__not_in'] = isset( $query_args['post__not_in'] ) ? array_merge( $query_args['post__not_in'], array( get_the_ID() ) ) : array( get_the_ID() );
1006 + $query_args['post__not_in'] = isset( $query_args['post__not_in'] ) ? array_merge( $query_args['post__not_in'], array( get_the_ID() ) ) : array( get_the_ID() ); // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Needed by the block query; the user picks these filters.
984 1007 }
985 1008
986 1009 if ( isset( $attr['queryQuick'] ) ) {
987 1010 if ( $attr['queryQuick'] != '' && $post_type != 'archiveBuilder' ) {
@@ -1007,9 +1030,9 @@
1007 1030
1008 1031 if ( isset( $attr['querySticky'] ) && $attr['querySticky'] ) {
1009 1032 if ( filter_var( $attr['querySticky'], FILTER_VALIDATE_BOOLEAN ) ) {
1010 1033 $sticky = get_option( 'sticky_posts', array() );
1011 - $query_args['post__not_in'] = isset( $query_args['post__not_in'] ) ? array_merge( $query_args['post__not_in'], $sticky ) : $sticky;
1034 + $query_args['post__not_in'] = isset( $query_args['post__not_in'] ) ? array_merge( $query_args['post__not_in'], $sticky ) : $sticky; // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Needed by the block query; the user picks these filters.
1012 1035 }
1013 1036 }
1014 1037
1015 1038 if ( ! isset( $attr['ajaxCall'] ) ) {
@@ -1016,9 +1039,9 @@
1016 1039 if ( isset( $attr['filterShow'] ) && $attr['filterShow'] && $attr['filterShow'] != 'false' ) {
1017 1040 if ( isset( $attr['checkFilter'] ) && isset( $attr['queryTax'] ) && isset( $attr['queryTaxValue'] ) ) {
1018 1041 $var = $this->getFilterQueryArgs( $attr );
1019 1042 if ( isset( $var ) && count( $var ) > 1 ) {
1020 - $query_args['tax_query'] = $var;
1043 + $query_args['tax_query'] = $var; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Needed by the block query; the user picks these filters.
1021 1044 }
1022 1045 } elseif ( isset( $attr['filterType'] ) && isset( $attr['filterValue'] ) ) {
1023 1046 $filterValue = strlen( $attr['filterValue'] ) > 2 ? $attr['filterValue'] : array();
1024 1047 $filterValue = is_array( $filterValue ) ? $filterValue : json_decode( $filterValue );
@@ -1031,9 +1054,9 @@
1031 1054 'terms' => $val,
1032 1055 // 'operator' => '=',
1033 1056 );
1034 1057 }
1035 - $query_args['tax_query'] = $final;
1058 + $query_args['tax_query'] = $final; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Needed by the block query; the user picks these filters.
1036 1059 }
1037 1060 }
1038 1061 }
1039 1062 }
@@ -1111,13 +1134,13 @@
1111 1134 }
1112 1135 }
1113 1136
1114 1137 if ( $is_valid ) {
1115 - $query_args['tax_query'] = $tax_query;
1138 + $query_args['tax_query'] = $tax_query; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- Needed by the block query; the user picks these filters.
1116 1139 }
1117 1140 }
1118 1141 // $query_args['test'] = is_array( $post_type ) && ! empty( $post_type ) ? $post_type : ( 'archiveBuilder' === $post_type ? 'post' : ( is_string( $post_type ) ? json_decode( $post_type ) : $post_type ) );
1119 - $query_args['wpnonce'] = wp_create_nonce( 'ultp-nonce' );
1142 + $query_args['wpnonce'] = wp_create_nonce( 'ultp-public-nonce' );
1120 1143
1121 1144 return apply_filters( 'ultp_frontend_query', $query_args );
1122 1145 }
1123 1146
@@ -1137,9 +1160,9 @@
1137 1160 $offset = array();
1138 1161 for ( $x = count( $offset_post ); $x > count( $offset_post ) - $queryOffset; $x-- ) {
1139 1162 $offset[] = $offset_post[ $x - 1 ]->ID;
1140 1163 }
1141 - $query['post__not_in'] = $offset;
1164 + $query['post__not_in'] = $offset; // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Needed by the block query; the user picks these filters.
1142 1165 }
1143 1166 } else {
1144 1167 $query['offset'] = isset( $queryOffset ) ? $queryOffset : 0;
1145 1168 }
@@ -1259,11 +1282,11 @@
1259 1282 $paged = is_front_page() ? get_query_var( 'page' ) : get_query_var( 'paged' );
1260 1283 $paged = $paged ? $paged : 1;
1261 1284
1262 1285 $paged = ! wp_doing_ajax() &&
1263 - isset( $_GET[ $blockId . '_page' ] ) &&
1264 - is_numeric( $_GET[ $blockId . '_page' ] ) ?
1265 - intval( $_GET[ $blockId . '_page' ] ) : $paged;
1286 + isset( $_GET[ $blockId . '_page' ] ) && // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only page number for block pagination.
1287 + is_numeric( $_GET[ $blockId . '_page' ] ) ? // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only page number for block pagination.
1288 + intval( $_GET[ $blockId . '_page' ] ) : $paged; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only page number for block pagination.
1266 1289
1267 1290 if ( $pages == '' ) {
1268 1291 global $wp_query;
1269 1292 $pages = $wp_query->max_num_pages;
@@ -1388,9 +1411,9 @@
1388 1411 * @return ARRAY
1389 1412 */
1390 1413 public function get_tax_data( $terms ) {
1391 1414 $temp = array();
1392 - $thumbnail_id = get_term_meta( $terms->term_id, 'ultp_category_image', true );
1415 + $thumbnail_id = $this->get_term_image_id( $terms );
1393 1416 $image_src = array();
1394 1417 if ( $thumbnail_id ) {
1395 1418 $image_sizes = ultimate_post()->get_image_size();
1396 1419 foreach ( $image_sizes as $key => $value ) {
@@ -1400,18 +1423,68 @@
1400 1423 }
1401 1424 }
1402 1425 }
1403 1426 $temp['url'] = get_term_link( $terms );
1404 - $temp['thumbnail_id'] = $thumbnail_id;
1427 + $temp['thumbnail_id'] = $thumbnail_id ? (string) $thumbnail_id : '';
1405 1428 $temp['name'] = $terms->name;
1406 1429 $temp['desc'] = $terms->description;
1407 1430 $temp['count'] = $terms->count;
1408 1431 $temp['image'] = $image_src;
1409 - $color = get_term_meta( $terms->term_id, 'ultp_category_color', true );
1432 + $color = $this->get_term_color( $terms );
1410 1433 $temp['color'] = $color ? $color : '#037fff';
1411 1434 return $temp;
1412 1435 }
1413 1436
1437 + /**
1438 + * Image attachment ID of a term, set by PostX Pro's Taxonomy Image & Color add-on.
1439 + *
1440 + * PostX Pro answers `ultp_term_image_id` only while the add-on is on.
1441 + *
1442 + * @param WP_Term $term Term.
1443 + * @return INT Attachment ID, or 0.
1444 + */
1445 + public function get_term_image_id( $term ) {
1446 + // PostX Pro before 1.9.0 does not hook the filter below.
1447 + if ( Xpo::is_pro_before_1_9_0() ) {
1448 + return absint( $this->get_term_meta_from_old_pro( $term, 'image' ) );
1449 + }
1450 + return absint( apply_filters( 'ultp_term_image_id', 0, $term ) );
1451 + }
1452 +
1453 + /**
1454 + * Colour of a term, set by PostX Pro's Taxonomy Image & Color add-on.
1455 + *
1456 + * PostX Pro answers `ultp_term_color` only while the add-on is on.
1457 + *
1458 + * @param WP_Term $term Term.
1459 + * @return STRING Colour, or ''.
1460 + */
1461 + public function get_term_color( $term ) {
1462 + // PostX Pro before 1.9.0 does not hook the filter below.
1463 + if ( Xpo::is_pro_before_1_9_0() ) {
1464 + return (string) $this->get_term_meta_from_old_pro( $term, 'color' );
1465 + }
1466 + $color = apply_filters( 'ultp_term_color', '', $term );
1467 + return is_string( $color ) ? $color : '';
1468 + }
1469 +
1470 + /**
1471 + * Get a term's image or colour as PostX Pro before 1.9.0 expects
1472 + *
1473 + * Newer versions provide them through the `ultp_term_image_id` and `ultp_term_color` filters.
1474 + *
1475 + * @param WP_Term $term Term.
1476 + * @param STRING $type 'image' or 'color'.
1477 + * @return STRING
1478 + */
1479 + private function get_term_meta_from_old_pro( $term, $type ) {
1480 + if ( ! $term instanceof \WP_Term ) {
1481 + return '';
1482 + }
1483 + $value = get_term_meta( $term->term_id, 'image' === $type ? 'ultp_category_image' : 'ultp_category_color', true );
1484 + return is_scalar( $value ) ? (string) $value : '';
1485 + }
1486 +
1414 1487 public function get_category_data( $catSlug, $number = 40, $type = '', $tax_slug = 'category', $archiveBuilder = '' ) {
1415 1488 $data = array();
1416 1489 if ( $type == 'child' ) {
1417 1490 if ( $archiveBuilder ) {
@@ -1597,11 +1670,12 @@
1597 1670 * @return STRING
1598 1671 */
1599 1672 public function filter_html( $filterText = '', $filterType = '', $filterValue = '[]', $filterMobileText = '...', $filterMobile = true ) {
1600 1673 $html = '';
1601 - $html .= '<ul ' . ( $filterMobile ? 'class="ultp-flex-menu"' : '' ) . ' data-name="' . ( $filterMobileText ? $filterMobileText : '&nbsp;' ) . '">';
1674 + $html .= '<ul ' . ( $filterMobile ? 'class="ultp-flex-menu"' : '' ) . ' data-name="' . ( $filterMobileText ? esc_attr( $filterMobileText ) : '&nbsp;' ) . '">';
1602 1675 $cat = $this->taxonomy( $filterType );
1603 1676 if ( $filterText ) {
1677 + // Already passed through wp_kses() by the caller, which keeps the allowed HTML.
1604 1678 $html .= '<li class="filter-item"><a class="filter-active" data-taxonomy="" href="#">' . $filterText . '</a></li>';
1605 1679 }
1606 1680 if ( $filterValue ) {
1607 1681 $filterValue = strlen( $filterValue ) > 2 ? $filterValue : array();
@@ -1608,9 +1682,12 @@
1608 1682 $filterValue = is_array( $filterValue ) ? $filterValue : json_decode( $filterValue );
1609 1683 if ( is_array( $filterValue ) && count( $filterValue ) ) {
1610 1684 foreach ( $filterValue as $val ) {
1611 1685 $val = isset( $val->value ) ? $val->value : $val;
1612 - $html .= '<li class="filter-item"><a data-taxonomy="' . $val . '" href="#">' . ( isset( $cat[ $val ] ) ? $cat[ $val ] : $val ) . '</a></li>';
1686 + if ( ! is_scalar( $val ) ) {
1687 + continue;
1688 + }
1689 + $html .= '<li class="filter-item"><a data-taxonomy="' . esc_attr( $val ) . '" href="#">' . esc_html( isset( $cat[ $val ] ) ? $cat[ $val ] : $val ) . '</a></li>';
1613 1690 }
1614 1691 }
1615 1692 }
1616 1693 $html .= '</ul>';
@@ -1629,26 +1706,56 @@
1629 1706 }
1630 1707
1631 1708
1632 1709 /**
1633 - * Check if a pro feature is visible based on license expiration date.
1710 + * Get the request body for the template service
1634 1711 *
1635 - * @since v.2.4.2
1636 - * @param STRING $date Date to compare with license expiration.
1637 - * @return BOOL True if the pro feature is visible, false otherwise.
1712 + * PostX Pro adds its license key through the `ultp_template_request_body` filter.
1713 + *
1714 + * @param ARRAY $body Request body.
1715 + * @param STRING $context Which request it is: builder_premade, starter_posts, starter_content or single_page.
1716 + * @return ARRAY
1638 1717 */
1639 - public function is_pro_feature_visible( $date ) {
1640 - if ( function_exists( 'ultimate_post_pro' ) ) {
1641 - $license_data = get_option( 'edd_ultp_license_data' );
1642 - if ( isset( $license_data['expires'] ) ) {
1643 - $expires = $license_data['expires'];
1644 - if ( 'lifetime' === $expires ) {
1645 - return true;
1646 - }
1647 - return strtotime( $date ) < strtotime( $expires );
1718 + public function get_template_request_body( $body, $context ) {
1719 + $filtered = apply_filters( 'ultp_template_request_body', $body, $context );
1720 + $body = is_array( $filtered ) ? $filtered : $body;
1721 +
1722 + // PostX Pro before 1.9.0 does not hook the filter above, so its license key is added here.
1723 + if ( ! isset( $body['license'] ) && Xpo::is_pro_before_1_9_0() ) {
1724 + $body['license'] = Xpo::get_lc_key();
1725 + }
1726 +
1727 + return $body;
1728 + }
1729 +
1730 +
1731 + /**
1732 + * Validate if the template service URL is from an allowed domain (SSRF protection).
1733 + *
1734 + * @param STRING $url The URL to validate.
1735 + * @return BOOL True if allowed, false otherwise.
1736 + */
1737 + public function is_allowed_template_endpoint( $url ) {
1738 + $allowed_domains = array(
1739 + 'postxkit.wpxpo.com',
1740 + );
1741 +
1742 + $parsed_url = wp_parse_url( $url );
1743 + if ( ! $parsed_url || empty( $parsed_url['host'] ) ) {
1744 + return false;
1745 + }
1746 +
1747 + $host = strtolower( $parsed_url['host'] );
1748 +
1749 + foreach ( $allowed_domains as $domain ) {
1750 + $domain = strtolower( $domain );
1751 +
1752 + // Exact match OR valid subdomain
1753 + if ( $host === $domain || substr( $host, - ( strlen( $domain ) + 1 ) ) === '.' . $domain ) {
1754 + return true;
1648 1755 }
1649 - return true;
1650 1756 }
1757 +
1651 1758 return false;
1652 1759 }
1653 1760
1654 1761 /**
@@ -1663,21 +1770,15 @@
1663 1770 */
1664 1771 public function get_excerpt( $post_id = 0, $show_seo_meta = 0, $show_full_excerpt = 0, $excerpt_limit = 55 ) {
1665 1772 $html = '';
1666 1773 if ( $show_seo_meta ) {
1667 - $str = '';
1668 - if ( function_exists( 'ultimate_post_pro' ) ) {
1669 - if ( ultimate_post()->get_setting( 'ultp_yoast' ) == 'true' ) {
1670 - $str = method_exists( ultimate_post_pro(), 'get_yoast_meta' ) ? ultimate_post_pro()->get_yoast_meta( $post_id ) : '';
1671 - } elseif ( ultimate_post()->get_setting( 'ultp_rankmath' ) == 'true' ) {
1672 - $str = method_exists( ultimate_post_pro(), 'get_rankmath_meta' ) ? ultimate_post_pro()->get_rankmath_meta( $post_id ) : '';
1673 - } elseif ( ultimate_post()->get_setting( 'ultp_aioseo' ) == 'true' ) {
1674 - $str = method_exists( ultimate_post_pro(), 'get_aioseo_meta' ) ? ultimate_post_pro()->get_aioseo_meta( $post_id ) : '';
1675 - } elseif ( ultimate_post()->get_setting( 'ultp_seopress' ) == 'true' ) {
1676 - $str = method_exists( ultimate_post_pro(), 'get_seopress_meta' ) ? ultimate_post_pro()->get_seopress_meta( $post_id ) : '';
1677 - } elseif ( ultimate_post()->get_setting( 'ultp_squirrly' ) == 'true' ) {
1678 - $str = method_exists( ultimate_post_pro(), 'get_squirrly_meta' ) ? ultimate_post_pro()->get_squirrly_meta( $post_id ) : '';
1679 - }
1774 + // PostX Pro answers this filter with the description from the active SEO plugin.
1775 + $str = apply_filters( 'ultp_seo_meta_excerpt', '', $post_id );
1776 + $str = is_string( $str ) ? $str : '';
1777 +
1778 + // PostX Pro before 1.9.0 does not hook the filter above.
1779 + if ( ! $str && Xpo::is_pro_before_1_9_0() ) {
1780 + $str = $this->get_seo_meta_from_old_pro( $post_id );
1680 1781 }
1681 1782 $html = $str ? $str : ultimate_post()->excerpt( $post_id, $excerpt_limit );
1682 1783 } elseif ( $show_full_excerpt == 0 ) {
1683 1784 $html = ultimate_post()->excerpt( $post_id, $excerpt_limit );
@@ -1687,8 +1788,34 @@
1687 1788 return $html;
1688 1789 }
1689 1790
1690 1791 /**
1792 + * Get the SEO meta description from PostX Pro before 1.9.0
1793 + *
1794 + * Newer versions provide it through the `ultp_seo_meta_excerpt` filter.
1795 + *
1796 + * @param INT $post_id Post ID.
1797 + * @return STRING
1798 + */
1799 + private function get_seo_meta_from_old_pro( $post_id ) {
1800 + $str = '';
1801 + if ( function_exists( 'ultimate_post_pro' ) ) {
1802 + if ( ultimate_post()->get_setting( 'ultp_yoast' ) == 'true' ) {
1803 + $str = method_exists( ultimate_post_pro(), 'get_yoast_meta' ) ? ultimate_post_pro()->get_yoast_meta( $post_id ) : '';
1804 + } elseif ( ultimate_post()->get_setting( 'ultp_rankmath' ) == 'true' ) {
1805 + $str = method_exists( ultimate_post_pro(), 'get_rankmath_meta' ) ? ultimate_post_pro()->get_rankmath_meta( $post_id ) : '';
1806 + } elseif ( ultimate_post()->get_setting( 'ultp_aioseo' ) == 'true' ) {
1807 + $str = method_exists( ultimate_post_pro(), 'get_aioseo_meta' ) ? ultimate_post_pro()->get_aioseo_meta( $post_id ) : '';
1808 + } elseif ( ultimate_post()->get_setting( 'ultp_seopress' ) == 'true' ) {
1809 + $str = method_exists( ultimate_post_pro(), 'get_seopress_meta' ) ? ultimate_post_pro()->get_seopress_meta( $post_id ) : '';
1810 + } elseif ( ultimate_post()->get_setting( 'ultp_squirrly' ) == 'true' ) {
1811 + $str = method_exists( ultimate_post_pro(), 'get_squirrly_meta' ) ? ultimate_post_pro()->get_squirrly_meta( $post_id ) : '';
1812 + }
1813 + }
1814 + return $str;
1815 + }
1816 +
1817 + /**
1691 1818 * Get embedded video HTML.
1692 1819 *
1693 1820 * @since v.1.0.0
1694 1821 * @param STRING $url Video URL.
@@ -1838,9 +1965,9 @@
1838 1965 public function get_post_content( $post_id, $builder_type = '' ) {
1839 1966 $content_post = get_post( $post_id );
1840 1967 $content = $content_post->post_content;
1841 1968 if ( $builder_type == 'divi' || $builder_type == 'elementor' ) {
1842 - $content = apply_filters( 'the_content', $content );
1969 + $content = apply_filters( 'the_content', $content ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Core hook.
1843 1970 } else {
1844 1971 global $wp_embed;
1845 1972 $content = $wp_embed->autoembed( $content );
1846 1973 $content = do_blocks( $content );
@@ -1848,9 +1975,9 @@
1848 1975 }
1849 1976 $content = str_replace( ']]>', ']]&gt;', $content );
1850 1977 $content = preg_replace( '%<p>&nbsp;\s*</p>%', '', $content );
1851 1978 $content = preg_replace( '/^(?:<br\s*\/?>\s*)+/', '', $content );
1852 - echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1979 + echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Rendered post content (do_blocks/the_content), the same markup core prints for the post.
1853 1980 }
1854 1981
1855 1982 /**
1856 1983 * String Part finder inside array.
@@ -2268,12 +2395,11 @@
2268 2395 'ultp-script',
2269 2396 'ultp_data_frontend',
2270 2397 array(
2271 2398 'url' => ULTP_URL,
2272 - 'active' => ultimate_post()->is_lc_active(),
2273 2399 'ultpSavedDLMode' => ultimate_post()->get_dl_mode(),
2274 2400 'ajax' => admin_url( 'admin-ajax.php' ),
2275 - 'security' => wp_create_nonce( 'ultp-nonce' ),
2401 + 'security' => wp_create_nonce( 'ultp-public-nonce' ),
2276 2402 'home_url' => home_url(),
2277 2403 'dark_logo' => get_option( 'ultp_site_dark_logo', false ),
2278 2404 )
2279 2405 );