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

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