Editor changes in 4.7

There are a few noteworthy changes to the editor in WordPress 4.7.

Some of the toolbar buttons have been rearranged to make them easier to access and to encourage proper use of the HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. elements they insert.  The headings drop down is now moved to the top row, and the strike-through and horizontal rule button are moved down. This also reflects their usage.

The underline and justify buttons have been removed from the bottom row. Underlining is a bad practice as readers can confuse it with links (bad 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)), and it does not insert a semantic element. Justifying has uneven browser implementation, and in many cases is bad for readability. Keyboard shortcuts for both will keep working.

For more information see #27159 that also has links to the discussions in SlackSlack Slack is a Collaborative Group Chat Platform https://slack.com/. The WordPress community has its own Slack Channel at https://make.wordpress.org/chat/..

Labels for keyboard shortcuts have been added to the tooltips for buttons and inside drop downs to make them easier to discover.

As always, feedback is welcome.

Ella and Andrew

#4-7, #dev-notes, #editor, #tinymce

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

Comment “allowed” checks in WordPress 4.7

In WP 4.4, comment submission was abstracted so that most of the logic was run in a function that returned a value, rather than inline in wp-comments-post.php. See #34059 for background. This overhaul was incomplete: the process of checking for comment floods and duplicate comments – wp_allow_comment() and friends – contained direct calls to die() and wp_die(), making it impossible to use the results of a failed comment check in the context of unit tests, 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/., or other clients. See #36901. [38778] introduced an optional parameter for wp_allow_comment() and related functions that lets the caller decide whether to preserve the default behavior (wp_die()) or, instead, to return WP_Error objects in the case of failed comment checks.

There is a small backward-incompatible change in [38778]. Historically [5947] it’s been possible to unhook the default comment flood check as follows:

remove_action( 'check_comment_flood', 'check_comment_flood_db', 10, 3 );

In order to maintain backward compatibility with this usage, while at the same time changing the comment flood check so that it returns a value, we’ve performed a trick: check_comment_flood_db() is still hooked in the same way, but is now a wrapper for an add_filter() call that hooksHooks In WordPress theme and development, hooks are functions that can be applied to an action or a Filter in WordPress. Actions are functions performed when a certain event occurs in WordPress. Filters allow you to modify certain functions. Arguments used to hook both filters and actions look the same. the actual comment flood checking function to the new 'wp_is_comment_flood' 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..

The backward compatibility break is as follows. Calling check_comment_flood_db() directly, in isolation, will no longer do anything (except to register a filter callback). If you need to run WP’s default comment flood check manually, outside the context of wp_allow_comment(), use the new wp_check_comment_flood() function. I searched 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/ and the 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/ 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 repo and didn’t find a single instance check_comment_flood_db() being used this way in the wild – it’s hard to imagine a situation where it’d be done, given its previous reliance on wp_die() – but if you’ve done custom work related to comment floods, it’s worth double-checking your code before 4.7 is released.

#4-7, #comments, #dev-notes

Changes to Customizer Sliding Panels/Sections in WordPress 4.7

Several updates to 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.’s sliding panels/sections implementation are going to be introduced in WordPress 4.7. The changes are now available in trunktrunk A directory in Subversion containing the latest development code in preparation for the next major release cycle. If you are running "trunk", then you are on the latest revision., so you can test them with existing themes and plugins. It is worth noting that all instances of custom sections and panels in the 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/ 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 and theme repositories were checked by @celloexpressions. No conflicts were found, but still testing is advised.

tl;dr – The Solution

