WordPress 4.8.1 release delayed by one day

The 4.8.1 release was due out today (August 1st), however a performance problem was raised during release preparations. Specifically, the ticketticket Created for both bug reports and feature development on the bug tracker. in question is #35293 and its problematic commit on the 4.8 branchbranch A directory in Subversion. WordPress uses branches to store the latest development code for each major release (3.9, 4.0, etc.). Branches are then updated with code for any minor releases of that branch. Sometimes, a major version of WordPress and its minor versions are collectively referred to as a "branch", such as "the 4.0 branch". has now been reverted, while the changes remain on trunk. A performance fix is pending in #41501, and it may be part of a subsequent 4.8.2 minor releaseMinor Release A set of releases or versions having the same minor version number may be collectively referred to as .x , for example version 5.2.x to refer to versions 5.2, 5.2.1, 5.2.3, and all other versions in the 5.2 (five dot two) branch of that software. Minor Releases often make improvements to existing features and functionality..

We’ve re-scheduled the 4.8.1 release to occur after the coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. dev chat tomorrow, at August 2nd, 21:00 UTC.

In the mean time, please continue testing the release. A new 4.8.1-RC2 package (ZIP) has been published for you to triple check on your test environments. For more information on testing, see Beta Testing. The 4.8.1-RC2 release contains 29 maintenance fixes and enhancements since 4.8.0, chief among them are fixes to the rich Text widget and the introduction of the Custom HTML widget. For a full list of changes in the release, consult the tickets closed and the changesets committed.

#4-8-1

Fixes to Text widget and introduction of Custom HTML widget in 4.8.1

The 4.8 release caused issues for many sites that had custom HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. in Text widgets, which until now had been common practice. So we’ve been working hard on fixes in the 4.8.1 release which aim to simultaneously serve the needs of novice users and advanced users alike: the rich Text widgetWidget A WordPress Widget is a small block that performs a specific function. You can add these widgets in sidebars also known as widget-ready areas on your web page. WordPress widgets were originally created to provide a simple and easy-to-use way of giving design and structure control of the WordPress theme to the user. (introduced in 4.8), a legacy mode for the Text widget, and a Custom HTML widget.

For more background on the changes in 4.8, see Addition of TinyMCE to the Text Widget. To review, the Text widget in 4.8 includes TinyMCE—the same visual editor used for writing post content—and it looks like:

Text Widget Legacy Mode

The issues with the introduction of TinyMCE to the Text widget revolve around the ways that TinyMCE attempts to clean up HTML code by deleting empty elements (such as those for dashicons) and dropping attributes it may not recognize (such as HTML5 Microdata attributes). Also with the 4.8’s removal of the “automatically add paragraphs” checkbox, there were also issues related to paragraphs and line breaks being added incorrectly.

Note that the Text widget was already designed to preserve the old behavior of the widget until it was modified and thus upgraded, so there are many instances of Text widgets in the wild today that could very well begin to break upon being modified. For this reason the issues were not reported right away and instead started to trickle in steadily after the release.

There were various solutions that were considered, but the one that had the consensus among contributors was:

[Check if the Text widget] was previously saved from an older version of WordPress before TinyMCE was added to the Text widget. If it is such a pre-existing Text widget instance, then use heuristics to detect if TinyMCE would negatively impact the contents of the widget, including the auto-p checkbox being unchecked, whether there are empty tags, and whether there are spandivscript, or style tags. When the Text widget is in this legacy mode, it can have a notice that informs users of the new HTML Code widget and that it should be used going forward. Likewise, in the new mode when TinyMCE is present, when the Text (HTML) tab is selected, there can be a note (perhaps an adminadmin (and super admin) pointer) that encourages users to use the HTML Code widget instead. By implementing this, novice users with basic content in their widgets win, and advanced users with custom HTML content in their widgets will cease from being negatively impacted.

The Text widget in legacy mode looks the same as the Text widget before 4.8, but with the addition of a new notice:

The legacy mode will only be presented for widgets created prior to 4.8.0 that have instance data which match the logic in the WP_Widget_Text::is_legacy_instance() method. The legacy mode will not be presented to newly created Text widgets. Once a Text widget is opened and saved in legacy mode, it will permanently stay in legacy mode. There is a new instance property called “visual” which will be set to false when a widget is saved in legacy mode. When a new Text widget is created, it is opened in the default visual mode and the new instance will get saved with visual=true.

Text Widget Filters

There is a change in how the filter instance property was used in 4.8.0: in that release, when a Text widget was modified, the fact that it had been upgraded was stored by overloading the filter boolean property to also have the value of "content", indicating that the widget gets content filters applied like a post does. Since this string is a truthy value, I reasoned it would normally work the same in filters that check ! empty( $instance['filter'] ), but it would fail in cases where a pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party tried true === $instance['filter']. So 4.8.1 reverts the overloading of the filter property to again be a boolean, and this should improve compatibility for widget_text filters. Whenever a Text widget is modified with the default visual mode (with TinyMCE) it will get both visual=true and filter=true saved in its instance. When a Text widget is modified in the legacy mode, it will always get visual=false and its filter property will reflect the checked state of the auto-paragraph checkbox.

Another note on filters: special consideration was made for shortcodes in the Text widget given the frequency of plugins and themes adding shortcodeShortcode A shortcode is a placeholder used within a WordPress post, page, or widget to insert a form or function generated by a plugin in a specific location on your site. support (since the widget does not recognized them by default in coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress.). Plugins and themes have done add_filter( 'widget_text', 'do_shortcode' ) to add support. Since the widget_text filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. applies before the new widget_text_content filter (as of 4.8), it will apply before wpautop will have applied, resulting in the possibility of extra line breaks being added undesirably if the shortcode output has new line characters. So to help prevent that from happening, the Text widget will temporarily move the do_shortcode handler from widget_text to widget_text_content just in time while the filters are being applied. See the relevant logic.

Help Pointers

For users who are accustomed to pasting HTML into the Text widget, when an attempt is made to paste markup into the visual editor a pointer will be displayed informing them that they should paste it into the Text tab instead, or to alternatively use the new Custom HTML widget (see section below):

Likewise, when a user opens the Text tab, it will also open a pointer to inform them of the Custom HTML widget:

While pointers are normally displayed on upgrades, these pointers will be displayed even on new installs since they reflect changes to long-standing behavior for the Text widget that users have become accustomed to. Any tutorials that instruct users to use the Text widget for pasting in arbitrary HTML should be updated to instruct the users to select the Custom HTML widget instead.

Custom HTML Widget

For advanced users or for any use case where arbitrary HTML needs to be displayed in a widget (such as a signup form or a 3rd party JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. widget), there is now a dedicated “Custom HTML” widget that is specifically for this purpose. It looks very similar to the classic Text widget, except it has a monospace font and it lacks the auto-paragraph checkbox:

Since users are prompted (per the pointers above) to try using the Custom HTML widget instead of the Text widget for some use cases, it is important that the widget content be able to be freely copied between the Text widget and the Custom HTML widget. For this reason, the Custom HTML widget retains the application of the widget_text filters like the Text widget does. The type of widget for which the filter is applying can be determined by looking at the type of the WP_Widget instance being passed as the last filter argument. When the widget_text filter is applied, it will pass the second $instance parameter in the same format as the Text widget, with title, text (instead of content), and filter and visual properties that are always both set to false (as if the instance was in legacy non-visual mode). In addition to re-applying the widget_text filter, the Custom HTML widget has a dedicated widget_custom_html_content filter whereas the the Text widget has a dedicated widget_text_content filter.

In addition to filter compatibility, the Custom HTML widget also tries to retain theme styling compatibility by using the same widget_text CSSCSS Cascading Style Sheets. class name on the outer widget wrapper and textwidget on the inner wrapper around the content itself. For any themes that wish to style the Custom HTML widget alone, there are the widget_custom_html and custom-html-widget class names used on the outer and inner wrapper elements respectively. For themes that wish to style the Text widget alone and exclude the Custom HTML widget, the :not() pseudo selector can be used, for example .widget_text:not(.widget_custom_html) and .textwidget:not(.custom-html-widget) for the outer and inner wrappers, respectively.

The markup generated by a Custom HTML widget on the frontend will look like:

<section id="custom_html-6" class="widget_text widget widget_custom_html">
  <h2 class="widget-title">My Title</h2>
  <div class="textwidget custom-html-widget">My Content</div>
</section>

This same Custom HTML widget’s instance data will look like:

{
  "title": "My Title",
  "content": "My Content"
}

For more specifics on the Custom HTML widget, refer to the subclass: WP_Widget_Custom_HTML.

