Skip to content

Commit 1880a50

Browse files
committed
Menus: Prevent HTML in menu item titles from being rendered unexpectedly.
Merges [60815] and [60816] to the 6.8 branch. Props audrasjb, desrosj, johnbillion, jorbin, phillsav, vortfu, westonruter git-svn-id: https://develop.svn.wordpress.org/branches/6.8@60818 602fd350-edb4-49c9-b593-d223f7449a82
1 parent e5c87e7 commit 1880a50

File tree

6 files changed

+161
-120
lines changed

6 files changed

+161
-120
lines changed

src/js/_enqueues/lib/nav-menu.js

Lines changed: 67 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -298,44 +298,46 @@
298298

299299
$.each( parentDropdowns, function() {
300300
var parentDropdown = $( this ),
301-
$html = '',
302-
$selected = '',
303-
currentItemID = parentDropdown.closest( 'li.menu-item' ).find( '.menu-item-data-db-id' ).val(),
304-
currentparentID = parentDropdown.closest( 'li.menu-item' ).find( '.menu-item-data-parent-id' ).val(),
301+
currentItemID = parseInt( parentDropdown.closest( 'li.menu-item' ).find( '.menu-item-data-db-id' ).val() ),
302+
currentParentID = parseInt( parentDropdown.closest( 'li.menu-item' ).find( '.menu-item-data-parent-id' ).val() ),
305303
currentItem = parentDropdown.closest( 'li.menu-item' ),
306304
currentMenuItemChild = currentItem.childMenuItems(),
307-
excludeMenuItem = [ currentItemID ];
305+
excludeMenuItem = /** @type {number[]} */ [ currentItemID ];
306+
307+
parentDropdown.empty();
308308

309309
if ( currentMenuItemChild.length > 0 ) {
310310
$.each( currentMenuItemChild, function(){
311311
var childItem = $(this),
312-
childID = childItem.find( '.menu-item-data-db-id' ).val();
312+
childID = parseInt( childItem.find( '.menu-item-data-db-id' ).val() );
313313

314314
excludeMenuItem.push( childID );
315315
});
316316
}
317317

318-
if ( currentparentID == 0 ) {
319-
$selected = 'selected';
320-
}
321-
322-
$html += '<option ' + $selected + ' value="0">' + wp.i18n._x( 'No Parent', 'menu item without a parent in navigation menu' ) + '</option>';
318+
parentDropdown.append(
319+
$( '<option>', {
320+
value: '0',
321+
selected: currentParentID === 0,
322+
text: wp.i18n._x( 'No Parent', 'menu item without a parent in navigation menu' ),
323+
} )
324+
);
323325

324326
$.each( menuItems, function() {
325327
var menuItem = $(this),
326-
$selected = '',
327-
menuID = menuItem.find( '.menu-item-data-db-id' ).val(),
328+
menuID = parseInt( menuItem.find( '.menu-item-data-db-id' ).val() ),
328329
menuTitle = menuItem.find( '.edit-menu-item-title' ).val();
329330

330331
if ( ! excludeMenuItem.includes( menuID ) ) {
331-
if ( currentparentID == menuID ) {
332-
$selected = 'selected';
333-
}
334-
$html += '<option ' + $selected + ' value="' + menuID + '">' + menuTitle + '</option>';
332+
parentDropdown.append(
333+
$( '<option>', {
334+
value: menuID.toString(),
335+
selected: currentParentID === menuID,
336+
text: menuTitle,
337+
} )
338+
);
335339
}
336340
});
337-
338-
parentDropdown.html( $html );
339341
});
340342

341343
});
@@ -349,9 +351,9 @@
349351
var orderDropdown = $( this ),
350352
menuItem = orderDropdown.closest( 'li.menu-item' ).first(),
351353
depth = menuItem.menuItemDepth(),
352-
isPrimaryMenuItem = ( 0 === depth ),
353-
$html = '',
354-
$selected = '';
354+
isPrimaryMenuItem = ( 0 === depth );
355+
356+
orderDropdown.empty();
355357

