PluginProbe ʕ •ᴥ•ʔ
Permalink Manager Lite / 2.2.20
Permalink Manager Lite v2.2.20
2.5.4 2.5.3.4 2.2.18 2.2.19.2 2.2.19.3 2.2.19.3.1 2.2.2 2.2.20 2.2.20.1 2.2.20.3 2.2.4 2.2.5 2.2.6 2.2.7.2 2.2.7.3 2.2.7.5 2.2.7.6 2.2.8.4 2.2.8.5 2.2.8.6 2.2.8.7 2.2.8.9 2.2.9.1 2.2.9.2 2.2.9.2.1 2.2.9.3 2.2.9.4 2.2.9.6 2.2.9.7 2.2.9.9 2.3.0 2.3.1.1 2.4.0 2.4.1 2.4.1.2 2.4.1.3 2.4.1.4 2.4.1.5 2.4.1.6 2.4.2 2.4.2.1 2.4.3 2.4.3.1 2.4.3.2 2.4.3.3 2.4.3.4 2.4.4 2.4.4.1 2.4.4.2 2.4.4.3 2.5.0 2.5.1 2.5.1.1 2.5.1.2 2.5.1.3 2.5.1.4 2.5.2 2.5.2.1 2.5.2.2 2.5.2.3 2.5.2.4 2.5.3 2.5.3.1 2.5.3.2 2.5.3.3 trunk 0.2 0.3 0.3.1 0.3.2 0.3.3 0.3.4 0.4 0.4.1 0.4.2 0.4.3 0.4.4 0.4.6 0.4.7 0.4.8 0.4.9 0.5.3 0.5.4 1.0.0 1.0.1 1.0.4 1.1.0 1.1.1 1.1.2 1.11.6.3 2.0.0 2.0.3 2.0.4 2.0.4.3 2.0.5.1 2.0.5.2 2.0.5.3 2.0.5.3.1 2.0.5.4 2.0.5.4a 2.0.5.5 2.0.5.6 2.0.5.6.1 2.0.5.7 2.0.5.9a 2.0.6.2.1 2.0.6.2a 2.0.6.3 2.1.0 2.1.1 2.1.2.4 2.2.0 2.2.1.1 2.2.1.2 2.2.11 2.2.12 2.2.13.1 2.2.14 2.2.15.1 2.2.16 2.2.17
permalink-manager / includes / core / permalink-manager-core-functions.php
permalink-manager / includes / core Last commit date
permalink-manager-actions.php 3 years ago permalink-manager-admin-functions.php 3 years ago permalink-manager-core-functions.php 3 years ago permalink-manager-debug.php 3 years ago permalink-manager-gutenberg.php 3 years ago permalink-manager-helper-functions.php 3 years ago permalink-manager-language-plugins.php 3 years ago permalink-manager-third-parties.php 3 years ago permalink-manager-uri-functions-post.php 3 years ago permalink-manager-uri-functions.php 3 years ago
permalink-manager-core-functions.php
890 lines
1 <?php
2
3 /**
4 * Core functions
5 */
6 class Permalink_Manager_Core_Functions extends Permalink_Manager_Class {
7
8 public function __construct() {
9 add_action( 'init', array($this, 'init_hooks'), 99);
10 }
11
12 function init_hooks() {
13 global $permalink_manager_options;
14
15 // Trailing slashes
16 add_filter( 'permalink_manager_filter_final_term_permalink', array($this, 'control_trailing_slashes'), 9);
17 add_filter( 'permalink_manager_filter_final_post_permalink', array($this, 'control_trailing_slashes'), 9);
18 add_filter( 'permalink_manager_filter_post_sample_uri', array($this, 'control_trailing_slashes'), 9);
19 add_filter( 'wpseo_canonical', array($this, 'control_trailing_slashes'), 9);
20 add_filter( 'wpseo_opengraph_url', array($this, 'control_trailing_slashes'), 9);
21 add_filter( 'paginate_links', array($this, 'control_trailing_slashes'), 9);
22
23 /**
24 * Detect & canonical URL/redirect functions
25 */
26 // Do not trigger in back-end
27 if(is_admin()) { return false; }
28
29 // Do not trigger if Customizer is loaded
30 if(function_exists('is_customize_preview') && is_customize_preview()) { return false; }
31
32 // Use the URIs set in this plugin
33 add_filter( 'request', array($this, 'detect_post'), 0, 1 );
34
35 // Redirect from old URIs to new URIs + adjust canonical redirect settings
36 add_action( 'template_redirect', array($this, 'new_uri_redirect_and_404'), 1);
37 add_action( 'wp', array($this, 'adjust_canonical_redirect'), 1);
38
39 // Case insensitive permalinks
40 if(!empty($permalink_manager_options['general']['case_insensitive_permalinks'])) {
41 add_action( 'parse_request', array($this, 'case_insensitive_permalinks'), 0);
42 }
43 // Force 404 on non-existing pagination pages
44 if(!empty($permalink_manager_options['general']['pagination_redirect'])) {
45 add_action( 'wp', array($this, 'fix_pagination_pages'), 0);
46 }
47 }
48
49 /**
50 * The most important Permalink Manager function
51 */
52 public static function detect_post($query, $request_url = false, $return_object = false) {
53 global $wpdb, $wp, $wp_rewrite, $permalink_manager, $permalink_manager_uris, $wp_filter, $permalink_manager_options, $pm_query;
54
55 // Check if the array with custom URIs is set
56 if(!(is_array($permalink_manager_uris))) return $query;
57
58 // Used in debug mode & endpoints
59 $old_query = $query;
60
61 /**
62 * 1. Prepare URL and check if it is correct (make sure that both requested URL & home_url share the same protoocl and get rid of www prefix)
63 */
64 $request_url = (!empty($request_url)) ? parse_url($request_url, PHP_URL_PATH) : $_SERVER['REQUEST_URI'];
65 $request_url = strtok($request_url, "?");
66
67 // Make sure that either $_SERVER['SERVER_NAME'] or $_SERVER['HTTP_HOST'] are set
68 if(empty($_SERVER['HTTP_HOST']) && empty($_SERVER['SERVER_NAME'])) { return $query; }
69
70 $http_host = (!empty($_SERVER['HTTP_HOST'])) ? $_SERVER['HTTP_HOST'] : preg_replace('/www\./i', '', $_SERVER['SERVER_NAME']);
71 $request_url = sprintf("http://%s%s", str_replace("www.", "", $http_host), $request_url);
72 $raw_home_url = trim(get_option('home'));
73 $home_url = preg_replace("/http(s)?:\/\/(www\.)?(.+?)\/?$/", "http://$3", $raw_home_url);
74
75 if(filter_var($request_url, FILTER_VALIDATE_URL)) {
76 // Check if "Deep Detect" is enabled
77 $deep_detect_enabled = apply_filters('permalink_manager_deep_uri_detect', true);
78
79 // Sanitize the URL
80 // $request_url = filter_var($request_url, FILTER_SANITIZE_URL);
81
82 // Keep only the URI
83 $request_url = str_replace($home_url, "", $request_url);
84
85 // Hotfix for language plugins
86 if(filter_var($request_url, FILTER_VALIDATE_URL)) {
87 $request_url = parse_url($request_url, PHP_URL_PATH);
88 }
89
90 $request_url = trim($request_url, "/");
91
92 // Get all the endpoints & pattern
93 $endpoints = Permalink_Manager_Helper_Functions::get_endpoints();
94 $pattern = "/^(.+?)(?|\/({$endpoints})(?|\/(.*)|$)|\/()([\d]+)\/?)?$/i";
95
96 // Use default REGEX to detect post
97 preg_match($pattern, $request_url, $regex_parts);
98 $uri_parts['lang'] = false;
99 $uri_parts['uri'] = (!empty($regex_parts[1])) ? $regex_parts[1] : "";
100 $uri_parts['endpoint'] = (!empty($regex_parts[2])) ? $regex_parts[2] : "";
101 $uri_parts['endpoint_value'] = (!empty($regex_parts[3])) ? $regex_parts[3] : "";
102
103 // Allow to filter the results by third-parties + store the URI parts with $pm_query global
104 $uri_parts = apply_filters('permalink_manager_detect_uri', $uri_parts, $request_url, $endpoints);
105
106 // Support comment pages
107 preg_match("/(.*)\/{$wp_rewrite->comments_pagination_base}-([\d]+)/", $uri_parts['uri'], $regex_parts);
108 if(!empty($regex_parts[2])) {
109 $uri_parts['uri'] = $regex_parts[1];
110 $uri_parts['endpoint'] = 'cpage';
111 $uri_parts['endpoint_value'] = $regex_parts[2];
112 }
113
114 // Support pagination endpoint
115 if($uri_parts['endpoint'] == $wp_rewrite->pagination_base) {
116 $uri_parts['endpoint'] = 'page';
117 }
118
119 // Stop the function if $uri_parts is empty
120 if(empty($uri_parts)) return $query;
121
122 // Store the URI parts in a separate global variable
123 $pm_query = $uri_parts;
124
125 // Get the URI parts from REGEX parts
126 $lang = $uri_parts['lang'];
127 $uri = $uri_parts['uri'];
128 $endpoint = $uri_parts['endpoint'];
129 $endpoint_value = $uri_parts['endpoint_value'];
130
131 // Trim slashes
132 $uri = trim($uri, "/");
133
134 // Ignore URLs with no URI grabbed
135 if(empty($uri)) return $query;
136
137 // Store an array with custom permalinks in a separate variable
138 $all_uris = $permalink_manager_uris;
139
140 // Check what content type should be loaded in case of duplicate ("posts" or "terms")
141 $duplicates_priority = apply_filters('permalink_manager_duplicates_priority', false);
142 if($duplicates_priority !== false) {
143 $uri_count = array_count_values($all_uris);
144
145 foreach($uri_count as $duplicated_uri => $count) {
146 if($count <= 1) { continue; }
147
148 $duplicates_ids = array_keys($all_uris, $duplicated_uri);
149
150 foreach($duplicates_ids as $id) {
151 if($duplicates_priority == 'posts' && !is_numeric($id)) {
152 unset($all_uris[$id]);
153 } else if($duplicates_priority !== 'posts' && is_numeric($id)) {
154 unset($all_uris[$id]);
155 }
156 }
157 }
158 }
159
160 // Exclude draft posts
161 $exclude_drafts = (isset($permalink_manager_options['general']['ignore_drafts'])) ? $permalink_manager_options['general']['ignore_drafts'] : false;
162 $exclude_drafts = apply_filters('permalink_manager_exclude_drafts', $exclude_drafts);
163
164 if($exclude_drafts) {
165 $post_ids = $wpdb->get_col("SELECT DISTINCT ID FROM {$wpdb->posts} AS p WHERE p.post_status = 'draft' ORDER BY ID DESC");
166 if(!empty($post_ids)) {
167 foreach($post_ids as $post_id) {
168 unset($permalink_manager_uris[$post_id]);
169 }
170 }
171 }
172
173 // Flip array for better performance
174 $all_uris = array_flip($all_uris);
175
176 // Attempt 1.
177 // Find the element ID
178 $element_id = isset($all_uris[$uri]) ? $all_uris[$uri] : false;
179
180 // Atempt 2.
181 // Decode both request URI & URIs array & make them lowercase (and save in a separate variable)
182 if(empty($element_id)) {
183 $uri = strtolower(urldecode($uri));
184
185 foreach($all_uris as $raw_uri => $uri_id) {
186 $raw_uri = urldecode($raw_uri);
187 $all_uris[$raw_uri] = $uri_id;
188 }
189
190 // Convert array keys lowercase
191 $all_uris = array_change_key_case($all_uris, CASE_LOWER);
192
193 $element_id = isset($all_uris[$uri]) ? $all_uris[$uri] : $element_id;
194 }
195
196 // Atempt 3.
197 // Check again in case someone used post/tax IDs instead of slugs
198 if($deep_detect_enabled && is_numeric($endpoint_value) && isset($all_uris["{$uri}/{$endpoint_value}"])) {
199 $element_id = $all_uris["{$uri}/{$endpoint_value}"];
200 $endpoint_value = $endpoint = "";
201 }
202
203 // Atempt 4.
204 // Check again for attachment custom URIs
205 if(empty($element_id) && isset($old_query['attachment'])) {
206 $element_id = isset($all_uris["{$uri}/{$endpoint}/{$endpoint_value}"]) ? $all_uris["{$uri}/{$endpoint}/{$endpoint_value}"] : $element_id;
207
208 if($element_id) {
209 $endpoint_value = $endpoint = "";
210 }
211 }
212
213 // Allow to filter the item_id by third-parties after initial detection
214 $element_id = apply_filters('permalink_manager_detected_element_id', $element_id, $uri_parts, $request_url);
215
216 // Clear the original query before it is filtered
217 $query = ($element_id) ? array() : $query;
218
219 /**
220 * 3A. Custom URI assigned to taxonomy
221 */
222 if(strpos($element_id, 'tax-') !== false) {
223 // Remove the "tax-" prefix
224 $term_id = intval(preg_replace("/[^0-9]/", "", $element_id));
225
226 // Filter detected post ID
227 $term_id = apply_filters('permalink_manager_detected_term_id', intval($term_id), $uri_parts, true);
228
229 // Get the variables to filter wp_query and double-check if taxonomy exists
230 $term = get_term($term_id);
231 $term_taxonomy = (!empty($term->taxonomy)) ? $term->taxonomy : false;
232
233 // Check if term is allowed
234 $disabled = ($term_taxonomy && Permalink_Manager_Helper_Functions::is_term_excluded($term)) ? true : false;
235
236 // Proceed only if the term is not removed and its taxonomy is not disabled
237 if(!$disabled && $term_taxonomy) {
238 // Get some term data
239 if($term_taxonomy == 'category') {
240 $query_parameter = 'category_name';
241 } else if($term_taxonomy == 'post_tag') {
242 $query_parameter = 'tag';
243 } else {
244 $query["taxonomy"] = $term_taxonomy;
245 $query_parameter = $term_taxonomy;
246 }
247 $term_ancestors = get_ancestors($term_id, $term_taxonomy);
248 $final_uri = $term->slug;
249
250 // Fix for hierarchical terms
251 if(!empty($term_ancestors)) {
252 foreach ($term_ancestors as $parent_id) {
253 $parent = get_term((int) $parent_id, $term_taxonomy);
254 if(!empty($parent->slug)) {
255 $final_uri = $parent->slug . '/' . $final_uri;
256 }
257 }
258 }
259
260 //$query["term"] = $final_uri;
261 $query["term"] = $term->slug;
262 //$query[$query_parameter] = $final_uri;
263 $query[$query_parameter] = $term->slug;
264 } else if($disabled) {
265 $broken_uri = true;
266 $query = $old_query;
267 } else {
268 $query = $old_query;
269 }
270 }
271 /**
272 * 3B. Custom URI assigned to post/page/cpt item
273 */
274 else if(isset($element_id) && is_numeric($element_id)) {
275 // Fix for revisions
276 $is_revision = wp_is_post_revision($element_id);
277 if($is_revision) {
278 $revision_id = $element_id;
279 $element_id = $is_revision;
280 }
281
282 // Filter detected post ID
283 $element_id = apply_filters('permalink_manager_detected_post_id', $element_id, $uri_parts);
284
285 $post_to_load = get_post($element_id);
286 $final_uri = (!empty($post_to_load->post_name)) ? $post_to_load->post_name : false;
287 $post_type = (!empty($post_to_load->post_type)) ? $post_to_load->post_type : false;
288
289 // Check if post is allowed
290 $disabled = ($post_type && Permalink_Manager_Helper_Functions::is_post_excluded($post_to_load)) ? true : false;
291
292 // Proceed only if the term is not removed and its taxonomy is not disabled
293 if(!$disabled && $post_type) {
294 $post_type_object = get_post_type_object($post_type);
295
296 // Fix for hierarchical CPT & pages
297 if(!(empty($post_to_load->ancestors)) && !empty($post_type_object->hierarchical)) {
298 foreach ($post_to_load->ancestors as $parent) {
299 $parent = get_post($parent);
300 if($parent && $parent->post_name) {
301 $final_uri = $parent->post_name . '/' . $final_uri;
302 }
303 }
304 }
305
306 // Alter the final query array
307 if($post_to_load->post_status == 'private' && (!is_user_logged_in() || current_user_can('read_private_posts', $element_id) !== true)) {
308 $element_id = null;
309 $query = $old_query;
310 } else if($post_to_load->post_status == 'draft' || empty($final_uri)) {
311 if(is_user_logged_in()) {
312 if($post_type == 'page') {
313 $query['page_id'] = $element_id;
314 } else {
315 $query['p'] = $element_id;
316 }
317
318 $query['preview'] = true;
319 $query['post_type'] = $post_type;
320 } else if($post_to_load->post_status == 'draft') {
321 $query['pagename'] = '-';
322 $query['error'] = '404';
323
324 $element_id = 0;
325 } else {
326 $query = $old_query;
327 }
328 } else if($post_type == 'page') {
329 $query['pagename'] = $final_uri;
330 // $query['post_type'] = $post_type;
331 } else if($post_type == 'post') {
332 $query['name'] = $final_uri;
333 } else if($post_type == 'attachment') {
334 $query['attachment'] = $final_uri;
335 } else {
336 // Get the query var
337 $query_var = (!empty($post_type_object->query_var)) ? $post_type_object->query_var : $post_type;
338
339 $query['name'] = $final_uri;
340 $query['post_type'] = $post_type;
341 $query[$query_var] = $final_uri;
342 }
343 } else if($disabled) {
344 $broken_uri = true;
345 $query = $old_query;
346 } else {
347 $query = $old_query;
348 }
349 }
350
351 /**
352 * 4. Auto-remove removed term custom URI & redirects (works if enabled in plugin settings)
353 */
354 if(!empty($broken_uri) && (!empty($permalink_manager_options['general']['auto_fix_duplicates'])) && $permalink_manager_options['general']['auto_fix_duplicates'] == 1) {
355 // Do not trigger if WP Rocket cache plugin is turned on
356 if(!defined('WP_ROCKET_VERSION') && is_array($permalink_manager_uris)) {
357 $broken_element_id = (!empty($revision_id)) ? $revision_id : $element_id;
358 $remove_broken_uri = (!empty($broken_element_id)) ? Permalink_Manager_Actions::force_clear_single_element_uris_and_redirects($broken_element_id) : '';
359
360 // Reload page if success
361 if($remove_broken_uri && !headers_sent()) {
362 header("Refresh:0");
363 exit();
364 }
365 }
366 }
367
368 /**
369 * 5A. Endpoints
370 */
371 if(!empty($element_id) && empty($disabled) && (!empty($endpoint) || !empty($endpoint_value))) {
372 if(is_array($endpoint)) {
373 foreach($endpoint as $endpoint_name => $endpoint_value) {
374 $query[$endpoint_name] = $endpoint_value;
375 }
376 } else if($endpoint == 'feed') {
377 $query[$endpoint] = 'feed';
378 } else if($endpoint == 'embed') {
379 $query[$endpoint] = true;
380 } else if($endpoint == 'page') {
381 $endpoint = 'paged';
382 if(is_numeric($endpoint_value)) {
383 $query[$endpoint] = $endpoint_value;
384 } else {
385 $query = $old_query;
386 }
387 } else if($endpoint == 'trackback') {
388 $endpoint = 'tb';
389 $query[$endpoint] = 1;
390 } else if(empty($endpoint) && is_numeric($endpoint_value)) {
391 $query['page'] = $endpoint_value;
392 } else {
393 $query[$endpoint] = $endpoint_value;
394 }
395
396 // Fix for attachments
397 if(!empty($query['attachment'])) {
398 $query = array('attachment' => $query['attachment'], 'do_not_redirect' => 1);
399 }
400 }
401
402 /**
403 * 5B. Endpoints - check if any endpoint is set with $_GET parameter
404 */
405 if(!empty($element_id) && $deep_detect_enabled && !empty($_GET)) {
406 $get_endpoints = array_intersect($wp->public_query_vars, array_keys($_GET));
407
408 if(!empty($get_endpoints)) {
409 // Append query vars from $_GET parameters
410 foreach($get_endpoints as $endpoint) {
411 // Numeric endpoints
412 $endpoint_value = (in_array($endpoint, array('page', 'paged', 'attachment_id'))) ? filter_var($_GET[$endpoint], FILTER_SANITIZE_NUMBER_INT) : $_GET[$endpoint];
413
414 // Ignore page endpoint if its value is 1
415 if(in_array($endpoint, array('page', 'paged')) && $endpoint_value == 1) { continue; }
416
417 // Replace whitespaces with '+' (for YITH WooCommerce Ajax Product Filter URLs only) and sanitize the value
418 $endpoint_value = (isset($_GET['yith_wcan'])) ? preg_replace('/\s+/', '+', $endpoint_value) : $endpoint_value;
419 $query[$endpoint] = sanitize_text_field($endpoint_value);
420 }
421 }
422 }
423
424 /**
425 * 6. Set global with detected item id
426 */
427 if(!empty($element_id) && empty($disabled)) {
428 $pm_query['id'] = $element_id;
429
430 // Make the redirects more clever - see new_uri_redirect_and_404() method
431 $query['do_not_redirect'] = 1;
432 }
433 }
434
435 /**
436 * 7. Debug data
437 */
438 if(!empty($term_taxonomy)) {
439 $content_type = "Taxonomy: {$term_taxonomy}";
440 } else if(!empty($post_type)) {
441 $content_type = "Post type: {$post_type}";
442 } else {
443 $content_type = '';
444 }
445 $uri_parts = (!empty($uri_parts)) ? $uri_parts : '';
446 $query = apply_filters('permalink_manager_filter_query', $query, $old_query, $uri_parts, $pm_query, $content_type);
447
448 if($return_object && !empty($term)) {
449 return $term;
450 } else if($return_object && !empty($post_to_load)) {
451 return $post_to_load;
452 } else {
453 return $query;
454 }
455 }
456
457 /**
458 * Trailing slash & remove BOM and double slashes
459 */
460 static function control_trailing_slashes($permalink) {
461 global $permalink_manager_options;
462
463 // Ignore empty permalinks
464 if(empty($permalink)) { return $permalink; }
465
466 // Keep the original permalink in a separate variable
467 $original_permalink = $permalink;
468
469 $trailing_slash_setting = (!empty($permalink_manager_options['general']['trailing_slashes'])) ? $permalink_manager_options['general']['trailing_slashes'] : "";
470
471 // Remove trailing slashes from URLs that end with file extension (eg. .html)
472 if(preg_match('/(http(?:s)?:\/\/(?:[^\/]+)\/.*\.([a-zA-Z]{3,4}))\/?$/', $permalink)) {
473 $permalink = preg_replace('/^(?!http(?:s):\/\/[^\/]+\/$)(.+?)([\/]*)(\[\?\#][^\/]+|$)/', '$1$3', $permalink); // Instead of untrailingslashit()
474 } else {
475 // Add trailing slashes
476 if(in_array($trailing_slash_setting, array(1, 10))) {
477 $permalink = preg_replace('/(.+?)([\/]*)(\[\?\#][^\/]+|$)/', '$1/$3', $permalink); // Instead of trailingslashit()
478 }
479 // Remove trailing slashes
480 else if(in_array($trailing_slash_setting, array(2, 20))) {
481 $permalink = preg_replace('/(.+?)([\/]*)(\[\?\#][^\/]+|$)/', '$1$3', $permalink); // Instead of untrailingslashit()
482 }
483 // Default settings
484 else {
485 $permalink = user_trailingslashit($permalink);
486 }
487 }
488
489 // Remove double slashes
490 $permalink = preg_replace('/([^:])(\/{2,})/', '$1/', $permalink);
491
492 // Remove trailing slashes from URLs that end with query string or anchors
493 $permalink = preg_replace('/([\?\#]{1}[^\/]+)([\/]+)$/', '$1', $permalink);
494
495 return apply_filters('permalink_manager_control_trailing_slashes', $permalink, $original_permalink);
496 }
497
498 /**
499 * Display 404 if requested page does not exist in pagination
500 */
501 function fix_pagination_pages() {
502 global $wp_query, $wp, $pm_query;
503
504 // 1. Get the queried object
505 $post = get_queried_object();
506 $post = (empty($post) && !empty($wp_query->post)) ? $wp_query->post : $post;
507
508 // 2. Check if post object is defined
509 if((!empty($post->post_type) && isset($post->post_content)) || (!empty($wp_query->max_num_pages))) {
510 // 2A. Check if pagination is detected
511 $current_page = (!empty($wp_query->query_vars['page'])) ? $wp_query->query_vars['page'] : 1;
512 $current_page = (empty($wp_query->query_vars['page']) && !empty($wp_query->query_vars['paged'])) ? $wp_query->query_vars['paged'] : $current_page;
513
514 // 2B. Count post pages
515 $post_content = (!empty($post->post_content)) ? $post->post_content : '';
516 $num_pages = (is_home() || is_archive() || is_search()) ? $wp_query->max_num_pages : substr_count(strtolower($post_content), '<!--nextpage-->') + 1;
517
518 // 2C. Remove 'do_not_redirect' parameter if the first page of content is requested to force canonical redirect
519 if(!empty($pm_query['id']) && is_numeric($pm_query['id']) && !empty($wp->query_vars['do_not_redirect']) && empty($pm_query['endpoint']) && $pm_query['endpoint_value'] == 1) {
520 $is_404 = true;
521 $wp->query_vars['do_not_redirect'] = 0;
522 set_query_var('p', $pm_query['id']);
523 } else {
524 $is_404 = ($current_page > 1 && ($current_page > $num_pages)) ? true : false;
525 }
526 }
527 // 3. Force 404 if no posts are loaded
528 else if(!empty($wp_query->query['paged']) && $wp_query->post_count == 0) {
529 $is_404 = true;
530 }
531 // 4. Force 404 if endpoint value is not set
532 else if(!empty($pm_query['endpoint']) && $pm_query['endpoint'] == 'page' && empty($pm_query['endpoint_value'])) {
533 $is_404 = true;
534 }
535
536 // 5. Block non-existent pages (Force 404 error)
537 if(!empty($is_404)) {
538 $wp_query->query = $wp_query->queried_object = $wp_query->queried_object_id = null;
539 $wp_query->set_404();
540 status_header(404);
541 nocache_headers();
542 $pm_query = '';
543 }
544 }
545
546 /**
547 * Redirects
548 */
549 function new_uri_redirect_and_404() {
550 global $wp_query, $wp, $wp_rewrite, $wpdb, $permalink_manager_uris, $permalink_manager_redirects, $permalink_manager_external_redirects, $permalink_manager_options, $pm_query;
551
552 // Get the redirection mode & trailing slashes settings
553 $redirect_mode = (!empty($permalink_manager_options['general']['redirect'])) ? $permalink_manager_options['general']['redirect'] : false;
554 $trailing_slashes_mode = (!empty($permalink_manager_options['general']['trailing_slashes'])) ? $permalink_manager_options['general']['trailing_slashes'] : false;
555 $trailing_slashes_redirect = (!empty($permalink_manager_options['general']['trailing_slashes_redirect'])) ? $permalink_manager_options['general']['trailing_slashes_redirect'] : false;
556 $extra_redirects = (!empty($permalink_manager_options['general']['extra_redirects'])) ? $permalink_manager_options['general']['extra_redirects'] : false;
557 $canonical_redirect = (!empty($permalink_manager_options['general']['canonical_redirect'])) ? $permalink_manager_options['general']['canonical_redirect'] : false;
558 $old_slug_redirect = (!empty($permalink_manager_options['general']['old_slug_redirect'])) ? $permalink_manager_options['general']['old_slug_redirect'] : false;
559 $endpoint_redirect = (!empty($permalink_manager_options['general']['endpoint_redirect'])) ? $permalink_manager_options['general']['endpoint_redirect'] : false;
560 $pagination_redirect = (!empty($permalink_manager_options['general']['pagination_redirect'])) ? $permalink_manager_options['general']['pagination_redirect'] : false;
561 $copy_query_redirect = (!empty($permalink_manager_options['general']['copy_query_redirect'])) ? $permalink_manager_options['general']['copy_query_redirect'] : false;
562 $redirect_type = '-';
563
564 // Get home URL
565 $home_url = rtrim(get_option('home'), "/");
566 $home_dir = parse_url($home_url, PHP_URL_PATH);
567
568 // Set up $correct_permalink variable
569 $correct_permalink = '';
570
571 // Get query string & URI
572 if(empty($_SERVER['REQUEST_URI'])) { return; }
573
574 $query_string = ($copy_query_redirect && !empty($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : '';
575 $old_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
576
577 // Fix for WP installed in directories (remove the directory name from the URI)
578 if(!empty($home_dir)) {
579 $home_dir_regex = preg_quote(trim($home_dir), "/");
580 $old_uri = preg_replace("/{$home_dir_regex}/", "", $old_uri, 1);
581 }
582
583 // Do not use custom redirects on author pages, search & front page
584 if(!is_author() && !is_front_page() && !is_home() && !is_feed() && !is_search() && empty($_GET['s'])) {
585 // Sometimes $wp_query indicates the wrong object if requested directly
586 $queried_object = get_queried_object();
587
588 // Unset 404 if custom URI is detected
589 if(isset($pm_query['id']) && (empty($queried_object->post_status) || $queried_object->post_status !== 'private')) {
590 $wp_query->is_404 = false;
591 }
592
593 /**
594 * 1A. External redirect
595 */
596 if(!empty($pm_query['id']) && !empty($permalink_manager_external_redirects[$pm_query['id']])) {
597 $external_url = $permalink_manager_external_redirects[$pm_query['id']];
598
599 if(filter_var($external_url, FILTER_VALIDATE_URL)) {
600 // Allow redirect
601 $wp_query->query_vars['do_not_redirect'] = 0;
602
603 wp_redirect($external_url, 301, PERMALINK_MANAGER_PLUGIN_NAME);
604 exit();
605 }
606 }
607
608 /**
609 * 1B. Custom redirects
610 */
611 if(empty($wp_query->query_vars['do_not_redirect']) && $extra_redirects && !empty($permalink_manager_redirects) && is_array($permalink_manager_redirects) && !empty($wp->request) && !empty($pm_query['uri'])) {
612 $uri = $pm_query['uri'];
613 $endpoint_value = $pm_query['endpoint_value'];
614
615 // Make sure that URIs with non-ASCII characters are also detected + Check the URLs that end with number
616 $decoded_url = urldecode($uri);
617 $endpoint_url = "{$uri}/{$endpoint_value}";
618
619 // Convert to lowercase to make case insensitive
620 $force_lowercase = apply_filters('permalink_manager_force_lowercase_uris', true);
621
622 if($force_lowercase) {
623 $uri = strtolower($uri);
624 $decoded_url = strtolower($decoded_url);
625 $endpoint_url = strtolower($endpoint_url);
626 }
627
628 // Check if the URI is not assigned to any post/term's redirects
629 foreach($permalink_manager_redirects as $element => $redirects) {
630 if(!is_array($redirects)) { continue; }
631
632 if(in_array($uri, $redirects) || in_array($decoded_url, $redirects) || (is_numeric($endpoint_value) && in_array($endpoint_url, $redirects))) {
633 // Post is detected
634 if(is_numeric($element)) {
635 $correct_permalink = get_permalink($element);
636 }
637 // Term is detected
638 else {
639 $term_id = intval(preg_replace("/[^0-9]/", "", $element));
640 $correct_permalink = get_term_link($term_id);
641 }
642
643 // The custom redirect is found so there is no need to query the rest of array
644 continue;
645 }
646 }
647
648 $redirect_type = (!empty($correct_permalink)) ? 'custom_redirect' : $redirect_type;
649 }
650
651 // Ignore WP-Content links
652 if(!empty($_SERVER['REQUEST_URI']) && (strpos($_SERVER['REQUEST_URI'], '/wp-content') !== false)) { return false; }
653
654 /**
655 * 1C. Pagination redirect
656 */
657 if($pagination_redirect && ((isset($wp_query->query_vars['paged']) && $wp_query->query_vars['paged'] == 1) || (isset($wp_query->query_vars['page']) && $wp_query->query_vars['page'] == 1 && !empty($pm_query['endpoint_value'])))) {
658 $pm_query['endpoint'] = $pm_query['endpoint_value'] = '';
659 $wp_query->query_vars['do_not_redirect'] = 0;
660 }
661
662 /**
663 * 1D. Enhance native redirect
664 */
665 if($canonical_redirect && empty($wp_query->query_vars['do_not_redirect']) && !empty($queried_object) && empty($correct_permalink)) {
666 // Affect only posts with custom URI and old URIs
667 if(!empty($queried_object->ID) && isset($permalink_manager_uris[$queried_object->ID]) && empty($wp_query->query['preview'])) {
668 // Ignore posts with specific statuses
669 if(!(empty($queried_object->post_status)) && in_array($queried_object->post_status, array('draft', 'pending', 'auto-draft', 'future'))) {
670 return;
671 }
672
673 // Check if the post is excluded
674 if(Permalink_Manager_Helper_Functions::is_post_excluded($queried_object)) { return; }
675
676 // Get the real URL
677 $correct_permalink = get_permalink($queried_object->ID);
678 }
679 // Affect only terms with custom URI and old URIs
680 else if(!empty($queried_object->term_id) && isset($permalink_manager_uris["tax-{$queried_object->term_id}"]) && defined('PERMALINK_MANAGER_PRO')) {
681 // Check if the term is excluded
682 if(Permalink_Manager_Helper_Functions::is_term_excluded($queried_object)) { return; }
683
684 // Get the real URL
685 $correct_permalink = get_term_link($queried_object->term_id, $queried_object->taxonomy);
686 }
687
688 $redirect_type = (!empty($correct_permalink)) ? 'native_redirect' : $redirect_type;
689 }
690
691 /**
692 * 1E. Old slug redirect
693 */
694 if($old_slug_redirect && !empty($pm_query['uri']) && empty($wp_query->query_vars['do_not_redirect']) && is_404() && empty($correct_permalink)) {
695 $slug = basename($pm_query['uri']);
696
697 $post_id = $wpdb->get_var($wpdb->prepare("SELECT post_id from {$wpdb->postmeta} WHERE meta_key = '_wp_old_slug' AND meta_value = %s", $slug));
698 if(!empty($post_id)) {
699 $correct_permalink = get_permalink($post_id);
700 $redirect_type = 'old_slug_redirect';
701 }
702 }
703
704 /**
705 * 2. Check if URL contains duplicated slashes
706 */
707 if(!empty($old_uri) && ($old_uri !== '/') && preg_match('/\/{2,}/', $old_uri)) {
708 $new_uri = ltrim(preg_replace('/([^:])([\/]+)/', '$1/', $old_uri), '/');
709 $correct_permalink = sprintf("%s/%s", $home_url, $new_uri);
710
711 $redirect_type = ($redirect_type == '-') ? 'duplicated_slash_redirect' : $redirect_type;
712 }
713
714 /**
715 * 3. Prevent redirect loop
716 */
717 if(!empty($correct_permalink) && is_string($correct_permalink) && !empty($wp->request) && !empty($redirect_type) && !in_array($redirect_type, array('slash_redirect', 'duplicated_slash_redirect'))) {
718 $current_uri = trim($wp->request, "/");
719 $redirect_uri = trim(parse_url($correct_permalink, PHP_URL_PATH), "/");
720
721 $correct_permalink = ($redirect_uri == $current_uri) ? null : $correct_permalink;
722 }
723
724 /**
725 * 4. Add endpoints to redirect URL
726 */
727 if(!empty($correct_permalink) && $endpoint_redirect && ($redirect_type !== 'slash_redirect') && (!empty($pm_query['endpoint_value']) || !empty($pm_query['endpoint']))) {
728 $endpoint_value = $pm_query['endpoint_value'];
729
730 if(empty($pm_query['endpoint']) && is_numeric($endpoint_value)) {
731 $correct_permalink = sprintf("%s/%d", trim($correct_permalink, "/"), $endpoint_value);
732 } else if(isset($pm_query['endpoint']) && !empty($endpoint_value)) {
733 if($pm_query['endpoint'] == 'cpage') {
734 $correct_permalink = sprintf("%s/%s-%s", trim($correct_permalink, "/"), $wp_rewrite->comments_pagination_base, $endpoint_value);
735 } else {
736 $correct_permalink = sprintf("%s/%s/%s", trim($correct_permalink, "/"), $pm_query['endpoint'], $endpoint_value);
737 }
738 } else {
739 $correct_permalink = sprintf("%s/%s", trim($correct_permalink, "/"), $pm_query['endpoint']);
740 }
741 }
742 } else {
743 $queried_object = '-';
744 }
745
746 /**
747 * 5. Check trailing slashes (ignore links with query parameters)
748 */
749 if($trailing_slashes_mode && $trailing_slashes_redirect && empty($correct_permalink) && empty($query_string) && !empty($old_uri) && !is_front_page()) {
750 // Check if $old_uri ends with slash or not
751 $ends_with_slash = (substr($old_uri, -1) == "/") ? true : false;
752 $trailing_slashes_mode = (preg_match("/.*\.([a-zA-Z]{3,4})\/?$/", $old_uri) && $trailing_slashes_mode == 1) ? 2 : $trailing_slashes_mode;
753
754 // Ignore empty URIs
755 if($old_uri != "/") {
756 $new_uri = trim($old_uri, '/');
757
758 // 2A. Force trailing slashes
759 if($trailing_slashes_mode == 1 && $ends_with_slash == false) {
760 $correct_permalink = sprintf("%s/%s/", $home_url, $new_uri);
761 }
762 // 2B. Remove trailing slashes
763 else if($trailing_slashes_mode == 2 && $ends_with_slash == true) {
764 $correct_permalink = sprintf("%s/%s", $home_url, $new_uri);
765 }
766 // 2C. Remove duplicated trailing slashes
767 else if($trailing_slashes_mode == 1 && preg_match('/[\/]{2,}$/', $old_uri)) {
768 $correct_permalink = sprintf("%s/%s/", $home_url, $new_uri);
769 }
770 }
771
772 $redirect_type = (!empty($correct_permalink)) ? 'slash_redirect' : '-';
773 }
774
775 /**
776 * 6. WWW prefix | SSL mismatch redirect
777 */
778 if(!empty($permalink_manager_options['general']['sslwww_redirect'])) {
779 $home_url_has_www = (strpos($home_url, 'www.') !== false) ? true : false;
780 $requested_url_has_www = (strpos($_SERVER['HTTP_HOST'], 'www.') !== false) ? true : false;
781 $home_url_has_ssl = (strpos($home_url, 'https') !== false) ? true : false;
782 $requested_url_has_ssl = is_ssl();
783
784 if(($home_url_has_www !== $requested_url_has_www) || ($home_url_has_ssl !== $requested_url_has_ssl)) {
785 $new_uri = ltrim($old_uri, '/');
786 $correct_permalink = sprintf("%s/%s", $home_url, $new_uri);
787
788 $redirect_type = 'www_redirect';
789 }
790 }
791
792 /**
793 * 7. Debug redirect
794 */
795 $correct_permalink = apply_filters('permalink_manager_filter_redirect', $correct_permalink, $redirect_type, $queried_object);
796
797 /**
798 * 8. Ignore default URIs (or do nothing if redirects are disabled)
799 */
800 if(!empty($correct_permalink) && is_string($correct_permalink) && !empty($redirect_mode)) {
801 // Allow redirect
802 $wp_query->query_vars['do_not_redirect'] = 0;
803
804 // Append query string
805 $correct_permalink = (!empty($query_string)) ? sprintf("%s?%s", strtok($correct_permalink, "?"), $query_string) : $correct_permalink;
806
807 // Adjust trailing slashes
808 $correct_permalink = self::control_trailing_slashes($correct_permalink);
809
810 // Prevent redirect loop
811 $rel_old_uri = wp_make_link_relative($old_uri);
812 $rel_correct_permalink = wp_make_link_relative($correct_permalink);
813
814 if($redirect_type === 'www_redirect' || $rel_old_uri !== $rel_correct_permalink) {
815 wp_safe_redirect($correct_permalink, $redirect_mode, PERMALINK_MANAGER_PLUGIN_NAME);
816 exit();
817 }
818 }
819 }
820
821 function adjust_canonical_redirect() {
822 global $permalink_manager_options, $permalink_manager_uris, $wp, $wp_rewrite;
823
824 // Adjust rewrite settings for trailing slashes
825 $trailing_slash_setting = (!empty($permalink_manager_options['general']['trailing_slashes'])) ? $permalink_manager_options['general']['trailing_slashes'] : "";
826 if(in_array($trailing_slash_setting, array(1, 10))) {
827 $wp_rewrite->use_trailing_slashes = true;
828 } else if(in_array($trailing_slash_setting, array(2, 20))) {
829 $wp_rewrite->use_trailing_slashes = false;
830 }
831
832 // Get endpoints
833 $endpoints = Permalink_Manager_Helper_Functions::get_endpoints();
834 $endpoints_array = ($endpoints) ? explode("|", $endpoints) : array();
835
836 // Check if any endpoint is called (fix for endpoints)
837 foreach($endpoints_array as $endpoint) {
838 if(!empty($wp->query_vars[$endpoint]) && !in_array($endpoint, array('attachment', 'page', 'paged', 'feed'))) {
839 $wp->query_vars['do_not_redirect'] = 1;
840 break;
841 }
842 }
843
844 // Do nothing for posts and terms without custom URIs (when canonical redirect is enabled)
845 if(is_singular() || is_tax() || is_category() || is_tag()) {
846 $element = get_queried_object();
847 if(!empty($element->ID)) {
848 $custom_uri = (!empty($permalink_manager_uris[$element->ID])) ? $permalink_manager_uris[$element->ID] : "";
849 } else if(!empty($element->term_id)) {
850 $custom_uri = (!empty($permalink_manager_uris["tax-{$element->term_id}"])) ? $permalink_manager_uris["tax-{$element->term_id}"] : "";
851 }
852 }
853
854 if(empty($permalink_manager_options['general']['canonical_redirect'])) {
855 remove_action('template_redirect', 'redirect_canonical');
856 }
857
858 if(empty($permalink_manager_options['general']['old_slug_redirect'])) {
859 remove_action('template_redirect', 'wp_old_slug_redirect');
860 }
861
862 if(!empty($wp->query_vars['do_not_redirect'])) {
863 // RankMath
864 remove_action('template_redirect', 'do_redirection', 11);
865 remove_action('wp', 'do_redirection', 11);
866
867 // SEOPress
868 remove_action('template_redirect', 'seopress_category_redirect', 1);
869
870 remove_action('template_redirect', 'wp_old_slug_redirect');
871 remove_action('template_redirect', 'redirect_canonical');
872 add_filter('wpml_is_redirected', '__return_false', 99, 2);
873 add_filter('pll_check_canonical_url', '__return_false', 99, 2);
874 }
875 }
876
877 /**
878 * Case insensitive permalinks
879 */
880 function case_insensitive_permalinks() {
881 global $permalink_manager_options, $permalink_manager_uris;
882
883 if(!empty($_SERVER['REQUEST_URI'])) {
884 $_SERVER['REQUEST_URI'] = strtolower($_SERVER['REQUEST_URI']);
885 $permalink_manager_uris = array_map('strtolower', $permalink_manager_uris);
886 }
887 }
888
889 }
890