PluginProbe
Redirection / 3.6.3
Redirection v3.6.3
5.10.0 5.9.0 5.8.1 5.8.0 3.7.2 3.7.3 4.0 4.0.1 4.1 4.1.1 4.2 4.2.1 4.2.2 4.2.3 4.3 4.3.1 4.3.2 4.3.3 4.4 4.4.1 4.4.2 4.5 4.5.1 4.6.2 4.7.1 All 130 releases
redirection / redirection-admin.php

redirection-admin.php in Redirection 3.6.3, at redirection-admin.php

557 lines 17.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 include dirname( __FILE__ ) . '/models/group.php';
4 include dirname( __FILE__ ) . '/models/monitor.php';
5 include dirname( __FILE__ ) . '/models/file-io.php';
6
7 define( 'RED_DEFAULT_PER_PAGE', 25 );
8 define( 'RED_MAX_PER_PAGE', 200 );
9
10 class Redirection_Admin {
11 private static $instance = null;
12 private $monitor;
13
14 static function init() {
15 if ( is_null( self::$instance ) ) {
16 self::$instance = new Redirection_Admin();
17 }
18
19 return self::$instance;
20 }
21
22 function __construct() {
23 add_action( 'admin_menu', array( $this, 'admin_menu' ) );
24 add_action( 'plugin_action_links_' . basename( dirname( REDIRECTION_FILE ) ) . '/' . basename( REDIRECTION_FILE ), array( $this, 'plugin_settings' ), 10, 4 );
25 add_filter( 'redirection_save_options', array( $this, 'flush_schedule' ) );
26 add_filter( 'set-screen-option', array( $this, 'set_per_page' ), 10, 3 );
27 add_action( 'redirection_redirect_updated', array( $this, 'set_default_group' ), 10, 2 );
28
29 if ( defined( 'REDIRECTION_FLYING_SOLO' ) && REDIRECTION_FLYING_SOLO ) {
30 add_filter( 'script_loader_src', array( $this, 'flying_solo' ), 10, 2 );
31 }
32
33 register_deactivation_hook( REDIRECTION_FILE, array( 'Redirection_Admin', 'plugin_deactivated' ) );
34 register_uninstall_hook( REDIRECTION_FILE, array( 'Redirection_Admin', 'plugin_uninstall' ) );
35
36 $this->monitor = new Red_Monitor( red_get_options() );
37 $this->run_hacks();
38 }
39
40 // These are only called on the single standard site, or in the network admin of the multisite - they run across all available sites
41 public static function plugin_activated() {
42 if ( is_network_admin() ) {
43 foreach ( get_sites() as $site ) {
44 switch_to_blog( $site->blog_id );
45
46 Redirection_Admin::update();
47 Red_Flusher::schedule();
48 red_set_options();
49
50 restore_current_blog();
51 }
52 } else {
53 Redirection_Admin::update();
54 Red_Flusher::schedule();
55 red_set_options();
56 }
57 }
58
59 // These are only called on the single standard site, or in the network admin of the multisite - they run across all available sites
60 public static function plugin_deactivated() {
61 if ( is_network_admin() ) {
62 foreach ( get_sites() as $site ) {
63 switch_to_blog( $site->blog_id );
64
65 Red_Flusher::clear();
66
67 restore_current_blog();
68 }
69 } else {
70 Red_Flusher::clear();
71 }
72 }
73
74 // These are only called on the single standard site, or in the network admin of the multisite - they run across all available sites
75 public static function plugin_uninstall() {
76 include_once dirname( REDIRECTION_FILE ) . '/models/database.php';
77
78 $database = new RE_Database();
79
80 if ( is_network_admin() ) {
81 foreach ( get_sites() as $site ) {
82 switch_to_blog( $site->blog_id );
83
84 $database->remove( REDIRECTION_FILE );
85
86 restore_current_blog();
87 }
88 } else {
89 $database->remove( REDIRECTION_FILE );
90 }
91 }
92
93 private static function update() {
94 $version = get_option( 'redirection_version' );
95
96 Red_Flusher::schedule();
97
98 if ( $version !== REDIRECTION_DB_VERSION || ( defined( 'REDIRECTION_FORCE_UPDATE' ) && REDIRECTION_FORCE_UPDATE ) ) {
99 include_once dirname( REDIRECTION_FILE ) . '/models/database.php';
100
101 $database = new RE_Database();
102
103 if ( $version === false ) {
104 $database->install();
105 }
106
107 $database->upgrade( $version, REDIRECTION_DB_VERSION );
108 }
109 }
110
111 // So it finally came to this... some plugins include their JS in all pages, whether they are needed or not. If there is an error
112 // then this can prevent Redirection running and it's a little sensitive about that. We use the nuclear option here to disable
113 // all other JS while viewing Redirection
114 public function flying_solo( $src, $handle ) {
115 if ( isset( $_SERVER['REQUEST_URI'] ) && strpos( $_SERVER['REQUEST_URI'], 'page=redirection.php' ) !== false ) {
116 if ( substr( $src, 0, 4 ) === 'http' && $handle !== 'redirection' && strpos( $src, 'plugins' ) !== false ) {
117 if ( $this->ignore_this_plugin( $src ) ) {
118 return false;
119 }
120 }
121 }
122
123 return $src;
124 }
125
126 private function ignore_this_plugin( $src ) {
127 $ignore = array(
128 'mootools',
129 'wp-seo-',
130 'authenticate',
131 'wordpress-seo',
132 'yikes',
133 );
134
135 foreach ( $ignore as $text ) {
136 if ( strpos( $src, $text ) !== false ) {
137 return true;
138 }
139 }
140
141 return false;
142 }
143
144 public function flush_schedule( $options ) {
145 Red_Flusher::schedule();
146 return $options;
147 }
148
149 function set_per_page( $status, $option, $value ) {
150 if ( $option === 'redirection_log_per_page' ) {
151 return max( 1, min( intval( $value, 10 ), RED_MAX_PER_PAGE ) );
152 }
153
154 return $status;
155 }
156
157 function plugin_settings( $links ) {
158 $settings_link = '<a href="tools.php?page=' . basename( REDIRECTION_FILE ) . '&amp;sub=options">' . __( 'Settings', 'redirection' ) . '</a>';
159 array_unshift( $links, $settings_link );
160 return $links;
161 }
162
163 function redirection_head() {
164 global $wp_version;
165
166 $this->check_rest_api();
167
168 if ( isset( $_REQUEST['action'] ) && isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'wp_rest' ) ) {
169 if ( $_REQUEST['action'] === 'fixit' ) {
170 $this->run_fixit();
171 } elseif ( $_REQUEST['action'] === 'rest_api' ) {
172 $this->set_rest_api( intval( $_REQUEST['rest_api'], 10 ) );
173 }
174 }
175
176 $build = REDIRECTION_VERSION . '-' . REDIRECTION_BUILD;
177 $preload = $this->get_preload_data();
178 $options = red_get_options();
179 $versions = array(
180 'Plugin: ' . REDIRECTION_VERSION,
181 'WordPress: ' . $wp_version . ' (' . ( is_multisite() ? 'multi' : 'single' ) . ')',
182 'PHP: ' . phpversion(),
183 'Browser: ' . Redirection_Request::get_user_agent(),
184 'JavaScript: ' . plugin_dir_url( REDIRECTION_FILE ) . 'redirection.js',
185 'REST API: ' . red_get_rest_api(),
186 );
187
188 $this->inject();
189
190 if ( ! isset( $_GET['sub'] ) || ( isset( $_GET['sub'] ) && ( in_array( $_GET['sub'], array( 'log', '404s', 'groups' ) ) ) ) ) {
191 add_screen_option( 'per_page', array(
192 /* translators: maximum number of log entries */
193 'label' => sprintf( __( 'Log entries (%d max)', 'redirection' ), RED_MAX_PER_PAGE ),
194 'default' => RED_DEFAULT_PER_PAGE,
195 'option' => 'redirection_log_per_page',
196 ) );
197 }
198
199 if ( defined( 'REDIRECTION_DEV_MODE' ) && REDIRECTION_DEV_MODE ) {
200 wp_enqueue_script( 'redirection', 'http://localhost:3312/redirection.js', array(), $build, true );
201 } else {
202 wp_enqueue_script( 'redirection', plugin_dir_url( REDIRECTION_FILE ) . 'redirection.js', array(), $build, true );
203 }
204
205 wp_enqueue_style( 'redirection', plugin_dir_url( REDIRECTION_FILE ) . 'redirection.css', array(), $build );
206
207 wp_localize_script( 'redirection', 'Redirectioni10n', array(
208 'WP_API_root' => esc_url_raw( red_get_rest_api() ),
209 'WP_API_nonce' => wp_create_nonce( 'wp_rest' ),
210 'pluginBaseUrl' => plugins_url( '', REDIRECTION_FILE ),
211 'pluginRoot' => admin_url( 'tools.php?page=redirection.php' ),
212 'per_page' => $this->get_per_page(),
213 'locale' => $this->get_i18n_data(),
214 'localeSlug' => get_locale(),
215 'token' => $options['token'],
216 'autoGenerate' => $options['auto_target'],
217 'preload' => $preload,
218 'versions' => implode( "\n", $versions ),
219 'version' => REDIRECTION_VERSION,
220 'api_setting' => $options['rest_api'],
221 ) );
222
223 $this->add_help_tab();
224 }
225
226 // Some plugins misbehave, so this attempts to 'fix' them so Redirection can get on with it's work
227 private function run_hacks() {
228 add_filter( 'ip-geo-block-admin', array( $this, 'ip_geo_block' ) );
229 }
230
231 /**
232 * This works around the IP Geo Block plugin being very aggressive and breaking Redirection
233 */
234 public function ip_geo_block( $validate ) {
235 $url = Redirection_Request::get_request_url();
236 $override = array(
237 'tools.php?page=redirection.php',
238 'action=red_proxy&rest_path=redirection',
239 );
240
241 foreach ( $override as $path ) {
242 if ( strpos( $url, $path ) !== false ) {
243 return array(
244 'result' => 'passed',
245 'auth' => false,
246 'asn' => false,
247 'code' => false,
248 'ip' => false,
249 );
250 }
251 }
252
253 return $validate;
254 }
255
256 public function check_rest_api() {
257 $options = red_get_options();
258
259 if ( $options['version'] !== REDIRECTION_VERSION || $options['rest_api'] === false || ( defined( 'REDIRECTION_FORCE_UPDATE' ) && REDIRECTION_FORCE_UPDATE ) ) {
260 include_once dirname( REDIRECTION_FILE ) . '/models/fixer.php';
261
262 $fixer = new Red_Fixer();
263 $status = $fixer->get_rest_status();
264
265 if ( $status['status'] === 'problem' ) {
266 $fixer->fix_rest();
267 } elseif ( $options['rest_api'] === false ) {
268 red_set_options( array( 'rest_api' => 0 ) );
269 }
270
271 red_set_options( array( 'version' => REDIRECTION_VERSION ) );
272 }
273 }
274
275 private function run_fixit() {
276 if ( current_user_can( apply_filters( 'redirection_role', 'manage_options' ) ) ) {
277 include_once dirname( REDIRECTION_FILE ) . '/models/fixer.php';
278
279 $fixer = new Red_Fixer();
280 $fixer->fix( $fixer->get_status() );
281 }
282 }
283
284 private function set_rest_api( $api ) {
285 if ( $api >= 0 && $api <= REDIRECTION_API_POST ) {
286 red_set_options( array( 'rest_api' => intval( $api, 10 ) ) );
287 }
288 }
289
290 private function get_preload_data() {
291 $page = '';
292 if ( isset( $_GET['sub'] ) && in_array( $_GET['sub'], array( 'group', '404s', 'log', 'io', 'options', 'support' ) ) ) {
293 $page = $_GET['sub'];
294 }
295
296 if ( $page === 'support' ) {
297 $api = new Redirection_Api_Plugin( REDIRECTION_API_NAMESPACE );
298
299 return array(
300 'pluginStatus' => $api->route_status( new WP_REST_Request() ),
301 );
302 }
303
304 return array();
305 }
306
307 private function add_help_tab() {
308 /* translators: URL */
309 $content = sprintf( __( 'You can find full documentation about using Redirection on the <a href="%s" target="_blank">redirection.me</a> support site.', 'redirection' ), 'https://redirection.me/support/?utm_source=redirection&utm_medium=plugin&utm_campaign=context-help' );
310 $title = __( 'Redirection Support', 'redirection' );
311
312 $current_screen = get_current_screen();
313 $current_screen->add_help_tab( array(
314 'id' => 'redirection',
315 'title' => 'Redirection',
316 'content' => "<h2>$title</h2><p>$content</p>",
317 ) );
318 }
319
320 private function get_per_page() {
321 $per_page = intval( get_user_meta( get_current_user_id(), 'redirection_log_per_page', true ), 10 );
322
323 return $per_page > 0 ? max( 5, min( $per_page, RED_MAX_PER_PAGE ) ) : RED_DEFAULT_PER_PAGE;
324 }
325
326 private function get_i18n_data() {
327 $locale = get_locale();
328
329 // WP 4.7
330 if ( function_exists( 'get_user_locale' ) ) {
331 $locale = get_user_locale();
332 }
333
334 $i18n_json = dirname( REDIRECTION_FILE ) . '/locale/json/redirection-' . $locale . '.json';
335
336 if ( is_file( $i18n_json ) && is_readable( $i18n_json ) ) {
337 $locale_data = @file_get_contents( $i18n_json );
338
339 if ( $locale_data ) {
340 return json_decode( $locale_data );
341 }
342 }
343
344 // Return empty if we have nothing to return so it doesn't fail when parsed in JS
345 return array();
346 }
347
348 function admin_menu() {
349 $hook = add_management_page( 'Redirection', 'Redirection', apply_filters( 'redirection_role', 'manage_options' ), basename( REDIRECTION_FILE ), array( &$this, 'admin_screen' ) );
350 add_action( 'load-' . $hook, array( $this, 'redirection_head' ) );
351 }
352
353 private function check_minimum_wp() {
354 $wp_version = get_bloginfo( 'version' );
355
356 if ( version_compare( $wp_version, REDIRECTION_MIN_WP, '<' ) ) {
357 /* translators: 1: Expected WordPress version, 2: Actual WordPress version */
358 $wp_requirement = sprintf( __( 'Redirection requires WordPress v%1$1s, you are using v%2$2s - please update your WordPress', 'redirection' ), REDIRECTION_MIN_WP, $wp_version );
359 ?>
360 <div class="react-error">
361 <h1><?php esc_html_e( 'Unable to load Redirection', 'redirection' ); ?></h1>
362 <p style="text-align: left"><?php echo esc_html( $wp_requirement ); ?></p>
363 </div>
364 <?php
365 return false;
366 }
367
368 return true;
369 }
370
371 private function check_minimum_php() {
372 if ( version_compare( PHP_VERSION, '5.4' ) < 0 ) {
373 /* translators: 1: Expected PHP version, 2: Actual PHP version */
374 $php_version = sprintf( __( 'Redirection requires PHP v%1$1s, you are using v%2$2s. This plugin will stop working from the next version.', 'redirection' ), '5.4', PHP_VERSION );
375 ?>
376 <div class="error">
377 <h1><?php esc_html_e( 'Unsupported PHP', 'redirection' ); ?></h1>
378 <p style="text-align: left"><?php echo esc_html( $php_version ); ?></p>
379 </div>
380 <?php
381 }
382 }
383
384 private function check_tables_exist() {
385 include_once dirname( REDIRECTION_FILE ) . '/models/database.php';
386
387 $database = new RE_Database();
388 $status = $database->get_status();
389
390 if ( $status['status'] !== 'good' ) {
391 /* translators: URL */
392 $reason = sprintf( __( 'Problems were detected with your database tables. Please visit the <a href="%s">support page</a> for more details.', 'redirection' ), 'tools.php?page=redirection.php&amp;sub=support' );
393 ?>
394 <div class="error">
395 <h3><?php esc_html_e( 'Redirection not installed properly', 'redirection' ); ?></h3>
396 <p style="text-align: left"><?php echo esc_html( $reason ); ?></p>
397 </div>
398 <?php
399
400 return false;
401 }
402
403 return true;
404 }
405
406 public function set_default_group( $id, $redirect ) {
407 red_set_options( array( 'last_group_id' => $redirect->get_group_id() ) );
408 }
409
410 function admin_screen() {
411 $version = red_get_plugin_data( REDIRECTION_FILE );
412 $version = $version['Version'];
413
414 Redirection_Admin::update();
415
416 if ( $this->check_minimum_php() === false ) {
417 return;
418 }
419
420 if ( $this->check_minimum_wp() === false ) {
421 return;
422 }
423
424 if ( $this->check_tables_exist() === false && ( ! isset( $_GET['sub'] ) || $_GET['sub'] !== 'support' ) ) {
425 return false;
426 }
427
428 ?>
429 <div id="react-modal"></div>
430 <div id="react-ui">
431 <div class="react-loading">
432 <h1><?php esc_html_e( 'Loading, please wait...', 'redirection' ); ?></h1>
433
434 <span class="react-loading-spinner"></span>
435 </div>
436 <noscript>Please enable JavaScript</noscript>
437
438 <div class="react-error" style="display: none">
439 <h1><?php esc_html_e( 'Unable to load Redirection ☹️', 'redirection' ); ?> v<?php echo esc_html( $version ); ?></h1>
440 <p><?php esc_html_e( "This may be caused by another plugin - look at your browser's error console for more details.", 'redirection' ); ?></p>
441 <p><?php esc_html_e( 'If you are using a page caching plugin or service (CloudFlare, OVH, etc) then you can also try clearing that cache.', 'redirection' ); ?></p>
442 <p><?php esc_html_e( 'Also check if your browser is able to load <code>redirection.js</code>:', 'redirection' ); ?></p>
443 <p><code><?php echo esc_html( plugin_dir_url( REDIRECTION_FILE ) . 'redirection.js?ver=' . urlencode( REDIRECTION_VERSION ) . '-' . urlencode( REDIRECTION_BUILD ) ); ?></code></p>
444 <p><?php esc_html_e( 'Please note that Redirection requires the WordPress REST API to be enabled. If you have disabled this then you won\'t be able to use Redirection', 'redirection' ); ?></p>
445 <p><?php esc_html_e( 'Please see the <a href="https://redirection.me/support/problems/">list of common problems</a>.', 'redirection' ); ?></p>
446 <p><?php esc_html_e( 'If you think Redirection is at fault then create an issue.', 'redirection' ); ?></p>
447 <p class="versions"><?php esc_html_e( '<code>Redirectioni10n</code> is not defined. This usually means another plugin is blocking Redirection from loading. Please disable all plugins and try again.', 'redirection' ); ?></p>
448 <p>
449 <a class="button-primary" target="_blank" href="https://github.com/johngodley/redirection/issues/new?title=Problem%20starting%20Redirection%20<?php echo esc_attr( $version ); ?>">
450 <?php esc_html_e( 'Create Issue', 'redirection' ); ?>
451 </a>
452 </p>
453 </div>
454 </div>
455
456 <script>
457 var prevError = window.onerror;
458 var errors = [];
459 var timeout = 0;
460 var timer = setInterval( function() {
461 if ( isRedirectionLoaded() ) {
462 resetAll();
463 } else if ( errors.length > 0 || timeout++ === 5 ) {
464 showError();
465 }
466 }, 5000 );
467
468 function isRedirectionLoaded() {
469 return typeof redirection !== 'undefined';
470 }
471
472 function showError() {
473 var errorText = "";
474
475 if ( errors.length > 0 ) {
476 errorText = "```\n" + errors.join( ',' ) + "\n```\n\n";
477 }
478
479 resetAll();
480 document.querySelector( '.react-loading' ).style.display = 'none';
481 document.querySelector( '.react-error' ).style.display = 'block';
482
483 if ( typeof Redirectioni10n !== 'undefined' ) {
484 document.querySelector( '.versions' ).innerHTML = Redirectioni10n.versions.replace( /\n/g, '<br />' );
485 document.querySelector( '.react-error .button-primary' ).href += '&body=' + encodeURIComponent( errorText ) + encodeURIComponent( Redirectioni10n.versions );
486 }
487 }
488
489 function resetAll() {
490 clearInterval( timer );
491 window.onerror = prevError;
492 }
493
494 window.onerror = function( error, url, line ) {
495 console.error( error );
496 errors.push( error + ' ' + url + ' ' + line );
497 };
498 </script>
499 <?php
500 }
501
502 private function user_has_access() {
503 return current_user_can( apply_filters( 'redirection_role', 'manage_options' ) );
504 }
505
506 function inject() {
507 if ( isset( $_GET['page'] ) && isset( $_GET['sub'] ) && $_GET['page'] === 'redirection.php' ) {
508 $this->try_export_logs();
509 $this->try_export_redirects();
510 $this->try_export_rss();
511 }
512 }
513
514 function try_export_rss() {
515 if ( isset( $_GET['token'] ) && $_GET['sub'] === 'rss' ) {
516 $options = red_get_options();
517
518 if ( $_GET['token'] === $options['token'] && ! empty( $options['token'] ) ) {
519 $items = Red_Item::get_all_for_module( intval( $_GET['module'] ) );
520
521 $exporter = Red_FileIO::create( 'rss' );
522 $exporter->force_download();
523 echo $exporter->get_data( $items, array() );
524 die();
525 }
526 }
527 }
528
529 private function try_export_logs() {
530 if ( $this->user_has_access() && isset( $_POST['export-csv'] ) && check_admin_referer( 'wp_rest' ) ) {
531 if ( isset( $_GET['sub'] ) && $_GET['sub'] === 'log' ) {
532 RE_Log::export_to_csv();
533 } else {
534 RE_404::export_to_csv();
535 }
536
537 die();
538 }
539 }
540
541 private function try_export_redirects() {
542 if ( $this->user_has_access() && $_GET['sub'] === 'io' && isset( $_GET['exporter'] ) && isset( $_GET['export'] ) && check_admin_referer( 'wp_rest' ) ) {
543 $export = Red_FileIO::export( $_GET['export'], $_GET['exporter'] );
544
545 if ( $export !== false ) {
546 $export['exporter']->force_download();
547 echo $export['data'];
548 die();
549 }
550 }
551 }
552 }
553
554 register_activation_hook( REDIRECTION_FILE, array( 'Redirection_Admin', 'plugin_activated' ) );
555
556 add_action( 'init', array( 'Redirection_Admin', 'init' ) );
557