356358
if ( isPrimaryMenuItem ) {
357359
var primaryItems = $( '.menu-item-depth-0' ),
@@ -360,17 +362,19 @@
360362
itemPosition = primaryItems.index( menuItem ) + 1;
361363

362364
for ( let i = 1; i < totalMenuItems + 1; i++ ) {
363-
$selected = '';
364-
if ( i == itemPosition ) {
365-
$selected = 'selected';
366-
}
367-
var itemString = wp.i18n.sprintf(
365+
var itemString = wp.i18n.sprintf(
368366
/* translators: 1: The current menu item number, 2: The total number of menu items. */
369367
wp.i18n._x( '%1$s of %2$s', 'part of a total number of menu items' ),
370368
i,
371369
totalMenuItems
372370
);
373-
$html += '<option ' + $selected + ' value="' + i + '">' + itemString + '</option>';
371+
orderDropdown.append(
372+
$( '<option>', {
373+
selected: i === itemPosition,
374+
value: i.toString(),
375+
text: itemString,
376+
} )
377+
);
374378
}
375379

376380
} else {
@@ -382,22 +386,22 @@
382386
itemPosition = $( subItems.parents('.menu-item').get().reverse() ).index( menuItem ) + 1;
383387

384388
for ( let i = 1; i < totalSubMenuItems + 1; i++ ) {
385-
$selected = '';
386-
if ( i == itemPosition ) {
387-
$selected = 'selected';
388-
}
389-
var submenuString = wp.i18n.sprintf(
389+
var submenuString = wp.i18n.sprintf(
390390
/* translators: 1: The current submenu item number, 2: The total number of submenu items. */
391391
wp.i18n._x( '%1$s of %2$s', 'part of a total number of menu items' ),
392392
i,
393393
totalSubMenuItems
394394
);
395-
$html += '<option ' + $selected + ' value="' + i + '">' + submenuString + '</option>';
395+
orderDropdown.append(
396+
$( '<option>', {
397+
selected: i === itemPosition,
398+
value: i.toString(),
399+
text: submenuString,
400+
} )
401+
);
396402
}
397403

398404
}
399-
400-
orderDropdown.html( $html );
401405
});
402406

403407
});
@@ -1290,13 +1294,18 @@
12901294
}
12911295

12921296
if ( this.checked === true ) {
1293-
$( '#pending-menu-items-to-delete ul' ).append(
1294-
'<li data-menu-item-id="' + menuItemID + '">' +
1295-
'<span class="pending-menu-item-name">' + menuItemName + '</span> ' +
1296-
'<span class="pending-menu-item-type">(' + menuItemType + ')</span>' +
1297-
'<span class="separator"></span>' +
1298-
'</li>'
1299-
);
1297+
const $li = $( '<li>', { 'data-menu-item-id': menuItemID } );
1298+
$li.append( $( '<span>', {
1299+
'class': 'pending-menu-item-name',
1300+
text: menuItemName
1301+
} ) );
1302+
$li.append( ' ' );
1303+
$li.append( $( '<span>', {
1304+
'class': 'pending-menu-item-type',
1305+
text: '(' + menuItemType + ')',
1306+
} ) );
1307+
$li.append( $( '<span>', { 'class': 'separator' } ) );
1308+
$( '#pending-menu-items-to-delete ul' ).append( $li );
13001309
}
13011310

13021311
$( '#pending-menu-items-to-delete li .separator' ).html( ', ' );
@@ -1699,20 +1708,26 @@
16991708
},
17001709

17011710
eventOnClickMenuSave : function() {
1702-
var locs = '',
1703-
menuName = $('#menu-name'),
1704-
menuNameVal = menuName.val();
1711+
var menuName = $('#menu-name'),
1712+
menuNameVal = menuName.val();
17051713

17061714
// Cancel and warn if invalid menu name.
17071715
if ( ! menuNameVal || ! menuNameVal.replace( /\s+/, '' ) ) {
17081716
menuName.parent().addClass( 'form-invalid' );
17091717
return false;
17101718
}
17111719
// Copy menu theme locations.
1720+
// Note: This appears to be dead code since #nav-menu-theme-locations no longer exists, perhaps removed in r32842.
1721+
var $updateNavMenu = $('#update-nav-menu');
17121722
$('#nav-menu-theme-locations select').each(function() {
1713-
locs += '<input type="hidden" name="' + this.name + '" value="' + $(this).val() + '" />';
1723+
$updateNavMenu.append(
1724+
$( '<input>', {
1725+
type: 'hidden',
1726+
name: this.name,
1727+
value: $( this ).val(),
1728+
} )
1729+
);
17141730
});
1715-
$('#update-nav-menu').append( locs );
17161731
// Update menu item position data.
17171732
api.menuList.find('.menu-item-data-position').val( function(index) { return index + 1; } );
17181733
window.onbeforeunload = null;
@@ -1755,7 +1770,10 @@
17551770
$item;
17561771

17571772
if( ! $items.length ) {
1758-
$('.categorychecklist', panel).html( '<li><p>' + wp.i18n.__( 'No results found.' ) + '</p></li>' );
1773+
const li = $( '<li>' );
1774+
const p = $( '<p>', { text: wp.i18n.__( 'No results found.' ) } );
1775+
li.append( p );
1776+
$('.categorychecklist', panel).empty().append( li );
17591777
$( '.spinner', panel ).removeClass( 'is-active' );
17601778
wrapper.addClass( 'has-no-menu-item' );
17611779
return;

src/js/_enqueues/wp/customize/nav-menus.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,13 @@
529529
return;
530530
}
531531

532-
this.currentMenuControl.addItemToMenu( menu_item.attributes );
532+
// Leave the title as empty to reuse the original title as a placeholder if set.
533+
var nav_menu_item = Object.assign( {}, menu_item.attributes );
534+
if ( nav_menu_item.title === nav_menu_item.original_title ) {
535+
nav_menu_item.title = '';
536+
}
537+
538+
this.currentMenuControl.addItemToMenu( nav_menu_item );
533539

534540
$( menuitemTpl ).find( '.menu-item-handle' ).addClass( 'item-added' );
535541
},
@@ -3136,7 +3142,6 @@
31363142
item,
31373143
{
31383144
nav_menu_term_id: menuControl.params.menu_id,
3139-
original_title: item.title,
31403145
position: position
31413146
}
31423147
);

