diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php index 640ff79272..dad9deaebe 100644 --- a/src/wp-admin/includes/ajax-actions.php +++ b/src/wp-admin/includes/ajax-actions.php @@ -3906,26 +3906,25 @@ function wp_ajax_test_url() { $href = get_bloginfo( 'url' ) . $href; } + // No redirects $response = wp_safe_remote_get( $href, array( 'timeout' => 15, // Use an explicit user-agent 'user-agent' => 'WordPress URL Test', ) ); - $message = null; + $error = false; if ( is_wp_error( $response ) ) { - $error = $response->get_error_message(); - - if ( strpos( $message, 'resolve host' ) !== false ) { - $message = array( 'error' => __( 'Invalid host name.' ) ); + if ( strpos( $response->get_error_message(), 'resolve host' ) !== false ) { + $error = true; } - - wp_send_json_error( $message ); + } elseif ( wp_remote_retrieve_response_code( $response ) === 404 ) { + $error = true; } - if ( wp_remote_retrieve_response_code( $response ) === 404 ) { - wp_send_json_error( array( 'error' => __( 'Not found, HTTP error 404.' ) ) ); + if ( $error ) { + wp_send_json_error( array( 'httpError' => true ) ); } wp_send_json_success(); diff --git a/src/wp-includes/class-wp-editor.php b/src/wp-includes/class-wp-editor.php index 0b1039bf83..85d943c972 100644 --- a/src/wp-includes/class-wp-editor.php +++ b/src/wp-includes/class-wp-editor.php @@ -1065,7 +1065,7 @@ final class _WP_Editors { 'Ctrl + letter:' => __( 'Ctrl + letter:' ), 'Letter' => __( 'Letter' ), 'Action' => __( 'Action' ), - 'Invalid host name.' => __( 'Invalid host name.' ), + 'Warning: the link has been inserted but the destination cannot be reached.' => __( 'Warning: the link has been inserted but the destination cannot be reached.' ), 'To move focus to other buttons use Tab or the arrow keys. To return focus to the editor press Escape or use one of the buttons.' => __( 'To move focus to other buttons use Tab or the arrow keys. To return focus to the editor press Escape or use one of the buttons.' ), 'When starting a new paragraph with one of these formatting shortcuts followed by a space, the formatting will be applied automatically. Press Backspace or Escape to undo.' => diff --git a/src/wp-includes/js/tinymce/plugins/wplink/plugin.js b/src/wp-includes/js/tinymce/plugins/wplink/plugin.js index ca38c2fb2f..8ba459a581 100644 --- a/src/wp-includes/js/tinymce/plugins/wplink/plugin.js +++ b/src/wp-includes/js/tinymce/plugins/wplink/plugin.js @@ -94,6 +94,9 @@ var doingUndoRedoTimer; var $ = window.jQuery; var urlErrors = {}; + var emailRegex = /^(mailto:)?[a-z0-9._%+-]+@[a-z0-9][a-z0-9.-]*\.[a-z]{2,63}$/i; + var speak = ( typeof window.wp !== 'undefined' && window.wp.a11y && window.wp.a11y.speak ) ? window.wp.a11y.speak : function() {}; + var hasLinkError = false; function getSelectedLink() { var href, html, @@ -147,6 +150,16 @@ }); } + function setLinkError( $link ) { + hasLinkError = true; + $link.attr( 'data-wplink-url-error', 'true' ); + speak( editor.translate( 'Warning: the link has been inserted but the destination cannot be reached.' ), 'assertive' ); + + if ( toolbar && toolbar.visible() ) { + toolbar.$el.find( '.wp-link-preview a' ).addClass( 'wplink-url-error' ); + } + } + function checkLink( node ) { var $link = editor.$( node ); var href = $link.attr( 'href' ); @@ -155,13 +168,14 @@ return; } - // Early check - if ( /^http/i.test( href ) && ! /\.[a-z]{2,63}(\/|$)/i.test( href ) ) { - urlErrors[href] = tinymce.translate( 'Invalid host name.' ); + hasLinkError = false; + + if ( /^http/i.test( href ) && ! /^https?:\/\/[a-z0-9][a-z0-9.-]*\.[a-z]{2,63}(\/|$)/i.test( href ) ) { + urlErrors[href] = true; } if ( urlErrors.hasOwnProperty( href ) ) { - $link.attr( 'data-wplink-url-error', 'true' ); + setLinkError( $link ); return; } else { $link.removeAttr( 'data-wplink-url-error' ); @@ -179,13 +193,9 @@ return; } - if ( response.data && response.data.error ) { - urlErrors[href] = response.data.error; - $link.attr( 'data-wplink-url-error', 'true' ); - - if ( toolbar && toolbar.visible() ) { - toolbar.$el.find( '.wp-link-preview a' ).addClass( 'wplink-url-error' ).attr( 'title', editor.dom.encode( response.data.error ) ); - } + if ( response.data && response.data.httpError ) { + urlErrors[href] = true; + setLinkError( $link ); } }); } @@ -274,7 +284,7 @@ return; } - if ( ! /^(?:[a-z]+:|#|\?|\.|\/)/.test( href ) ) { + if ( ! /^(?:[a-z]+:|#|\?|\.|\/)/.test( href ) && ! emailRegex.test( href ) ) { href = 'http://' + href; } @@ -291,8 +301,8 @@ editor.nodeChanged(); // Audible confirmation message when a link has been inserted in the Editor. - if ( typeof window.wp !== 'undefined' && window.wp.a11y && typeof window.wpLinkL10n !== 'undefined' ) { - window.wp.a11y.speak( window.wpLinkL10n.linkInserted ); + if ( typeof window.wpLinkL10n !== 'undefined' && ! hasLinkError ) { + speak( window.wpLinkL10n.linkInserted ); } } ); @@ -449,10 +459,9 @@ $input.val( ui.item.permalink ); $( element.firstChild.nextSibling ).val( ui.item.title ); - if ( 9 === event.keyCode && typeof window.wp !== 'undefined' && - window.wp.a11y && typeof window.wpLinkL10n !== 'undefined' ) { + if ( 9 === event.keyCode && typeof window.wpLinkL10n !== 'undefined' ) { // Audible confirmation message when a link has been selected. - window.wp.a11y.speak( window.wpLinkL10n.linkSelected ); + speak( window.wpLinkL10n.linkSelected ); } return false; @@ -536,7 +545,7 @@ editor.on( 'wptoolbar', function( event ) { var linkNode = editor.dom.getParent( event.element, 'a' ), - $linkNode, href, edit, title; + $linkNode, href, edit; if ( typeof window.wpLink !== 'undefined' && window.wpLink.modalOpen ) { editToolbar.tempHide = true; @@ -561,12 +570,12 @@ previewInstance.setURL( href ); event.element = linkNode; event.toolbar = toolbar; - title = urlErrors.hasOwnProperty( href ) ? editor.dom.encode( urlErrors[ href ] ) : null; if ( $linkNode.attr( 'data-wplink-url-error' ) === 'true' ) { - toolbar.$el.find( '.wp-link-preview a' ).addClass( 'wplink-url-error' ).attr( 'title', title ); + toolbar.$el.find( '.wp-link-preview a' ).addClass( 'wplink-url-error' ); } else { - toolbar.$el.find( '.wp-link-preview a' ).removeClass( 'wplink-url-error' ).attr( 'title', null ); + toolbar.$el.find( '.wp-link-preview a' ).removeClass( 'wplink-url-error' ); + hasLinkError = false; } } } else if ( editToolbar.visible() ) { diff --git a/src/wp-includes/js/wplink.js b/src/wp-includes/js/wplink.js index 836f446e88..db03036fa6 100644 --- a/src/wp-includes/js/wplink.js +++ b/src/wp-includes/js/wplink.js @@ -2,8 +2,8 @@ var wpLink; ( function( $, wpLinkL10n, wp ) { var editor, searchTimer, River, Query, correctedURL, linkNode, - emailRegexp = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i, - urlRegexp = /^(https?|ftp):\/\/[A-Z0-9.-]+\.[A-Z]{2,4}[^ "]*$/i, + emailRegexp = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,63}$/i, + urlRegexp = /^(https?|ftp):\/\/[A-Z0-9.-]+\.[A-Z]{2,63}[^ "]*$/i, inputs = {}, rivers = {}, isTouch = ( 'ontouchend' in document );