[--overwrite]
418 | *
419 | * @param array $args Positional arguments.
420 | * @param array $assoc_args Key-value associative arguments.
421 | */
422 | public function export_to_csv( $args, $assoc_args ) {
423 | $filename = $assoc_args['csv'] ? $assoc_args['csv'] : false;
424 | $overwrite = isset( $assoc_args['overwrite'] ) ? (bool) $assoc_args['overwrite'] : false;
425 |
426 | if ( ! $filename ) {
427 | WP_CLI::error( 'Invalid CSV file!' );
428 | }
429 |
430 | if ( file_exists( $filename ) && ! $overwrite ) {
431 | WP_CLI::error( 'CSV file already exists!' );
432 | } elseif ( file_exists( $filename ) && $overwrite ) {
433 | WP_CLI::warning( 'Overwriting file ' . $filename );
434 | }
435 |
436 | $file_descriptor = fopen( $filename, 'wb' );
437 |
438 | if ( ! $file_descriptor ) {
439 | WP_CLI::error( 'Invalid CSV filename!' );
440 | }
441 |
442 | $posts_per_page = 100;
443 | $paged = 1;
444 | $post_count = array_sum( (array) wp_count_posts( Post_Type::POST_TYPE ) );
445 | $progress = \WP_CLI\Utils\make_progress_bar( 'Exporting ' . number_format( $post_count ) . ' redirects', $post_count );
446 | $output = array();
447 |
448 | do {
449 | $posts = get_posts(
450 | array(
451 | 'posts_per_page' => $posts_per_page,
452 | 'paged' => $paged,
453 | 'post_type' => Post_Type::POST_TYPE,
454 | 'post_status' => 'any',
455 | 'suppress_filters' => 'false',
456 | )
457 | );
458 |
459 | foreach ( $posts as $post ) {
460 | $redirect_from = $post->post_title;
461 | $redirect_to = ( $post->post_parent && 0 !== $post->post_parent ) ? $post->post_parent : $post->post_excerpt;
462 | $output[] = array( $redirect_from, $redirect_to );
463 | }
464 | $progress->tick( $posts_per_page );
465 |
466 | if ( function_exists( 'vip_inmemory_cleanup' ) ) {
467 | vip_inmemory_cleanup();
468 | }
469 |
470 | ++$paged;
471 | } while ( count( $posts ) );
472 |
473 | $progress->finish();
474 | WP_CLI\Utils\write_csv( $file_descriptor, $output );
475 | fclose( $file_descriptor );
476 | }
477 | }
478 |
--------------------------------------------------------------------------------
/includes/class-wpcom-legacy-redirector-ui.php:
--------------------------------------------------------------------------------
1 | ' . esc_html( $redirect_not_valid_text ) . '
' . esc_html__( 'If you are doing an external redirect, make sure you safelist the domain using the "allowed_redirect_hosts" filter.', 'wpcom-legacy-redirector' ) . '
';
56 | break;
57 | case '404':
58 | echo '' . esc_html( $redirect_not_valid_text ) . '
' . esc_html__( 'Redirect is pointing to a page with the HTTP status of 404.', 'wpcom-legacy-redirector' ) . '
';
59 | break;
60 | case 'valid':
61 | echo '' . esc_html__( 'Redirect Valid.', 'wpcom-legacy-redirector' ) . '
';
62 | break;
63 | case 'private':
64 | echo '' . esc_html( $redirect_not_valid_text ) . '
' . esc_html__( 'The redirect is pointing to content that is not publiclly accessible.', 'wpcom-legacy-redirector' ) . '
';
65 | break;
66 | case 'null':
67 | echo '' . esc_html( $redirect_not_valid_text ) . '
' . esc_html__( 'The redirect is pointing to a Post ID that does not exist.', 'wpcom-legacy-redirector' ) . '
';
68 | }
69 | }
70 | }
71 |
72 | /**
73 | * Remove "draft" from the status filters for vip-legacy-redirect post type.
74 | *
75 | * @param array $views Status filters.
76 | * @return array
77 | */
78 | public function vip_redirects_custom_post_status_filters( $views ) {
79 | unset( $views['draft'] );
80 | return $views;
81 | }
82 |
83 |
84 | /**
85 | * Return error data when validate check fails.
86 | *
87 | * @param string $validate String that passes back the validate result in order to output the right notice.
88 | * @param int $post_id The Post ID.
89 | */
90 | public function vip_legacy_redirect_sendback( $validate, $post_id ) {
91 | $sendback = remove_query_arg( array( 'validate', 'ids' ), wp_get_referer() );
92 | wp_safe_redirect(
93 | add_query_arg(
94 | array(
95 | 'validate' => $validate,
96 | 'ids' => $post_id,
97 | ),
98 | $sendback
99 | )
100 | );
101 | exit();
102 | }
103 | /**
104 | * Validate the Redirect To URL.
105 | */
106 | public function validate_vip_legacy_redirect() {
107 | if ( isset( $_GET['action'] ) && 'validate' === $_GET['action'] ) {
108 | $post = get_post( $_GET['post'] );
109 | if ( ! isset( $_REQUEST['_validate_redirect'] ) || ! wp_verify_nonce( $_REQUEST['_validate_redirect'], 'validate_vip_legacy_redirect' ) ) {
110 | return;
111 | } else {
112 | $redirect = WPCOM_Legacy_Redirector::get_redirect( $post );
113 | $status = WPCOM_Legacy_Redirector::check_if_404( $redirect );
114 |
115 | // Check if $redirect is invalid.
116 | if ( ! wp_validate_redirect( $redirect, false ) ) {
117 | $this->vip_legacy_redirect_sendback( 'invalid', $post->ID );
118 | }
119 | // Check if $redirect is a 404.
120 | if ( 404 === $status ) {
121 | $this->vip_legacy_redirect_sendback( '404', $post->ID );
122 | }
123 | // Check if $redirect is not publicly visible.
124 | if ( 'private' === $redirect ) {
125 | $this->vip_legacy_redirect_sendback( 'private', $post->ID );
126 | }
127 | // Check if $redirect is pointing to a null Post ID.
128 | if ( 'null' === $redirect ) {
129 | $this->vip_legacy_redirect_sendback( 'null', $post->ID );
130 | }
131 | // Check if $redirect is valid.
132 | if ( ( wp_validate_redirect( $redirect, false ) && 404 !== $status ) || 'valid' === $redirect ) {
133 | $this->vip_legacy_redirect_sendback( 'valid', $post->ID );
134 | }
135 | }
136 | }
137 | }
138 | /**
139 | * Validate the redirect that is being added.
140 | */
141 | public function add_redirect_validation() {
142 | if ( ! current_user_can( Capability::MANAGE_REDIRECTS_CAPABILITY ) ) {
143 | return;
144 | }
145 | $errors = array();
146 | $messages = array();
147 | if ( isset( $_POST['redirect_from'] ) && isset( $_POST['redirect_to'] ) ) {
148 | if (
149 | ! isset( $_POST['redirect_nonce_field'] )
150 | || ! wp_verify_nonce( $_POST['redirect_nonce_field'], 'add_redirect_nonce' )
151 | ) {
152 | $errors[] = array(
153 | 'label' => __( 'Error', 'wpcom-legacy-redirector' ),
154 | 'message' => __( 'Sorry, your nonce did not verify.', 'wpcom-legacy-redirector' ),
155 | );
156 | } else {
157 | $redirect_from = sanitize_text_field( $_POST['redirect_from'] );
158 |
159 | // We apply the home_url() prefix to $redirect_from.
160 | $redirect_url_info = Utils::mb_parse_url( home_url() . $redirect_from);
161 | $redirect_from = $redirect_url_info['path'];
162 | if( !empty( $redirect_url_info['query'] ) ) {
163 | $redirect_from .= '?' . $redirect_url_info['query'];
164 | }
165 |
166 | $redirect_to = sanitize_text_field( $_POST['redirect_to'] );
167 | if ( WPCOM_Legacy_Redirector::validate( $redirect_from, $redirect_to ) ) {
168 | $output = WPCOM_Legacy_Redirector::insert_legacy_redirect( $redirect_from, $redirect_to, true );
169 | if ( true === $output ) {
170 | $follow_home_domain = Utils::get_home_domain_without_path();
171 | $link = '' . esc_html( $redirect_from ) . '';
172 | $messages[] = __( 'The redirect was added successfully. Check Redirect: ', 'wpcom-legacy-redirector' ) . $link;
173 | } elseif ( is_wp_error( $output ) ) {
174 | foreach ( $output->get_error_messages() as $error ) {
175 | $errors[] = array(
176 | 'label' => __( 'Error', 'wpcom-legacy-redirector' ),
177 | 'message' => $error,
178 | );
179 | }
180 | }
181 | } else {
182 | $errors[] = array(
183 | 'label' => __( 'Error', 'wpcom-legacy-redirector' ),
184 | 'message' => __( 'Check the values you are using to save the redirect. All fields are required. "Redirect From" and "Redirect To" should not match.', 'wpcom-legacy-redirector' ),
185 | );
186 | }
187 | }
188 | }
189 | return array( $errors, $messages );
190 | }
191 | /**
192 | * Generate the Add Redirect page.
193 | */
194 | public function generate_page_html() {
195 | $array = $this->add_redirect_validation();
196 |
197 | $errors = $array[0];
198 | $messages = $array[1];
199 |
200 | $redirect_from_value = isset( $_POST['redirect_from'], $errors[0] ) ? sanitize_text_field( wp_unslash( $_POST['redirect_from'] ) ) : '/';
201 | $redirect_to_value = isset( $_POST['redirect_to'], $errors[0] ) ? sanitize_text_field( wp_unslash( $_POST['redirect_to'] ) ) : '/';
202 | ?>
203 |
218 |
219 |
220 |
221 |
226 |
227 |
228 |
229 |
230 |
:
231 |
232 |
233 |
234 |
235 |
266 |
267 |
268 | register();
40 | }
41 |
42 | /**
43 | * Initialize and register other classes.
44 | */
45 | public static function init() {
46 | $post_type = new Post_Type();
47 | $post_type->register();
48 |
49 | $list_redirects = new List_Redirects();
50 | $list_redirects->init();
51 | }
52 |
53 | /**
54 | * Remove Bulk Edit from the Bulk Actions drop-down on the CPT's edit screen UI.
55 | *
56 | * @param array $actions Current bulk actions available to drop-down.
57 | * @return array Available bulk actions minus edit functionality.
58 | */
59 | public static function remove_bulk_edit( $actions ) {
60 | unset( $actions['edit'] );
61 | return $actions;
62 | }
63 |
64 | /**
65 | * Performs redirect if current URL is a 404 and redirect rule exists.
66 | */
67 | public static function maybe_do_redirect() {
68 | // Avoid the overhead of running this on every single pageload.
69 | // We move the overhead to the 404 page but the trade-off for site performance is worth it.
70 | if ( ! is_404() ) {
71 | return;
72 | }
73 |
74 | if ( ! isset( $_SERVER['REQUEST_URI'] ) ) {
75 | return;
76 | }
77 |
78 | // $_SERVER is being sanitized in self::normalise_url().
79 | // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
80 | $redirect_data = Lookup::get_redirect_data( self::normalise_url( $_SERVER['REQUEST_URI'] ) );
81 |
82 | if ( ! $redirect_data || ! isset( $redirect_data['redirect_uri'] ) ) {
83 | return;
84 | }
85 |
86 | // Third argument introduced to support the x_redirect_by header to denote WP redirect source.
87 | if ( version_compare( get_bloginfo( 'version' ), '5.1.0', '>=' ) ) {
88 | wp_safe_redirect( $redirect_data['redirect_uri'], $redirect_data['redirect_status'], WPCOM_LEGACY_REDIRECTOR_PLUGIN_NAME );
89 | } else {
90 | header( 'X-legacy-redirect: HIT' );
91 | wp_safe_redirect( $redirect_data['redirect_uri'], $redirect_data['redirect_status'] );
92 | }
93 |
94 | // We need this here to make sure wp_safe_redirect() redirects correctly.
95 | exit;
96 | }
97 |
98 | /**
99 | * Enqueue the JS that builds the link previews.
100 | *
101 | * @since 1.4.0
102 | *
103 | * @param string $hook Get the current page hook.
104 | */
105 | public static function wpcom_legacy_add_redirect_js( $hook ) {
106 | if ( 'vip-legacy-redirect_page_wpcom-legacy-redirector' !== $hook ) {
107 | return;
108 | }
109 |
110 | wp_enqueue_script( 'wpcom-legacy-redirector', plugins_url( '/../js/admin-add-redirects.js', __FILE__ ), array(), WPCOM_LEGACY_REDIRECTOR_VERSION, true );
111 | wp_localize_script( 'wpcom-legacy-redirector', 'wpcomLegacyRedirector', array( 'siteurl' => home_url() ) );
112 | }
113 |
114 | /**
115 | * Insert redirect as CPT in the database.
116 | *
117 | * @param string $from_url URL or path that should be redirected; should have leading slash if path.
118 | * @param int|string $redirect_to The post ID or URL to redirect to.
119 | * @param bool $validate Validate $from_url and $redirect_to values.
120 | * @param bool $return_id Return the insertd post ID if successful, rather than boolean true.
121 | * @return bool|WP_Error True or post ID if inserted; false if not permitted; otherwise error upon validation issue.
122 | */
123 | public static function insert_legacy_redirect( $from_url, $redirect_to, $validate = true, $return_id = false ) {
124 | if ( ! ( defined( 'WP_CLI' ) && WP_CLI ) && ! is_admin() && ! apply_filters( 'wpcom_legacy_redirector_allow_insert', false ) ) {
125 | // Never run on the front end.
126 | return false;
127 | }
128 |
129 | $from_url = self::normalise_url( $from_url );
130 | if ( is_wp_error( $from_url ) ) {
131 | return $from_url;
132 | }
133 | $from_url_hash = self::get_url_hash( $from_url );
134 |
135 | if ( $validate ) {
136 | $valid_urls = self::validate_urls( $from_url, $redirect_to );
137 | if ( is_object( $valid_urls ) ) {
138 | return $valid_urls;
139 | } else {
140 | $valid_urls[0] = $from_url;
141 | $valid_urls[1] = $redirect_to;
142 | }
143 | }
144 |
145 | $args = array(
146 | 'post_name' => $from_url_hash,
147 | 'post_title' => $from_url,
148 | 'post_type' => Post_Type::POST_TYPE,
149 | );
150 |
151 | if ( is_numeric( $redirect_to ) ) {
152 | $args['post_parent'] = $redirect_to;
153 | } elseif ( false !== Utils::mb_parse_url( $redirect_to ) ) {
154 | $args['post_excerpt'] = esc_url_raw( $redirect_to );
155 | } else {
156 | return self::throw_error( 'invalid-redirect-url', 'Invalid redirect_to param; should be a post_id or a URL' );
157 | }
158 |
159 | $inserted_post_id = wp_insert_post( $args );
160 |
161 | wp_cache_delete( $from_url_hash, self::CACHE_GROUP );
162 |
163 | if ( $return_id ) {
164 | return $inserted_post_id;
165 | }
166 |
167 | return true;
168 | }
169 |
170 | /**
171 | * Validate the URLs.
172 | *
173 | * @param string $from_url URL to redirect (source).
174 | * @param string $redirect_to URL to redirect to (destination).
175 | * @return array|WP_Error Error if invalid redirect URL specified; returns array of params otherwise.
176 | */
177 | public static function validate_urls( $from_url, $redirect_to ) {
178 | if ( false !== Lookup::get_redirect_uri( $from_url ) ) {
179 | return new WP_Error( 'duplicate-redirect-uri', 'A redirect for this URI already exists' );
180 | }
181 | if ( is_numeric( $redirect_to ) || false !== strpos( $redirect_to, 'http' ) ) {
182 | if ( is_numeric( $redirect_to ) && true !== self::vip_legacy_redirect_parent_id( $redirect_to ) ) {
183 | $message = __( 'Redirect is pointing to a Post ID that does not exist.', 'wpcom-legacy-redirector' );
184 | return new WP_Error( 'empty-postid', $message );
185 | }
186 | if ( ! wp_validate_redirect( $redirect_to ) ) {
187 | $message = __( 'If you are doing an external redirect, make sure you safelist the domain using the "allowed_redirect_hosts" filter.', 'wpcom-legacy-redirector' );
188 | return new WP_Error( 'external-url-not-allowed', $message );
189 | }
190 | } else {
191 | if ( false === self::validate( $from_url, $redirect_to ) ) {
192 | $message = __( '"Redirect From" and "Redirect To" values are required and should not match.', 'wpcom-legacy-redirector' );
193 | return new WP_Error( 'invalid-values', $message );
194 | }
195 | if ( 404 !== absint( self::check_if_404( home_url() . $from_url ) ) ) {
196 | $message = __( 'Redirects need to be from URLs that have a 404 status.', 'wpcom-legacy-redirector' );
197 | return new WP_Error( 'non-404', $message );
198 | }
199 | if ( 'private' === self::vip_legacy_redirect_check_if_public( $from_url ) ) {
200 | $message = __( 'You are trying to redirect from a URL that is currently private.', 'wpcom-legacy-redirector' );
201 | return new WP_Error( 'private-url', $message );
202 | }
203 | if ( 'private' === self::vip_legacy_redirect_check_if_public( $redirect_to ) && '/' !== $redirect_to ) {
204 | $message = __( 'You are trying to redirect to a URL that is currently not public.', 'wpcom-legacy-redirector' );
205 | return new WP_Error( 'non-public', $message );
206 | }
207 | if ( 'null' === self::vip_legacy_redirect_check_if_public( $redirect_to ) && '/' !== $redirect_to ) {
208 | $message = __( 'You are trying to redirect to a URL that does not exist.', 'wpcom-legacy-redirector' );
209 | return new WP_Error( 'invalid', $message );
210 | }
211 | }
212 | /**
213 | * Filter the result of the redirect validation.
214 | *
215 | * @param array $params {
216 | * Array containing the URLs to validate.
217 | *
218 | * @type string $from_url URL to redirect (source).
219 | * @type string $redirect_to URL to redirect to (destination).
220 | * }
221 | */
222 | return apply_filters( 'wpcom_legacy_redirector_validate_urls', array( $from_url, $redirect_to ) );
223 | }
224 |
225 | /**
226 | * Utility to get MD5 hash of URL.
227 | *
228 | * @param string $url URL to hash.
229 | * @return string Hash representation of string.
230 | */
231 | public static function get_url_hash( $url ) {
232 | return md5( $url );
233 | }
234 |
235 | /**
236 | * Throws new error method
237 | *
238 | * @throws \Exception $message.
239 | *
240 | * @param string $code Error code.
241 | * @param string $message Error message.
242 | * @return \WP_Error | void
243 | */
244 | public static function throw_error( $code, $message ) {
245 |
246 | if ( class_exists( '\WP_Error' ) ) {
247 | return new \WP_Error( $code, $message );
248 | }
249 |
250 | throw new \Exception( $message );
251 | }
252 |
253 | /**
254 | * Takes a request URL and "normalises" it, stripping common elements.
255 | * Removes scheme and host from the URL, as redirects should be independent of these.
256 | *
257 | * @param string $url URL to transform.
258 | * @return string|WP_Error Transformed URL; error if validation failed.
259 | */
260 | public static function normalise_url( $url ) {
261 |
262 | // Sanitise the URL first rather than trying to normalise a non-URL.
263 | $url = esc_url_raw( $url );
264 | if ( empty( $url ) ) {
265 | return self::throw_error( 'invalid-redirect-url', 'The URL does not validate' );
266 | }
267 |
268 | // Break up the URL into it's constituent parts.
269 | $components = Utils::mb_parse_url( $url );
270 |
271 | // Avoid playing with unexpected data.
272 | if ( ! is_array( $components ) ) {
273 | return self::throw_error( 'url-parse-failed', 'The URL could not be parsed' );
274 | }
275 |
276 | // We should have at least a path or query.
277 | if ( ! isset( $components['path'] ) && ! isset( $components['query'] ) ) {
278 | return self::throw_error( 'url-parse-failed', 'The URL contains neither a path nor query string' );
279 | }
280 |
281 | // Make sure $components['query'] is set, to avoid errors.
282 | $components['query'] = ( isset( $components['query'] ) ) ? $components['query'] : '';
283 |
284 | // All we want is path and query strings
285 | // Note this strips hashes (#) too
286 | // @todo should we destory the query strings and rebuild with `add_query_arg()`?
287 | $normalised_url = $components['path'];
288 |
289 | // Only append '?' and the query if there is one.
290 | if ( ! empty( $components['query'] ) ) {
291 | $normalised_url = $components['path'] . '?' . $components['query'];
292 | }
293 |
294 | return $normalised_url;
295 | }
296 |
297 | /**
298 | * Utility function to lowercase a string.
299 | *
300 | * @param string $a_string To apply lowercase.
301 | * @return string Lowercase representation of string.
302 | */
303 | public static function lowercase( $a_string ) {
304 | return ! empty( $a_string ) ? strtolower( $a_string ) : $a_string;
305 | }
306 |
307 | /**
308 | * Utility function to lowercase, trim, and remove trailing slashes from URL.
309 | * Trailing slashes would not be removed if query string was present.
310 | *
311 | * @param string $url URL to be transformed.
312 | * @return string Transformed URL.
313 | */
314 | public static function transform( $url ) {
315 | return trim( self::lowercase( $url ), '/' );
316 | }
317 |
318 | /**
319 | * Check redirect source and destination URL's are different.
320 | *
321 | * @param string $from_url URL to redirect (source).
322 | * @param string $redirect_to URL to redirect to (destination).
323 | * @return bool True if URL's are different; false if they match or either param is empty.
324 | */
325 | public static function validate( $from_url, $redirect_to ) {
326 | return ( ! empty( $from_url ) && ! empty( $redirect_to ) && self::transform( $from_url ) !== self::transform( $redirect_to ) );
327 | }
328 |
329 | /**
330 | * Get response code to later check if URL is a 404.
331 | *
332 | * @param string $url The URL.
333 | * @return int|string HTTP response code; empty string if no response code.
334 | */
335 | public static function check_if_404( $url ) {
336 | if ( function_exists( 'vip_safe_wp_remote_get' ) ) {
337 | $response = vip_safe_wp_remote_get( $url );
338 | } else {
339 | $response = wp_remote_get( $url );
340 | // If it was an error, try again with no SSL verification, in case it was a self-signed certificate: https://github.com/Automattic/WPCOM-Legacy-Redirector/issues/64.
341 | if ( is_wp_error( $response ) ) {
342 | $args = array(
343 | 'sslverify' => false,
344 | );
345 | $response = wp_remote_get( $url, $args );
346 | }
347 | }
348 | $response_code = '';
349 | if ( is_array( $response ) ) {
350 | $response_code = wp_remote_retrieve_response_code( $response );
351 | }
352 | return $response_code;
353 | }
354 |
355 | /**
356 | * Check if $redirect is a public Post.
357 | *
358 | * @param string $excerpt The Excerpt.
359 | * @return string If post status not published returns 'private'; otherwise 'null'.
360 | */
361 | public static function vip_legacy_redirect_check_if_public( $excerpt ) {
362 | $post_types = get_post_types();
363 | $post_obj = get_page_by_path( $excerpt, OBJECT, $post_types );
364 |
365 | if ( ! is_null( $post_obj ) ) {
366 | if ( 'publish' !== get_post_status( $post_obj->ID ) ) {
367 | return 'private';
368 | }
369 | } else {
370 | return 'null';
371 | }
372 | }
373 |
374 | /**
375 | * Get the redirect URL to pass on to validate.
376 | * We look for the excerpt, root, check if private, and check post parent IDs.
377 | *
378 | * @param object $post The Post.
379 | * @return string The redirect URL.
380 | */
381 | public static function get_redirect( $post ) {
382 | if ( has_excerpt( $post->ID ) ) {
383 | $excerpt = get_the_excerpt( $post->ID );
384 |
385 | // Check if redirect is a full URL or not.
386 | if ( 0 === strpos( $excerpt, 'http' ) ) {
387 | $redirect = $excerpt;
388 | } elseif ( '/' === $excerpt ) {
389 | $redirect = 'valid';
390 | } elseif ( 'private' === self::vip_legacy_redirect_check_if_public( $excerpt ) ) {
391 | $redirect = 'private';
392 | } else {
393 | $redirect = home_url() . $excerpt;
394 | }
395 | } else {
396 | // If it's not stored as an Excerpt, it will be stored as a post_parent ID.
397 | // Post Parent IDs are always internal redirects.
398 | $redirect = self::vip_legacy_redirect_parent_id( $post );
399 | }
400 | return $redirect;
401 | }
402 |
403 | /**
404 | * Check if the excerpt is the home URL.
405 | *
406 | * @param string $excerpt The Excerpt of a post.
407 | * @return bool True if is home URL matches param.
408 | */
409 | public static function check_if_excerpt_is_home( $excerpt ) {
410 | if ( '/' === $excerpt || home_url() === $excerpt ) {
411 | return true;
412 | }
413 | }
414 |
415 | /**
416 | * Run checks for the Post Parent ID of the redirect.
417 | *
418 | * @param object $post The Post.
419 | * @return bool|string True on success, false if parent not found, 'private' if not published.
420 | */
421 | public static function vip_legacy_redirect_parent_id( $post ) {
422 | if ( isset( $_POST['redirect_to'] ) && true !== self::check_if_excerpt_is_home( $post ) ) {
423 | if ( null !== get_post( $post ) && 'publish' === get_post_status( $post ) ) {
424 | return true;
425 | }
426 | } else {
427 | if ( is_int( $post ) ) {
428 | $post = get_post( $post );
429 | }
430 | $parent = get_post( $post->post_parent );
431 | if ( null === get_post( $post->post_parent ) ) {
432 | return false;
433 | } elseif ( 'publish' !== get_post_status( $parent ) ) {
434 | return 'private';
435 | } else {
436 | $parent_slug = $parent->post_name;
437 | return $parent_slug;
438 | }
439 | }
440 | }
441 | }
442 |
--------------------------------------------------------------------------------
/js/admin-add-redirects.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Provides helpful preview of what redirect URLs will look like.
3 | */
4 | function update_redirect_preview( redirectFieldId, previewHolderId ) {
5 | let redirectField = document.getElementById( redirectFieldId );
6 |
7 | redirectField.onkeyup = function() {
8 | let prefix = '';
9 | let siteUrl = wpcomLegacyRedirector.siteurl;
10 |
11 | // If it just contains an integer, we assume it is a Post ID.
12 | if ( redirectField.value.match( /^\d+$/ ) ) {
13 | prefix = '?p=';
14 | }
15 |
16 | // If it starts with `http`, then we assume it is an absolute URL.
17 | if ( redirectField.value.match( /^http.+/ ) ) {
18 | prefix = '';
19 | siteUrl = '';
20 | }
21 |
22 | document.getElementById( previewHolderId ).textContent = siteUrl + prefix + redirectField.value;
23 | }
24 | }
25 |
26 | update_redirect_preview( 'redirect_from', 'redirect_from_preview' );
27 | update_redirect_preview( 'redirect_to', 'redirect_to_preview' );
28 |
--------------------------------------------------------------------------------
/phpunit-integration.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./includes
16 |
17 |
18 |
19 |
20 | ./tests/Integration/
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./includes
16 |
17 |
18 |
19 |
20 | ./tests/Unit/
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tests/Behat/FeatureContext.php:
--------------------------------------------------------------------------------
1 | install_wp();
30 |
31 | // Symlink the current project folder into the WP folder as a plugin.
32 | $project_dir = realpath( self::get_vendor_dir() . '/../' );
33 | $plugin_dir = $this->variables['RUN_DIR'] . '/wp-content/plugins';
34 | $this->ensure_dir_exists( $plugin_dir );
35 | $this->proc( "ln -s {$project_dir} {$plugin_dir}/wpcom-legacy-redirector" )->run_check();
36 |
37 | // Activate the plugin.
38 | $this->proc( 'wp plugin activate wpcom-legacy-redirector' )->run_check();
39 | }
40 |
41 | /**
42 | * Ensure that a requested directory exists and create it recursively as needed.
43 | *
44 | * Copied as is from the Tradutorre repo as well.
45 | *
46 | * @param string $directory Directory to ensure the existence of.
47 | * @throws \RuntimeException Directory could not be created.
48 | */
49 | private function ensure_dir_exists( $directory ) {
50 | $parent = dirname( $directory );
51 |
52 | if ( ! empty( $parent ) && ! is_dir( $parent ) ) {
53 | $this->ensure_dir_exists( $parent );
54 | }
55 |
56 | // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_mkdir
57 | if ( ! is_dir( $directory ) && ! mkdir( $directory ) && ! is_dir( $directory ) ) {
58 | throw new \RuntimeException( esc_html( "Could not create directory '{$directory}'." ) );
59 | }
60 | }
61 |
62 | /**
63 | * Add host to allowed_redirect_hosts.
64 | *
65 | * @Given :host is allowed to be redirected
66 | *
67 | * @param string $host Host name to add.
68 | */
69 | public function i_add_host_to_allowed_redirect_hosts( $host ) {
70 | $filter_allowed_redirect_hosts = <<variables['RUN_DIR'] . "/wp-content/mu-plugins/allowed_redirect_hosts-{$host}.php",
76 | $filter_allowed_redirect_hosts
77 | );
78 | }
79 |
80 | /**
81 | * Add a published post.
82 | *
83 | * @Given there is a published post with a slug of :post_name
84 | *
85 | * @param string $post_name Post name to use.
86 | */
87 | public function there_is_a_published_post( $post_name ) {
88 | $this->proc( "wp post create --post_title='{$post_name}' --post_name='{$post_name}' --post_status='publish'" )->run_check();
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/tests/Integration/CapabilityTest.php:
--------------------------------------------------------------------------------
1 | unregister();
27 | }
28 |
29 | /**
30 | * Test capabilities for Capability::MANAGE_REDIRECTS_CAPABILITY.
31 | *
32 | * @return void
33 | */
34 | public function test_new_admin_capability() {
35 | $capability = new Capability();
36 |
37 | // We need to force clear capabilities here as the wp_options `roles` option might not get cleared after a failed test.
38 | $capability->unregister();
39 |
40 | // in WP_User class, if multisite and user is administrator, all capabilities are allowed, so this test is not useful.
41 | if ( ! is_multisite() ) {
42 | // We check if a new admin user does not have the redirect capability.
43 | $user_id = 1;
44 | $this->assertUserNotHasRedirectCapability( $user_id );
45 | }
46 |
47 | $this->assertRoleNotHasRedirectsCapability( 'administrator' );
48 | $this->assertRoleNotHasRedirectsCapability( 'editor' );
49 | $this->assertRoleNotHasRedirectsCapability( 'subscriber' );
50 | $this->assertRoleNotHasRedirectsCapability( '' );
51 |
52 | $capability->register();
53 |
54 | $this->assertRoleHasRedirectsCapability( 'administrator' );
55 | $this->assertRoleHasRedirectsCapability( 'editor' );
56 | $this->assertRoleNotHasRedirectsCapability( 'subscriber' ); // Should be no change.
57 | $this->assertRoleNotHasRedirectsCapability( '' ); // Should be no change.
58 | }
59 |
60 | /**
61 | * Test the Capability unregister method.
62 | *
63 | * @return void
64 | */
65 | public function test_capability_can_be_unregistered() {
66 | $capability = new Capability();
67 | $capability->register();
68 |
69 | $this->assertFalse( $capability->register() );
70 |
71 | $this->assertRoleHasRedirectsCapability( 'administrator' );
72 |
73 | $capability->unregister();
74 |
75 | $this->assertRoleNotHasRedirectsCapability( 'administrator' );
76 |
77 | $this->assertTrue( $capability->register() );
78 | }
79 |
80 | /**
81 | * Check if a specific user has Redirects capability.
82 | *
83 | * @param int|WP_User $user ID of the user, or WP_User object.
84 | * @return bool True if the user has the redirects capability, false otherwise.
85 | */
86 | private function assertUserHasRedirectCapability( $user ) {
87 | if ( is_numeric( $user ) ) {
88 | $user = wp_set_current_user( $user );
89 | }
90 |
91 | return $this->assertTrue( $user->has_cap( Capability::MANAGE_REDIRECTS_CAPABILITY ) );
92 | }
93 |
94 | /**
95 | * Check if a specific user does NOT have Redirects capability.
96 | *
97 | * @param int|WP_User $user ID of the user, or WP_User object.
98 | * @return bool True if the user does not have the redirects capability, false otherwise.
99 | */
100 | private function assertUserNotHasRedirectCapability( $user ) {
101 | if ( is_numeric( $user ) ) {
102 | $user = wp_set_current_user( $user );
103 | }
104 |
105 | return $this->assertFalse( $user->has_cap( Capability::MANAGE_REDIRECTS_CAPABILITY ) );
106 | }
107 |
108 | /**
109 | * Check if a role has Redirects capability.
110 | *
111 | * @param string $role Name of the role to check e.g. administrator.
112 | * @return bool True if the role has the redirects capability, false otherwise.
113 | */
114 | private function assertRoleHasRedirectsCapability( $role ) {
115 | $user_id = self::factory()->user->create( array( 'role' => $role ) );
116 | $user = wp_set_current_user( $user_id );
117 |
118 | return $this->assertUserHasRedirectCapability( $user );
119 | }
120 |
121 | /**
122 | * Check if a role does NOT have Redirects capability.
123 | *
124 | * @param string $role Name of the role to check e.g. administrator.
125 | * @return bool True if the role does not have the redirects capability, false otherwise.
126 | */
127 | private function assertRoleNotHasRedirectsCapability( $role ) {
128 | $user_id = self::factory()->user->create( array( 'role' => $role ) );
129 | $user = wp_set_current_user( $user_id );
130 |
131 | return $this->assertUserNotHasRedirectCapability( $user );
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/tests/Integration/LookupTest.php:
--------------------------------------------------------------------------------
1 | assertEquals( $to_url, $redirect_data['redirect_uri'] );
31 | $this->assertEquals( $redirect_status, $redirect_data['redirect_status'] );
32 |
33 | }
34 |
35 | /**
36 | * Data provider for tests methods
37 | *
38 | * @return array
39 | */
40 | public function get_protected_redirect_data() {
41 | return array(
42 | 'redirect unicode characters with querystring' => array(
43 | '/فوتوغرافيا/?test=فوتوغرافيا',
44 | 'http://example.com/some_other_page',
45 | '301',
46 | ),
47 | 'redirect_simple' => array(
48 | '/test',
49 | 'http://example.com/',
50 | '301',
51 | ),
52 | 'redirect_unicode_no_query' => array(
53 | '/فوتوغرافيا/',
54 | 'http://example.com/',
55 | '301',
56 | ),
57 | );
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/tests/Integration/PostTypeTest.php:
--------------------------------------------------------------------------------
1 | assertTrue( post_type_exists( Post_Type::POST_TYPE ) );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tests/Integration/RedirectsTest.php:
--------------------------------------------------------------------------------
1 |
26 | */
27 | public function get_redirect_data() {
28 | return array(
29 | 'redirect_relative_path' => array(
30 | '/non-existing-page',
31 | '/test2',
32 | home_url() . '/test2',
33 | ),
34 |
35 | 'redirect_unicode_in_path' => array(
36 | // https://www.w3.org/International/articles/idn-and-iri/ .
37 | '/JP納豆',
38 | 'http://example.com',
39 | ),
40 |
41 | 'redirect Arabic in path' => array(
42 | // https://www.w3.org/International/articles/idn-and-iri/ .
43 | '/فوتوغرافيا/?test=فوتوغرافيا',
44 | 'http://example.com',
45 | ),
46 |
47 | 'redirect_simple' => array(
48 | '/simple-redirect',
49 | 'http://example.com',
50 | ),
51 |
52 | 'redirect_with_querystring' => array(
53 | '/a-redirect?with=query-string',
54 | 'http://example.com',
55 | ),
56 |
57 | 'redirect_with_hashes' => array(
58 | // The plugin should strip the hash and only store the URL path.
59 | '/hash-redirect#with-hash',
60 | 'http://example.com',
61 | ),
62 | );
63 | }
64 |
65 | /**
66 | * Test redirect is inserted successfully and returns true.
67 | *
68 | * @dataProvider get_redirect_data
69 | * @covers WPCOM_Legacy_Redirector::insert_legacy_redirect
70 | * @param string $from From path.
71 | * @param string $to Destination.
72 | */
73 | public function test_redirect_is_inserted_successfully_and_returns_true( $from, $to, $expected = null ) {
74 | $redirect = WPCOM_Legacy_Redirector::insert_legacy_redirect( $from, $to, false );
75 | $this->assertTrue( $redirect, 'insert_legacy_redirect() and return true, failed' );
76 |
77 | $redirect = Lookup::get_redirect_uri( $from );
78 |
79 | if ( \is_null( $expected ) ) {
80 | $expected = $to;
81 | }
82 | $this->assertEquals( $expected, $redirect, 'get_redirect_uri(), failed - got "' . $redirect . '", expected "' . $to . '"' );
83 | }
84 |
85 | /**
86 | * Test redirect is inserted successfully and returns a post ID.
87 | *
88 | * @covers WPCOM_Legacy_Redirector::insert_legacy_redirect
89 | */
90 | public function test_redirect_is_inserted_successfully_and_returns_post_id() {
91 | $redirect = WPCOM_Legacy_Redirector::insert_legacy_redirect( '/simple-redirect', 'http://example.com', false, true );
92 | self::assertIsInt( $redirect, 'insert_legacy_redirect() and return post ID, failed' );
93 | }
94 |
95 | /**
96 | * Data Provider of Redirect Rules and test urls for Protected Params
97 | *
98 | * @return array
99 | */
100 | public function get_protected_redirect_data() {
101 | return array(
102 | 'redirect_simple_protected' => array(
103 | '/simple-redirectA/',
104 | 'http://example.com/',
105 | '/simple-redirectA/?utm_source=XYZ',
106 | 'http://example.com/?utm_source=XYZ',
107 | ),
108 |
109 | 'redirect_protected_with_querystring' => array(
110 | '/b-redirect/?with=query-string',
111 | 'http://example.com/',
112 | '/b-redirect/?with=query-string&utm_medium=123',
113 | 'http://example.com/?utm_medium=123',
114 | ),
115 |
116 | 'redirect_protected_with_hashes' => array(
117 | // The plugin should strip the hash and only store the URL path.
118 | '/hash-redirectA/#with-hash',
119 | 'http://example.com/',
120 | '/hash-redirectA/?utm_source=SDF#with-hash',
121 | 'http://example.com/?utm_source=SDF',
122 | ),
123 |
124 | 'redirect_multiple_protected' => array(
125 | '/simple-redirectC/',
126 | 'http://example.com/',
127 | '/simple-redirectC/?utm_source=XYZ&utm_medium=FALSE&utm_campaign=543',
128 | 'http://example.com/?utm_source=XYZ&utm_medium=FALSE&utm_campaign=543',
129 | ),
130 | );
131 | }
132 |
133 | /**
134 | * Verify that safelisted parameters are maintained on final redirect URLs.
135 | *
136 | * @dataProvider get_protected_redirect_data
137 | * @covers WPCOM_Legacy_Redirector::insert_legacy_redirect
138 | * @covers \Automattic\LegacyRedirector\Lookup::get_redirect_uri
139 | * @param string $from From path.
140 | * @param string $to Destination.
141 | * @param string $protected_from From path with preserved params.
142 | * @param string $protected_to Destination. with preserved params.
143 | */
144 | public function test_protected_query_redirect( $from, $to, $protected_from, $protected_to ) {
145 | add_filter(
146 | 'wpcom_legacy_redirector_preserve_query_params',
147 | function ( $preserved_params ) {
148 | array_push(
149 | $preserved_params,
150 | 'utm_source',
151 | 'utm_medium',
152 | 'utm_campaign'
153 | );
154 | return $preserved_params;
155 | }
156 | );
157 |
158 | $redirect = WPCOM_Legacy_Redirector::insert_legacy_redirect( $from, $to, false );
159 | $this->assertTrue( $redirect, 'insert_legacy_redirect failed' );
160 |
161 | $redirect = Lookup::get_redirect_uri( $protected_from );
162 | $this->assertEquals( $redirect, $protected_to, 'get_redirect_uri failed' );
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/tests/Integration/TestCase.php:
--------------------------------------------------------------------------------
1 | register method to make sure update_option is only called once and mocking wpcom_vip_add_role_caps function
21 | *
22 | * @covers \Automattic\LegacyRedirector\Capability::register
23 | * @uses \Automattic\LegacyRedirector\Capability::get_capabilities_version_key
24 | * @return void
25 | */
26 | public function test_register_method_is_only_called_once() {
27 | $capability = new Capability();
28 |
29 | Functions\when( 'wpcom_vip_add_role_caps' )
30 | ->justReturn( true );
31 |
32 | Functions\expect( 'get_option' )
33 | ->once()
34 | ->andReturn( 0 );
35 |
36 | Functions\expect( 'update_option' )
37 | ->once()
38 | ->andReturn( true );
39 |
40 | $capability->register();
41 |
42 | Functions\expect( 'get_option' )
43 | ->once()
44 | ->andReturn( $capability::CAPABILITIES_VER );
45 |
46 | Functions\expect( 'update_option' )
47 | ->never();
48 |
49 | $capability->register();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/tests/Unit/CliTest.php:
--------------------------------------------------------------------------------
1 | static function ( $url, $component ) {
21 | return parse_url( $url, $component );
22 | },
23 | 'esc_url_raw', // Return 1st param unchanged.
24 | )
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/Unit/PreservableParamsTest.php:
--------------------------------------------------------------------------------
1 |
28 | */
29 | public function data_get_preservable_querystring_params_from_url() {
30 | return array(
31 | 'No querystring' => array(
32 | 'https://example.com',
33 | array( 'foo', 'bar', 'baz' ),
34 | array(),
35 | ),
36 | 'Empty list of param keys' => array(
37 | 'https://example.com?foo=123&bar=456',
38 | array(),
39 | array(),
40 | ),
41 | 'Single key' => array(
42 | 'https://example.com?foo=123&bar=qwerty&baz=456',
43 | array( 'foo' ),
44 | array(
45 | 'foo' => '123',
46 | ),
47 | ),
48 | 'Multiple keys' => array(
49 | 'https://example.com?foo=123&bar=qwerty&baz=456',
50 | array( 'foo', 'bar' ),
51 | array(
52 | 'foo' => '123',
53 | 'bar' => 'qwerty',
54 | ),
55 | ),
56 | 'Multiple instance of preservable keys' => array(
57 | 'https://example.com?foo=123&bar=qwerty&baz=456',
58 | array( 'foo', 'bar', 'foo' ),
59 | array(
60 | 'foo' => '123',
61 | 'bar' => 'qwerty',
62 | ),
63 | ),
64 | 'Multiple instance of URL keys' => array(
65 | 'https://example.com?foo=123&bar=qwerty&foo=456',
66 | array( 'foo', 'bar', 'foo' ),
67 | array(
68 | 'foo' => '123',
69 | 'bar' => 'qwerty',
70 | // phpcs:ignore Universal.Arrays.DuplicateArrayKey.Found -- intentional duplicate.
71 | 'foo' => '456',
72 | ),
73 | ),
74 | 'URL key is an array' => array(
75 | 'https://example.com?foo[]=123&bar=qwerty&foo[]=456',
76 | array( 'foo', 'bar', 'foo' ),
77 | array(
78 | 'foo' => array(
79 | '123',
80 | '456',
81 | ),
82 | 'bar' => 'qwerty',
83 | ),
84 | ),
85 | 'String returned from filter' => array(
86 | 'https://example.com?foo=123&bar=456',
87 | 'foo',
88 | new UnexpectedValueException(),
89 | ),
90 | 'Int returned from filter' => array(
91 | 'https://example.com?foo=123&bar=456',
92 | 0,
93 | new UnexpectedValueException(),
94 | ),
95 | 'Associative array returned from filter' => array(
96 | 'https://example.com?foo=123&bar=456',
97 | array(
98 | 'foo' => 0,
99 | 'baz' => 1,
100 | ),
101 | new UnexpectedValueException(),
102 | ),
103 | );
104 | }
105 |
106 | /**
107 | * Test that preservable parameters from the querystring are preserved.
108 | *
109 | * @covers \Automattic\LegacyRedirector\Lookup::get_preservable_querystring_params_from_url
110 | * @uses. \Automattic\LegacyRedirector\Utils::mb_parse_url
111 | * @dataProvider data_get_preservable_querystring_params_from_url
112 | * @param string $url The URL to parse.
113 | * @param array $preservable_param_keys The keys that should be preserved.
114 | * @param array $expected The expected outcome.
115 | */
116 | public function test_get_preservable_querystring_params_from_url( $url, $preservable_param_keys, $expected ) {
117 | Monkey\Filters\expectApplied( 'wpcom_legacy_redirector_preserve_query_params' )
118 | ->once()
119 | ->andReturn( $preservable_param_keys );
120 |
121 | Monkey\Functions\stubs(
122 | array(
123 | 'wp_parse_url' => static function ( $url, $component ) {
124 | // phpcs:ignore WordPress.WP.AlternativeFunctions.parse_url_parse_url
125 | return parse_url( $url, $component );
126 | },
127 | )
128 | );
129 |
130 | if ( ! is_array( $expected ) ) {
131 | $this->expectException( get_class( $expected ) );
132 | }
133 |
134 | $actual = Lookup::get_preservable_querystring_params_from_url( $url );
135 |
136 | self::assertSame( $expected, $actual, 'Preserved keys and values do not match.' );
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/tests/Unit/RedirectsTest.php:
--------------------------------------------------------------------------------
1 | expectException( $expected_domain );
31 | }
32 |
33 | $this->assertSame( $expected_return, WPCOM_Legacy_Redirector::normalise_url( $url ) );
34 |
35 | }
36 |
37 | /**
38 | * Data provider for tests methods for normalise_url tests
39 | *
40 | * @return array
41 | */
42 | public function get_protected_redirect_data_full_url_only() {
43 | return array(
44 | 'redirect_simple_url_no_end_slash' => array(
45 | 'https://www.example1.org',
46 | 'error',
47 | 'Exception',
48 | '',
49 | '',
50 | ),
51 | 'redirect_simple_url_with_end_slash' => array(
52 | 'https://www.example1.org/',
53 | 'https',
54 | 'www.example1.org',
55 | '/',
56 | '',
57 | ),
58 | 'redirect_ascii_path_with_multiple_slashes' => array(
59 | 'https://www.example1.org///test///?test2=123&test=456',
60 | 'https',
61 | 'www.example1.org',
62 | '///test///',
63 | 'test2=123&test=456',
64 | ),
65 | 'redirect_unicode_path_with_multiple_slashes_and_query' => array(
66 | 'https://www.example1.org///test///?فوتوغرافيا/?test=فوتوغرافيا',
67 | 'https',
68 | 'www.example1.org',
69 | '///test///',
70 | 'فوتوغرافيا/?test=فوتوغرافيا',
71 | ),
72 | 'redirect_unicode_path_with_multiple_slashes' => array(
73 | 'https://www.example1.org//فوتوغرافيا/?test=فوتوغرافيا',
74 | 'https',
75 | 'www.example1.org',
76 | '//فوتوغرافيا/',
77 | 'test=فوتوغرافيا',
78 | ),
79 | );
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/tests/Unit/UtilsTest.php:
--------------------------------------------------------------------------------
1 | do_assertion_mb_parse_url( $url, $expected_schema, $expected_domain, $expected_path, $expected_query );
28 |
29 | }
30 |
31 | /**
32 | * Data provider for tests methods
33 | *
34 | * @return array
35 | */
36 | public function get_protected_redirect_data() {
37 | return array(
38 | 'redirect_simple_url_no_end_slash' => array(
39 | 'https://www.example1.org',
40 | 'https',
41 | 'www.example1.org',
42 | '',
43 | '',
44 | ),
45 | 'redirect_simple_url_with_end_slash' => array(
46 | 'http://www.example2.org/',
47 | 'http',
48 | 'www.example2.org',
49 | '/',
50 | '',
51 | ),
52 | 'redirect_url_with_path' => array(
53 | 'https://www.example3.com/test',
54 | 'https',
55 | 'www.example3.com',
56 | '/test',
57 | '',
58 | ),
59 | 'redirect_unicode_url_with_query' => array(
60 | 'http://www.example4.com//فوتوغرافيا/?test=فوتوغرافيا',
61 | 'http',
62 | 'www.example4.com',
63 | '//فوتوغرافيا/',
64 | 'test=فوتوغرافيا',
65 | ),
66 | 'redirect_unicode_path_with_query' => array(
67 | '/فوتوغرافيا/?test=فوتوغرافيا',
68 | '',
69 | '',
70 | '/فوتوغرافيا/',
71 | 'test=فوتوغرافيا',
72 | ),
73 | 'redirect_unicode_path_with_multiple_parameters' => array(
74 | '/فوتوغرافيا/?test2=فوتوغرافيا&test=فوتوغرافيا',
75 | '',
76 | '',
77 | '/فوتوغرافيا/',
78 | 'test2=فوتوغرافيا&test=فوتوغرافيا',
79 | ),
80 | 'redirect_malformed_url' => array(
81 | 'http://',
82 | 'exception',
83 | 'InvalidArgumentException',
84 | '',
85 | '',
86 | ),
87 |
88 | );
89 | }
90 |
91 | /**
92 | * Do assertion method for testing mb_parse_url().
93 | *
94 | * @param string $url URL to test redirection against, can be a full blown URL with schema.
95 | * @param string $expected_scheme Expected URL schema return. | `exception` string for Exceptions.
96 | * @param string $expected_host Expected URL hostname return. | Exception Type String, like `InvalidArgumentException`.
97 | * @param string $expected_path Expected URL path return.
98 | * @param string $expected_query Expected URL query return.
99 | * @return void
100 | */
101 | private function do_assertion_mb_parse_url( $url, $expected_scheme, $expected_host, $expected_path, $expected_query ) {
102 |
103 | if ( 'exception' === $expected_scheme ) {
104 | $this->expectException( $expected_host );
105 | }
106 |
107 | $path_info = Utils::mb_parse_url( $url );
108 |
109 | if ( ! isset( $path_info['scheme'] ) ) {
110 | $path_info['scheme'] = '';
111 | }
112 | if ( ! isset( $path_info['host'] ) ) {
113 | $path_info['host'] = '';
114 | }
115 | if ( ! isset( $path_info['path'] ) ) {
116 | $path_info['path'] = '';
117 | }
118 | if ( ! isset( $path_info['query'] ) ) {
119 | $path_info['query'] = '';
120 | }
121 |
122 | $this->assertIsArray( $path_info );
123 | $this->assertGreaterThan( 1, count( $path_info ) );
124 | $this->assertSame( $expected_host, $path_info['host'] );
125 | $this->assertSame( $expected_path, $path_info['path'] );
126 | $this->assertSame( $expected_query, $path_info['query'] );
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/tests/Unit/bootstrap.php:
--------------------------------------------------------------------------------
1 |