PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.2
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.2
4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 All 200 releases
← All changes | includes/REST/Docs.php +92 -10 4.5.64.9.2 View file →
@@ -105,18 +105,27 @@
105 105
106 106 public function get_docs_faq_counts() {
107 107 // Initialize the return array
108 108 $counts = [
109 - 'created_docs' => 0,
110 - 'published_docs' => 0,
111 - 'created_faq' => 0,
112 - 'published_faq' => 0
109 + 'created_docs' => 0,
110 + 'published_docs' => 0,
111 + 'created_faq' => 0,
112 + 'published_faq' => 0,
113 + 'created_product_faq' => 0,
114 + 'published_product_faq' => 0
113 115 ];
114 116
115 - // Get all docs (any status)
117 + // QA-004: this endpoint is public (Docs::permission_check() returns true),
118 + // so the draft-inclusive "created" totals must not leak unpublished-content
119 + // volume to anonymous / low-privilege callers. Only users who can edit
120 + // others' posts see the any-status counts; everyone else gets published
121 + // counts (created_* == published_*).
122 + $created_status = current_user_can( 'edit_others_posts' ) ? 'any' : 'publish';
123 +
124 + // Get all docs (created = any status for privileged users, else published)
116 125 $all_docs_query = new WP_Query([
117 126 'post_type' => 'docs',
118 - 'post_status' => 'any',
127 + 'post_status' => $created_status,
119 128 'posts_per_page' => -1,
120 129 'fields' => 'ids',
121 130 'no_found_rows' => true,
122 131 ]);
@@ -131,19 +140,49 @@
131 140 'no_found_rows' => true,
132 141 ]);
133 142 $counts['published_docs'] = $published_docs_query->post_count;
134 143
135 - // Get all FAQs (any status)
144 + // Product FAQ groups share the betterdocs_faq post type but live in the
145 + // betterdocs_product_faq_category taxonomy. Split the counts so the
146 + // General FAQ Builder stats exclude Product FAQs (no leakage) and the
147 + // WooCommerce tab can show its own Product FAQ totals.
148 + $product_terms = get_terms([
149 + 'taxonomy' => 'betterdocs_product_faq_category',
150 + 'hide_empty' => false,
151 + 'fields' => 'ids',
152 + ]);
153 + $product_terms = ( ! is_wp_error( $product_terms ) && ! empty( $product_terms ) ) ? $product_terms : [];
154 +
155 + $exclude_product_tax_query = ! empty( $product_terms ) ? [
156 + [
157 + 'taxonomy' => 'betterdocs_product_faq_category',
158 + 'field' => 'term_id',
159 + 'terms' => $product_terms,
160 + 'operator' => 'NOT IN',
161 + ],
162 + ] : [];
163 +
164 + $include_product_tax_query = ! empty( $product_terms ) ? [
165 + [
166 + 'taxonomy' => 'betterdocs_product_faq_category',
167 + 'field' => 'term_id',
168 + 'terms' => $product_terms,
169 + 'operator' => 'IN',
170 + ],
171 + ] : [];
172 +
173 + // General FAQs (created = any status for privileged users), excluding Product FAQs.
136 174 $all_faq_query = new WP_Query([
137 175 'post_type' => 'betterdocs_faq',
138 - 'post_status' => 'any',
176 + 'post_status' => $created_status,
139 177 'posts_per_page' => -1,
140 178 'fields' => 'ids',
141 179 'no_found_rows' => true,
180 + 'tax_query' => $exclude_product_tax_query,
142 181 ]);
143 182 $counts['created_faq'] = $all_faq_query->post_count;
144 183
145 - // Get published FAQs only
184 + // Published General FAQs, excluding Product FAQs.
146 185 $published_faq_query = new WP_Query([
147 186 'post_type' => 'betterdocs_faq',
148 187 'post_status' => 'publish',
149 188 'posts_per_page' => -1,
@@ -148,11 +187,36 @@
148 187 'post_status' => 'publish',
149 188 'posts_per_page' => -1,
150 189 'fields' => 'ids',
151 190 'no_found_rows' => true,
191 + 'tax_query' => $exclude_product_tax_query,
152 192 ]);
153 193 $counts['published_faq'] = $published_faq_query->post_count;
154 194
195 + if ( ! empty( $product_terms ) ) {
196 + // Product FAQs (created = any status for privileged users).
197 + $all_product_faq_query = new WP_Query([
198 + 'post_type' => 'betterdocs_faq',
199 + 'post_status' => $created_status,
200 + 'posts_per_page' => -1,
201 + 'fields' => 'ids',
202 + 'no_found_rows' => true,
203 + 'tax_query' => $include_product_tax_query,
204 + ]);
205 + $counts['created_product_faq'] = $all_product_faq_query->post_count;
206 +
207 + // Published Product FAQs.
208 + $published_product_faq_query = new WP_Query([
209 + 'post_type' => 'betterdocs_faq',
210 + 'post_status' => 'publish',
211 + 'posts_per_page' => -1,
212 + 'fields' => 'ids',
213 + 'no_found_rows' => true,
214 + 'tax_query' => $include_product_tax_query,
215 + ]);
216 + $counts['published_product_faq'] = $published_product_faq_query->post_count;
217 + }
218 +
155 219 return $counts;
156 220 }
157 221
158 222 /**
@@ -639,9 +703,27 @@
639 703 public function search_insert( $request ) {
640 704 $search_input = sanitize_text_field( $request->get_param( 's' ) );
641 705 $no_result = sanitize_text_field( $request->get_param( 'no_result' ) );
642 706
643 - return betterdocs()->query->insert_search_keyword( $search_input, $no_result );
707 + $result = betterdocs()->query->insert_search_keyword( $search_input, $no_result );
708 +
709 + if ( ! empty( $search_input ) ) {
710 + /**
711 + * Fires after a front-end docs search is logged.
712 + *
713 + * Pro hooks this to record a 'search' event into the raw events
714 + * table, which the Advanced Analytics aggregator rolls up into
715 + * betterdocs_analytics_search (search volume + zero-result rate —
716 + * the primary content-gap signal).
717 + *
718 + * @param string $search_input The search term.
719 + * @param bool $no_result True when the search returned no results.
720 + * @param WP_REST_Request $request The ingest request.
721 + */
722 + do_action( 'betterdocs_analytics_search_recorded', $search_input, ! empty( $no_result ), $request );
723 + }
724 +
725 + return $result;
644 726 }
645 727
646 728
647 729 public function get_terms_name_and_slug( $request ) {