Customize: Provide validation feedback for invalid Custom Link URLs in nav menu items.

Props RMarks, EGregor, umangvaghela123, andrew.taylor, celloexpressions, westonruter, voldemortensen.
Fixes #32816.

Built from https://develop.svn.wordpress.org/trunk@41697


git-svn-id: http://core.svn.wordpress.org/trunk@41531 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Weston Ruter 2017-10-03 03:43:47 +00:00
parent 51ee4233f1
commit 2d69c9ef4f
8 changed files with 38 additions and 12 deletions

View File

@ -559,6 +559,7 @@
#custom-menu-item-name.invalid, #custom-menu-item-name.invalid,
#custom-menu-item-url.invalid, #custom-menu-item-url.invalid,
.edit-menu-item-url.invalid,
.menu-name-field.invalid, .menu-name-field.invalid,
.menu-name-field.invalid:focus, .menu-name-field.invalid:focus,
#available-menu-items .new-content-item .create-item-input.invalid, #available-menu-items .new-content-item .create-item-input.invalid,

File diff suppressed because one or more lines are too long

View File

@ -559,6 +559,7 @@
#custom-menu-item-name.invalid, #custom-menu-item-name.invalid,
#custom-menu-item-url.invalid, #custom-menu-item-url.invalid,
.edit-menu-item-url.invalid,
.menu-name-field.invalid, .menu-name-field.invalid,
.menu-name-field.invalid:focus, .menu-name-field.invalid:focus,
#available-menu-items .new-content-item .create-item-input.invalid, #available-menu-items .new-content-item .create-item-input.invalid,

File diff suppressed because one or more lines are too long

View File

@ -535,23 +535,34 @@
submitLink: function() { submitLink: function() {
var menuItem, var menuItem,
itemName = $( '#custom-menu-item-name' ), itemName = $( '#custom-menu-item-name' ),
itemUrl = $( '#custom-menu-item-url' ); itemUrl = $( '#custom-menu-item-url' ),
urlRegex,
urlValue;
if ( ! this.currentMenuControl ) { if ( ! this.currentMenuControl ) {
return; return;
} }
/*
* Copyright (c) 2010-2013 Diego Perini, MIT licensed
* https://gist.github.com/dperini/729294
* see also https://mathiasbynens.be/demo/url-regex
* modified to allow protocol-relative URLs
*/
urlRegex = /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i;
urlValue = itemUrl.val();
if ( '' === itemName.val() ) { if ( '' === itemName.val() ) {
itemName.addClass( 'invalid' ); itemName.addClass( 'invalid' );
return; return;
} else if ( '' === itemUrl.val() || 'http://' === itemUrl.val() ) { } else if ( '' === urlValue || 'http://' === urlValue || ! ( '/' === urlValue[0] || urlRegex.test( urlValue ) ) ) {
itemUrl.addClass( 'invalid' ); itemUrl.addClass( 'invalid' );
return; return;
} }
menuItem = { menuItem = {
'title': itemName.val(), 'title': itemName.val(),
'url': itemUrl.val(), 'url': urlValue,
'type': 'custom', 'type': 'custom',
'type_label': api.Menus.data.l10n.custom_label, 'type_label': api.Menus.data.l10n.custom_label,
'object': 'custom' 'object': 'custom'
@ -1387,7 +1398,8 @@
*/ */
_setupUpdateUI: function() { _setupUpdateUI: function() {
var control = this, var control = this,
settingValue = control.setting(); settingValue = control.setting(),
updateNotifications;
control.elements = {}; control.elements = {};
control.elements.url = new api.Element( control.container.find( '.edit-menu-item-url' ) ); control.elements.url = new api.Element( control.container.find( '.edit-menu-item-url' ) );
@ -1470,6 +1482,13 @@
} }
} }
}); });
// Style the URL field as invalid when there is an invalid_url notification.
updateNotifications = function() {
control.elements.url.element.toggleClass( 'invalid', control.setting.notifications.has( 'invalid_url' ) );
};
control.setting.notifications.bind( 'add', updateNotifications );
control.setting.notifications.bind( 'removed', updateNotifications );
}, },
/** /**

File diff suppressed because one or more lines are too long

View File

@ -641,7 +641,7 @@ class WP_Customize_Nav_Menu_Item_Setting extends WP_Customize_Setting {
* @since 4.3.0 * @since 4.3.0
* *
* @param array $menu_item_value The value to sanitize. * @param array $menu_item_value The value to sanitize.
* @return array|false|null Null if an input isn't valid. False if it is marked for deletion. * @return array|false|null|WP_Error Null or WP_Error if an input isn't valid. False if it is marked for deletion.
* Otherwise the sanitized value. * Otherwise the sanitized value.
*/ */
public function sanitize( $menu_item_value ) { public function sanitize( $menu_item_value ) {
@ -701,7 +701,12 @@ class WP_Customize_Nav_Menu_Item_Setting extends WP_Customize_Setting {
$menu_item_value['attr_title'] = wp_unslash( apply_filters( 'excerpt_save_pre', wp_slash( $menu_item_value['attr_title'] ) ) ); $menu_item_value['attr_title'] = wp_unslash( apply_filters( 'excerpt_save_pre', wp_slash( $menu_item_value['attr_title'] ) ) );
$menu_item_value['description'] = wp_unslash( apply_filters( 'content_save_pre', wp_slash( $menu_item_value['description'] ) ) ); $menu_item_value['description'] = wp_unslash( apply_filters( 'content_save_pre', wp_slash( $menu_item_value['description'] ) ) );
if ( '' !== $menu_item_value['url'] ) {
$menu_item_value['url'] = esc_url_raw( $menu_item_value['url'] ); $menu_item_value['url'] = esc_url_raw( $menu_item_value['url'] );
if ( '' === $menu_item_value['url'] ) {
return new WP_Error( 'invalid_url', __( 'Invalid URL.' ) ); // Fail sanitization if URL is invalid.
}
}
if ( 'publish' !== $menu_item_value['status'] ) { if ( 'publish' !== $menu_item_value['status'] ) {
$menu_item_value['status'] = 'draft'; $menu_item_value['status'] = 'draft';
} }

View File

@ -4,7 +4,7 @@
* *
* @global string $wp_version * @global string $wp_version
*/ */
$wp_version = '4.9-alpha-41696'; $wp_version = '4.9-alpha-41697';
/** /**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema. * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.