Here is a list of tickets related to the Text widget and Custom HTML widget which are closed in the 4.8.1 release:

  • #40907: Introduce widget dedicated for HTML code
  • #40951: New Text Widget – Switching Between Visual/Text Editor Strips Out Code
  • #40960: Set `’filter’ => ‘content’` on starter content “business info” widget
  • #40960: Widgets: The Text widget should respect the “Disable the visual editor when writing” setting
  • #40972: TinyMCE editor in Text widget does not have RTL contents
  • #40974: Updated text widget do not save text (when using paste)
  • #40986: Widgets: text widget and media widgets cannot be edited in accessibilityAccessibility Accessibility (commonly shortened to a11y) refers to the design of products, devices, services, or environments for people with disabilities. The concept of accessible design ensures both “direct access” (i.e. unassisted) and “indirect access” meaning compatibility with a person’s assistive technology (for example, computer screen readers). (https://en.wikipedia.org/wiki/Accessibility) mode
  • #41021: Text widget does not show Title field or TinyMCE editor
  • #41158:  Increase tinymce panel z-index
  • #41361: Text widget can raise JSJS JavaScript, a web scripting language typically executed in the browser. Often used for advanced user interfaces and behaviors. error if customize-base is enqueued on widgets admin screen
  • #41386: Text Widget – Wording – Legacy Mode 4.8.1 betaBeta A pre-release of software that is given out to a large group of users to trial under real conditions. Beta versions have gone through alpha testing in-house and are generally fairly close in look, feel and function to the final product; however, design changes often occur as part of the process.
  • #41392: Theme styles for Text widget do not apply to Custom HTML widget
  • #41394: Text widget: Rename legacy mode to visual mode and improve back-compat for widget_text filters

 

#4-8-1, #dev-notes, #tinymce, #widgets

Media Widgets for Images, Video, and Audio

