PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.4.1
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.4.1
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 3.5.2 All 199 releases
betterdocs / includes / Core / Rewrite.php

Rewrite.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.4.1, at includes/Core/Rewrite.php

314 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDeveloper\BetterDocs\Core;
4
5 use WP_Post;
6 use WPDeveloper\BetterDocs\Utils\Base;
7 use WPDeveloper\BetterDocs\Utils\Database;
8
9 class Rewrite extends Base {
10 protected $settings;
11 protected $database;
12
13 public function __construct( Settings $settings, Database $database ) {
14 $this->settings = $settings;
15 $this->database = $database;
16 }
17
18 public function init() {
19 add_action( 'init', [ $this, 'rules' ] );
20 add_action( 'betterdocs::settings::saved', [ $this, 'save_permalink_structure' ], 2, 3 );
21 add_action( 'template_redirect', [ $this, 'handle_pagination_redirect' ] );
22 }
23
24 /**
25 * Handle pagination redirect
26 * Redirects pagination attempts to main archive
27 */
28 public function handle_pagination_redirect() {
29 if ( ! is_post_type_archive( 'docs' ) ) {
30 return;
31 }
32
33 $paged = get_query_var( 'paged' );
34 if ( $paged > 1 ) {
35 global $wp_query;
36 $wp_query->set_404();
37 status_header( 404 );
38 nocache_headers();
39 }
40 }
41
42 public function remove_knowledge_base_placeholder( $permalink ) {
43 $permalink_array = $this->permalink_structure( $permalink, 'arraywithpercent' );
44 $permalink_array = array_filter(
45 $permalink_array,
46 function ( $item ) {
47 return $item !== '%knowledge_base%';
48 }
49 );
50
51 return trailingslashit( implode( '/', $permalink_array ) );
52 }
53
54 /**
55 * This method is hooked with an action called 'betterdocs::settings::saved'
56 *
57 * @since 2.5.0
58 *
59 * @param bool $_saved
60 * @param array $_settings
61 * @param array $_old_settings
62 *
63 * @return void
64 */
65 public function save_permalink_structure( $_saved, $_settings, $_old_settings ) {
66 if ( ! isset( $_settings['permalink_structure'] ) ) {
67 return;
68 }
69
70 $_permalink_structure = $_settings['permalink_structure'];
71
72 /**
73 * TODO: Let's re-check later to optimize it.
74 */
75 if ( ! betterdocs()->is_pro_active() ) {
76 $_permalink_structure = $this->remove_knowledge_base_placeholder( $_permalink_structure );
77 }
78
79 if ( $_permalink_structure !== $_old_settings['permalink_structure'] ) {
80 $this->settings->save( 'permalink_structure', $this->permalink_structure( $_permalink_structure ) );
81 }
82
83 $old_docs_page = isset( $_old_settings['docs_page'] ) ? $_old_settings['docs_page'] : '';
84 $current_docs_page = isset( $_settings['docs_page'] ) ? $_settings['docs_page'] : '';
85
86 /**
87 * This block of code decides whether it needs to be flushed or not.
88 * Flush happens after register the post type.
89 */
90 switch ( true ) {
91 case $_permalink_structure !== $_old_settings['permalink_structure']:
92 case $_settings['docs_slug'] !== $_old_settings['docs_slug']:
93 case $_settings['builtin_doc_page'] !== $_old_settings['builtin_doc_page']:
94 case $current_docs_page !== $old_docs_page:
95 case $_settings['tag_slug'] !== $_old_settings['tag_slug']:
96 case $_settings['category_slug'] !== $_old_settings['category_slug']:
97 $this->database->set_transient( 'betterdocs_flush_rewrite_rules', true );
98 break;
99 }
100 }
101
102 public function permalink_structure( $structure = '', $output = 'string' ) {
103 if ( empty( $structure ) ) {
104 $structure = $this->settings->get( 'permalink_structure', 'docs' );
105 }
106
107 $docs_slug = $this->get_base_slug();
108
109 $_structure_array = explode( '%', $structure );
110 if ( $_structure_array[0] == '/' ) {
111 $structure = $docs_slug . $structure;
112 } elseif ( $_structure_array[0] == '' ) {
113 $structure = $docs_slug . '/' . $structure;
114 }
115
116 $structure = trim( $structure, '/' );
117
118 if ( $output === 'string' ) {
119 return $structure;
120 }
121
122 if ( $output === 'array' ) {
123 $structure = explode( '/', $structure );
124 $structure = array_map(
125 function ( $item ) {
126 return trim( $item, '/%' );
127 },
128 $structure
129 );
130 }
131
132 if ( $output === 'arraywithpercent' ) {
133 $structure = explode( '/', $structure );
134 $structure = array_map(
135 function ( $item ) {
136 return trim( $item, '/' );
137 },
138 $structure
139 );
140 }
141
142 return $structure;
143 }
144
145 public function hierarchy_based_slug_switch() {
146 $hierarchy_slug = $this->settings->get( 'enable_category_hierarchy_slugs', false );
147 return $hierarchy_slug;
148 }
149
150 public function get_base_slug() {
151 $docs_slug = $this->settings->get( 'docs_slug', 'docs' );
152 $docs_page = $this->settings->get( 'docs_page', 0 );
153 $builtin_doc_page = $this->settings->get( 'builtin_doc_page', true );
154
155 if ( ! $builtin_doc_page && $docs_page !== 0 ) {
156 $post_info = get_post( $docs_page );
157 $docs_slug = $post_info instanceof WP_Post ? $post_info->post_name : $docs_slug;
158 }
159
160 $docs_slug = trim( $docs_slug, '/' );
161
162 return $docs_slug;
163 }
164
165 public function normalzie_doc_perma_structure( $structure ) {
166 $structure = $this->permalink_structure( $structure, 'arraywithpercent' );
167 $structure = array_reduce(
168 $structure,
169 function ( $carry, $item ) {
170 if ( strpos( $item, '%' ) !== false ) {
171 $carry[] = $item;
172 } elseif ( $carry && strpos( end( $carry ), '%' ) === false ) {
173 $carry[ count( $carry ) - 1 ] .= "/$item";
174 } else {
175 $carry[] = $item;
176 }
177
178 return $carry;
179 },
180 []
181 );
182
183 $structure[] = '%docs%';
184
185 $group = array_filter(
186 $structure,
187 function ( $item ) {
188 return strpos( $item, '%' ) === 0;
189 }
190 );
191
192 return [
193 'raw' => $structure,
194 'group' => $group
195 ];
196 }
197
198 public function make_regex( $segments ) {
199 return array_reduce(
200 $segments,
201 function ( $carry, $item ) {
202 $carry .= ( strpos( $item, '%' ) !== false ? '([^/]+)' : $item ) . '/';
203 return $carry;
204 },
205 '^'
206 ) . '?$';
207 }
208
209 public function make_query( $segments ) {
210 $query = [];
211 $matchId = 1;
212
213 foreach ( $segments as $segment ) {
214 $query[] = trim( $segment, '%' ) . '=$matches[' . $matchId . ']';
215 ++$matchId;
216 }
217
218 return 'index.php?' . implode( '&', $query ) . '&post_type=docs';
219 }
220
221 public function rules() {
222 /**
223 * docs can be dynamic / as its a root slug
224 *
225 * https://url/docs/kb-slug
226 * https://url/docs/kb-slug/category-slug
227 * https://url/docs-category/category-slug
228 *
229 * Docs Visit:
230 * docs can be dynamic / as its a root slug
231 * or docs can be change by settings.
232 *
233 * https://url/docs/(docs-slug)
234 * https://url/docs/(cat-slug)/(docs-slug)
235 * https://url/docs/(kb-slug)/(docs-slug)
236 * https://url/docs/(kb-slug)/(cat-slug)/(docs-slug)
237 * https://url/docs/(cat-slug)/(kb-slug)/(docs-slug)
238 */
239
240 $base = $this->get_base_slug();
241 $hierarchy_slug = $this->hierarchy_based_slug_switch();
242
243 // Add rule to handle pagination attempts
244 $this->add_rewrite_rule(
245 '^' . $base . '/page/([0-9]+)/?$',
246 'index.php?post_type=docs',
247 'top'
248 );
249
250 $base = $this->get_base_slug();
251 $this->add_rewrite_rule( '^' . $base . '/(feed|rdf|rss|rss2|atom)/?$', 'index.php?post_type=docs&feed=$matches[1]' );
252 $this->add_rewrite_rule( '^' . $base . '/authors/([0-9]+)/?$', 'index.php?post_type=docs&author=$matches[1]' );
253 $this->add_rewrite_rule( '^' . $base . '/authors/([0-9]+)/page/([0-9]+)/?$', 'index.php?post_type=docs&author=$matches[1]&page=$matches[2]' );
254
255 /**
256 * This code of blocks used to determine single docs permalink.
257 */
258 $_docs_perma_struct = betterdocs()->settings->get( 'permalink_structure', 'docs' );
259 $_normalized_structure = $this->normalzie_doc_perma_structure( $_docs_perma_struct );
260
261 if( $hierarchy_slug ) { // register hierarchy based slug rewrite rule, for single doc permalink
262 // When MKB or WPML is used, the actual base for single docs might differ from the generic $base
263 $perma_base = isset( $_normalized_structure['raw'][0] ) && strpos( $_normalized_structure['raw'][0], '%' ) === false
264 ? $_normalized_structure['raw'][0]
265 : $base;
266
267 $this->add_rewrite_rule( '^' . $perma_base . '/(.+?)/([^/]+)(?:/([0-9]+))?/?$', 'index.php?doc_category=$matches[1]&docs=$matches[2]&post_type=docs', 'top' );
268
269 // Removed flush_rewrite_rules() from here. Calling flush_rewrite_rules() on the 'init' hook causes
270 // infinite database updates and destroys custom rules when WPML or MKB dynamically filters keys.
271 }
272
273 $_feed_regex = $_normalized_structure['raw'];
274 $_feed_regex[] = '(feed|rdf|rss|rss2|atom)';
275 $_feed_group = $_normalized_structure['group'];
276 $_feed_group[] = '%feed%';
277
278 $this->add_rewrite_rule( $this->make_regex( $_feed_regex ), $this->make_query( $_feed_group ) );
279
280 $this->add_rewrite_rule(
281 $this->make_regex( $_normalized_structure['raw'] ),
282 $this->make_query( $_normalized_structure['group'] )
283 );
284
285 $this->add_rewrite_rule( '^' . $base . '/(feed|rdf|rss|rss2|atom)/?$', 'index.php?post_type=docs&feed=$matches[1]' );
286 }
287
288 public function add_rewrite_rule( $regex, $query, $after = 'top' ) {
289 add_rewrite_rule( $regex, $query, $after );
290 }
291
292 public function docs_type_rewrite( $rewrite, $slug ) {
293 $permalink = betterdocs()->settings->get( 'permalink_structure', 'docs/' );
294 $permalink_structure = $this->permalink_structure( $permalink, 'array' );
295 $permalink = apply_filters( 'betterdocs_docs_type_rewrite_permalink', $permalink, $slug, $permalink_structure );
296
297 /**
298 * TODO: Let's re-check later for optimization.
299 */
300 if ( ! betterdocs()->is_pro_active() ) {
301 $permalink = $this->remove_knowledge_base_placeholder( $permalink );
302 }
303
304 return apply_filters(
305 'betterdocs_docs_rewrite',
306 [
307 'slug' => trim( $permalink, '/' ),
308 'with_front' => false
309 ],
310 $slug
311 );
312 }
313 }
314