Customize: Provide validation feedback for invalid Custom Link URLs in nav menu items.
Props RMarks, EGregor, umangvaghela123, andrew.taylor, celloexpressions, westonruter, voldemortensen. Fixes #32816. git-svn-id: https://develop.svn.wordpress.org/trunk@41697 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
57b92c4116
commit
2e5f329cc9
@ -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,
|
||||||
|
@ -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 );
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -641,8 +641,8 @@ 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 ) {
|
||||||
// Menu is marked for deletion.
|
// Menu is marked for deletion.
|
||||||
@ -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'] ) ) );
|
||||||
|
|
||||||
$menu_item_value['url'] = esc_url_raw( $menu_item_value['url'] );
|
if ( '' !== $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';
|
||||||
}
|
}
|
||||||
|
@ -472,6 +472,44 @@ class Test_WP_Customize_Nav_Menu_Item_Setting extends WP_UnitTestCase {
|
|||||||
$this->assertNull( $setting->sanitize( 'not an array' ) );
|
$this->assertNull( $setting->sanitize( 'not an array' ) );
|
||||||
$this->assertNull( $setting->sanitize( 123 ) );
|
$this->assertNull( $setting->sanitize( 123 ) );
|
||||||
|
|
||||||
|
$valid_urls = array(
|
||||||
|
'http://example.com/',
|
||||||
|
'https://foo.example.com/hello.html',
|
||||||
|
'mailto:nobody@example.com?subject=hi',
|
||||||
|
'ftp://example.com/',
|
||||||
|
'ftps://example.com/',
|
||||||
|
'news://news.server.example/example.group.this',
|
||||||
|
'irc://irc.freenode.net/wordpress',
|
||||||
|
'gopher://example.com',
|
||||||
|
'nntp://news.server.example/example.group.this',
|
||||||
|
'feed://example.com/',
|
||||||
|
'telnet://example.com',
|
||||||
|
'mms://example.com',
|
||||||
|
'rtsp://example.com/',
|
||||||
|
'svn://develop.svn.wordpress.org/trunk',
|
||||||
|
'tel:000-000-000',
|
||||||
|
'fax:000-000-000',
|
||||||
|
'xmpp:user@host?message',
|
||||||
|
'webcal://example.com',
|
||||||
|
'urn:org.wordpress',
|
||||||
|
);
|
||||||
|
foreach ( $valid_urls as $valid_url ) {
|
||||||
|
$url_setting = $setting->sanitize( array( 'url' => $valid_url ) );
|
||||||
|
$this->assertInternalType( 'array', $url_setting );
|
||||||
|
$this->assertEquals( $valid_url, $url_setting['url'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$invalid_urls = array(
|
||||||
|
'javascript:alert(1)',
|
||||||
|
'unknown://something.out-there',
|
||||||
|
'smtp://user:pass@mailserver.thing',
|
||||||
|
);
|
||||||
|
foreach ( $invalid_urls as $invalid_url ) {
|
||||||
|
$url_setting = $setting->sanitize( array( 'url' => $invalid_url ) );
|
||||||
|
$this->assertInstanceOf( 'WP_Error', $url_setting );
|
||||||
|
$this->assertEquals( 'invalid_url', $url_setting->get_error_code() );
|
||||||
|
}
|
||||||
|
|
||||||
$unsanitized = array(
|
$unsanitized = array(
|
||||||
'object_id' => 'bad',
|
'object_id' => 'bad',
|
||||||
'object' => '<b>hello</b>',
|
'object' => '<b>hello</b>',
|
||||||
@ -479,7 +517,7 @@ class Test_WP_Customize_Nav_Menu_Item_Setting extends WP_UnitTestCase {
|
|||||||
'position' => -123,
|
'position' => -123,
|
||||||
'type' => 'custom<b>',
|
'type' => 'custom<b>',
|
||||||
'title' => '\o/ o\'o Hi<script>unfilteredHtml()</script>',
|
'title' => '\o/ o\'o Hi<script>unfilteredHtml()</script>',
|
||||||
'url' => 'javascript:alert(1)',
|
'url' => '', // Note the javascript: protocol is checked above and results in a hard validation error, beyond mere sanitization.
|
||||||
'target' => '" onclick="',
|
'target' => '" onclick="',
|
||||||
'attr_title' => '\o/ o\'o <b>bolded</b><script>unfilteredHtml()</script>',
|
'attr_title' => '\o/ o\'o <b>bolded</b><script>unfilteredHtml()</script>',
|
||||||
'description' => '\o/ o\'o <b>Hello world</b><script>unfilteredHtml()</script>',
|
'description' => '\o/ o\'o <b>Hello world</b><script>unfilteredHtml()</script>',
|
||||||
|
Loading…
Reference in New Issue
Block a user