WP 404 Auto Redirect to Similar Post - Version 1.0

Version Description

  • Added: Log redirections in the /wp-content/debug.log file.
  • Added: Expose 'WP-404-Auto-Redirect' headers on 404 pages. (Admin).
  • Added: Groups & Engines Feature.
  • Added: Groups & Engines Documentation & Examples (developers).
  • Added: Groups & Engines Admin panel.
  • Added: Index.php file redirecting to root domain (avoid plugin folder file listing).
  • Added: action('wp404arsp/search/init', $query)
  • Added: filter('wp404arsp/search/group', $group, $query)
  • Added: filter('wp404arsp/search/query', $query)
  • Added: filter('wp404arsp/search/engine/{engine}', $result, $query, $group)
  • Added: filter('wp404arsp/search/results', $query)
  • Added: filter('wp404arsp/search/redirect', $redirect, $query)
  • Improvement: Core reworked from scratch for better extensibility.
  • Removed: 'Hooks' tab
Download this release

Release Info

Developer hwk-fr
Plugin Icon 128x128 WP 404 Auto Redirect to Similar Post
Version 1.0
Comparing to
See all releases

Code changes from version 0.9.0.2 to 1.0

class/class-engines.php ADDED
@@ -0,0 +1,490 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if(!defined('ABSPATH'))
4
+ exit;
5
+
6
+ if(!class_exists('WP_404_Auto_Redirect_Engines')){
7
+
8
+ class WP_404_Auto_Redirect_Engines {
9
+
10
+ public $get_engines = array();
11
+
12
+ function __construct(){
13
+
14
+ // Engines: Register
15
+ add_action('wp404arsp/search/init', array($this, 'register_engines'), 1, 2);
16
+
17
+ // Engines: Results
18
+ add_filter('wp404arsp/search/engine/default_fix_url', array($this, 'engine_default_fix_url'), 1, 3);
19
+ add_filter('wp404arsp/search/engine/default_direct', array($this, 'engine_default_direct'), 1, 3);
20
+ add_filter('wp404arsp/search/engine/default_post', array($this, 'engine_default_post'), 1, 3);
21
+ add_filter('wp404arsp/search/engine/default_term', array($this, 'engine_default_term'), 1, 3);
22
+ add_filter('wp404arsp/search/engine/default_post_fallback', array($this, 'engine_default_post_fallback'), 1, 3);
23
+
24
+ }
25
+
26
+ function register_engines(){
27
+
28
+ $this->register_engine(array(
29
+ 'name' => 'Default: Fix URL',
30
+ 'slug' => 'default_fix_url',
31
+ 'weight' => 100,
32
+ 'primary' => true
33
+ ));
34
+
35
+ $this->register_engine(array(
36
+ 'name' => 'Default: Direct Match',
37
+ 'slug' => 'default_direct',
38
+ 'weight' => 100,
39
+ 'primary' => false
40
+ ));
41
+
42
+ $this->register_engine(array(
43
+ 'name' => 'Default: Search Post',
44
+ 'slug' => 'default_post',
45
+ 'weight' => 100,
46
+ 'primary' => true
47
+ ));
48
+
49
+ $this->register_engine(array(
50
+ 'name' => 'Default: Search Term',
51
+ 'slug' => 'default_term',
52
+ 'weight' => 100,
53
+ 'primary' => false
54
+ ));
55
+
56
+ $this->register_engine(array(
57
+ 'name' => 'Default: Post Fallback',
58
+ 'slug' => 'default_post_fallback',
59
+ 'weight' => 100,
60
+ 'primary' => true
61
+ ));
62
+
63
+ }
64
+
65
+ function register_engine($args){
66
+
67
+ $args = wp_parse_args($args, array(
68
+ 'name' => false,
69
+ 'slug' => false,
70
+ 'weight' => 100,
71
+ 'primary' => false
72
+ ));
73
+
74
+ if(!$args['name'])
75
+ return;
76
+
77
+ if(!$args['slug'])
78
+ $args['slug'] = wp404arsp_sanitize($args['name'], '_');
79
+
80
+ if(empty($args['slug']))
81
+ return;
82
+
83
+ $engine = apply_filters('wp404arsp/define/engine/' . $args['slug'], $args);
84
+ if(!$engine)
85
+ return;
86
+
87
+ $this->get_engines[] = $args;
88
+
89
+ }
90
+
91
+
92
+ /*
93
+ * Engines: Deregister
94
+ *
95
+ */
96
+
97
+ function deregister_engine($slug){
98
+
99
+ if(empty($this->get_engines))
100
+ return;
101
+
102
+ $reset = false;
103
+
104
+ // Engines
105
+ foreach($this->get_engines as $e => $engine){
106
+
107
+ if($engine['slug'] != $slug)
108
+ continue;
109
+
110
+ // Groups: Engine
111
+ wp404arsp_deregister_groups_engine($slug);
112
+
113
+ // Engine
114
+ unset($this->get_engines[$e]);
115
+ $reset = true;
116
+ break;
117
+
118
+ }
119
+
120
+ if($reset)
121
+ $this->get_engines = array_values($this->get_engines);
122
+
123
+ }
124
+
125
+ function get_engine_by_slug($slug){
126
+
127
+ if(empty($this->get_engines))
128
+ return false;
129
+
130
+ foreach($this->get_engines as $engine){
131
+ if($engine['slug'] != $slug)
132
+ continue;
133
+
134
+ return $engine;
135
+ }
136
+
137
+ return false;
138
+
139
+ }
140
+
141
+ function engine_exists($slug){
142
+
143
+ return $this->get_engine_by_slug($slug);
144
+
145
+ }
146
+
147
+ /*
148
+ * Engine: Fix URL
149
+ *
150
+ * Condition: If Pagination Regex exists in the Requested URL.
151
+ * If so, redirect to the same URL without the Pagination.
152
+ *
153
+ */
154
+
155
+ function engine_default_fix_url($result, $query, $group){
156
+
157
+ // Fix URL '/p=6' instead of '/?p=6'
158
+ if(preg_match('#/(?<param>p)=(?<val>[0-9]+)/?$#i', $query['request']['url'], $args)){
159
+
160
+ if(get_post($args['val'])){
161
+ return array(
162
+ 'url' => get_permalink($args['val']),
163
+ 'score' => 1,
164
+ 'why' => "Fix Requested URL. Sending redirection to the correct permalink."
165
+ );
166
+ }
167
+
168
+ }
169
+
170
+ // Fix Pagination
171
+ elseif(preg_match('#/(?<slug>page|paged)/(?<page>[0-9]+)/?$#i', $query['request']['url'], $pagination)){
172
+ $url = home_url() . str_replace($pagination[0], '', $query['request']['url']);
173
+
174
+ return array(
175
+ 'url' => $url,
176
+ 'score' => 1,
177
+ 'why' => "Pagination found in the Requested URL. Sending redirection to the same URL without Pagination."
178
+ );
179
+ }
180
+
181
+ // Pagination not found
182
+ return "No Fix to apply in the Requested URL.";
183
+
184
+ }
185
+
186
+
187
+ /*
188
+ * Engine: Direct Match
189
+ *
190
+ * Condition: None.
191
+ * Use get_page_by_path() to find the Post with the exact Name.
192
+ *
193
+ */
194
+
195
+ function engine_default_direct($result, $query, $group){
196
+
197
+ // No Post Types available
198
+ if(!$post_types = wp404arsp_get_post_types($query['settings']))
199
+ return "All Post Types are disabled in settings.";
200
+
201
+ // init Found
202
+ $found = false;
203
+
204
+ // Get keywords
205
+ $keywords = $query['request']['keywords']['array'];
206
+
207
+ // Add all keywords combined
208
+ $keywords[] = $query['request']['keywords']['all'];
209
+
210
+ foreach($keywords as $k){
211
+
212
+ // Not found: continue
213
+ if(!$post = get_page_by_path($k, 'object', $post_types))
214
+ continue;
215
+
216
+ $found = true;
217
+ $score = count(explode('-', $k));
218
+ break;
219
+
220
+ }
221
+
222
+ // Found
223
+ if($found){
224
+
225
+ // Found but post Status not 'published'. Stop direct match
226
+ if(get_post_status($post->ID) != 'publish'){
227
+
228
+ return "Post Status not published. Stop direct match.";
229
+
230
+ }
231
+
232
+ // Post Meta 'wp404arsp_no_redirect = 1'. Stop direct match
233
+ elseif($query['settings']['rules']['exclude']['post_meta'] && get_post_meta($post->ID, 'wp404arsp_no_redirect', true) == '1'){
234
+
235
+ return "Exluded Post Meta Set. Stop direct match.";
236
+
237
+ }
238
+
239
+ // Everything is Okay.
240
+ else{
241
+
242
+ return array(
243
+ 'score' => $score,
244
+ 'url' => get_permalink($post->ID),
245
+ 'why' => "Part of the requested URL already exist as Post of the Post Type: <strong>" . get_post_type($post->ID) . "</strong>"
246
+ );
247
+
248
+ }
249
+
250
+ }
251
+
252
+ // Not Found
253
+ else{
254
+
255
+ return "No Direct Match in any Post Types.";
256
+
257
+ }
258
+
259
+ }
260
+
261
+
262
+ /*
263
+ * Engine: Search Post
264
+ *
265
+ * Condition: None.
266
+ * Search similar Post in any available Post Types.
267
+ *
268
+ */
269
+
270
+ function engine_default_post($result, $query, $group){
271
+
272
+ // No Post Types available
273
+ if(!$post_types = wp404arsp_get_post_types($query['settings']))
274
+ return "All Post Types are disabled in settings.";
275
+
276
+ $wp_query = false;
277
+
278
+ // Post_Type found in WP_Query
279
+ if(!wp404arsp_is_empty($query['request']['wp_query']['post_type'])){
280
+
281
+ $wp_query = true;
282
+ $wp_query_post_type = $query['request']['wp_query']['post_type'];
283
+
284
+ // Post_Type found in WP_Query is disabled in settings. Disable this specific search
285
+ if(!in_array($wp_query_post_type, wp404arsp_get_post_types($query['settings'])))
286
+ $wp_query = false;
287
+
288
+ }
289
+
290
+ // Keywords
291
+ $keywords = explode('-', $query['request']['keywords']['all']);
292
+
293
+ // Search Args
294
+ $args = array(
295
+ 'keywords' => $keywords, // Add keywords
296
+ 'mode' => 'post' // Search for Post
297
+ );
298
+
299
+ if($wp_query)
300
+ $args['post_type'] = $wp_query_post_type; // Add specific Post Type Search
301
+
302
+ // Run Search
303
+ $search = wp404arsp_search($args, $query);
304
+
305
+ // Found
306
+ if($search['score'] > 0){
307
+
308
+ // Reason
309
+ $why = "Similar Post of Post Type <strong>" . get_post_type($search['post_id']) . "</strong> was found.";
310
+
311
+ // Change reason if Post_Type found in WP_Query
312
+ if($wp_query)
313
+ $why = "Similar Post found in the WP Query: Post Type <strong>" . $wp_query_post_type . "</strong>.";
314
+
315
+ // Return result
316
+ return array(
317
+ 'score' => $search['score'],
318
+ 'url' => get_permalink($search['post_id']),
319
+ 'why' => $why
320
+ );
321
+
322
+ }
323
+
324
+ // Not found
325
+ else{
326
+
327
+ // Reason
328
+ $why = "No similar Post found.";
329
+
330
+ // Change reason if Post_Type found in WP_Query
331
+ if($wp_query){
332
+ $why = "No Post found in the WP Query: Post Type <strong>" . $wp_query_post_type . "</strong>.";
333
+
334
+ // Set Query Var for Engine: WP Query Fallback
335
+ set_query_var('wp404arsp_engine_default_post', $wp_query_post_type);
336
+
337
+ }
338
+
339
+ return $why;
340
+
341
+ }
342
+ }
343
+
344
+
345
+ /*
346
+ * Engine: Search Term
347
+ *
348
+ * Condition: None.
349
+ * Search similar Term in any available Taxonomies.
350
+ *
351
+ */
352
+
353
+ function engine_default_term($result, $query, $group){
354
+
355
+ // Taxonomies Disabled
356
+ if($query['settings']['rules']['disable']['taxonomies'])
357
+ return "Taxonomies Disabled in settings.";
358
+
359
+ $wp_query = false;
360
+
361
+ // Taxonomy found in WP_Query
362
+ if(!wp404arsp_is_empty($query['request']['wp_query']['taxonomy'])){
363
+
364
+ $wp_query = true;
365
+ $wp_query_taxonomy = $query['request']['wp_query']['taxonomy'];
366
+
367
+ // Taxonomy found in WP_Query is disabled in settings. Disable this specific search
368
+ if(!in_array($wp_query_taxonomy, wp404arsp_get_taxonomies($query['settings'])))
369
+ $wp_query = false;
370
+
371
+ }
372
+
373
+ // Keywords
374
+ $keywords = explode('-', $query['request']['keywords']['all']);
375
+
376
+ // Search Args
377
+ $args = array(
378
+ 'keywords' => $keywords, // Add keywords
379
+ 'mode' => 'term' // Search for Term
380
+ );
381
+
382
+ if($wp_query)
383
+ $args['taxonomy'] = $wp_query_taxonomy; // Add specific Taxonomy Search
384
+
385
+ // Run Search
386
+ $search = wp404arsp_search($args, $query);
387
+
388
+ // Found
389
+ if($search['score'] > 0){
390
+
391
+ // Get Term
392
+ $term = get_term($search['term_id']);
393
+
394
+ // Reason
395
+ $why = "Similar Term was found in the Taxonomy <strong>" . $term->taxonomy . "</strong>.";
396
+
397
+ // Change reason if Taxonomy found in WP_Query
398
+ if($wp_query)
399
+ $why = "Similar Term found in the WP Query: Taxonomy <strong>" . $wp_query_taxonomy . "</strong>.";
400
+
401
+ return array(
402
+ 'score' => $search['score'],
403
+ 'url' => get_term_link($search['term_id']),
404
+ 'why' => $why
405
+ );
406
+
407
+ }
408
+
409
+ // Not found
410
+ else{
411
+
412
+ // Reason
413
+ $why = "No similar Term was found in Taxonomies.";
414
+
415
+ // Change reason if Taxonomy found in WP_Query
416
+ if($wp_query)
417
+ $why = "No Term found in the WP Query: Taxonomy <strong>" . $wp_query_taxonomy . "</strong>.";
418
+
419
+ return $why;
420
+
421
+ }
422
+
423
+ }
424
+
425
+
426
+ /*
427
+ * Engine: Post Fallback
428
+ *
429
+ * Condition: If the Post Type is set in the current WP_Query AND the engine "WP Query: Post Type" failed to found a similar Post inside it.
430
+ * If the engine "WP Query: Post Type" failed, it will set a Query Var 'wp404arsp_engine_wpq_pt'. This engine will use it to fallback to the Post Type Archive.
431
+ *
432
+ */
433
+
434
+ function engine_default_post_fallback($result, $query, $group){
435
+
436
+ // Query Var: 'wp404arsp_engine_default_post' set by the engine "Default: WP Query" because it failed.
437
+ $post_type = get_query_var('wp404arsp_engine_default_post', false);
438
+
439
+ if($post_type){
440
+
441
+ // Get Post Type Archive Link
442
+ $url = get_post_type_archive_link($post_type);
443
+ if(empty($url))
444
+ $url = home_url();
445
+
446
+ return array(
447
+ 'url' => $url,
448
+ 'score' => 1,
449
+ 'why' => "WP Query Fallback: Redirecting to Post Type <strong>" . $post_type . "</strong> Archive."
450
+ );
451
+
452
+ }
453
+
454
+ // No Query Var. Stop.
455
+ else{
456
+
457
+ return "No Post Type in the WP Query.";
458
+
459
+ }
460
+ }
461
+
462
+ }
463
+
464
+ wp404arsp()->engines = new WP_404_Auto_Redirect_Engines();
465
+
466
+ }
467
+
468
+ function wp404arsp_register_engine($engine){
469
+
470
+ return wp404arsp()->engines->register_engine($engine);
471
+
472
+ }
473
+
474
+ function wp404arsp_deregister_engine($slug){
475
+
476
+ return wp404arsp()->engines->deregister_engine($slug);
477
+
478
+ }
479
+
480
+ function wp404arsp_get_engine_by_slug($slug){
481
+
482
+ return wp404arsp()->engines->get_engine_by_slug($slug);
483
+
484
+ }
485
+
486
+ function wp404arsp_engine_exists($slug){
487
+
488
+ return wp404arsp()->engines->engine_exists($slug);
489
+
490
+ }
class/class-groups.php ADDED
@@ -0,0 +1,341 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if(!defined('ABSPATH'))
4
+ exit;
5
+
6
+ if(!class_exists('WP_404_Auto_Redirect_Groups')){
7
+
8
+ class WP_404_Auto_Redirect_Groups {
9
+
10
+ public $get_groups = array();
11
+
12
+ function __construct(){
13
+
14
+ // Groups: Register
15
+ add_action('wp404arsp/search/init', array($this, 'register_groups'), 1, 2);
16
+
17
+ }
18
+
19
+ function register_groups(){
20
+
21
+ $this->register_group(array(
22
+ 'name' => 'Default',
23
+ 'slug' => 'default',
24
+ 'custom' => false,
25
+ 'engines' => array(
26
+ 'default_fix_url',
27
+ 'default_direct',
28
+ 'default_post',
29
+ 'default_term',
30
+ 'default_post_fallback',
31
+ )
32
+ ));
33
+
34
+ }
35
+
36
+
37
+ /*
38
+ * Group: Register
39
+ *
40
+ */
41
+
42
+ function register_group($args){
43
+
44
+ $args = wp_parse_args($args, array(
45
+ 'name' => false,
46
+ 'slug' => wp404arsp_sanitize($args['name'], '_'),
47
+ 'custom' => true,
48
+ 'engines' => array()
49
+ ));
50
+
51
+ if(empty($args['name']) || empty($args['slug']))
52
+ return;
53
+
54
+ if($args['slug'] == 'default' && $args['custom'])
55
+ return;
56
+
57
+ if(!$args['custom'])
58
+ unset($args['custom']);
59
+
60
+ $group = apply_filters('wp404arsp/define/group/' . $args['slug'], $args);
61
+ if(!$group)
62
+ return;
63
+
64
+ if(!empty($group['engines'])){
65
+
66
+ $reset = false;
67
+ foreach($group['engines'] as $e => $engine){
68
+ if(wp404arsp_engine_exists($engine))
69
+ continue;
70
+
71
+ unset($group['engines'][$e]);
72
+ $reset = true;
73
+ }
74
+
75
+ if($reset)
76
+ $group['engines'] = array_values($group['engines']);
77
+
78
+ }
79
+
80
+ $this->get_groups[] = $group;
81
+
82
+ }
83
+
84
+
85
+ /*
86
+ * Group: Register
87
+ *
88
+ */
89
+
90
+ function register_group_engines($args){
91
+
92
+ $args = wp_parse_args($args, array(
93
+ 'group' => false,
94
+ 'engines' => array()
95
+ ));
96
+
97
+ if(!$args['group'] || empty($this->get_groups))
98
+ return;
99
+
100
+ foreach($this->get_groups as &$group){
101
+ if($group['slug'] != $args['group'])
102
+ continue;
103
+
104
+ $group['engines'] = $args['engines'];
105
+
106
+ if(empty($args['engines']))
107
+ break;
108
+
109
+ $reset = false;
110
+ foreach($args['engines'] as $e => $engine){
111
+ if(wp404arsp_engine_exists($engine))
112
+ continue;
113
+
114
+ unset($group['engines'][$e]);
115
+ $reset = true;
116
+ }
117
+
118
+ if($reset)
119
+ $group['engines'] = array_values($group['engines']);
120
+
121
+ }
122
+
123
+ }
124
+
125
+
126
+ /*
127
+ * Group: Deregister
128
+ *
129
+ */
130
+
131
+ function deregister_group($slug){
132
+
133
+ if(empty($this->get_groups) || $slug == 'default')
134
+ return;
135
+
136
+ $reset = false;
137
+
138
+ // Engines
139
+ foreach($this->get_groups as $g => $group){
140
+
141
+ if($group['slug'] != $slug)
142
+ continue;
143
+
144
+ // Engine
145
+ unset($this->get_groups[$g]);
146
+ $reset = true;
147
+ break;
148
+
149
+ }
150
+
151
+ if($reset)
152
+ $this->get_groups = array_values($this->get_groups);
153
+
154
+ }
155
+
156
+ /*
157
+ * One Group: Deregister 1 engine
158
+ *
159
+ */
160
+
161
+ function deregister_group_engine($args){
162
+
163
+ $args = wp_parse_args($args, array(
164
+ 'group' => false,
165
+ 'engine' => false
166
+ ));
167
+
168
+ if(!$args['group'] || !$args['engine'])
169
+ return;
170
+
171
+ foreach($this->get_groups as $g => $group){
172
+
173
+ if($group['slug'] != $args['group'] || empty($group['engines']))
174
+ continue;
175
+
176
+ $reset = false;
177
+
178
+ foreach($group['engines'] as $ge => $group_engine){
179
+
180
+ if($group_engine != $args['engine'])
181
+ continue;
182
+
183
+ unset($this->get_groups[$g]['engines'][$ge]);
184
+ $reset = true;
185
+ break;
186
+
187
+ }
188
+
189
+ if($reset)
190
+ $this->get_groups[$g]['engines'] = array_values($this->get_groups[$g]['engines']);
191
+
192
+ break;
193
+
194
+ }
195
+
196
+ }
197
+
198
+
199
+ /*
200
+ * All Groups: Deregister 1 Engine
201
+ *
202
+ */
203
+
204
+ function deregister_groups_engine($slug){
205
+
206
+ foreach($this->get_groups as $g => $group){
207
+
208
+ if(empty($group['engines']))
209
+ continue;
210
+
211
+ $reset = false;
212
+
213
+ foreach($group['engines'] as $ge => $group_engine){
214
+
215
+ if($group_engine != $slug)
216
+ continue;
217
+
218
+ unset($this->get_groups[$g]['engines'][$ge]);
219
+ $reset = true;
220
+ break;
221
+
222
+ }
223
+
224
+ if($reset)
225
+ $this->get_groups[$g]['engines'] = array_values($this->get_groups[$g]['engines']);
226
+
227
+ }
228
+
229
+ }
230
+
231
+
232
+ /*
233
+ * Group: Re-order
234
+ *
235
+ */
236
+
237
+ function reorder_group_engines($args){
238
+
239
+ $args = wp_parse_args($args, array(
240
+ 'group' => false,
241
+ 'engine' => false,
242
+ 'order' => 0
243
+ ));
244
+
245
+ $get_engines = wp404arsp()->engines->get_engines;
246
+
247
+ if(!$args['group'] || empty($this->get_groups) || !$args['engine'] || empty($get_engines))
248
+ return;
249
+
250
+ $group_key = false;
251
+ $engine_key = false;
252
+
253
+ foreach($this->get_groups as $g => $group){
254
+ if($group['slug'] != $args['group'] || empty($group['engines']))
255
+ continue;
256
+
257
+ foreach($group['engines'] as $e => $engine){
258
+ if($engine == $args['engine']){
259
+ $group_key = $g;
260
+ $engine_key = $e;
261
+ break;
262
+ }
263
+ }
264
+ }
265
+
266
+ if($engine_key === false || $group_key === false)
267
+ return;
268
+
269
+ wp404arsp_array_move_by_key($this->get_groups[$group_key]['engines'], $engine_key, $args['order']);
270
+
271
+ }
272
+
273
+ function get_group_by_slug($slug){
274
+
275
+ if(empty($this->get_groups))
276
+ return false;
277
+
278
+ foreach($this->get_groups as $group){
279
+ if($group['slug'] != $slug)
280
+ continue;
281
+
282
+ return $group;
283
+ }
284
+
285
+ return false;
286
+
287
+ }
288
+
289
+ function group_exists($slug){
290
+
291
+ return $this->get_group_by_slug($slug);
292
+
293
+ }
294
+
295
+ }
296
+
297
+ wp404arsp()->groups = new WP_404_Auto_Redirect_Groups();
298
+
299
+ }
300
+
301
+ function wp404arsp_register_group($args){
302
+
303
+ return wp404arsp()->groups->register_group($args);
304
+
305
+ }
306
+
307
+ function wp404arsp_register_group_engines($args){
308
+
309
+ return wp404arsp()->groups->register_group_engines($args);
310
+
311
+ }
312
+
313
+ function wp404arsp_deregister_group_engine($args){
314
+
315
+ return wp404arsp()->groups->deregister_group_engine($args);
316
+
317
+ }
318
+
319
+ function wp404arsp_deregister_groups_engine($slug){
320
+
321
+ return wp404arsp()->groups->deregister_groups_engine($slug);
322
+
323
+ }
324
+
325
+ function wp404arsp_reorder_group_engines($args){
326
+
327
+ return wp404arsp()->groups->reorder_group_engines($args);
328
+
329
+ }
330
+
331
+ function wp404arsp_get_group_by_slug($slug){
332
+
333
+ return wp404arsp()->groups->get_group_by_slug($slug);
334
+
335
+ }
336
+
337
+ function wp404arsp_group_exists($slug){
338
+
339
+ return wp404arsp()->groups->group_exists($slug);
340
+
341
+ }
class/class-search.php ADDED
@@ -0,0 +1,235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if(!defined('ABSPATH'))
4
+ exit;
5
+
6
+ if(!class_exists('WP_404_Auto_Redirect_Search')):
7
+
8
+ class WP_404_Auto_Redirect_Search {
9
+
10
+ function sql($args, $query){
11
+ global $wpdb;
12
+
13
+ $args = wp_parse_args($args, array(
14
+ 'keywords' => false,
15
+ 'mode' => 'post',
16
+ 'post_type' => false,
17
+ 'taxonomy' => false
18
+ ));
19
+
20
+ if(!$args['keywords']){
21
+ return array(
22
+ 'result' => array(
23
+ 'score' => 0
24
+ ),
25
+ 'dump' => false
26
+ );
27
+ }
28
+
29
+ // Mode: Post
30
+ if($args['mode'] == 'post'){
31
+
32
+ // Post Type Args not set && All Post Types are excluded in settings. Early Stop.
33
+ if(!$args['post_type'] && empty($query['settings']['rules']['include']['post_types'])){
34
+ return array(
35
+ 'result' => array(
36
+ 'score' => 0
37
+ ),
38
+ 'dump' => false
39
+ );
40
+ }
41
+
42
+ $sql = "SELECT p.ID, ";
43
+
44
+ if(is_array($args['keywords'])){
45
+
46
+ foreach($args['keywords'] as $k){
47
+ $sql .= "
48
+ if(INSTR(LCASE(p.post_name), '" . $k . "'), 1, 0) + ";
49
+ }
50
+
51
+ }else{
52
+
53
+ $sql .= "
54
+ if(INSTR(LCASE(p.post_name), '" . $args['keywords'] . "'), 1, 0) + ";
55
+
56
+ }
57
+
58
+ $sql .= "0 AS score FROM " . $wpdb->posts . " AS p";
59
+
60
+ if($query['settings']['rules']['exclude']['post_meta']){
61
+
62
+ $sql .= "
63
+ INNER JOIN " . $wpdb->postmeta . " AS pm ON(p.ID = pm.post_id)
64
+ WHERE p.post_status = 'publish' AND (pm.meta_key = 'wp404arsp_no_redirect' AND pm.meta_value != '1') ";
65
+
66
+ }else{
67
+
68
+ $sql .= "
69
+ WHERE p.post_status = 'publish' ";
70
+
71
+ }
72
+
73
+ if($args['post_type'] != 'any' && $args['post_type'] != array('any')){
74
+
75
+ $get_post_types = array();
76
+
77
+ // Load Settings Post Types
78
+ if(!$args['post_type']){
79
+
80
+ $get_post_types = $query['settings']['rules']['include']['post_types'];
81
+
82
+ // Post Type Array
83
+ }elseif(is_array($args['post_type']) && !empty($args['post_type'])){
84
+
85
+ $get_post_types = $args['post_type'];
86
+
87
+ // Single Post Type
88
+ }elseif(is_string($args['post_type'])){
89
+
90
+ $get_post_types[] = $args['post_type'];
91
+
92
+ }
93
+
94
+ if(!empty($get_post_types)){
95
+
96
+ $post_types = array();
97
+
98
+ foreach($get_post_types as $pt){
99
+ $post_types[] = "
100
+ p.post_type = '" . $pt . "'";
101
+ }
102
+
103
+ $sql .= 'AND
104
+ (' . implode(' OR ', $post_types) . ')';
105
+
106
+ }
107
+
108
+ }
109
+
110
+ $sql .= "
111
+ ORDER BY score DESC, post_modified DESC LIMIT 1";
112
+
113
+ }
114
+
115
+ // Mode: Term
116
+ elseif($args['mode'] == 'term'){
117
+
118
+ // Taxonomy Args not set && All Taxonomies are excluded in settings. Early Stop.
119
+ if(!$args['taxonomy'] && (empty($query['settings']['rules']['include']['taxonomies']) || $query['settings']['rules']['disable']['taxonomies'])){
120
+ return array(
121
+ 'result' => array(
122
+ 'score' => 0
123
+ ),
124
+ 'dump' => false
125
+ );
126
+ }
127
+
128
+ $sql = "SELECT t.term_id, ";
129
+
130
+ if(is_array($args['keywords'])){
131
+
132
+ foreach($args['keywords'] as $k){
133
+ $sql .= "
134
+ if(INSTR(LCASE(t.slug), '" . $k . "'), 1, 0) + ";
135
+ }
136
+
137
+ }else{
138
+
139
+ $sql .= "
140
+ if(INSTR(LCASE(t.slug), '" . $args['keywords'] . "'), 1, 0) + ";
141
+
142
+ }
143
+
144
+ $sql .= "
145
+ 0 AS score FROM " . $wpdb->terms . " AS t";
146
+
147
+ $sql .= "
148
+ INNER JOIN " . $wpdb->term_taxonomy . " AS tt ON(t.term_id = tt.term_id)";
149
+
150
+ if($query['settings']['rules']['exclude']['term_meta']){
151
+ $sql .= "
152
+ INNER JOIN " . $wpdb->termmeta . " AS tm ON(t.term_id = tm.term_id)";
153
+ }
154
+
155
+ if($args['taxonomy'] != 'any' && $args['taxonomy'] != array('any')){
156
+
157
+ $get_taxonomies = array();
158
+
159
+ // Load Settings Post Types
160
+ if(!$args['taxonomy']){
161
+
162
+ $get_taxonomies = $query['settings']['rules']['include']['taxonomies'];
163
+
164
+ // Post Type Array
165
+ }elseif(is_array($args['taxonomy']) && !empty($args['taxonomy'])){
166
+
167
+ $get_taxonomies = $args['taxonomy'];
168
+
169
+ // Single Post Type
170
+ }elseif(is_string($args['taxonomy'])){
171
+
172
+ $get_taxonomies[] = $args['taxonomy'];
173
+
174
+ }
175
+
176
+ if(!empty($get_taxonomies)){
177
+
178
+ $taxonomies = array();
179
+
180
+ foreach($get_taxonomies as $tax){
181
+ $taxonomies[] = "
182
+ tt.taxonomy = '" . $tax . "'";
183
+ }
184
+
185
+ $sql .= '
186
+ WHERE (' . implode(' OR ', $taxonomies) . ')';
187
+
188
+ if($query['settings']['rules']['exclude']['term_meta']){
189
+ $sql .= "
190
+ AND (tm.meta_key = 'wp404arsp_no_redirect' AND tm.meta_value != '1')";
191
+ }
192
+
193
+ }
194
+
195
+ }
196
+
197
+ $sql .= "
198
+ ORDER BY score DESC LIMIT 1";
199
+
200
+ }
201
+
202
+ $search = $wpdb->get_row($sql, 'ARRAY_A');
203
+
204
+ // init Result
205
+ $result = array();
206
+
207
+ // SQL Dump
208
+ $result['sql'] = $sql;
209
+
210
+ // Post ID
211
+ if(isset($search['ID']) && !empty($search['ID']))
212
+ $result['post_id'] = (int) $search['ID'];
213
+
214
+ // Term ID
215
+ if(isset($search['term_id']) && !empty($search['term_id']))
216
+ $result['term_id'] = (int) $search['term_id'];
217
+
218
+ // Score
219
+ $result['score'] = 0;
220
+ if(isset($search['score']) && !empty($search['score']))
221
+ $result['score'] = (int) $search['score'];
222
+
223
+ // Return Result
224
+ return $result;
225
+ }
226
+
227
+ }
228
+
229
+ wp404arsp()->search = new WP_404_Auto_Redirect_Search();
230
+
231
+ endif;
232
+
233
+ function wp404arsp_search($args, $query){
234
+ return wp404arsp()->search->sql($args, $query);
235
+ }
class/class-settings.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if(!defined('ABSPATH'))
4
+ exit;
5
+
6
+ if(!class_exists('WP_404_Auto_Redirect_Settings')):
7
+
8
+ class WP_404_Auto_Redirect_Settings {
9
+
10
+ function get(){
11
+
12
+ $option = get_option('wp404arsp_settings');
13
+
14
+ // 0.9.0.2 Deprecated compatibility
15
+ // ---------------------------------------
16
+ if(!wp404arsp_is_empty($option['rules']['redirection']['exclude'])){
17
+ $option['rules']['exclude'] = $option['rules']['redirection']['exclude'];
18
+ unset($option['rules']['redirection']['exclude']);
19
+ }
20
+
21
+ if(!wp404arsp_is_empty($option['rules']['redirection']['disable'])){
22
+ $option['rules']['disable'] = $option['rules']['redirection']['disable'];
23
+ unset($option['rules']['redirection']['disable']);
24
+ }
25
+
26
+ if(isset($option['rules']['redirection']))
27
+ unset($option['rules']['redirection']);
28
+ // ---------------------------------------
29
+
30
+ // Defaults
31
+ $settings = wp404arsp_parse_args_recursive($option, array(
32
+ 'debug' => null,
33
+ 'headers' => null,
34
+ 'log' => null,
35
+ 'method' => 301,
36
+ 'priority' => 999,
37
+ 'fallback' => array(
38
+ 'type' => 'home',
39
+ 'url' => home_url(),
40
+ 'home_url' => home_url(),
41
+ ),
42
+ 'rules' => array(
43
+ 'include' => array(
44
+ 'post_types' => array(),
45
+ 'taxonomies' => array(),
46
+ ),
47
+ 'exclude' => array(
48
+ 'post_meta' => null,
49
+ 'term_meta' => null,
50
+ 'post_types' => array(),
51
+ 'taxonomies' => array(),
52
+ ),
53
+ 'disable' => array(
54
+ 'taxonomies' => null
55
+ )
56
+ )
57
+ ));
58
+
59
+ // Include
60
+ $settings['rules']['include']['post_types'] = wp404arsp_get_post_types($settings);
61
+ $settings['rules']['include']['taxonomies'] = wp404arsp_get_taxonomies($settings);
62
+
63
+ // Falback
64
+ if($settings['fallback']['type'] == 'home')
65
+ $settings['fallback']['url'] = home_url();
66
+
67
+ // Headers
68
+ if(((int)$settings['method'] != 301) && ((int)$settings['method'] != 302))
69
+ $settings['method'] = 301;
70
+
71
+ // Return
72
+ return $settings;
73
+
74
+ }
75
+
76
+ }
77
+
78
+ wp404arsp()->settings = new WP_404_Auto_Redirect_Settings();
79
+
80
+ endif;
81
+
82
+ function wp404arsp_settings_get(){
83
+
84
+ return wp404arsp()->settings->get();
85
+
86
+ }
includes/admin.php ADDED
@@ -0,0 +1,585 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if(!defined('ABSPATH'))
4
+ exit;
5
+
6
+ if(!class_exists('WP_404_Auto_Redirect'))
7
+ return;
8
+
9
+ trait WP_404_Auto_Redirect_Admin {
10
+
11
+ function admin_menu(){
12
+ add_submenu_page('options-general.php', 'WP 404 Auto Redirect', 'WP 404 Auto Redirect', 'manage_options', 'wp-404-auto-redirect', array($this, 'admin_page'));
13
+ }
14
+
15
+ function admin_link($links, $plugin_file){
16
+ $plugin = plugin_basename(__FILE__);
17
+ if($plugin != $plugin_file)
18
+ return $links;
19
+
20
+ return array_merge(
21
+ $links,
22
+ array('<a href="' . admin_url('options-general.php?page=wp-404-auto-redirect') . '">' . __('Settings', 'wp404-auto-redirect') . '</a>')
23
+ );
24
+ }
25
+
26
+ function admin_settings(){
27
+ register_setting('wp404arsp_settings', 'wp404arsp_settings');
28
+ }
29
+
30
+ function admin_scripts($page){
31
+ if($page != 'settings_page_wp-404-auto-redirect')
32
+ return;
33
+
34
+ wp_enqueue_script('wp404arsp_admin_js', plugins_url('../assets/admin.js', __FILE__), array('jquery'));
35
+ wp_enqueue_style('wp404arsp_admin_css', plugins_url('../assets/admin.css', __FILE__));
36
+ }
37
+
38
+ function admin_page(){
39
+ ?>
40
+ <div class="wrap" id="wp404arsp_settings">
41
+ <h1 class="wp-heading-inline">WP 404 Auto Redirect to Similar Post</h1>
42
+ <hr class="wp-header-end" />
43
+
44
+ <h2 class="nav-tab-wrapper">
45
+ <a href="#settings" class="nav-tab nav-tab-active"><?php _e('Settings', 'wp404-auto-redirect'); ?></a>
46
+ <a href="#post-types" class="nav-tab"><?php _e('Post Types', 'wp404-auto-redirect'); ?></a>
47
+ <a href="#taxonomies" class="nav-tab"><?php _e('Taxonomies', 'wp404-auto-redirect'); ?></a>
48
+ <a href="#hooks" class="nav-tab"><?php _e('Engines', 'wp404-auto-redirect'); ?></a>
49
+ </h2>
50
+
51
+ <div id="poststuff">
52
+ <div id="post-body" class="metabox-holder columns-2">
53
+ <div id="post-body-content">
54
+
55
+ <form method="post" action="options.php">
56
+ <?php
57
+ settings_fields('wp404arsp_settings');
58
+ do_settings_sections('wp404arsp_settings');
59
+ $settings = wp404arsp_settings_get();
60
+ ?>
61
+
62
+ <div class="meta-box-sortables ui-sortable">
63
+
64
+ <!-- Tab: Settings -->
65
+ <div class="nav-tab-panel" id="settings">
66
+
67
+ <div class="postbox">
68
+ <div class="inside">
69
+ <table class="form-table">
70
+ <tbody>
71
+
72
+ <tr>
73
+ <th scope="row"><?php _e('Debug Mode', 'wp404-auto-redirect'); ?></th>
74
+ <td>
75
+ <fieldset>
76
+ <legend class="screen-reader-text"><span><?php _e('Debug Mode', 'wp404-auto-redirect'); ?></span></legend>
77
+ <label for="wp404arsp_settings_debug">
78
+ <input
79
+ name="wp404arsp_settings[debug]"
80
+ id="wp404arsp_settings_debug"
81
+ value="1"
82
+ type="checkbox"
83
+ <?php checked(1, $settings['debug'], true); ?>
84
+ />
85
+ <?php _e('Enable', 'wp404-auto-redirect'); ?>
86
+ </label>
87
+ </fieldset>
88
+ <p class="description"><?php _e('Display the Debug Console instead of being redirected. <code>Administrators</code> only.', 'wp404-auto-redirect'); ?></p>
89
+ </td>
90
+ </tr>
91
+
92
+ <tr>
93
+ <th scope="row"><?php _e('Expose Headers', 'wp404-auto-redirect'); ?></th>
94
+ <td>
95
+ <fieldset>
96
+ <legend class="screen-reader-text"><span><?php _e('Expose Headers', 'wp404-auto-redirect'); ?></span></legend>
97
+ <label for="wp404arsp_settings_headers">
98
+ <input
99
+ name="wp404arsp_settings[headers]"
100
+ id="wp404arsp_settings_headers"
101
+ value="1"
102
+ type="checkbox"
103
+ <?php checked(1, $settings['headers'], true); ?>
104
+ />
105
+ <?php _e('Enable', 'wp404-auto-redirect'); ?>
106
+ </label>
107
+ </fieldset>
108
+ <p class="description"><?php _e('Expose \'WP-404-Auto-Redirect\' headers on 404 pages. <code>Administrators</code> only.', 'wp404-auto-redirect'); ?></p>
109
+ </td>
110
+ </tr>
111
+
112
+ <tr>
113
+ <th scope="row"><?php _e('Log Redirections', 'wp404-auto-redirect'); ?></th>
114
+ <td>
115
+
116
+ <?php if(!WP_DEBUG || !WP_DEBUG_LOG){ ?>
117
+ <fieldset>
118
+ <legend class="screen-reader-text"><span><?php _e('Log Redirections', 'wp404-auto-redirect'); ?></span></legend>
119
+ <p class="description"><?php _e('To enable this feature, please set <code>WP_DEBUG</code> and <code>WP_DEBUG_LOG</code> to <code>true</code>. Read the <a href="https://codex.wordpress.org/Editing_wp-config.php#Debug" target="_blank">WP Config documentation</a>.', 'wp404-auto-redirect'); ?></p>
120
+ </fieldset>
121
+ <?php }else{ ?>
122
+ <fieldset>
123
+ <legend class="screen-reader-text"><span><?php _e('Log Redirections', 'wp404-auto-redirect'); ?></span></legend>
124
+ <label for="wp404arsp_settings_log">
125
+ <input
126
+ name="wp404arsp_settings[log]"
127
+ id="wp404arsp_settings_log"
128
+ value="1"
129
+ type="checkbox"
130
+ <?php checked(1, $settings['log'], true); ?>
131
+ />
132
+ <?php _e('Enable', 'wp404-auto-redirect'); ?>
133
+ </label>
134
+ </fieldset>
135
+ <p class="description"><?php _e('Log redirections in the <code>/wp-content/debug.log</code> file.', 'wp404-auto-redirect'); ?></p>
136
+ <?php } ?>
137
+
138
+ </td>
139
+ </tr>
140
+
141
+ <tr>
142
+ <th scope="row"><?php _e('Fallback Behavior', 'wp404-auto-redirect'); ?></th>
143
+ <td>
144
+ <fieldset>
145
+ <legend class="screen-reader-text"><span><?php _e('Fallback Behavior', 'wp404-auto-redirect'); ?></span></legend>
146
+ <label for="wp404arsp_settings_fallback_type">
147
+ <select name="wp404arsp_settings[fallback][type]" id="wp404arsp_settings_fallback_type">
148
+ <option value="home" <?php if($settings['fallback']['type'] == 'home') echo "selected"; ?>><?php _e('Redirect to Homepage', 'wp404-auto-redirect'); ?></option>
149
+ <option value="custom" <?php if($settings['fallback']['type'] == 'custom') echo "selected"; ?>><?php _e('Custom Redirection', 'wp404-auto-redirect'); ?></option>
150
+ <option value="disabled" <?php if($settings['fallback']['type'] == 'disabled') echo "selected"; ?>><?php _e('Default 404', 'wp404-auto-redirect'); ?></option>
151
+ </select>
152
+ </label>
153
+
154
+ <?php
155
+ $fallback = array(
156
+ 'value' => home_url(),
157
+ 'class' => 'disabled',
158
+ 'attr' => 'readonly="readonly"',
159
+ );
160
+
161
+ if($settings['fallback']['type'] == 'custom'){
162
+ $fallback['value'] = $settings['fallback']['url'];
163
+ $fallback['attr'] = '';
164
+ $fallback['class'] = '';
165
+ }
166
+
167
+ if($settings['fallback']['type'] == 'disabled'){
168
+ $fallback['value'] = '';
169
+ $fallback['attr'] = '';
170
+ $fallback['class'] = 'hidden';
171
+ }
172
+ ?>
173
+
174
+ <input name="wp404arsp_settings[fallback][home_url]" id="wp404arsp_settings_fallback_home_url" type="hidden" value="<?php echo home_url(); ?>" />
175
+ <input name="wp404arsp_settings[fallback][url]" id="wp404arsp_settings_fallback_url" type="text" value="<?php echo $fallback['value']; ?>" class="<?php echo $fallback['class']; ?>" <?php echo $fallback['attr']; ?> />
176
+
177
+ </fieldset>
178
+ <p class="description"><?php _e('If nothing similar is found, this behavior will be applied.', 'wp404-auto-redirect'); ?></p>
179
+ </td>
180
+ </tr>
181
+
182
+ <tr>
183
+ <th scope="row"><?php _e('Redirections Headers', 'wp404-auto-redirect'); ?></th>
184
+ <td>
185
+ <fieldset>
186
+ <legend class="screen-reader-text"><span><?php _e('Redirections Headers', 'wp404-auto-redirect'); ?></span></legend>
187
+ <label for="wp404arsp_settings_method">
188
+ <select name="wp404arsp_settings[method]" id="wp404arsp_settings_method">
189
+ <option value="301" <?php if($settings['method'] == 301) echo "selected"; ?>>301 Status</option>
190
+ <option value="302" <?php if($settings['method'] == 302) echo "selected"; ?>>302 Status</option>
191
+ </select>
192
+ </label>
193
+ </fieldset>
194
+ <p class="description"><?php _e('Learn more about <a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes" target="_blank">HTTP headers & redirections</a>.', 'wp404-auto-redirect'); ?></p>
195
+ </td>
196
+ </tr>
197
+
198
+ <tr>
199
+ <th scope="row"><?php _e('Plugin Priority', 'wp404-auto-redirect'); ?></th>
200
+ <td>
201
+ <fieldset>
202
+ <legend class="screen-reader-text"><span><?php _e('Plugin Priority', 'wp404-auto-redirect'); ?></span></legend>
203
+ <label for="wp404arsp_settings_priority">
204
+ <input
205
+ type="number"
206
+ name="wp404arsp_settings[priority]"
207
+ id="wp404arsp_settings_priority"
208
+ value="<?php echo isset($settings['priority']) ? $settings['priority'] : '999'; ?>"
209
+ required
210
+ />
211
+ </label>
212
+ </fieldset>
213
+ <p class="description"><?php _e('Advanced users only. Default: <code>999</code>', 'wp404-auto-redirect'); ?></p>
214
+ </td>
215
+ </tr>
216
+
217
+ </tbody>
218
+ </table>
219
+
220
+ </div>
221
+ </div>
222
+
223
+ <div id="wp404arsp_settings_redirection_preview">
224
+ <div class="postbox">
225
+ <div class="inside">
226
+ <table class="form-table">
227
+ <tbody>
228
+
229
+ <tr>
230
+ <th scope="row"><?php echo home_url(); ?></th>
231
+ <td>
232
+ <input class="request" type="text" value="/example-url" />
233
+ <p class="description"><?php _e('Enter the URL you would like to test, starting with <code>/</code>.', 'wp404-auto-redirect'); ?></p>
234
+
235
+ <p class="submit">
236
+ <?php submit_button(__('Preview', 'wp404-auto-redirect'), 'secondary', '', false); ?>
237
+ <span class="loading spinner"></span>
238
+ </p>
239
+ </td>
240
+ </tr>
241
+ </tbody>
242
+ </table>
243
+
244
+ <div class="results"></div>
245
+
246
+ </div>
247
+ </div>
248
+ </div>
249
+
250
+ </div>
251
+
252
+ <!-- Tab: Post Types -->
253
+ <div class="nav-tab-panel" id="post-types">
254
+
255
+ <div class="postbox">
256
+ <div class="inside">
257
+ <table class="form-table">
258
+ <tbody>
259
+
260
+ <tr>
261
+ <th scope="row"><?php _e('Exclude Post Meta', 'wp404-auto-redirect'); ?></th>
262
+ <td>
263
+ <fieldset>
264
+ <legend class="screen-reader-text"><span><?php _e('Exclude Post Meta', 'wp404-auto-redirect'); ?></span></legend>
265
+ <label for="wp404arsp_settings_rules_redirection_exclude_post_meta">
266
+ <input
267
+ name="wp404arsp_settings[rules][exclude][post_meta]"
268
+ id="wp404arsp_settings_rules_redirection_exclude_post_meta"
269
+ type="checkbox"
270
+ value="1"
271
+ <?php checked(1, $settings['rules']['exclude']['post_meta'], true); ?>
272
+ />
273
+ <?php _e('Enable', 'wp404-auto-redirect'); ?>
274
+ </label>
275
+ </fieldset>
276
+ <p class="description"><?php _e('Exclude posts with the post meta: <code>wp404arsp_no_redirect = 1</code> from possible redirections.', 'wp404-auto-redirect'); ?></p>
277
+ </td>
278
+ </tr>
279
+
280
+ <tr>
281
+ <th scope="row"><?php _e('Exclude Post Type(s)', 'wp404-auto-redirect'); ?></th>
282
+ <td>
283
+ <fieldset>
284
+ <legend class="screen-reader-text"><span><?php _e('Exclude Post Type(s)', 'wp404-auto-redirect'); ?></span></legend>
285
+ <div id="wp404arsp_settings_rules_redirection_exclude_post_types">
286
+ <?php foreach(get_post_types(array('public' => true), 'objects') as $post_type) { ?>
287
+ <?php
288
+ $checked = '';
289
+ if(
290
+ isset($settings['rules']['exclude']['post_types']) &&
291
+ is_array($settings['rules']['exclude']['post_types']) &&
292
+ in_array($post_type->name, $settings['rules']['exclude']['post_types'])
293
+ )
294
+ $checked = 'checked="checked"'; ?>
295
+ <div><input type="checkbox" name="wp404arsp_settings[rules][exclude][post_types][]" id="wp404arsp_settings_rules_redirection_exclude_post_types_<?php echo $post_type->name; ?>" value="<?php echo $post_type->name; ?>" <?php echo $checked; ?> />
296
+ <label for="wp404arsp_settings_rules_redirection_exclude_post_types_<?php echo $post_type->name; ?>"><?php echo $post_type->label; ?></label></div>
297
+ <?php } ?>
298
+ </div>
299
+ </fieldset>
300
+ <p class="description"><?php _e('Exclude one or multiple post types from possible redirections.', 'wp404-auto-redirect'); ?></p>
301
+ </td>
302
+ </tr>
303
+
304
+ </tbody>
305
+ </table>
306
+ </div>
307
+ </div>
308
+
309
+ </div>
310
+
311
+ <!-- Tab: Taxonomies -->
312
+ <div class="nav-tab-panel" id="taxonomies">
313
+
314
+ <div class="postbox">
315
+ <div class="inside">
316
+ <table class="form-table">
317
+ <tbody>
318
+
319
+ <tr>
320
+ <th scope="row"><?php _e('Disable Taxonomy Redirection', 'wp404-auto-redirect'); ?></th>
321
+ <td>
322
+ <fieldset>
323
+ <legend class="screen-reader-text"><span><?php _e('Disable Taxonomy Redirection', 'wp404-auto-redirect'); ?></span></legend>
324
+ <label for="wp404arsp_settings_rules_redirection_disable_taxonomies">
325
+ <input
326
+ name="wp404arsp_settings[rules][disable][taxonomies]"
327
+ id="wp404arsp_settings_rules_redirection_disable_taxonomies"
328
+ type="checkbox"
329
+ value="1"
330
+ <?php checked(1, $settings['rules']['disable']['taxonomies'], true); ?>
331
+ />
332
+ <?php _e('Disable', 'wp404-auto-redirect'); ?>
333
+ </label>
334
+ </fieldset>
335
+ <p class="description"><?php _e('Never redirect to terms archives.', 'wp404-auto-redirect'); ?></p>
336
+ </td>
337
+ </tr>
338
+
339
+ <tr class="wp404arsp_settings_taxonomies">
340
+ <th scope="row"><?php _e('Exclude Term Meta', 'wp404-auto-redirect'); ?></th>
341
+ <td>
342
+ <fieldset>
343
+ <legend class="screen-reader-text"><span><?php _e('Exclude Term Meta', 'wp404-auto-redirect'); ?></span></legend>
344
+ <label for="wp404arsp_settings_rules_redirection_exclude_term_meta">
345
+ <input
346
+ name="wp404arsp_settings[rules][exclude][term_meta]"
347
+ id="wp404arsp_settings_rules_redirection_exclude_term_meta"
348
+ type="checkbox"
349
+ value="1"
350
+ <?php checked(1, $settings['rules']['exclude']['term_meta'], true); ?>
351
+ />
352
+ <?php _e('Enable', 'wp404-auto-redirect'); ?>
353
+ </label>
354
+ </fieldset>
355
+ <p class="description"><?php _e('Exclude terms with the term meta: <code>wp404arsp_no_redirect = 1</code> from possible redirections.', 'wp404-auto-redirect'); ?></p>
356
+ </td>
357
+ </tr>
358
+
359
+ <tr class="wp404arsp_settings_taxonomies">
360
+ <th scope="row"><?php _e('Exclude Taxonomie(s)', 'wp404-auto-redirect'); ?></th>
361
+ <td>
362
+ <fieldset>
363
+ <legend class="screen-reader-text"><span><?php _e('Exclude Taxonomie(s)', 'wp404-auto-redirect'); ?></span></legend>
364
+ <div id="wp404arsp_settings_rules_redirection_exclude_taxonomies">
365
+ <?php foreach(get_taxonomies(array('public' => true), 'objects') as $taxonomy) { ?>
366
+ <?php
367
+ $checked = '';
368
+ if(
369
+ isset($settings['rules']['exclude']['taxonomies']) &&
370
+ is_array($settings['rules']['exclude']['taxonomies']) &&
371
+ in_array($taxonomy->name, $settings['rules']['exclude']['taxonomies'])
372
+ )
373
+ $checked = 'checked="checked"'; ?>
374
+ <div><input type="checkbox" name="wp404arsp_settings[rules][exclude][taxonomies][]" id="wp404arsp_settings_rules_redirection_exclude_taxonomies_<?php echo $taxonomy->name; ?>" value="<?php echo $taxonomy->name; ?>" <?php echo $checked; ?> />
375
+ <label for="wp404arsp_settings_rules_redirection_exclude_taxonomies_<?php echo $taxonomy->name; ?>"><?php echo $taxonomy->label; ?></label></div>
376
+ <?php } ?>
377
+ </div>
378
+ </fieldset>
379
+ <p class="description"><?php _e('Exclude one or multiple taxonomies from possible redirections.', 'wp404-auto-redirect'); ?></p>
380
+ </td>
381
+ </tr>
382
+
383
+ </tbody>
384
+ </table>
385
+ </div>
386
+ </div>
387
+
388
+ </div>
389
+
390
+ <!-- Tab: Hooks -->
391
+ <div class="nav-tab-panel" id="hooks">
392
+
393
+ <?php
394
+ do_action('wp404arsp/search/init', false);
395
+
396
+ // init Groups
397
+ $groups = array();
398
+ $groups = wp404arsp()->groups->get_groups;
399
+ $groups_count = count($groups);
400
+
401
+ // init Engines
402
+ $engines = array();
403
+ $engines = wp404arsp()->engines->get_engines;
404
+ $engines_count = count($engines);
405
+ ?>
406
+
407
+ <div style="float:left; width:49%; margin-right:2%;">
408
+ <table class="widefat" style="margin-bottom:20px;">
409
+
410
+ <thead>
411
+ <tr>
412
+ <th colspan="2" class="row-title"><h3 style="margin:7px 0;">Groups <span style="color:#555d66;">(<?php echo $groups_count; ?>)</span></h3></th>
413
+ </tr>
414
+ </thead>
415
+
416
+ <thead>
417
+ <tr>
418
+ <th class="row-title">Name</th>
419
+ <th>Engines</th>
420
+ </tr>
421
+ </thead>
422
+
423
+ <tbody>
424
+ <?php if(!empty($groups)){ ?>
425
+
426
+ <?php $i=0; foreach($groups as $group){ $i++; ?>
427
+ <tr <?php echo !($i % 2) ? 'class="alternate"': ''; ?>>
428
+ <td class="row-title">
429
+ <span style="color: #0073aa;"><?php echo $group['name']; ?></span><br />
430
+ <small style="font-weight:normal;"><?php echo $group['slug']; ?></small>
431
+ </td>
432
+ <td>
433
+ <?php foreach($group['engines'] as $engine){ ?>
434
+
435
+ <?php if($engine = wp404arsp_get_engine_by_slug($engine)){ ?>
436
+ <div style="margin-bottom:5px;">
437
+ <?php echo $engine['name']; ?>
438
+ </div>
439
+ <?php } ?>
440
+ <?php } ?>
441
+
442
+ </td>
443
+ </tr>
444
+
445
+ <?php } ?>
446
+
447
+ <?php }else{ ?>
448
+
449
+ <tr>
450
+ <td class="row-title" colspan="2" style="text-align:center;">
451
+ <em>No Groups found.</em>
452
+ </td>
453
+ </tr>
454
+
455
+ <?php } ?>
456
+ </tbody>
457
+
458
+ </table>
459
+ </div>
460
+
461
+ <div style="float:left; width:49%;">
462
+ <table class="widefat" style="margin-bottom:20px;">
463
+ <thead>
464
+ <tr>
465
+ <th colspan="4" class="row-title"><h3 style="margin:7px 0;">Engines <span style="color:#555d66;">(<?php echo $engines_count; ?>)</span></h3></th>
466
+ </tr>
467
+ </thead>
468
+
469
+ <thead>
470
+ <tr>
471
+ <th class="row-title">Name</th>
472
+ <th>Weight</th>
473
+ <th>Primary</th>
474
+ <th>Defined</th>
475
+ </tr>
476
+ </thead>
477
+
478
+ <tbody>
479
+ <?php if(!empty($engines)){ ?>
480
+
481
+ <?php $i=0; foreach($engines as $engine){ $i++; ?>
482
+
483
+ <?php $has_filter = has_filter('wp404arsp/search/engine/' . $engine['slug']); ?>
484
+
485
+ <tr <?php echo !($i % 2) ? 'class="alternate"': ''; ?> <?php if(!$has_filter) echo 'style="background:#f7e5e5;"'; ?>>
486
+ <td class="row-title">
487
+ <span style="color: #0073aa;"><?php echo $engine['name']; ?></span><br />
488
+ <small style="font-weight:normal;"><?php echo $engine['slug']; ?></small>
489
+ </td>
490
+ <td><?php echo $engine['weight']; ?></td>
491
+ <td>
492
+ <?php if($engine['primary']){ ?>
493
+ <span class="dashicons dashicons-yes"></span>
494
+ <?php } ?>
495
+ </td>
496
+ <td>
497
+ <?php if($has_filter){ ?>
498
+
499
+ <span class="dashicons dashicons-yes"></span>
500
+
501
+ <?php }else{ ?>
502
+
503
+ <span class="dashicons dashicons-no" style="color:#cc0000;"></span>
504
+
505
+ <?php } ?>
506
+ </td>
507
+ </tr>
508
+ <?php } ?>
509
+
510
+ <?php }else{ ?>
511
+
512
+ <tr>
513
+ <td class="row-title" colspan="3" style="text-align:center;">
514
+ <em>No Engines found.</em>
515
+ </td>
516
+ </tr>
517
+
518
+ <?php } ?>
519
+ </tbody>
520
+
521
+ </table>
522
+
523
+ </div>
524
+ <div style="clear:both;"></div>
525
+
526
+ </div>
527
+
528
+ </div>
529
+
530
+ <div class="postbox">
531
+ <div class="inside">
532
+ <p class="submit">
533
+ <?php submit_button(__('Save Settings', 'wp404-auto-redirect'), 'primary', '', false); ?>
534
+ </p>
535
+ </div>
536
+ </div>
537
+
538
+ </form>
539
+
540
+ </div>
541
+
542
+ <?php $plugin_data = get_plugin_data(WP404ARSP_FILE, false, false); ?>
543
+ <div id="postbox-container-1" class="postbox-container">
544
+ <div class="meta-box-sortables">
545
+ <div class="postbox">
546
+
547
+ <div class="inside">
548
+ <img src="<?php echo plugins_url('../assets/logo.png', __FILE__); ?>" class="logo" />
549
+
550
+ <p><?php _e('Automatically redirect all your 404 pages to similar posts based on Title, Post Types & Taxonomies.', 'wp404-auto-redirect'); ?></p>
551
+
552
+ <h3><?php _e('Rate us', 'wp404-auto-redirect'); ?></h3>
553
+
554
+ <p><?php _e('Enjoying this plugin? Please rate us. It\'s always much appreciated!', 'wp404-auto-redirect'); ?></p>
555
+ <p><a href="https://wordpress.org/support/plugin/wp-404-auto-redirect-to-similar-post/reviews/#new-post" target="_blank" class="button"><?php _e('Rate this plugin', 'wp404-auto-redirect'); ?></a></p>
556
+
557
+ <?php if(!wp404arsp_is_empty($plugin_data['Version'])){ ?>
558
+
559
+ <h3><?php _e('Changelog', 'wp404-auto-redirect'); ?></h3>
560
+ <p><?php _e('See what\'s new in', 'wp404-auto-redirect'); ?> <a href="https://wordpress.org/plugins/wp-404-auto-redirect-to-similar-post/#developers" target="_blank" style="text-decoration:none;">version <?php echo $plugin_data['Version']; ?></a>.</p>
561
+
562
+ <?php } ?>
563
+
564
+ <h3><?php _e('Resources', 'wp404-auto-redirect'); ?></h3>
565
+
566
+ <ul>
567
+ <li><a href="https://wordpress.org/plugins/wp-404-auto-redirect-to-similar-post/" target="_blank" style="text-decoration:none;"><i class="dashicons dashicons-admin-home"></i> <?php _e('Website', 'wp404-auto-redirect'); ?></a></li>
568
+ <li><a href="https://wordpress.org/plugins/wp-404-auto-redirect-to-similar-post/" target="_blank" style="text-decoration:none;"><i class="dashicons dashicons-sos"></i> <?php _e('Documentation', 'wp404-auto-redirect'); ?></a></li>
569
+ <li><a href="https://wordpress.org/support/plugin/wp-404-auto-redirect-to-similar-post" target="_blank" style="text-decoration:none;"><i class="dashicons dashicons-editor-help"></i> <?php _e('Support', 'wp404-auto-redirect'); ?></a></li>
570
+ </ul>
571
+ </div>
572
+
573
+ </div>
574
+ </div>
575
+ </div>
576
+
577
+ </div>
578
+ <br class="clear">
579
+
580
+ </div>
581
+ </div>
582
+ <?php
583
+
584
+ }
585
+ }
includes/ajax.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if(!defined('ABSPATH'))
4
+ exit;
5
+
6
+ if(!class_exists('WP_404_Auto_Redirect'))
7
+ return;
8
+
9
+ trait WP_404_Auto_Redirect_Ajax {
10
+
11
+ function preview(){
12
+ if(!current_user_can('administrator'))
13
+ wp_die();
14
+
15
+ $request = urldecode(htmlspecialchars($_POST['request']));
16
+ if(empty($request))
17
+ wp_die();
18
+
19
+ $this->request($request, true);
20
+ wp_die();
21
+ }
22
+ }
includes/debug.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if(!defined('ABSPATH'))
4
+ exit;
5
+
6
+ if(!class_exists('WP_404_Auto_Redirect'))
7
+ return;
8
+
9
+ trait WP_404_Auto_Redirect_Debug {
10
+
11
+ function debug($query){
12
+ $title = 'Fallback Redirection disabled. Displaying 404.';
13
+
14
+ if(isset($query['redirect']['url']) && !empty($query['redirect']['url']))
15
+ $title = 'Redirection: ' . "<a href='" . $query['redirect']['url'] . "'>" . $query['redirect']['url'] . "</a>" . ' (' . $query['settings']['method'] . ' Headers)';
16
+ ?>
17
+
18
+ <style type="text/css">
19
+ .wp404arsp_debug_page{margin:0 auto; max-width:1150px; font-family: arial, sans-serif;}
20
+ .wp404arsp_debug_page .logo{text-align:center;}
21
+ .wp404arsp_debug_page .logo img{margin:0 auto;}
22
+ .wp404arsp_debug_page h2,
23
+ .wp404arsp_debug_page h4{text-align:center;}
24
+ .wp404arsp_debug_page pre{background:#f4f4f4; padding:15px; overflow:auto;}
25
+ .wp404arsp_debug_page a{color:blue;}
26
+ .wp404arsp_debug_page p{font-size:12px;}
27
+ </style>
28
+ <div class="wp404arsp_debug_page">
29
+
30
+ <?php if(!$query['preview']){ ?>
31
+ <div class="logo">
32
+ <img src="<?php echo plugins_url('../assets/logo.png', __FILE__); ?>" class="logo" />
33
+ </div>
34
+ <h2>WP 404 Auto Redirect to Similar Post</h2>
35
+ <p>This is the <strong>debug console</strong> of WP 404 Auto redirect to Similar Post Plugin which is only visible to administrators. Head over your <a href="<?php echo admin_url('options-general.php?page=wp-404-auto-redirect'); ?>">settings page</a> if you would like to disable it.</p>
36
+ <hr />
37
+ <?php } ?>
38
+
39
+ <h3>Summary:</h3>
40
+
41
+ <pre>Requested URL: <a href="<?php echo home_url(); ?><?php echo $query['request']['url']; ?>"><?php echo home_url(); ?><?php echo $query['request']['url']; ?></a><br />
42
+ <?php echo $title; ?><br />
43
+ Engine: <?php echo $query['redirect']['engine']; ?><br />
44
+ Details: <?php echo $query['redirect']['why']; ?></pre>
45
+
46
+ <h3>Advanced:</h3>
47
+ <pre><?php print_r($query); ?></pre>
48
+ </div>
49
+
50
+ <?php
51
+ exit;
52
+ }
53
+ }
includes/helpers.php ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if(!defined('ABSPATH'))
4
+ exit;
5
+
6
+ function wp404arsp_set_result($result, $engine){
7
+
8
+ $engine = wp_parse_args($engine, array(
9
+ 'name' => false,
10
+ 'slug' => false,
11
+ 'weight' => 100,
12
+ 'primary' => false,
13
+ ));
14
+
15
+ if(empty($engine['name']) || empty($engine['slug']))
16
+ return false;
17
+
18
+ if(is_array($result)){
19
+
20
+ $result = wp_parse_args($result, array(
21
+ 'url' => false,
22
+ 'score' => 0,
23
+ 'why' => __("No reason.", 'wp-404-auto-redirect'),
24
+ ));
25
+
26
+ }else{
27
+
28
+ $result = array(
29
+ 'url' => false,
30
+ 'score' => 0,
31
+ 'why' => $result,
32
+ );
33
+
34
+ }
35
+
36
+ $result['score'] = (int) $result['score'] * $engine['weight'];
37
+
38
+ return array_merge(array('engine' => $engine['name'], 'primary' => $engine['primary']), $result);
39
+
40
+ }
41
+
42
+ // isset + !empty
43
+ function wp404arsp_is_empty(&$var){
44
+ return (isset($var) && !empty($var)) ? false : true;
45
+ }
46
+
47
+ function wp404arsp_sanitize($input, $delimiter = '-'){
48
+
49
+ return str_replace(array('-', '_'), $delimiter, sanitize_title(str_replace(array('_', '/', '?', '#', '=', '&amp;', '&'), '-', $input)));
50
+
51
+ }
52
+
53
+ function wp404arsp_parse_args_recursive(&$a, $b){
54
+
55
+ $a = (array) $a;
56
+ $b = (array) $b;
57
+ $result = $b;
58
+
59
+ foreach($a as $k => &$v){
60
+ if(is_array($v) && isset($result[$k])){
61
+ $result[$k] = wp404arsp_parse_args_recursive($v, $result[$k]);
62
+ }else{
63
+ $result[$k] = $v;
64
+ }
65
+ }
66
+
67
+ return $result;
68
+
69
+ }
70
+
71
+ function wp404arsp_array_swap($key1, $key2, $array){
72
+
73
+ $newArray = array();
74
+
75
+ foreach($array as $key => $value){
76
+
77
+ if($key == $key1){
78
+ $newArray[$key2] = $array[$key2];
79
+
80
+ }elseif ($key == $key2){
81
+ $newArray[$key1] = $array[$key1];
82
+
83
+ }else{
84
+ $newArray[$key] = $value;
85
+
86
+ }
87
+
88
+ }
89
+
90
+ return $newArray;
91
+
92
+ }
93
+
94
+
95
+ function wp404arsp_array_move_by_key(&$a, $oldpos, $newpos){
96
+
97
+ if($oldpos == $newpos)
98
+ return;
99
+
100
+ array_splice($a, max($newpos,0), 0, array_splice($a, max($oldpos,0), 1));
101
+
102
+ }
103
+
104
+ function wp404arsp_get_post_types($settings){
105
+
106
+ $get_post_types = get_post_types(array('public' => true), 'names');
107
+
108
+ // No exclude
109
+ if(!isset($settings['rules']['exclude']['post_types']) || !is_array($settings['rules']['exclude']['post_types']) || empty($settings['rules']['exclude']['post_types']))
110
+ return $get_post_types;
111
+
112
+ // Exclude
113
+ $return = array();
114
+ foreach($get_post_types as $post_type){
115
+ if(in_array($post_type, $settings['rules']['exclude']['post_types']))
116
+ continue;
117
+
118
+ $return[] = $post_type;
119
+ }
120
+
121
+ return $return;
122
+
123
+ }
124
+
125
+ function wp404arsp_get_taxonomies($settings){
126
+
127
+ $get_taxonomies = get_taxonomies(array('public' => true), 'names');
128
+
129
+ // No exclude
130
+ if(!isset($settings['rules']['exclude']['taxonomies']) || !is_array($settings['rules']['exclude']['taxonomies']) || empty($settings['rules']['exclude']['taxonomies']))
131
+ return $get_taxonomies;
132
+
133
+ // Exclude
134
+ $return = array();
135
+ foreach($get_taxonomies as $taxonomy){
136
+ if(in_array($taxonomy, $settings['rules']['exclude']['taxonomies']))
137
+ continue;
138
+
139
+ $return[] = $taxonomy;
140
+ }
141
+
142
+ return $return;
143
+
144
+ }
index.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Silence is golden.
3
+
4
+ // ... And redirection to domain root is love <3
5
+ if(!isset($_SERVER['SCRIPT_URL']) || empty($_SERVER['SCRIPT_URL']) || !isset($_SERVER['SCRIPT_URI']) || empty($_SERVER['SCRIPT_URI']))
6
+ return;
7
+
8
+ $domain = str_replace($_SERVER['SCRIPT_URL'], '', $_SERVER['SCRIPT_URI']);
9
+ if(empty($domain))
10
+ return;
11
+
12
+ header('Location: ' . $domain, true, 301);
13
+ exit();
readme.txt CHANGED
@@ -4,17 +4,17 @@ Donate link: https://hwk.fr/
4
  Tags: SEO, 404, Redirect, 301, Similar, Related, Search, Broken Link, Webmaster Tools, Google
5
  Requires at least: 4.0
6
  Tested up to: 4.9.8
7
- Stable tag: 0.9.0.2
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
11
- Automatically Redirect any 404 to a Similar Post based on the Requested URL Title & Post Type using 301 or 302 Redirects!
12
 
13
  == Description ==
14
 
15
  Welcome to WP 404 Auto Redirect to Similar Post!
16
 
17
- This plugin automatically redirect all your 404 requests to similar posts based on Title, Post Types & Taxonomies. If nothing similar is found, it will redirect visitors to the homepage or a custom URL.
18
 
19
  = Features: =
20
 
@@ -30,13 +30,41 @@ This plugin automatically redirect all your 404 requests to similar posts based
30
  * Choose the redirection HTTP header status:
31
  * 301 headers
32
  * 302 headers
33
- * Exclude one or multiple post types from possible redirections.
34
- * Exclude one or multiple taxonomies from possible redirections.
35
- * Exclude one or multiple posts based on a custom post meta.
36
- * Exclude one or multiple terms based on a custom term meta.
37
  * Display the Debug Console instead of being redirected (Admin).
38
  * Preview possible redirection from the administration panel.
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  = But Also: =
41
 
42
  * Easy to Install / Uninstall.
@@ -45,16 +73,18 @@ This plugin automatically redirect all your 404 requests to similar posts based
45
 
46
  = Compatibility: =
47
 
48
- WP 404 Auto Redirect to Similar Post is 100% compatible with all popular redirection plugins. If you use one of them, but missed a manual redirection and a 404 is about to be displayed, WP 404 Auto Redirect to Similar Post will take action.
49
 
50
  * [Redirection](https://wordpress.org/plugins/redirection/)
51
  * [Simple 301 Redirects](https://wordpress.org/plugins/simple-301-redirects/)
52
  * [Yoast Redirections](https://yoast.com/wordpress/plugins/seo/redirects-manager/)
53
  * etc...
54
 
 
 
55
  == Reviews ==
56
 
57
- They talk about it! :)
58
 
59
  * [Quels plugins utiliser pour corriger les erreurs 404 sous WordPress ?](https://sebastienpierrepack.com/plugins-corriger-404-wordpress/)
60
  * [6+ 404 Redirect WordPress Plugins 2018 (Free and Paid)](https://www.formget.com/404-redirect-wordpress-plugins/)
@@ -68,97 +98,257 @@ They talk about it! :)
68
 
69
  == Frequently Asked Questions ==
70
 
71
- = Hooks: Filters & Actions =
72
 
73
- **Note:** If you don't know about filters & actions, please read the official [WordPress Plugin API](https://codex.wordpress.org/Plugin_API).
74
 
75
- **Filter: wp404arsp/init**
76
 
77
- This filter is applied at the initialization of the plugin, in the `template_redirect` action.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
- By default, `$init = true` which means that the plugin will take action when a 404 is about to be displayed. If is set to `false`, then le plugin will stop it's initialization.
 
 
80
 
81
- The second argument: `$request_uri` is the requested URI sent to the browser.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
- Returning `$init` as a boolean is **mandatory**.
84
 
85
- Usage example:
86
  `
87
- add_filter('wp404arsp/init', 'my_404_no_init', 10, 2);
88
- function my_404_no_init($init, $request_uri){
89
- if($request_uri == 'https://www.my-website.com/custom-page')
90
- $init = false;
 
 
 
 
91
 
92
- return $init;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  }
94
- `
95
 
96
- **Filter: wp404arsp/redirect**
 
 
97
 
98
- This filter is applied right before the plugin sends a redirection. You can use it to set a custom behavior depending on multiple factors.
99
 
100
- The array's key `$args['url']` will be used as the redirection's URI.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
- If the key `$args['url']` is set to `false`, the plugin's fallback behavior will be bypassed and the default 404 page will be displayed.
103
 
104
- The second argument: `$settings` is an array of the plugin's settings.
105
 
106
- Returning an `$args` array is **mandatory**.
107
 
108
- Args array structure:
109
  `
110
- Array(
111
- [request] => Array(
112
- [full] => /this-is-404-page
113
- [dirname] =>
114
- [filename] => this-is-404-page
115
- [extension] =>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  )
 
 
 
 
 
 
 
 
 
 
 
117
 
118
- [preview] =>
119
- [process] => Array(
120
- [request] => /this-is-404-page
121
- [keywords] => Array
122
- (
123
- [sanitized] => this-is-404-page
124
- [explode] => Array
125
- (
126
- [0] => this-is-404-page
127
- )
128
 
129
- )
130
 
131
- )
132
 
133
- [why] => Nothing similar found matching the requested URL.
134
- [url] => https://www.my-website.com
135
- )
136
  `
 
 
 
137
 
138
- Usage example:
 
 
 
 
 
139
  `
140
- add_filter('wp404arsp/redirect', 'my_404_redirect', 10, 2);
141
- function my_404_redirect($args, $settings){
142
- if($args['request']['full'] == '/this-is-404-page')
143
- $args['url'] = 'https://www.my-website.com/an-another-fallback-page';
 
 
 
 
 
 
 
 
 
 
 
144
 
145
- return $args;
 
146
  }
147
  `
148
 
149
- **Action: wp404arsp/after_redirect**
 
 
150
 
151
- This action is called right after a redirection is sent to the browser. You can use it to perform custom actions.
152
 
153
- Usage example:
154
  `
155
- add_action('wp404arsp/after_redirect', 'my_404_after_redirect', 10, 2);
156
- function my_404_after_redirect($args, $settings){
 
 
 
 
157
  // Send me an e-mail
158
  wp_mail(
159
  'my@email.com',
160
  'WP 404 Auto Redirect: New redirection',
161
- 'Hi! New redirection from ' . $args['request']['full'] . ' to ' . $args['url'],
162
  array('Content-Type: text/html; charset=UTF-8')
163
  );
164
 
@@ -180,11 +370,27 @@ function my_404_after_redirect($args, $settings){
180
  1. Admin: Settings Page
181
  2. Admin: Post Types
182
  3. Admin: Taxonomies
183
- 4. Admin: Preview
184
  5. Front: Debug Console
185
 
186
  == Changelog ==
187
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  = 0.9.0.2 =
189
  * Fix: Plugin priority set to 999 by debault
190
  * Added: New Filter available `('wp404arsp/init', $init, $request_uri)`
4
  Tags: SEO, 404, Redirect, 301, Similar, Related, Search, Broken Link, Webmaster Tools, Google
5
  Requires at least: 4.0
6
  Tested up to: 4.9.8
7
+ Stable tag: 1.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
11
+ Automatically Redirect any 404 page to a Similar Post based on the Title Post Type & Taxonomy using 301 or 302 Redirects!
12
 
13
  == Description ==
14
 
15
  Welcome to WP 404 Auto Redirect to Similar Post!
16
 
17
+ This plugin automatically redirect any 404 page to similar posts based on Title, Post Types & Taxonomies. If nothing similar is found, redirect visitors to the homepage or a custom URL.
18
 
19
  = Features: =
20
 
30
  * Choose the redirection HTTP header status:
31
  * 301 headers
32
  * 302 headers
33
+ * Exclude Post Types from possible redirections.
34
+ * Exclude Taxonomies from possible redirections.
35
+ * Exclude Posts based on a custom post meta.
36
+ * Exclude Terms based on a custom term meta.
37
  * Display the Debug Console instead of being redirected (Admin).
38
  * Preview possible redirection from the administration panel.
39
 
40
+ = *New* Features: =
41
+
42
+ * Expose 'WP-404-Auto-Redirect' headers on 404 pages. (Admin).
43
+ * Log redirections in the /wp-content/debug.log file.
44
+ * Create your own search engines logic.
45
+ * Create your own search engines groups & fire sequence.
46
+
47
+ = *New* Engines & Groups: =
48
+
49
+ WP 404 Auto Redirect to Similar Post 1.0 introduces the concept of engines and groups which let you customize your own searching & matching logic. The plugin comes with 5 engines and 1 default group out of the box!
50
+
51
+ Default Group Engines:
52
+
53
+ 1. Fix URL
54
+ Find and fix common URL mistakes.
55
+
56
+ 2. Direct Match
57
+ Search for a Post matching perfectly keywords.
58
+
59
+ 4. Search Post
60
+ Search for a similar Post.
61
+
62
+ 5. Search Term
63
+ Search for a similar Term.
64
+
65
+ 6. Search Post: Fallback
66
+ If a Post Type is set in the WP Query, redirect to the Post Type Archive.
67
+
68
  = But Also: =
69
 
70
  * Easy to Install / Uninstall.
73
 
74
  = Compatibility: =
75
 
76
+ WP 404 Auto Redirect to Similar Post is 100% compatible with all popular manual redirection plugins:
77
 
78
  * [Redirection](https://wordpress.org/plugins/redirection/)
79
  * [Simple 301 Redirects](https://wordpress.org/plugins/simple-301-redirects/)
80
  * [Yoast Redirections](https://yoast.com/wordpress/plugins/seo/redirects-manager/)
81
  * etc...
82
 
83
+ If you use one of them, but missed a manual redirection and a 404 is about to be displayed, WP 404 Auto Redirect to Similar Post will cover you.
84
+
85
  == Reviews ==
86
 
87
+ They talk about us! :)
88
 
89
  * [Quels plugins utiliser pour corriger les erreurs 404 sous WordPress ?](https://sebastienpierrepack.com/plugins-corriger-404-wordpress/)
90
  * [6+ 404 Redirect WordPress Plugins 2018 (Free and Paid)](https://www.formget.com/404-redirect-wordpress-plugins/)
98
 
99
  == Frequently Asked Questions ==
100
 
101
+ = Developers: Create a Custom Group =
102
 
103
+ **Advanced Usage:** If you don't know how to use filters & actions, please read the official [WordPress Plugin API](https://codex.wordpress.org/Plugin_API).
104
 
 
105
 
106
+ `
107
+ // Create a Group with only 3 Default Engines, and set a custom fire sequence
108
+ add_action('wp404arsp/search/init', 'my_404_group');
109
+ function my_404_group($query){
110
+
111
+ wp404arsp_register_group(array(
112
+
113
+ // Set Group Name
114
+ 'name' => 'My Group',
115
+
116
+ // Set Group Slug
117
+ 'slug' => 'my_group',
118
+
119
+ // Set Engines & the fire sequence
120
+ 'engines' => array(
121
+ 'default_post', // Add Default: Search Post Engine
122
+ 'default_fix_url', // Add Default: Fix URL Engine
123
+ 'default_direct', // Add Default: Default: Direct Match Engine
124
+ )
125
+
126
+ ));
127
+
128
+ }
129
 
130
+ // Trigger the Custom Group: 'My Group' when the 404 Page URL starts with '/product/xxxx/'
131
+ add_filter('wp404arsp/search/group', 'my_404_group_trigger', 10, 2);
132
+ function my_404_group_trigger($group, $query){
133
 
134
+ // Developers: Print $query array for more request context
135
+
136
+ // Our condition: 404 Page URL starts with '/product/xxxx/'
137
+ if(preg_match('#/product/(.+?)/?$#i', $query['request']['url'])){
138
+ $group = 'my_group'; // My Group Slug
139
+ }
140
+
141
+ // Always return Group
142
+ return $group;
143
+
144
+ }
145
+ `
146
+
147
+ = Developers: Create a Custom Engine =
148
+
149
+ **Advanced Usage:** If you don't know how to use filters & actions, please read the official [WordPress Plugin API](https://codex.wordpress.org/Plugin_API).
150
 
 
151
 
 
152
  `
153
+ // Create a Custom Engine
154
+ add_action('wp404arsp/search/init', 'my_404_group_engine');
155
+ function my_404_group_engine($query){
156
+
157
+ wp404arsp_register_engine(array(
158
+
159
+ // Set Engine Name
160
+ 'name' => 'My Engine',
161
 
162
+ // Set Engine Slug
163
+ 'slug' => 'my_engine',
164
+
165
+ // Set Engine Weight (Score = Keyword_Found * Weight)
166
+ 'weight' => 100,
167
+
168
+ // Set Primary Option (true|false). If Primary is true, then stop fire sequence if the score > 0.
169
+ 'primary' => true
170
+
171
+ ));
172
+
173
+ // Use the engine in a new Group called 'My Group'
174
+ wp404arsp_register_group(array(
175
+
176
+ // Set Group Name
177
+ 'name' => 'My Group',
178
+
179
+ // Set Group Slug
180
+ 'slug' => 'my_group',
181
+
182
+ // Set Engines & the fire sequence
183
+ 'engines' => array(
184
+ 'my_engine', // Add My Engine
185
+ )
186
+
187
+ ));
188
+
189
+ }
190
+
191
+ // Trigger the Custom Group: 'My Group' when the 404 Page URL starts with '/product/xxxx/'
192
+ add_filter('wp404arsp/search/group', 'my_404_group_trigger', 10, 2);
193
+ function my_404_group_trigger($group, $query){
194
+
195
+ // Developers: Print $query array for more request context
196
+
197
+ // Our condition: 404 Page URL starts with '/product/xxxx/'
198
+ if(preg_match('#/product/(.+?)/?$#i', $query['request']['url'])){
199
+ $group = 'my_group'; // My Group Slug
200
+ }
201
+
202
+ // Always return Group
203
+ return $group;
204
+
205
  }
 
206
 
207
+ // Define a Custom Engine Logic
208
+ add_filter('wp404arsp/search/engine/my_engine', 'my_404_engine_definition', 10, 3);
209
+ function my_404_engine_definition($result, $query, $group){
210
 
211
+ // Developers: Print $query array for more request context
212
 
213
+ // You have access to $query & the current $group as a context for the engine logic
214
+ // In this example 'My Engine' is the only engine in 'My Group'
215
+ // 'My Group' is triggered when the 404 Page URL starts with '/product/xxxx/'
216
+
217
+ // What we want: Search for a similar post inside a specific Post Type: 'project'
218
+
219
+ // Grab all Keywords in the URL
220
+ $keywords = explode('-', $query['request']['keywords']['all']);
221
+
222
+ // Run Search
223
+ $search = wp404arsp_search(array(
224
+ 'keywords' => $keywords, // Add keywords
225
+ 'mode' => 'post', // Search for Post
226
+ 'post_type' => 'project', // inside Post Type: 'project'
227
+ ), $query);
228
+
229
+ // Found something!
230
+ if($search['score'] > 0){
231
+
232
+ // Return result
233
+ return array(
234
+ 'score' => $search['score'],
235
+ 'url' => get_permalink($search['post_id']),
236
+ 'why' => "This engine is Awesome! We found a similar Product inside the Post Type <strong>project</strong>!"
237
+ );
238
+
239
+ }
240
+
241
+ // Nothing found :(
242
+ else{
243
+
244
+ return "Mehh... No similar Product found inside the Post Type <strong>project</strong>.";
245
+
246
+ }
247
+
248
+ }
249
+ `
250
 
251
+ = Developers: Manipulate existing Groups & Engines =
252
 
253
+ **Advanced Usage:** If you don't know how to use filters & actions, please read the official [WordPress Plugin API](https://codex.wordpress.org/Plugin_API).
254
 
 
255
 
 
256
  `
257
+ add_action('wp404arsp/search/init', 'my_404_manipulate_groups_and_engines');
258
+ function my_404_manipulate_groups_and_engines($query){
259
+
260
+ // Move the default engine 'Direct Match' at the end of the 'Default Group' fire Sequence
261
+ wp404arsp_reorder_group_engines(array(
262
+
263
+ // Target Group Slug
264
+ 'group' => 'default',
265
+
266
+ // Target Engine Slug
267
+ 'engine' => 'default_direct',
268
+
269
+ // Set new Position in fire sequence. (In this example: 4 instead of 2).
270
+ 'order' => 4
271
+
272
+ ));
273
+
274
+ // Register new Engines & Fire Sequence for the existing Group 'My Group'
275
+ wp404arsp_register_group_engines(array(
276
+
277
+ // Target Group Slug
278
+ 'group' => 'my_group',
279
+
280
+ // New Engines & Fire Sequence
281
+ 'engines' => array(
282
+ 'my_engine', // Add Custom: My Engine
283
+ 'default_post' // Add Default: Search Post Engine
284
  )
285
+
286
+ ));
287
+
288
+ // Deregister an existing Engine.
289
+ // The engine will be removed from any Groups which use it. The engine won't be registered anymore.
290
+
291
+ // Target specific Engine Slug
292
+ wp404arsp_deregister_engine('my_another_engine');
293
+
294
+ }
295
+ `
296
 
297
+ = Developers: Always use a custom Group =
 
 
 
 
 
 
 
 
 
298
 
299
+ **Advanced Usage:** If you don't know how to use filters & actions, please read the official [WordPress Plugin API](https://codex.wordpress.org/Plugin_API).
300
 
 
301
 
 
 
 
302
  `
303
+ // Always trigger the Custom Group 'My Group' instead of the Default Group
304
+ add_filter('wp404arsp/search/group', 'my_404_group_trigger_forever', 10, 2);
305
+ function my_404_group_trigger_forever($group, $query){
306
 
307
+ // Developers: Print $query array for more request context
308
+
309
+ // Always return 'My Group'
310
+ return 'my_group';
311
+
312
+ }
313
  `
314
+
315
+ = Developers: Disable the plugin initialization at some conditions =
316
+
317
+ **Advanced Usage:** If you don't know how to use filters & actions, please read the official [WordPress Plugin API](https://codex.wordpress.org/Plugin_API).
318
+
319
+
320
+ `
321
+ // Do not load the plugin if the 404 URL starts with '/calendar/xxxx/'
322
+ add_filter('wp404arsp/init', 'my_404_no_init', 10, 2);
323
+ function my_404_no_init($init, $query){
324
+
325
+ // Developers: Print $query array for more request context
326
+
327
+ if(preg_match('#/calendar/(.+?)/?$#i', $query['request']['url']))
328
+ $init = false;
329
 
330
+ return $init;
331
+
332
  }
333
  `
334
 
335
+ = Developers: Send an e-mail after every redirection =
336
+
337
+ **Advanced Usage:** If you don't know how to use filters & actions, please read the official [WordPress Plugin API](https://codex.wordpress.org/Plugin_API).
338
 
 
339
 
 
340
  `
341
+ // Do something after a redirection
342
+ add_action('wp404arsp/after_redirect', 'my_404_after_redirect');
343
+ function my_404_after_redirect($query){
344
+
345
+ // Developers: Print $query array for more request context
346
+
347
  // Send me an e-mail
348
  wp_mail(
349
  'my@email.com',
350
  'WP 404 Auto Redirect: New redirection',
351
+ 'Hi! New redirection from ' . $args['request']['url'] . ' to ' . $query['redirection']['url'],
352
  array('Content-Type: text/html; charset=UTF-8')
353
  );
354
 
370
  1. Admin: Settings Page
371
  2. Admin: Post Types
372
  3. Admin: Taxonomies
373
+ 4. Admin: Engines
374
  5. Front: Debug Console
375
 
376
  == Changelog ==
377
 
378
+ = 1.0 =
379
+ * Added: Log redirections in the /wp-content/debug.log file.
380
+ * Added: Expose 'WP-404-Auto-Redirect' headers on 404 pages. (Admin).
381
+ * Added: Groups & Engines Feature.
382
+ * Added: Groups & Engines Documentation & Examples (developers).
383
+ * Added: Groups & Engines Admin panel.
384
+ * Added: Index.php file redirecting to root domain (avoid plugin folder file listing).
385
+ * Added: `action('wp404arsp/search/init', $query)`
386
+ * Added: `filter('wp404arsp/search/group', $group, $query)`
387
+ * Added: `filter('wp404arsp/search/query', $query)`
388
+ * Added: `filter('wp404arsp/search/engine/{engine}', $result, $query, $group)`
389
+ * Added: `filter('wp404arsp/search/results', $query)`
390
+ * Added: `filter('wp404arsp/search/redirect', $redirect, $query)`
391
+ * Improvement: Core reworked from scratch for better extensibility.
392
+ * Removed: 'Hooks' tab
393
+
394
  = 0.9.0.2 =
395
  * Fix: Plugin priority set to 999 by debault
396
  * Added: New Filter available `('wp404arsp/init', $init, $request_uri)`
wp-404-auto-redirect-similar-post.php CHANGED
@@ -1,988 +1,411 @@
1
  <?php
2
  /**
3
  * Plugin Name: WP 404 Auto Redirect to Similar Post
4
- * Description: Automatically Redirect any 404 to a Similar Post based on the Title, Post Type, Category & Taxonomy using 301 Redirects!
5
  * Author: hwk-fr
6
- * Version: 0.9.0.2
7
  * Author URI: http://hwk.fr
8
  */
9
-
10
  if(!defined('ABSPATH'))
11
- die('You are not allowed to call this page directly.');
12
-
13
- add_action('admin_menu', 'wp404arsp_admin_menu');
14
- function wp404arsp_admin_menu(){
15
- $menu = add_submenu_page('options-general.php', 'WP 404 Auto Redirect', 'WP 404 Auto Redirect', 'manage_options', 'wp-404-auto-redirect', 'wp404arsp_admin_page_html');
16
-
17
- add_action('admin_print_scripts-' . $menu, function(){
18
- wp_enqueue_script('wp404arsp_admin_js', plugins_url('assets/admin.js', __FILE__), array('jquery'));
19
- wp_enqueue_style('wp404arsp_admin_css', plugins_url('assets/admin.css', __FILE__));
20
- });
21
-
22
- }
23
-
24
- add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'wp404arsp_plugin_link');
25
- function wp404arsp_plugin_link($links){
26
- return array_merge($links, array('<a href="' . admin_url('options-general.php?page=wp-404-auto-redirect') . '">Settings</a>'));
27
- }
28
-
29
- add_action('admin_init', 'wp404arsp_admin_settings');
30
- function wp404arsp_admin_settings(){
31
- register_setting('wp404arsp_settings', 'wp404arsp_settings');
32
- }
33
-
34
- function wp404arsp_admin_page_html(){
35
- ?>
36
- <div class="wrap" id="wp404arsp_settings">
37
- <h1 class="wp-heading-inline">WP 404 Auto Redirect to Similar Post</h1>
38
- <hr class="wp-header-end" />
39
-
40
- <h2 class="nav-tab-wrapper">
41
- <a href="#settings" class="nav-tab nav-tab-active"><?php _e('Settings', 'wp404-auto-redirect'); ?></a>
42
- <a href="#post-types" class="nav-tab"><?php _e('Post Types', 'wp404-auto-redirect'); ?></a>
43
- <a href="#taxonomies" class="nav-tab"><?php _e('Taxonomies', 'wp404-auto-redirect'); ?></a>
44
- <a href="#preview" class="nav-tab"><?php _e('Preview', 'wp404-auto-redirect'); ?></a>
45
- <a href="#hooks" class="nav-tab"><?php _e('Hooks', 'wp404-auto-redirect'); ?></a>
46
- </h2>
47
-
48
- <div id="poststuff">
49
- <div id="post-body" class="metabox-holder columns-2">
50
- <div id="post-body-content">
51
-
52
- <form method="post" action="options.php">
53
- <?php
54
- settings_fields('wp404arsp_settings');
55
- do_settings_sections('wp404arsp_settings');
56
- $settings = wp404arsp_get_settings();
57
- ?>
58
-
59
- <div class="meta-box-sortables ui-sortable">
60
-
61
- <!-- Tab: Settings -->
62
- <div class="nav-tab-panel" id="settings">
63
-
64
- <div class="postbox">
65
- <div class="inside">
66
- <table class="form-table">
67
- <tbody>
68
-
69
- <tr>
70
- <th scope="row"><?php _e('Debug Mode', 'wp404-auto-redirect'); ?></th>
71
- <td>
72
- <fieldset>
73
- <legend class="screen-reader-text"><span><?php _e('Debug Mode', 'wp404-auto-redirect'); ?></span></legend>
74
- <label for="wp404arsp_settings_debug">
75
- <input
76
- name="wp404arsp_settings[debug]"
77
- id="wp404arsp_settings_debug"
78
- value="1"
79
- type="checkbox"
80
- <?php checked(1, $settings['debug'], true); ?>
81
- />
82
- <?php _e('Enable', 'wp404-auto-redirect'); ?>
83
- </label>
84
- </fieldset>
85
- <p class="description"><?php _e('Display the Debug Console instead of being redirected. <code>Administrators</code> only.', 'wp404-auto-redirect'); ?></p>
86
- </td>
87
- </tr>
88
-
89
- <tr>
90
- <th scope="row"><?php _e('Fallback Behavior', 'wp404-auto-redirect'); ?></th>
91
- <td>
92
- <fieldset>
93
- <legend class="screen-reader-text"><span><?php _e('Fallback Behavior', 'wp404-auto-redirect'); ?></span></legend>
94
- <label for="wp404arsp_settings_fallback_type">
95
- <select name="wp404arsp_settings[fallback][type]" id="wp404arsp_settings_fallback_type">
96
- <option value="home" <?php if($settings['fallback']['type'] == 'home') echo "selected"; ?>><?php _e('Redirect to Homepage', 'wp404-auto-redirect'); ?></option>
97
- <option value="custom" <?php if($settings['fallback']['type'] == 'custom') echo "selected"; ?>><?php _e('Custom Redirection', 'wp404-auto-redirect'); ?></option>
98
- <option value="disabled" <?php if($settings['fallback']['type'] == 'disabled') echo "selected"; ?>><?php _e('Default 404', 'wp404-auto-redirect'); ?></option>
99
- </select>
100
- </label>
101
-
102
- <?php
103
- $fallback = array(
104
- 'value' => home_url(),
105
- 'class' => 'disabled',
106
- 'attr' => 'readonly="readonly"',
107
- );
108
-
109
- if($settings['fallback']['type'] == 'custom'){
110
- $fallback['value'] = $settings['fallback']['url'];
111
- $fallback['attr'] = '';
112
- $fallback['class'] = '';
113
- }
114
-
115
- if($settings['fallback']['type'] == 'disabled'){
116
- $fallback['value'] = '';
117
- $fallback['attr'] = '';
118
- $fallback['class'] = 'hidden';
119
- }
120
- ?>
121
-
122
- <input name="wp404arsp_settings[fallback][home_url]" id="wp404arsp_settings_fallback_home_url" type="hidden" value="<?php echo home_url(); ?>" />
123
- <input name="wp404arsp_settings[fallback][url]" id="wp404arsp_settings_fallback_url" type="text" value="<?php echo $fallback['value']; ?>" class="<?php echo $fallback['class']; ?>" <?php echo $fallback['attr']; ?> />
124
-
125
- </fieldset>
126
- <p class="description"><?php _e('If nothing similar is found, this behavior will be applied.', 'wp404-auto-redirect'); ?></p>
127
- </td>
128
- </tr>
129
-
130
- <tr>
131
- <th scope="row"><?php _e('Redirect Headers', 'wp404-auto-redirect'); ?></th>
132
- <td>
133
- <fieldset>
134
- <legend class="screen-reader-text"><span><?php _e('Redirect Headers', 'wp404-auto-redirect'); ?></span></legend>
135
- <label for="wp404arsp_settings_method">
136
- <select name="wp404arsp_settings[method]" id="wp404arsp_settings_method">
137
- <option value="301" <?php if($settings['method'] == 301) echo "selected"; ?>>301 method</option>
138
- <option value="302" <?php if($settings['method'] == 302) echo "selected"; ?>>302 method</option>
139
- </select>
140
- </label>
141
- </fieldset>
142
- <p class="description"><?php _e('Learn more about <a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes" target="_blank">HTTP headers & redirections</a>.', 'wp404-auto-redirect'); ?></p>
143
- </td>
144
- </tr>
145
-
146
- <tr>
147
- <th scope="row"><?php _e('Plugin Priority', 'wp404-auto-redirect'); ?></th>
148
- <td>
149
- <fieldset>
150
- <legend class="screen-reader-text"><span><?php _e('Plugin Priority', 'wp404-auto-redirect'); ?></span></legend>
151
- <label for="wp404arsp_settings_priority">
152
- <input
153
- type="number"
154
- name="wp404arsp_settings[priority]"
155
- id="wp404arsp_settings_priority"
156
- value="<?php echo isset($settings['priority']) ? $settings['priority'] : '999'; ?>"
157
- required
158
- />
159
- </label>
160
- </fieldset>
161
- <p class="description"><?php _e('Advanced users only. Default: <code>999</code>', 'wp404-auto-redirect'); ?></p>
162
- </td>
163
- </tr>
164
-
165
- </tbody>
166
- </table>
167
-
168
- </div>
169
- </div>
170
- </div>
171
-
172
- <!-- Tab: Post Types -->
173
- <div class="nav-tab-panel" id="post-types">
174
-
175
- <div class="postbox">
176
- <div class="inside">
177
- <table class="form-table">
178
- <tbody>
179
-
180
- <tr>
181
- <th scope="row"><?php _e('Exclude Post Meta', 'wp404-auto-redirect'); ?></th>
182
- <td>
183
- <fieldset>
184
- <legend class="screen-reader-text"><span><?php _e('Exclude Post Meta', 'wp404-auto-redirect'); ?></span></legend>
185
- <label for="wp404arsp_settings_rules_redirection_exclude_post_meta">
186
- <input
187
- name="wp404arsp_settings[rules][redirection][exclude][post_meta]"
188
- id="wp404arsp_settings_rules_redirection_exclude_post_meta"
189
- type="checkbox"
190
- value="1"
191
- <?php checked(1, $settings['rules']['redirection']['exclude']['post_meta'], true); ?>
192
- />
193
- <?php _e('Enable', 'wp404-auto-redirect'); ?>
194
- </label>
195
- </fieldset>
196
- <p class="description"><?php _e('Exclude posts with the post meta: <code>wp404arsp_no_redirect = 1</code> from possible redirections.', 'wp404-auto-redirect'); ?></p>
197
- </td>
198
- </tr>
199
-
200
- <tr>
201
- <th scope="row"><?php _e('Exclude Post Type(s)', 'wp404-auto-redirect'); ?></th>
202
- <td>
203
- <fieldset>
204
- <legend class="screen-reader-text"><span><?php _e('Exclude Post Type(s)', 'wp404-auto-redirect'); ?></span></legend>
205
- <div id="wp404arsp_settings_rules_redirection_exclude_post_types">
206
- <?php foreach(get_post_types(array('public' => true), 'objects') as $post_type) { ?>
207
- <?php
208
- $checked = '';
209
- if(
210
- isset($settings['rules']['redirection']['exclude']['post_types']) &&
211
- is_array($settings['rules']['redirection']['exclude']['post_types']) &&
212
- in_array($post_type->name, $settings['rules']['redirection']['exclude']['post_types'])
213
- )
214
- $checked = 'checked="checked"'; ?>
215
- <div><input type="checkbox" name="wp404arsp_settings[rules][redirection][exclude][post_types][]" id="wp404arsp_settings_rules_redirection_exclude_post_types_<?php echo $post_type->name; ?>" value="<?php echo $post_type->name; ?>" <?php echo $checked; ?> />
216
- <label for="wp404arsp_settings_rules_redirection_exclude_post_types_<?php echo $post_type->name; ?>"><?php echo $post_type->label; ?></label></div>
217
- <?php } ?>
218
- </div>
219
- </fieldset>
220
- <p class="description"><?php _e('Exclude one or multiple post types from possible redirections.', 'wp404-auto-redirect'); ?></p>
221
- </td>
222
- </tr>
223
-
224
- </tbody>
225
- </table>
226
- </div>
227
- </div>
228
-
229
- </div>
230
-
231
- <!-- Tab: Taxonomies -->
232
- <div class="nav-tab-panel" id="taxonomies">
233
-
234
- <div class="postbox">
235
- <div class="inside">
236
- <table class="form-table">
237
- <tbody>
238
-
239
- <tr>
240
- <th scope="row"><?php _e('Disable Taxonomy Redirection', 'wp404-auto-redirect'); ?></th>
241
- <td>
242
- <fieldset>
243
- <legend class="screen-reader-text"><span><?php _e('Disable Taxonomy Redirection', 'wp404-auto-redirect'); ?></span></legend>
244
- <label for="wp404arsp_settings_rules_redirection_disable_taxonomies">
245
- <input
246
- name="wp404arsp_settings[rules][redirection][disable][taxonomies]"
247
- id="wp404arsp_settings_rules_redirection_disable_taxonomies"
248
- type="checkbox"
249
- value="1"
250
- <?php checked(1, $settings['rules']['redirection']['disable']['taxonomies'], true); ?>
251
- />
252
- <?php _e('Disable', 'wp404-auto-redirect'); ?>
253
- </label>
254
- </fieldset>
255
- <p class="description"><?php _e('Never redirect to terms archives.', 'wp404-auto-redirect'); ?></p>
256
- </td>
257
- </tr>
258
-
259
- <tr class="wp404arsp_settings_taxonomies">
260
- <th scope="row"><?php _e('Exclude Term Meta', 'wp404-auto-redirect'); ?></th>
261
- <td>
262
- <fieldset>
263
- <legend class="screen-reader-text"><span><?php _e('Exclude Term Meta', 'wp404-auto-redirect'); ?></span></legend>
264
- <label for="wp404arsp_settings_rules_redirection_exclude_term_meta">
265
- <input
266
- name="wp404arsp_settings[rules][redirection][exclude][term_meta]"
267
- id="wp404arsp_settings_rules_redirection_exclude_term_meta"
268
- type="checkbox"
269
- value="1"
270
- <?php checked(1, $settings['rules']['redirection']['exclude']['term_meta'], true); ?>
271
- />
272
- <?php _e('Enable', 'wp404-auto-redirect'); ?>
273
- </label>
274
- </fieldset>
275
- <p class="description"><?php _e('Exclude terms with the term meta: <code>wp404arsp_no_redirect = 1</code> from possible redirections.', 'wp404-auto-redirect'); ?></p>
276
- </td>
277
- </tr>
278
-
279
- <tr class="wp404arsp_settings_taxonomies">
280
- <th scope="row"><?php _e('Exclude Taxonomie(s)', 'wp404-auto-redirect'); ?></th>
281
- <td>
282
- <fieldset>
283
- <legend class="screen-reader-text"><span><?php _e('Exclude Taxonomie(s)', 'wp404-auto-redirect'); ?></span></legend>
284
- <div id="wp404arsp_settings_rules_redirection_exclude_taxonomies">
285
- <?php foreach(get_taxonomies(array('public' => true), 'objects') as $taxonomy) { ?>
286
- <?php
287
- $checked = '';
288
- if(
289
- isset($settings['rules']['redirection']['exclude']['taxonomies']) &&
290
- is_array($settings['rules']['redirection']['exclude']['taxonomies']) &&
291
- in_array($taxonomy->name, $settings['rules']['redirection']['exclude']['taxonomies'])
292
- )
293
- $checked = 'checked="checked"'; ?>
294
- <div><input type="checkbox" name="wp404arsp_settings[rules][redirection][exclude][taxonomies][]" id="wp404arsp_settings_rules_redirection_exclude_taxonomies_<?php echo $taxonomy->name; ?>" value="<?php echo $taxonomy->name; ?>" <?php echo $checked; ?> />
295
- <label for="wp404arsp_settings_rules_redirection_exclude_taxonomies_<?php echo $taxonomy->name; ?>"><?php echo $taxonomy->label; ?></label></div>
296
- <?php } ?>
297
- </div>
298
- </fieldset>
299
- <p class="description"><?php _e('Exclude one or multiple taxonomies from possible redirections.', 'wp404-auto-redirect'); ?></p>
300
- </td>
301
- </tr>
302
-
303
- </tbody>
304
- </table>
305
- </div>
306
- </div>
307
-
308
- </div>
309
-
310
- <!-- Tab: Preview -->
311
- <div class="nav-tab-panel" id="preview">
312
-
313
- <div id="wp404arsp_settings_redirection_preview">
314
- <div class="postbox">
315
- <div class="inside">
316
- <table class="form-table">
317
- <tbody>
318
-
319
- <tr>
320
- <th scope="row"><?php echo home_url(); ?></th>
321
- <td>
322
- <input class="request" type="text" value="/example-url" />
323
- <p class="description"><?php _e('Enter the URL you would like to test, starting with <code>/</code>.', 'wp404-auto-redirect'); ?></p>
324
-
325
- <p class="submit">
326
- <?php submit_button(__('Preview URL', 'wp404-auto-redirect'), 'secondary', '', false); ?>
327
- <span class="loading spinner"></span>
328
- </p>
329
- </td>
330
- </tr>
331
- </tbody>
332
- </table>
333
-
334
- <div class="results"></div>
335
-
336
- </div>
337
- </div>
338
- </div>
339
-
340
- </div>
341
-
342
- <!-- Tab: Hooks -->
343
- <div class="nav-tab-panel" id="hooks">
344
-
345
- <div class="postbox">
346
- <div class="inside">
347
- <p><strong>Note:</strong> If you don't know about filters & actions, please read the official <a href="https://codex.wordpress.org/Plugin_API" target="_blank">WordPress Plugin API</a>.</p>
348
-
349
- <h3>Filter: wp404arsp/init</h3>
350
-
351
- <p>This filter is applied at the initialization of the plugin, in the <code>template_redirect</code> action. By default, <code>$init = true</code> which means that the plugin will take action when a 404 is about to be displayed. If is set to <code>false</code>, then le plugin will stop it's initialization. The second argument: <code>$request_uri</code> is the requested URI sent to the browser.</p>
352
-
353
- <p>Returning <code>$init</code> as a boolean is <strong>mandatory</strong>.</p>
354
-
355
- <p><strong>Usage example:</strong></p>
356
- <pre><code style="display:block; white-space:pre-wrap;">add_filter('wp404arsp/init', 'my_404_no_init', 10, 2);
357
- function my_404_no_init($init, $request_uri){
358
- if($request_uri == 'https://www.my-website.com/custom-page')
359
- $init = false;
360
-
361
- return $init;
362
- }</code></pre>
363
- </div>
364
- </div>
365
-
366
- <div class="postbox">
367
- <div class="inside">
368
-
369
- <h3>Filter: wp404arsp/redirect</h3>
370
-
371
- <p>This filter is applied right before the plugin sends a redirection. You can use it to set a custom behavior depending on multiple factors. The array's key <code>$args['url']</code> will be used as the redirection's URI. If the key <code>$args['url']</code> is set to <code>false</code>, the plugin's fallback behavior will be bypassed and the default 404 page will be displayed.
372
-
373
- <p>The second argument: <code>$settings</code> is an array of the plugin's settings.</p>
374
-
375
- <p>Returning an <code>$args</code> array is <strong>mandatory</strong>.</p>
376
-
377
- <p><strong>Args array structure:</strong></p>
378
- <pre><code style="display:block; white-space:pre-wrap;">Array(
379
- [request] => Array(
380
- [full] => /this-is-404-page
381
- [dirname] =>
382
- [filename] => this-is-404-page
383
- [extension] =>
384
- )
385
-
386
- [preview] =>
387
- [process] => Array(
388
- [request] => /this-is-404-page
389
- [keywords] => Array
390
- (
391
- [sanitized] => this-is-404-page
392
- [explode] => Array
393
- (
394
- [0] => this-is-404-page
395
- )
396
-
397
- )
398
-
399
- )
400
-
401
- [why] => Nothing similar found matching the requested URL.
402
- [url] => https://www.my-website.com
403
- )</code></pre>
404
- <p><strong>Usage example:</strong></p>
405
- <pre><code style="display:block; white-space:pre-wrap;">add_filter('wp404arsp/redirect', 'my_404_redirect', 10, 2);
406
- function my_404_redirect($args, $settings){
407
- if($args['request']['full'] == '/this-is-404-page')
408
- $args['url'] = 'https://www.my-website.com/an-another-fallback-page';
409
-
410
- return $args;
411
- }</code></pre>
412
- </div>
413
- </div>
414
-
415
- <div class="postbox">
416
- <div class="inside">
417
-
418
- <h3>Action: wp404arsp/after_redirect</h3>
419
-
420
- <p>This action is called right after a redirection is sent to the browser. You can use it to perform custom actions.</p>
421
-
422
- <p><strong>Usage example:</strong></p>
423
- <pre><code style="display:block; white-space:pre-wrap;">add_action('wp404arsp/after_redirect', 'my_404_after_redirect', 10, 2);
424
- function my_404_after_redirect($args, $settings){
425
- // Send me an e-mail
426
- wp_mail(
427
- 'my@email.com',
428
- 'WP 404 Auto Redirect: New redirection',
429
- 'Hi! New redirection from ' . $args['request']['full'] . ' to ' . $args['url'],
430
- array('Content-Type: text/html; charset=UTF-8')
431
- );
432
-
433
- return;
434
- }</code></pre>
435
- </div>
436
- </div>
437
-
438
- </div>
439
-
440
- </div>
441
-
442
- <div class="postbox">
443
- <div class="inside">
444
- <p class="submit">
445
- <?php submit_button(__('Save Settings', 'wp404-auto-redirect'), 'primary', '', false); ?>
446
- </p>
447
- </div>
448
- </div>
449
-
450
- </form>
451
-
452
- </div>
453
-
454
- <div id="postbox-container-1" class="postbox-container">
455
- <div class="meta-box-sortables">
456
- <div class="postbox">
457
 
458
- <div class="inside">
459
- <img src="<?php echo plugins_url('assets/logo.png', __FILE__); ?>" class="logo" />
460
 
461
- <p><?php _e('Found a bug? Need help? Head over the <a href="https://wordpress.org/plugins/wp-404-auto-redirect-to-similar-post/" target="_blank">official plugin page</a>.', 'wp404-auto-redirect'); ?></p>
462
-
463
- <hr />
464
-
465
- <p><?php _e('Enjoying this plugin? Please rate it. It\'s always much appreciated!', 'wp404-auto-redirect'); ?></p>
466
- <p>
467
- <a href="https://wordpress.org/support/plugin/wp-404-auto-redirect-to-similar-post/reviews/#new-post" target="_blank" class="button"><?php _e('Rate the plugin', 'wp404-auto-redirect'); ?></a>
468
- </p>
469
- </div>
470
-
471
- </div>
472
- </div>
473
- </div>
474
 
475
- </div>
476
- <br class="clear">
477
-
478
- </div>
479
- </div>
480
- <?php }
481
 
482
- $wp404arsp_settings = get_option('wp404arsp_settings');
483
- $wp404arsp_priority = isset($wp404arsp_settings['priority']) ? $wp404arsp_settings['priority'] : 999;
 
 
484
 
485
- add_action('template_redirect', 'wp404arsp_init', $wp404arsp_priority);
486
- function wp404arsp_init(){
487
- if(!is_404())
488
- return;
489
-
490
- if(wp_doing_ajax())
491
- return;
492
 
493
- if(is_admin())
494
- return;
495
-
496
- if(!isset($_SERVER['REQUEST_URI']) || empty($_SERVER['REQUEST_URI']))
497
- return;
498
 
499
- if(isset($_SERVER['SCRIPT_URI']) && !empty($_SERVER['SCRIPT_URI']) && $_SERVER['SCRIPT_URI'] == admin_url('admin-ajax.php'))
500
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
 
502
- if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest')
503
- return;
 
 
 
 
 
 
 
 
504
 
505
- if(!apply_filters('wp404arsp/init', true, $_SERVER['REQUEST_URI']))
506
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507
 
508
- wp404arsp_request($_SERVER['REQUEST_URI']);
509
- }
510
-
511
- function wp404arsp_request($request, $preview = false){
512
-
513
- $query = array();
514
- $query['request']['full'] = urldecode(htmlspecialchars($request));
515
- $query['preview'] = $preview;
516
-
517
- if(preg_match('#/(?<slug>page|paged)/(?<page>[0-9]+)/?$#i', $request, $pagination)){
518
- $request_no_pagination = str_replace($pagination[0], '', $request);
519
- $query['request']['pagination'] = array(
520
- 'full' => $pagination[0],
521
- 'slug' => $pagination['slug'],
522
- 'page' => $pagination['page']
523
- );
524
- }
525
-
526
- $path = pathinfo(urldecode(htmlspecialchars(strtok($request, '?'))));
527
- $path['filename'] = wp404arsp_sanitize(pathinfo(basename($request), PATHINFO_FILENAME));
528
- $path['dirname'] = trim($path['dirname'], '/');
529
- $path['dirname'] = trim($path['dirname'], '\\');
530
-
531
- $query['request']['dirname'] = $path['dirname'];
532
- $query['request']['filename'] = $path['filename'];
533
- $query['request']['extension'] = (!empty($path['extension'])) ? $path['extension'] : '';
534
-
535
- // Pagination found, try the URL without pagination
536
- if(!empty($query['request']['pagination']))
537
- return wp404arsp_redirect(
538
- array_merge($query, array(
539
- 'url' => home_url() . $request_no_pagination,
540
- 'why' => "Pagination found in the requested URL. Trying to reach same URL without pagination."
541
- ))
542
  );
543
-
544
- wp404arsp_setup($query);
545
-
546
- }
547
-
548
- function wp404arsp_setup($query){
549
- if(!isset($query['request']['full']) || empty($query['request']['full']))
550
- return;
551
-
552
- $settings = wp404arsp_get_settings();
553
- $query['process']['request'] = $query['request']['full'];
554
-
555
- global $wp_query;
556
- $current_wp_query = $wp_query->query;
557
-
558
- if(isset($current_wp_query['post_type'])){
559
-
560
- $query['process']['post_type'] = $current_wp_query['post_type'];
561
- $post_type = get_post_type_object($current_wp_query['post_type']);
562
- if( stripos($query['request']['full'], $post_type->rewrite['slug']) !== false )
563
- $query['process']['request'] = str_replace('//', '/', substr_replace($query['request']['full'], '', stripos($query['request']['full'], $post_type->rewrite['slug']), strlen($post_type->rewrite['slug'])));
564
-
565
- elseif( stripos($query['request']['full'], $post_type->has_archive) !== false )
566
- $query['process']['request'] = str_replace('//', '/', substr_replace($query['request']['full'], '', stripos($query['request']['full'], $post_type->has_archive), strlen($post_type->has_archive)));
567
- }
568
-
569
- $query['process']['keywords']['sanitized'] = wp404arsp_sanitize(str_replace('.' . $query['request']['extension'], '', $query['process']['request']));
570
-
571
- $keywords = explode('/', trim($query['process']['request'], '/'));
572
- foreach($keywords as $keyword){
573
- if(isset($query['request']['extension']) && !empty($query['request']['extension']))
574
- $keyword = str_replace('.' . $query['request']['extension'], '', $keyword);
575
-
576
- $query['process']['keywords']['explode'][] = $keyword;
577
- }
578
- $query['process']['keywords']['explode'] = array_reverse($query['process']['keywords']['explode']);
579
-
580
- $args = wp404arsp_search($query, $settings);
581
- return wp404arsp_redirect($args, $settings);
582
- }
583
-
584
- function wp404arsp_search($query, $settings){
585
-
586
- // Direct
587
- if(($get_post_types = wp404arsp_get_post_types('names')) && !empty($get_post_types)){
588
- foreach($get_post_types as $post_type){
589
- if(!$post = get_page_by_path($query['process']['keywords']['explode'][0], 'object', $post_type))
590
- continue;
591
 
592
- if(get_post_status($post->ID) != 'publish')
593
- break;
 
 
 
 
 
 
 
 
 
594
 
595
- if($settings['rules']['redirection']['exclude']['post_meta'] && get_post_meta($post->ID, 'wp404arsp_no_redirect', true) == '1')
596
- break;
 
 
 
597
 
598
- return array_merge($query, array(
599
- 'score' => 'direct',
600
- 'id' => (int)$post->ID,
601
- 'url' => get_permalink((int)$post->ID),
602
- 'why' => "Part of the requested URL already exist as post of the post_type <strong>" . get_post_type((int)$post->ID) . "</strong>"
603
- ));
604
-
605
  }
 
 
 
 
 
 
 
 
 
 
 
606
  }
607
-
608
- // Search
609
- $keywords = explode('-', $query['process']['keywords']['sanitized']);
610
- $sql = wp404arsp_search_sql(array('keywords' => $keywords));
611
-
612
- // Term
613
- if(empty($sql['result']) || $sql['result']['score'] < 1){
614
- if($settings['rules']['redirection']['disable']['taxonomies'])
615
- return array_merge($query, array(
616
- 'why' => "Nothing similar found matching the requested URL."
617
- ));
618
-
619
- $sql = wp404arsp_search_sql(array('keywords' => $keywords, 'mode' => 'term'));
620
-
621
- if(empty($sql['result']) || $sql['result']['score'] < 1)
622
- return array_merge($query, array(
623
- 'why' => "Nothing similar found matching the requested URL."
624
- ));
625
-
626
- return array_merge($query, array(
627
- 'score' => $sql['result']['score'],
628
- 'id' => (int)$sql['result']['term_id'],
629
- 'url' => get_term_link((int)$sql['result']['term_id']),
630
- 'why' => "No similar post found, but a similar <strong>term</strong> was found."
631
- ));
632
- }
633
-
634
- // Post
635
- $return = array_merge($query, array(
636
- 'score' => $sql['result']['score'],
637
- 'id' => (int)$sql['result']['ID'],
638
- 'url' => get_permalink((int)$sql['result']['ID']),
639
- 'why' => "Similar post of post_type <strong>" . get_post_type((int)$sql['result']['ID']) . "</strong> was found."
640
- ));
641
-
642
- // Post: If Post Type found, check other post types & compare score
643
- if(isset($query['process']['post_type']) && !empty($query['process']['post_type'])){
644
-
645
- $sql = wp404arsp_search_sql(array('keywords' => $keywords, 'post_type' => $query['process']['post_type']));
646
- if(!empty($sql['result']) && $sql['result']['score'] >= 1 && $sql['result']['score'] >= $return['score']){
647
- $return = array_merge($query, array(
648
- 'score' => $sql['result']['score'],
649
- 'id' => (int)$sql['result']['ID'],
650
- 'url' => get_permalink((int)$sql['result']['ID']),
651
- 'why' => "Similar post of post_type <strong>" . $query['process']['post_type'] . "</strong> was found."
652
- ));
653
-
654
- }else{
655
- $return['why'] = "Post of post_type <strong>" . $query['process']['post_type'] . "</strong> was found, but the highest score was found within the post_type <strong>" . get_post_type($return['id']) . "</strong>";
656
-
657
- }
658
-
659
- }
660
-
661
- return $return;
662
-
663
- }
664
-
665
- function wp404arsp_redirect($args, $settings = false){
666
- if(!$settings)
667
- $settings = wp404arsp_get_settings();
668
-
669
- $fallback = $settings['fallback']['url'];
670
- if($settings['fallback']['type'] == 'disabled')
671
- $fallback = false;
672
-
673
- if((!isset($args['url']) || empty($args['url'])))
674
- $args['url'] = $fallback;
675
-
676
- $args = apply_filters('wp404arsp/redirect', $args, $settings);
677
- if(!$args['url'])
678
- return;
679
-
680
- if(is_user_logged_in() && current_user_can('administrator') && ($settings['debug'] || (isset($args['preview']) && $args['preview'])))
681
- return wp404arsp_debug($args);
682
-
683
- wp404arsp_redirect_to($args, $settings);
684
-
685
- return;
686
- }
687
-
688
- function wp404arsp_redirect_to($args, $settings){
689
- // Copy/paste from legacy WP_Redirect function()
690
- // File: wp-includes/pluggable.php
691
-
692
- // Added: 'WP-404-Auto-Redirect: true' header
693
- // Added: 'wp404arsp/after_redirect' action
694
- // Added: PHP exit;
695
-
696
- global $is_IIS;
697
 
698
- $status = $settings['method'];
699
- $location = apply_filters('wp_redirect', $args['url'], $status);
700
- $status = apply_filters('wp_redirect_status', $status, $location);
701
-
702
- if(!$location)
703
- return false;
704
-
705
- $location = wp_sanitize_redirect($location);
706
-
707
- if(!$is_IIS && PHP_SAPI != 'cgi-fcgi')
708
- status_header($status);
709
-
710
- header("Location: $location", true, $status);
711
- header('WP-404-Auto-Redirect: true');
712
-
713
- do_action('wp404arsp/after_redirect', $args, $settings);
714
-
715
- exit;
716
- }
717
-
718
- function wp404arsp_debug($args){
719
- $settings = wp404arsp_get_settings();
720
- $method = $settings['method'];
721
- $fallback = $settings['fallback']['type'] != 'disabled' ? $settings['fallback']['url'] : false;
722
-
723
- $title = '';
724
- if(isset($args['url']) && !empty($args['url'])){
725
- $title = 'Automatic Redirection: ' . "<a href='" . $args['url'] . "'>" . $args['url'] . "</a>" . ' (' . $method . ' Headers)';
726
-
727
- }elseif(!$fallback){
728
- $title = 'Fallback Redirection disabled. Displaying 404.';
729
-
730
- }else{
731
- $title = 'Fallback Redirection: ' . "<a href='" . $fallback . "'>" . $fallback . "</a>" . ' (' . $method . ' Headers)';
732
-
733
- }
734
-
735
- ob_start(); ?>
736
-
737
- <style type="text/css">
738
- .wp404arsp_debug_page{margin:0 auto; max-width:1150px; font-family: arial, sans-serif;}
739
- .wp404arsp_debug_page h2,
740
- .wp404arsp_debug_page h4{text-align:center;}
741
- .wp404arsp_debug_page pre{background:#f4f4f4; padding:15px; overflow:auto;}
742
- .wp404arsp_debug_page a{color:blue;}
743
- .wp404arsp_debug_page p{font-size:12px;}
744
- </style>
745
- <div class="wp404arsp_debug_page">
746
- <?php if(!isset($args['preview'])){ ?>
747
- <h2>WP 404 Auto Redirect to Similar Post</h2>
748
- <p>This is the <strong>debug console</strong> of WP 404 Auto redirect to Similar Post Plugin which is only visible to administrators. Head over your <a href="<?php echo admin_url('options-general.php?page=wp-404-auto-redirect'); ?>">settings page</a> if you would like to disable it.</p>
749
- <hr />
750
- <?php } ?>
751
- <pre>Requested URL: <a href="<?php echo home_url(); ?><?php echo $args['request']['full']; ?>"><?php echo home_url(); ?><?php echo $args['request']['full']; ?></a><br />
752
- <?php echo $title; ?><br />
753
- Details: <?php echo $args['why']; ?></pre>
754
- <pre><?php print_r($args); ?></pre>
755
- </div>
756
-
757
- <?php
758
- echo ob_get_clean();
759
- exit;
760
- }
761
-
762
- function wp404arsp_search_sql($args = array()){
763
- global $wpdb;
764
-
765
- $settings = wp404arsp_get_settings();
766
- $args = wp_parse_args($args, array(
767
- 'keywords' => null,
768
- 'mode' => null,
769
- 'post_type' => null,
770
- ));
771
-
772
- if(empty($args['mode']) || !empty($args['post_type'])){
773
-
774
- $sql = "SELECT p.ID, ";
775
- foreach($args['keywords'] as $k){
776
- $sql .= "
777
- if(INSTR(LCASE(p.post_name), '" . $k . "'), 1, 0) + ";
778
- }
779
-
780
- $sql .= "0 AS score FROM " . $wpdb->posts . " AS p";
781
 
782
- if($settings['rules']['redirection']['exclude']['post_meta']){
783
- $sql .= "
784
- INNER JOIN " . $wpdb->postmeta . " AS pm ON(p.ID = pm.post_id)
785
- WHERE p.post_status = 'publish' AND (pm.meta_key = 'wp404arsp_no_redirect' AND pm.meta_value != '1') AND ";
 
 
 
 
 
 
 
 
 
786
 
787
- }else{
788
- $sql .= "
789
- WHERE p.post_status = 'publish' AND ";
 
 
790
 
791
- }
792
-
793
- if(!empty($args['post_type'])){
794
- $sql .= "
795
- p.post_type = '" . $args['post_type'] . "'";
796
-
797
- }else{
798
-
799
- if(($get_post_types = wp404arsp_get_post_types('names')) && !empty($get_post_types)){
800
- foreach($get_post_types as $pt){
801
- $post_types[] = "
802
- p.post_type = '" . $pt . "'";
803
- }
804
- $sql .= '(' . implode(' OR ', $post_types) . ')';
 
 
 
 
805
  }
806
-
807
- }
808
-
809
- $sql .= "
810
- ORDER BY score DESC, post_modified DESC LIMIT 1";
811
-
812
- }elseif($args['mode'] == 'term'){
813
-
814
- $sql = "SELECT t.term_id, ";
815
-
816
- foreach($args['keywords'] as $k){
817
- $sql .= "
818
- if(INSTR(LCASE(t.slug), '" . $k . "'), 1, 0) + ";
819
- }
820
-
821
- $sql .= "
822
- 0 AS score FROM " . $wpdb->terms . " AS t";
823
 
824
- $sql .= "
825
- INNER JOIN " . $wpdb->term_taxonomy . " AS tt ON(t.term_id = tt.term_id)";
826
 
827
- if($settings['rules']['redirection']['exclude']['term_meta']){
828
- $sql .= "
829
- INNER JOIN " . $wpdb->termmeta . " AS tm ON(t.term_id = tm.term_id)";
830
- }
831
 
832
- if(($get_taxonomies = wp404arsp_get_taxonomies('names')) && !empty($get_taxonomies)){
833
- foreach($get_taxonomies as $tax){
834
- $taxonomies[] = "
835
- tt.taxonomy = '" . $tax . "'";
836
- }
837
- $sql .= '
838
- WHERE (' . implode(' OR ', $taxonomies) . ')';
839
-
840
- if($settings['rules']['redirection']['exclude']['term_meta']){
841
- $sql .= "
842
- AND (tm.meta_key = 'wp404arsp_no_redirect' AND tm.meta_value != '1')";
 
 
843
  }
844
  }
845
 
846
- $sql .= "
847
- ORDER BY score DESC LIMIT 1";
848
-
849
- }
850
-
851
- return array(
852
- 'result' => $wpdb->get_row($sql, 'ARRAY_A'),
853
- 'dump' => $sql
854
- );
855
- }
856
-
857
- add_action('wp_ajax_wp404arsp_ajax_preview', 'wp404arsp_ajax_preview');
858
- function wp404arsp_ajax_preview() {
859
- if(!current_user_can('administrator'))
860
- return;
861
-
862
- $request = $_POST['request'];
863
- wp404arsp_request($request, true);
864
- wp_die();
865
- }
866
-
867
- function wp404arsp_sanitize($input){
868
- return sanitize_title(str_replace(array('_', '/'), '-', $input));
869
- }
870
-
871
- function wp404arsp_get_settings(){
872
- $option = get_option('wp404arsp_settings');
873
- $return = wp404arsp_wp_parse_args_recursive($option, array(
874
- 'debug' => null,
875
- 'logs' => null,
876
- 'method' => 301,
877
- 'priority' => 999,
878
- 'fallback' => array(
879
- 'type' => 'home',
880
- 'url' => home_url(),
881
- 'home_url' => home_url(),
882
- ),
883
- 'rules' => array(
884
- 'redirection' => array(
885
- 'exclude' => array(
886
- 'post_types' => array(),
887
- 'post_meta' => null,
888
- 'taxonomies' => array(),
889
- 'term_meta' => null,
890
- ),
891
- 'disable' => array(
892
- 'taxonomies' => null
893
- )
894
- ),
895
- )
896
- ));
897
-
898
- if($return['fallback']['type'] == 'home')
899
- $return['fallback']['url'] = home_url();
900
-
901
- if(((int)$return['method'] != 301) && ((int)$return['method'] != 302))
902
- $return['method'] = 301;
903
-
904
- return $return;
905
- }
906
-
907
- function wp404arsp_wp_parse_args_recursive(&$a, $b){
908
- $a = (array) $a;
909
- $b = (array) $b;
910
- $result = $b;
911
-
912
- foreach($a as $k => &$v){
913
- if(is_array($v) && isset($result[$k])){
914
- $result[$k] = wp404arsp_wp_parse_args_recursive($v, $result[$k]);
915
- }else{
916
- $result[$k] = $v;
917
  }
 
 
 
 
 
 
 
918
  }
919
-
920
- return $result;
921
- }
922
 
923
- function wp404arsp_get_post_types($type = 'names'){
924
- $settings = wp404arsp_get_settings();
925
- $get_post_types = get_post_types(array('public' => true), $type);
926
-
927
- // No exclude
928
- if( !isset($settings['rules']['redirection']['exclude']['post_types']) || !is_array($settings['rules']['redirection']['exclude']['post_types']) || empty($settings['rules']['redirection']['exclude']['post_types']) )
929
- return $get_post_types;
930
-
931
- // Exclude
932
- $filtered_post_types = array();
933
- foreach($get_post_types as $post_type){
934
- $search = $post_type;
935
- if($type == 'objects')
936
- $search = $post_type->name;
937
 
938
- if(in_array($search, $settings['rules']['redirection']['exclude']['post_types']))
939
- continue;
 
 
 
 
 
 
940
 
941
- $filtered_post_types[] = $search;
942
  }
943
-
944
- return $filtered_post_types;
945
- }
946
 
947
- function wp404arsp_get_taxonomies($type = 'names'){
948
- $settings = wp404arsp_get_settings();
949
- $get_taxonomies = get_taxonomies(array('public' => true), $type);
950
-
951
- // No exclude
952
- if( !isset($settings['rules']['redirection']['exclude']['taxonomies']) || !is_array($settings['rules']['redirection']['exclude']['taxonomies']) || empty($settings['rules']['redirection']['exclude']['taxonomies']) )
953
- return $get_taxonomies;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
954
 
955
- // Exclude
956
- $filtered_taxonomies = array();
957
- foreach($get_taxonomies as $taxonomy){
958
- $search = $taxonomy;
959
- if($type == 'objects')
960
- $search = $taxonomy->name;
 
 
 
 
 
 
 
 
 
961
 
962
- if(in_array($search, $settings['rules']['redirection']['exclude']['taxonomies']))
963
- continue;
 
964
 
965
- $filtered_taxonomies[] = $search;
966
  }
967
 
968
- return $filtered_taxonomies;
969
  }
970
 
971
- function wp404arsp_remove_stop_words($input){
972
- $words = array('a', 'about', 'above', 'after', 'again', 'against', 'all', 'am', 'an', 'and', 'any', 'are', 'arent', 'as', 'at', 'be', 'because', 'been', 'before', 'being', 'below', 'between', 'both', 'but', 'by', 'cant', 'cannot', 'could', 'couldnt', 'did', 'didnt', 'do', 'does', 'doesnt', 'doing', 'dont', 'down', 'during', 'each', 'few', 'for', 'from', 'further', 'had', 'hadnt', 'has', 'hasnt', 'have', 'havent', 'having', 'he', 'hed', 'hell', 'hes', 'her', 'here', 'heres', 'hers', 'herself', 'him', 'himself', 'his', 'how', 'hows', 'i', 'id', 'ill', 'im', 'ive', 'if', 'in', 'into', 'is', 'isnt', 'it', 'its', 'itself', 'lets', 'me', 'more', 'most', 'mustnt', 'my', 'myself', 'no', 'nor', 'not', 'of', 'off', 'on', 'once', 'only', 'or', 'other', 'ought', 'our', 'ours', 'ourselves', 'out', 'over', 'own', 'same', 'shant', 'she', 'shed', 'shell', 'shes', 'should', 'shouldnt', 'so', 'some', 'such', 'than', 'that', 'thats', 'the', 'their', 'theirs', 'them', 'themselves', 'then', 'there', 'theres', 'these', 'they', 'theyd', 'theyll', 'theyre', 'theyve', 'this', 'those', 'to', 'too', 'under', 'until', 'up', 'very', 'was', 'wasnt', 'we', 'wed', 'well', 'were', 'weve', 'werent', 'what', 'whats', 'when', 'whens', 'where', 'wheres', 'which', 'while', 'who', 'whos', 'whom', 'why', 'whys', 'with', 'wont', 'would', 'wouldnt', 'you', 'youd', 'youll', 'youre', 'youve', 'your', 'yours', 'category', 'page', 'paged');
 
 
 
 
 
 
 
 
973
 
974
- if(is_array($input)){
975
- $return = array_diff($input, $words);
976
-
977
- foreach($return as $key => $val){
978
- if(is_numeric($val))
979
- unset($return[$key]);
980
- }
981
-
982
- return array_values($return);
983
-
984
- }else{
985
- return preg_replace('/\b('.implode('|', $words).')\b/', '', $input);
986
-
987
- }
988
- }
1
  <?php
2
  /**
3
  * Plugin Name: WP 404 Auto Redirect to Similar Post
4
+ * Description: Automatically Redirect any 404 page to a Similar Post based on the Title, Post Type & Taxonomy using 301 Redirects!
5
  * Author: hwk-fr
6
+ * Version: 1.0
7
  * Author URI: http://hwk.fr
8
  */
9
+
10
  if(!defined('ABSPATH'))
11
+ exit;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
+ if(!defined('WP404ARSP_PATH'))
14
+ define('WP404ARSP_PATH', plugin_dir_path(__FILE__));
15
 
16
+ if(!defined('WP404ARSP_FILE'))
17
+ define('WP404ARSP_FILE', __FILE__);
 
 
 
 
 
 
 
 
 
 
 
18
 
19
+ if(!class_exists('WP_404_Auto_Redirect')):
 
 
 
 
 
20
 
21
+ // Traits
22
+ include_once(WP404ARSP_PATH . 'includes/admin.php');
23
+ include_once(WP404ARSP_PATH . 'includes/ajax.php');
24
+ include_once(WP404ARSP_PATH . 'includes/debug.php');
25
 
26
+ class WP_404_Auto_Redirect {
 
 
 
 
 
 
27
 
28
+ Use WP_404_Auto_Redirect_Admin;
29
+ Use WP_404_Auto_Redirect_Ajax;
30
+ Use WP_404_Auto_Redirect_Debug;
 
 
31
 
32
+ function init(){
33
+
34
+ // Helpers
35
+ include_once(WP404ARSP_PATH . 'includes/helpers.php');
36
+
37
+ // Classes
38
+ include_once(WP404ARSP_PATH . 'class/class-engines.php');
39
+ include_once(WP404ARSP_PATH . 'class/class-groups.php');
40
+ include_once(WP404ARSP_PATH . 'class/class-search.php');
41
+ include_once(WP404ARSP_PATH . 'class/class-settings.php');
42
+
43
+ // WP: Admin
44
+ add_action('admin_menu', array($this, 'admin_menu'), 10, 1);
45
+ add_filter('plugin_action_links', array($this, 'admin_link'), 10, 2);
46
+ add_action('admin_init', array($this, 'admin_settings'), 10, 1);
47
+ add_action('admin_enqueue_scripts', array($this, 'admin_scripts'), 10, 1);
48
+
49
+ // WP: Run
50
+ add_action('template_redirect', array($this, 'run'), $this->priority());
51
+
52
+ // Preview
53
+ add_action('wp_ajax_wp404arsp_ajax_preview', array($this, 'preview'), 1, 1);
54
+
55
+ // Log
56
+ add_action('wp404arsp/after_redirect', array($this, 'log'), 1, 1);
57
+
58
+ /**
59
+ ****************************************************************************
60
+ * Filters & Actions Fire Sequence:
61
+ ****************************************************************************
62
+ *
63
+ * filter('wp404arsp/init', true, $request )
64
+ *
65
+ * action('wp404arsp/search/init', $query )
66
+ * filter('wp404arsp/search/group', $group, $query )
67
+ * filter('wp404arsp/search/query', $query )
68
+ * filter('wp404arsp/search/engine/{engine}', $result, $query, $group )
69
+ * filter('wp404arsp/search/results', $query )
70
+ * filter('wp404arsp/search/redirect', $redirect, $query )
71
+ *
72
+ * filter('wp404arsp/redirect', $query )
73
+ * action('wp404arsp/after_redirect', $query )
74
+ *
75
+ ****************************************************************************
76
+ */
77
+
78
+ }
79
 
80
+ function priority(){
81
+
82
+ $priority = 999;
83
+ $wp404arsp_settings = get_option('wp404arsp_settings');
84
+ if(isset($wp404arsp_settings['priority']) && (!empty($wp404arsp_settings['priority']) || $wp404arsp_settings['priority'] === 0))
85
+ $priority = $wp404arsp_settings['priority'];
86
+
87
+ return $priority;
88
+
89
+ }
90
 
91
+ function run(){
92
+
93
+ // is 404
94
+ if(!is_404() || wp_doing_ajax() || is_admin() || wp404arsp_is_empty($_SERVER['REQUEST_URI']))
95
+ return;
96
+
97
+ // Admin Ajax
98
+ if(!wp404arsp_is_empty($_SERVER['SCRIPT_URI']) && $_SERVER['SCRIPT_URI'] == admin_url('admin-ajax.php'))
99
+ return;
100
+
101
+ // XMLRequest
102
+ if(!wp404arsp_is_empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest')
103
+ return;
104
+
105
+ // Sanitize Request
106
+ $request = urldecode(filter_input(INPUT_SERVER, 'REQUEST_URI'));
107
+ if(empty($request))
108
+ return;
109
+
110
+ $this->request($request);
111
+
112
+ }
113
 
114
+ function request($request, $preview = false){
115
+
116
+ // Pathinfo
117
+ $path = pathinfo(strtok($request, '?'));
118
+ $path['dirname'] = str_replace('\\', '/', $path['dirname']);
119
+
120
+ // Params
121
+ $params = array();
122
+ $request_parts = parse_url($request);
123
+ if(!wp404arsp_is_empty($request_parts['query']))
124
+ wp_parse_str($request_parts['query'], $params);
125
+
126
+ // Query
127
+ $query = array(
128
+ 'preview' => $preview,
129
+ 'request' => array(
130
+ 'url' => $request,
131
+ 'referrer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : false,
132
+ 'dirname' => $path['dirname'],
133
+ 'filename' => $path['filename'],
134
+ 'extension' => (!wp404arsp_is_empty($path['extension']) ? $path['extension'] : ''),
135
+ 'params' => $params,
136
+ 'keywords' => array(
137
+ 'all' => '',
138
+ 'array' => array()
139
+ ),
140
+ )
 
 
 
 
 
 
 
141
  );
142
+
143
+ // Remove Params in URL
144
+ $url = strtok($query['request']['url'], '?');
145
+
146
+ // Keywords: Sanitize
147
+ $query['request']['keywords']['all'] = str_replace('.' . $query['request']['extension'], '', $url);
148
+ $query['request']['keywords']['all'] = wp404arsp_sanitize($query['request']['keywords']['all']);
149
+
150
+ // Keywords: Explode Array
151
+ $keywords = explode('/', trim($url, '/'));
152
+ foreach($keywords as $keyword){
153
+ if(!wp404arsp_is_empty($query['request']['extension']))
154
+ $keyword = str_replace('.' . $query['request']['extension'], '', $keyword);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
+ $query['request']['keywords']['array'][] = wp404arsp_sanitize($keyword);
157
+ }
158
+
159
+ // Keywords: Reverse for priority (last part is probably the most important)
160
+ $query['request']['keywords']['array'] = array_reverse($query['request']['keywords']['array']);
161
+
162
+ // WP Query
163
+ global $wp_query;
164
+
165
+ if(isset($wp_query->query_vars)){
166
+ $query_vars = $wp_query->query_vars;
167
 
168
+ // WP Query: Post Type found
169
+ if(!wp404arsp_is_empty($query_vars['post_type']) && !wp404arsp_is_empty($query_vars['name'])){
170
+ $query['request']['wp_query']['post_type'] = $query_vars['post_type'];
171
+ $query['request']['wp_query']['name'] = $query_vars['name'];
172
+ }
173
 
174
+ // WP Query: Taxonomy found
175
+ if(!wp404arsp_is_empty($query_vars['taxonomy']) && !wp404arsp_is_empty($query_vars['term'])){
176
+ $query['request']['wp_query']['taxonomy'] = $query_vars['taxonomy'];
177
+ $query['request']['wp_query']['term'] = $query_vars['term'];
178
+ }
 
 
179
  }
180
+
181
+ // Settings
182
+ $query['settings'] = wp404arsp_settings_get($query);
183
+
184
+ // Filter init
185
+ if(!apply_filters('wp404arsp/init', true, $query))
186
+ return;
187
+
188
+ // Search
189
+ $this->search($query);
190
+
191
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
 
193
+ function search($query){
194
+
195
+ // init Engines & Groups
196
+ do_action('wp404arsp/search/init', $query);
197
+
198
+ // add Engines & Groups
199
+ $query['engines'] = wp404arsp()->engines->get_engines;
200
+ $query['groups'] = wp404arsp()->groups->get_groups;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
 
202
+ // init Search
203
+ $query['search'] = array();
204
+
205
+ // init Search Group
206
+ $query['search']['group'] = apply_filters('wp404arsp/search/group', 'default', $query);
207
+
208
+ // Filter Query
209
+ $query = apply_filters('wp404arsp/search/query', $query);
210
+
211
+ // Run Search
212
+ if(!empty($query['groups']) && !empty($query['search']['group'])){
213
+
214
+ foreach($query['groups'] as $g => $group){
215
 
216
+ if($group['slug'] != $query['search']['group'])
217
+ continue;
218
+
219
+ if(empty($query['engines']) || empty($query['groups'][$g]['engines']))
220
+ break;
221
 
222
+ foreach($query['groups'][$g]['engines'] as $e_slug){
223
+
224
+ if(!$engine = wp404arsp_get_engine_by_slug($e_slug))
225
+ continue;
226
+
227
+ if(!$result = apply_filters('wp404arsp/search/engine/' . $engine['slug'], false, $query, $group))
228
+ continue;
229
+
230
+ $result = wp404arsp_set_result($result, $engine);
231
+ if(!$result)
232
+ continue;
233
+
234
+ $query['search']['results'][] = $result;
235
+
236
+ // Stop Search if Engine's Primary = true AND Score > 0
237
+ if($result['score'] > 0 && $result['primary'])
238
+ break;
239
+
240
  }
241
+
242
+ break;
243
+
244
+ }
245
+
246
+ }
 
 
 
 
 
 
 
 
 
 
 
247
 
248
+ // Filter Search Results
249
+ $query['search'] = apply_filters('wp404arsp/search/results', $query['search'], $query);
250
 
251
+ // init Redirection
252
+ $query['redirect'] = false;
 
 
253
 
254
+ // Redirection by highest score
255
+ if(!empty($query['search']['results'])){
256
+ $s=0; foreach($query['search']['results'] as $r){
257
+
258
+ if($r['score'] > $s)
259
+ $query['redirect'] = $r;
260
+
261
+ // Stop if engine = primary
262
+ if($r['score'] > 0 && $r['primary'] === true)
263
+ break;
264
+
265
+ $s = $r['score'];
266
+
267
  }
268
  }
269
 
270
+ // Redirection fallback
271
+ if(!$query['redirect']){
272
+
273
+ $fallback = $query['settings']['fallback']['url'];
274
+
275
+ if($query['settings']['fallback']['type'] == 'disabled')
276
+ $fallback = false;
277
+
278
+ $engine = array(
279
+ 'name' => 'None',
280
+ 'slug' => 'none'
281
+ );
282
+
283
+ $query['redirect'] = wp404arsp_set_result(array(
284
+ 'url' => $fallback,
285
+ 'score' => 0,
286
+ 'why' => "Nothing found. Applying fallback behavior."
287
+ ), $engine);
288
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
289
  }
290
+
291
+ // Filter Search Redirect
292
+ $query['redirect'] = apply_filters('wp404arsp/search/redirect', $query['redirect'], $query);
293
+
294
+ // Redirect
295
+ $this->redirect($query);
296
+
297
  }
 
 
 
298
 
299
+ function redirect($query){
300
+
301
+ // Filter: wp404arsp/redirect
302
+ $query = apply_filters('wp404arsp/redirect', $query);
303
+
304
+ // Debug
305
+ if(is_user_logged_in() && current_user_can('administrator') && ($query['settings']['debug'] || $query['preview']))
306
+ return $this->debug($query);
 
 
 
 
 
 
307
 
308
+ // Fallback: 404
309
+ if(!$query['redirect']['url'])
310
+ return;
311
+
312
+ // Redirect
313
+ $this->redirect_to($query);
314
+
315
+ return;
316
 
 
317
  }
 
 
 
318
 
319
+ function redirect_to($query){
320
+
321
+ // Copy/paste from legacy WP_Redirect function()
322
+ // File: wp-includes/pluggable.php
323
+
324
+ // Added: 'WP-404-Auto-Redirect: true' header
325
+ // Added: 'wp404arsp/after_redirect' action
326
+ // Added: PHP exit;
327
+
328
+ global $is_IIS;
329
+
330
+ $status = $query['settings']['method'];
331
+ $location = apply_filters('wp_redirect', $query['redirect']['url'], $status);
332
+ $status = apply_filters('wp_redirect_status', $status, $location);
333
+
334
+ if(!$location)
335
+ return false;
336
+
337
+ $location = wp_sanitize_redirect($location);
338
+
339
+ if(!$is_IIS && PHP_SAPI != 'cgi-fcgi')
340
+ status_header($status);
341
+
342
+ header("Location: $location", true, $status);
343
+
344
+ // Expose Headers
345
+ if(current_user_can('administrator') && $query['settings']['headers']){
346
+ header('WP-404-Auto-Redirect: true');
347
+
348
+ if(isset($query['search']['group']))
349
+ header('WP-404-Auto-Redirect-Group: ' . $query['search']['group']);
350
+
351
+ if(isset($query['redirect']['engine']))
352
+ header('WP-404-Auto-Redirect-Engine: ' . $query['redirect']['engine']);
353
+
354
+ if(isset($query['redirect']['primary']))
355
+ header('WP-404-Auto-Redirect-Primary: ' . $query['redirect']['primary']);
356
+
357
+ if(isset($query['redirect']['engine']))
358
+ header('WP-404-Auto-Redirect-Score: ' . $query['redirect']['engine']);
359
+
360
+ if(isset($query['redirect']['why']))
361
+ header('WP-404-Auto-Redirect-Why: ' . strip_tags($query['redirect']['why']));
362
+ }
363
+
364
+ // Action: wp404arsp/after_redirect
365
+ do_action('wp404arsp/after_redirect', $query);
366
+
367
+ exit;
368
+
369
+ }
370
 
371
+ function log($query){
372
+
373
+ if(empty($query['settings']['log']) || !WP_DEBUG || !WP_DEBUG_LOG)
374
+ return;
375
+
376
+ $request_url = home_url() . $query['request']['url'];
377
+ $redirect = $query['redirect']['url'];
378
+ $group = $query['search']['group'];
379
+ $engine = $query['redirect']['engine'];
380
+ $score = $query['redirect']['score'];
381
+ $why = strip_tags($query['redirect']['why']);
382
+
383
+ // Cloudflare Fix
384
+ if(isset($_SERVER["HTTP_CF_CONNECTING_IP"]))
385
+ $_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
386
 
387
+ $ip = $_SERVER['REMOTE_ADDR'];
388
+
389
+ error_log('WP 404 Auto Redirect: ' . $request_url . ' => ' . $redirect . ' (Group: ' . $group . ' | Engine: ' . $engine . ' | Score: ' . $score . ' | Why: ' . $why . ' | IP: ' . $ip . ')');
390
 
 
391
  }
392
 
 
393
  }
394
 
395
+ function wp404arsp(){
396
+ global $wp404arsp;
397
+
398
+ if(isset($wp404arsp))
399
+ return $wp404arsp;
400
+
401
+ $wp404arsp = new WP_404_Auto_Redirect();
402
+ $wp404arsp->init();
403
+
404
+ return $wp404arsp;
405
 
406
+ }
407
+
408
+ // init
409
+ wp404arsp();
410
+
411
+ endif;