PluginProbe
Redirection / 3.7
Redirection v3.7
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.7, at redirection-admin.php

535 lines 17.8 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: version number */
77 $message = sprintf( __( 'Redirection needs to be <a href="%1$1s">updated to version %2$2s</a>.' ), 'tools.php?page=' . basename( REDIRECTION_FILE ), REDIRECTION_DB_VERSION );
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 wp_localize_script( 'redirection', 'Redirectioni10n', array(
198 'WP_API_root' => esc_url_raw( red_get_rest_api() ),
199 'WP_API_nonce' => wp_create_nonce( 'wp_rest' ),
200 'pluginBaseUrl' => plugins_url( '', REDIRECTION_FILE ),
201 'pluginRoot' => admin_url( 'tools.php?page=redirection.php' ),
202 'per_page' => $this->get_per_page(),
203 'locale' => $this->get_i18n_data(),
204 'localeSlug' => get_locale(),
205 'token' => $options['token'],
206 'autoGenerate' => $options['auto_target'],
207 'preload' => $preload,
208 'versions' => implode( "\n", $versions ),
209 'version' => REDIRECTION_VERSION,
210 'api_setting' => $options['rest_api'],
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 red_set_options( array( 'version' => REDIRECTION_VERSION ) );
263 }
264 }
265
266 private function run_fixit() {
267 if ( current_user_can( apply_filters( 'redirection_role', 'manage_options' ) ) ) {
268 include_once dirname( REDIRECTION_FILE ) . '/models/fixer.php';
269
270 $fixer = new Red_Fixer();
271 $result = $fixer->fix( $fixer->get_status() );
272
273 if ( is_wp_error( $result ) ) {
274 $this->fixit_failed = $result;
275 }
276 }
277 }
278
279 private function set_rest_api( $api ) {
280 if ( $api >= 0 && $api <= REDIRECTION_API_POST ) {
281 red_set_options( array( 'rest_api' => intval( $api, 10 ) ) );
282 }
283 }
284
285 private function get_preload_data() {
286 if ( $this->get_menu_page() === 'support' ) {
287 $api = new Redirection_Api_Plugin( REDIRECTION_API_NAMESPACE );
288
289 return array(
290 'pluginStatus' => $api->route_status( new WP_REST_Request() ),
291 );
292 }
293
294 return array();
295 }
296
297 private function add_help_tab() {
298 /* translators: URL */
299 $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' );
300 $title = __( 'Redirection Support', 'redirection' );
301
302 $current_screen = get_current_screen();
303 $current_screen->add_help_tab( array(
304 'id' => 'redirection',
305 'title' => 'Redirection',
306 'content' => "<h2>$title</h2><p>$content</p>",
307 ) );
308 }
309
310 private function get_per_page() {
311 $per_page = intval( get_user_meta( get_current_user_id(), 'redirection_log_per_page', true ), 10 );
312
313 return $per_page > 0 ? max( 5, min( $per_page, RED_MAX_PER_PAGE ) ) : RED_DEFAULT_PER_PAGE;
314 }
315
316 private function get_i18n_data() {
317 $locale = get_locale();
318
319 // WP 4.7
320 if ( function_exists( 'get_user_locale' ) ) {
321 $locale = get_user_locale();
322 }
323
324 $i18n_json = dirname( REDIRECTION_FILE ) . '/locale/json/redirection-' . $locale . '.json';
325
326 if ( is_file( $i18n_json ) && is_readable( $i18n_json ) ) {
327 $locale_data = @file_get_contents( $i18n_json );
328
329 if ( $locale_data ) {
330 return json_decode( $locale_data );
331 }
332 }
333
334 // Return empty if we have nothing to return so it doesn't fail when parsed in JS
335 return array();
336 }
337
338 function admin_menu() {
339 $hook = add_management_page( 'Redirection', 'Redirection', apply_filters( 'redirection_role', 'manage_options' ), basename( REDIRECTION_FILE ), array( &$this, 'admin_screen' ) );
340 add_action( 'load-' . $hook, array( $this, 'redirection_head' ) );
341 }
342
343 private function check_minimum_wp() {
344 $wp_version = get_bloginfo( 'version' );
345
346 if ( version_compare( $wp_version, REDIRECTION_MIN_WP, '<' ) ) {
347 return false;
348 }
349
350 return true;
351 }
352
353 public function set_default_group( $id, $redirect ) {
354 red_set_options( array( 'last_group_id' => $redirect->get_group_id() ) );
355 }
356
357 public function admin_screen() {
358 if ( $this->check_minimum_wp() === false ) {
359 return $this->show_minimum_wordpress();
360 }
361
362 if ( $this->fixit_failed ) {
363 $this->show_fixit_failed();
364 }
365
366 Red_Flusher::schedule();
367
368 $this->show_main();
369 }
370
371 private function show_fixit_failed() {
372 ?>
373 <div class="notice notice-error">
374 <h1><?php echo esc_html( $this->fixit_failed->get_error_message() ); ?></h1>
375 <p><?php echo esc_html( $this->fixit_failed->get_error_data() ); ?></p>
376 </div>
377 <?php
378 }
379
380 private function show_minimum_wordpress() {
381 /* translators: 1: Expected WordPress version, 2: Actual WordPress version */
382 $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 );
383 ?>
384 <div class="react-error">
385 <h1><?php esc_html_e( 'Unable to load Redirection', 'redirection' ); ?></h1>
386 <p><?php echo esc_html( $wp_requirement ); ?></p>
387 </div>
388 <?php
389 }
390
391 private function show_load_fail() {
392 ?>
393 <div class="react-error" style="display: none">
394 <h1><?php esc_html_e( 'Unable to load Redirection ☹️', 'redirection' ); ?> v<?php echo esc_html( REDIRECTION_VERSION ); ?></h1>
395 <p><?php esc_html_e( "This may be caused by another plugin - look at your browser's error console for more details.", 'redirection' ); ?></p>
396 <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>
397 <p><?php _e( 'Also check if your browser is able to load <code>redirection.js</code>:', 'redirection' ); ?></p>
398 <p><code><?php echo esc_html( plugin_dir_url( REDIRECTION_FILE ) . 'redirection.js?ver=' . urlencode( REDIRECTION_VERSION ) . '-' . urlencode( REDIRECTION_BUILD ) ); ?></code></p>
399 <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>
400 <p><?php _e( 'Please see the <a href="https://redirection.me/support/problems/">list of common problems</a>.', 'redirection' ); ?></p>
401 <p><?php esc_html_e( 'If you think Redirection is at fault then create an issue.', 'redirection' ); ?></p>
402 <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>
403 <p>
404 <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 ); ?>">
405 <?php esc_html_e( 'Create Issue', 'redirection' ); ?>
406 </a>
407 </p>
408 </div>
409 <?php
410 }
411
412 private function show_main() {
413 ?>
414 <div id="react-modal"></div>
415 <div id="react-ui">
416 <div class="react-loading">
417 <h1><?php esc_html_e( 'Loading, please wait...', 'redirection' ); ?></h1>
418
419 <span class="react-loading-spinner"></span>
420 </div>
421 <noscript><?php esc_html_e( 'Please enable JavaScript', 'redirection' ); ?></noscript>
422
423 <?php $this->show_load_fail(); ?>
424 </div>
425
426 <script>
427 var prevError = window.onerror;
428 var errors = [];
429 var timeout = 0;
430 var timer = setInterval( function() {
431 if ( isRedirectionLoaded() ) {
432 resetAll();
433 } else if ( errors.length > 0 || timeout++ === 5 ) {
434 showError();
435 }
436 }, 5000 );
437
438 function isRedirectionLoaded() {
439 return typeof redirection !== 'undefined';
440 }
441
442 function showError() {
443 var errorText = "";
444
445 if ( errors.length > 0 ) {
446 errorText = "```\n" + errors.join( ',' ) + "\n```\n\n";
447 }
448
449 resetAll();
450 document.querySelector( '.react-loading' ).style.display = 'none';
451 document.querySelector( '.react-error' ).style.display = 'block';
452
453 if ( typeof Redirectioni10n !== 'undefined' ) {
454 document.querySelector( '.versions' ).innerHTML = Redirectioni10n.versions.replace( /\n/g, '<br />' );
455 document.querySelector( '.react-error .button-primary' ).href += '&body=' + encodeURIComponent( errorText ) + encodeURIComponent( Redirectioni10n.versions );
456 }
457 }
458
459 function resetAll() {
460 clearInterval( timer );
461 window.onerror = prevError;
462 }
463
464 window.onerror = function( error, url, line ) {
465 console.error( error );
466 errors.push( error + ' ' + url + ' ' + line );
467 };
468 </script>
469 <?php
470 }
471
472 private function user_has_access() {
473 return current_user_can( apply_filters( 'redirection_role', 'manage_options' ) );
474 }
475
476 private function inject() {
477 if ( isset( $_GET['page'] ) && $this->get_menu_page() !== 'redirects' && $_GET['page'] === 'redirection.php' ) {
478 $this->try_export_logs();
479 $this->try_export_redirects();
480 $this->try_export_rss();
481 }
482 }
483
484 private function get_menu_page() {
485 if ( isset( $_GET['sub'] ) && in_array( $_GET['sub'], array( 'group', '404s', 'log', 'io', 'options', 'support', true ) ) ) {
486 return $_GET['sub'];
487 }
488
489 return 'redirects';
490 }
491
492 public function try_export_rss() {
493 if ( isset( $_GET['token'] ) && $this->get_menu_page() === 'rss' ) {
494 $options = red_get_options();
495
496 if ( $_GET['token'] === $options['token'] && ! empty( $options['token'] ) ) {
497 $items = Red_Item::get_all_for_module( intval( $_GET['module'] ) );
498
499 $exporter = Red_FileIO::create( 'rss' );
500 $exporter->force_download();
501 echo $exporter->get_data( $items, array() );
502 die();
503 }
504 }
505 }
506
507 private function try_export_logs() {
508 if ( $this->user_has_access() && isset( $_POST['export-csv'] ) && check_admin_referer( 'wp_rest' ) ) {
509 if ( $this->get_menu_page() === 'log' ) {
510 RE_Log::export_to_csv();
511 } else {
512 RE_404::export_to_csv();
513 }
514
515 die();
516 }
517 }
518
519 private function try_export_redirects() {
520 if ( $this->user_has_access() && $_GET['sub'] === 'io' && isset( $_GET['exporter'] ) && isset( $_GET['export'] ) && check_admin_referer( 'wp_rest' ) ) {
521 $export = Red_FileIO::export( $_GET['export'], $_GET['exporter'] );
522
523 if ( $export !== false ) {
524 $export['exporter']->force_download();
525 echo $export['data'];
526 die();
527 }
528 }
529 }
530 }
531
532 register_activation_hook( REDIRECTION_FILE, array( 'Redirection_Admin', 'plugin_activated' ) );
533
534 add_action( 'init', array( 'Redirection_Admin', 'init' ) );
535