PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.4
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.4
4.9.4 4.9.3 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 All 202 releases
← All changes | includes/Core/Request.php +50 -0 4.9.3 → 4.9.4 View file →
@@ -912,8 +912,17 @@
912 912 if ( $this->invalid_request_query_vars !== null ) {
913 913 return;
914 914 }
915 915
916 + // A single-doc request whose main query returned nothing (private or
917 + // draft doc for a visitor who can't read it, a doc in another/inactive
918 + // WPML language) must stay a 404. Marking it singular here would render
919 + // the single template against a null post and expose the doc's title
920 + // through the queried object. See betterdocs/betterdocs#173.
921 + if ( $this->is_singular_docs_request() && ! $this->main_query_has_posts() ) {
922 + return;
923 + }
924 +
916 925 // Check if we have doc_category or knowledge_base in query vars
917 926 if ( isset( $wp_query->query_vars['doc_category'] ) && ! empty( $wp_query->query_vars['doc_category'] ) ) {
918 927 // If this is already identified as singular, don't override it
919 928 if ( $wp_query->is_singular() || $wp_query->is_singular ) {
@@ -1080,8 +1089,16 @@
1080 1089 if ( $this->invalid_request_query_vars !== null ) {
1081 1090 return $status_header;
1082 1091 }
1083 1092
1093 + // A single-doc request whose main query returned nothing is a genuine
1094 + // 404 — whatever the slug/term lookups below would find. Checked before
1095 + // both overrides, so neither can turn it into a soft-404 200.
1096 + // See betterdocs/betterdocs#173.
1097 + if ( 404 === (int) $code && $this->is_singular_docs_request() && ! $this->main_query_has_posts() ) {
1098 + return $status_header;
1099 + }
1100 +
1084 1101 // If a 404 is being sent but the queried object is a valid single docs post,
1085 1102 // override with 200. This guards against false 404s on single docs pages.
1086 1103 if ( $code == 404 &&
1087 1104 isset( $wp_query->queried_object ) &&
@@ -1142,8 +1159,41 @@
1142 1159 return 'HTTP/1.1 200 OK';
1143 1160 }
1144 1161
1145 1162 return $status_header;
1163 + }
1164 +
1165 + /**
1166 + * Whether the main query targets a single doc (`name`, `docs` or `p`),
1167 + * as opposed to an archive.
1168 + *
1169 + * @return bool
1170 + */
1171 + protected function is_singular_docs_request() {
1172 + global $wp_query;
1173 +
1174 + if ( ! $wp_query instanceof \WP_Query ) {
1175 + return false;
1176 + }
1177 +
1178 + $query_vars = $wp_query->query_vars;
1179 +
1180 + return ! empty( $query_vars['name'] )
1181 + || ! empty( $query_vars['docs'] )
1182 + || ( isset( $query_vars['p'] ) && (int) $query_vars['p'] > 0 );
1183 + }
1184 +
1185 + /**
1186 + * Whether the main query actually returned posts. Slug and term lookups
1187 + * only prove something exists; this is what the visitor can see — it
1188 + * already honours post status, capabilities and WPML's language filter.
1189 + *
1190 + * @return bool
1191 + */
1192 + protected function main_query_has_posts() {
1193 + global $wp_query;
1194 +
1195 + return $wp_query instanceof \WP_Query && ! empty( $wp_query->posts );
1146 1196 }
1147 1197
1148 1198 /**
1149 1199 * Ensure tax_query is always initialized as an object