GSoC Menu Customizer Update: Scalable Menus

Since my last GSoC update, I’ve spent a fair amount of time helping prepare the Customizer for 4.0 beta 1. But I’ve also continued working on the Menu Customizer and have a lot of progress to report.

Add & Delete Menus

You can now add new menus, via the “+ New Menu” section. Added menus currently have some issues, though; you’ll probably need to reload the page before adding items works. The problems stem from the lack of a proper JS API for adding, deleting, and managing Sections and Settings (and Panels), and the incompleteness of the existing Control JS API. This will probably need to be resolved in core before the Menu Customizer can be considered for core integration, see #28709.

I’ve also implemented a menu-deletion mode, which can be toggled from the add-menu section. It’s too easy to delete menus otherwise, even with an AYS confirming the delete, because deleted menus cannot be restored, and are not “previewed” before being published to the db (added menus aren’t either). It’s probably worth augmenting the AYS to state the menu name being deleted, and to add an extra warning if it’s active in a theme location or a widget.

Saving Menus and Menu Item Data in a Scalable Way

In core, menus do not scale well at all. You don’t have to look very deep into the code to see why – massive amounts of data for each item are hidden on the admin screens (much of which never changes) and then must be updated every time a change is made.

Since one of the goals of this project is to experiment with new approaches, I’ve begun implementing a new approach for saving menu data, which is currently in use in the plugin. Thanks to my mentors @ethitter and @obenland for guiding me on the best approach to take here, and @westonruter for the way he implemented the Widget Customizer UI, which inspired this exact approach. Here’s how it works:

  • Each menu has a nav_menu Customizer control that contains an ordered array of numerical menu item ids (known throughout the core menus codebase as their db ids).
  • When an item is added, it is created as an orphaned draft via ajax, and its id is added to the nav_menu setting’s array.
  • When an item is deleted, its id is removed from the nav_menu setting’s array.
  • When menu items are reordered, the order of ids in the nav_menu id is updated to match.
  • When menu items are moved into and out of sub-menus, the parent menu item id is updated in the individual item’s data (not yet implemented).
  • When a menu item field is changed (by default, this would mean changing the label or, for custom items, url fileds; there are screen options for several others), the original item is cloned and the copy is updated with the new data, using a wrapper for wp_update_nav_menu_item() that doesn’t require passing all existing (unchanged) menu item data. The cloned item’s id is returned and replaces the original id in the nav_menu setting (thereby marking the original item for deletion). Additional changes are saved to the cloned item until the settings are saved, at which point all items are marked for a new clone to be created if changes are made (not yet implemented).
  • When the user saves their changes from the Customizer (via the customize_update_nav_menu action), the array of ids is compared to the currently-published menu’s items. If there are items that are no longer present, those are marked for deletion. For each of the new ids, the corresponding menu item (which already exists) is updated to be published, assigned to the corresponding menu (for the new items created as orphaned drafts), and the item’s menu_order is set to the id’s position in the nav_menus setting array. Finally, all of the removed items are deleted.

While menu previewing in the customizer is not yet implemented, it will also be able to use the nav_menu setting’s array of ids to display an augmented set of menu items. I’m also still working on ensuring that menu item data is not posted during the customize-save ajax, but the data isn’t needed so we’re most of the way there already.

UI Aside

customize-header-bigflat-buttons-close

Quick aside: @DrewAPicture pointed out in IRC that the new Customizer close and panel-back icons don’t really match the save button. I’ve done some rough explorations of potential alternatives; if anyone’s interested in discussing them and possibly implementing a change here, feel free to ping me in IRC (@celloexpressions) and/or create a ticket and/or comment here.

Finally, I’m hoping to finish implementing menu previewing by the end of this week, fully utilizing the Customizer. Once this is done, I’ll essentially be at feature-complete stage (other than some little details and several known bugs) and ready to iterate (I’m already planning on working on the add-menu-items backend, as it currently doesn’t scale).

#customize, #gsoc, #gsoc2014, #menu-customizer, #menus