#22070 closed defect (bug) (duplicate)
Deleting menus with no title / slow menu saving
| Reported by: |
|
Owned by: | |
|---|---|---|---|
| Milestone: | Priority: | normal | |
| Severity: | normal | Version: | 3.4 |
| Component: | Menus | Keywords: | |
| Focuses: | Cc: |
Description
Hello there
I saw that when saving nav menus in wp-admin/nav-menus.php WordPress executes the following lines, if a menu item is saved (no matter if newly added or existing) that doesn't have a title:
if ( empty( $_POST['menu-item-title'][$_key] ) ) continue;
I changed this to the following to make it work. Problem is, it needs to be removed from $menu_items, because it's handled like a deleted menu else.
if ( empty( $_POST['menu-item-title'][$_key] ) ) {
if (isset($menu_items[$_POST['menu-item-db-id'][$_key]]))
unset($menu_items[$_POST['menu-item-db-id'][$_key]]);
continue;
}
}
Sidenote:
With that fixed, I was able to solve a major saving bug. Many of our customers have a lot of menu items in out nav_menu (like 100 - 600), which without a timeout would take 10 minutes and more to save.
So I created some hotfix jQuery script that does "dirty-handling". Basically it marks everything that's touched (like changed data, adding a menu item or moving things around) as "dirty". On submit it doesn't nothing more than change every "undirty" menu items title to an empty string so it doesn't get saved.
I think WordPress core developers could integrate something like this with far less code since I wasn't able to use events - I didn't really had a lot of time to fix this. So if it helps anyone, this is the jQuery code I used to create the fix:
jQuery(function($) {
// the currently displayed menu form
var $form = $('#update-nav-menu');
// Listen to new menus, since we have no possibility to hook
// we have to interval it, because we can't capture an event here
$('.submit-add-to-menu').click(function() {
var $item_count = $form.find('.menu-item-handle').length;
var $interval_id = setInterval(function() {
var $current_item_count = $form.find('.menu-item-handle').length;
if ($item_count < $current_item_count) {
clearInterval($interval_id);
// Add the dirty flag and set it to dirty immediately
add_dirty_flags(1);
// Reassign the mousedown/up events
assign_mouse_events();
}
},200);
});
// add a hidden field, telling if the menu is dirty (by default, it's not)
function add_dirty_flags($flag) {
$form.find('.menu-item-handle').each(function() {
if ($(this).find('.dirty-handle').length == 0) {
var $html = '<input type="hidden" class="dirty-handle" value="' + $flag + '" />';
$(this).append($html);
console.log('flag added');
}
});
}
// (re)assigns mouse events to menu items
function assign_mouse_events() {
var $menu_items = $('.menu-item-bar');
// Unbind previously assigned events
$menu_items.unbind('mouseup').unbind('mousedown');
// Dirty Flag handler if a click happens
$menu_items.mousedown(function() {
$(this).find('.dirty-handle').val('1');
});
// if the parent changes on release the mouse handle, change all items with the new parent to dirty
$menu_items.mouseup(function() {
menu_save_mouseup($(this));
});
}
// The call back for mouseup on menu bars
function menu_save_mouseup($this) {
var $temp_object = $this.parent();
// Mark everything with the same parent dirty
setTimeout(function() {
var $parent_id = $temp_object.find('.menu-item-data-parent-id').val();
// Go through all fields, dirtying everything that has the same parent
$('.menu-item-data-parent-id').each(function() {
if ($(this).val() == $parent_id)
$(this).parent().prev().find('.dirty-handle').val('1');
});
},200);
}
// On submit make every undirty menu an empty title, so it won't get saved
// We're using a hoax in nav-menus.php, line 335 here..
$form.submit(function() {
// Now traverse all handles and make only dirties saveable
$form.find('.menu-item-handle').each(function() {
// Find the dirty flag
var $is_dirty = $(this).find('.dirty-handle').val();
$(this).find('.dirty-handle').remove();
if ($is_dirty == 0) {
// The div containing the menu informations
var $forms = $(this).parent().next();
// Remove the title value so it doesn't get saved
$forms.find('.edit-menu-item-title').val('');
}
});
return true;
});
// Assign mouse events on first load
assign_mouse_events();
// Add all dirty flags (not dirty by default of course)
add_dirty_flags(0);
});
Since WordPress now only saves the menu's that changed it doesn't matter how many menu items a customer has, but only how many he want's to save at one time.
Related: #14134, #17031