src/wp-includes/class-wp-customize-nav-menus.php

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,15 @@ public function load_available_items_query( $object_type = 'post_type', $object_
191191
}
192192
} elseif ( 'post' !== $object_name && 0 === $page && $post_type->has_archive ) {
193193
// Add a post type archive link.
194+
$title = $post_type->labels->archives;
194195
$items[] = array(
195-
'id' => $object_name . '-archive',
196-
'title' => $post_type->labels->archives,
197-
'type' => 'post_type_archive',
198-
'type_label' => __( 'Post Type Archive' ),
199-
'object' => $object_name,
200-
'url' => get_post_type_archive_link( $object_name ),
196+
'id' => $object_name . '-archive',
197+
'title' => $title,
198+
'original_title' => $title,
199+
'type' => 'post_type_archive',
200+
'type_label' => __( 'Post Type Archive' ),
201+
'object' => $object_name,
202+
'url' => get_post_type_archive_link( $object_name ),
201203
);
202204
}
203205

@@ -244,14 +246,16 @@ public function load_available_items_query( $object_type = 'post_type', $object_
244246
$post_type_label = implode( ',', $post_states );
245247
}
246248

249+
$title = html_entity_decode( $post_title, ENT_QUOTES, get_bloginfo( 'charset' ) );
247250
$items[] = array(
248-
'id' => "post-{$post->ID}",
249-
'title' => html_entity_decode( $post_title, ENT_QUOTES, get_bloginfo( 'charset' ) ),
250-
'type' => 'post_type',
251-
'type_label' => $post_type_label,
252-
'object' => $post->post_type,
253-
'object_id' => (int) $post->ID,
254-
'url' => get_permalink( (int) $post->ID ),
251+
'id' => "post-{$post->ID}",
252+
'title' => $title,
253+
'original_title' => $title,
254+
'type' => 'post_type',
255+
'type_label' => $post_type_label,
256+
'object' => $post->post_type,
257+
'object_id' => (int) $post->ID,
258+
'url' => get_permalink( (int) $post->ID ),
255259
);
256260
}
257261
} elseif ( 'taxonomy' === $object_type ) {
@@ -276,14 +280,16 @@ public function load_available_items_query( $object_type = 'post_type', $object_
276280
}
277281

278282
foreach ( $terms as $term ) {
283+
$title = html_entity_decode( $term->name, ENT_QUOTES, get_bloginfo( 'charset' ) );
279284
$items[] = array(
280-
'id' => "term-{$term->term_id}",
281-
'title' => html_entity_decode( $term->name, ENT_QUOTES, get_bloginfo( 'charset' ) ),
282-
'type' => 'taxonomy',
283-
'type_label' => get_taxonomy( $term->taxonomy )->labels->singular_name,
284-
'object' => $term->taxonomy,
285-
'object_id' => (int) $term->term_id,
286-
'url' => get_term_link( (int) $term->term_id, $term->taxonomy ),
285+
'id' => "term-{$term->term_id}",
286+
'title' => $title,
287+
'original_title' => $title,
288+
'type' => 'taxonomy',
289+
'type_label' => get_taxonomy( $term->taxonomy )->labels->singular_name,
290+
'object' => $term->taxonomy,
291+
'object_id' => (int) $term->term_id,
292+
'url' => get_term_link( (int) $term->term_id, $term->taxonomy ),
287293
);
288294
}
289295
}

0 commit comments

Comments
 (0)