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

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