Fast previewing changes to Menus in the Customizer

The foundation of the Customizer is built on the concept of the “setting”, an abstract representation of anything in WordPress: options, theme mods, posts, terms, and so on. Settings are linked to controls which are responsible for manipulating them. When a setting is changed it gets pushed into the preview window so that the modification can be reviewed before being published live.

There are two ways (transports) for how a setting can be pushed into the preview: refresh and postMessage. The refresh transport does a full page refresh with the modified settings applied, and so it relies on PHP to apply the setting changes. The refresh transport is designed to be compatible with all themes, but for heavy sites refresh can be excruciating slow and taxing on the server. For this reason, the postMessage transport was introduced for previewing changes. This transport uses JavaScript to apply the changes instantly in the preview without any Ajax requests or PHP calls. This is great, but themes have to opt-in to postMessage transport for settings because they must also include JavaScript code which duplicates the logic which is already being executed on the server by PHP. The postMessage transport violates the DRY principle.

With that background out of the way…

Partial Refresh of Menus

In WordPress 4.3 menu management is being added to the Customizer, extending the types of objects that the Customizer can manipulate to include nav_menu taxonomy terms and nav_menu_item posts. We wanted this menu management experience to be fast so it had to avoid the slow full-page refresh transport. Nevertheless, the postMessage transport wasn’t possible to use either since we wouldn’t be able to duplicate all of the PHP menu walker logic and theme/plugin extensions for rendering menus purely in JavaScript. We also wanted to enable fast previewing of menu changes by default. So we implemented a postMessage/refresh hybrid approach which uses postMessage to sync the menus settings to the preview, and then the preview does an Ajax request to render just the contents of the menu container, and this Ajax response is then inserted into the DOM to replace the previous menu container. The technical name for this approach we have been calling “partial refresh”, but you can call it “fast preview”.

When the Customizer determines it can’t do a partial refresh, it falls back to performing a full page refresh. Conditions for why a menu change will get a full page refresh include:

  • if the menu was just assigned to a location for the first time or removed from being assigned to a location, since the theme may have some changes to the layout that it may need to perform;
  • or if wp_nav_menu() is called with with echo being false, since the theme may be doing some string replacement in this case which we can’t replicate in the partial-refresh Ajax request;
  • or if wp_nav_menu() is called with with a non-JSON serializable fallback_cb, such as a closure or a object method—this arg has to be empty or a string;
  • or if wp_nav_menu() is called with with a walker object instance, as opposed to a class name string;
  • or if wp_nav_menu() is called without a theme_location arg and the menu arg is not an term ID or a menu object;
  • or if the menu selected in a Custom Menu widget is changed, since partial refresh for widgets is not yet supported (however, see feature plugin)—subsequent changes to the menu will result in fast partial refreshes if the above conditions

When a menu is being updated via partial refresh, the menu container will receive a classname customize-partial-refreshing, which by default changes the opacity to 25%.

Theme Support

The fast preview for menu changes is being enabled by default so that theme authors don’t have to opt-in to the functionality like they do for instant preview (postMessage transport). In general this should be fine. However, if the theme includes some JavaScript which dynamically manipulates the rendered menu container, such as adding event handlers for expanding/collapsing submenus, then the theme would need to include some JS to re-apply the modifications once the menu is partial-refreshed, as the old DOM for the menu container gets replaced.

The Twenty Fifteen theme actually uses JS to expand/collapse submenu items, so it needed a patch in 4.3 to re-setup a partial-refreshed main nav menu. In addition to firing the menu container DOM setup logic once at jQuery.ready(), it now invokes the functionality whenever the menu is partial-refreshed. See the initMainNavigation() JS function.

So in general, if your theme needs to dynamically initialize menus with JavaScript, the pattern for the initialization code should be:

jQuery(function($) {
	function initMainNavigation( container ) {
		/* set up container... */
	}
	initMainNavigation( $( '.main-navigation' ) );

	$( document ).on( 'customize-preview-menu-refreshed', function( e, params ) {
		if ( 'primary' === params.wpNavMenuArgs.theme_location ) {
			initMainNavigation( params.newContainer );
			/* optionally sync a previous menu state from params.oldContainer... */
		}
	});
});

The params being passed to the event handler consists of the following properties:

  • newContainer: jQuery object containing the new menu container element retrieved from Ajax; this is what you would manipulate to initialize.
  • oldContainer: the previous jQuery object holding the element for the replaced menu container; this is useful if there is any state in the old menu that should persist in the new menu, such as which submenus are expanded (as in Twenty Fifteen).
  • wpNavMenuArgs: The array of arguments passed to wp_nav_menu() in the template, such as template_location.
  • instanceNumber: The index for which wp_nav_menu() call being updated.

The Future

