PluginProbe
Forumax – AI Powered Advanced Community Forum Plugin / 2.5.0
Forumax – AI Powered Advanced Community Forum Plugin v2.5.0
2.5.1 2.5.0 2.4.4 2.4.3 2.4.2 2.4.1 2.4.0 trunk 1.0.8 1.1.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 All 31 releases
← All changes | includes/ajax_actions.php +123 -91 2.4.0 → 2.5.0 View file →
@@ -13,8 +13,43 @@
13 13 add_action( 'wp_ajax_forumax_search_data_fetch', 'forumax_search_data_fetch' );
14 14 add_action( 'wp_ajax_nopriv_forumax_search_data_fetch', 'forumax_search_data_fetch' );
15 15
16 16 /**
17 + * Build the ancestor "breadcrumb" trail for a search result, mirroring the
18 + * EazyDocs search UI (e.g. Forum › Topic for a reply).
19 + *
20 + * @param int $post_id Post ID.
21 + * @param string $type Post type of the result.
22 + * @return array List of ancestor titles, ordered from top-most down.
23 + */
24 +function forumax_search_result_breadcrumb( $post_id, $type ) {
25 + $crumbs = array();
26 +
27 + if ( 'reply' === $type && function_exists( 'bbp_get_reply_forum_id' ) ) {
28 + $forum_id = bbp_get_reply_forum_id( $post_id );
29 + $topic_id = bbp_get_reply_topic_id( $post_id );
30 + if ( $forum_id ) {
31 + $crumbs[] = get_the_title( $forum_id );
32 + }
33 + if ( $topic_id ) {
34 + $crumbs[] = get_the_title( $topic_id );
35 + }
36 + } elseif ( 'topic' === $type && function_exists( 'bbp_get_topic_forum_id' ) ) {
37 + $forum_id = bbp_get_topic_forum_id( $post_id );
38 + if ( $forum_id ) {
39 + $crumbs[] = get_the_title( $forum_id );
40 + }
41 + } else {
42 + // Generic hierarchy fallback (forums with parents, docs, pages, etc.).
43 + foreach ( array_reverse( get_post_ancestors( $post_id ) ) as $ancestor_id ) {
44 + $crumbs[] = get_the_title( $ancestor_id );
45 + }
46 + }
47 +
48 + return array_values( array_filter( $crumbs ) );
49 +}
50 +
51 +/**
17 52 * Fetch search data.
18 53 */
19 54 function forumax_search_data_fetch() {
20 55
@@ -20,31 +55,65 @@
20 55
21 56 // Check nonce.
22 57 check_ajax_referer( 'forumax-nonce', 'nonce' );
23 58
24 - $opt = get_option( 'bbpc_opt' );
25 - $is_ajax_search_tab = $opt['is_ajax_search_tab'] ?? '';
26 -
27 59 // Sanitize keyword.
28 - $keyword = isset( $_POST['keyword'] ) ? sanitize_text_field( $_POST['keyword'] ) : '';
29 - $post_type = isset( $_POST['post_type'] ) ? sanitize_text_field( $_POST['post_type'] ) : '';
60 + $keyword = isset( $_POST['keyword'] ) ? sanitize_text_field( wp_unslash( $_POST['keyword'] ) ) : '';
61 + $post_type = isset( $_POST['post_type'] ) ? sanitize_text_field( wp_unslash( $_POST['post_type'] ) ) : '';
30 62
31 - // Determine which post types to query.
32 - if ( ! empty( $post_type ) ) {
63 + // Determine which post types to query. An explicit single post type narrows
64 + // the search; 'all' (or no value) searches the whole forum: forums, topics
65 + // and replies, so content matches from any of them are returned.
66 + if ( ! empty( $post_type ) && 'all' !== $post_type ) {
33 67 $query_post_types = array( $post_type );
34 68 } elseif ( class_exists( 'bbPress' ) ) {
35 - $query_post_types = array( 'forum', 'topic' );
69 + $query_post_types = array( 'forum', 'topic', 'reply' );
36 70 } else {
37 71 $query_post_types = array( 'post' );
38 72 }
39 73
74 + // Determine which statuses to include. bbPress stores closed topics with the
75 + // 'closed' post status (and uses 'private'/'hidden' for restricted forums),
76 + // none of which are returned by WP_Query's default 'publish'-only behavior.
77 + // Include the publicly viewable bbPress statuses so closed topics still match.
78 + if ( class_exists( 'bbPress' ) ) {
79 + $query_post_status = array( 'publish', 'closed', 'private', 'hidden' );
80 + } else {
81 + $query_post_status = array( 'publish' );
82 + }
83 +
84 + // Force the search to match BOTH the title and the content. This overrides
85 + // any other plugin/theme `posts_search` filter that may otherwise restrict
86 + // matching to titles only, so content keywords return results too.
87 + $bbpc_search_clause = static function ( $search, $wp_query ) use ( $keyword ) {
88 + global $wpdb;
89 +
90 + if ( '' === $keyword || ! $wp_query->get( 'bbpc_ajax_search' ) ) {
91 + return $search;
92 + }
93 +
94 + $like = '%' . $wpdb->esc_like( $keyword ) . '%';
95 +
96 + return $wpdb->prepare(
97 + " AND ( {$wpdb->posts}.post_title LIKE %s OR {$wpdb->posts}.post_content LIKE %s ) ",
98 + $like,
99 + $like
100 + );
101 + };
102 +
103 + add_filter( 'posts_search', $bbpc_search_clause, 999, 2 );
104 +
40 105 // Main results query.
41 106 $results = new \WP_Query( array(
42 - 'post_type' => $query_post_types,
43 - 's' => $keyword,
44 - 'posts_per_page' => 10,
107 + 'post_type' => $query_post_types,
108 + 'post_status' => $query_post_status,
109 + 's' => $keyword,
110 + 'posts_per_page' => 10,
111 + 'bbpc_ajax_search' => true,
45 112 ) );
46 113
114 + remove_filter( 'posts_search', $bbpc_search_clause, 999 );
115 +
47 116 $results_count = $results->found_posts;
48 117
49 118 if ( $results_count > 0 ) {
50 119 $nsoresult = 'data-result=';
@@ -68,11 +137,12 @@
68 137 if ( ! isset( $grouped[ $type ] ) ) {
69 138 $grouped[ $type ] = array();
70 139 }
71 140 $grouped[ $type ][] = array(
72 - 'id' => get_the_ID(),
73 - 'title' => get_the_title(),
74 - 'url' => get_the_permalink(),
141 + 'id' => get_the_ID(),
142 + 'title' => get_the_title(),
143 + 'url' => get_the_permalink(),
144 + 'breadcrumb' => forumax_search_result_breadcrumb( get_the_ID(), $type ),
75 145 );
76 146 endwhile;
77 147 wp_reset_postdata();
78 148
@@ -79,96 +149,58 @@
79 149 // Render grouped results.
80 150 $type_labels = array(
81 151 'forum' => __( 'Forums', 'forumax' ),
82 152 'topic' => __( 'Topics', 'forumax' ),
153 + 'reply' => __( 'Replies', 'forumax' ),
83 154 'docs' => __( 'Documentation', 'forumax' ),
84 155 'post' => __( 'Posts', 'forumax' ),
85 156 );
86 157
158 + // Document icon shared by every result row.
159 + $result_icon = '<svg class="bbpc-result-icon" width="16" height="16" viewBox="0 0 17 17" fill="currentColor" role="img" aria-hidden="true"><path d="M14.72,0H2.28A2.28,2.28,0,0,0,0,2.28V14.72A2.28,2.28,0,0,0,2.28,17H14.72A2.28,2.28,0,0,0,17,14.72V2.28A2.28,2.28,0,0,0,14.72,0ZM2.28,1H14.72A1.28,1.28,0,0,1,16,2.28V5.33H1V2.28A1.28,1.28,0,0,1,2.28,1ZM1,14.72V6.33H5.33V16H2.28A1.28,1.28,0,0,1,1,14.72ZM14.72,16H6.33V6.33H16v8.39A1.28,1.28,0,0,1,14.72,16Z"></path></svg>';
160 +
161 + // Result type tabs (All / Forums / Topics / Replies), mirroring EazyDocs.
162 + ?>
163 + <div class="bbpc-result-tabs">
164 + <button type="button" class="bbpc-tab active" data-tab="all"><?php esc_html_e( 'All', 'forumax' ); ?></button>
165 + <?php foreach ( $grouped as $type => $posts ) :
166 + $tab_label = isset( $type_labels[ $type ] ) ? $type_labels[ $type ] : ucfirst( $type );
167 + ?>
168 + <button type="button" class="bbpc-tab" data-tab="<?php echo esc_attr( $type ); ?>"><?php echo esc_html( $tab_label ); ?></button>
169 + <?php endforeach; ?>
170 + </div>
171 + <?php
172 +
87 173 foreach ( $grouped as $type => $posts ) :
88 174 $label = isset( $type_labels[ $type ] ) ? $type_labels[ $type ] : ucfirst( $type );
89 - echo '<h2 class="bbpc-search-title">' . esc_html( $label ) . '</h2>';
90 - foreach ( $posts as $post_item ) :
91 - ?>
92 - <div class="search-result-item forum" onclick="document.location='<?php echo esc_url( $post_item['url'] ); ?>'">
93 - <a href="<?php echo esc_url( $post_item['url'] ); ?>">
94 - <?php echo esc_html( $post_item['title'] ); ?>
95 - </a>
96 - </div>
97 - <?php
98 - endforeach;
175 + ?>
176 + <div class="bbpc-result-group" data-type="<?php echo esc_attr( $type ); ?>">
177 + <div class="bbpc-result-group-label"><?php echo esc_html( $label ); ?></div>
178 + <?php foreach ( $posts as $post_item ) : ?>
179 + <div class="search-result-item" data-url="<?php echo esc_url( $post_item['url'] ); ?>" onclick="document.location='<?php echo esc_url( $post_item['url'] ); ?>'">
180 + <a href="<?php echo esc_url( $post_item['url'] ); ?>" class="bbpc-result-link">
181 + <?php echo $result_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- static markup. ?>
182 + <span class="bbpc-result-body">
183 + <span class="bbpc-result-title"><?php echo esc_html( $post_item['title'] ); ?></span>
184 + <span class="bbpc-result-type"><?php echo esc_html( $label ); ?></span>
185 + </span>
186 + </a>
187 + <?php if ( ! empty( $post_item['breadcrumb'] ) ) : ?>
188 + <ol class="bbpc-result-breadcrumb">
189 + <?php foreach ( $post_item['breadcrumb'] as $crumb ) : ?>
190 + <li class="bbpc-breadcrumb-item"><?php echo esc_html( $crumb ); ?></li>
191 + <?php endforeach; ?>
192 + </ol>
193 + <?php endif; ?>
194 + </div>
195 + <?php endforeach; ?>
196 + </div>
197 + <?php
99 198 endforeach;
100 199 endif;
101 200
102 201 echo '</div>';
103 - echo '<h5 class="not-found-text">' . esc_html__( 'Not Found Result!', 'forumax' ) . '</h5>';
104 202 echo '</div>';
105 - wp_die();
106 -}
107 -
108 -/**
109 - * Forum Search results
110 - */
111 -add_action( 'wp_ajax_forumax_search_data_forum', 'forumax_search_data_forum' );
112 -add_action( 'wp_ajax_nopriv_forumax_search_data_forum', 'forumax_search_data_forum' );
113 -
114 -/**
115 - * Search data forum.
116 - */
117 -function forumax_search_data_forum() {
118 -
119 - // Check nonce
120 - check_ajax_referer( 'forumax-nonce', 'nonce' );
121 -
122 - // Sanitize keyword
123 - $keyword = isset( $_POST['keyword'] ) ? sanitize_text_field( $_POST['keyword'] ) : '';
124 -
125 - // Forums query
126 - if ( class_exists( 'bbPress' ) ) :
127 -
128 - echo '<div class="search-results-tab show" id="search-forum-results">';
129 -
130 - $forum = new WP_Query( [
131 - 'post_type' => 'forum',
132 - 's' => $keyword,
133 - 'posts_per_page' => 5,
134 - ] );
135 -
136 - if ( $forum->have_posts() ) :
137 - echo '<h2 class="bbpc-search-title">' . esc_html__( 'Forums', 'forumax' ) . '</h2>';
138 - while ( $forum->have_posts() ) : $forum->the_post();
139 - ?>
140 - <div class="search-result-item forum" onclick="document.location='<?php echo esc_url( get_the_permalink( get_the_ID() ) ); ?>'">
141 - <a href="<?php echo esc_url( get_the_permalink( get_the_ID() ) ); ?>">
142 - <?php the_title(); ?>
143 - </a>
144 - </div>
145 - <?php
146 - endwhile;
147 - wp_reset_postdata();
148 - endif;
149 -
150 - $topics = new WP_Query( [
151 - 'post_type' => 'topic',
152 - 's' => $keyword,
153 - 'posts_per_page' => 5,
154 - ] );
155 -
156 - if ( $topics->have_posts() ) :
157 - echo '<h2 class="bbpc-search-title">' . esc_html__( 'Topics', 'forumax' ) . '</h2>';
158 - while ( $topics->have_posts() ) : $topics->the_post();
159 - ?>
160 - <div class="search-result-item forum" onclick="document.location='<?php echo esc_url( get_the_permalink( get_the_ID() ) ); ?>'">
161 - <a href="<?php echo esc_url( get_the_permalink( get_the_ID() ) ); ?>">
162 - <?php the_title(); ?>
163 - </a>
164 - </div>
165 - <?php
166 - endwhile;
167 - wp_reset_postdata();
168 - endif;
169 - echo '</div>';
170 - endif;
171 203 wp_die();
172 204 }
173 205
174 206 /**