As first introduced in the Image Widget Merge Proposal, WordPress 4.8 includes media widgets (#32417) for not only images (#39993) but also video (#39994) and audio (#39995), on top of an extensibleExtensible This is the ability to add additional functionality to the code. Plugins extend the WordPress core software. base for introducing additional media widgets in the future, such as for galleries and playlists. To quote [40640]:

The last time a new widgetWidget A WordPress Widget is a small block that performs a specific function. You can add these widgets in sidebars also known as widget-ready areas on your web page. WordPress widgets were originally created to provide a simple and easy-to-use way of giving design and structure control of the WordPress theme to the user. was introduced, Vuvuzelas were a thing, Angry Birds started taking over phones, and WordPress stopped shipping with Kubrick. Seven years and 17 releases without new widgets have been enough, time to spice up your sidebarSidebar A sidebar in WordPress is referred to a widget-ready area used by WordPress themes to display information that is not a part of the main content. It is not always a vertical column on the side. It can be a horizontal rectangle below or above the content area, footer, header, or any where in the theme.!

Since widgets are a very old part of WordPress (since 2.2), widgets in coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. have been very much entirely built using PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher with some Ajax sprinkled on top. In the time since WP_Widget was introduced in 2.8, WordPress has made dramatic shifts toward developing interfaces in JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/., including with the CustomizerCustomizer Tool built into WordPress core that hooks into most modern themes. You can use it to preview and modify many of your site’s appearance settings. in 3.4 and the Media Library in 3.5, and more recently with the focus on the REST APIREST API The REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/. and the editor (Gutenberg).

Given that the media widgets are naturally interfacing with the media library JSJS JavaScript, a web scripting language typically executed in the browser. Often used for advanced user interfaces and behaviors., it is necessary that the media widgets make use of JavaScript to construct their UIUI User interface instead of relying on PHP. The media widgets fully reuse the existing media modal frames for not only selecting the media to display but also to edit all of its properties: attachments can be selected from the media library, while external media can be inserted by URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org.

Initial groundwork for shimming JavaScript into widgets was added in 3.9 via the widget-added and widget-updated events, which are also being utilized in 4.8 with the Addition of TinyMCE to the Text Widget. A more recent proposal for making JavaScript more of a first class citizen can be found in #33507 and the media widgets incorporate some of its patterns that were also prototyped in the JS Widgets pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party. The media widgets make use of a Backbone View to manage the widget control’s UI and a Backbone Model for reading and manipulating the widget instance data.

Base Media Widget

PHP: wp-includes/widgets/class-wp-widget-media.php
JS: wp-admin/js/widgets/media-widgets.js

The three widgets all extend a WP_Widget_Media PHP abstract class; in JS the Backbone view wp.mediaWidgets.MediaWidgetControl and wp.mediaWidgets.MediaWidgetModel are extended. A unique aspect of how the media widgets work is how instance data is validated and sanitized. Normally widgets utilize procedural code to sanitize instances via a subclassed WP_Widget::update() method. The media widgets, however, make use of a REST API schema returned from WP_Widget_Media::get_instance_schema() to sanitize instances declaratively. The WP_Widget_Media::update() method iterates over the schema and uses it to sanitize and validate the instance properties. (Adding schemas to the base WP_Widget class is also proposed in #35574.) The JSONJSON JSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. It is used primarily to transmit data between a server and web application, as an alternative to XML. schema is extended to include a couple custom properties: media_prop provides the media JS property name that the widget instance maps, and the should_preview_update flag indicates whether a change to that prop should cause the control preview to re-render (this would be retired with a ReactReact React is a JavaScript library that makes it easy to reason about, construct, and maintain stateless and stateful user interfaces. https://reactjs.org/. rewrite and/or leveraging of corresponding blocks in GutenbergGutenberg The Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. https://wordpress.org/gutenberg/).

The WP_Widget_Media abstract class has one abstract method which subclasses must implement: WP_Widget_Media::render_media(). This is the method that WP_Widget_Media::widget() calls with the $instance props to render the media for a given widget media type. Before the props are passed to the method, the $instance is applied through  widget_{$id_base}_instance filters.

A media widget subclass should output additional JS templates as the control needs by extending WP_Widget_Media::render_control_template_scripts(). Scripts that the widget control requires can be enqueued by extending WP_Widget_Media::enqueue_admin_scripts(); this is also where the PHP exports can be done with calls to wp_add_inline_script().

The REST API schema is exported from PHP to JS on the subclassed MediaWidgetModel prototypes. Other properties on the prototypes which should be extended include l10n and mime_type. The model subclasses are registered by assigning them to the wp.mediaWidgets.modelConstructors object, keyed by the widget ID base (e.g. media_image, media_video, etc). In the same way, the Backbone View wp.mediaWidgets.MediaWidgetControl is subclassed and registered by adding to the wp.mediaWidgets.controlConstructors object, also keyed by widget ID base. This is similar to how control types are registered in the Customizer.

The MediaWidgetControl and MediaWidgetModel will be instantiated once a widget control is expanded. Their instances will be then added to the wp.mediaWidgets.widgetControls object and wp.mediaWidgets.modelCollection respectively.

There is a subclass of a media controller and a couple media views in the wp.mediaWidgets namespace. These extensions to media classes are needed due to current limitations in media extensibility. They may be removed/reduced with improvements in #40427.

As with the incorporation of TinyMCE into the Text widget, the incorporation of the media library into the media widget has necessitated constructing the widget’s form fields differently than how they are normally done. Widgets in core have historically utilized static HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. for their control form fields. Every time a user hits “Save” the form fields get sent in an Ajax request which passes them to the WP_Widget::update() method and then the Ajax response sends back the output of WP_Widget::form() which then replaces the entire form. (Note widgets in the Customizer behave differently since there is no Save button in the widget, as updates are synced and previewed as changes are made; read more about Live Widget Previews.) This worked for static HTML forms in the past, but in the case of the media widgets the UI built with JavaScript instead of server-side PHP.

To avoid having to rebuild the media preview every time the user hits Save on the adminadmin (and super admin) screen, the media widget puts its UI elements outside of the container that is “managed” by the server which gets replaced with each save. (A similar approach has also been employed by the new TinyMCE-extended Text widget in 4.8.) Since core does not yet represent a widget’s state in a JavaScript model (again see #33507), the media widget syncs its MediaWidgetModel props with hidden inputs that get rendered by WP_Media_Widget::form() in order to be sent to the server for preview and saving. The container for the media widget’s fields is .media-widget-control and the traditional container for a widget’s input fields as rendered by WP_Widget::form() is .widget-content:

For examples of how to implement media widgets, see the three implementations included in 4.8 as follows.

Image Widget

PHP: wp-includes/widgets/class-wp-widget-media-image.php
JS: wp-admin/js/widgets/media-image-widget.js

Field Type Default Description
attachment_id integer 0 Attachment post ID
url string "" URL to the media file
title string "" Title for the widget
size string "medium" Size
width integer 0 Width
height integer 0 Height
caption string "" Caption
alt string "" Alternative Text
link_type string "none" Link To
link_url string "" URL
image_classes string "" Image CSSCSS Cascading Style Sheets. Class
link_classes string "" Link CSS Class
link_rel string "" Link Rel
link_target_blank boolean false Open link in a new tab
image_title string "" Image Title Attribute

Video Widget

PHP: wp-includes/widgets/class-wp-widget-media-video.php
JS: wp-admin/js/widgets/media-video-widget.js

The video widget allows for embeddable video formats to be selected from the media library or linked to externally by URL. In addition, URLs to YouTube or Vimeo may also be provided since the video shortcodeShortcode A shortcode is a placeholder used within a WordPress post, page, or widget to insert a form or function generated by a plugin in a specific location on your site. logic supports rendering them via MediaElement.js. It is possible for plugins to add support for additional oEmbed providers via extending the video widget control’s isHostedVideo method; for more, see the Jetpack PR for adding VideoPress support.

Field Type Default Description
attachment_id integer 0 Attachment post ID
url string "" URL to the media file
title string "" Title for the widget
preload string "metadata" Preload
loop boolean false LoopLoop The Loop is PHP code used by WordPress to display posts. Using The Loop, WordPress processes each post to be displayed on the current page, and formats it according to how it matches specified criteria within The Loop tags. Any HTML or PHP code in the Loop will be processed on each post. https://codex.wordpress.org/The_Loop.
content string "" Tracks (subtitles, captions, descriptions, chapters, or metadata)
mp4 string "" URL to the mp4 video source file
m4v string "" URL to the m4v video source file
webm string "" URL to the webm video source file
ogv string "" URL to the ogv video source file
flv string "" URL to the flv video source file

Note that the presence of format-specific fields is dependent on what is returned by wp_get_video_extensions().

Audio Widget

PHP: wp-includes/widgets/class-wp-widget-media-audio.php
JS: wp-admin/js/widgets/media-audio-widget.js

The audio widget allows for embeddable audio formats to be selected from the media library or linked to externally by URL. Note that there are no oEmbed audio formats supported since the audio shortcode logic only supports rendering players for actual audio files.

Field Type Default Description
attachment_id integer 0 Attachment post ID
url string "" URL to the media file
title string "" Title for the widget
preload string "none" Preload
loop boolean false Loop
mp3 string "" URL to the mp3 audio source file
ogg string "" URL to the ogg audio source file
m4a string "" URL to the m4a audio source file
wav string "" URL to the wav audio source file

Note that the presence of format-specific fields is dependent on what is returned by wp_get_audio_extensions().

Default Themes Updates

Themes that add custom styles to the MediaElement.js player (namely Twenty Thirteen and Twenty Fourteen) were updated from just styling it within syndicated content, to also include instances within widgets. Most themes don’t restrict styles for captioned images or media players to just post content, that is, limit CSS selectors to classes output by post_class(). If your theme does, make sure to either remove that constraint or include a .widget selector.

Conclusion

The work on the new media widgets in core was conducted in the Core Media Widgets plugin and on its corresponding wp-core-media-widgets GitHubGitHub GitHub is a website that offers online implementation of git repositories that can easily be shared, copied and modified by other developers. Public repositories are free to host, private repositories require a paid subscription. GitHub introduced the concept of the ‘pull request’ where code changes done in branches by contributors can be reviewed and discussed before being merged be the repository owner. https://github.com/ repo. Many of the decisions that were made in the architecture of the feature can be found there in the GitHub issues and pull requests.

Keep in mind that the media widgets will likely undergo many more changes with the incorporation of the Gutenberg editor, and that widgets themselves will likely see many changes to align with Gutenberg’s editor blocks which are now being prototyped. Essentially if you can insert a given type of blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. into the editor, there should also be a widget available for representing the same content.

#4-8, #dev-notes, #media-widgets

Addition of TinyMCE to the Text Widget

Update: A follow-up post has been published regarding Fixes to Text widget and introduction of Custom HTML widget in 4.8.1.

In its first couple years, WordPress lacked rich/visual text editing. Before TinyMCE was incorporated in WordPress 2.0, users had to edit post content as raw HTML with some support from the Quicktags buttons. When widgets were introduced in WordPress 2.2, the Text widget was included which allowed a user to add content to their sidebar. Nevertheless, unlike the post editor, the Text widget did not incorporate TinyMCE, nor did it include Quicktags. For twelve years, since TinyMCE was added to core in 2005, users have had to hack around with HTML in their Text widgets to do things as simple as make text bold or add links. This has been featured even as recently as the 4.7 release video. Well, as of WordPress 4.8, the Text widget is finally getting the same treatment as the post editor with the introduction of TinyMCE for visual text editing, while still supporting raw HTML editing via a Text tab but now with the additional help of Quicktags:

Text widget: visual tab Text widget: text (HTML) tab

A primary reason for the long delay in incorporating TinyMCE into the Text widget was the difficulty of cleanly instantiating another copy of the WordPress visual editor dynamically after the page has loaded. Since WordPress 3.3 there has been the wp_editor() PHP function for instantiating an editor for the initial page load, but there was no facility for instantiating editors afterward, such as when adding a new Text widget to a sidebar. So in #35760 a new JS API was introduced for dynamically instantiating WP editors on the page: wp.editor.initialize(). This JS API is used for the new TinyMCE-powered Text widget; for more, please see Editor API changes in 4.8.

Note that by default the Text widget only features buttons for bold, italic, unordered list, ordered list, and link. If you want to add additional buttons to the toolbar, you may use a plugin to enqueue the following JS to add the blockquote button, for example:

jQuery( document ).on( 'tinymce-editor-setup', function( event, editor ) {
	if ( editor.settings.toolbar1 && -1 === editor.settings.toolbar1.indexOf( 'blockquote' ) ) {
		editor.settings.toolbar1 += ',blockquote';
	}
});

Likewise, a custom button can be added via code like:

jQuery( document ).on( 'tinymce-editor-setup', function( event, editor ) {
	editor.settings.toolbar1 += ',mybutton';
	editor.addButton( 'mybutton', {
		text: 'My button',
		icon: false,
		onclick: function () {
			editor.insertContent( 'Text from my button' );
		}
	});
});

A reason for why there is no “Add Media” button for the editor in the Text widget is that WordPress 4.8 also includes dedicated media widgets for adding images, video, and audio to sidebars. Another reason is a current technical limitation whereby arbitrary media embeds (oEmbeds) fail to work properly outside the context of post content (see #34115). For this reason if you try to embed a video or something else by pasting in a URL it will not currently expand into the embedded content. The same is true for shortcodes: they are not processed by default since many shortcodes presuppose a global $post as the context within which they expect to be running. Plugins may opt-in selectively to support individual shortcodes by filtering widget_text as they have had to do for many years now.

While not supporting shortcodes, the updated Text widget does have some of the same filters applying to it as the_content, in particular:

  • wpautop
  • wptexturize
  • capital_P_dangit
  • convert_smilies

The Text widget has supported a “filter” instance property which was reflected in the UI via a checkbox for whether or not to automatically add paragraphs and line break tags (wpautop). For the updated Text widget, this checkbox is removed entirely in favor of aligning its behavior with the post editor where this behavior is always enabled. These filters only apply on Text widgets that have been updated/touched after the 4.8 upgrade. When a Text widget is modified in 4.8, the filter instance prop gets set to a static string “content both the filter instance property and a new visual property will be set to true and then it will apply a new widget_text_content filter which then apply the above functions to the widget text. For Text widgets created prior to 4.8, they will be opened in a legacy mode with the old behavior when it is determined that TinyMCE could mutate the content undesirably; such legacy instances will be saved with visual=false instance property. Read more about Fixes to Text widget and introduction of Custom HTML widget in 4.8.1.

Important: When pasting HTML into the “Text” (HTML) tab of the Text widget, any extraneous line breaks should be removed or else unwanted paragraphs and line beaks may result. This is particularly important when you paste in script or style tags (as in the case of 3rd-party JavaScript embeds), since auto-inserted paragraphs will cause script errors; this will be fixed in #2833. This behavior aligns with longstanding behavior in the post editor, so it is not new, although it does differ from how the Text widget has previously behaved. As noted above, for previously-existing Text widgets that had the “auto-add paragraphs” checkbox unchecked (and thus the filter instance prop set to false), the previous behavior of not doing wpautop will be maintained: only once the widgets are modified will any extraneous line breaks need to be removed. Any such Text widgets created prior to 4.8 will be go into a legacy mode as of 4.8.1 to prevent any data loss. Going forward, arbitrary HTML should be placed into the new Custom HTML widget instead.

The incorporation of TinyMCE into the Text widget has necessitated constructing the widget’s form fields differently than how they are normally done. Widgets in core have historically utilized static HTML for their control form fields. Every time a user hits “Save” the form fields get sent in an Ajax request which passes them to the WP_Widget::update() method and then the Ajax response sends back the output of WP_Widget::form() which then replaces the entire form. (Note widgets in the Customizer behave differently since there is no Save button in the widget, as updates are synced and previewed as changes are made; read more about Live Widget Previews.) This worked for static HTML forms in the past, but TinyMCE is a JavaScript component. To avoid having to rebuild TinyMCE every time the user hits Save on the admin screen, the Text widget puts the title field and TinyMCE text field outside of the container that is “managed” by the server which gets replaced with each save. (A similar approach has also been employed by the new media widgets in 4.8.) The fields in the Text widget’s UI sync with hidden inputs which get synchronized with the server; in the Customizer, changes to the TinyMCE field will be previewed after 1 second with denouncing. The container for the Text widget’s fields is .text-widget-fields and the traditional container for a widget’s input fields as rendered by WP_Widget::form() is .widget-content:

Themes should already account for the most common HTML elements within the text widgets and provide appropriate styles for them—the addition of the editor toolbar increases the likelihood its elements will be used in the future. Most default themes needed additional styles for ordered and unordered lists within widgets, so theme authors are encouraged to double-check their themes and test them with content in the text widget that includes markup provided by the editor toolbar.

Initial groundwork for shimming JavaScript into widgets was added in 3.9 via the widget-added and widget-updated events. A more recent proposal for making JavaScript more of a first class citizen in widgets can be found in #33507 and the media widgets incorporate some of its patterns that were also prototyped in the JS Widgets plugin. The synchronization of a widget’s state (instance properties) via hidden text fields can be eliminated once a widget’s state can be fully represented in a JavaScript model.

The Text widget is implemented as a Backbone.js view which is available at wp.textWidgets.TextWidgetControl. Instances of this view are then stored in the wp.textWidgets.widgetControls object. The widget’s control JS will only instantiate for a given widget once it is first expanded, when the widget-added event fires. What’s more is that the widget will only first initialize once the container is fully expanded since TinyMCE is not able to initialize properly inside of a container that is animating. In order to capture when TinyMCE inside the Text widget is initialized, you should use a TinyMCE event like tinymce-editor-setup. Note also that due the Document Object Model, when a widget is moved to a new location in a sidebar and this the TinyMCE iframe is moved in the DOM, this dynamically-created iframe is reloaded and thus emptied out. For this reason, every time a Text widget is moved to a new location the TinyMCE editor in the widget will be removed and then re-initialized. Keep this in mind when extending.

Keep also in mind that the Text widget will likely undergo many more changes with the incorporation of the Gutenberg editor, and that widgets themselves will likely see many changes to align with Gutenberg’s editor blocks which are now being prototyped.

#4-8, #dev-notes, #editor, #widgets

Removal of core embedding support for WMV and WMA file formats

With the introduction of the video widgetWidget A WordPress Widget is a small block that performs a specific function. You can add these widgets in sidebars also known as widget-ready areas on your web page. WordPress widgets were originally created to provide a simple and easy-to-use way of giving design and structure control of the WordPress theme to the user. (#39994) and audio widget (#39995) it was discovered that the WMV and WMA file formats are no longer supported by MediaElement.js as of v3. While coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. is currently shipping with MediaElement.js v2.22, these file formats only play if the Silverlight pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party is installed, something which is less and less common since the plugin is no longer supported as of Chrome 45 and as of Firefox 52. So with the planned upgrade of MediaElement.js from 2.22 to 4.x (see #39686), it is a good time with the WP 4.8 major releasemajor release A release, identified by the first two numbers (3.6), which is the focus of a full release cycle and feature development. WordPress uses decimaling count for major release versions, so 2.8, 2.9, 3.0, and 3.1 are sequential and comparable in scope. to remove support for the formats. This has been done in [40813] for #40819. Plugins may continue to add embedding support for these file formats by re-adding them via the wp_video_extensions and wp_audio_extensions filters, for example:

function add_wmv_to_wp_video_extensions( $extensions ) {
    $extensions[] = 'wmv';
    return $extensions;
}
add_filter( 'wp_video_extensions', 'add_wmv_to_wp_video_extensions' );

function add_wma_to_wp_audio_extensions( $extensions ) {
    $extensions[] = 'wma';
    return $extensions;
}
add_filter( 'wp_audio_extensions', 'add_wma_to_wp_audio_extensions' );
Plugins would also need to implement fallback rendering routines via the wp_video_shortcode_override and wp_audio_shortcode_override filters, or else use a different library than MediaElement.js altogether via the wp_video_shortcode_library and wp_audio_shortcode_library filters. Note that there is no functional difference before and after the removal of WMA and WMV support. When MediaElement.js doesn’t support a format, it displays a download link. Before and after [40813], the following is ho  the audio and video shortcodes look like with WMA and WMV files respectively: In the adminadmin (and super admin), the functional difference is that these formats are not listed as being available for selection in the Video and Audio widgets. Additionally, when you Add Media in the post editor, there is now no option to embed the player for these formats; only a link to the media will be provided, just as when attempting to add non-media files to content.

#4-8, #core-media, #dev-notes, #mediaelement

Customizer sidebar width is now variable

A common request for the CustomizerCustomizer Tool built into WordPress core that hooks into most modern themes. You can use it to preview and modify many of your site’s appearance settings. has been to grant more room for the controls that appear in its sidebarSidebar A sidebar in WordPress is referred to a widget-ready area used by WordPress themes to display information that is not a part of the main content. It is not always a vertical column on the side. It can be a horizontal rectangle below or above the content area, footer, header, or any where in the theme.. Ticketticket Created for both bug reports and feature development on the bug tracker. #32296 was created to allow the sidebar pane to be user-resizable with a grabber just like the Dev Tools pane in Chrome can be resized. After a lot of back and forth, the scope was reduced to remove the user-resizable aspect and to instead address a more fundamental issue that the sidebar is exceedingly and unnecessarily narrow on high-resolution displays. So while 4.8 doesn’t include a user-resizable controls pane, there is still a feature plugin for possible future merge. Important to that end, the changes that have been merged begin to introduce the fundamental prerequisite for a variable-width sidebar pane: responsive controls.

As can be seen in [40511] and [40567], no longer should Customizer controls assume that their container will always be 300px wide. Given the new responsive breakpoints for high-resolution screens, the pane can now be between 300px and 600px wide depending on the screen width. And actually this has already been the case on smaller screens (e.g. mobile) where the preview and pane aren’t shown at the same time; it now also applies to very large screens where there is ample space for both the preview and a wider pane.

Custom controls in plugins and themes should utilize alternative approaches to doing layout than using pixel widths. Use of percentage-based widths or flexbox will help ensure that controls will appear properly in larger displays, while also making controls future-compatible when the sidebar width could be user-resizable.

#4-8, #customize, #dev-notes

Customize Changesets Technical Design Decisions

2016-11-15: Updated the description for how and when setting changes are written into the changeset, and also how the iframeiframe iFrame is an acronym for an inline frame. An iFrame is used inside a webpage to load another HTML document and render it. This HTML document may also contain JavaScript and/or CSS which is loaded at the time when iframe tag is parsed by the user’s browser. window is loaded during a refresh when there are pending changeset yet to be written into the changeset. For more see comment. Also included information about storing modifying user with each setting change, among other tweaks.

This is a technical deep-dive into Customize Changesets (#30937), a proposed feature formerly known as Customizer Transactions. Because changesets make some very low-level changes to how the customizerCustomizer Tool built into WordPress core that hooks into most modern themes. You can use it to preview and modify many of your site’s appearance settings. works, I felt it was important for these technical details to be shared concurrently with a 4.7 merge proposal since developers of themes and plugins that extend the customizer heavily will need to be well advised of the changes. As such, again, this is a long and technical post. Consider it a pre-merge dev notedev note Each important change in WordPress Core is documented in a developers note, (usually called dev note). Good dev notes generally include a description of the change, the decision that led to this change, and a description of how developers are supposed to work with that change. Dev notes are published on Make/Core blog during the beta phase of WordPress release cycle. Publishing dev notes is particularly important when plugin/theme authors and WordPress developers need to be aware of those changes.In general, all dev notes are compiled into a Field Guide at the beginning of the release candidate phase..

Feedback on the transactions/changesets idea has been positive although somewhat quiet. After the initial patch and the Customize Snapshots feature pluginFeature Plugin A plugin that was created with the intention of eventually being proposed for inclusion in WordPress Core. See Features as Plugins., there is now a third iteration on the patch, which is currently in testing and review, and I believe it will be ready for inclusion in WordPress 4.7. I’ll first recap the customizer in general and then explain how changesets will open up a lot of new possibilities for live preview and also fix a lot of defects while we’re at it.

The customizer is WordPress’s framework for doing live previews of any change on your site. Its purpose is to eliminate the “save and surprise” behavior that happens when making changes in the WP adminadmin (and super admin). All changes you make in the customizer are transient and nothing impacts the live site until you hit the Save & Publish button. A user can feel free to experiment with making changes to their site and be secure in the knowledge that they aren’t going to break their site for visitors since they can preview the impacts of their changes. A very powerful aspect of the customizer is that you can make as many changes to as many settings as you want, including changing the site title, headerHeader The header of your site is typically the first thing people will experience. The masthead or header art located across the top of your page is part of the look and feel of your website. It can influence a visitor’s opinion about your content and you/ your organization’s brand. It may also look different on different screen sizes. image, background image, nav menu, widgets, and other aspects of your site, and all of these changes are previewed together and will go live together when published: changes in the customizer are bundled/batched together.

The TL;DR for this post is that customize changesets make changes in the customizer persistent, like autosave drafts. For users, the customizer tab can be closed and re-opened and the changes will persist. Users can make changes to one theme and switch to another in the customizer without losing the changes upon switching. A customizer session can be bookmarked to come back to later or this URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org can be shared with someone else to review and make additional changes (the URLs expire after a week without changes). The new APIs make possible many new user-facing features in future releases and feature plugins, including saving drafts, submitting changesets as pending for review, scheduling changes, and more.

Limitations before Changesets (WP≤4.6)

In saying that unsaved changes made in the customizer (before changesets) are transient, I did not mean that they somehow get saved in an actual transient. No, they are much more ephemeral than that. Changes in the customizer currently only exist in your browser’s memory. If you try leaving the customizer with unsaved changes you get prompted with an “Are you sure?” (AYS) dialog because once you leave customize.php all of the changes you made will be permanently lost. This issue is especially painful in the context of switching themes in the current customizer: if you modify the site’s title and static front pageStatic Front Page A WordPress website can have a dynamic blog-like front page, or a “static front page” which is used to show customized content. Typically this is the first page you see when you visit a site url, like wordpress.org for example. and then try switching to a different theme, that AYS dialog will appear and warn you that your changes will be lost (since switching a theme currently requires the customizer reload). Not a great user experience. (See #36485.)

Since none of the changes made in the current customizer are persistent in any way, this also has implications for how changes are previewed. When a full refresh or selective refresh is done in the preview, all of the changed settings have to be sent with each request so that the customized state is available to the preview filters to apply in the response, and as such each request has needed to use the POST method. What this in turn has meant is that links in the preview can’t work normally. When you click a link the URL gets sent via postMessage from the preview to the parent frame and where an Ajax POST request is then made to that URL and the response is then written into an about:blank iframe window via document.write(). This causes a couple issues: JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. running in the preview will return /wp-admin/customize.php when looking at window.location as opposed to the expected URL; this largely prevents using JavaScript for URL routing, and it causes problems with JSJS JavaScript, a web scripting language typically executed in the browser. Often used for advanced user interfaces and behaviors. libraries like jQuery UIUI User interface (see #23225, #30028). Additionally, any REST APIREST API The REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/. calls to GET data will not include the customized state applied in the responses (without hacky workarounds with _method).

Fundamentals for Changesets

The coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. concept being introduced in Customize Changesets is that the customized state for a given live preview session should be persistent in the database. Each time the customizer is opened a random UUID is generated to serve as the identifier for the changes in that session: the changeset. When changes are made to settings the values will get sent in an Ajax request to be written into a customize_changeset custom post typeCustom Post Type WordPress can hold and display many different types of content. A single item of such a content is generally called a post, although post is also a specific post type. Custom Post Types gives your site the ability to have templated posts, to simplify the concept. whose post_name is the UUID for the customizer session. Once the changes have been written into the changeset post, then any request to WordPress (including to the REST API) can be made with a customize_changeset_uuid query param with the desired UUID and this will result in the customizer being bootstrapped and the changes from that changeset being applied for preview in the response. Changes are written into the changeset post at the AUTOSAVE_INTERVAL or whenever focus is removed from the controls window or at beforeunload.

The data from changesets is designed to be incorporated into the existing customizer APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. to maximize backwards compatibility. Namely, the way that the customizer obtains the customized state is by making calls to WP_Customize_Manager::unsanitized_post_values(). When there is a changeset associated with the given UUID, then the values from the changeset will serve as the base array that is returned by this method. If there is any $_POST['customized'] in the request, then it will be merged on top of this base array. Likewise, calls to $wp_customize->set_post_value() will also result in the supplied values being merged on top of underlying changeset data.

Since a UUID query param is all that is needed to apply the customized state, this means that the customizer iframe can now just use the frontend preview URL with UUID query param directly in its src attribute and load the iframe window naturally, as opposed to using about:blank with a document.write(). When there are pending changes not yet written into the changeset, then the iframe window is loaded by programmatically submitting a form with a POST method containing the pending customized data. Likewise, the UUID will also get added as a query parameter to customize.php itself via history.replaceState() which means you can freely reload the customizer and your changes will persist.

The customize_changeset post uses the post_content to store the changed values in a JSONJSON JSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. It is used primarily to transmit data between a server and web application, as an alternative to XML.-encoded object, with the setting IDs as keys mapping to objects that contain the value and other setting params like type.A submitted setting value will only be written into the changeset if it passes the validation constraints, if the setting is recognized, and if the user can do the capability associated with the setting. An example changeset:

{
    "blogname": {
        "value": "My Blog",
        "type": "option",
        "user_id": 1
    },
    "twentysixteen::header_textcolor": {
        "value": "blank",
        "type": "theme_mod",
        "user_id": 2
    }
}

You’ll note that the header_textcolor setting has a theme slug as a prefix. This is done so that changes to the theme mods won’t be carried over from theme to theme when switching between them in a customizer session. If you set the text color in Twenty Sixteen but then switch over to Twenty Seventeen to try changing colors there, you can then switch back to Twenty Sixteen in the same customizer session and the text color you set before the theme switch will be restored. Additionally, when activating a theme in the customizer and there are theme mods for other themes that do not get saved, these theme mods will get stashed in a customize_stashed_theme_mods option to then be restored the next time the theme is previewed in the customizer. Lastly, the user_id for the user who last modified a given setting is stored with the setting’s params in the changeset so that at the time of saving (publishing) the changes into the database the user can be switched-to so that filters (such as Kses) apply as expected.

The customize_changeset posts by default get created with the auto-draft status so that they will get automatically garbage-collected after a week of no modifications. When the Save & Publish button is pressed, a customize_save Ajax request will be sent with a status of publish for that post. When a customize_changeset post transitions to publish, the customizer will hook in to load the values from the changeset post into an active changeset and then iterate over each of the settings and save each one. Upon publishing, a changeset post will by default then get immediately trashed so that it will also get garbage-collected.

Since transitioning a changeset post to the publish status is what causes its setting values to be saved, this means that setting changes can be scheduled (see #28721). Since setting values are only written into the changeset if they pass validation and authorization checks, the values can be saved asynchronously without a user with the required capabilities being logged in. An update to the site title can be scheduled for publishing even though no user with edit_theme_options will be logged-in when WP Cron runs. Here’s how this can be done with WP-CLIWP-CLI WP-CLI is the Command Line Interface for WordPress, used to do administrative and development tasks in a programmatic way. The project page is http://wp-cli.org/ https://make.wordpress.org/cli/:

wp post create \
    --post_type=customize_changeset \
    --post_name=$( uuidgen ) \
    --post_status=future \
    --post_date="2017-01-01 00:00:00" \
    --post_content='{"blogname":{"value":"Happy New Year!"}}'

There is no new UI for changesets being proposed for 4.7 (other than the addition of the changeset_uuid query param) and the removal of the theme switch AYS dialog. (Note that the AYS dialog remains for the customizer otherwise since there is no UI that lists changesets to allow the user to navigate back to that state.) Adding a UI for scheduling changesets to core can be explored in future releases.

Extension Opportunities for Plugins

There are also exciting new APIs that plugins will be able to leverage to create new UIs and extend functionality.

  • The show_ui flag on the custom post type can be turned on so that all changeset posts can be listed. When viewing the edit post screen for given changeset, a metaboxMetabox A post metabox is a draggable box shown on the post editing screen. Its purpose is to allow the user to select or enter information in addition to the main post content. This information should be related to the post in some way. can be added to show the contents of the changeset.
  • By adding adding support for revisions to the customize_changeset post type, published changesets will no longer be automatically trashed (and garbage-collected). With this there is automatic revisionsRevisions The WordPress revisions system stores a record of each saved draft or published update. The revision system allows you to see what changes were made in each revision by dragging a slider (or using the Next/Previous buttons). The display indicates what has changed in each revision. and an audit trail for changes made in the customizer (see #31089).
  • A “Save Draft” button can be added next to “Save & Publish” that calls wp.customize.previewer.save({status: 'draft'}). This will prevent the changeset from being an auto-draft and thus garbage-collected.
  • Users could collaborate together on a single changeset with setting-level concurrency locking.
  • Normally when updating a changeset post, a revision is prevented from being created even when they are enabled. However, when making an update to a changeset via wp.customize.previewer.save() where the status is supplied (such as draft) then a new revision will be made. These revisions can be browsed from the edit post screen. Since the JSON in the post_content is pretty-printed, the diffs are easy to read.
  • The publish_posts metaMeta Meta is a term that refers to the inside workings of a group. For us, this is the team that works on internal WordPress sites like WordCamp Central and Make WordPress. capability for customize_snapshots post type can be overridden to something other than customize, and for users without the new capability a “Submit” button can be added to the UI to submit a changeset for review via wp.customize.previewer.save({status: 'pending'}).
  • Changesets can be scheduled for publishing from the customizer by hooking up a button to make a call like to wp.customize.previewer.save({status: 'future', date: '2017-01-01 00:00:00'}).
  • The customize_changeset post type supports title even though it is not displayed anywhere by default. A pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party could add a “commit message” UI for changesets where there could be a title could be entered in an input field and sent via wp.customize.previewer.save({status: 'draft', title: 'Add 2017 greeting'}).
  • The can_export flag can be turned on for the registered post type so that customize_changeset posts can be exported.

Several of these features already exist in the Customize Snapshots plugin among several others, but the plugin is currently using its own standalone snapshots API with a customize_snapshot post type rather than re-using what is being proposed in 4.7 for core, as the plugin is a prototype for changesets in core. The plugin will be able to be refactored after changesets are committed to core, allowing a lot of code to be removed from it. I’ve also put together a couple of these UI features in a little Gist plugin.

Selective Refresh and Seamless Refresh

In the original proposal for “transactions”, I said that selective refresh was prerequisite for getting transactions/changesets into core. The reason for this is that the initial patchpatch A special text file that describes changes to code, by identifying the files and lines which are added, removed, and altered. It may also be referred to as a diff. A patch can be applied to a codebase for testing. got rid of the PreviewFrame construct in the customizer JS API since I thought it was no longer necessary. I thought that a refresh should just be invoked simply with location.reload() in the iframe. The problem with full page refreshes via location.reload() is that they are not seamless due to a “flash of reloading content”. However, when working on the latest patch I grew to appreciate the approach of loading a new iframe in the background and hot-swapping with the foreground once loaded. And anyway, I realized there was no need to remove the “seamless refresh” functionality. Creating a new iframe for each change to the previewed URL also prevents the browser history from unexpectedly being amended.

Aside: Now that the iframe is loaded with a regular URL supplied in its src attribute, you can now manually refresh the preview iframe just like you would refresh any other iframe. Just context click (right click) on the preview frame and select “Reload Frame” from the context window.

Frontend Preview

Again, now that the customizer preview is loaded into the iframe via a regular URL, you can grab the value of the iframe[src] (or iframe[data-src] in the case of loading a preview with changes not yet written into the changeset) and actually open it in a separate window altogether and not have the customizer controls pane present at all. When you look at the preview URL from the iframe you’ll see the normal frontend URL you’re previewing along with three additional query parameters such as:

  • customize_changeset_uuid=598cdda1-b785-431d-8c6d-6c421300ed9f
  • customize_theme=twentysixteen
  • customize_messenger_channel=preview-1

Only the first one, customize_changeset_uuid, is required to bootstrap the customizer and preview the state. The second one, customize_theme, must be supplied when previewing a theme switch. Otherwise, it can be omitted. And lastly, when the customize_messenger_channel param is present, the customizer preview will hide the admin bar and yield control of navigation to the parent frame: clicks on links and submitting forms will continue to preventDefault with the intended url destination being sent to the parent frame to populate wp.customize.previewer.previewUrl which then causes the iframe[src] to be set to that URL. Maintaining this method of navigation from the preview is important for compatibility with plugins that add custom params to the URL being previewed by intercepting updates to the previewUrl. When a changeset is published, a new UUID is sent in the response and it is sent to the preview in the saved message and it replaces the old UUID in the URL via history.replaceState(). Also note that calls to history.replaceState() and history.pushState() are wrapped so that the customize state query prams will be injected into whatever url is supplied. Whenever the URL is changed with JavaScript, the new URL will be sent to the parent frame along with the wp.customize.settings for the autofocused panels, sections, and controls. This allows for JS-routed apps to take control over contextual panels, sections, and controls.

When the customize_messenger_channel query param is absent, then links can be clicked and forms submitted without any preventDefault. The admin bar will be shown and the Customize link will include the changeset UUID param. The customizer preview JS will ensure that the customizer state query params above will persist by injecting them into all site links and by adding them as hidden inputs to all site forms. If a link or form points to somewhere outside of the site (and is thus not previewable) then the mouse will get a cursor:not-allowed and wp.a11y.speak() will explain that it is not previewable (see #31517). The customizer preview will keep sending keep-alive messages up to the controls pane parent window so that if a user does end up getting routed to an non-previewable URL, the customizer controls pane will know that the preview is no longer connected and so when a setting with postMessage transport is modified it will intelligently fallback to using refresh instead.

The way that REST API requests get the customized state included is via the jQuery.ajaxPrefilter(). If an Ajax request is made with a URL to somewhere on the site, then the customize state query params will be appended to the URL.

Since changesets can only be modified by an authorized user and since previewing is a read-only operation, the URL for the customize preview including the UUID can be shared with unauthenticated users to preview those changes. Since a UUID is random it serves not only as a unique identifier but it also effectively serves as a secret key so that such customizer previews cannot be guessed. Note that unauthenticated users would still be forbidden from accessing the preview URL if they supply a customize_theme that differs from the active theme, as they do not have the switch_themes capability.

Aside: Since the customizer preview can be loaded by itself in its own window, this opens the door to being able to use the customizer for frontend editing and bootstrapping the customizer while on the frontend.

Another aside: The customizer should be able to point to a headless frontend to preview, as long as the frontend looks for the customize_changeset_uuid query param and passed it along in any REST API calls, while also including the customize-preview.js file.

REST API Endpoints

The initial changesets patch does not include REST API endpoints for the 4.7 release. However, there should be a feature plugin developed that adds endpoints for listing all changesets, inspecting the contents of a changeset, and the other CRUDCRUD Create, read, update and delete, the four basic functions of storing data. (More on Wikipedia.) operations. With these endpoints in place, entirely new customizer interfaces can be developed that have nothing to do with customize.php at all. Namely, mobile apps could create changesets and include the associated UUID in REST API requests for various endpoints to preview changes to the app itself. Some initial read-only endpoints were added to the Customize Snapshots plugin.

Summary of API Changes

PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher:

  • Added: The WP_Customize_Manager class’s constructor now takes an $args param for passing in the changeset UUID, previewed theme, and messenger channel. This $args param is intended to be used instead of global variables, although the globals will still be read as a fallback for backwards compatibility.
  • Changed: The query param for theme switches has been changed from theme to a prefixed customize_theme. This will reduce the chance of a conflictconflict A conflict occurs when a patch changes code that was modified after the patch was created. These patches are considered stale, and will require a refresh of the changes before it can be applied, or the conflicts will need to be resolved., although theme will still be read as a fallback.
  • Added: New custom post type: customize_changeset.
  • Changed: Modified semantics of WP_Customize_Manager::save() to handle updating a changeset in addition to the previous behavior of persisting setting values into the database.
  • Added: WP_Customize_Manager::changeset_uuid(), which returns the UUID for the current session.
  • Added: WP_Customize_Manager::changeset_post_id(), which returns the post ID for a given customize_changeset post if one yet exists for the current UUID.
  • Added: WP_Customize_Manager::changeset_data() which returns the array of data stored in the changeset.
  • Changed: WP_Customize_Manager::unsanitized_post_values() now takes parameters for whether to exclude changeset data or post input data. Also, post input data will be ignored by default if the current user cannot customize.
  • Added: customize_changeset_save filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. to modify the data being persisted into the changeset.
  • Added: WP_Customize_Manager::publish_changeset_values() which is called when a customize_changeset post transitions to a publish status.
  • Added: Theme mods in a changeset which are not associated with the activated theme will get stored in a customize_stashed_theme_mods option so that they can be restored the next time the inactive theme is previewed.
  • Added: WP_Customize_Manager::is_cross_domain().
  • Added: WP_Customize_Manager::get_allowed_urls().
  • Added: wp_generate_uuid4(), a new global function for generating random UUIDs.
  • Changed: Settings are registered for nav menus and widgets even if the user cannot edit_theme_options so that a changeset containing values for them can be previewed.
  • Deprecated: WP_Customzie_Manager::wp_redirect_status().
  • Deprecated: WP_Customize_Manager::wp_die_handler().
  • Deprecated: WP_Customize_Manager::remove_preview_signature().

JavaScript:

  • Added: wp.customize.state('previewerAlive') state containing a boolean indicating whether the preview is sending keep-alive messages.
  • Added: wp.customize.state('saving') state containing a boolean indicating whether wp.customize.preview.save() is currently making a request.
  • Added: wp.customize.state('changesetStatus') state containing the current post status for the customize_changeset post.
  • Added: wp.customize.requestChangesetUpdate() method which allows a plugin to programmatically push changes into a changeset. This allows additional params to be attached to a given setting in a changeset aside from value, and it also allows settings to be removed from a changeset altogether by sending null as the setting params object.
  • Added: changeset-save event is triggered on wp.customize with the pending changes object being sent to to the server.
  • Added: changeset-saved event is triggered on wp.customize when a changeset update request completes successfully, with a changeset-error event triggered on a failure.
  • Added: changeset-saved message is sent to the preview once a changeset has been saved so that JS can safely make make REST API request with the customized state being available.
  • Added: The millisecond values used in debounce and setInterval calls are now stored in wp.customize.settings.timeouts rather than being hard-coded in the JS.
  • Changed: Seamless refreshes will wait for any pending changeset updates to complete before initiating.
  • Changed: The beforeunload event handler for the AYS dialog now has a customize-confirm namespace.
  • Added: Settings for the changeset are exposed under wp.customize.settings.changeset, including the current UUID and the post status.
  • Moved: wp.customize.utils has been moved from customize-controls to customize-base.
  • Added: wp.customize.utils.parseQueryString() to parse a query string into an object of query params.
  • Fixed: Prevent modified nav menus from initiating selective refresh with each page load when customizer is loaded via HTTPSHTTPS HTTPS is an acronym for Hyper Text Transfer Protocol Secure. HTTPS is the secure version of HTTP, the protocol over which data is sent between your browser and the website that you are connected to. The 'S' at the end of HTTPS stands for 'Secure'. It means all communications between your browser and the website are encrypted. This is especially helpful for protecting sensitive data like banking information..
  • Fixed: Links in the preview that are just internal jump links no longer have preventDefault called. See #34142.
  • Added: wp.customize.isLinkPreviewable() to customize-preview which returns whether a given link can be previewed.

Thanks

The work on transactions/snapshots/changesets has been going on for almost 2 years now. I want to thank Derek Herman (@valendesigns) for a lot of his work on the Customize Snapshots plugin that took the key concepts from the initial transactions proposal and started to combine them with a lot of compelling user-facing features.

I hope that changesets will make the Customize API all the more powerful to build compelling cutting-edge applications in WordPress.

#4-7, #customize, #dev-notes

Customize Changesets (formerly Transactions) Merge Proposal

This is a merge proposal and overview of Customize Changesets (#30937), a project formerly known and proposed as CustomizerCustomizer Tool built into WordPress core that hooks into most modern themes. You can use it to preview and modify many of your site’s appearance settings. Transactions back in January 2015. The customizer is WordPress’s framework for doing live previews of any change on your site. One of the biggest problems the customizer faces right now is that changes are ephemeral. If you navigate away, you lose what you are working on. Additionally, you can not share proposed changes with others, nor can you take the changes you are working on and save to continue working later.

Imagine a WordPress user named Tina. Tina is building a website for her daughter’s band. The band has been getting more and more popular, and Tina wants to experiment with some new widgets in the sidebarSidebar A sidebar in WordPress is referred to a widget-ready area used by WordPress themes to display information that is not a part of the main content. It is not always a vertical column on the side. It can be a horizontal rectangle below or above the content area, footer, header, or any where in the theme. but also wants to be able to share the proposed solutions with her husband Matthew. Right now, Tina and Matthew would need to be in the same room to collaborate. Or Tina would need to take screenshots, but that kind of defeats the purpose of live preview. If only there was a way to make customizer changes persistent without publishing them.

Introducing Customize Changesets

Customize changesets make changes in the customizer persistent, like autosave drafts. Users can make changes to one theme and switch to another in the customizer without losing the changes upon switching. A customizer session can be bookmarked to come back to later or this URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org can be shared with someone else to review and make additional changes (the URLs expire after a week without changes). The new APIs make possible many new user-facing features in future releases and feature plugins, including saving long-lived drafts, submitting changesets as pending for review, scheduling changes, seeing the previewed state on the frontend without being in an iframeiframe iFrame is an acronym for an inline frame. An iFrame is used inside a webpage to load another HTML document and render it. This HTML document may also contain JavaScript and/or CSS which is loaded at the time when iframe tag is parsed by the user’s browser., sharing preview URLs with others who do not have customizer access, and others.

Customize changesets allow each change you make in the customizer for a given live preview session to be persistent in the database. A unique identifier (a UUID like f67efbbf-c663-4271-ab1c-95ce1d447979) for each live preview session is generated and as soon as a change is made, the change setting value is sent in an Ajax request to be written into a custom post typeCustom Post Type WordPress can hold and display many different types of content. A single item of such a content is generally called a post, although post is also a specific post type. Custom Post Types gives your site the ability to have templated posts, to simplify the concept. whose post_name is the UUID for the customizer session. Once the changes have been written into the changeset post, then any request to WordPress (including to the REST APIREST API The REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/.) can be made with a customize_changeset_uuid query param with the desired UUID and this will result in the customizer being bootstrapped and the changes from that changeset being applied for preview in the response. The unique UUID means that customizer sessions can be sent to other users and also that they can be used as query parameters on the front end.

Design and Technical Decisions

For the initial coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. merge, no UIUI User interface changes are being proposed. The feature will only be exposed as the new query parameter on the URL. Adding a UI to this feature will happen in a future release. As such, the proposal for customize changesets is similar to the proposal for including the REST API infrastructure: it provides a foundation for new core features in future releases and a platform for plugins to add new features. Nevertheless, while the customize changesets patchpatch A special text file that describes changes to code, by identifying the files and lines which are added, removed, and altered. It may also be referred to as a diff. A patch can be applied to a codebase for testing. doesn’t introduce any new features it does fix several long-standing issues related to incompatibilities between JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. running on the site’s frontend when previewed in the customizer. Under the hood, the customize changesets patch touches on many of the lowest level pieces of the customizer. Please check out the Customize Changesets Technical Design Decisions to see what is happening under the hood.

Testing

Please test! If you use any plugins that extend the customizer, please ensure that there aren’t any regressions. The patch is intended to be fully backwards compatible and users shouldn’t notice any difference in normal use. Two things to look for when testing is as soon as you make a change, you should see a customize_uuid query param added to the URL. You should be able to reload and find your changes persist (note the AYS dialog is retained because there is no UI yet for listing changesets). Also, when you navigate around the preview it should feel much more natural like normal browsing as opposed to having a fade effect. Otherwise, previewing settings that require refresh should still work as normal, as will settings that preview with JavaScript and selective refresh.

The patch is in a GitHubGitHub GitHub is a website that offers online implementation of git repositories that can easily be shared, copied and modified by other developers. Public repositories are free to host, private repositories require a paid subscription. GitHub introduced the concept of the ‘pull request’ where code changes done in branches by contributors can be reviewed and discussed before being merged be the repository owner. https://github.com/ pull request and you can apply the patch via:

grunt patch:https://github.com/xwp/wordpress-develop/pull/161

If you’re using the GitGit Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency. Git is easy to learn and has a tiny footprint with lightning fast performance. Most modern plugin and theme development is being done with this version control system. https://git-scm.com/. repo from develop.git.wordpress.orgWordPress.org The community site where WordPress code is created and shared by the users. This is where you can download the source code for WordPress core, plugins and themes as well as the central location for community conversations and organization. https://wordpress.org/ then you can check out the branchbranch A directory in Subversion. WordPress uses branches to store the latest development code for each major release (3.9, 4.0, etc.). Branches are then updated with code for any minor releases of that branch. Sometimes, a major version of WordPress and its minor versions are collectively referred to as a "branch", such as "the 4.0 branch". directly via:

git remote add -f xwp https://github.com/xwp/wordpress-develop.git && git checkout xwp/trac-30937

I’d appreciate code review feedback directly on the pull request. For any revisionsRevisions The WordPress revisions system stores a record of each saved draft or published update. The revision system allows you to see what changes were made in each revision by dragging a slider (or using the Next/Previous buttons). The display indicates what has changed in each revision. to the patch, please open a pull request to that trac-30937 branch if possible.

The Future

In future releases we can explore new UIs to take advantage of the new capabilities that changesets provide. New UIs can provide a way to schedule changes, the ability to undo the last change, show an audit log (revision history) for changes, collaborative editing of a customizer changeset, and so on. Future feature projects will explore many of these and feature plugins will start to prototype them.

Thanks to @jorbin who contributed to this proposal post.

#4-7, #customize, #feature-projects, #proposal

Pre-instantiated Widget Registration in 4.6

Since WP_Widget was introduced in 2.8 the register_widget() and unregister_widget() functions required the class name (string) of a WP_Widget subclass to be supplied. As of 4.6 these functions also accept a class instance (object) of a WP_Widget subclass as well. See #28216.

Two key benefits for allowing objects to be instantiated are:

  1. Widgets can now be instantiated and registered with constructor dependency injection.
  2. New widgetWidget A WordPress Widget is a small block that performs a specific function. You can add these widgets in sidebars also known as widget-ready areas on your web page. WordPress widgets were originally created to provide a simple and easy-to-use way of giving design and structure control of the WordPress theme to the user. types can now be added dynamically, such as adding a Recent Posts widget for each post type, per #35990.

#4-6, #dev-notes, #widgets

Customizer APIs in 4.6 for Setting Validation and Notifications

As described in the Improving Setting Validation in the Customizer proposal post and detailed in #34893 and #36944, WordPress 4.6 includes new APIs related to validation of CustomizerCustomizer Tool built into WordPress core that hooks into most modern themes. You can use it to preview and modify many of your site’s appearance settings. setting values. The Customizer has had sanitization of setting values since it was introduced. Sanitization involves coercing a value into something safe to persist to the database: common examples are converting a value into an integer or stripping tags from some text input. As such, sanitization is a lossy operation.

But what happens in sanitization if a provided value is irrecoverable, beyond the ability to sanitize? The Customizer did allow sanitizers to return null in such cases which resulted in the value being skipped entirely from previewing and saving, but there was no feedback to the user that the value was skipped. Additionally, when multiple settings were modified but some were skipped due to returning null, the result was that a save operation would only persist the non-skipped settings to database: a user would unexpectedly find that only some of their settings were applied, resulting in an inconsistent saved state. Save operations were not transactional/atomic.

These are the problems that the Customizer setting validation improvements in WordPress 4.6 are designed to address. With setting validation:

  1. All modified settings are validated up-front before any of them are saved.
  2. If any setting is invalidinvalid A resolution on the bug tracker (and generally common in software development, sometimes also notabug) that indicates the ticket is not a bug, is a support request, or is generally invalid., the Customizer save request is rejected: a save thus becomes transactional with all the settings left dirty to try saving again. (The Customizer transactions proposal is closely related to setting validation here.)
  3. Validation error messages are displayed to the user, prompting them to fix their mistake and try again.

Sanitization and validation are also both part of the REST APIREST API The REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/. infrastructure via WP_REST_Request::sanitize_params() and WP_REST_Request::validate_params(), respectively. A setting’s value goes through validation before it goes through sanitization (this is contrary to the original proposal, see #36944).

Validation Behavior

As noted above, if any setting is invalid, a Customizer save request is blocked. When save request is rejected due to setting invalidity, the controls that have the invalid setting will get the error notifications added to them, and one of these controls will be focused so that the user can correct the mistake. If there is an control with an invalid setting in the current section expanded, then this is the focus that will get the focus. Otherwise, the section containing an invalid control will get expanded and the control then focused.

Validation of setting values on the server does not only occur when a save is attempted. The setting values are validated and re-validated with each full refresh and selective refresh, and the their validity states are returned from the server in the responses. This means that setting validity will be reported in conjunction with updates to the preview. This is important for a couple reasons:

  1. When a setting is invalid, the previewed value will render using the previously-saved value or the default setting value, so the notification provides an explanation for why their changes aren’t appearing in the preview.
  2. As soon as a value is corrected and the preview request finishes, the error notification will be removed from the control. This means the user doesn’t have to try to saving to see whether the value has been corrected or not.

This second point has a key implication for validating settings that are previewed purely via JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. (the postMessage transport without selective refresh): you should perform the validation logic for these using JavaScript as well. See client-side validation below.

Adding Validation to a Setting

Validate Callback

Just as you can supply a sanitize_callback when registering a setting, you can also supply a validate_callback arg:

$wp_customize->add_setting( 'established_year', array(
    'sanitize_callback' => 'absint',
    'validate_callback' => 'validate_established_year'
) );
function validate_established_year( $validity, $value ) {
    $value = intval( $value );
    if ( empty( $value ) || ! is_numeric( $value ) ) {
        $validity->add( 'required', __( 'You must supply a valid year.' ) );
    } elseif ( $value < 1900 ) {
        $validity->add( 'year_too_small', __( 'Year is too old.' ) );
    } elseif ( $value > gmdate( 'Y' ) ) {
        $validity->add( 'year_too_big', __( 'Year is too new.' ) );
    }
    return $validity;
}

Just as supplying a sanitize_callback arg adds a filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. for customize_sanitize_{$setting_id}, so too supplying a validate_callback arg will add a filter for customize_validate_{$setting_id}. Assuming that the WP_Customize_Setting instances apply filters on these in their validate methods, you can add this filter if you need to add validation for settings that have been previously added.

The validate_callback and any customize_validate_{$setting_id} filter callbacks take a WP_Error instance is its first argument (which initially is empty of any errors added), followed by the $value being sanitized, and lastly the WP_Customize_Setting instance that is being validated.

Validate Method

A second way to add validation to a setting is by overriding the validate method on a WP_Customize_Setting subclass. For example:

class Established_Year_Setting extends WP_Customize_Setting {
    function validate( $value ) {
        if ( empty( $value ) || ! is_numeric( $value ) ) {
            return new WP_Error( 'required', __( 'You must supply a valid year.' ) );
        }
        if ( $value < 1900 ) {
            return new WP_Error( 'year_too_small', __( 'Year is too old.' ) ); 
        } 
        if ( $value > gmdate( 'Y' ) ) {
            return new WP_Error( 'year_too_big', __( 'Year is too new.' ) );
        }
        return true;
    }
}

Validating via Sanitization

The last way to add validation to a setting is to overload the sanitization routine to include validation as well. Setting sanitization and setting validation are closely related. Sanitization is for coercing/cleaning data. Validation is a pass/fail operation and is key to prevent data from being accepted when it is “too far gone” to be recovered from or when it is undesirable for the value to be silently and lossily cleaned. As noted above, setting’s sanitization logic can continue to return null and this will get interpreted as a setting’s value being invalid with a returned generic “Invalid value” error. You may also return a WP_Error instance from a sanitization routine which can be a handy way to consolidate the closely-related sanitization/validation logic; if you do this, make sure that you opt to return null in WP≤4.5, as otherwise this would result in a WP_Error instance being saved as a setting’s value (!!):

function sanitize_number( $value ) {
    $can_validate = method_exists( 'WP_Customize_Setting', 'validate' );
    if ( ! is_numeric( $value ) ) {
        return $can_validate ? new WP_Error( 'nan', __( 'Not a number' ) ) : null;
    }
    return intval( $value );
}

Again, this useful if you want to consolidate the closely-related logic into a single routine and also when to perform late validation after a value has been sanitized.

Client-side Validation

As noted above, if you have a setting that is previewed purely via JavaScript (and the postMessage transport without selective refresh), you should also add client-side validation as well. If you don’t then any validation errors will persist until a refresh happens or a save is attempted. Client-side validation must not take the place of server-side validation, since it would be trivial for a malicious user to bypass the client-side validation to save an invalid value if corresponding server-side validation is not in place.

There is actually already a validate method available on the wp.customize.Setting JSJS JavaScript, a web scripting language typically executed in the browser. Often used for advanced user interfaces and behaviors. class (actually, the wp.customize.Value base class). It was introduced with the inception of the Customizer in WP 3.4, although I believe it has been rarely utilized. The validate method’s name is a bit misleading, as it actually behaves very similarly to the WP_Customize_Setting::sanitize() PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher method. Nevertheless, the method can be used to both sanitize and validate a value in JS. Note that this JS runs in the context of the Customizer pane not the preview, so any such JS should have customize-controls as a dependency (not customize-preview) and enqueued during the customize_controls_enqueue_scripts action. Some example JS validation:

wp.customize( 'established_year', function ( setting ) {
	setting.validate = function ( value ) {
		var code, notification;
		var year = parseInt( value, 10 );

		code = 'required';
		if ( isNaN( year ) ) {
			notification = new wp.customize.Notification( code, {message: myPlugin.l10n.requiredYear} );
			setting.notifications.add( code, notification );
		} else {
			setting.notifications.remove( code );
		}

		if ( isNaN( year ) ) {
			return value;
		}

		code = 'year_too_small';
		if ( year < 1900 ) {
			notification = new wp.customize.Notification( code, {message: myPlugin.l10n.yearTooSmall} );
			setting.notifications.add( code, notification );
		} else {
			setting.notifications.remove( code );
		}

		code = 'year_too_big';
		if ( year > new Date().getFullYear() ) {
			notification = new wp.customize.Notification( code, {message: myPlugin.l10n.yearTooBig} );
			setting.notifications.add( code, notification );
		} else {
			setting.notifications.remove( code );
		}

		return value;
	};
} );

Notifications APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways.

An error notification is added to a setting’s notifications collection when a setting’s validation routine returns a WP_Error instance. Each error added to a PHP WP_Error instance is represented as a wp.customize.Notification in JavaScript:

  • A WP_Error‘s code is available as notification.code in JS.
  • A WP_Error‘s message is available as notification.message in JS. Note that if there are multiple messages added to a given error code in PHP they will be concatenated into a single message in JS.
  • A WP_Error‘s data is available as notification.data in JS. This is useful to pass additional error context from the server to the client.

Any time that a WP_Error is returned from a validation routine on the server it will result in a wp.customize.Notification being created with a type property of “error”.

While setting non-error notifications from PHP is not currently supported (see #37281), you can also add non-error notifications with JS as follows:

wp.customize( 'blogname', function( setting ) {
    setting.bind( function( value ) {
        var code = 'long_title';
        if ( value.length > 20 ) {
            setting.notifications.add( code, new wp.customize.Notification(
                code,
                {
                    type: 'warning',
                    message: 'Theme prefers title with max 20 chars.'
                }
            ) );
        } else {
            setting.notifications.remove( code );
        }
    } );
} );

You can also supply “info” as a notification’s type. The default type is “error”. Custom types may also be supplied, and the notifications can be styled with CSSCSS Cascading Style Sheets. selector matching notice.notice-foo where “foo” is the type supplied. A control may also override the default behavior for how notifications are rendered by overriding the wp.customize.Control#renderNotifications method.

Error notification

Warning notification

Info notification

custom-notification

More Examples

A couple plugins that make use of validation:

  • Customize Validate Entitled Settings: Demo that forces the site title, widgetWidget A WordPress Widget is a small block that performs a specific function. You can add these widgets in sidebars also known as widget-ready areas on your web page. WordPress widgets were originally created to provide a simple and easy-to-use way of giving design and structure control of the WordPress theme to the user. titles, and nav menu labels to be populated with title case and lacking exclamations and questions.
  • Customize Posts: Validation errors added when posts would fail to save due to empty content, post locking, or save conflicts (with the specific conflicted fields being returned as WP_Error data). This pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party also features syncing back the server-sanitized setting values to upon save, something which I think should be in coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. at some point.
  • Standalone Customizer Controls: Demonstration of a controls used outside the Customizer with notifications added via JS based on HTML5 validity state.

Summary of API Changes

PHP changes:

  • Introduces WP_Customize_Setting::validate(), WP_Customize_Setting::$validate_callback, and the customize_validate_{$setting_id} filter.
  • Introduces WP_Customize_Manager::validate_setting_values() to do validation (and sanitization) for the setting values supplied, returning a list of WP_Error instances for invalid settings.
  • Attempting to save settings that are invalid will result in the save being blocked entirely, with the errors being sent in the customize_save_response. Modifies WP_Customize_Manager::save() to check all settings for validity issues prior to calling their save methods.
  • Introduces WP_Customize_Setting::json() for parity with the other Customizer classes. This includes exporting of the type.
  • Modifies WP_Customize_Manager::post_value() to apply validate after sanitize, and if validation fails, to return the $default.
  • Introduces customize_save_validation_before action which fires right before the validation checks are made prior to saving.

JS changes:

  • Introduces wp.customize.Notification in JS which to represent WP_Error instances returned from the server when setting validation fails.
  • Introduces wp.customize.Setting.prototype.notifications.
  • Introduces wp.customize.Control.prototype.notifications, which are synced with a control’s settings’ notifications.
  • Introduces wp.customize.Control.prototype.renderNotifications() to re-render a control’s notifications in its notification area. This is called automatically when the notifications collection changes.
  • Introduces wp.customize.settingConstructor, allowing custom setting types to be used in the same way that custom controls, panels, and sections can be made.
  • Injects a notification area into existing controls which is populated in response to the control’s notifications collection changing. A custom control can customize the placement of the notification area by overriding the new getNotificationsContainerElement method.
  • When a save fails due to setting invalidity, the invalidity errors will be added to the settings to then populate in the controls’ notification areas, and the first such invalid control will be focused.

#4-6, #customize, #dev-notes