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