The root “panel” of the customizer (the links to the panels and panel-less sections) is now logically independent in the DOM from the panels and sections it links to, and likewise the links to sections within a panel are disconnected in the DOM from the container elements for the sections they link to. By keeping these separate, no margin-top hacks are needed anymore: the panel/section to be shown simply needs to be positioned to the top of the customizer pane. This also means 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) hacks which set the root of the customizer to visibility:hidden but a nested child element to visibility:visible are not needed anymore (see #33258). To maintain the accessibility benefit that came “for free“ with the previous nested hierarchical unordered list, aria-owns property has been implemented to relate the panel/section links with the panel/section containers they link to.

For more context, please refer to the original ticketticket Created for both bug reports and feature development on the bug tracker.: #34391.

The 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. should also fix related issues: #34344, #35947.

Flat Panels/Sections Structure

So far panels and sections markup was nested inside one huge ul list inside the customizer pane.

<div id="customize-theme-controls">
  <ul>
    <li id="accordion-section-foo" class="accordion-section control-section...">
      <h3 class="accordion-section-title">...</h3>
      <ul class="accordion-section-content...">
        <li class="customize-section-description-container">...</li>
        <li class="customize-control customize-control-text">...</li>
        ...
      </ul>
    </li>
    <li id="accordion-panel-bar" class="accordion-section control-section control-panel...">
      <h3 class="accordion-section-title">...</h3>
      <ul class="accordion-sub-container control-panel-content">
        <li class="panel-meta accordion-section...">...</li>
        <li id="accordion-section-baz" class="accordion-section control-section control-subsection">...</li>
      </ul>
    </li>
  </ul>
</div>

With #34391 being merged each panel/section content element is detached from the root “panel” and appended to the parent container #customize-theme-controls right after the root list.

<div id="customize-theme-controls">
  <ul class="customize-pane-parent">
    <li id="accordion-section-foo" class="accordion-section control-section..." aria-owns="sub-accordion-section-foo">
      <h3 class="accordion-section-title">...</h3>
    </li>
    <li id="accordion-panel-bar" class="accordion-section control-section control-panel..." aria-owns="sub-accordion-panel-bar">
      <h3 class="accordion-section-title">...</h3>
    </li>
  </ul>
  <ul id="sub-accordion-section-foo" class="customize-pane-child accordion-section-content accordion-section control-section...">
    <li class="customize-section-description-container">...</li>
    <li class="customize-control customize-control-text">...</li>
    ...
  </ul>
  <ul id="sub-accordion-panel-bar" class="customize-pane-child accordion-sub-container control-panel-content accordion-section control-section control-panel...">
    <li class="panel-meta accordion-section...">...</li>
    <li id="accordion-section-baz" class="accordion-section control-section control-subsection" aria-owns="sub-accordion-section-baz">
      ...
    </li>
  </ul>
  <ul id="sub-accordion-section-baz" ...>
    ...
  </ul>
</div>

A few additional notes:

  • New getContent() method of the Container class has been introduced.
    • It is responsible for the actual detachment of the content element (usually ul).
    • It should be overridden in the custom sections and panels if it makes more sense to not detach the content element from the parent and keep it nested.
    • It should also be overridden if custom expanding logic is used (e.g. ‘New Menu’ section in the CoreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress.).
  • New Container class properties are now available:
    • headContainer is a jQuery object containing the parent element (usually the li). So far the same object was kept in the container property.
    • contentContainer is a jQuery object containing the child element (usually the ul).
    • container is now a jQuery object with two members: headContainer and contentContainer. This way the backwards-compatibility is improved, as any jQuery methods working on a set of elements like find() or on() should still work with the container as before.
  • CSSCSS Cascading Style Sheets. classes that were possessed by the parent element are copied to the child container, which also is a way to improve the backwards-compatibility.
  • In order to keep logical relationship between the parent and the child elements (which no longer can be determined naturally from the HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. structure), the parent element has new aria-owns property. It lists all children elements of the container.
  • _recalculateTopMargin() method was dropped completely, as it is no longer relevant.

I highly encourage developers who have done any JSJS JavaScript, a web scripting language typically executed in the browser. Often used for advanced user interfaces and behaviors./CSS-heavy work with the customizer’s panels/sections to review the changes introduced by #34391 and test them with themes and plugins.

Transitioning translateX() Instead of left

Along the update to the inner structure of the panels/sections, the user experience of sliding panels has been improved. Now instead of transitioning left position of the container, translateX() is used. It makes the animation smoother and more performant.

  • A busy class has been introduced. It is added to the panels/sections that are going through CSS transition at a given moment.
  • The busy class is added and removed in the Container‘s new method: _animateChangeExpanded( completeCallback ). The method takes care of detecting if transitions are supported by the browser, initiating the transition and listening for normalised transitionend event. It takes one parameter, completeCallback, which is called when the transition is complete.

I hope that working with customizer’s sections and panels will be easier and more reliable with the new structure. If you find any bugbug A bug is an error or unexpected result. Performance improvements, code optimization, and are considered enhancements, not defects. After feature freeze, only bugs are dealt with, with regressions (adverse changes from the previous version) being the highest priority. related to the update, feel free to add a comment here or directly in the TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress. ticket: #34391.

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

New Functions, Hooks, and Behaviour for Theme Developers in WordPress 4.7

WordPress 4.7 introduces some new goodies for theme developers. These changes make powerful new functionality available to themes and plugins. It would be great to see theme developers testing this functionality and providing feedback here in the comments or on the individual tickets linked below.

The get_theme_file_uri() Function, and Friends

#18302

The get_template_part() function, introduced way back in WordPress 3.0, is a fundamental one to theme developers and child theming. The function looks in the child themeChild theme A Child Theme is a customized theme based upon a Parent Theme. It’s considered best practice to create a child theme if you want to modify the CSS of your theme. https://developer.wordpress.org/themes/advanced-topics/child-themes/. for the specified file, and falls back to the parent theme if the file doesn’t exist. This allows a template part to be easily overridden by a child theme, simply by means of the file existing.

The new get_theme_file_uri() function introduced in WordPress 4.7 enables this child theme behaviour for theme file URLs, for example when a CSSCSS Cascading Style Sheets. or 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/. file is enqueued. Here’s an example of its use:

wp_enqueue_script( 'my-script', get_theme_file_uri( 'js/my-script.js' ) );

The above code enqueues the URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org of the js/my-script.js file from the child theme if it exists, falling back to the URL of the file in the parent theme. Now your parent theme can enable each of its enqueued assets to easily be overridden by a child theme. And of course, if no child theme is in use then the function simply uses the parent theme URL, just like get_template_part().

The companion function get_theme_file_path() has also been introduced. This is the file path equivalent of get_theme_file_uri(). One use case for this function is if you like to dynamically generate the version parameter for your enqueued assets based on the last modified timestamp of the file (using filemtime()). You can continue to do so by using this function:

wp_enqueue_script(
	'my-script',
	get_theme_file_uri( 'js/my-script.js' ),
	array(),
	filemtime( get_theme_file_path( 'js/my-script.js' ) )
);

Finally, get_parent_theme_file_uri() and get_parent_theme_file_path() are also introduced, which specifically reference the file URL or file path to the file in the parent theme (and regardless of whether or not the file exists). For consistency, these functions can be used as replacements for when you may have otherwise used get_template_directory_uri() and get_template_directory() respectively.

The {$type}_template_hierarchy 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.

#14310

This new dynamically-named filter allows the complete template hierarchy of a given request type to be filtered by 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 or theme. Although it’s technically possible for the template hierarchy to be filtered using the existing template_include filter, this new filter allows it to be done in a much more clean, simple, and future-proof way, and without the need to reimplement the entire hierarchy logic within the filter’s callback function.

The filter names available by default are:

  • embed_template_hierarchy
  • 404_template_hierarchy
  • search_template_hierarchy
  • frontpage_template_hierarchy
  • home_template_hierarchy
  • taxonomy_template_hierarchy
  • attachment_template_hierarchy
  • single_template_hierarchy
  • page_template_hierarchy
  • singular_template_hierarchy
  • category_template_hierarchy
  • tag_template_hierarchy
  • author_template_hierarchy
  • date_template_hierarchy
  • archive_template_hierarchy
  • paged_template_hierarchy
  • index_template_hierarchy

Here’s an example of the usage of this new filter to add a year-based file to the top of the hierarchy for date archives:

add_filter( 'date_template_hierarchy', function( array $templates ) {
	$year = get_query_var( 'year' );
	array_unshift( $templates, "year-{$year}.php" );
	return $templates;
} );

Here’s a slightly more complex example of adding a file to the hierarchy for a categoryCategory The 'category' taxonomy lets you group posts / content together that share a common bond. Categories are pre-defined and broad ranging. archive based on the value of its term 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. field:

add_filter( 'category_template_hierarchy', function( array $templates ) {
	$format = get_term_meta( get_queried_object_id(), 'format', true );
	if ( $format ) {
		$new = "category-format-{$format}.php";
		$pos = array_search( 'category.php', $templates );
		array_splice( $templates, $pos, 0, $new );
	}
	return $templates;
} );

More usage examples can be seen in the comment thread on the ticketticket Created for both bug reports and feature development on the bug tracker.: #14310.

This filter also allows debugging plugins to access and display the complete template hierarchy for each request, so you can see which files WordPress is looking for in your theme. The latest version of Query Monitor already supports this functionality.

It’s important to remember that the consistency of the template hierarchy in WordPress is what makes standardised theme structures possible. It’s highly recommended that you do not remove templates from the candidate hierarchy using these new filters, unless you’re absolutely certain of what you’re doing.

Simpler Template Names for Content with Non-ASCII Slugs

#37655

Given a post or term with a non-ASCII name, such as hello-world-😀, the URL-encoded form of the name is used in the template hierarchy. For example, on the single post view the hierarchy looked like this prior to WordPress 4.7:

  • single-post-hello-world-%f0%9f%98%80.php
  • single-post.php
  • single.php
  • singular.php
  • index.php

This isn’t very user-friendly, so WordPress 4.7 adds a new, higher priority template to the hierarchy which uses the non-encoded form of the name:

  • single-post-hello-world-😀.php
  • single-post-hello-world-%f0%9f%98%80.php
  • single-post.php
  • single.php
  • singular.php
  • index.php

This makes it much clearer what a template file refers to when building templates for specific posts or terms that include non-ASCII characters in their name.

#4-7, #dev-notes

WP_Hook: Next Generation Actions and Filters

WordPress 4.7 introduces a significant reworking of action and 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. iteration to address bugs that arose from recursive callbacks and from callbacks that changed the hooked callbacks on currently running actions/filters. What does this mean for you, the 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 or theme developer? In almost all cases, nothing. Everything should continue to run as expected, and this should fix a number of hard-to-trace bugs when different plugins are stepping on each others callbacks.

Who is affected?

If your plugin directly accesses the $wp_filter global rather than using the public hooksHooks In WordPress theme and development, hooks are functions that can be applied to an action or a Filter in WordPress. Actions are functions performed when a certain event occurs in WordPress. Filters allow you to modify certain functions. Arguments used to hook both filters and actions look the same. 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., you might run into compatibility issues.

Case 1: Directly setting callbacks in the $wp_filter array

$wp_filter['save_post'][10]['my_special_key'] = array( 'function' => 'my_callback_function', 'accepted_args' => 2 );
This will no longer work, and will cause a fatal error. $wp_filter['save_post'] is no longer a simple array. Rather, it is an object that implements the ArrayAccess interface. You have two options to work around. The first (and preferred) method is to use the add_filter() or add_action() functions.
add_action( 'save_post', 'my_callback_function', 10, 2 );
If, for some reason, you absolutely cannot, you can still work around this.
if ( ! isset( $wp_filter['save_post'] ) ) {
    $wp_filter['save_post'] = new WP_Hook();
}
$wp_filter[ 'save_post' ]->callbacks[10]['my_special_key'] = array( 'function' => 'my_callback_function', 'accepted_args' => 2 );

Case 2: Directly unsetting callbacks in the $wp_filter array

unset( $wp_filter['save_post'][10][ $my_callback_id ] );
This will fail for the same reason as case one. To work around, you can use the standard remove_filter() / remove_action() functions.
remove_action( 'save_post', 'my_callback_function', 10, 2 );
Or, if you absolutely must access the array directly:
if ( isset( $wp_filter[ 'save_post' ] ) ) {
    unset( $wp_filter['save_post']->callbacks[10][ $my_callback_id ] );
}

Case 3: Checking if a hook is an array

if ( isset( $wp_filter['save_post'] ) && is_array( $wp_filter['save_post'] ) ) {
    // do something
}
This will always return false. $wp_filter['save_post'] is a WP_Hook object. To check if a hook has callbacks, use has_action() or has_filter().
if ( has_action( 'save_post' ) ) {
    // do something
}

Case 4: Moving the $wp_filter array pointer manually

If you’re calling next() or prev() on the $wp_filter array pointer to manually manage the order that callbacks are called in (or if you’re doing it to work around #17817), you will likely be unsuccessful. Use callback priorities, add_action() / add_filter(), and remove_action() / remove_filter() appropriately and let WordPress manage execution order.

Other Cases

Almost all other cases where you might manipulate the $wp_filter global directly should continue to function as expected. The WP_Hook object implements the ArrayAccess and IteratorAggregate interfaces so that, while it’s not recommended, you may continue to iterate over callbacks in $wp_filter or directly retrieve priorities from the callback array.

Props

While there were many contributors of the course of this ticketticket Created for both bug reports and feature development on the bug tracker., all of whom are listed in the changeset, I’d like to especially thank @jbrinley, for his work in architecting this solution, researching and testing potential compatibility issues, and for authoring the bulk of this post.

What’s Next

We’re confident in how solid the code is for the majority of sites, and for the major edge cases, so now it’s time to add the code to the nightly build, so all y’all can test it against your sites and plugins. This is being treated as an early commit of a feature project, so the final decision for whether this code will be kept in WordPress 4.7 will be made no later than 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. 1, which gives us a month and a half to see how it effects usage in the wider world. The current nightly build contains this update, for your testing enjoyment. If you have any feedback on the code, please leave a comment on this post. Please post any bugs you find to Core Trac.

#4-7, #dev-notes, #feature-projects, #hooks, #plugins