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

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