PluginProbe
Redirection / 4.0.1
Redirection v4.0.1
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 4.0.1, at redirection-admin.php

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