PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.5.5
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.5.5
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.5.5, at includes/Core/Rewrite.php

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