Safe Redirect Manager - Version 1.8

Version Description

  • Improved escaping
  • Custom redirect capability
  • Code refactor
  • Fix root redirect in sub directory bug
  • Fix broken html
Download this release

Release Info

Developer tlovett1
Plugin Icon 128x128 Safe Redirect Manager
Version 1.8
Comparing to
See all releases

Code changes from version 1.7.8 to 1.8

Dockunit.json DELETED
@@ -1,13 +0,0 @@
1
- {
2
- "containers": [
3
- {
4
- "prettyName": "PHP-FPM 5.6",
5
- "image": "dockunit/prebuilt-images:php-mysql-phpunit-5.6-fpm",
6
- "beforeScripts": [
7
- "service mysql start",
8
- "bash bin/install-wp-tests.sh wordpress_test root '' localhost 4.1"
9
- ],
10
- "testCommand": "phpunit"
11
- }
12
- ]
13
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -1,4 +1,4 @@
1
- Safe Redirect Manager [![Build Status](https://travis-ci.org/tlovett1/Safe-Redirect-Manager.svg?branch=master)](https://travis-ci.org/tlovett1/Safe-Redirect-Manager) [![Dockunit Status](https://dockunit.io/svg/tlovett1/Safe-Redirect-Manager?master)](https://dockunit.io/projects/tlovett1/Safe-Redirect-Manager#master)
2
  ==============
3
 
4
  A WordPress plugin to safely and easily manage your website's HTTP redirects.
@@ -10,7 +10,7 @@ them store redirects in the options table or in custom tables. Most of them prov
10
  of them have serious performance implications (404 error logging). Safe Redirect Manager stores redirects as Custom
11
  Post Types. This makes your data portable and your website scalable. Safe Redirect Manager is built to handle enterprise
12
  level traffic and is used on major publishing websites. The plugin comes with only what you need following the
13
- WordPress mantra decisions not options. Actions in filters make the plugin very extensible.
14
 
15
  ## Installation
16
 
@@ -55,7 +55,7 @@ temporarily moved, or 301, permanently moved.
55
 
56
  * Redirects are cached using the Transients API. Cache busts occur when redirects are added, updated, and deleted
57
  so you shouldn't be serving stale redirects.
58
- * By default the plugin only allows at most 150 redirects to prevent performance issues. There is a filter
59
  `srm_max_redirects` that you can utilize to up this number.
60
  * "Redirect From" and requested paths are case insensitive by default.
61
 
@@ -71,35 +71,20 @@ add_filter( 'my_srm_redirect_loop_filter', '__return_true' );
71
 
72
  #### Setup
73
  Follow the configuration instructions above to setup the plugin. I recommend developing the plugin locally in an
74
- environment such as [Varying Vagrant Vagrants](https://github.com/Varying-Vagrant-Vagrants/VVV).
75
 
76
  #### Translation
77
- Safe Redirect Manager has a [.pot file](https://github.com/tlovett1/Safe-Redirect-Manager/blob/master/languages/safe-redirect-manager.pot)
78
- containing strings ready for translation. You can use a program like [POedit](http://poedit.net) to generate .po/.mo
79
- files for your language.
80
 
81
  #### Testing
82
  Within the terminal change directories to the plugin folder. Initialize your unit testing environment by running the
83
  following command:
84
 
85
- For VVV users:
86
  ```bash
87
- bash bin/install-wp-tests.sh wordpress_test root root localhost latest
88
  ```
89
 
90
- For VIP Quickstart users:
91
- ```bash
92
- bash bin/install-wp-tests.sh wordpress_test root '' localhost latest
93
- ```
94
-
95
- where:
96
-
97
- * wordpress_test is the name of the test database (all data will be deleted!)
98
- * root is the MySQL user name
99
- * root is the MySQL user password (if you're running VVV). Blank if you're running VIP Quickstart.
100
- * localhost is the MySQL server host
101
- * latest is the WordPress version; could also be 3.7, 3.6.2 etc.
102
-
103
  Run the plugin tests:
104
  ```bash
105
  phpunit
1
+ Safe Redirect Manager [![Build Status](https://travis-ci.org/tlovett1/safe-redirect-manager.svg?branch=master)](https://travis-ci.org/tlovett1/safe-redirect-manager)
2
  ==============
3
 
4
  A WordPress plugin to safely and easily manage your website's HTTP redirects.
10
  of them have serious performance implications (404 error logging). Safe Redirect Manager stores redirects as Custom
11
  Post Types. This makes your data portable and your website scalable. Safe Redirect Manager is built to handle enterprise
12
  level traffic and is used on major publishing websites. The plugin comes with only what you need following the
13
+ WordPress mantra, decisions not options. Actions in filters make the plugin very extensible.
14
 
15
  ## Installation
16
 
55
 
56
  * Redirects are cached using the Transients API. Cache busts occur when redirects are added, updated, and deleted
57
  so you shouldn't be serving stale redirects.
58
+ * By default the plugin only allows at most 250 redirects to prevent performance issues. There is a filter
59
  `srm_max_redirects` that you can utilize to up this number.
60
  * "Redirect From" and requested paths are case insensitive by default.
61
 
71
 
72
  #### Setup
73
  Follow the configuration instructions above to setup the plugin. I recommend developing the plugin locally in an
74
+ environment such as [WP Local Docker](https://github.com/10up/wp-local-docker).
75
 
76
  #### Translation
77
+ Safe Redirect Manager has a [.pot file](https://github.com/tlovett1/Safe-Redirect-Manager/blob/master/lang/safe-redirect-manager.pot)
78
+ containing strings ready for translation.
 
79
 
80
  #### Testing
81
  Within the terminal change directories to the plugin folder. Initialize your unit testing environment by running the
82
  following command:
83
 
 
84
  ```bash
85
+ bash bin/install-wp-tests.sh database username password host version
86
  ```
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  Run the plugin tests:
89
  ```bash
90
  phpunit
assets/banner-772x250.png ADDED
Binary file
assets/icon-256x256.png ADDED
Binary file
bin/install-wp-tests.sh DELETED
@@ -1,78 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- if [ $# -lt 3 ]; then
4
- echo "usage: $0 <db-name> <db-user> <db-pass> [db-host] [wp-version]"
5
- exit 1
6
- fi
7
-
8
- DB_NAME=$1
9
- DB_USER=$2
10
- DB_PASS=$3
11
- DB_HOST=${4-localhost}
12
- WP_VERSION=${5-latest}
13
-
14
- WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib}
15
- WP_CORE_DIR=/tmp/wordpress/
16
-
17
- set -ex
18
-
19
- install_wp() {
20
- mkdir -p $WP_CORE_DIR
21
-
22
- if [ $WP_VERSION == 'latest' ]; then
23
- local ARCHIVE_NAME='latest'
24
- else
25
- local ARCHIVE_NAME="wordpress-$WP_VERSION"
26
- fi
27
-
28
- wget -nv -O /tmp/wordpress.tar.gz http://wordpress.org/${ARCHIVE_NAME}.tar.gz --no-check-certificate
29
- tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR
30
-
31
- wget -nv -O $WP_CORE_DIR/wp-content/db.php https://raw.github.com/markoheijnen/wp-mysqli/master/db.php --no-check-certificate
32
- }
33
-
34
- install_test_suite() {
35
- # portable in-place argument for both GNU sed and Mac OSX sed
36
- if [[ $(uname -s) == 'Darwin' ]]; then
37
- local ioption='-i .bak'
38
- else
39
- local ioption='-i'
40
- fi
41
-
42
- # set up testing suite
43
- mkdir -p $WP_TESTS_DIR
44
- cd $WP_TESTS_DIR
45
- svn co --quiet --trust-server-cert --non-interactive https://develop.svn.wordpress.org/trunk/tests/phpunit/includes/
46
-
47
- wget -nv -O wp-tests-config.php http://develop.svn.wordpress.org/trunk/wp-tests-config-sample.php --no-check-certificate
48
- sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR':" wp-tests-config.php
49
- sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" wp-tests-config.php
50
- sed $ioption "s/yourusernamehere/$DB_USER/" wp-tests-config.php
51
- sed $ioption "s/yourpasswordhere/$DB_PASS/" wp-tests-config.php
52
- sed $ioption "s|localhost|${DB_HOST}|" wp-tests-config.php
53
- }
54
-
55
- install_db() {
56
- # parse DB_HOST for port or socket references
57
- local PARTS=(${DB_HOST//\:/ })
58
- local DB_HOSTNAME=${PARTS[0]};
59
- local DB_SOCK_OR_PORT=${PARTS[1]};
60
- local EXTRA=""
61
-
62
- if ! [ -z $DB_HOSTNAME ] ; then
63
- if [[ "$DB_SOCK_OR_PORT" =~ ^[0-9]+$ ]] ; then
64
- EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
65
- elif ! [ -z $DB_SOCK_OR_PORT ] ; then
66
- EXTRA=" --socket=$DB_SOCK_OR_PORT"
67
- elif ! [ -z $DB_HOSTNAME ] ; then
68
- EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
69
- fi
70
- fi
71
-
72
- # create database
73
- mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
74
- }
75
-
76
- install_wp
77
- install_test_suite
78
- install_db
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
composer.json DELETED
@@ -1,11 +0,0 @@
1
- {
2
- "name": "tlovett1/safe-redirect-manager",
3
- "type": "wordpress-plugin",
4
- "description": "Easily and safely manage HTTP redirects.",
5
- "authors": [
6
- {
7
- "name": "Taylor Lovett",
8
- "email": "taylorl@10up.com"
9
- }
10
- ]
11
- }
 
 
 
 
 
 
 
 
 
 
 
inc/classes/class-srm-post-type.php ADDED
@@ -0,0 +1,605 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Setup SRM post type
4
+ */
5
+
6
+ class SRM_Post_Type {
7
+
8
+ public $status_code_labels = array(); // Defined later to allow i18n
9
+
10
+ private $whitelist_hosts = array();
11
+
12
+ /**
13
+ * Sets up redirect manager
14
+ *
15
+ * @since 1.8
16
+ */
17
+ public function setup() {
18
+ $this->status_code_labels = array(
19
+ 301 => esc_html__( 'Moved Permanently', 'safe-redirect-manager' ),
20
+ 302 => esc_html__( 'Found', 'safe-redirect-manager' ),
21
+ 303 => esc_html__( 'See Other', 'safe-redirect-manager' ),
22
+ 307 => esc_html__( 'Temporary Redirect', 'safe-redirect-manager' ),
23
+ 403 => esc_html__( 'Forbidden', 'safe-redirect-manager' ),
24
+ 404 => esc_html__( 'Not Found', 'safe-redirect-manager' ),
25
+ );
26
+
27
+ add_action( 'init', array( $this, 'action_register_post_types' ) );
28
+ add_action( 'save_post', array( $this, 'action_save_post' ) );
29
+ add_filter( 'manage_redirect_rule_posts_columns', array( $this, 'filter_redirect_columns' ) );
30
+ add_action( 'manage_redirect_rule_posts_custom_column', array( $this, 'action_custom_redirect_columns' ), 10, 2 );
31
+ add_action( 'transition_post_status', array( $this, 'action_transition_post_status' ), 10, 3 );
32
+ add_filter( 'post_updated_messages', array( $this, 'filter_redirect_updated_messages' ) );
33
+ add_action( 'admin_notices', array( $this, 'action_redirect_chain_alert' ) );
34
+ add_filter( 'the_title', array( $this, 'filter_admin_title' ), 100, 2 );
35
+ add_filter( 'bulk_actions-edit-redirect_rule', array( $this, 'filter_bulk_actions' ) );
36
+ add_action( 'admin_print_styles-edit.php', array( $this, 'action_print_logo_css' ), 10, 1 );
37
+ add_action( 'admin_print_styles-post.php', array( $this, 'action_print_logo_css' ), 10, 1 );
38
+ add_action( 'admin_print_styles-post-new.php', array( $this, 'action_print_logo_css' ), 10, 1 );
39
+ add_filter( 'post_type_link', array( $this, 'filter_post_type_link' ), 10, 2 );
40
+
41
+ // Search filters
42
+ add_filter( 'posts_join', array( $this, 'filter_search_join' ) );
43
+ add_filter( 'posts_where', array( $this, 'filter_search_where' ) );
44
+ add_filter( 'posts_distinct', array( $this, 'filter_search_distinct' ) );
45
+ add_filter( 'post_row_actions', array( $this, 'filter_disable_quick_edit' ), 10, 2 );
46
+ }
47
+
48
+ /**
49
+ * Remove quick edit
50
+ *
51
+ * @param array $actions
52
+ * @param WP_Post $post
53
+ * @since 1.8
54
+ * @return array
55
+ */
56
+ public function filter_disable_quick_edit( $actions = array(), $post ) {
57
+ if ( 'redirect_rule' === get_post_type( $post ) && isset( $actions['inline hide-if-no-js'] ) ) {
58
+ unset( $actions['inline hide-if-no-js'] );
59
+ unset( $actions['view'] );
60
+ }
61
+
62
+ return $actions;
63
+ }
64
+
65
+ /**
66
+ * Join posts table with postmeta table on search
67
+ *
68
+ * @since 1.2
69
+ * @param string $join
70
+ * @uses get_query_var
71
+ * @return string
72
+ */
73
+ public function filter_search_join( $join ) {
74
+ global $wp_query;
75
+
76
+ if ( empty( $wp_query ) || 'redirect_rule' !== get_query_var( 'post_type' ) ) {
77
+ return $join;
78
+ }
79
+
80
+ global $wpdb;
81
+
82
+ $s = get_query_var( 's' );
83
+ if ( ! empty( $s ) ) {
84
+ $join .= " LEFT JOIN $wpdb->postmeta AS m ON ($wpdb->posts.ID = m.post_id) ";
85
+ }
86
+ return $join;
87
+ }
88
+
89
+ /**
90
+ * Return distinct search results
91
+ *
92
+ * @since 1.2
93
+ * @param string $distinct
94
+ * @uses get_query_var
95
+ * @return string
96
+ */
97
+ public function filter_search_distinct( $distinct ) {
98
+ global $wp_query;
99
+
100
+ if ( empty( $wp_query ) || 'redirect_rule' !== get_query_var( 'post_type' ) ) {
101
+ return $distinct;
102
+ }
103
+
104
+ return 'DISTINCT';
105
+ }
106
+
107
+ /**
108
+ * Join posts table with postmeta table on search
109
+ *
110
+ * @since 1.2
111
+ * @param string $where
112
+ * @uses is_search, get_query_var
113
+ * @return string
114
+ */
115
+ public function filter_search_where( $where ) {
116
+ global $wp_query;
117
+
118
+ if ( empty( $wp_query ) || 'redirect_rule' !== get_query_var( 'post_type' ) || ! is_search() || empty( $where ) ) {
119
+ return $where;
120
+ }
121
+
122
+ $terms = $this->get_search_terms();
123
+
124
+ if ( empty( $terms ) ) {
125
+ return $where;
126
+ }
127
+
128
+ $exact = get_query_var( 'exact' );
129
+ $n = ( ! empty( $exact ) ) ? '' : '%';
130
+
131
+ $search = '';
132
+ $seperator = '';
133
+ $search .= '(';
134
+
135
+ // we check the meta values against each term in the search
136
+ foreach ( $terms as $term ) {
137
+ $search .= $seperator;
138
+ // Used esc_sql instead of wpdb->prepare since wpdb->prepare wraps things in quotes
139
+ $search .= sprintf( "( ( m.meta_value LIKE '%s%s%s' AND m.meta_key = '%s') OR ( m.meta_value LIKE '%s%s%s' AND m.meta_key = '%s') )", $n, esc_sql( $term ), $n, esc_sql( '_redirect_rule_from' ), $n, esc_sql( $term ), $n, esc_sql( '_redirect_rule_to' ) );
140
+
141
+ $seperator = ' OR ';
142
+ }
143
+
144
+ $search .= ')';
145
+
146
+ $where = preg_replace( '/\(\(\(.*?\)\)\)/is', '((' . $search . '))', $where );
147
+
148
+ return $where;
149
+ }
150
+
151
+ /**
152
+ * Get an array of search terms
153
+ *
154
+ * @since 1.2
155
+ * @uses get_query_var
156
+ * @return array
157
+ */
158
+ private function get_search_terms() {
159
+ $s = get_query_var( 's' );
160
+
161
+ if ( ! empty( $s ) ) {
162
+ preg_match_all( '/".*?("|$)|((?<=[\\s",+])|^)[^\\s",+]+/', stripslashes( $s ), $matches );
163
+ $search_terms = array_map( create_function( '$a', 'return trim( $a, "\\"\'\\n\\r " );' ), $matches[0] );
164
+ }
165
+ return $search_terms;
166
+ }
167
+
168
+ /**
169
+ * Swap tools logo for plugin logo
170
+ *
171
+ * @since 1.1
172
+ * @uses plugins_url
173
+ * @return void
174
+ */
175
+ public function action_print_logo_css() {
176
+ if ( $this->is_plugin_page() ) {
177
+ ?>
178
+ <style type="text/css">
179
+ #visibility, .view-switch, .posts .inline-edit-col-left .inline-edit-group, #preview-action {
180
+ display: none;
181
+ }
182
+ #srm_redirect_rule_from {
183
+ width: 60%;
184
+ }
185
+ </style>
186
+ <?php
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Limit the bulk actions available in the Manage Redirects view
192
+ *
193
+ * @since 1.0
194
+ * @return array
195
+ */
196
+ public function filter_bulk_actions( $actions ) {
197
+
198
+ // No bulk editing at this time
199
+ unset( $actions['edit'] );
200
+
201
+ return $actions;
202
+ }
203
+
204
+ /**
205
+ * Whether or not this is an admin page specific to the plugin
206
+ *
207
+ * @since 1.1
208
+ * @uses get_post_type
209
+ * @return bool
210
+ */
211
+ private function is_plugin_page() {
212
+ return (bool) ( get_post_type() === 'redirect_rule' || ( isset( $_GET['post_type'] ) && 'redirect_rule' === $_GET['post_type'] ) );
213
+ }
214
+
215
+ /**
216
+ * Echoes admin message if redirect chains exist
217
+ *
218
+ * @since 1.0
219
+ * @uses apply_filters
220
+ * @return void
221
+ */
222
+ public function action_redirect_chain_alert() {
223
+ global $hook_suffix;
224
+ if ( $this->is_plugin_page() ) {
225
+
226
+ /**
227
+ * check_for_possible_redirect_loops() runs in best case Theta(n^2) so if you have 100 redirects, this method
228
+ * will be running slow. Let's disable it by default.
229
+ */
230
+ if ( apply_filters( 'srm_check_for_possible_redirect_loops', false ) ) {
231
+ if ( srm_check_for_possible_redirect_loops() ) {
232
+ ?>
233
+ <div class="updated">
234
+ <p><?php esc_html_e( 'Safe Redirect Manager Warning: Possible redirect loops and/or chains have been created.', 'safe-redirect-manager' ); ?></p>
235
+ </div>
236
+ <?php
237
+ }
238
+ } if ( srm_max_redirects_reached() ) {
239
+ ?>
240
+ <?php
241
+ if ( 'post-new.php' === $hook_suffix ) :
242
+ ?>
243
+ <style type="text/css">#post { display: none; }</style><?php endif; ?>
244
+ <div class="error">
245
+ <p><?php esc_html_e( 'Safe Redirect Manager Error: You have reached the maximum allowable number of redirects', 'safe-redirect-manager' ); ?></p>
246
+ </div>
247
+ <?php
248
+ }
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Filters title out for redirect from in post manager
254
+ *
255
+ * @since 1.0
256
+ * @param string $title
257
+ * @param int $post_id
258
+ * @uses is_admin, get_post_meta
259
+ * @return string
260
+ */
261
+ public function filter_admin_title( $title, $post_id = 0 ) {
262
+ if ( ! is_admin() ) {
263
+ return $title;
264
+ }
265
+
266
+ $redirect = get_post( $post_id );
267
+ if ( empty( $redirect ) ) {
268
+ return $title;
269
+ }
270
+
271
+ if ( $redirect->post_type !== 'redirect_rule' ) {
272
+ return $title;
273
+ }
274
+
275
+ $redirect_from = get_post_meta( $post_id, '_redirect_rule_from', true );
276
+ if ( ! empty( $redirect_from ) ) {
277
+ return $redirect_from;
278
+ }
279
+
280
+ return $title;
281
+ }
282
+
283
+ /**
284
+ * Customizes updated messages for redirects
285
+ *
286
+ * @since 1.0
287
+ * @param array $messages
288
+ * @uses esc_url, get_permalink, add_query_var, wp_post_revision_title
289
+ * @return array
290
+ */
291
+ public function filter_redirect_updated_messages( $messages ) {
292
+ global $post, $post_ID;
293
+
294
+ $messages['redirect_rule'] = array(
295
+ 0 => '', // Unused. Messages start at index 1.
296
+ 1 => sprintf( esc_html__( 'Redirect rule updated.', 'safe-redirect-manager' ), esc_url( get_permalink( $post_ID ) ) ),
297
+ 2 => esc_html__( 'Custom field updated.', 'safe-redirect-manager' ),
298
+ 3 => esc_html__( 'Custom field deleted.', 'safe-redirect-manager' ),
299
+ 4 => esc_html__( 'Redirect rule updated.', 'safe-redirect-manager' ),
300
+ /* translators: %s: date and time of the revision */
301
+ 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Redirect rule restored to revision from %s', 'safe-redirect-manager' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
302
+ 6 => sprintf( esc_html__( 'Redirect rule published.', 'safe-redirect-manager' ), esc_url( get_permalink( $post_ID ) ) ),
303
+ 7 => esc_html__( 'Redirect rule saved.', 'safe-redirect-manager' ),
304
+ 8 => sprintf( esc_html__( 'Redirect rule submitted.', 'safe-redirect-manager' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post_ID ) ) ) ),
305
+ 9 => sprintf(
306
+ esc_html__( 'Redirect rule scheduled for: %1$s.', 'safe-redirect-manager' ),
307
+ // translators: Publish box date format, see http://php.net/date
308
+ date_i18n( esc_html__( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( get_permalink( $post_ID ) )
309
+ ),
310
+ 10 => sprintf( esc_html__( 'Redirect rule draft updated.', 'safe-redirect-manager' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post_ID ) ) ) ),
311
+ );
312
+
313
+ return $messages;
314
+ }
315
+
316
+ /**
317
+ * Clear redirect cache if appropriate post type is transitioned
318
+ *
319
+ * @since 1.0
320
+ * @param string $new_status
321
+ * @param string $old_status
322
+ * @param object $post
323
+ * @return void
324
+ */
325
+ public function action_transition_post_status( $new_status, $old_status, $post ) {
326
+ if ( ! is_object( $post ) ) {
327
+ return;
328
+ }
329
+
330
+ // recreate redirect cache
331
+ if ( 'redirect_rule' === $post->post_type ) {
332
+ srm_flush_cache();
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Displays custom columns on redirect manager screen
338
+ *
339
+ * @since 1.0
340
+ * @param string $column
341
+ * @param int $post_id
342
+ * @uses get_post_meta, esc_html, admin_url
343
+ * @return void
344
+ */
345
+ public function action_custom_redirect_columns( $column, $post_id ) {
346
+ if ( 'srm_redirect_rule_to' === $column ) {
347
+ echo esc_html( get_post_meta( $post_id, '_redirect_rule_to', true ) );
348
+ } elseif ( 'srm_redirect_rule_status_code' === $column ) {
349
+ echo absint( get_post_meta( $post_id, '_redirect_rule_status_code', true ) );
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Add new columns to manage redirect screen
355
+ *
356
+ * @since 1.0
357
+ * @param array $columns
358
+ * @return array
359
+ */
360
+ public function filter_redirect_columns( $columns ) {
361
+ $columns['srm_redirect_rule_to'] = esc_html__( 'Redirect To', 'safe-redirect-manager' );
362
+ $columns['srm_redirect_rule_status_code'] = esc_html__( 'HTTP Status Code', 'safe-redirect-manager' );
363
+
364
+ // Change the title column
365
+ $columns['title'] = esc_html__( 'Redirect From', 'safe-redirect-manager' );
366
+
367
+ // Move date column to the back
368
+ unset( $columns['date'] );
369
+ $columns['date'] = esc_html__( 'Date', 'safe-redirect-manager' );
370
+
371
+ return $columns;
372
+ }
373
+
374
+ /**
375
+ * Saves meta info for redirect rules
376
+ *
377
+ * @since 1.0
378
+ * @param int $post_id
379
+ * @uses current_user_can, get_post_type, wp_verify_nonce, update_post_meta, delete_post_meta
380
+ * @return void
381
+ */
382
+ public function action_save_post( $post_id ) {
383
+ if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ! current_user_can( 'edit_post', $post_id ) || 'revision' === get_post_type( $post_id ) ) {
384
+ return;
385
+ }
386
+
387
+ // Update post meta for redirect rules
388
+ if ( ! empty( $_POST['srm_redirect_nonce'] ) && wp_verify_nonce( $_POST['srm_redirect_nonce'], 'srm-save-redirect-meta' ) ) {
389
+
390
+ if ( ! empty( $_POST['srm_redirect_rule_from_regex'] ) ) {
391
+ $allow_regex = (bool) $_POST['srm_redirect_rule_from_regex'];
392
+ update_post_meta( $post_id, '_redirect_rule_from_regex', $allow_regex );
393
+ } else {
394
+ $allow_regex = false;
395
+ delete_post_meta( $post_id, '_redirect_rule_from_regex' );
396
+ }
397
+
398
+ if ( ! empty( $_POST['srm_redirect_rule_from'] ) ) {
399
+ update_post_meta( $post_id, '_redirect_rule_from', srm_sanitize_redirect_from( $_POST['srm_redirect_rule_from'], $allow_regex ) );
400
+ } else {
401
+ delete_post_meta( $post_id, '_redirect_rule_from' );
402
+ }
403
+
404
+ if ( ! empty( $_POST['srm_redirect_rule_to'] ) ) {
405
+ update_post_meta( $post_id, '_redirect_rule_to', srm_sanitize_redirect_to( $_POST['srm_redirect_rule_to'] ) );
406
+ } else {
407
+ delete_post_meta( $post_id, '_redirect_rule_to' );
408
+ }
409
+
410
+ if ( ! empty( $_POST['srm_redirect_rule_status_code'] ) ) {
411
+ update_post_meta( $post_id, '_redirect_rule_status_code', absint( $_POST['srm_redirect_rule_status_code'] ) );
412
+ } else {
413
+ delete_post_meta( $post_id, '_redirect_rule_status_code' );
414
+ }
415
+
416
+ /**
417
+ * This fixes an important bug where the redirect cache was not up-to-date. Previously the cache was only being
418
+ * updated on transition_post_status which gets called BEFORE save post. But since save_post is where all the important
419
+ * redirect info is saved, updating the cache before it is not sufficient.
420
+ */
421
+ srm_flush_cache();
422
+ }
423
+ }
424
+
425
+ /**
426
+ * Registers post types for plugin
427
+ *
428
+ * @since 1.0
429
+ * @uses register_post_type, _x, plugins_url, apply_filters
430
+ * @return void
431
+ */
432
+ public function action_register_post_types() {
433
+ $redirect_labels = array(
434
+ 'name' => esc_html_x( 'Safe Redirect Manager', 'post type general name', 'safe-redirect-manager' ),
435
+ 'singular_name' => esc_html_x( 'Redirect', 'post type singular name', 'safe-redirect-manager' ),
436
+ 'add_new' => _x( 'Create Redirect Rule', 'redirect rule', 'safe-redirect-manager' ),
437
+ 'add_new_item' => esc_html__( 'Safe Redirect Manager', 'safe-redirect-manager' ),
438
+ 'edit_item' => esc_html__( 'Edit Redirect Rule', 'safe-redirect-manager' ),
439
+ 'new_item' => esc_html__( 'New Redirect Rule', 'safe-redirect-manager' ),
440
+ 'all_items' => esc_html__( 'Safe Redirect Manager', 'safe-redirect-manager' ),
441
+ 'view_item' => esc_html__( 'View Redirect Rule', 'safe-redirect-manager' ),
442
+ 'search_items' => esc_html__( 'Search Redirects', 'safe-redirect-manager' ),
443
+ 'not_found' => esc_html__( 'No redirect rules found.', 'safe-redirect-manager' ),
444
+ 'not_found_in_trash' => esc_html__( 'No redirect rules found in trash.', 'safe-redirect-manager' ),
445
+ 'parent_item_colon' => '',
446
+ 'menu_name' => esc_html__( 'Safe Redirect Manager', 'safe-redirect-manager' ),
447
+ );
448
+
449
+ $redirect_capability = 'srm_manage_redirects';
450
+
451
+ $roles = array( 'administrator' );
452
+
453
+ foreach ( $roles as $role ) {
454
+ $role = get_role( $role );
455
+
456
+ if ( empty( $role ) || $role->has_cap( $redirect_capability ) ) {
457
+ continue;
458
+ }
459
+
460
+ $role->add_cap( $redirect_capability );
461
+ }
462
+
463
+ $redirect_capability = apply_filters( 'srm_restrict_to_capability', $redirect_capability );
464
+
465
+ $capabilities = array(
466
+ 'edit_post' => $redirect_capability,
467
+ 'read_post' => $redirect_capability,
468
+ 'delete_post' => $redirect_capability,
469
+ 'delete_posts' => $redirect_capability,
470
+ 'edit_posts' => $redirect_capability,
471
+ 'edit_others_posts' => $redirect_capability,
472
+ 'publish_posts' => $redirect_capability,
473
+ 'read_private_posts' => $redirect_capability,
474
+ );
475
+
476
+ $redirect_args = array(
477
+ 'labels' => $redirect_labels,
478
+ 'public' => false,
479
+ 'publicly_queryable' => true,
480
+ 'show_ui' => true,
481
+ 'show_in_menu' => 'tools.php',
482
+ 'query_var' => false,
483
+ 'rewrite' => false,
484
+ 'capability_type' => 'post',
485
+ 'capabilities' => $capabilities,
486
+ 'has_archive' => false,
487
+ 'hierarchical' => false,
488
+ 'register_meta_box_cb' => array( $this, 'action_redirect_rule_metabox' ),
489
+ 'menu_position' => 80,
490
+ 'supports' => array( '' ),
491
+ );
492
+ register_post_type( 'redirect_rule', $redirect_args );
493
+ }
494
+
495
+ /**
496
+ * Registers meta boxes for redirect rule post type
497
+ *
498
+ * @since 1.0
499
+ * @uses add_meta_box
500
+ * @return void
501
+ */
502
+ public function action_redirect_rule_metabox() {
503
+ add_meta_box( 'redirect_settings', esc_html__( 'Redirect Settings', 'safe-redirect-manager' ), array( $this, 'redirect_rule_metabox' ), 'redirect_rule', 'normal', 'core' );
504
+ }
505
+
506
+ /**
507
+ * Echoes HTML for redirect rule meta box
508
+ *
509
+ * @since 1.0
510
+ * @param object $post
511
+ * @uses wp_nonce_field, get_post_meta, esc_attr, selected
512
+ * @return void
513
+ */
514
+ public function redirect_rule_metabox( $post ) {
515
+ wp_nonce_field( 'srm-save-redirect-meta', 'srm_redirect_nonce' );
516
+
517
+ $redirect_from = get_post_meta( $post->ID, '_redirect_rule_from', true );
518
+ $redirect_to = get_post_meta( $post->ID, '_redirect_rule_to', true );
519
+ $status_code = get_post_meta( $post->ID, '_redirect_rule_status_code', true );
520
+ $enable_regex = get_post_meta( $post->ID, '_redirect_rule_from_regex', true );
521
+
522
+ if ( empty( $status_code ) ) {
523
+ $status_code = apply_filters( 'srm_default_direct_status', 302 );
524
+ }
525
+ ?>
526
+ <p>
527
+ <label for="srm_redirect_rule_from"><?php esc_html_e( 'Redirect From:', 'safe-redirect-manager' ); ?></label><br />
528
+ <input type="text" name="srm_redirect_rule_from" id="srm_redirect_rule_from" value="<?php echo esc_attr( $redirect_from ); ?>" />
529
+ <input type="checkbox" name="srm_redirect_rule_from_regex" id="srm_redirect_rule_from_regex" <?php checked( true, (bool) $enable_regex ); ?> value="1" />
530
+ <label for="srm_redirect_rule_from_regex"><?php esc_html_e( 'Enable Regular Expressions (advanced)', 'safe-redirect-manager' ); ?></label>
531
+ </p>
532
+ <p class="description"><?php esc_html_e( 'This path should be relative to the root of this WordPress installation (or the sub-site, if you are running a multi-site). Appending a (*) wildcard character will match all requests with the base. Warning: Enabling regular expressions will disable wildcards and completely change the way the * symbol is interpretted.', 'safe-redirect-manager' ); ?></p>
533
+
534
+ <p>
535
+ <label for="srm_redirect_rule_to"><?php esc_html_e( 'Redirect To:', 'safe-redirect-manager' ); ?></label><br />
536
+ <input class="widefat" type="text" name="srm_redirect_rule_to" id="srm_redirect_rule_to" value="<?php echo esc_attr( $redirect_to ); ?>" />
537
+ </p>
538
+ <p class="description"><?php esc_html_e( 'This can be a URL or a path relative to the root of your website (not your WordPress installation). Ending with a (*) wildcard character will append the request match to the redirect.', 'safe-redirect-manager' ); ?></p>
539
+
540
+ <p>
541
+ <label for="srm_redirect_rule_status_code"><?php esc_html_e( 'HTTP Status Code:', 'safe-redirect-manager' ); ?></label>
542
+ <select name="srm_redirect_rule_status_code" id="srm_redirect_rule_status_code">
543
+ <?php foreach ( srm_get_valid_status_codes() as $code ) : ?>
544
+ <option value="<?php echo esc_attr( $code ); ?>" <?php selected( $status_code, $code ); ?>><?php echo esc_html( $code . ' ' . $this->status_code_labels[ $code ] ); ?></option>
545
+ <?php endforeach; ?>
546
+ </select>
547
+ <em><?php esc_html_e( "If you don't know what this is, leave it as is.", 'safe-redirect-manager' ); ?></em>
548
+ </p>
549
+ <?php
550
+ }
551
+
552
+ /**
553
+ * Return a permalink for a redirect post, which is the "redirect from"
554
+ * URL for that redirect.
555
+ *
556
+ * @since 1.7
557
+ * @param string $permalink The permalink
558
+ * @param object $post A Post object
559
+ * @uses home_url, get_post_meta
560
+ * @return string The permalink
561
+ */
562
+ public function filter_post_type_link( $permalink, $post ) {
563
+ if ( 'redirect_rule' !== $post->post_type ) {
564
+ return $permalink;
565
+ }
566
+
567
+ // We can't do anything to provide a permalink
568
+ // for regex enabled redirects.
569
+ if ( get_post_meta( $post->ID, '_redirect_rule_from_regex', true ) ) {
570
+ return $permalink;
571
+ }
572
+
573
+ // We can't do anything if there is a wildcard in the redirect from
574
+ $redirect_from = get_post_meta( $post->ID, '_redirect_rule_from', true );
575
+ if ( false !== strpos( $redirect_from, '*' ) ) {
576
+ return $permalink;
577
+ }
578
+
579
+ // Use default permalink if no $redirect_from exists - this prevents duplicate GUIDs
580
+ if ( empty( $redirect_from ) ) {
581
+ return $permalink;
582
+ }
583
+
584
+ return home_url( $redirect_from );
585
+ }
586
+
587
+ /**
588
+ * Return singleton instance of class
589
+ *
590
+ * @return object
591
+ * @since 1.8
592
+ */
593
+ public static function factory() {
594
+ static $instance = false;
595
+
596
+ if ( ! $instance ) {
597
+ $instance = new self();
598
+ $instance->setup();
599
+ }
600
+
601
+ return $instance;
602
+ }
603
+ }
604
+
605
+ SRM_Post_Type::factory();
inc/classes/class-srm-redirect.php ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Handle redirection
4
+ */
5
+
6
+ class SRM_Redirect {
7
+ /**
8
+ * Store whitelisted host
9
+ *
10
+ * @var array
11
+ */
12
+ private $whitelist_host;
13
+
14
+ /**
15
+ * Initialize redirect listening
16
+ *
17
+ * @since 1.8
18
+ */
19
+ public function setup() {
20
+ add_action( 'parse_request', array( $this, 'maybe_redirect' ), 0 );
21
+ }
22
+
23
+ /**
24
+ * Apply whitelisted host to allowed_redirect_hosts filter
25
+ *
26
+ * @since 1.8
27
+ * @param array $hosts
28
+ * @return array
29
+ */
30
+ public function filter_allowed_redirect_hosts( $hosts ) {
31
+ $without_www = preg_replace( '/^www\./i', '', $this->whitelist_host );
32
+ $with_www = 'www.' . $without_www;
33
+
34
+ $hosts[] = $without_www;
35
+ $hosts[] = $with_www;
36
+
37
+ return array_unique( $hosts );
38
+ }
39
+
40
+ /**
41
+ * Check current url against redirects
42
+ *
43
+ * @since 1.8
44
+ */
45
+ public function maybe_redirect() {
46
+
47
+ // Don't redirect from wp-admin
48
+ if ( is_admin() ) {
49
+ return;
50
+ }
51
+
52
+ $redirects = srm_get_redirects();
53
+
54
+ // If we have no redirects, there is no need to continue
55
+ if ( empty( $redirects ) ) {
56
+ return;
57
+ }
58
+
59
+ // get requested path and add a / before it
60
+ $requested_path = esc_url_raw( $_SERVER['REQUEST_URI'] );
61
+ $requested_path = untrailingslashit( stripslashes( $requested_path ) );
62
+
63
+ /**
64
+ * If WordPress resides in a directory that is not the public root, we have to chop
65
+ * the pre-WP path off the requested path.
66
+ */
67
+ if ( function_exists( 'wp_parse_url' ) ) {
68
+ $parsed_home_url = wp_parse_url( home_url() );
69
+ } else {
70
+ $parsed_home_url = parse_url( home_url() );
71
+ }
72
+
73
+ if ( isset( $parsed_home_url['path'] ) && '/' !== $parsed_home_url['path'] ) {
74
+ $requested_path = preg_replace( '@' . $parsed_home_url['path'] . '@i', '', $requested_path, 1 );
75
+ }
76
+
77
+ if ( empty( $requested_path ) ) {
78
+ $requested_path = '/';
79
+ }
80
+
81
+ // Allow redirects to be filtered
82
+ $redirects = apply_filters( 'srm_registered_redirects', $redirects, $requested_path );
83
+
84
+ // Allow for case insensitive redirects
85
+ $case_insensitive = apply_filters( 'srm_case_insensitive_redirects', true );
86
+
87
+ if ( $case_insensitive ) {
88
+ $regex_flag = 'i';
89
+ // normalized path is used for matching but not for replace
90
+ $normalized_requested_path = strtolower( $requested_path );
91
+ } else {
92
+ $regex_flag = '';
93
+ $normalized_requested_path = $requested_path;
94
+ }
95
+
96
+ foreach ( (array) $redirects as $redirect ) {
97
+
98
+ $redirect_from = untrailingslashit( $redirect['redirect_from'] );
99
+ if ( empty( $redirect_from ) ) {
100
+ $redirect_from = '/'; // this only happens in the case where there is a redirect on the root
101
+ }
102
+
103
+ $redirect_to = $redirect['redirect_to'];
104
+ $status_code = $redirect['status_code'];
105
+ $enable_regex = ( isset( $redirect['enable_regex'] ) ) ? $redirect['enable_regex'] : false;
106
+
107
+ // check if the redirection destination is valid, otherwise just skip it
108
+ if ( empty( $redirect_to ) ) {
109
+ continue;
110
+ }
111
+
112
+ // check if requested path is the same as the redirect from path
113
+ if ( $enable_regex ) {
114
+ $matched_path = preg_match( '@' . $redirect_from . '@' . $regex_flag, $requested_path );
115
+ } else {
116
+ if ( $case_insensitive ) {
117
+ $redirect_from = strtolower( $redirect_from );
118
+ }
119
+
120
+ $matched_path = ( $normalized_requested_path === $redirect_from );
121
+
122
+ // check if the redirect_from ends in a wildcard
123
+ if ( ! $matched_path && ( strrpos( $redirect_from, '*' ) === strlen( $redirect_from ) - 1 ) ) {
124
+ $wildcard_base = substr( $redirect_from, 0, strlen( $redirect_from ) - 1 );
125
+
126
+ // mark as match if requested path matches the base of the redirect from
127
+ $matched_path = ( substr( $normalized_requested_path, 0, strlen( $wildcard_base ) ) === $wildcard_base );
128
+ if ( ( strrpos( $redirect_to, '*' ) === strlen( $redirect_to ) - 1 ) ) {
129
+ $redirect_to = rtrim( $redirect_to, '*' ) . ltrim( substr( $requested_path, strlen( $wildcard_base ) ), '/' );
130
+ }
131
+ }
132
+ }
133
+
134
+ if ( $matched_path ) {
135
+ /**
136
+ * Whitelist redirect host
137
+ */
138
+ if ( function_exists( 'wp_parse_url' ) ) {
139
+ $parsed_redirect = wp_parse_url( $redirect_to );
140
+ } else {
141
+ $parsed_redirect = parse_url( $redirect_to );
142
+ }
143
+
144
+ if ( is_array( $parsed_redirect ) && ! empty( $parsed_redirect['host'] ) ) {
145
+ $this->whitelist_host = $parsed_redirect['host'];
146
+ add_filter( 'allowed_redirect_hosts', array( $this, 'filter_allowed_redirect_hosts' ) );
147
+ }
148
+
149
+ // Allow for regex replacement in $redirect_to
150
+ if ( $enable_regex ) {
151
+ $redirect_to = preg_replace( '@' . $redirect_from . '@' . $regex_flag, $redirect_to, $requested_path );
152
+ }
153
+
154
+ $sanitized_redirect_to = esc_url_raw( $redirect_to );
155
+
156
+ do_action( 'srm_do_redirect', $requested_path, $sanitized_redirect_to, $status_code );
157
+
158
+ if ( defined( 'PHPUNIT_SRM_TESTSUITE' ) && PHPUNIT_SRM_TESTSUITE ) {
159
+ // Don't actually redirect if we are testing
160
+ return;
161
+ }
162
+
163
+ header( 'X-Safe-Redirect-Manager: true' );
164
+
165
+ // if we have a valid status code, then redirect with it
166
+ if ( in_array( $status_code, srm_get_valid_status_codes(), true ) ) {
167
+ wp_safe_redirect( $sanitized_redirect_to, $status_code );
168
+ } else {
169
+ wp_safe_redirect( $sanitized_redirect_to );
170
+ }
171
+
172
+ exit;
173
+ }
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Return singleton instance of class
179
+ *
180
+ * @return object
181
+ * @since 1.8
182
+ */
183
+ public static function factory() {
184
+ static $instance = false;
185
+
186
+ if ( ! $instance ) {
187
+ $instance = new self();
188
+ $instance->setup();
189
+ }
190
+
191
+ return $instance;
192
+ }
193
+ }
194
+
195
+ SRM_Redirect::factory();
inc/classes/class-srm-wp-cli.php ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Setup SRM WP CLI commands
4
+ */
5
+
6
+ class SRM_WP_CLI extends WP_CLI_Command {
7
+
8
+
9
+ /**
10
+ * List all of the currently configured redirects
11
+ *
12
+ * @subcommand list
13
+ */
14
+ public function _list() {
15
+ $fields = array(
16
+ 'ID',
17
+ 'redirect_from',
18
+ 'redirect_to',
19
+ 'status_code',
20
+ 'enable_regex',
21
+ 'post_status',
22
+ );
23
+
24
+ $table = new \cli\Table();
25
+ $table->setHeaders( $fields );
26
+
27
+ $redirects = srm_get_redirects( array( 'post_status' => 'any' ), true );
28
+
29
+ foreach ( $redirects as $redirect ) {
30
+ $line = array();
31
+ foreach ( $fields as $field ) {
32
+ if ( 'enable_regex' === $field ) {
33
+ $line[] = ( $redirect[ $field ] ) ? 'true' : 'false';
34
+ } else {
35
+ $line[] = $redirect[ $field ];
36
+ }
37
+ }
38
+
39
+ $table->addRow( $line );
40
+ }
41
+
42
+ $table->display();
43
+
44
+ WP_CLI::line( 'Total of ' . count( $redirects ) . ' redirects' );
45
+ }
46
+
47
+ /**
48
+ * Create a redirect
49
+ *
50
+ * @subcommand create
51
+ * @synopsis <from> <to> [<status-code>] [<enable-regex>] [<post-status>]
52
+ */
53
+ public function create( $args ) {
54
+ $defaults = array(
55
+ '',
56
+ '',
57
+ 302,
58
+ false,
59
+ 'publish',
60
+ );
61
+ // array_merge() doesn't work here because our keys are numeric
62
+ foreach ( $defaults as $key => $value ) {
63
+ if ( ! isset( $args[ $key ] ) ) {
64
+ $args[ $key ] = $defaults[ $key ];
65
+ }
66
+ }
67
+ list( $from, $to, $status_code, $enable_regex, $post_status ) = $args;
68
+
69
+ // User might've passed as string.
70
+ if ( empty( $enable_regex ) || 'false' === $enable_regex ) {
71
+ $enable_regex = false;
72
+ }
73
+
74
+ if ( empty( $from ) || empty( $to ) ) {
75
+ WP_CLI::error( '<from> and <to> are required arguments.' );
76
+ }
77
+
78
+ $ret = srm_create_redirect( $from, $to, $status_code, $enable_regex, $post_status );
79
+
80
+ if ( is_wp_error( $ret ) ) {
81
+ WP_CLI::error( $ret->get_error_message() );
82
+ } else {
83
+ WP_CLI::success( "Created redirect as #{$ret}" );
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Delete a redirect
89
+ *
90
+ * @subcommand delete
91
+ * @synopsis <id>
92
+ */
93
+ public function delete( $args ) {
94
+ $id = ( ! empty( $args[0] ) ) ? (int) $args[0] : 0;
95
+
96
+ $redirect = get_post( $id );
97
+ if ( ! $redirect || 'redirect_rule' !== $redirect->post_type ) {
98
+ WP_CLI::error( "{$id} isn't a valid redirect." );
99
+ }
100
+
101
+ wp_delete_post( $id );
102
+
103
+ srm_flush_cache();
104
+
105
+ WP_CLI::success( "Redirect #{$id} has been deleted." );
106
+ }
107
+
108
+ /**
109
+ * Update the redirect cache
110
+ *
111
+ * @subcommand update-cache
112
+ */
113
+ public function update_cache() {
114
+ srm_flush_cache();
115
+
116
+ WP_CLI::success( 'Redirect cache has been updated.' );
117
+ }
118
+
119
+ /**
120
+ * Import .htaccess file redirects
121
+ *
122
+ * @subcommand import-htaccess
123
+ * @synopsis <file>
124
+ */
125
+ public function import_htaccess( $args, $assoc_args ) {
126
+ list( $file ) = $args;
127
+
128
+ $contents = file_get_contents( $file );
129
+ if ( ! $contents ) {
130
+ WP_CLI::error( 'Error retrieving .htaccess file' );
131
+ }
132
+
133
+ $pieces = explode( PHP_EOL, $contents );
134
+ $created = 0;
135
+ $skipped = 0;
136
+ foreach ( $pieces as $piece ) {
137
+
138
+ // Ignore if this line isn't a redirect
139
+ if ( ! preg_match( '/^Redirect( permanent)?/i', $piece ) ) {
140
+ continue;
141
+ }
142
+
143
+ // Parse the redirect
144
+ $redirect = preg_replace( '/\s{2,}/', ' ', $piece );
145
+ $redirect = preg_replace( '/^Redirect( permanent)? (.*)$/i', '$2', trim( $redirect ) );
146
+ $redirect = explode( ' ', $redirect );
147
+
148
+ // if there are three parts to the redirect, we assume the first part is a status code
149
+ if ( 2 === count( $redirect ) ) {
150
+ $from = $redirect[0];
151
+ $to = $redirect[1];
152
+ $http_status = 301;
153
+ } elseif ( 3 === count( $redirect ) ) {
154
+ $http_status = $redirect[0];
155
+ $from = $redirect[1];
156
+ $to = $redirect[2];
157
+ } else {
158
+ continue;
159
+ }
160
+
161
+ // Validate
162
+ if ( ! $from || ! $to ) {
163
+ WP_CLI::warning( "Skipping - '{$piece}' is formatted improperly." );
164
+ continue;
165
+ }
166
+
167
+ $sanitized_redirect_from = srm_sanitize_redirect_from( $from );
168
+ $sanitized_redirect_to = srm_sanitize_redirect_to( $to );
169
+
170
+ $id = srm_create_redirect( $sanitized_redirect_from, $sanitized_redirect_to, $http_status );
171
+ if ( is_wp_error( $id ) ) {
172
+ WP_CLI::warning( 'Error - ' . $id->get_error_message() );
173
+ $skipped++;
174
+ } else {
175
+ WP_CLI::line( "Success - Created redirect from '{$sanitized_redirect_from}' to '{$sanitized_redirect_to}'" );
176
+ $created++;
177
+ }
178
+ }
179
+ WP_CLI::success( "All done! {$created} redirects were created, {$skipped} were skipped" );
180
+ }
181
+
182
+ /**
183
+ * Imports redirects from CSV file.
184
+ *
185
+ * ## OPTIONS
186
+ *
187
+ * <file>
188
+ * : Path to one or more valid CSV file for import. This file should contain
189
+ * redirection from and to URLs, regex flag and HTTP redirection code. Here
190
+ * is example table:
191
+ *
192
+ * | source | target | regex | code |
193
+ * |----------------------------|--------------------|-------|------|
194
+ * | /legacy-url | /new-url | 0 | 301 |
195
+ * | /category-1 | /new-category-slug | 0 | 302 |
196
+ * | /tes?t/[0-9]+/path/[^/]+/? | /go/here | 1 | 302 |
197
+ * | ... | ... | ... | ... |
198
+ *
199
+ * You can also use exported redirects from "Redirection" plugin, which you
200
+ * can download here: /wp-admin/tools.php?page=redirection.php&sub=modules
201
+ *
202
+ * <source-column>
203
+ * : Header title for sources column mapping.
204
+ *
205
+ * <target-column>
206
+ * : Header title for target column mapping.
207
+ *
208
+ * <regex-column>
209
+ * : Header title for regex column mapping.
210
+ *
211
+ * <code-column>
212
+ * : Header title for code column mapping.
213
+ *
214
+ * ## EXAMPLE
215
+ *
216
+ * wp safe-redirect-manager import redirections.csv
217
+ *
218
+ * @synopsis <file> [--source=<source-column>] [--target=<target-column>] [--regex=<regex-column>] [--code=<code-column>]
219
+ *
220
+ * @since 1.7.6
221
+ *
222
+ * @access public
223
+ * @param array $args The array of input files.
224
+ * @param array $assoc_args The array of column mappings.
225
+ */
226
+ public function import( $args, $assoc_args ) {
227
+ $mapping = wp_parse_args(
228
+ $assoc_args, array(
229
+ 'source' => 'source',
230
+ 'target' => 'target',
231
+ 'regex' => 'regex',
232
+ 'code' => 'code',
233
+ )
234
+ );
235
+
236
+ $created = $skipped = 0;
237
+
238
+ foreach ( $args as $file ) {
239
+ $processed = srm_import_file( $file, $mapping );
240
+ if ( ! empty( $processed ) ) {
241
+ $created += $processed['created'];
242
+ $skipped += $processed['skipped'];
243
+
244
+ WP_CLI::success( basename( $file ) . ' file processed successfully.' );
245
+ }
246
+ }
247
+
248
+ WP_CLI::success( "All done! {$created} redirects were imported, {$skipped} were skipped" );
249
+ }
250
+ }
251
+
252
+ WP_CLI::add_command( 'safe-redirect-manager', 'SRM_WP_CLI' );
inc/functions.php ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Get redirects from the database
5
+ *
6
+ * @since 1.8
7
+ * @param array $args Any arguments to filter by
8
+ * @param bool $hard Force cache refresh
9
+ * @return array $redirects An array of redirects
10
+ */
11
+ function srm_get_redirects( $args = array(), $hard = false ) {
12
+
13
+ if ( $hard || false === ( $redirects = get_transient( '_srm_redirects' ) ) ) {
14
+
15
+ $redirects = array();
16
+
17
+ $posts_per_page = 100;
18
+
19
+ $i = 1;
20
+
21
+ $default_max_redirects = apply_filters( 'srm_max_redirects', 250 );
22
+
23
+ while ( true ) {
24
+ if ( count( $redirects ) >= $default_max_redirects ) {
25
+ break;
26
+ }
27
+
28
+ $defaults = array(
29
+ 'posts_per_page' => $posts_per_page,
30
+ 'post_status' => 'publish',
31
+ 'paged' => $i,
32
+ 'fields' => 'ids',
33
+ );
34
+
35
+ $query_args = array_merge( $defaults, $args );
36
+
37
+ // Some arguments that don't need to be configurable
38
+ $query_args['post_type'] = 'redirect_rule';
39
+ $query_args['update_term_cache'] = false;
40
+
41
+ $redirect_query = new WP_Query( $query_args );
42
+
43
+ if ( ! $redirect_query->have_posts() ) {
44
+ break;
45
+ }
46
+
47
+ foreach ( $redirect_query->posts as $redirect_id ) {
48
+ if ( count( $redirects ) >= $default_max_redirects ) {
49
+ break 2;
50
+ }
51
+
52
+ $redirects[] = array(
53
+ 'ID' => $redirect_id,
54
+ 'post_status' => get_post_status( $redirect_id ),
55
+ 'redirect_from' => get_post_meta( $redirect_id, '_redirect_rule_from', true ),
56
+ 'redirect_to' => get_post_meta( $redirect_id, '_redirect_rule_to', true ),
57
+ 'status_code' => (int) get_post_meta( $redirect_id, '_redirect_rule_status_code', true ),
58
+ 'enable_regex' => (bool) get_post_meta( $redirect_id, '_redirect_rule_from_regex', true ),
59
+ );
60
+ }
61
+
62
+ $i++;
63
+
64
+ }
65
+
66
+ set_transient( '_srm_redirects', $redirects );
67
+ }
68
+
69
+ return $redirects;
70
+ }
71
+
72
+ /**
73
+ * Returns true if max redirects have been reached
74
+ *
75
+ * @since 1.8
76
+ * @return bool
77
+ */
78
+ function srm_max_redirects_reached() {
79
+ $default_max_redirects = apply_filters( 'srm_max_redirects', 250 );
80
+
81
+ $redirects = srm_get_redirects();
82
+
83
+ return ( count( $redirects ) >= $default_max_redirects );
84
+ }
85
+
86
+ /**
87
+ * Get valid HTTP status codes
88
+ *
89
+ * @since 1.8
90
+ * @return array
91
+ */
92
+ function srm_get_valid_status_codes() {
93
+ return apply_filters( 'srm_valid_status_codes', array( 301, 302, 303, 307, 403, 404 ) );
94
+ }
95
+
96
+ /**
97
+ * Flush redirect cache
98
+ *
99
+ * @since 1.8
100
+ */
101
+ function srm_flush_cache() {
102
+ delete_transient( '_srm_redirects' );
103
+ }
104
+
105
+
106
+ /**
107
+ * Check for potential redirect loops or chains
108
+ *
109
+ * @since 1.8
110
+ * @return boolean
111
+ */
112
+ function srm_check_for_possible_redirect_loops() {
113
+ $redirects = srm_get_redirects();
114
+
115
+ $current_url = parse_url( home_url() );
116
+ $this_host = ( is_array( $current_url ) && ! empty( $current_url['host'] ) ) ? $current_url['host'] : '';
117
+
118
+ foreach ( $redirects as $redirect ) {
119
+ $redirect_from = $redirect['redirect_from'];
120
+
121
+ // check redirect from against all redirect to's
122
+ foreach ( $redirects as $compare_redirect ) {
123
+ $redirect_to = $compare_redirect['redirect_to'];
124
+
125
+ $redirect_url = parse_url( $redirect_to );
126
+ $redirect_host = ( is_array( $redirect_url ) && ! empty( $redirect_url['host'] ) ) ? $redirect_url['host'] : '';
127
+
128
+ // check if we are redirecting locally
129
+ if ( empty( $redirect_host ) || $redirect_host === $this_host ) {
130
+ $redirect_from_url = preg_replace( '/(http:\/\/|https:\/\/|www\.)/i', '', home_url() . $redirect_from );
131
+ $redirect_to_url = $redirect_to;
132
+ if ( ! preg_match( '/https?:\/\//i', $redirect_to_url ) ) {
133
+ $redirect_to_url = $this_host . $redirect_to_url;
134
+ } else {
135
+ $redirect_to_url = preg_replace( '/(http:\/\/|https:\/\/|www\.)/i', '', $redirect_to_url );
136
+ }
137
+
138
+ // possible loop/chain found
139
+ if ( $redirect_to_url === $redirect_from_url ) {
140
+ return true;
141
+ }
142
+ }
143
+ }
144
+ }
145
+
146
+ return false;
147
+ }
148
+
149
+ /**
150
+ * Creates a redirect post, this function will be useful for import/exports scripts
151
+ *
152
+ * @param string $redirect_from
153
+ * @param string $redirect_to
154
+ * @param int $status_code
155
+ * @param bool $enable_regex
156
+ * @param string $post_status
157
+ * @since 1.8
158
+ * @uses wp_insert_post, update_post_meta
159
+ * @return int|WP_Error
160
+ */
161
+ function srm_create_redirect( $redirect_from, $redirect_to, $status_code = 302, $enable_regex = false, $post_status = 'publish' ) {
162
+ global $wpdb;
163
+
164
+ $sanitized_redirect_from = srm_sanitize_redirect_from( $redirect_from );
165
+ $sanitized_redirect_to = srm_sanitize_redirect_to( $redirect_to );
166
+ $sanitized_status_code = absint( $status_code );
167
+ $sanitized_enable_regex = (bool) $enable_regex;
168
+ $sanitized_post_status = sanitize_key( $post_status );
169
+
170
+ // check and make sure no parameters are empty or invalid after sanitation
171
+ if ( empty( $sanitized_redirect_from ) || empty( $sanitized_redirect_to ) ) {
172
+ return new WP_Error( 'invalid-argument', esc_html__( 'Redirect from and/or redirect to arguments are invalid.', 'safe-redirect-manager' ) );
173
+ }
174
+
175
+ if ( ! in_array( $sanitized_status_code, srm_get_valid_status_codes(), true ) ) {
176
+ return new WP_Error( 'invalid-argument', esc_html__( 'Invalid status code.', 'safe-redirect-manager' ) );
177
+ }
178
+
179
+ // Check to ensure this redirect doesn't already exist
180
+ if ( $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key=%s AND meta_value=%s", '_redirect_rule_from', $sanitized_redirect_from ) ) ) {
181
+ return new WP_Error( 'duplicate-redirect', sprintf( esc_html__( 'Redirect already exists for %s', 'safe-redirect-manager' ), $sanitized_redirect_from ) );
182
+ }
183
+
184
+ // create the post
185
+ $post_args = array(
186
+ 'post_type' => 'redirect_rule',
187
+ 'post_status' => $sanitized_post_status,
188
+ 'post_author' => 1,
189
+ );
190
+
191
+ $post_id = wp_insert_post( $post_args );
192
+
193
+ if ( 0 >= $post_id ) {
194
+ return new WP_Error( 'error-creating', esc_html__( 'An error occurred creating the redirect.', 'safe-redirect-manager' ) );
195
+ }
196
+
197
+ // update the posts meta info
198
+ update_post_meta( $post_id, '_redirect_rule_from', $sanitized_redirect_from );
199
+ update_post_meta( $post_id, '_redirect_rule_to', $sanitized_redirect_to );
200
+ update_post_meta( $post_id, '_redirect_rule_status_code', $sanitized_status_code );
201
+ update_post_meta( $post_id, '_redirect_rule_from_regex', $sanitized_enable_regex );
202
+
203
+ // We need to update the cache after creating this redirect
204
+ srm_flush_cache();
205
+
206
+ return $post_id;
207
+ }
208
+
209
+
210
+
211
+ /**
212
+ * Sanitize redirect to path
213
+ *
214
+ * The only difference between this function and just calling esc_url_raw is
215
+ * esc_url_raw( 'test' ) == 'http://test', whereas sanitize_redirect_path( 'test' ) == '/test'
216
+ *
217
+ * @since 1.8
218
+ * @param string $path
219
+ * @return string
220
+ */
221
+ function srm_sanitize_redirect_to( $path ) {
222
+ $path = trim( $path );
223
+
224
+ if ( preg_match( '/^www\./i', $path ) ) {
225
+ $path = 'http://' . $path;
226
+ }
227
+
228
+ if ( ! preg_match( '/^https?:\/\//i', $path ) ) {
229
+ if ( strpos( $path, '/' ) !== 0 ) {
230
+ $path = '/' . $path;
231
+ }
232
+ }
233
+
234
+ return esc_url_raw( $path );
235
+ }
236
+
237
+ /**
238
+ * Sanitize redirect from path
239
+ *
240
+ * @since 1.8
241
+ * @param string $path
242
+ * @param boolean $allow_regex
243
+ * @return string
244
+ */
245
+ function srm_sanitize_redirect_from( $path, $allow_regex = false ) {
246
+
247
+ $path = trim( $path );
248
+
249
+ if ( empty( $path ) ) {
250
+ return '';
251
+ }
252
+
253
+ // dont accept paths starting with a .
254
+ if ( ! $allow_regex && strpos( $path, '.' ) === 0 ) {
255
+ return '';
256
+ }
257
+
258
+ // turn path in to absolute
259
+ if ( preg_match( '/https?:\/\//i', $path ) ) {
260
+ $path = preg_replace( '/^(http:\/\/|https:\/\/)(www\.)?[^\/?]+\/?(.*)/i', '/$3', $path );
261
+ } elseif ( ! $allow_regex && strpos( $path, '/' ) !== 0 ) {
262
+ $path = '/' . $path;
263
+ }
264
+
265
+ // the @ symbol will break our regex engine
266
+ $path = str_replace( '@', '', $path );
267
+
268
+ return $path;
269
+ }
270
+
271
+ /**
272
+ * Imports redirects from CSV file or stream.
273
+ *
274
+ * @since 1.8
275
+ *
276
+ * @access public
277
+ * @param string|resource $file File path, file pointer or stream to read redirects from.
278
+ * @param array $args The array of arguments. Includes column mapping and CSV settings.
279
+ * @return array Returns importing statistic on success, otherwise FALSE.
280
+ */
281
+ function srm_import_file( $file, $args ) {
282
+ $handle = $file;
283
+ $close_handle = false;
284
+ $doing_wp_cli = defined( 'WP_CLI' ) && WP_CLI;
285
+
286
+ // filter arguments
287
+ $args = apply_filters( 'srm_import_file_args', $args );
288
+
289
+ // enable line endings auto detection
290
+ @ini_set( 'auto_detect_line_endings', true );
291
+
292
+ // open file pointer if $file is not a resource
293
+ if ( ! is_resource( $file ) ) {
294
+ $handle = fopen( $file, 'rb' );
295
+ if ( ! $handle ) {
296
+ $doing_wp_cli && WP_CLI::error( sprintf( 'Error retrieving %s file', basename( $file ) ) );
297
+ return false;
298
+ }
299
+
300
+ $close_handle = true;
301
+ }
302
+
303
+ // process all rows of the file
304
+ $created = $skipped = 0;
305
+ $headers = fgetcsv( $handle );
306
+
307
+ while ( ( $row = fgetcsv( $handle ) ) ) {
308
+ // validate
309
+ $rule = array_combine( $headers, $row );
310
+ if ( empty( $rule[ $args['source'] ] ) || empty( $rule[ $args['target'] ] ) ) {
311
+ $doing_wp_cli && WP_CLI::warning( 'Skipping - redirection rule is formatted improperly.' );
312
+ $skipped++;
313
+ continue;
314
+ }
315
+
316
+ // sanitize
317
+ $redirect_from = srm_sanitize_redirect_from( $rule[ $args['source'] ] );
318
+ $redirect_to = srm_sanitize_redirect_to( $rule[ $args['target'] ] );
319
+ $status_code = ! empty( $rule[ $args['code'] ] ) ? $rule[ $args['code'] ] : 302;
320
+ $regex = ! empty( $rule[ $args['regex'] ] ) ? filter_var( $rule[ $args['regex'] ], FILTER_VALIDATE_BOOLEAN ) : false;
321
+
322
+ // import
323
+ $id = srm_create_redirect( $redirect_from, $redirect_to, $status_code, $regex );
324
+
325
+ if ( is_wp_error( $id ) ) {
326
+ $doing_wp_cli && WP_CLI::warning( $id );
327
+ $skipped++;
328
+ } else {
329
+ $doing_wp_cli && WP_CLI::line( "Success - Created redirect from '{$redirect_from}' to '{$redirect_to}'" );
330
+ $created++;
331
+ }
332
+ }
333
+
334
+ // close an open file pointer if we've opened it
335
+ if ( $close_handle ) {
336
+ fclose( $handle );
337
+ }
338
+
339
+ // return result statistic
340
+ return array(
341
+ 'created' => $created,
342
+ 'skipped' => $skipped,
343
+ );
344
+ }
{languages → lang}/safe-redirect-manager-sk_SK.mo RENAMED
File without changes
{languages → lang}/safe-redirect-manager-sk_SK.po RENAMED
File without changes
lang/safe-redirect-manager.pot ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2017 Taylor Lovett (10up)
2
+ # This file is distributed under the GPLv2 or later.
3
+ msgid ""
4
+ msgstr ""
5
+ "Project-Id-Version: Safe Redirect Manager 1.8\n"
6
+ "Report-Msgid-Bugs-To: "
7
+ "https://wordpress.org/support/plugin/safe-redirect-manager\n"
8
+ "POT-Creation-Date: 2017-12-08 06:00:17+00:00\n"
9
+ "MIME-Version: 1.0\n"
10
+ "Content-Type: text/plain; charset=utf-8\n"
11
+ "Content-Transfer-Encoding: 8bit\n"
12
+ "PO-Revision-Date: 2017-MO-DA HO:MI+ZONE\n"
13
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
+ "Language-Team: LANGUAGE <LL@li.org>\n"
15
+ "X-Generator: node-wp-i18n 1.0.4\n"
16
+
17
+ #: inc/classes/class-srm-post-type.php:19
18
+ msgid "Moved Permanently"
19
+ msgstr ""
20
+
21
+ #: inc/classes/class-srm-post-type.php:20
22
+ msgid "Found"
23
+ msgstr ""
24
+
25
+ #: inc/classes/class-srm-post-type.php:21
26
+ msgid "See Other"
27
+ msgstr ""
28
+
29
+ #: inc/classes/class-srm-post-type.php:22
30
+ msgid "Temporary Redirect"
31
+ msgstr ""
32
+
33
+ #: inc/classes/class-srm-post-type.php:23
34
+ msgid "Forbidden"
35
+ msgstr ""
36
+
37
+ #: inc/classes/class-srm-post-type.php:24
38
+ msgid "Not Found"
39
+ msgstr ""
40
+
41
+ #: inc/classes/class-srm-post-type.php:234
42
+ msgid ""
43
+ "Safe Redirect Manager Warning: Possible redirect loops and/or chains have "
44
+ "been created."
45
+ msgstr ""
46
+
47
+ #: inc/classes/class-srm-post-type.php:245
48
+ msgid ""
49
+ "Safe Redirect Manager Error: You have reached the maximum allowable number "
50
+ "of redirects"
51
+ msgstr ""
52
+
53
+ #: inc/classes/class-srm-post-type.php:296
54
+ #: inc/classes/class-srm-post-type.php:299
55
+ msgid "Redirect rule updated."
56
+ msgstr ""
57
+
58
+ #: inc/classes/class-srm-post-type.php:297
59
+ msgid "Custom field updated."
60
+ msgstr ""
61
+
62
+ #: inc/classes/class-srm-post-type.php:298
63
+ msgid "Custom field deleted."
64
+ msgstr ""
65
+
66
+ #: inc/classes/class-srm-post-type.php:301
67
+ #. translators: %s: date and time of the revision
68
+ msgid "Redirect rule restored to revision from %s"
69
+ msgstr ""
70
+
71
+ #: inc/classes/class-srm-post-type.php:302
72
+ msgid "Redirect rule published."
73
+ msgstr ""
74
+
75
+ #: inc/classes/class-srm-post-type.php:303
76
+ msgid "Redirect rule saved."
77
+ msgstr ""
78
+
79
+ #: inc/classes/class-srm-post-type.php:304
80
+ msgid "Redirect rule submitted."
81
+ msgstr ""
82
+
83
+ #: inc/classes/class-srm-post-type.php:306
84
+ msgid "Redirect rule scheduled for: %1$s."
85
+ msgstr ""
86
+
87
+ #: inc/classes/class-srm-post-type.php:308
88
+ #. translators: Publish box date format, see http:php.net/date
89
+ msgid "M j, Y @ G:i"
90
+ msgstr ""
91
+
92
+ #: inc/classes/class-srm-post-type.php:310
93
+ msgid "Redirect rule draft updated."
94
+ msgstr ""
95
+
96
+ #: inc/classes/class-srm-post-type.php:361
97
+ msgid "Redirect To"
98
+ msgstr ""
99
+
100
+ #: inc/classes/class-srm-post-type.php:362
101
+ msgid "HTTP Status Code"
102
+ msgstr ""
103
+
104
+ #: inc/classes/class-srm-post-type.php:365
105
+ msgid "Redirect From"
106
+ msgstr ""
107
+
108
+ #: inc/classes/class-srm-post-type.php:369
109
+ msgid "Date"
110
+ msgstr ""
111
+
112
+ #. Plugin Name of the plugin/theme
113
+ msgid "Safe Redirect Manager"
114
+ msgstr ""
115
+
116
+ #: inc/classes/class-srm-post-type.php:438
117
+ msgid "Edit Redirect Rule"
118
+ msgstr ""
119
+
120
+ #: inc/classes/class-srm-post-type.php:439
121
+ msgid "New Redirect Rule"
122
+ msgstr ""
123
+
124
+ #: inc/classes/class-srm-post-type.php:441
125
+ msgid "View Redirect Rule"
126
+ msgstr ""
127
+
128
+ #: inc/classes/class-srm-post-type.php:442
129
+ msgid "Search Redirects"
130
+ msgstr ""
131
+
132
+ #: inc/classes/class-srm-post-type.php:443
133
+ msgid "No redirect rules found."
134
+ msgstr ""
135
+
136
+ #: inc/classes/class-srm-post-type.php:444
137
+ msgid "No redirect rules found in trash."
138
+ msgstr ""
139
+
140
+ #: inc/classes/class-srm-post-type.php:488
141
+ msgid "Redirect Settings"
142
+ msgstr ""
143
+
144
+ #: inc/classes/class-srm-post-type.php:512
145
+ msgid "Redirect From:"
146
+ msgstr ""
147
+
148
+ #: inc/classes/class-srm-post-type.php:515
149
+ msgid "Enable Regular Expressions (advanced)"
150
+ msgstr ""
151
+
152
+ #: inc/classes/class-srm-post-type.php:517
153
+ msgid ""
154
+ "This path should be relative to the root of this WordPress installation (or "
155
+ "the sub-site, if you are running a multi-site). Appending a (*) wildcard "
156
+ "character will match all requests with the base. Warning: Enabling regular "
157
+ "expressions will disable wildcards and completely change the way the * "
158
+ "symbol is interpretted."
159
+ msgstr ""
160
+
161
+ #: inc/classes/class-srm-post-type.php:520
162
+ msgid "Redirect To:"
163
+ msgstr ""
164
+
165
+ #: inc/classes/class-srm-post-type.php:523
166
+ msgid ""
167
+ "This can be a URL or a path relative to the root of your website (not your "
168
+ "WordPress installation). Ending with a (*) wildcard character will append "
169
+ "the request match to the redirect."
170
+ msgstr ""
171
+
172
+ #: inc/classes/class-srm-post-type.php:526
173
+ msgid "HTTP Status Code:"
174
+ msgstr ""
175
+
176
+ #: inc/classes/class-srm-post-type.php:532
177
+ msgid "If you don't know what this is, leave it as is."
178
+ msgstr ""
179
+
180
+ #: inc/functions.php:172
181
+ msgid "Redirect from and/or redirect to arguments are invalid."
182
+ msgstr ""
183
+
184
+ #: inc/functions.php:176
185
+ msgid "Invalid status code."
186
+ msgstr ""
187
+
188
+ #: inc/functions.php:181
189
+ msgid "Redirect already exists for %s"
190
+ msgstr ""
191
+
192
+ #: inc/functions.php:194
193
+ msgid "An error occurred creating the redirect."
194
+ msgstr ""
195
+
196
+ #. Author URI of the plugin/theme
197
+ msgid "https://10up.com"
198
+ msgstr ""
199
+
200
+ #. Description of the plugin/theme
201
+ msgid "Easily and safely manage HTTP redirects."
202
+ msgstr ""
203
+
204
+ #. Author of the plugin/theme
205
+ msgid "Taylor Lovett (10up)"
206
+ msgstr ""
207
+
208
+ #: inc/classes/class-srm-post-type.php:434
209
+ msgctxt "post type general name"
210
+ msgid "Safe Redirect Manager"
211
+ msgstr ""
212
+
213
+ #: inc/classes/class-srm-post-type.php:435
214
+ msgctxt "post type singular name"
215
+ msgid "Redirect"
216
+ msgstr ""
217
+
218
+ #: inc/classes/class-srm-post-type.php:436
219
+ msgctxt "redirect rule"
220
+ msgid "Create Redirect Rule"
221
+ msgstr ""
languages/safe-redirect-manager-fr_FR.mo DELETED
Binary file
languages/safe-redirect-manager-fr_FR.po DELETED
@@ -1,185 +0,0 @@
1
- # Copyright (C) 2012 Safe Redirect Manager
2
- # This file is distributed under the same license as the Safe Redirect Manager package.
3
- msgid ""
4
- msgstr ""
5
- "Project-Id-Version: Safe Redirect Manager 1.4\n"
6
- "Report-Msgid-Bugs-To: http://wordpress.org/tag/safe-redirect-manager\n"
7
- "POT-Creation-Date: 2012-10-09 23:02:23+00:00\n"
8
- "MIME-Version: 1.0\n"
9
- "Content-Type: text/plain; charset=UTF-8\n"
10
- "Content-Transfer-Encoding: 8bit\n"
11
- "PO-Revision-Date: 2014-01-22 14:16+0100\n"
12
- "Last-Translator: Jean-Christophe Brebion <pro@jcbrebion.com>\n"
13
- "Language-Team: Jean-Christophe Brebion <pro@jcbrebion.com>\n"
14
- "X-Generator: Poedit 1.6.3\n"
15
- "Plural-Forms: nplurals=2; plural=(n > 1);\n"
16
- "Language: fr_FR\n"
17
- "X-Poedit-SourceCharset: UTF-8\n"
18
-
19
- #: safe-redirect-manager.php:259
20
- msgid ""
21
- "Safe Redirect Manager Warning: Possible redirect loops and/or chains have "
22
- "been created."
23
- msgstr ""
24
- "Avertissement de Safe Redirect Manager: il est possible que vous ayez créé "
25
- "des boucles et/ou chaînes de redirections."
26
-
27
- #: safe-redirect-manager.php:266
28
- msgid ""
29
- "Safe Redirect Manager Error: You have reached the maximum allowable number "
30
- "of redirects"
31
- msgstr ""
32
- "Erreur de Safe Redirect Manager: Vous avez atteint le nombre maximal "
33
- "autorisé de redirections."
34
-
35
- #: safe-redirect-manager.php:367 safe-redirect-manager.php:370
36
- msgid "Redirect rule updated."
37
- msgstr "Règle de redirection mise à jour."
38
-
39
- #: safe-redirect-manager.php:368
40
- msgid "Custom field updated."
41
- msgstr "Champ personnalisé mis à jour."
42
-
43
- #: safe-redirect-manager.php:369
44
- msgid "Custom field deleted."
45
- msgstr "Champ personnalisé supprimé."
46
-
47
- #. translators: %s: date and time of the revision
48
- #: safe-redirect-manager.php:372
49
- msgid "Redirect rule restored to revision from %s"
50
- msgstr "Règle de redirection restaurée de la révision à partir de %s"
51
-
52
- #: safe-redirect-manager.php:373
53
- msgid "Redirect rule published."
54
- msgstr "Règle de redirection publiée."
55
-
56
- #: safe-redirect-manager.php:374
57
- msgid "Redirect rule saved."
58
- msgstr "Règle de redirection enregistrée."
59
-
60
- #: safe-redirect-manager.php:375
61
- msgid "Redirect rule submitted."
62
- msgstr "Règle de redirection envoyée."
63
-
64
- #: safe-redirect-manager.php:376
65
- msgid "Redirect rule scheduled for: <strong>%1$s</strong>."
66
- msgstr "Règle de redirection planifiée pour: <strong>%1$s</strong>."
67
-
68
- #. translators: Publish box date format, see http:php.net/date
69
- #: safe-redirect-manager.php:378
70
- msgid "M j, Y @ G:i"
71
- msgstr "j M Y @ G:i"
72
-
73
- #: safe-redirect-manager.php:379
74
- msgid "Redirect rule draft updated."
75
- msgstr "Brouillon de règle de redirection mis à jour."
76
-
77
- #: safe-redirect-manager.php:431
78
- msgid "Redirect To"
79
- msgstr "Redirection vers"
80
-
81
- #: safe-redirect-manager.php:432
82
- msgid "HTTP Status Code"
83
- msgstr "Code de statut HTTP"
84
-
85
- #: safe-redirect-manager.php:435
86
- msgid "Redirect From"
87
- msgstr "Redirection à partir de"
88
-
89
- #: safe-redirect-manager.php:439
90
- msgid "Date"
91
- msgstr "Date"
92
-
93
- #: safe-redirect-manager.php:491
94
- msgctxt "post type general name"
95
- msgid "Safe Redirect Manager"
96
- msgstr "Safe Redirect Manager"
97
-
98
- #: safe-redirect-manager.php:492
99
- msgctxt "post type singular name"
100
- msgid "Redirect"
101
- msgstr "Redirection"
102
-
103
- #. Plugin Name of the plugin/theme
104
- #: safe-redirect-manager.php:494 safe-redirect-manager.php:497
105
- #: safe-redirect-manager.php:503
106
- msgid "Safe Redirect Manager"
107
- msgstr "Redirections"
108
-
109
- #: safe-redirect-manager.php:495
110
- msgid "Edit Redirect Rule"
111
- msgstr "Modifier la règle de redirection"
112
-
113
- #: safe-redirect-manager.php:496
114
- msgid "New Redirect Rule"
115
- msgstr "Nouvelle règle de redirection"
116
-
117
- #: safe-redirect-manager.php:498
118
- msgid "View Redirect Rule"
119
- msgstr "Voir la règle de redirection"
120
-
121
- #: safe-redirect-manager.php:499
122
- msgid "Search Redirects"
123
- msgstr "Rechercher une redirection"
124
-
125
- #: safe-redirect-manager.php:500
126
- msgid "No redirect rules found."
127
- msgstr "Aucune règle de redirection trouvée."
128
-
129
- #: safe-redirect-manager.php:501
130
- msgid "No redirect rules found in trash."
131
- msgstr "Aucune règle de redirection trouvée dans la corbeille."
132
-
133
- #: safe-redirect-manager.php:544
134
- msgid "Redirect Settings"
135
- msgstr "Paramètres de redirection"
136
-
137
- #: safe-redirect-manager.php:565
138
- msgid "Redirect From:"
139
- msgstr "Redirection à partir de:"
140
-
141
- #: safe-redirect-manager.php:567
142
- msgid ""
143
- "This path should be relative to the root of this WordPress installation (or "
144
- "the sub-site, if you are running a multi-site). Appending a (*) wildcard "
145
- "character will match all requests with the base."
146
- msgstr ""
147
- "Le chemin doit être relatif à la racine de votre installation WordPress (ou "
148
- "du sous-site, si vous utilisez une installation multi-site). Ajouter le "
149
- "caractère de remplacement (*) permet d'accepter toutes les requêtes à partir "
150
- "de la base."
151
-
152
- #: safe-redirect-manager.php:571
153
- msgid "Redirect To:"
154
- msgstr "Redirection vers:"
155
-
156
- #: safe-redirect-manager.php:573
157
- msgid ""
158
- "This can be a URL or a path relative to the root of your website (not your "
159
- "WordPress installation). Ending with a (*) wildcard character will append "
160
- "the request match to the redirect."
161
- msgstr ""
162
- "Ceci peut être une URL ou un chemin relatif à la racine du site web (et non "
163
- "pas votre installation WordPress). Terminer avec le caractère de "
164
- "remplacement (*) vous permettra d'ajouter la requête à la redirection."
165
-
166
- #: safe-redirect-manager.php:577
167
- msgid "HTTP Status Code:"
168
- msgstr "Code de statut HTTP:"
169
-
170
- #: safe-redirect-manager.php:583
171
- msgid "If you don't know what this is, leave it as is."
172
- msgstr "Si vous ne savez pas ce que c'est, ne changez pas la valeur."
173
-
174
- #. Plugin URI of the plugin/theme
175
- #. Author URI of the plugin/theme
176
- msgid "http://www.10up.com"
177
- msgstr "http://www.10up.com"
178
-
179
- #. Description of the plugin/theme
180
- msgid "Easily and safely manage HTTP redirects."
181
- msgstr "Gérez simplement et sans risque les redirections HTTP."
182
-
183
- #. Author of the plugin/theme
184
- msgid "Taylor Lovett (10up LLC), VentureBeat"
185
- msgstr "Taylor Lovett (10up LLC), VentureBeat"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
languages/safe-redirect-manager.pot DELETED
@@ -1,173 +0,0 @@
1
- # Copyright (C) 2012 Safe Redirect Manager
2
- # This file is distributed under the same license as the Safe Redirect Manager package.
3
- msgid ""
4
- msgstr ""
5
- "Project-Id-Version: Safe Redirect Manager 1.4-working\n"
6
- "Report-Msgid-Bugs-To: http://wordpress.org/tag/safe-redirect-manager\n"
7
- "POT-Creation-Date: 2012-10-09 23:02:23+00:00\n"
8
- "MIME-Version: 1.0\n"
9
- "Content-Type: text/plain; charset=UTF-8\n"
10
- "Content-Transfer-Encoding: 8bit\n"
11
- "PO-Revision-Date: 2012-MO-DA HO:MI+ZONE\n"
12
- "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
- "Language-Team: LANGUAGE <LL@li.org>\n"
14
-
15
- #: safe-redirect-manager.php:259
16
- msgid ""
17
- "Safe Redirect Manager Warning: Possible redirect loops and/or chains have "
18
- "been created."
19
- msgstr ""
20
-
21
- #: safe-redirect-manager.php:266
22
- msgid ""
23
- "Safe Redirect Manager Error: You have reached the maximum allowable number "
24
- "of redirects"
25
- msgstr ""
26
-
27
- #: safe-redirect-manager.php:367 safe-redirect-manager.php:370
28
- msgid "Redirect rule updated."
29
- msgstr ""
30
-
31
- #: safe-redirect-manager.php:368
32
- msgid "Custom field updated."
33
- msgstr ""
34
-
35
- #: safe-redirect-manager.php:369
36
- msgid "Custom field deleted."
37
- msgstr ""
38
-
39
- #. translators: %s: date and time of the revision
40
- #: safe-redirect-manager.php:372
41
- msgid "Redirect rule restored to revision from %s"
42
- msgstr ""
43
-
44
- #: safe-redirect-manager.php:373
45
- msgid "Redirect rule published."
46
- msgstr ""
47
-
48
- #: safe-redirect-manager.php:374
49
- msgid "Redirect rule saved."
50
- msgstr ""
51
-
52
- #: safe-redirect-manager.php:375
53
- msgid "Redirect rule submitted."
54
- msgstr ""
55
-
56
- #: safe-redirect-manager.php:376
57
- msgid "Redirect rule scheduled for: <strong>%1$s</strong>."
58
- msgstr ""
59
-
60
- #. translators: Publish box date format, see http:php.net/date
61
- #: safe-redirect-manager.php:378
62
- msgid "M j, Y @ G:i"
63
- msgstr ""
64
-
65
- #: safe-redirect-manager.php:379
66
- msgid "Redirect rule draft updated."
67
- msgstr ""
68
-
69
- #: safe-redirect-manager.php:431
70
- msgid "Redirect To"
71
- msgstr ""
72
-
73
- #: safe-redirect-manager.php:432
74
- msgid "HTTP Status Code"
75
- msgstr ""
76
-
77
- #: safe-redirect-manager.php:435
78
- msgid "Redirect From"
79
- msgstr ""
80
-
81
- #: safe-redirect-manager.php:439
82
- msgid "Date"
83
- msgstr ""
84
-
85
- #: safe-redirect-manager.php:491
86
- msgctxt "post type general name"
87
- msgid "Safe Redirect Manager"
88
- msgstr ""
89
-
90
- #: safe-redirect-manager.php:492
91
- msgctxt "post type singular name"
92
- msgid "Redirect"
93
- msgstr ""
94
-
95
- #. #-#-#-#-# plugin.pot (Safe Redirect Manager 1.4-working) #-#-#-#-#
96
- #. Plugin Name of the plugin/theme
97
- #: safe-redirect-manager.php:494 safe-redirect-manager.php:497
98
- #: safe-redirect-manager.php:503
99
- msgid "Safe Redirect Manager"
100
- msgstr ""
101
-
102
- #: safe-redirect-manager.php:495
103
- msgid "Edit Redirect Rule"
104
- msgstr ""
105
-
106
- #: safe-redirect-manager.php:496
107
- msgid "New Redirect Rule"
108
- msgstr ""
109
-
110
- #: safe-redirect-manager.php:498
111
- msgid "View Redirect Rule"
112
- msgstr ""
113
-
114
- #: safe-redirect-manager.php:499
115
- msgid "Search Redirects"
116
- msgstr ""
117
-
118
- #: safe-redirect-manager.php:500
119
- msgid "No redirect rules found."
120
- msgstr ""
121
-
122
- #: safe-redirect-manager.php:501
123
- msgid "No redirect rules found in trash."
124
- msgstr ""
125
-
126
- #: safe-redirect-manager.php:544
127
- msgid "Redirect Settings"
128
- msgstr ""
129
-
130
- #: safe-redirect-manager.php:565
131
- msgid "Redirect From:"
132
- msgstr ""
133
-
134
- #: safe-redirect-manager.php:567
135
- msgid ""
136
- "This path should be relative to the root of this WordPress installation (or "
137
- "the sub-site, if you are running a multi-site). Appending a (*) wildcard "
138
- "character will match all requests with the base."
139
- msgstr ""
140
-
141
- #: safe-redirect-manager.php:571
142
- msgid "Redirect To:"
143
- msgstr ""
144
-
145
- #: safe-redirect-manager.php:573
146
- msgid ""
147
- "This can be a URL or a path relative to the root of your website (not your "
148
- "WordPress installation). Ending with a (*) wildcard character will append "
149
- "the request match to the redirect."
150
- msgstr ""
151
-
152
- #: safe-redirect-manager.php:577
153
- msgid "HTTP Status Code:"
154
- msgstr ""
155
-
156
- #: safe-redirect-manager.php:583
157
- msgid "If you don't know what this is, leave it as is."
158
- msgstr ""
159
-
160
- #. #-#-#-#-# plugin.pot (Safe Redirect Manager 1.4-working) #-#-#-#-#
161
- #. Plugin URI of the plugin/theme
162
- #. #-#-#-#-# plugin.pot (Safe Redirect Manager 1.4-working) #-#-#-#-#
163
- #. Author URI of the plugin/theme
164
- msgid "http://www.10up.com"
165
- msgstr ""
166
-
167
- #. Description of the plugin/theme
168
- msgid "Easily and safely manage HTTP redirects."
169
- msgstr ""
170
-
171
- #. Author of the plugin/theme
172
- msgid "Taylor Lovett (10up LLC), VentureBeat"
173
- msgstr ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
phpunit.xml DELETED
@@ -1,14 +0,0 @@
1
- <phpunit
2
- bootstrap="tests/bootstrap.php"
3
- backupGlobals="false"
4
- colors="true"
5
- convertErrorsToExceptions="true"
6
- convertNoticesToExceptions="true"
7
- convertWarningsToExceptions="true"
8
- >
9
- <testsuites>
10
- <testsuite>
11
- <directory prefix="test-" suffix=".php">./tests/</directory>
12
- </testsuite>
13
- </testsuites>
14
- </phpunit>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -1,29 +1,31 @@
1
  === Safe Redirect Manager ===
2
  Contributors: tlovett1, tollmanz, taylorde, 10up, jakemgold, danielbachhuber, VentureBeat
3
- Tags: http redirects, redirect manager, url redirection, safe http redirection, multisite redirects
4
  Requires at least: 3.1
5
- Tested up to: 4.8
6
- Stable tag: 1.7.8
7
 
8
  Safely and easily manage your website's HTTP redirects.
9
 
10
  == Description ==
11
 
12
- Safe Redirect Manager is a HTTP redirect manager for WordPress. An easy-to-use UI allows you to redirect locations to new URL's with the HTTP status codes of your choosing. The plugin uses the wp_safe_redirect function which only allows redirects to whitelisted hosts for security purposes. The plugin automatically handles whitelisting hosts for you. This plugin works great with Multisite.
13
 
14
- [Fork the plugin on GitHub.](https://github.com/tlovett1/Safe-Redirect-Manager)
15
 
16
  == Installation ==
17
 
18
  Extract the zip file and just drop the contents in the wp-content/plugins/ directory of your WordPress installation and then activate the Plugin from Plugins page.
19
 
20
- == Screenshots ==
21
-
22
- 1. This view shows all your redirects. You can filter them by date or even search through them.
23
- 2. This is the edit redirect page. Specify a "from" path, "to" path/URL, and a status code. You can schedule redirects for later dates just like posts.
24
-
25
  == Changelog ==
26
 
 
 
 
 
 
 
 
27
  = 1.7.8 (Dec. 16, 2015) =
28
  * Fix SQL injection bug and no search terms warning
29
 
1
  === Safe Redirect Manager ===
2
  Contributors: tlovett1, tollmanz, taylorde, 10up, jakemgold, danielbachhuber, VentureBeat
3
+ Tags: http redirects, redirect manager, url redirection, safe http redirection, multisite redirects, redirects
4
  Requires at least: 3.1
5
+ Tested up to: 5.0
6
+ Stable tag: trunk
7
 
8
  Safely and easily manage your website's HTTP redirects.
9
 
10
  == Description ==
11
 
12
+ Safe Redirect Manager is a HTTP redirect manager for WordPress. An easy-to-use UI allows you to redirect locations to new URL's with the HTTP status codes of your choosing. This plugin works great with multisite.
13
 
14
+ [Fork the plugin on GitHub.](https://github.com/tlovett1/safe-redirect-manager)
15
 
16
  == Installation ==
17
 
18
  Extract the zip file and just drop the contents in the wp-content/plugins/ directory of your WordPress installation and then activate the Plugin from Plugins page.
19
 
 
 
 
 
 
20
  == Changelog ==
21
 
22
+ = 1.8 =
23
+ * Improved escaping
24
+ * Custom redirect capability
25
+ * Code refactor
26
+ * Fix root redirect in sub directory bug
27
+ * Fix broken html
28
+
29
  = 1.7.8 (Dec. 16, 2015) =
30
  * Fix SQL injection bug and no search terms warning
31
 
safe-redirect-manager.php CHANGED
@@ -1,1069 +1,32 @@
1
  <?php
2
- /*
3
- Plugin Name: Safe Redirect Manager
4
- Plugin URI: http://www.10up.com
5
- Description: Easily and safely manage HTTP redirects.
6
- Author: Taylor Lovett (10up)
7
- Version: 1.7.8
8
- Author URI: http://www.10up.com
9
-
10
- GNU General Public License, Free Software Foundation <http://creativecommons.org/licenses/GPL/2.0/>
11
-
12
- This program is free software; you can redistribute it and/or modify
13
- it under the terms of the GNU General Public License as published by
14
- the Free Software Foundation; either version 2 of the License, or
15
- (at your option) any later version.
16
-
17
- This program is distributed in the hope that it will be useful,
18
- but WITHOUT ANY WARRANTY; without even the implied warranty of
19
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
- GNU General Public License for more details.
21
-
22
- You should have received a copy of the GNU General Public License
23
- along with this program; if not, write to the Free Software
24
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25
-
26
- */
27
-
28
- if ( defined( 'WP_CLI' ) && WP_CLI )
29
- require_once dirname( __FILE__ ) . '/inc/wp-cli.php';
30
-
31
- class SRM_Safe_Redirect_Manager {
32
-
33
- public $redirect_post_type = 'redirect_rule';
34
- private $redirect_nonce_name = 'srm_redirect_nonce';
35
- private $redirect_nonce_action = 'srm-save-redirect-meta';
36
-
37
- public $meta_key_redirect_from = '_redirect_rule_from';
38
- public $meta_key_redirect_to = '_redirect_rule_to';
39
- public $meta_key_redirect_status_code = '_redirect_rule_status_code';
40
- public $meta_key_enable_redirect_from_regex = '_redirect_rule_from_regex';
41
-
42
- public $cache_key_redirects = '_srm_redirects';
43
-
44
- public $valid_status_codes = array( 301, 302, 303, 307, 403, 404 );
45
-
46
- public $status_code_labels = array(
47
- 301 => 'Moved Permanently',
48
- 302 => 'Found',
49
- 303 => 'See Other',
50
- 307 => 'Temporary Redirect',
51
- 403 => 'Forbidden',
52
- 404 => 'Not Found',
53
- );
54
-
55
- private $whitelist_hosts = array();
56
-
57
- public $default_max_redirects = 150;
58
-
59
- /**
60
- * Sets up redirect manager
61
- *
62
- * @since 1.0
63
- * @uses add_action, add_filter
64
- * @return object
65
- */
66
- public function __construct() {
67
- add_action( 'init', array( $this, 'action_init_load_textdomain' ), 9 );
68
- add_action( 'init', array( $this, 'action_init' ) );
69
- add_action( 'init', array( $this, 'action_register_post_types' ) );
70
- add_action( 'parse_request', array( $this, 'action_parse_request' ), 0 );
71
- add_action( 'save_post', array( $this, 'action_save_post' ) );
72
- add_filter( 'manage_' . $this->redirect_post_type . '_posts_columns' , array( $this, 'filter_redirect_columns' ) );
73
- add_action( 'manage_' . $this->redirect_post_type . '_posts_custom_column' , array( $this, 'action_custom_redirect_columns' ), 10, 2 );
74
- add_action( 'transition_post_status', array( $this, 'action_transition_post_status' ), 10, 3 );
75
- add_filter( 'post_updated_messages', array( $this, 'filter_redirect_updated_messages' ) );
76
- add_action( 'admin_notices', array( $this, 'action_redirect_chain_alert' ) );
77
- add_filter( 'the_title', array( $this, 'filter_admin_title' ), 100, 2 );
78
- add_filter( 'bulk_actions-edit-' . $this->redirect_post_type, array( $this, 'filter_bulk_actions' ) );
79
- add_action( 'admin_print_styles-edit.php', array( $this, 'action_print_logo_css' ), 10, 1 );
80
- add_action( 'admin_print_styles-post.php', array( $this, 'action_print_logo_css' ), 10, 1 );
81
- add_action( 'admin_print_styles-post-new.php', array( $this, 'action_print_logo_css' ), 10, 1 );
82
- add_filter( 'post_type_link', array( $this, 'filter_post_type_link' ), 10, 2 );
83
-
84
- // Search filters
85
- add_filter( 'posts_join', array( $this, 'filter_search_join' ) );
86
- add_filter( 'posts_where', array( $this, 'filter_search_where' ) );
87
- add_filter( 'posts_distinct', array( $this, 'filter_search_distinct' ) );
88
- }
89
-
90
- /**
91
- * Localize plugin
92
- *
93
- * @since 1.7
94
- * @uses load_plugin_textdomain
95
- * @return void
96
- */
97
- public function action_init_load_textdomain() {
98
- load_plugin_textdomain( 'safe-redirect-manager', false, basename( dirname( __FILE__ ) ) . '/languages' );
99
- }
100
-
101
- /**
102
- * Join posts table with postmeta table on search
103
- *
104
- * @since 1.2
105
- * @param string $join
106
- * @uses get_query_var
107
- * @return string
108
- */
109
- public function filter_search_join( $join ) {
110
- global $wp_query;
111
-
112
- if ( empty( $wp_query ) || $this->redirect_post_type != get_query_var( 'post_type' ) )
113
- return $join;
114
-
115
- global $wpdb;
116
-
117
- $s = get_query_var( 's' );
118
- if ( ! empty( $s ) ) {
119
- $join .= " LEFT JOIN $wpdb->postmeta AS m ON ($wpdb->posts.ID = m.post_id) ";
120
- }
121
- return $join;
122
- }
123
-
124
- /**
125
- * Return distinct search results
126
- *
127
- * @since 1.2
128
- * @param string $distinct
129
- * @uses get_query_var
130
- * @return string
131
- */
132
- public function filter_search_distinct( $distinct ) {
133
- global $wp_query;
134
-
135
- if ( empty( $wp_query ) || $this->redirect_post_type != get_query_var( 'post_type' ) )
136
- return $distinct;
137
-
138
- return 'DISTINCT';
139
- }
140
-
141
- /**
142
- * Join posts table with postmeta table on search
143
- *
144
- * @since 1.2
145
- * @param string $where
146
- * @uses is_search, get_query_var
147
- * @return string
148
- */
149
- public function filter_search_where( $where ) {
150
- global $wp_query;
151
-
152
- if ( empty( $wp_query ) || $this->redirect_post_type != get_query_var( 'post_type' ) || ! is_search() || empty( $where ) )
153
- return $where;
154
-
155
- $terms = $this->get_search_terms();
156
-
157
- if ( empty( $terms ) ) {
158
- return $where;
159
- }
160
-
161
- $exact = get_query_var( 'exact' );
162
- $n = ( ! empty( $exact ) ) ? '' : '%';
163
-
164
- $search = '';
165
- $seperator = '';
166
- $search .= '(';
167
-
168
- // we check the meta values against each term in the search
169
- foreach ( $terms as $term ) {
170
- $search .= $seperator;
171
- // Used esc_sql instead of wpdb->prepare since wpdb->prepare wraps things in quotes
172
- $search .= sprintf( "( ( m.meta_value LIKE '%s%s%s' AND m.meta_key = '%s') OR ( m.meta_value LIKE '%s%s%s' AND m.meta_key = '%s') )", $n, esc_sql( $term ), $n, esc_sql( $this->meta_key_redirect_from ), $n, esc_sql( $term ), $n, esc_sql( $this->meta_key_redirect_to ) );
173
-
174
- $seperator = ' OR ';
175
- }
176
-
177
- $search .= ')';
178
-
179
- $where = preg_replace( '/\(\(\(.*?\)\)\)/is', '((' . $search . '))', $where );
180
-
181
- return $where;
182
- }
183
-
184
- /**
185
- * Get an array of search terms
186
- *
187
- * @since 1.2
188
- * @uses get_query_var
189
- * @return array
190
- */
191
- private function get_search_terms() {
192
- $s = get_query_var( 's' );
193
-
194
- if ( ! empty( $s ) ) {
195
- preg_match_all( '/".*?("|$)|((?<=[\\s",+])|^)[^\\s",+]+/', stripslashes( $s ), $matches );
196
- $search_terms = array_map( create_function( '$a', 'return trim( $a, "\\"\'\\n\\r " );' ), $matches[0] );
197
- }
198
- return $search_terms;
199
- }
200
-
201
- /**
202
- * Swap tools logo for plugin logo
203
- *
204
- * @since 1.1
205
- * @uses plugins_url
206
- * @return void
207
- */
208
- public function action_print_logo_css() {
209
- if ( $this->is_plugin_page() ) {
210
- ?>
211
- <style type="text/css">
212
- #visibility, .view-switch, .posts .inline-edit-col-left .inline-edit-group {
213
- display: none;
214
- }
215
- #srm<?php echo $this->meta_key_redirect_from; ?> {
216
- width: 60%;
217
- }
218
- </style>
219
- <?php
220
- }
221
- }
222
-
223
- /**
224
- * Limit the bulk actions available in the Manage Redirects view
225
- *
226
- * @since 1.0
227
- * @return array
228
- */
229
- public function filter_bulk_actions( $actions ) {
230
-
231
- // No bulk editing at this time
232
- unset( $actions['edit'] );
233
-
234
- return $actions;
235
- }
236
-
237
- /**
238
- * Creates a redirect post, this function will be useful for import/exports scripts
239
- *
240
- * @param string $redirect_from
241
- * @param string $redirect_to
242
- * @param int $status_code
243
- * @param bool $enable_regex
244
- * @param string $post_status
245
- * @since 1.3
246
- * @uses wp_insert_post, update_post_meta
247
- * @return int|WP_Error
248
- */
249
- public function create_redirect( $redirect_from, $redirect_to, $status_code = 302, $enable_regex = false, $post_status = 'publish' ) {
250
- global $wpdb;
251
-
252
- $sanitized_redirect_from = $this->sanitize_redirect_from( $redirect_from );
253
- $sanitized_redirect_to = $this->sanitize_redirect_to( $redirect_to );
254
- $sanitized_status_code = absint( $status_code );
255
- $sanitized_enable_regex = (bool)$enable_regex;
256
- $sanitized_post_status = sanitize_key( $post_status );
257
-
258
- // check and make sure no parameters are empty or invalid after sanitation
259
- if ( empty( $sanitized_redirect_from ) || empty( $sanitized_redirect_to ) )
260
- return new WP_Error( 'invalid-argument', __( 'Redirect from and/or redirect to arguments are invalid.', 'safe-redirect-manager' ) );
261
-
262
- if ( ! in_array( $sanitized_status_code, $this->valid_status_codes ) )
263
- return new WP_Error( 'invalid-argument', __( 'Invalid status code.', 'safe-redirect-manager' ) );
264
-
265
- // Check to ensure this redirect doesn't already exist
266
- if ( $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key=%s AND meta_value=%s", $this->meta_key_redirect_from, $sanitized_redirect_from ) ) )
267
- return new WP_Error( 'duplicate-redirect', sprintf( __( 'Redirect already exists for %s', 'safe-redirect-manager' ), $sanitized_redirect_from ) );
268
-
269
- // create the post
270
- $post_args = array(
271
- 'post_type' => $this->redirect_post_type,
272
- 'post_status' => $sanitized_post_status,
273
- 'post_author' => 1
274
- );
275
-
276
- $post_id = wp_insert_post( $post_args );
277
-
278
- if ( 0 >= $post_id )
279
- return new WP_Error( 'error-creating', __( 'An error occurred creating the redirect.', 'safe-redirect-manager' ) );
280
-
281
- // update the posts meta info
282
- update_post_meta( $post_id, $this->meta_key_redirect_from, $sanitized_redirect_from );
283
- update_post_meta( $post_id, $this->meta_key_redirect_to, $sanitized_redirect_to );
284
- update_post_meta( $post_id, $this->meta_key_redirect_status_code, $sanitized_status_code );
285
- update_post_meta( $post_id, $this->meta_key_enable_redirect_from_regex, $sanitized_enable_regex );
286
-
287
- // We need to update the cache after creating this redirect
288
- $this->update_redirect_cache();
289
-
290
- return $post_id;
291
- }
292
-
293
- /**
294
- * Whether or not this is an admin page specific to the plugin
295
- *
296
- * @since 1.1
297
- * @uses get_post_type
298
- * @return bool
299
- */
300
- private function is_plugin_page() {
301
- return (bool) ( get_post_type() == $this->redirect_post_type || ( isset( $_GET['post_type'] ) && $this->redirect_post_type == $_GET['post_type'] ) );
302
- }
303
-
304
- /**
305
- * Echoes admin message if redirect chains exist
306
- *
307
- * @since 1.0
308
- * @uses apply_filters
309
- * @return void
310
- */
311
- public function action_redirect_chain_alert() {
312
- global $hook_suffix;
313
- if ( $this->is_plugin_page() ) {
314
-
315
- /**
316
- * check_for_possible_redirect_loops() runs in best case Theta(n^2) so if you have 100 redirects, this method
317
- * will be running slow. Let's disable it by default.
318
- */
319
- if ( apply_filters( 'srm_check_for_possible_redirect_loops', false ) ) {
320
- if ( $this->check_for_possible_redirect_loops() ) {
321
- ?>
322
- <div class="updated">
323
- <p><?php _e( 'Safe Redirect Manager Warning: Possible redirect loops and/or chains have been created.', 'safe-redirect-manager' ); ?></p>
324
- </div>
325
- <?php
326
- }
327
- } if ( $this->max_redirects_reached() ) {
328
- ?>
329
- <?php if ( 'post-new.php' == $hook_suffix ) : ?><style type="text/css">#post { display: none; }</style><?php endif; ?>
330
- <div class="error">
331
- <p><?php _e( 'Safe Redirect Manager Error: You have reached the maximum allowable number of redirects', 'safe-redirect-manager' ); ?></p>
332
- </div>
333
- <?php
334
- }
335
- }
336
- }
337
-
338
- /**
339
- * Returns true if max redirects have been reached
340
- *
341
- * @since 1.0
342
- * @uses apply_filters, get_transient
343
- * @return bool
344
- */
345
- public function max_redirects_reached() {
346
- if ( false === ( $redirects = get_transient( $this->cache_key_redirects ) ) ) {
347
- $redirects = $this->update_redirect_cache();
348
- }
349
-
350
- return ( count( $redirects ) >= $this->default_max_redirects );
351
- }
352
-
353
- /**
354
- * Check for potential redirect loops or chains
355
- *
356
- * @since 1.0
357
- * @uses home_url, get_transient
358
- * @return boolean
359
- */
360
- public function check_for_possible_redirect_loops() {
361
- if ( false === ( $redirects = get_transient( $this->cache_key_redirects ) ) ) {
362
- $redirects = $this->update_redirect_cache();
363
- }
364
-
365
- $current_url = parse_url( home_url() );
366
- $this_host = ( is_array( $current_url ) && ! empty( $current_url['host'] ) ) ? $current_url['host'] : '';
367
-
368
- foreach ( $redirects as $redirect ) {
369
- $redirect_from = $redirect['redirect_from'];
370
-
371
- // check redirect from against all redirect to's
372
- foreach ( $redirects as $compare_redirect ) {
373
- $redirect_to = $compare_redirect['redirect_to'];
374
-
375
- $redirect_url = parse_url( $redirect_to );
376
- $redirect_host = ( is_array( $redirect_url ) && ! empty( $redirect_url['host'] ) ) ? $redirect_url['host'] : '';
377
-
378
- // check if we are redirecting locally
379
- if ( empty( $redirect_host ) || $redirect_host == $this_host ) {
380
- $redirect_from_url = preg_replace( '/(http:\/\/|https:\/\/|www\.)/i', '', home_url() . $redirect_from );
381
- $redirect_to_url = $redirect_to;
382
- if ( ! preg_match( '/https?:\/\//i', $redirect_to_url ) )
383
- $redirect_to_url = $this_host . $redirect_to_url;
384
- else
385
- $redirect_to_url = preg_replace( '/(http:\/\/|https:\/\/|www\.)/i', '', $redirect_to_url );
386
-
387
- // possible loop/chain found
388
- if ( $redirect_to_url == $redirect_from_url )
389
- return true;
390
- }
391
- }
392
- }
393
-
394
- return false;
395
- }
396
-
397
- /**
398
- * Filters title out for redirect from in post manager
399
- *
400
- * @since 1.0
401
- * @param string $title
402
- * @param int $post_id
403
- * @uses is_admin, get_post_meta
404
- * @return string
405
- */
406
- public function filter_admin_title( $title, $post_id = 0 ) {
407
- if ( ! is_admin() )
408
- return $title;
409
-
410
- $redirect = get_post( $post_id );
411
- if ( empty( $redirect ) )
412
- return $title;
413
-
414
- if ( $redirect->post_type != $this->redirect_post_type )
415
- return $title;
416
-
417
- $redirect_from = get_post_meta( $post_id, $this->meta_key_redirect_from, true );
418
- if ( ! empty( $redirect_from ) )
419
- return $redirect_from;
420
-
421
- return $title;
422
- }
423
-
424
- /**
425
- * Customizes updated messages for redirects
426
- *
427
- * @since 1.0
428
- * @param array $messages
429
- * @uses esc_url, get_permalink, add_query_var, wp_post_revision_title
430
- * @return array
431
- */
432
- public function filter_redirect_updated_messages( $messages ) {
433
- global $post, $post_ID;
434
-
435
- $messages[$this->redirect_post_type] = array(
436
- 0 => '', // Unused. Messages start at index 1.
437
- 1 => sprintf( __( 'Redirect rule updated.', 'safe-redirect-manager' ), esc_url( get_permalink( $post_ID ) ) ),
438
- 2 => __( 'Custom field updated.', 'safe-redirect-manager' ),
439
- 3 => __( 'Custom field deleted.', 'safe-redirect-manager' ),
440
- 4 => __( 'Redirect rule updated.', 'safe-redirect-manager' ),
441
- /* translators: %s: date and time of the revision */
442
- 5 => isset( $_GET['revision'] ) ? sprintf( __('Redirect rule restored to revision from %s', 'safe-redirect-manager' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
443
- 6 => sprintf( __( 'Redirect rule published.', 'safe-redirect-manager' ), esc_url( get_permalink( $post_ID ) ) ),
444
- 7 => __( 'Redirect rule saved.', 'safe-redirect-manager' ),
445
- 8 => sprintf( __( 'Redirect rule submitted.', 'safe-redirect-manager' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post_ID ) ) ) ),
446
- 9 => sprintf( __( 'Redirect rule scheduled for: <strong>%1$s</strong>.', 'safe-redirect-manager' ),
447
- // translators: Publish box date format, see http://php.net/date
448
- date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
449
- 10 => sprintf( __( 'Redirect rule draft updated.', 'safe-redirect-manager' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post_ID ) ) ) ),
450
- );
451
-
452
- return $messages;
453
- }
454
-
455
- /**
456
- * Clear redirect cache if appropriate post type is transitioned
457
- *
458
- * @since 1.0
459
- * @param string $new_status
460
- * @param string $old_status
461
- * @param object $post
462
- * @uses delete_transient
463
- * @return void
464
- */
465
- public function action_transition_post_status( $new_status, $old_status, $post ) {
466
- if ( ! is_object( $post ) )
467
- return;
468
-
469
- // recreate redirect cache
470
- if ( $this->redirect_post_type == $post->post_type ) {
471
- delete_transient( $this->cache_key_redirects );
472
- $this->update_redirect_cache();
473
- }
474
- }
475
-
476
- /**
477
- * Displays custom columns on redirect manager screen
478
- *
479
- * @since 1.0
480
- * @param string $column
481
- * @param int $post_id
482
- * @uses get_post_meta, esc_html, admin_url
483
- * @return void
484
- */
485
- public function action_custom_redirect_columns( $column, $post_id ) {
486
- if ( 'srm' . $this->meta_key_redirect_to == $column ) {
487
- echo esc_html( get_post_meta( $post_id, $this->meta_key_redirect_to, true ) );
488
- } elseif ( 'srm' . $this->meta_key_redirect_status_code == $column ) {
489
- echo absint( get_post_meta( $post_id, $this->meta_key_redirect_status_code, true ) );
490
- }
491
- }
492
-
493
- /**
494
- * Add new columns to manage redirect screen
495
- *
496
- * @since 1.0
497
- * @param array $columns
498
- * @return array
499
- */
500
- public function filter_redirect_columns( $columns ) {
501
- $columns['srm' . $this->meta_key_redirect_to] = __( 'Redirect To', 'safe-redirect-manager' );
502
- $columns['srm'. $this->meta_key_redirect_status_code] = __( 'HTTP Status Code', 'safe-redirect-manager' );
503
-
504
- // Change the title column
505
- $columns['title'] = __( 'Redirect From', 'safe-redirect-manager' );
506
-
507
- // Move date column to the back
508
- unset( $columns['date'] );
509
- $columns['date'] = __( 'Date', 'safe-redirect-manager' );
510
-
511
- return $columns;
512
- }
513
-
514
- /**
515
- * Saves meta info for redirect rules
516
- *
517
- * @since 1.0
518
- * @param int $post_id
519
- * @uses current_user_can, get_post_type, wp_verify_nonce, update_post_meta, delete_post_meta
520
- * @return void
521
- */
522
- public function action_save_post( $post_id ) {
523
- if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ! current_user_can( 'edit_post', $post_id ) || 'revision' == get_post_type( $post_id ) )
524
- return;
525
-
526
- // Update post meta for redirect rules
527
- if ( ! empty( $_POST[$this->redirect_nonce_name] ) && wp_verify_nonce( $_POST[$this->redirect_nonce_name], $this->redirect_nonce_action ) ) {
528
-
529
- if ( ! empty( $_POST['srm' . $this->meta_key_enable_redirect_from_regex] ) ) {
530
- $allow_regex = (bool) $_POST['srm' . $this->meta_key_enable_redirect_from_regex];
531
- update_post_meta( $post_id, $this->meta_key_enable_redirect_from_regex, $allow_regex );
532
- } else {
533
- $allow_regex = false;
534
- delete_post_meta( $post_id, $this->meta_key_enable_redirect_from_regex );
535
- }
536
-
537
- if ( ! empty( $_POST['srm' . $this->meta_key_redirect_from] ) ) {
538
- update_post_meta( $post_id, $this->meta_key_redirect_from, $this->sanitize_redirect_from( $_POST['srm' . $this->meta_key_redirect_from], $allow_regex ) );
539
- } else {
540
- delete_post_meta( $post_id, $this->meta_key_redirect_from );
541
- }
542
-
543
- if ( ! empty( $_POST['srm' . $this->meta_key_redirect_to] ) ) {
544
- update_post_meta( $post_id, $this->meta_key_redirect_to, $this->sanitize_redirect_to( $_POST['srm' . $this->meta_key_redirect_to] ) );
545
- } else {
546
- delete_post_meta( $post_id, $this->meta_key_redirect_to );
547
- }
548
-
549
- if ( ! empty( $_POST['srm' . $this->meta_key_redirect_status_code] ) ) {
550
- update_post_meta( $post_id, $this->meta_key_redirect_status_code, absint( $_POST['srm' . $this->meta_key_redirect_status_code] ) );
551
- } else {
552
- delete_post_meta( $post_id, $this->meta_key_redirect_status_code );
553
- }
554
-
555
- /**
556
- * This fixes an important bug where the redirect cache was not up-to-date. Previously the cache was only being
557
- * updated on transition_post_status which gets called BEFORE save post. But since save_post is where all the important
558
- * redirect info is saved, updating the cache before it is not sufficient.
559
- */
560
- $this->update_redirect_cache();
561
- }
562
- }
563
-
564
- /**
565
- * Registers post types for plugin
566
- *
567
- * @since 1.0
568
- * @uses register_post_type, _x, plugins_url, apply_filters
569
- * @return void
570
- */
571
- public function action_register_post_types() {
572
- $redirect_labels = array(
573
- 'name' => _x( 'Safe Redirect Manager', 'post type general name', 'safe-redirect-manager' ),
574
- 'singular_name' => _x( 'Redirect', 'post type singular name', 'safe-redirect-manager' ),
575
- 'add_new' => _x( 'Create Redirect Rule', $this->redirect_post_type, 'safe-redirect-manager' ),
576
- 'add_new_item' => __( 'Safe Redirect Manager', 'safe-redirect-manager' ),
577
- 'edit_item' => __( 'Edit Redirect Rule', 'safe-redirect-manager' ),
578
- 'new_item' => __( 'New Redirect Rule', 'safe-redirect-manager' ),
579
- 'all_items' => __( 'Safe Redirect Manager', 'safe-redirect-manager' ),
580
- 'view_item' => __( 'View Redirect Rule', 'safe-redirect-manager' ),
581
- 'search_items' => __( 'Search Redirects', 'safe-redirect-manager' ),
582
- 'not_found' => __( 'No redirect rules found.', 'safe-redirect-manager' ),
583
- 'not_found_in_trash' => __( 'No redirect rules found in trash.', 'safe-redirect-manager' ),
584
- 'parent_item_colon' => '',
585
- 'menu_name' => __( 'Safe Redirect Manager', 'safe-redirect-manager' )
586
- );
587
- $redirect_capability = 'manage_options';
588
- $redirect_capability = apply_filters( 'srm_restrict_to_capability', $redirect_capability );
589
- $capabilities = array(
590
- 'edit_post' => $redirect_capability,
591
- 'read_post' => $redirect_capability,
592
- 'delete_post' => $redirect_capability,
593
- 'delete_posts' => $redirect_capability,
594
- 'edit_posts' => $redirect_capability,
595
- 'edit_others_posts' => $redirect_capability,
596
- 'publish_posts' => $redirect_capability,
597
- 'read_private_posts' => $redirect_capability
598
- );
599
-
600
- $redirect_args = array(
601
- 'labels' => $redirect_labels,
602
- 'public' => false,
603
- 'publicly_queryable' => true,
604
- 'show_ui' => true,
605
- 'show_in_menu' => 'tools.php',
606
- 'query_var' => false,
607
- 'rewrite' => false,
608
- 'capability_type' => 'post',
609
- 'capabilities' => $capabilities,
610
- 'has_archive' => false,
611
- 'hierarchical' => false,
612
- 'register_meta_box_cb' => array( $this, 'action_redirect_rule_metabox' ),
613
- 'menu_position' => 80,
614
- 'supports' => array( '' )
615
- );
616
- register_post_type( $this->redirect_post_type, $redirect_args );
617
- }
618
-
619
- /**
620
- * Registers meta boxes for redirect rule post type
621
- *
622
- * @since 1.0
623
- * @uses add_meta_box
624
- * @return void
625
- */
626
- public function action_redirect_rule_metabox() {
627
- add_meta_box( 'redirect_settings', __( 'Redirect Settings', 'safe-redirect-manager' ), array( $this, 'redirect_rule_metabox' ), $this->redirect_post_type, 'normal', 'core' );
628
- }
629
-
630
- /**
631
- * Echoes HTML for redirect rule meta box
632
- *
633
- * @since 1.0
634
- * @param object $post
635
- * @uses wp_nonce_field, get_post_meta, esc_attr, selected
636
- * @return void
637
- */
638
- public function redirect_rule_metabox( $post ) {
639
- wp_nonce_field( $this->redirect_nonce_action, $this->redirect_nonce_name );
640
-
641
- $redirect_from = get_post_meta( $post->ID, $this->meta_key_redirect_from, true );
642
- $redirect_to = get_post_meta( $post->ID, $this->meta_key_redirect_to, true );
643
- $status_code = get_post_meta( $post->ID, $this->meta_key_redirect_status_code, true );
644
- $enable_regex = get_post_meta( $post->ID, $this->meta_key_enable_redirect_from_regex, true );
645
- if ( empty( $status_code ) )
646
- $status_code = apply_filters( 'srm_default_direct_status', 302 );
647
- ?>
648
- <p>
649
- <label for="srm<?php echo $this->meta_key_redirect_from; ?>"><?php _e( 'Redirect From:', 'safe-redirect-manager' ); ?></label><br />
650
- <input type="text" name="srm<?php echo $this->meta_key_redirect_from; ?>" id="srm<?php echo $this->meta_key_redirect_from; ?>" value="<?php echo esc_attr( $redirect_from ); ?>" />
651
- <input type="checkbox" name="srm<?php echo $this->meta_key_enable_redirect_from_regex; ?>" id="srm<?php echo $this->meta_key_enable_redirect_from_regex; ?>" <?php checked( true, (bool) $enable_regex ); ?> value="1" />
652
- <label for="srm<?php echo $this->meta_key_enable_redirect_from_regex; ?>"><?php _e( 'Enable Regular Expressions (advanced)', 'safe-redirect-manager' ); ?></label><br />
653
- <p class="description"><?php _e( "This path should be relative to the root of this WordPress installation (or the sub-site, if you are running a multi-site). Appending a (*) wildcard character will match all requests with the base. Warning: Enabling regular expressions will disable wildcards and completely change the way the * symbol is interpretted.", 'safe-redirect-manager' ); ?></p>
654
- </p>
655
-
656
- <p>
657
- <label for="srm<?php echo $this->meta_key_redirect_to; ?>"><?php _e( 'Redirect To:', 'safe-redirect-manager' ); ?></label><br />
658
- <input class="widefat" type="text" name="srm<?php echo $this->meta_key_redirect_to; ?>" id="srm<?php echo $this->meta_key_redirect_to; ?>" value="<?php echo esc_attr( $redirect_to ); ?>" /><br />
659
- <p class="description"><?php _e( "This can be a URL or a path relative to the root of your website (not your WordPress installation). Ending with a (*) wildcard character will append the request match to the redirect.", 'safe-redirect-manager'); ?></p>
660
- </p>
661
-
662
- <p>
663
- <label for="srm<?php echo $this->meta_key_redirect_status_code; ?>"><?php _e( 'HTTP Status Code:', 'safe-redirect-manager' ); ?></label>
664
- <select name="srm<?php echo $this->meta_key_redirect_status_code; ?>" id="srm<?php echo $this->meta_key_redirect_status_code; ?>">
665
- <?php foreach ( $this->valid_status_codes as $code ) : ?>
666
- <option value="<?php echo esc_attr( $code ); ?>" <?php selected( $status_code, $code ); ?>><?php echo esc_html( $code . ' ' . $this->status_code_labels[$code] ); ?></option>
667
- <?php endforeach; ?>
668
- </select>
669
- <em><?php _e( "If you don't know what this is, leave it as is.", 'safe-redirect-manager' ); ?></em>
670
- </p>
671
- <?php
672
- }
673
-
674
- /**
675
- * Localize plugin
676
- *
677
- * @since 1.0
678
- * @uses load_plugin_textdomain, plugin_basename
679
- * @return void
680
- */
681
- public function action_init() {
682
- load_plugin_textdomain( 'safe-redirect-manager', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
683
-
684
- $this->default_max_redirects = apply_filters( 'srm_max_redirects', $this->default_max_redirects );
685
- }
686
-
687
- /**
688
- * Apply whitelisted hosts to allowed_redirect_hosts filter
689
- *
690
- * @since 1.0
691
- * @param array $content
692
- * @return array
693
- */
694
- public function filter_allowed_redirect_hosts( $content ) {
695
-
696
- foreach ( $this->whitelist_hosts as $host ) {
697
- $without_www = preg_replace( '/^www\./i', '', $host );
698
- $with_www = 'www.' . $without_www;
699
-
700
- if ( ! in_array( $without_www, $content ) ) $content[] = $without_www;
701
- if ( ! in_array( $with_www, $content ) ) $content[] = $with_www;
702
- }
703
-
704
- return $content;
705
- }
706
-
707
- /**
708
- * Get redirects from the database
709
- *
710
- * @since 1.6
711
- * @param array $args Any arguments to filter by
712
- * @return array $redirects An array of redirects
713
- */
714
- public function get_redirects( $args = array() ) {
715
-
716
- $redirects = array();
717
-
718
- if ( $this->default_max_redirects > 50 )
719
- $posts_per_page = 50;
720
- else
721
- $posts_per_page = $this->default_max_redirects;
722
-
723
- $i = 1;
724
- do {
725
-
726
- $defaults = array(
727
- 'posts_per_page' => $posts_per_page,
728
- 'post_status' => 'publish',
729
- 'paged' => $i,
730
- );
731
-
732
- $query_args = array_merge( $defaults, $args );
733
-
734
- // Some arguments that don't need to be configurable
735
- $query_args['post_type'] = $this->redirect_post_type;
736
- $query_args['no_found_rows'] = false;
737
- $query_args['update_term_cache'] = false;
738
-
739
- $redirect_query = new WP_Query( $query_args );
740
-
741
- foreach( $redirect_query->posts as $redirect ) {
742
- $redirects[] = array(
743
- 'ID' => $redirect->ID,
744
- 'post_status' => $redirect->post_status,
745
- 'redirect_from' => get_post_meta( $redirect->ID, $this->meta_key_redirect_from, true ),
746
- 'redirect_to' => get_post_meta( $redirect->ID, $this->meta_key_redirect_to, true ),
747
- 'status_code' => (int)get_post_meta( $redirect->ID, $this->meta_key_redirect_status_code, true ),
748
- 'enable_regex' => (bool)get_post_meta( $redirect->ID, $this->meta_key_enable_redirect_from_regex, true ),
749
- );
750
- }
751
-
752
- if ( count( $redirects ) == $this->default_max_redirects
753
- || count( $redirect_query->posts ) < $posts_per_page )
754
- $build = false;
755
- else
756
- $build = true;
757
-
758
- $i++;
759
-
760
- } while ( $build );
761
-
762
- return $redirects;
763
- }
764
-
765
- /**
766
- * Force update on the redirect cache and return cache
767
- *
768
- * @since 1.0
769
- * @uses set_transient, get_post_meta, the_post, have_posts, get_the_ID
770
- * @return array
771
- */
772
- public function update_redirect_cache() {
773
-
774
- $redirect_cache = $this->get_redirects();
775
-
776
- set_transient( $this->cache_key_redirects, $redirect_cache );
777
-
778
- return $redirect_cache;
779
- }
780
-
781
- /**
782
- * Check current url against redirects
783
- *
784
- * @since 1.0
785
- * @uses esc_url_raw, wp_safe_redirect, untrailingslashit, get_transient, add_filter
786
- * @return void
787
- */
788
- public function action_parse_request() {
789
-
790
- // Don't redirect from wp-admin
791
- if ( is_admin() )
792
- return;
793
-
794
- // get redirects from cache or recreate it
795
- if ( false === ( $redirects = get_transient( $this->cache_key_redirects ) ) ) {
796
- $redirects = $this->update_redirect_cache();
797
- }
798
-
799
- // If we have no redirects, there is no need to continue
800
- if ( empty( $redirects ) )
801
- return;
802
-
803
- // get requested path and add a / before it
804
- $requested_path = esc_url_raw( $_SERVER['REQUEST_URI'] );
805
- $requested_path = stripslashes( $requested_path );
806
-
807
- $requested_path = untrailingslashit( $requested_path );
808
- if ( empty( $requested_path ) ){
809
- $requested_path = '/';
810
- }
811
-
812
- /**
813
- * If WordPress resides in a directory that is not the public root, we have to chop
814
- * the pre-WP path off the requested path.
815
- */
816
- $parsed_home_url = parse_url( home_url() );
817
- if ( isset( $parsed_home_url['path'] ) && '/' != $parsed_home_url['path'] ) {
818
- $requested_path = preg_replace( '@' . $parsed_home_url['path'] . '@i', '', $requested_path, 1 );
819
- }
820
-
821
- // Allow redirects to be filtered
822
- $redirects = apply_filters( 'srm_registered_redirects', $redirects, $requested_path );
823
-
824
- // Allow for case insensitive redirects
825
- $case_insensitive = apply_filters( 'srm_case_insensitive_redirects', true );
826
- if ( $case_insensitive ) {
827
- $regex_flag = 'i';
828
- // normalized path is used for matching but not for replace
829
- $normalized_requested_path = strtolower( $requested_path );
830
- } else {
831
- $regex_flag = '';
832
- $normalized_requested_path = $requested_path;
833
- }
834
-
835
- foreach ( (array)$redirects as $redirect ) {
836
-
837
- $redirect_from = untrailingslashit( $redirect['redirect_from'] );
838
- if ( empty( $redirect_from ) )
839
- $redirect_from = '/'; // this only happens in the case where there is a redirect on the root
840
-
841
- $redirect_to = $redirect['redirect_to'];
842
- $status_code = $redirect['status_code'];
843
- $enable_regex = ( isset( $redirect['enable_regex'] ) ) ? $redirect['enable_regex'] : false;
844
-
845
- // check if the redirection destination is valid, otherwise just skip it
846
- if ( empty( $redirect_to ) )
847
- continue;
848
-
849
- // check if requested path is the same as the redirect from path
850
- if ( $enable_regex ) {
851
- $matched_path = preg_match( '@' . $redirect_from . '@' . $regex_flag, $requested_path );
852
- } else {
853
- if ( $case_insensitive ) {
854
- $redirect_from = strtolower( $redirect_from );
855
- }
856
-
857
- $matched_path = ( $normalized_requested_path == $redirect_from );
858
-
859
- // check if the redirect_from ends in a wildcard
860
- if ( !$matched_path && (strrpos( $redirect_from, '*' ) === strlen( $redirect_from ) - 1) ) {
861
- $wildcard_base = substr( $redirect_from, 0, strlen( $redirect_from ) - 1 );
862
-
863
- // mark as match if requested path matches the base of the redirect from
864
- $matched_path = (substr( $normalized_requested_path, 0, strlen( $wildcard_base ) ) == $wildcard_base);
865
- if ( (strrpos( $redirect_to, '*' ) == strlen( $redirect_to ) - 1 ) ) {
866
- $redirect_to = rtrim( $redirect_to, '*' ) . ltrim( substr( $requested_path, strlen( $wildcard_base ) ), '/' );
867
- }
868
- }
869
- }
870
-
871
- if ( $matched_path ) {
872
- // whitelist redirect to host if necessary
873
- $parsed_redirect = parse_url( $redirect_to );
874
- if ( is_array( $parsed_redirect ) && ! empty( $parsed_redirect['host'] ) ) {
875
- $this->whitelist_hosts[] = $parsed_redirect['host'];
876
- add_filter( 'allowed_redirect_hosts' , array( $this, 'filter_allowed_redirect_hosts' ) );
877
- }
878
-
879
- // Allow for regex replacement in $redirect_to
880
- if ( $enable_regex ) {
881
- $redirect_to = preg_replace( '@' . $redirect_from . '@' . $regex_flag, $redirect_to, $requested_path );
882
- }
883
-
884
- $sanitized_redirect_to = esc_url_raw( $redirect_to );
885
-
886
- do_action( 'srm_do_redirect', $requested_path, $sanitized_redirect_to, $status_code );
887
-
888
- if ( defined( 'PHPUNIT_SRM_TESTSUITE' ) && PHPUNIT_SRM_TESTSUITE ) {
889
- // Don't actually redirect if we are testing
890
- return;
891
- }
892
-
893
- header( 'X-Safe-Redirect-Manager: true' );
894
-
895
- // if we have a valid status code, then redirect with it
896
- if ( in_array( $status_code, $this->valid_status_codes ) )
897
- wp_safe_redirect( $sanitized_redirect_to, $status_code );
898
- else
899
- wp_safe_redirect( $sanitized_redirect_to );
900
- exit;
901
- }
902
- }
903
- }
904
-
905
- /**
906
- * Sanitize redirect to path
907
- *
908
- * The only difference between this function and just calling esc_url_raw is
909
- * esc_url_raw( 'test' ) == 'http://test', whereas sanitize_redirect_path( 'test' ) == '/test'
910
- *
911
- * @since 1.0
912
- * @param string $path
913
- * @uses esc_url_raw
914
- * @return string
915
- */
916
- public function sanitize_redirect_to( $path ) {
917
- $path = trim( $path );
918
-
919
- if ( preg_match( '/^www\./i', $path ) )
920
- $path = 'http://' . $path;
921
-
922
- if ( ! preg_match( '/^https?:\/\//i', $path ) )
923
- if ( strpos( $path, '/' ) !== 0 )
924
- $path = '/' . $path;
925
-
926
- return esc_url_raw( $path );
927
- }
928
-
929
- /**
930
- * Sanitize redirect from path
931
- *
932
- * @since 1.0
933
- * @param string $path
934
- * @param boolean $allow_regex
935
- * @uses esc_url_raw
936
- * @return string
937
- */
938
- public function sanitize_redirect_from( $path, $allow_regex = false ) {
939
-
940
- $path = trim( $path );
941
-
942
- if ( empty( $path ) )
943
- return '';
944
-
945
- // dont accept paths starting with a .
946
- if ( ! $allow_regex && strpos( $path, '.' ) === 0 )
947
- return '';
948
-
949
- // turn path in to absolute
950
- if ( preg_match( '/https?:\/\//i', $path ) )
951
- $path = preg_replace( '/^(http:\/\/|https:\/\/)(www\.)?[^\/?]+\/?(.*)/i', '/$3', $path );
952
- elseif ( ! $allow_regex && strpos( $path, '/' ) !== 0 )
953
- $path = '/' . $path;
954
-
955
- // the @ symbol will break our regex engine
956
- $path = str_replace( '@', '', $path );
957
-
958
- return $path;
959
- }
960
-
961
- /**
962
- * Return a permalink for a redirect post, which is the "redirect from"
963
- * URL for that redirect.
964
- *
965
- * @since 1.7
966
- * @param string $permalink The permalink
967
- * @param object $post A Post object
968
- * @uses home_url, get_post_meta
969
- * @return string The permalink
970
- */
971
- public function filter_post_type_link( $permalink, $post ) {
972
- if ( $this->redirect_post_type != $post->post_type )
973
- return $permalink;
974
-
975
- // We can't do anything to provide a permalink
976
- // for regex enabled redirects.
977
- if ( get_post_meta( $post->ID, $this->meta_key_enable_redirect_from_regex, true ) )
978
- return $permalink;
979
-
980
- // We can't do anything if there is a wildcard in the redirect from
981
- $redirect_from = get_post_meta( $post->ID, $this->meta_key_redirect_from, true );
982
- if ( false !== strpos( $redirect_from, '*' ) )
983
- return $permalink;
984
-
985
- // Use default permalink if no $redirect_from exists - this prevents duplicate GUIDs
986
- if ( empty( $redirect_from ) ) {
987
- return $permalink;
988
- }
989
-
990
- return home_url( $redirect_from );
991
- }
992
-
993
- /**
994
- * Imports redirects from CSV file or stream.
995
- *
996
- * @since 1.7.6
997
- *
998
- * @access public
999
- * @param string|resource $file File path, file pointer or stream to read redirects from.
1000
- * @param array $args The array of arguments. Includes column mapping and CSV settings.
1001
- * @return array Returns importing statistic on success, otherwise FALSE.
1002
- */
1003
- public function import_file( $file, $args ) {
1004
- $handle = $file;
1005
- $close_handle = false;
1006
- $doing_wp_cli = defined( 'WP_CLI' ) && WP_CLI;
1007
-
1008
- // filter arguments
1009
- $args = apply_filters( 'srm_import_file_args', $args );
1010
-
1011
- // enable line endings auto detection
1012
- @ini_set( 'auto_detect_line_endings', true );
1013
-
1014
- // open file pointer if $file is not a resource
1015
- if ( ! is_resource( $file ) ) {
1016
- $handle = fopen( $file, 'rb' );
1017
- if ( ! $handle ) {
1018
- $doing_wp_cli && WP_CLI::error( sprintf( "Error retrieving %s file", basename( $file ) ) );
1019
- return false;
1020
- }
1021
-
1022
- $close_handle = true;
1023
- }
1024
-
1025
- // process all rows of the file
1026
- $created = $skipped = 0;
1027
- $headers = fgetcsv( $handle );
1028
- while( ( $row = fgetcsv( $handle ) ) ) {
1029
- // validate
1030
- $rule = array_combine( $headers, $row );
1031
- if ( empty( $rule[$args['source']] ) || empty( $rule[$args['target']] ) ) {
1032
- $doing_wp_cli && WP_CLI::warning( "Skipping - redirection rule is formatted improperly." );
1033
- $skipped++;
1034
- continue;
1035
- }
1036
-
1037
- // sanitize
1038
- $redirect_from = $this->sanitize_redirect_from( $rule[$args['source']] );
1039
- $redirect_to = $this->sanitize_redirect_to( $rule[$args['target']] );
1040
- $status_code = ! empty( $rule[$args['code']] ) ? $rule[$args['code']] : 302;
1041
- $regex = ! empty( $rule[$args['regex']] ) ? filter_var( $rule[$args['regex']], FILTER_VALIDATE_BOOLEAN ) : false;
1042
-
1043
- // import
1044
- $id = $this->create_redirect( $redirect_from, $redirect_to, $status_code, $regex );
1045
- if ( is_wp_error( $id ) ) {
1046
- $doing_wp_cli && WP_CLI::warning( $id );
1047
- $skipped++;
1048
- } else {
1049
- $doing_wp_cli && WP_CLI::line( "Success - Created redirect from '{$redirect_from}' to '{$redirect_to}'" );
1050
- $created++;
1051
- }
1052
- }
1053
 
1054
- // close an open file pointer if we've opened it
1055
- if ( $close_handle ) {
1056
- fclose( $handle );
1057
- }
1058
 
1059
- // return result statistic
1060
- return array(
1061
- 'created' => $created,
1062
- 'skipped' => $skipped,
1063
- );
1064
- }
1065
 
 
 
1066
  }
1067
-
1068
- global $safe_redirect_manager;
1069
- $safe_redirect_manager = new SRM_Safe_Redirect_Manager();
1
  <?php
2
+ /**
3
+ * Plugin Name: Safe Redirect Manager
4
+ * Plugin URI: https://10up.com
5
+ * Description: Easily and safely manage HTTP redirects.
6
+ * Author: Taylor Lovett (10up)
7
+ * Version: 1.8
8
+ * Text Domain: safe-redirect-manager
9
+ * Domain Path: /lang/
10
+ * Author URI: https://10up.com
11
+ * License: GPLv2 or later
12
+ * License URI: http://www.gnu.org/licenses/gpl-2.0.html
13
+ */
14
+
15
+ /**
16
+ * Localize plugin
17
+ *
18
+ * @since 1.8
19
+ */
20
+ function srm_load_textdomain() {
21
+ load_plugin_textdomain( 'safe-redirect-manager', false, dirname( __FILE__ ) . '/lang' );
22
+ }
23
+ add_action( 'plugins_loaded', 'srm_load_textdomain' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
+ require_once( dirname( __FILE__ ) . '/inc/functions.php' );
26
+ require_once( dirname( __FILE__ ) . '/inc/classes/class-srm-post-type.php' );
27
+ require_once( dirname( __FILE__ ) . '/inc/classes/class-srm-redirect.php' );
 
28
 
 
 
 
 
 
 
29
 
30
+ if ( defined( 'WP_CLI' ) && WP_CLI ) {
31
+ require_once( dirname( __FILE__ ) . '/inc/classes/class-srm-wp-cli.php' );
32
  }
 
 
 
screenshot-1.png DELETED
Binary file
screenshot-2.png DELETED
Binary file
tests/bootstrap.php DELETED
@@ -1,18 +0,0 @@
1
- <?php
2
-
3
- $_tests_dir = getenv( 'WP_TESTS_DIR' );
4
- if ( ! $_tests_dir ) {
5
- $_tests_dir = '/tmp/wordpress-tests-lib';
6
- }
7
-
8
- define( 'PHPUNIT_SRM_TESTSUITE', 1 );
9
-
10
- require_once ( $_tests_dir . '/includes/functions.php' );
11
-
12
- function _manually_load_plugin() {
13
- require dirname( __FILE__ ) . '/../safe-redirect-manager.php';
14
- }
15
- tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );
16
-
17
- require( $_tests_dir . '/includes/bootstrap.php' );
18
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/test-core.php DELETED
@@ -1,484 +0,0 @@
1
- <?php
2
-
3
- class SRMTestCore extends WP_UnitTestCase {
4
-
5
- /**
6
- * Test root redirect
7
- *
8
- * @since 1.7.3
9
- */
10
- public function testRootRedirect() {
11
- global $safe_redirect_manager;
12
-
13
- $_SERVER['REQUEST_URI'] = '/';
14
- $redirected = false;
15
- $redirect_to = '/gohere';
16
- $safe_redirect_manager->create_redirect( '/', $redirect_to );
17
-
18
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
19
- if ( $redirected_to === $redirect_to ) {
20
- $redirected = true;
21
- }
22
- }, 10, 3 );
23
-
24
- $safe_redirect_manager->action_parse_request();
25
-
26
- $this->assertTrue( $redirected );
27
- }
28
-
29
- /**
30
- * Test redirect with cases
31
- *
32
- * @since 1.7.4
33
- */
34
- public function testCaseInsensitiveRedirect() {
35
- global $safe_redirect_manager;
36
-
37
- $_SERVER['REQUEST_URI'] = '/ONE';
38
- $redirected = false;
39
- $redirect_to = '/gohere';
40
- $safe_redirect_manager->create_redirect( '/one/', $redirect_to );
41
-
42
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
43
- if ( $redirected_to === $redirect_to ) {
44
- $redirected = true;
45
- }
46
- }, 10, 3 );
47
-
48
- $safe_redirect_manager->action_parse_request();
49
-
50
- $this->assertTrue( $redirected );
51
-
52
- $_SERVER['REQUEST_URI'] = '/one';
53
- $redirected = false;
54
- $redirect_to = '/gohere';
55
- $safe_redirect_manager->create_redirect( '/ONE/', $redirect_to );
56
-
57
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
58
- if ( $redirected_to === $redirect_to ) {
59
- $redirected = true;
60
- }
61
- }, 10, 3 );
62
-
63
- $safe_redirect_manager->action_parse_request();
64
-
65
- $this->assertTrue( $redirected );
66
- }
67
-
68
- /**
69
- * Try a redirect after filtering case sensitivity
70
- *
71
- * @since 1.7.4
72
- */
73
- public function testCaseSensitiveRedirect() {
74
- global $safe_redirect_manager;
75
-
76
- $_SERVER['REQUEST_URI'] = '/ONE';
77
- $redirected = false;
78
- $redirect_to = '/gohere';
79
- $safe_redirect_manager->create_redirect( '/one/', $redirect_to );
80
-
81
- add_filter( 'srm_case_insensitive_redirects', function( $value ) {
82
- return false;
83
- }, 10, 1 );
84
-
85
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
86
- if ( $redirected_to === $redirect_to ) {
87
- $redirected = true;
88
- }
89
- }, 10, 3 );
90
-
91
- $safe_redirect_manager->action_parse_request();
92
-
93
- $this->assertFalse( $redirected );
94
- }
95
-
96
- /**
97
- * Test case sensitive redirect to
98
- *
99
- * @since 1.7.4
100
- */
101
- public function testCaseSensitiveRedirectTo() {
102
- global $safe_redirect_manager;
103
-
104
- $_SERVER['REQUEST_URI'] = '/ONE';
105
- $redirected = false;
106
- $redirect_to = '/goHERE';
107
- $safe_redirect_manager->create_redirect( '/one/', $redirect_to );
108
-
109
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
110
- if ( $redirected_to === $redirect_to ) {
111
- $redirected = true;
112
- }
113
- }, 10, 3 );
114
-
115
- $safe_redirect_manager->action_parse_request();
116
-
117
- $this->assertTrue( $redirected );
118
- }
119
-
120
- /**
121
- * Test basic wildcards
122
- *
123
- * @since 1.7.4
124
- */
125
- public function testBasicWildcard() {
126
- global $safe_redirect_manager;
127
-
128
- $_SERVER['REQUEST_URI'] = '/one/dfsdf';
129
- $redirected = false;
130
- $redirect_to = '/gohere';
131
- $safe_redirect_manager->create_redirect( '/one*', $redirect_to );
132
-
133
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
134
- if ( $redirected_to === $redirect_to ) {
135
- $redirected = true;
136
- }
137
- }, 10, 3 );
138
-
139
- $safe_redirect_manager->action_parse_request();
140
-
141
- $this->assertTrue( $redirected );
142
- }
143
-
144
- /**
145
- * Test replace wildcards
146
- *
147
- * @since 1.7.4
148
- */
149
- public function testReplaceWildcard() {
150
- global $safe_redirect_manager;
151
-
152
- $_SERVER['REQUEST_URI'] = '/one/two';
153
- $redirected = false;
154
- $redirect_to = '/gohere/*';
155
- $safe_redirect_manager->create_redirect( '/one/*', $redirect_to );
156
-
157
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
158
- if ( $redirected_to === '/gohere/two' ) {
159
- $redirected = true;
160
- }
161
- }, 10, 3 );
162
-
163
- $safe_redirect_manager->action_parse_request();
164
-
165
- $this->assertTrue( $redirected );
166
- }
167
-
168
- /**
169
- * Test lots of permutations of URL trailing slashes with and without regex
170
- *
171
- * @since 1.7.3
172
- */
173
- public function testTrailingSlashes() {
174
- /**
175
- * First without regex
176
- */
177
-
178
- global $safe_redirect_manager;
179
-
180
- $_SERVER['REQUEST_URI'] = '/one';
181
- $redirected = false;
182
- $redirect_to = '/gohere';
183
- $safe_redirect_manager->create_redirect( '/one/', $redirect_to );
184
-
185
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
186
- if ( $redirected_to === $redirect_to ) {
187
- $redirected = true;
188
- }
189
- }, 10, 3 );
190
-
191
- $safe_redirect_manager->action_parse_request();
192
-
193
- $this->assertTrue( $redirected );
194
-
195
- $_SERVER['REQUEST_URI'] = '/one/';
196
- $redirected = false;
197
- $redirect_to = '/gohere';
198
- $safe_redirect_manager->create_redirect( '/one', $redirect_to );
199
-
200
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
201
- if ( $redirected_to === $redirect_to ) {
202
- $redirected = true;
203
- }
204
- }, 10, 3 );
205
-
206
- $safe_redirect_manager->action_parse_request();
207
-
208
- $this->assertTrue( $redirected );
209
-
210
- $_SERVER['REQUEST_URI'] = '/one/two';
211
- $redirected = false;
212
- $redirect_to = '/gohere';
213
- $safe_redirect_manager->create_redirect( '/one/two/', $redirect_to );
214
-
215
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
216
- if ( $redirected_to === $redirect_to ) {
217
- $redirected = true;
218
- }
219
- }, 10, 3 );
220
-
221
- $safe_redirect_manager->action_parse_request();
222
-
223
- $this->assertTrue( $redirected );
224
-
225
- $_SERVER['REQUEST_URI'] = '/one/two/';
226
- $redirected = false;
227
- $redirect_to = '/gohere';
228
- $safe_redirect_manager->create_redirect( '/one/two', $redirect_to );
229
-
230
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
231
- if ( $redirected_to === $redirect_to ) {
232
- $redirected = true;
233
- }
234
- }, 10, 3 );
235
-
236
- $safe_redirect_manager->action_parse_request();
237
-
238
- $this->assertTrue( $redirected );
239
-
240
- /**
241
- * Now with regex
242
- */
243
-
244
- $_SERVER['REQUEST_URI'] = '/one/two';
245
- $redirected = false;
246
- $redirect_to = '/gohere';
247
- $safe_redirect_manager->create_redirect( '/.*/', $redirect_to, 301, true );
248
-
249
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
250
- if ( $redirected_to === $redirect_to ) {
251
- $redirected = true;
252
- }
253
- }, 10, 3 );
254
-
255
- $safe_redirect_manager->action_parse_request();
256
-
257
- $this->assertTrue( $redirected );
258
-
259
- $_SERVER['REQUEST_URI'] = '/one/two/';
260
- $redirected = false;
261
- $redirect_to = '/gohere';
262
- $safe_redirect_manager->create_redirect( '/.*', $redirect_to, 301, true );
263
-
264
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
265
- if ( $redirected_to === $redirect_to ) {
266
- $redirected = true;
267
- }
268
- }, 10, 3 );
269
-
270
- $safe_redirect_manager->action_parse_request();
271
-
272
- $this->assertTrue( $redirected );
273
- }
274
-
275
- /**
276
- * Test some simple redirections
277
- *
278
- * @since 1.7.3
279
- */
280
- public function testSimplePath() {
281
- global $safe_redirect_manager;
282
-
283
- $_SERVER['REQUEST_URI'] = '/test';
284
- $redirected = false;
285
- $redirect_to = '/gohere';
286
- $safe_redirect_manager->create_redirect( '/test', $redirect_to );
287
-
288
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
289
- if ( $redirected_to === $redirect_to ) {
290
- $redirected = true;
291
- }
292
- }, 10, 3 );
293
-
294
- $safe_redirect_manager->action_parse_request();
295
-
296
- $this->assertTrue( $redirected );
297
-
298
- /**
299
- * Test longer path with no trailing slash
300
- */
301
-
302
- $_SERVER['REQUEST_URI'] = '/test/this/path';
303
- $redirected = false;
304
- $redirect_to = '/gohere';
305
- $safe_redirect_manager->create_redirect( '/test/this/path/', $redirect_to );
306
-
307
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
308
- if ( $redirected_to === $redirect_to ) {
309
- $redirected = true;
310
- }
311
- }, 10, 3 );
312
-
313
- $safe_redirect_manager->action_parse_request();
314
-
315
- $this->assertTrue( $redirected );
316
-
317
- /**
318
- * Test a redirect miss
319
- */
320
-
321
- $_SERVER['REQUEST_URI'] = '/test/wrong/path';
322
- $redirected = false;
323
- $redirect_to = '/gohere';
324
- $safe_redirect_manager->create_redirect( '/test/right/path/', $redirect_to );
325
-
326
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
327
- if ( $redirected_to === $redirect_to ) {
328
- $redirected = true;
329
- }
330
- }, 10, 3 );
331
-
332
- $safe_redirect_manager->action_parse_request();
333
-
334
- $this->assertTrue( ! $redirected );
335
- }
336
-
337
- /**
338
- * Test regex redirections
339
- *
340
- * @since 1.7.3
341
- */
342
- public function testSimplePathRegex() {
343
- global $safe_redirect_manager;
344
-
345
- $_SERVER['REQUEST_URI'] = '/tet/555/path/sdfsfsdf';
346
- $redirected = false;
347
- $redirect_to = '/gohere';
348
- $safe_redirect_manager->create_redirect( '/tes?t/[0-9]+/path/[^/]+/?', $redirect_to, 301, true );
349
-
350
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
351
- if ( $redirected_to === $redirect_to ) {
352
- $redirected = true;
353
- }
354
- }, 10, 3 );
355
-
356
- $safe_redirect_manager->action_parse_request();
357
-
358
- $this->assertTrue( $redirected );
359
-
360
- /**
361
- * Test regex replacement
362
- */
363
-
364
- $_SERVER['REQUEST_URI'] = '/well/everything-else/strip';
365
- $redirected = false;
366
- $redirect_to = '/$1';
367
- $safe_redirect_manager->create_redirect( '/([a-z]+)/.*', $redirect_to, 301, true );
368
-
369
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
370
- if ( $redirected_to === '/well' ) {
371
- $redirected = true;
372
- }
373
- }, 10, 3 );
374
-
375
- $safe_redirect_manager->action_parse_request();
376
-
377
- $this->assertTrue( $redirected );
378
-
379
- /**
380
- * Test regex miss
381
- */
382
-
383
- $_SERVER['REQUEST_URI'] = '/another/test';
384
- $redirected = false;
385
- $redirect_to = '/gohere';
386
- $safe_redirect_manager->create_redirect( '/[0-9]+', $redirect_to, 301, true );
387
-
388
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
389
- if ( $redirected_to === $redirect_to ) {
390
- $redirected = true;
391
- }
392
- }, 10, 3 );
393
-
394
- $safe_redirect_manager->action_parse_request();
395
-
396
- $this->assertTrue( ! $redirected );
397
- }
398
-
399
- /**
400
- * Test that replace (both wildcard and regex) doesn't change the casing on the matched part
401
- *
402
- * @since 1.7.5
403
- */
404
- public function testReplaceCasing() {
405
- global $safe_redirect_manager;
406
-
407
- // with wildcard
408
- $_SERVER['REQUEST_URI'] = '/myfiles1/FooBar.JPEG';
409
- $redirected = false;
410
- $redirect_to = '/images1/*';
411
- $safe_redirect_manager->create_redirect( '/myfiles1/*', $redirect_to );
412
-
413
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
414
- if ( $redirected_to === '/images1/FooBar.JPEG' ) {
415
- $redirected = true;
416
- }
417
- }, 10, 3 );
418
-
419
- $safe_redirect_manager->action_parse_request();
420
-
421
- $this->assertTrue( $redirected );
422
-
423
- // with regex
424
- $_SERVER['REQUEST_URI'] = '/myfiles2/FooBar.JPEG';
425
- $redirected = false;
426
- $redirect_to = '/images2/$1';
427
- $safe_redirect_manager->create_redirect( '/myfiles2/(.*\.jpe?g)', $redirect_to, 301, true );
428
-
429
- add_action( 'srm_do_redirect', function( $requested_path, $redirected_to, $status_code ) use ( &$redirect_to, &$redirected ) {
430
- if ( $redirected_to === '/images2/FooBar.JPEG' ) {
431
- $redirected = true;
432
- }
433
- }, 10, 3 );
434
-
435
- $safe_redirect_manager->action_parse_request();
436
-
437
- $this->assertTrue( $redirected );
438
- }
439
-
440
- /**
441
- * Tests import redirects from file.
442
- *
443
- * @since 1.7.6
444
- *
445
- * @access public
446
- * @global SRM_Safe_Redirect_Manager $safe_redirect_manager The plugin instance.
447
- */
448
- public function testFileImport() {
449
- global $safe_redirect_manager;
450
-
451
- // create temp file and fill up it with redirects
452
- $tmp_file = tmpfile();
453
-
454
- $redirects = array(
455
- // headers
456
- array( 'http code', 'legacy url', 'new url', 'is_regex' ),
457
- // redirects
458
- array( 302, '/some-url', '/new-url', 0 ),
459
- array( 301, '/broken-url', '/fixed-url', 0 ),
460
- array( 301, '/reg?ex/\d+/path', '/go/here', 1 ),
461
- );
462
-
463
- foreach ( $redirects as $row ) {
464
- fputcsv( $tmp_file, $row );
465
- }
466
-
467
- // let's import it
468
- fseek( $tmp_file, 0 );
469
- $processed = $safe_redirect_manager->import_file( $tmp_file, array(
470
- 'source' => 'legacy url',
471
- 'target' => 'new url',
472
- 'regex' => 'is_regex',
473
- 'code' => 'http code',
474
- ) );
475
-
476
- // assert results
477
- $this->assertTrue( is_array( $processed ) && ! empty( $processed['created'] ) );
478
- $this->assertEquals( count( $redirects ) - 1, $processed['created'] );
479
-
480
- // close temp file
481
- fclose( $tmp_file );
482
- }
483
-
484
- }