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/REST_API.php +113 -35 5.0.32 → 5.1.1 View file →
@@ -20,8 +20,34 @@
20 20 */
21 21 class REST_API {
22 22
23 23 /**
24 + * Upper bound for the search endpoint's postPerPage. Matches the max of the
25 + * Search block's "Show Initial Post" slider, so no UI value is ever capped.
26 + * This is the same max range of the slider in gutenburg editor.
27 + * 100 is enough for search bar result.
28 + *
29 + * @since v.5.0.35
30 + */
31 + const SEARCH_POSTS_PER_PAGE_MAX = 100;
32 +
33 + /**
34 + * Longest accepted search term. Longer strings only widen the LIKE pattern
35 + * scanned against every row; no real query needs more.
36 + *
37 + * @since v.5.0.35
38 + */
39 + const SEARCH_TEXT_MAX_LENGTH = 200;
40 +
41 + /**
42 + * Upper bound for excerptLimit. Also keeps $num_words + 1 inside integer range
43 + * in wp_trim_words(), which otherwise overflows to float on a huge value.
44 + *
45 + * @since v.5.0.35
46 + */
47 + const SEARCH_EXCERPT_WORDS_MAX = 400;
48 +
49 + /**
24 50 * Setup class.
25 51 *
26 52 * @since v.1.0.0
27 53 */
@@ -400,9 +426,9 @@
400 426 * @return ARRAY | Response Image Size as Array
401 427 */
402 428 public function ultp_route_post_data( $prams ) {
403 429 $prams = $prams->get_params();
404 - if ( ! ( isset( $prams['wpnonce'] ) && wp_verify_nonce( sanitize_key( wp_unslash( $prams['wpnonce'] ) ), 'ultp-nonce' ) ) ) {
430 + if ( ! ( isset( $prams['wpnonce'] ) && wp_verify_nonce( sanitize_key( wp_unslash( $prams['wpnonce'] ) ), 'ultp-admin-nonce' ) ) ) {
405 431 die();
406 432 }
407 433 $data = array();
408 434 $loop = new \WP_Query( ultimate_post()->get_query( ultimate_post()->ultp_rest_sanitize_params( $prams ) ) );
@@ -488,9 +514,9 @@
488 514 $v[] = array(
489 515 'slug' => $val->slug,
490 516 'name' => $val->name,
491 517 'url' => get_term_link( $val->term_id ),
492 - 'color' => get_term_meta( $val->term_id, 'ultp_category_color', true ),
518 + 'color' => ultimate_post()->get_term_color( $val ),
493 519 );
494 520 }
495 521 $var['category'] = $v;
496 522 }
@@ -509,9 +535,9 @@
509 535 * @param ARRAY | $prams (ARRAY).
510 536 * @return ARRAY | Response Taxonomy List as Array
511 537 */
512 538 public function ultp_route_common_data( $prams ) {
513 - if ( ! ( isset( $_REQUEST['wpnonce'] ) && wp_verify_nonce( sanitize_key( wp_unslash( $_REQUEST['wpnonce'] ) ), 'ultp-nonce' ) ) ) {
539 + if ( ! ( isset( $_REQUEST['wpnonce'] ) && wp_verify_nonce( sanitize_key( wp_unslash( $_REQUEST['wpnonce'] ) ), 'ultp-admin-nonce' ) ) ) {
514 540 return rest_ensure_response( array() );
515 541 }
516 542
517 543 $all_post_type = ultimate_post()->get_post_type();
@@ -561,9 +587,9 @@
561 587 * @return ARRAY | Response Taxonomy List as Array
562 588 */
563 589 public function ultp_route_taxonomy_info_data( $prams ) {
564 590 $prams = $prams->get_params();
565 - if ( ! ( isset( $prams['wpnonce'] ) && wp_verify_nonce( sanitize_key( wp_unslash( $prams['wpnonce'] ) ), 'ultp-nonce' ) ) ) {
591 + if ( ! ( isset( $prams['wpnonce'] ) && wp_verify_nonce( sanitize_key( wp_unslash( $prams['wpnonce'] ) ), 'ultp-admin-nonce' ) ) ) {
566 592 return rest_ensure_response( array() );
567 593 }
568 594 $taxValue = isset( $prams['taxValue'] ) ? ultimate_post()->ultp_rest_sanitize_params( $prams['taxValue'] ) : '';
569 595 $queryNumber = isset( $prams['queryNumber'] ) ? ultimate_post()->ultp_rest_sanitize_params( $prams['queryNumber'] ) : '';
@@ -633,28 +659,61 @@
633 659 $post = $server->get_params();
634 660 $searchText = isset( $post['searchText'] ) ? ultimate_post()->ultp_rest_sanitize_params( $post['searchText'] ) : '';
635 661 $paged = isset( $post['paged'] ) ? ultimate_post()->ultp_rest_sanitize_params( $post['paged'] ) : '';
636 662 $postPerPage = isset( $post['postPerPage'] ) ? ultimate_post()->ultp_rest_sanitize_params( $post['postPerPage'] ) : '';
637 - $query_args = array(
663 +
664 + // Public route: cap posts_per_page so -1 can never mean "unlimited".
665 + // 0 survives the cap, and WP_Query resolves it to the posts_per_page option.
666 + $postPerPage = min( absint( $postPerPage ), self::SEARCH_POSTS_PER_PAGE_MAX );
667 + // is_scalar first: searchText arrives as an array if the caller sends one, and
668 + // casting that to string is an "Array to string conversion" warning.
669 + $searchText = is_scalar( $searchText ) ? mb_substr( (string) $searchText, 0, self::SEARCH_TEXT_MAX_LENGTH ) : '';
670 +
671 + $query_args = array(
638 672 's' => $searchText,
639 673 'paged' => $paged,
640 - 'compare' => 'LIKE',
641 674 'orderby' => 'relevance',
642 675 'posts_per_page' => $postPerPage,
643 676 );
644 677 if ( isset( $post['exclude'] ) && is_array( $post['exclude'] ) && count( $post['exclude'] ) > 0 ) {
645 - $post['exclude'] = ultimate_post()->ultp_rest_sanitize_params( $post['exclude'] );
646 - $post_exclude = array();
647 - foreach ( $post['exclude'] as $data ) {
648 - $post_exclude[ $data['title'] ] = $data['title'];
678 + $all_types = get_post_types( array( 'public' => true ), 'names' );
679 + $post_exclude = array();
680 +
681 + // Bound before sanitising: this route is public, and sanitising an array
682 + // whose length the caller picks is itself the attack. Excluding more post
683 + // types than exist is meaningless, so nothing valid is dropped.
684 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Length is bounded here only; every element is sanitized below before use.
685 + $exclude_input = array_slice( $post['exclude'], 0, count( $all_types ) );
686 +
687 + foreach ( $exclude_input as $data ) {
688 + // A flat array of strings would index a string here and warn.
689 + if ( ! is_array( $data ) || ! isset( $data['title'] ) || ! is_scalar( $data['title'] ) ) {
690 + continue;
691 + }
692 + $exclude_type = sanitize_text_field( $data['title'] );
693 + $post_exclude[ $exclude_type ] = $exclude_type;
649 694 }
650 - $all_types = get_post_types( array( 'public' => true ), 'names' );
651 - $post_type = array_diff_key( $all_types, $post_exclude );
652 - $query_args['post_type'] = $post_type;
695 +
696 + $query_args['post_type'] = array_diff_key( $all_types, $post_exclude );
653 697 }
654 698 $output = '';
655 699 $query_result = new \WP_Query( $query_args );
656 700
701 + /**
702 + * Resolved once here rather than guarded at each use, since the request may
703 + * omit any of them. The loose == is deliberate: the two callers disagree on
704 + * type -- the editor sends real booleans, the frontend sends 1/0 parsed out
705 + * of data attributes -- and both must keep working.
706 + */
707 + $show_image = isset( $post['image'] ) && $post['image'] == 1;
708 + $show_category = isset( $post['category'] ) && $post['category'] == 1;
709 + $show_author = isset( $post['author'] ) && $post['author'] == 1;
710 + $show_date = isset( $post['date'] ) && $post['date'] == 1;
711 + $show_excerpt = isset( $post['excerpt'] ) && $post['excerpt'] == 1;
712 + $excerpt_limit = isset( $post['excerptLimit'] )
713 + ? min( absint( $post['excerptLimit'] ), self::SEARCH_EXCERPT_WORDS_MAX )
714 + : 55;
715 +
657 716 if ( $query_result->have_posts() ) {
658 717 while ( $query_result->have_posts() ) {
659 718 $query_result->the_post();
660 719 $post_id = get_the_ID();
@@ -660,40 +719,59 @@
660 719 $post_id = get_the_ID();
661 720 $title = get_the_title();
662 721
663 722 $output .= '<div class="ultp-search-result__item">';
664 - if ( $post['image'] == 1 && has_post_thumbnail() ) {
665 - $thumb_id = get_post_thumbnail_id( $post_id );
666 - $output .= '<img class="ultp-searchresult-image" src=' . wp_get_attachment_image_src( $thumb_id, 'thumbnail', false )[0] . ' alt="' . $title . '"/>';
723 +
724 + if ( $show_image && has_post_thumbnail() ) {
725 + // has_post_thumbnail() only proves the _thumbnail_id meta exists.
726 + // If the attachment behind it was deleted, src() returns false.
727 + $thumb = wp_get_attachment_image_src( get_post_thumbnail_id( $post_id ), 'thumbnail', false );
728 + if ( ! empty( $thumb[0] ) ) {
729 + $output .= '<img class="ultp-searchresult-image" src="' . esc_url( $thumb[0] ) . '" alt="' . esc_attr( $title ) . '"/>';
730 + }
667 731 }
668 - $output .= '<div class="ultp-searchresult-content">';
669 - $output .= '<div class="ultp-rescontent-meta">';
670 - // Category.
671 - $post_cat = get_the_terms( $post_id, 'category' );
672 - if ( $post['category'] == 1 && $post_cat && count( $post_cat ) ) {
732 +
733 + $output .= '<div class="ultp-searchresult-content">';
734 + $output .= '<div class="ultp-rescontent-meta">';
735 +
736 + // Category.
737 + $post_cat = get_the_terms( $post_id, 'category' );
738 + if ( $show_category && $post_cat && ! is_wp_error( $post_cat ) ) {
673 739 $output .= '<div class="ultp-searchresult-category">';
674 740 foreach ( $post_cat as $cat ) {
675 - $output .= '<a href="' . get_term_link( $cat->term_id ) . '">' . $cat->name . '</a>';
741 + $term_link = get_term_link( $cat->term_id );
742 + if ( is_wp_error( $term_link ) ) {
743 + continue;
744 + }
745 + $output .= '<a href="' . esc_url( $term_link ) . '">' . esc_html( $cat->name ) . '</a>';
676 746 }
677 - $output .= '</div>';
747 + $output .= '</div>';
678 748 }
679 - // Author.
680 - if ( $post['author'] == 1 ) {
749 +
750 + // Author.
751 + if ( $show_author ) {
681 752 $user_id = get_the_author_meta( 'ID' );
682 - $output .= '<a href="' . get_author_posts_url( $user_id ) . '" class="ultp-searchresult-author">' . get_the_author_meta( 'display_name' ) . '</a>';
753 + $output .= '<a href="' . esc_url( get_author_posts_url( $user_id ) ) . '" class="ultp-searchresult-author">' . esc_html( get_the_author_meta( 'display_name' ) ) . '</a>';
683 754 }
684 - // Date.
685 - if ( $post['date'] == 1 ) {
686 - $output .= '<div class="ultp-searchresult-publishdate">' . get_the_date( 'F j, Y' ) . '</div>';
755 +
756 + // Date.
757 + if ( $show_date ) {
758 + $output .= '<div class="ultp-searchresult-publishdate">' . esc_html( get_the_date( 'F j, Y' ) ) . '</div>';
687 759 }
688 - $output .= '</div>';
689 - $output .= '<a href="' . get_permalink() . '" class="ultp-searchresult-title">' . $title . '</a>';
690 - if ( $post['excerpt'] == 1 ) {
691 - $output .= '<div class="ultp-searchresult-excerpt">' . wp_trim_words( get_the_excerpt(), isset( $post['excerptLimit'] ) ? ultimate_post()->ultp_rest_sanitize_params( $post['excerptLimit'] ) : 55 ) . '</div>';
760 +
761 + $output .= '</div>';
762 + $output .= '<a href="' . esc_url( get_permalink() ) . '" class="ultp-searchresult-title">' . esc_html( $title ) . '</a>';
763 +
764 + if ( $show_excerpt ) {
765 + // wp_trim_words() strips tags, so what comes back is plain text.
766 + $output .= '<div class="ultp-searchresult-excerpt">' . esc_html( wp_trim_words( get_the_excerpt(), $excerpt_limit ) ) . '</div>';
692 767 }
693 - $output .= '</div>';
694 - $output .= '</div>';
768 +
769 + $output .= '</div>';
770 + $output .= '</div>';
695 771 }
772 + // the_post() overwrote the global $post; hand it back.
773 + wp_reset_postdata();
696 774 }
697 775
698 776 return array(
699 777 'post_data' => $output,