Partial refresh is an “experimental transport” introduced here specifically for menus. In 4.4 we hope to generalize the framework so that any setting can be setup for fast preview, starting with widgets (see #27355). There is a Customize Partial Refresh feature plugin that implements widget partial refresh in a beta state. This partial refresh functionality I’ve also identified as being critical for Customizer Transactions.

#4-3, #customize, #dev-notes, #feature-plugins, #feature-selective-refresh, #kickoff, #menu-customizer, #menus, #partial-refresh, #proposal

Feature Plugin Merge Proposal: Menu Customizer

The Customizer team is proposing to merge the Menu Customizer plugin into core for WordPress 4.3. In this post, I’ll outline the purpose and history of this project, as well as highlighting the improvements that we have made.

Purpose & Goals

The purpose of the Menu Customizer project is to move navigation menu management from the WordPress admin to the Customizer. In the process, we hope to offer an updated design with improved user flow, a mobile-first interface, improved accessibility, rebuild the administration UI from the ground up to be JavaScript-driven, solve long-standing problems with the current implementation (#14134), and clarify the purposes and capabilities of the menus feature. Additionally, Menu Customizer contributes significantly to the long-term goal to move all appearance functionality and, really, everything that could benefit from live previewing, from the admin to the Customizer.

Background

Menu Customizer started out as my Google Summer of Code 2014 Project. The initial proposal and revised schedule highlight the initial goals and provide good perspective for where we’ve come in the past year. See also the periodic posts here on make/core for updates. Development has happened on GitHub since the project opened to the community.

Core API Improvements

As I began developing this feature in plugin form, it became clear that the core Customizer API would need a lot of improvements to support something as complex as menus. Countless tickets have worked towards this goal over the past year, from the addition of the concept of Panels ( #27406) to JS/Underscore-templated controls ( #29572), and now, full support for dynamically-added sections and controls ( #30737, #30738, and #30741).

Developers are still realizing the full potential of the Customizer API, and Menu Customizer pushes the boundaries of what can be done here pretty far. One of the goals with our approach is to bring as much functionality that should be natively available in the API into core as possible. With the improvements made here already, as well as the future potential to continue abstracting functionality like the add-menu-items fly-out panel or the ability to add screen options in Customizer Panels, Menu Customizer broadens the potential for developers to extend the Customizer to do anything, in core, plugins, and themes.

Contributors

In the initial GSoC project, I (@celloexpressions) developed the plugin from scratch, using Widgets in the Customizer as the design basis, with @ethitter and @obenland serving as my mentors. When the project was opened to the community for contributions, several designers and developers stepped up to help. Code contributors to date include @westonruter, @valendesigns, @voldemortensen, @adamsilverstein, and @kucrut. @designsimply and @folletto have also put in a tremendous amount of time helping with design and usability.

Plugin Overview

I highly recommend trying the plugin, which currently requires the latest version of 4.3 alpha. @designsimply has prepared a video demo:

[wpvideo xdKZ9CeM]

I’ve posted a comparison of the mobile menus flow in the admin and the Customizer on make/flow, and @designsimply has also posted flows there (more flows with more recent versions of the plugin on trunk are still needed). Usability testing has been conducted on usertesting.com, with results posted on make/design. As further refinements are made, additional testing and feedback can be incorporated to make the new experience as polished as possible.

A couple of technical details: each menu is a Customizer section, and new menus can be added (dynamically adding new Customizer sections and controls in the process). Menu items are Customizer controls. To maximize scalability, menu items are all rendered using a single JS template, only when their containing menu section is expanded. The add-menu-item panel loads available menu items on an as-needed basis via ajax. The plugin uses several custom Customizer objects including a custom panel that implements screen options, two custom sections (menus, for lazy-loading of menu items, and new menus, which is rendered as a button toggle), and several custom controls. But everything is built off of the core Customizer API.

A summary of some key improvements that the plugin includes:

  • Modernized, simplified, and more compact UI
  • Mobile-first design that leverages the Customizer
  • Scalable, JavaScript-driven and avoids performance issues ( #14134)
  • All menus easily accessible in one place, without page reloads
  • Live previews of active menus as they are edited
  • Menu locations that can be set from the main panel or while editing
  • Global search that includes all post types and terms in all taxonomies
  • Quick-delete for deleting several items sequentially
  • “Original” item links open directly into the live preview
  • The Customizer API can be used to hook into the experience in countless ways with plugins. Support for additional menu item fields can be added much more easily now in a future release, potentially leveraging the Fields API

Core Tickets Fixed

Menu Customizer fixes numerous tickets on core trac. This is not an exhaustive list, but covers many bigger ones:

  • #14134: Menus item are limited to 16 item and will not save more than that
  • #28138: Updating menu item requires passing all of a menu item’s data to wp_update_nav_menu_item() (The plugin steps around this, we can actually fix it in core on merge)
  • #32218: Remove title attribute option in Menu Editor (off by default)
  • #19272: Add Filter to Nav Menu Support Themes Text (can modify via Customizer API)
  • #21603: Add ability to delete multiple menu items
  • #16828: Add filter on initial_meta_boxes for nav menu Probably fixed, all are shown currently, which could use improvement but it will default to more being shown at least
  • #19464: Auto add do_action for menu in admin (can use Customizer API)
  • #31391: Make the list of registered nav menus (locations) filterable (can use Customizer API)
  • #32440: on Menu page, turn posts by default on “view options”
  • #18517: Visual Feedback for Nav Menu UI

The Plan for the Menus Component

This project has a very explicit goal of not just adding menu management to the Customizer, but also removing the existing admin page in the process. The menu management screen has significant, fundamental problems in its implementation and will never scale (see #14134) without a significant refactoring along the lines of what we’ve done with the Customizer. Additionally, the new UI in the Customizer is considerably more polished than the admin screen and already includes numerous features and bugfixes proposed for core (see above). Ultimately, the new UI provides a much better experience for all users, including desktop, mobile, accessibility, etc.

The plan for the “removal” of the old menus admin screen is as follows:

  • Immediately and officially “deprecate” it: wind down any ongoing development efforts and focus all new administration-focused Menus component work on the new UI in the Customizer. Update trac tickets accordingly, and add a “Manage in Customize” link to the existing screen. Any existing tickets proposing enhancements or new features for menu administration would be required to be adapted to the Customizer version, with the (discouraged) option of also making changes to the old screen.
  • Point the “Menus” link in the admin bar to Menus in the Customizer in 4.3. Remove that menu from the admin bar in 4.4 in favor of a top-level Customize link, and put something more useful in its place (as all of its core links will point to the Customizer now).
  • Retain the admin screen codebase, along with existing links to it throughout the admin.
  • In WordPress 4.5 or 4.6, remove all core links (including admin menu) to the Menus admin screen, or point them to the Customizer. This would likely coincide with a similar change for Widgets and Themes to use the Customizer versions exclusively, once full feature-parity is achieved with the Customizer versions of the other features (Menus has it now). At this point the admin screen would be accessible only by plugin-added links or for users who cannot access the Customizer (no-js, IE7, IE8&9 with domain mapping, a very small percentage of users overall).
  • The admin screen and related code would likely not be removed entirely from core in the foreseeable future, and critical bugs or security issues would still be addressed. New feature development and enhancements would be restricted to the Customizer version.

The above plan is fairly aggressive, to eliminate any ambiguity about future plans and intentions and to avoid the potential for mass trac ticket rot. The fact that the Menus component has no maintainers and has not received significant attention since the 3.6 release indicates that there is a general lack of developer interest in dealing with the mess that the current system is. I am willing to step up as a component maintainer for Menus if the above plan is implemented.

Ongoing Work

We have a few issues left that work working on. Notably, @westonruter has proposed refactoring the way menu item settings are handled, along with menu creation and deleted, and has begun work there, but wouldn’t finish until after a core merge due to time constraints and integration with core code. @adamsilverstein is working on improving drag & drop to work with sub menus. There are also several minor issues remaining on GitHub, which would either be handled in the next couplle days or after merge (many issues have been punted to after a potential merge already).

#customize, #feature-plugins, #menu-customizer, #menus, #merge, #proposal

Customize Component Update – 5/25/15

The Customizer team has been busy working on some major user-facing and behind-the-scenes changes targeting WordPress 4.3. This update will provide an overview of our goals, current status, and pathways for contributors to get involved.

We’re planning on holding an extra meeting in #core-customize at 5pm PDT today, 5/25, (May 26, 00:00 UTC) to discuss progress and assignments/plans for the coming week-plus before the feature-plugin merge window opens. This time won’t work for our European contributors but hopefully more of us can make it than can make the old Friday time. (cc @westonruter, @valendesigns, @voldemortensen, @ocean90, @folletto).

Redesigned Customizer Flow/UX

Customizer mobile flow redesign walkthrough gif

New Customizer flow introduced in #31336, on a mobile viewport with a recent (not latest) patch.

#31336 introduces a re-imagined Customizer experience that clarifies the flow, distinguishes navigation from control UI, and improves design through careful adjustments to colors, contrast, and spacing. The primary changes are centered around removing the accordion UI in favor of panel-like horizontal sliding for sections, allowing us to introduce consistent headers across panels and sections to build on the work that was started in 4.2 with the new themes section. Design work for this change was started at the end of 4.0 last August, and @folletto has continued refining the flows since then while I’ve done most of the patching work.

At this stage, we’re trying to get a first-pass patch committed as soon as possible as it’s holding up some of our other priorities including #30737 and Menu Customizer work. We need help testing the patch on different browsers & devices, as well as for any further usability feedback. Once the initial patch is committed, it will be easier to contribute via additional smaller patches.

In addition to this broad UI/UX change, we have several other proposed UI changes being considered for 4.3. Icons for Customizer sections and panels are ready to go pending feedback ( #28504). If time allows and there is enough interest, we could also potentially introduce an improved collapsed mode ( #29949) and device-size preview for the Customizer preview ( #31195) in 4.3. We may also do a full accessibility audit and make fixes accordingly later in the release, per recent discussion on #31336.

Menu Customizer

I just pushed version 0.4, which includes:

  • In-Customizer previewing of “original” menu item links
  • A redesign of the screen options popup
  • The addition of theme location checkboxes within menu sections (which fully cross-sync with the menu locations section)
  • Theme locations listed within menu section names
  • Menus can only be deleted when they aren’t assigned to any locations
  • Available menu item searching, and a first-pass redesign of the available menu items panel to match the new look for core in #31336 (more to come here)
  • Integration of the existing theme location controls into the plugin
  • Also notable, I compiled a list of ~10 (and counting) core tickets that Menu Customizer fixes; most notably, #14134

Big thanks to @valendesigns, @folletto, and @voldemortensen for helping out with these changes. We have several major issues to fix still, so please jump in on GitHub if you’re interested in helping out.

Now is also the time for an extensive review process to begin on all fronts, so that we can address any issues in time to make core merge in just a few short weeks. We need several kinds of feedback:

  • General usability feedback
  • Usability testing
  • Accessibility review/feedback
  • Design/UI/UX feedback (test with #31336)
  • Inline documentation review (should be mostly there, but needs a thorough review and cleanup)
  • Code review/cleanup
  • Lead dev/committer review for additional issues

Note that testing should be done with the Menu Customizer plugin and WordPress 4.3 alpha, and that the patch on #31336 is currently a dependency for everything design-related. The plugin can be tested on 4.2 right now, but some pieces of the UI may be a bit off, and we’ll be breaking 4.2-compat in the near future as critical dependencies are merged into core. The most urgent dependencies are #31336 and #30737 (both of which require updates in the plugin and prevent key functionality from working). #30738 and #30741 would be really nice to have, but we can more comfortably hack around those issues at the time of core merge if we have to.

Partial Refresh

@westonruter has finished integrating partial refresh into Menu Customizer (it already worked for widgets), and is now abstracting it one layer further to be available for general use via the API. Partial refresh is an in-between option for setting previewing between doing a full refresh of the preview and fully implementing setting changes in JS via postMessage; settings are previewed by refreshing only part of the preview window.

Other Updates

We’ve made some changes to our component page. Customize is no longer a sub-component of Themes (note the URL change). I also added a summary of our major projects over the past several releases, as well as a brief roadmap/wishlist for future-release projects, and a few routes for getting involved in contributing. @valendesigns and I also triaged the roughly-20 tickets needed a response yesterday, as well as triaging a good chunk of the enhancement tickets in the component that haven’t had activity in more then 4 months. @valendesigns has also stepped up as another component maintainer, joining @westonruter, @voldemortensen, and myself.

#4-3, #customize, #menu-customizer, #menus

Customizer Chat Summary

Priorities for Customizer development in 4.3:
1) Customizer Concurrency (aka Control Locking)
2) Partial Refresh
3) Menu Customizer
4) Theme Installation
5) UI/UX changes

Customizer Concurrency doesn’t need much Core changes to implement (except for temp hooks maybe), so it makes sense to remain as a plugin, living here: https://github.com/xwp/wp-customize-widgets-plus
@westonruter aims to have beta ready by end of this week.

Partial Refresh is available as a feature plugin and can be tried out now: https://wordpress.org/plugins/customize-partial-refresh/
There are a couple issues still needed for that one and feedback on the API for opt-in.

Menu Customizer will be picked back up to have that ready ASAP for merge review (probably needs about a month). @voldemortensen, @valendesigns, @westonruter, and @celloexpressions will all work on dev, as well as anyone else who wants to jump in. @sheri will get started with design review, possibly with help from others (@folletto). The plugin currently works with WordPress 4.1 or 4.2. We can push lots of testing to start happening now since it’s pretty much functionally complete. It just needs a lot of back-end improvements.

Theme Installation and UI/UX changes were not discussed in more detail and will be worked on as time allows after completing the first three items.

Slack logs.

#4-3, #customize, #menu-customizer, #menus

Menu Customizer: Call for Contributors

After a few months off from working on the Menu Customizer to focus on improving the Customizer API in core, I’m starting to pick up development on the feature-plugin. Now that it’s approaching a reasonably usable state, and is compatible with the latest major release of WordPress (4.1), I’d like to begin efforts to see if we can propose merging it into core for WordPress 4.2.

But there is a lot of work to be done. When Menu Customizer was my GSoC project, it was closed to contributors per GSoC rules. But development is now open to everyone, and I could use a lot of help with both development and non-development tasks. Here’s a list of items that need work:

  •  Development
    • Build-out the core API for adding Customizer sections and controls entirely with JavaScript, #30741 and its related tickets (PHP, JS)
    • Drag & Drop menu item reordering needs to do sub-menus (code imported from nav-menus.php is commented out in menu-customizer.js currently) (JS)
    • Fix problems with previewing updates to menu items, and with previewing newly-added menus once items are added (JS)
    • Eliminate the PHP closure that currently facilitates menu previewing, for PHP 5.2 compatibility (PHP)
    • Redo the add-menu-items “panel” to lazy-load its contents & utilize Backbone sub-views (PHP, JS)
    • Improve the core Customizer on mobile, then make Menu Customizer work on mobile (CSS)
    • Think about an API or otherwise action hooks to allow plugins to add menu item fields, #27066, #21898, #18584, etc. (PHP)
    • Inline docs audit, once we’re mostly done (PHP, JS)
    • Comprehensive code review by people like @westonruter, @ocean90, or @nacin, once we’re mostly “done”, preferably before a core merge proposal. Initial code review/cleanup from anyone can start now
  • Design
    • Overall UI audit/review, propose changes
    • Consider things like #29158 in relation to how the menus UI looks
    • Discuss approach to screen options (currently an icon in the Menus panel header)
    • UX audit, propose changes
    • Evaluate user flows & menus use-cases
    • Conduct user tests
  • Other
    • General user feedback – getting the word out about the plugin and collecting feedback (reviews & support forms on the .org repo would be a good place for feedback). Anyone reading this can try the plugin and provide feedback too 🙂
    • Accessibility audit
    • Backwards-compatibility audit; in particular, assessing whether Menu Customizer could replace the Menus admin screen, and what further features or use-cases would need to be addressed to do so
    • Research the history of the Menus UI in core and document how Menu Customizer addresses ongoing concerns; also consider open tickets in the Menus component (for merge proposal)

Development is happening on the WordPress.org plugins repo the a GitHub repo. Some helpful links: create a ticket, Menu Customizer tickets, development log. It’ll probably also be possible to contribute via Github if that’s your preference – talk to @westonruter about how he does it.

This project will primarily take place over the next month, when core development is largely on hold for the holidays and between releases, and when I’m in between semesters at school. The goal is to be merge-ready before the 4.2 feature-plugin merge consideration happens in January. If you’re interested in helping out, please comment on this post or ping me in WordPress Slack in #core (@celloexpressions).

Due to the timing of this project around the holidays, we’ll probably do mostly asynchronous communication, but I would like to try a kick-off meeting in #core Slack on Monday, December 22, 2014 18:00 UTC; please come by if you’re interested!

#customize, #menu-customizer, #menus

JS/Underscore-template-rendered Custom Customizer Controls in WordPress 4.1

The Customizer is a JavaScript-driven feature of WordPress core, but until recently, most of the APIs for extending it in themes and plugins were PHP-oriented. In WordPress 4.1, we’re introducing more complete JS models for the different UI objects that comprise the Customizer. In the process, all controls are now placed into the DOM with JavaScript, rather than being output directly in PHP.

At the same time, we’ve been working on issues of scalability and performance. In particular, bringing the navigation menu management experience into the Customizer has highlighted several areas with room for improvement. With menus, each menu item is a Customizer control with several fields, so a site with hundreds of menu items across different menus will end up sending a lot of repetitive HTML down from PHP, and we currently have to send the full markup for a menu item control down from the server when adding menu items in an Ajax call.

#29572 offered a solution to these challenges: an optional API that allows Customizer controls to be written as JavaScript templates. Rather than populating a control’s container with markup rendered on the server and obtained via an Ajax call, we can now use JS templates to render these controls on the client without any server-side call. In the future, new controls could be added dynamically (lazy-loaded, #28580) by leveraging the control-type templates already loaded in the Customizer.

In the remainder of this post, I’ll walk through how to use this API, its benefits, and example use-cases that are already benefiting WordPress core in 4.1.

Registered Control Types

In order to introduce a concept of having one template for multiple Customizer controls of the same type, we needed to introduce a way to register a type of control with the Customize Manager. Previously, custom control objects were only encountered when custom controls were added using WP_Customize_Manager::add_control(). But detecting added control types to render one template per type wouldn’t allow new controls to be created dynamically if no other instances of that type were loaded. So we’ve introduced WP_Customize_Manager::register_control_type(). Usage is simple:

add_action( 'customize_register', '29527_customize_register' );
function 29527_customize_register( $wp_customize ) {
	// Define a custom control class, WP_Customize_Custom_Control.
	// Register the class so that it's JS template is available in the Customizer.
	$wp_customize->register_control_type( 'WP_Customize_Custom_Control' );
}

All registered control types will have their templates printed to the Customizer by WP_Customize_Manager::print_control_templates().

Sending PHP Control Data to JavaScript

While Customizer control data has always been passed to the control JS models, and this has always been able to be extended, you’re much more likely to need to send data down when working with JS templates. Anything that you would want access to in render_content() in PHP will need to be exported to JavaScript to be accessible in your control template. WP_Customize_Control exports the following control class variables by default:

  • type
  • label
  • description
  • active (boolean state)

You can add additional parameters specific to your custom control by overriding WP_Customize_Control::to_json() in your custom control subclass. In most cases, you’ll want to call the parent class’ to_json method also, to ensure that all core variables are exported as well. Here’s an example from the core color control:

public function to_json() {
	parent::to_json();
	$this->json['statuses'] = $this->statuses;
	$this->json['defaultValue'] = $this->setting->default;
}

JS/Underscore Templating, + examples

Once you’ve registered your custom control class as a control type and exported any custom class variables, you can create the template that will render the control UI. You’ll override WP_Customize_Control::content_template() (empty by default) as a replacement for WP_Customize_Control::render_content(). Render content is still called, so be sure to override it with an empty function in your subclass as well.

Underscore-style custom control templates are very similar to PHP. As more and more of WordPress core becomes JavaScript-driven, these templates are becoming increasingly more common. Some sample template code in core can be found in media, revisions, the theme browser, and even in the Twenty Fifteen theme, where a JS template is used to both save the color scheme data and instantly preview color scheme changes in the Customizer. The best way to learn how these templates work is to study similar code in core and, accordingly, I’ll briefly explain an example here now.

class WP_Customize_Color_Control extends WP_Customize_Control {
	public $type = 'color';
// ...
	/**
	 * Render a JS template for the content of the color picker control.
	 */
	public function content_template() {
		?>
		<# var defaultValue = '';
		if ( data.defaultValue ) {
			if ( '#' !== data.defaultValue.substring( 0, 1 ) ) {
				defaultValue = '#' + data.defaultValue;
			} else {
				defaultValue = data.defaultValue;
			}
			defaultValue = ' data-default-color=' + defaultValue; // Quotes added automatically.
		} #>
		<label>
			<# if ( data.label ) { #>
				<span class="customize-control-title">{{{ data.label }}}</span>
			<# } #>
			<# if ( data.description ) { #>
				<span class="description customize-control-description">{{{ data.description }}}</span>
			<# } #>
			<div class="customize-control-content">
				<input class="color-picker-hex" type="text" maxlength="7" placeholder="<?php esc_attr_e( 'Hex Value' ); ?>" {{ defaultValue }} />
			</div>
		</label>
		<?php
	}
}

In the above template for the core custom color control, you can see that after the closing PHP tag, we have a JS template. <# #> notation is used around statements to be evaluated – in most cases, this is used for conditionals. All of the control instance data that we exported to JS is stored in the `data` object, and we can print a variable using double or triple brace notation {{ }}. As I said before, the best way to get the hang of writing controls like this is to read through existing examples. WP_Customize_Upload_Control was recently updated to leverage this API as well, integrating nicely with the way the media manager is implemented, and squeezing a ton of functionality out of a minimal amount of code. If you want some really good practice, try converting some of the other core controls to use this API – and submit patches to core too, of course!

Working with Controls in JavaScript

The Customizer has always had an API for working with controls in JavaScript. Now that the Customizer supports JS-rendered controls, this API will be even more useful, as you can do things like re-rendering the entire control if its data changes significantly (think media attachment previewing, for example), rather than doing direct DOM manipulation. Again, the core code is the best place to start getting a feel for this API, but it essentially works like subclasses do in PHP. See @westonruter‘s post for details on how this side of the API has evolved in 4.1, and take a look at the control-related models in wp-admin/js/customize-controls.js.

Putting the pieces together

Here’s a summary of what’s needed to leverage the new API in a custom Customizer control subclass:

  1. Make your render_content() function empty (but it does need to exist to override the default one).
  2. Create a new function, content_template(), and put the old contents of render_content() there.
  3. Add any custom class variables that are needed for the control to be exported to the JavaScript in the browser (the JSON data) by modifying the to_json() function (see WP_Customize_Color_Control for an example).
  4. Convert the PHP from render_content() into a JS template, using <# #> around JS statements to evaluate and {{ }} around variables to print. PHP class variables are available in the data object; for example, the label can be printed with {{ data.label }}.
  5. Register the custom control class/type. This critical step tells the Customizer to print the template for this control. This is distinct from just printing templates for all controls that were added because the ideas are that many instances of this control type could be rendered from one template, and that any registered control types would be available for dynamic control-creation in the future. Just do something like $wp_customize->register_control_type( 'WP_Customize_Color_Control' );.

The PHP-only parts of the API are still fully supported and perfectly fine to use. But, with WordPress 4.0’s decreased need for custom controls, and given our long term goals for making the Customizer more flexible for doing things like switching themes in the Customizer without a pageload, I strongly encourage using this new API for all custom Customizer controls where feasible.

#4-1, #customize, #dev-notes, #menu-customizer, #menus

Toward a Complete JavaScript API for the Customizer

The Customizer is the first true JS-driven feature in core. That’s awesome, especially coming out of WCSF where JavaScript has been highlighted so prominently between Backbone.js, the WP REST API, Node.js, and socket.io. The Customizer has a great architecture with models for settings, controls, watchable-values, collections, events, and asynchronous callbacks. Nevertheless, the JavaScript API in the Customizer is incomplete.

Existing Challenges

When widgets were added to the Customizer in 3.9, adding widget controls to sidebar sections required direct DOM manipulation. For controls there is at least a Control model to manage state For sections and panels, however, there are no JS models at all, and so adding them dynamically is even more of a challenge. And this is the exact challenge that Nick Halsey’s Menu Customizer plugin currently has to struggle through.

When Customizer panels were added in 4.0 to group all widget area sections into a “Widgets” panel, a bug was introduced whereby shift-clicking a widget in the preview no longer revealed the widget control in the Customizer pane because the sections were inside of the collapsed Widgets panel: there were no models to represent the state for whether or not a panel or section were currently expanded. Without models, a fix of this would require more messy DOM traversal to check if parent accordion sections were expanded or not. Storing data in the DOM is bad.

In 4.0 the concept of contextual controls were added to the Customizer. This allowed controls to be registered with an active_callback, such as is_front_page, which the preview would execute and pass back up to the Customizer pane to then show/hide the control based on which URL was being previewed. This worked well, except when all controls in a section were inactive: then the result was a Customizer section without any visible controls. Instead, the expected behavior would be for the section to automatically collapse as well when all controls inside become inactive. Again, this problem stems from the fact that there is no JS model to represent a section and to list out the controls associated with it.

For the past three weeks I’ve been focused on fleshing out the Customizer API to address these challenges, and to facilitate doing new dynamic things in the Customizer. The parent ticket for this is #28709: Improve/introduce Customizer JS models for Controls, Sections, and Panels.

Models for Panels and Sections

As noted above, there is a wp.customize.Control model, and then there is a wp.customize.control collection (yes, it is singular) to store all control instances. So to follow the pattern established by controls, in the patch there is a wp.customize.Panel and wp.customize.Section, along with wp.customize.panel and wp.customize.section collections (both singular again). So just as with controls, you can iterate over panels and sections via:

wp.customize.panel.each( function ( panel ) { /* ... */ } );
wp.customize.section.each( function ( section ) { /* ... */ } );

Relating Controls, Sections, and Panels together

When registering a new control in PHP, you pass in the parent section ID:

$wp_customize->add_control( 'blogname', array(
	'label' => __( 'Site Title' ),
	'section' => 'title_tagline',
) );

In the proposed JavaScript API, a control’s section can be obtained predictably:

id = wp.customize.control( 'blogname' ).section(); // => title_tagline

To get the section object from the ID, you just look up the section by the ID as normal: wp.customize.section( id ).

You can move a control to another section using this section state as well, here moving it to the Navigation section:

wp.customize.control( 'blogname' ).section( 'nav' );

Likewise, you can get a section’s panel ID in the same way:

id = wp.customize.section( 'sidebar-widgets-sidebar-1' ).panel(); // => widgets

You can go the other way as well, to get the children of panels and sections:

sections = wp.customize.panel( 'widgets' ).sections();
controls = wp.customize.section( 'title_tagline' ).controls();

You can use these to move all controls from one section to another:

_.each( wp.customize.section( 'title_tagline' ).controls(), function ( control ) {
	control.section( 'nav' );
});

Contextual Panels and Sections

Also just as with controls, when you invoke $wp_customize->add_section() you can pass an active_callback param to indicate whether the section is relevant to the currently-previewed URL; the same goes for panels. A good example of a contextual section is only showing the “Static Front Page” section if the preview is currently on the front-page:

function contextual_static_front_page_section( $wp_customize ) {
	$wp_customize->get_section( 'static_front_page' )->active_callback = 'is_front_page';
}
add_action( 'customize_register', 'contextual_static_front_page_section', 11 );

Nevertheless, this will not usually be needed because a section inherits its active state from its control children (and a panel inherits from its section children), via the new isContextuallyActive() method. If all controls within a section become inactive, then the section will automatically become inactive.

As with controls, Panel and Section instances have an active state (a wp.customize.Value instance). When the active state changes, the panel, section, and control instances invoke their respective onChangeActive method, which by default slides the container element up and down, if false and true respectively. There are also activate() and deactivate() methods now for manipulating this active state, for panels, sections, and controls:

wp.customize.section( 'nav' ).deactivate(); // slide up
wp.customize.section( 'nav' ).activate({ duration: 1000 }); // slide down slowly
wp.customize.section( 'colors' ).deactivate({ duration: 0 }); // hide immediately
wp.customize.section( 'nav' ).deactivate({ completeCallback: function () {
	wp.customize.section( 'colors' ).activate(); // show after nav hides completely
} });

Note that manually changing the active state would only stick until the preview refreshes or loads another URL. The activate()/deactivate() methods are designed to follow the pattern of the new expanded state.

Expanded State

As noted above, in 4.0 when panels were introduced, a bug was introduced whereby shift-clicking a widget in the preview fails to show the widget control if the Widgets panel is not already open. With the proposed changes, panels, sections, and (widget) controls have an expanded state (another wp.customize.Value instance). When the state changes, the onChangeExpanded method is called which by will handle Panels sliding in and out, and sections sliding up and down (and widget controls up and down, as they are like sections). So now when a widget control needs to be shown, the control’s section and panel can simply have their expanded state to true in order to reveal the control. Expanding a section automatically expands its parent panel. Expanding a widget control, automatically expands its containing section and that section’s panel.

As with activate()/deactivate() to manage the active state, there are expand() and collapse() methods to manage the expanded state. These methods also take a similar params object, including duration and completeCallback. The params object for Section.expand() accepts an additional parameter “allowMultiple” to facilitate dragging widget controls between sidebar sections. By default expanding one section will automatically collapse all other open sections, and so this param overrides that. You can use this, for instance, to expand all sections at once so you can see all controls without having to click to reveal each accordion section one by one:

wp.customize.section.each(function ( section ) {
	if ( ! section.panel() ) {
		section.expand({ allowMultiple: true });
	}
});

Focusing

Building upon the expand()/collapse() methods for panels, sections, and controls, these models also support a focus() method which not only expands all of the necessary element, but also scrolls the target container into view and puts the browser focus on the first focusable element in the container. For instance, to expand the “Static Front Page” section and focus on select dropdown for the “Front page”:

wp.customize.control( 'page_on_front' ).focus()

This naturally fixes the #29529, mentioned above.

The focus functionality is used to implement autofocus: deep-linking to panels, sections, and controls inside of the customizer. Consider these URLs:

  • …/wp-admin/customize.php?autofocus[panel]=widgets
  • …/wp-admin/customize.php?autofocus[section]=colors
  • …/wp-admin/customize.php?autofocus[control]=blogname

This can be used to add a link on the widgets admin page to link directly to the widgets panel within the Customizer.

Priorities

When registering a panel, section, or control in PHP, you can supply a priority parameter. This value is now stored in a wp.customize.Value instance for each respective Panel, Section, and Control instance. For example, you can obtain the priority for the widgets panel via:

priority = wp.customize.panel( 'widgets' ).priority(); // => 110

You can then dynamically change the priority and the Customizer panel will automatically re-arrange to reflect the new priorities:

wp.customize.panel( 'widgets' ).priority( 1 ); // move Widgets to the top

Custom types for Panels and Sections

Just as Customizer controls can have custom types (ColorControlImageControlHeaderControl…) which have custom behaviors in JS:

wp.customize.controlConstructor.FooControl = wp.customize.Control.extend({ /*...*/ });

So too can Panels and Sections have custom behaviors in the proposed changes. A type parameter can be passed when creating a Panel or Section, and then in JavaScript a constructor corresponding to that type can be registered. For instance:

PHP:

add_action( 'customize_register', function ( $wp_customize ) {
	class WP_Customize_Harlem_Shake_Section extends WP_Customize_Section {
		public $type = 'HarlemShake';
	}
	$section = new WP_Customize_Harlem_Shake_Section(
		$wp_customize,
		'harlem_shake',
		array( 'title' => __( 'Harlem Shake' ) )
	);
	$wp_customize->add_section( $section );
	$wp_customize->add_setting( 'harlem_shake_countdown', array(
		'default' => 15,
	));
	$wp_customize->add_control( 'harlem_shake_countdown', array(
		'label' => __( 'Countdown' ),
		'section' => 'harlem_shake',
		'setting' => 'harlem_shake_countdown',
		'type' => 'number',
	));
});

JS:

wp.customize.sectionConstructor.HarlemShake = wp.customize.Section.extend({
	shake: function () {
		// This can be invoked via wp.customize.section( 'harlem_shake' ).shake();
		console.info( 'SHAKE!!' );
	}
});

Next Steps

  • Continue discussion on parent ticket #28709: Improve/introduce Customizer JS models for Controls, Sections, and Panels.
  • Review JavaScript changes in pull request. Anyone is free to open a PR to onto the existing branch on GitHub to make changes. Props to Ryan Kienstra (@ryankienstra) and Nick Halsey (@celloexpressions) for their contributions.
  • Update logic for adding widget controls to use new API (adding widgets is using the old pseudo-API and it is currently broken); allow controls to be added manually.
  • Work with Nick Halsey to make sure that dynamically-created sections and controls suit the needs of Menu Customizer, and make sure that it works for other plugins like Customize Posts.
  • Build upon the initial QUnit tests to add coverage for existing JS API and newly added API (#28579).
  • Harden the logic for animating the Customizer panel into view.
  • Get feedback from other Core devs and get initial patch committed.

Thanks to Nick Halsey (@celloexpressions) for his proofreading and feedback on the drafts of this blog post.

#customize, #menu-customizer, #menus, #widget-customizer

GSoC Menu Customizer Update: Live-previewing Menus

I’ve finished implementing menu-previewing in the Menu Customizer plugin, building on the scalable approach to saving menus that I discussed in my last update. The entire Menus experience is now consolidated into a Menus panel in the Customizer, where you can preview your menus as you make changes. It’s really nice to have Menus easily accessible alongside the rest of the site-appearance-management tools in the Customizer.

I only have about a week and a half left in my GSoC project, and I’m hoping to focus on improving the add-new-menu-item panel in my remaining time, making it scale and implementing the search functionality. I’m also planning on cleaning up the codebase and implementing the ability to manage menu hierarchy (submenus).

If you’re interested in testing the Menu Customizer, and live-previewing changes to your menus, you can get the plugin here. Please note that it currently requires PHP 5.3+, but it’s getting less and less alpha by the day.

Post-GSoC Plans

After the GSoC coding period is over, I’m planning on transitioning Menu Customizer development to the feature-plugin format, gathering a group of interested contributors and holding weekly meetings to coordinate our efforts. While it won’t be ready for core consideration by 4.1, and requires some more core Customizer infrastructure to really work well, we’ll continue working on the plugin until menus in the Customizer really shine, and are ready for core.

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

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

GSoC Menu Customizer Update

Since this is my first post here, a quick introduction. I’m a student at the University of Southern California studying Civil Engineering, Architecture, and Music Composition. I’ve been contributing to WordPress Core for just over a year and this summer I’m pleased to be working on WordPress full-time for my Google Summer of Code project.

Overview

The goal of the Menu Customizer project is to add Custom Menu management to the Customizer. Ideally, the project should be able to replace the existing Menus screen, with full feature parity, but that’s obviously a bigger discussion that would take place later. For more details, check out my initial proposal.

Current Status

I started six weeks ago and have built out most of the plugin’s UI and structure. However, I still need to build the menu-item previewing and saving components of the project. The UI closely resembles the Widgets-in-customizer UI, with sections for each menu and controls for each item. New menu items are added via a slide-out panel, where they’re currently grouped by post type/taxonomy, custom links, and a global search feature. The existing “Navigation” Customizer section has been re-branded to “Theme Locations,” and emphasizes the ability to add menus to widgets. Development is being done on the plugin repo, and you can download and play with it from there, but note that adding items creates orphaned draft menu items that are never published currently. Here’s a demo of the current plugin:

(If the embedded video doesn’t play for you, try this link: https://cloudup.com/cVJbk3u32QV)

The add-menu-item UI and implementation will be getting a lot of attention throughout the rest of my project. Items are added immediately, rather than the existing two-step checkboxes and adding several at once process, and menu items can now be deleted without having to open their settings, making deletion and addition more streamlined.

When editing menu items, changing the navigation label of an item instantly updates its drag-and-drop handle, and updating a menu name updates the corresponding Customizer section. Items can be reordered or moved into sub-menus via either drag-and-drop or a reordering mode similar to that of the Widget Customizer.

To minimalize the UI, given the limited space in the customizer, the “Title Attribute” field has been turned off by default, and all of the existing menu-item-field screen options are available, syncing with the existing Menus screen. I might look into building a core API for customizer screen options now that #27406 is in trunk, time permitting.

A good amount of my time in the past couple weeks has been dedicated to #27406, which is a prerequisite for the Menu Customizer to be realistic given the need to allow users to create new menus (and in turn, new Customizer sections). Committed to trunk yesterday, it introduces a “Panels” API to the Customizer. Panels are a way to group sections, adding a new layer of hierarchy to the Customizer. In the Widget Customizer, all widget areas are added to the Widgets panel, allowing widgets to take over the entire Customizer as needed. The Menu Customizer plugin does the same thing with Menus, and requires trunk accordingly.

Upcoming Work

My next steps are to implement menu-adding and deleting, to implement reorderability/sortability, and then to spend quite a bit of time developing a saving/previewing system that scales well (see #14134  and related). This will most likely involve creating clones of changed menu items (posts) and menus (taxonomy terms). Once all of that’s finished, the plugin should be feature-complete, and ready for iteration.

Core Patches

I’ve also taken the opportunity to spend a fair amount of time working on core patches related to either Menus or the Customizer, as this project is widely expanding my knowledge of both areas. A couple of examples that have made it into core include #27406 – Customizer Panels, and #23076 – which adds live menu-item label-updating to the existing Menu screen. I’m planning on continuing to work on Menus and the Customizer via tickets/patches throughout my project as time allows.

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