Whitespace changes in navigation for 4.7

In [38523] the argument item_spacing was introduced for the functions wp_nav_menu(), wp_list_pages(), and wp_page_menu() to:

  • ensure whitespace equivalent output when wp_nav_menu() falls back to wp_list_pages(), and,
  • allow theme authors to control how whitespace is output.

Backward compatibility changes.

There is a backward compatibility breakage when wp_nav_menu() is empty and falls back to using wp_list_pages().

By default, wp_nav_menu() outputs markup with whitespace between each list item (</li> <li>). Prior to WordPress 4.7 when falling back to wp_list_pages() there would be no whitespace between list items (</li><li>). This caused layout to change when certain styles were applied.

From WordPress 4.7 onward when falling back to a page list, wp_nav_menu() will output markup with whitespace between each list item.

There is no change to the default behaviour when calling wp_list_pages() directly, it will not have any whitespace between each item in the menu.

Controlling how menus and page lists output whitespace.

From WordPress 4.7 onwards, theme authors will be able to control whether whitespace in wp_nav_menu(), wp_list_pages() and wp_page_menu() with the item_spacing argument.

The item_spacing argument accepts either preserve or discard. To discard the whitespace in a menu, call the function with:

wp_nav_menu( array(
	'theme_location' => 'top',
	'menu_id'        => 'top-menu',
	'item_spacing'   => 'discard', // default 'preserve'
) );

The same argument can be used for wp_list_pages(), and wp_page_menu() although the default is discard.

#4-7, #dev-notes, #menus

Changed loading order for current user in 4.7

With the introduction of user locales it’s required to load the current user earlier in the bootstrap. Since WordPress 3.4 this is already the case 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., see #24169 and the following simplified function stack:

{main}()                              .../customize.php:0
require_once( '/wp-admin/admin.php' ) .../customize.php:13
require_once( '/wp-load.php' )        .../admin.php:31
require_once( '/wp-config.php' )      .../wp-load.php:44
require_once( '/wp-settings.php' )    .../wp-config.php:118
do_action( 'plugins_loaded' )         .../wp-settings.php:295
_wp_customize_include()               .../plugin.php:524
WP_Customize_Manager->__construct()   .../theme.php:2086
WP_Customize_Widgets->__construct()   .../class-wp-customize-manager.php:266
current_user_can()                    .../class-wp-customize-widgets.php:97
wp_get_current_user()                 .../capabilities.php:448

For other requests the stack looks like this:

{main}()                              .../index.php:0
require_once( '/wp-admin/admin.php' ) .../index.php:10
require_once( '/wp-load.php' )        .../admin.php:31
require_once( '/wp-config.php' )      .../wp-load.php:44
require_once( '/wp-settings.php' )    .../wp-config.php:118
WP->init()                            .../wp-settings.php:398
wp_get_current_user()                 .../class-wp.php:595

WP->init() runs between the after_setup_theme and the init action.

With WordPress 4.7 the function stack for adminadmin (and super admin) requests will look like this:

{main}()                              .../index.php:0
require_once( '/wp-admin/admin.php' ) .../index.php:10
require_once( '/wp-load.php' )        .../admin.php:31
require_once( '/wp-config.php' )      .../wp-load.php:42
require_once( '/wp-settings.php' )    .../wp-config.php:127
load_default_textdomain()             .../wp-settings.php:389
get_user_locale()                     .../l10n.php:665
wp_get_current_user()                 .../l10n.php:92

That’s because load_default_textdomain() needs to know the localeLocale A locale is a combination of language and regional dialect. Usually locales correspond to countries, as is the case with Portuguese (Portugal) and Portuguese (Brazil). Other examples of locales include Canadian English and U.S. English. of the current user. load_default_textdomain() is called after setup_theme and before after_setup_theme (which is before WP->init()).
If you compare this with the stack for the customizer then you’ll notice that wp_get_current_user() is still loaded much later.

get_user_locale() is also used in the other text domain loading functions like load_plugin_textdomain() or load_theme_textdomain(). For backward compatibility we’ve made sure that no fatal errors are thrown when one of them is called before WordPress is fully initialized, see [39127] and [39134].

Until recently BuddyPress and bbPressbbPress Free, open source software built on top of WordPress for easily creating forums on sites. https://bbpress.org. had a custom notice when a user was initialized without using WP->init(). This was fixed in #7305-buddypress and #2309-bbpress together with a new wp_roles_init 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. in coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress.. The new filter allows plugins to add their own custom roles whenever they’re initialized, see #23016.

#4-7, #bootstrap-load, #dev-notes

User Admin Languages and Locale Switching in 4.7

In WordPress 4.7 users will be able to select their preferred localeLocale A locale is a combination of language and regional dialect. Usually locales correspond to countries, as is the case with Portuguese (Portugal) and Portuguese (Brazil). Other examples of locales include Canadian English and U.S. English. (language) when editing their profile. 🎉🎉 This allows for greater personalization of the WordPress adminadmin (and super admin) and therefore a better user experience.

The back end will be displayed in the user’s individual locale while the locale used on the front end equals the one set for the whole site. If the user didn’t specify a locale, the site’s locale will be used as a fallback. The new locale property of the WP_User class can be used to retrieve the user’s locale setting.

The new user language option when editing the profile

The new user language option

To enable sending emails in the language of the recipient and not the current user’s language, an 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. has been introduced to switch locales and translations at any point during the page load.

Here are some of the new API functions with usage examples.

get_user_locale( $user_id )

This function is used to retrieve the locale of any user. When no user ID is set it uses the current user. The user locale should be used for any user-facing screens in the admin.

The user locale is stored as a user 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. locale. Therefore it can be modified with the get_{$meta_type}_metadata 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..
If the meta field is empty the value of get_locale() is returned.

switch_to_locale( $locale )

switch_to_locale() switches the locale and (un)loads text domains according to a given locale. This includes the global $wp_locale object as well. It can be used together with get_locale() or get_user_locale(). CoreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress.’s main purpose of this function is to be able to send emails like password/email change notifications in the recipient’s language. Admin emails, emails for  get_option( 'admin_email' ), are using get_locale(), the site language.

When switching a locale several actions are fired which allows one to perform further actions:

  • change_locale: Fires when a locale is switched or restored. Core uses it to re-init post types and taxonomies.
  • switch_locale: Fires when a locale is switched.
  • restore_previous_locale: Fires when a locale is restored to the previous one.

restore_previous_locale() and restore_current_locale()

restore_previous_locale() can be used to restore the previous locale. Example:

$locale_switched = switch_to_locale( get_user_locale() );

// Do something.

if ( $locale_switched ) {
    restore_previous_locale();
}

Use restore_current_locale() if you want to empty the current stack and restore the original locale.

is_locale_switched()

As the name implies, it checks whether switch_to_locale() is in effect.

WP_Locale_Switcher

switch_to_locale(), restore_previous_locale(), restore_current_locale(), and is_locale_switched() are wrapping the same named methods of a new WP_Locale_Switcher class. This class is responsible for holding a stack of locales and filtering the actual locale through the locale filter.

Note about admin-ajax.php

As admin-ajax.php is in the admin, anyone getting translated strings via Ajax will get strings in the user’s locale when they are logged in. You can use switch_to_locale( get_locale() ) to ensure the string is returned in the site’s locale, rather then the user’s locale. Or, ideally, leverage 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/.. 💪🏼

For background information on these changes see #29783, #26511, and #38485.

Related 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 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.: Changed loading order for current user in 4.7.

#4-7, #dev-notes, #i18n

Multisite Focused Changes in 4.7

Howdy. The 4.7 release cycle has been a chance to build on some of the work from the last couple releases in multisitemultisite Used to describe a WordPress installation with a network of multiple blogs, grouped by sites. This installation type has shared users tables, and creates separate database tables for each blog (wp_posts becomes wp_0_posts). See also network, blog, site.  If you’d like more detail, check out the full list of multisite focused changes in this release.

get_blog_details() replaced with get_site()

A lot of progress has been made over the last few releases to get things in place for this transition. Now that WP_Site and WP_Network objects exist and are accessible with functions like get_site() and get_network(), they can be implemented throughout coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress..

In WordPress 4.7, get_blog_details() was replaced throughout core code with the modern  get_site(). The roadmap for this includes deprecating get_blog_details() in WordPress 4.8, so take this cycle as a chance to move your code in that direction.

get_site() is often a direct replacement, though get_sites() can also be used to query for sites when an ID is not available.

See #37102 for details on this change.

blog_details 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. deprecated

In combination with the decision to stop using get_blog_details() throughout core, the (not widely used) blog_details filter has been deprecated. It has been added to get_site() to provide backward compatibility with the above change and will fire with a deprecation notice. 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 code should use the site_details filter instead. See #38491 for details on this change.

_network_option actions and filters get $network_id

The $network_id associated with the use of a _network_option() function is now passed to the filters and actions that fire within. This provides granular control that was not available when first introduced. See #38319, #38320, #38321, and #38322 for details on this change.

wp_get_network() deprecated

It is now recommended that get_network() is used instead. See #37553 for this change.

 

 

#4-7, #dev-notes, #multisite, #network-sites

Attributes for Resource Hints in 4.7

WordPress 4.6 added support for Resource Hints, a W3CW3C The World Wide Web Consortium (W3C) is an international community where Member organizations, a full-time staff, and the public work together to develop Web standards.https://www.w3.org/. specification that “defines the dns-prefetch, preconnect, prefetch, and prerender relationships of the HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. Link Element (<link>)”. These can be used to assist the browser in the decision process of which origins it should connect to, and which resources it should fetch and preprocess to improve page performance.

With WordPress 4.7, you’re now able to pass specific HTML attributes to these resource hints to make even better use of them. Namely, the as, crossorigin, pr, and type attributes can now be defined when using the wp_resource_hints 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.. See #38121 for more information.

Here’s an example of how one can use this new feature:

function makewp_example_resource_hints_attributes( $hints, $relation_type ) {
	if ( 'prefetch' === $relation_type ) {
		$hints[] = array(
			'crossorigin' => 'use-credentials',
			'as'          => 'style',
			'pr'          => 0.5,
			'href'        => 'https://example.com/foo.css',
		);
	}

	return $hints;
}

add_filter( 'wp_resource_hints', 'makewp_example_resource_hints_attributes', 10, 2 );

While crossorigin can be used to set the CORS settings for a resource, pr indicates the expected probability that the specified resource hint will be used. The official W3C specification has more information about these attributes.

Note: preload is not yet supported by wp_resource_hints(), mainly because of a lack of browser support and benefit for coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress.. This will continue to be reevaluated as browser support evolves for these emerging features.

#4-7, #dev-notes, #script-loader

New Post Type Labels in 4.7

In WordPress 4.7, two additional labels have been made available for custom post types. These get passed in via the labels argument when using register_post_type(). The following labels are new:

  • view_items – The post type archive label used in the toolbar on the edit screen. Default “View Posts” / “View Pages”. See #34113.
  • attributes – The label used for the title of the post attributes 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. box (used to select post type templates). Default “Post Attributes”/ “Page Attributes”. See #18375.

 

#4-7, #dev-notes, #i18n

Post Type Templates in 4.7

WordPress has supported custom page templates for over 12 years, allowing developers to create various layouts for specific pages. While this feature is very helpful, it has always been limited to the ‘page’ post type and not was not available to other post types. With WordPress 4.7, it will be.

By opening up the page template functionality to all post types, the template hierarchy’s flexibility continues to improve.

In addition to the Template Name file 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., the post types supported by a template can be specified using Template Post Type: post, foo, bar. Here’s an example:

<?php
/*
Template Name: Full-width layout
Template Post Type: post, page, product
*/

// … your code here

That way, you’ll be able to select this full-width template for posts, pages, and products.

When at least one template exists for a post type, the ‘Post Attributes’ 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. box will be displayed in the back end, without the need to add post type support for 'page-attributes' or anything else. The ‘Post Attributes’ label can be customized per post type using the 'attributes' label when registering a post type.

Backward Compatibility

Let’s say you want to publicly release a theme with support for post type templates. WordPress versions before 4.7 will ignore the Template Post Type header and show the template in the list of page templates, even though it only works for regular posts. To prevent that, you can hook into the theme_page_templates 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 exclude it from the list. Here’s an example:

/**
 * Hides the custom post template for pages on WordPress 4.6 and older
 *
 * @param array $post_templates Array of page templates. Keys are filenames, values are translated names.
 * @return array Filtered array of page templates.
 */
function makewp_exclude_page_templates( $post_templates ) {
	if ( version_compare( $GLOBALS['wp_version'], '4.7', '<' ) ) {
		unset( $post_templates['templates/my-full-width-post-template.php'] );
	}

	return $post_templates;
}

add_filter( 'theme_page_templates', 'makewp_exclude_page_templates' );

That way you can support 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. templates in WordPress 4.7 and beyond while maintaining full backward compatibility.

Note that theme_page_templates is actually a dynamic theme_{$post_type}_templates filter. The dynamic portion of the hook name, $post_type, refers to the post type supported by the templates. E.g. you can hook into theme_product_templates to filter the list of templates for the product post type.

For further information about this new feature, see the corresponding ticketticket Created for both bug reports and feature development on the bug tracker. #18375.

#4-7, #dev-notes

wp_list_sort() and WP_List_Util in 4.7

If you have been working with arrays of multiple objects or arrays of multiple arrays before, you might be familiar with the functions wp_list_filter(), wp_list_pluck(), and wp_filter_object_list() which is essentially a combination of the previous two. WordPress 4.7 brings a few enhancements to handling such object or array lists.

New Utility Function to Sort Lists

WordPress 4.7 introduces a new function wp_list_sort() that makes it easy to sort object or array lists by one or more of its elements’ properties.

Usage

In addition to the function’s first parameter $list which should receive the array to sort, you can pass the name of one of the elements’ properties as the second parameter $orderby and the sort direction (either ‘ASC’ or ‘DESC’) as the third parameter $order. For example, if you had a list of post objects and you needed to sort them by their dates in descending order, you would use the function as follows:

$sorted_posts = wp_list_sort( $posts, 'post_date', 'DESC' )

Similar to the orderby and order arguments of the query classes in WordPress, the function also supports sorting by multiple properties by passing an array of properties and their sort direction as the $orderby parameter. When using the function like this, the $order parameter can be omitted and will be ignored. For example, here’s how you’d first order some posts by their dates in descending order and then by their titles in ascending order afterwards:

$sorted_posts = wp_list_sort( $posts, array(
  'post_date'  => 'DESC',
  'post_title' => 'ASC',
) )

The fourth parameter $preserve_keys is a boolean and can be set to true if the array is associative and its keys should be preserved. By default the parameter is set to false, thus most appropriate for indexed arrays.

Internals

Internally the function uses PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20’s usort() by default, or uasort() if the $preserve_keys parameter is set to true. Numeric values will be compared by their amount while strings will be compared via strcmp(). The function should not be used to compare booleans or non-scalar values. Be aware that PHP’s sorting algorithms are not stable, so when two elements have identical values for all the properties compared, they will not necessarily remain in the same order as before.

Better Organization of List Utilities

While wp_list_sort() provides new functionality, existing list utilities have been revamped as well: WordPress 4.7 introduces a new WP_List_Util class as a central access point for handling lists. The previously mentioned functions have been adjusted to become wrappers for methods inside that new class.

Beside the structural improvements of moving such functionality into a centralized class, the class provides an optimized way to run multiple tasks on a list, e.g. filtering, sorting and plucking a property out of a list through one instance of the class.

By using the method WP_List_Util::get_output() you can access the list in its current state at any time, while WP_List_Util::get_input() can be used to access it in its original state. A good example on how to use the new class is wp_filter_object_list() which handles both filtering and plucking a property.

For the background discussion around these changes, see #37128.

#4-7, #dev-notes

WP_Taxonomy in 4.7

Similar to how WordPress 4.6 introduced WP_Post_Type, version 4.7 will introduce a new WP_Taxonomy class. This changes the global $wp_taxonomies to an array of WP_Taxonomy objects.

WP_Taxonomy provides methods to handle rewrite rules and 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.. These methods are used internally by register_taxonomy() and unregister_taxonomy(). Each taxonomyTaxonomy A taxonomy is a way to group things together. In WordPress, some common taxonomies are category, link, tag, or post format. https://codex.wordpress.org/Taxonomies#Default_Taxonomies. argument is now a property of WP_Taxonomy.

The following function has been changed to return a WP_Taxonomy object:

  • get_taxonomy()

The following hook parameters are now a WP_Taxonomy object:

  • The second parameter $taxonomy of xmlrpc_prepare_taxonomy.
  • The second parameter $tax of term_search_min_chars.

The following function accepts a WP_Taxonomy object now:

  • get_taxonomy_labels()

Just like with WP_Post_Type, introducing such a class makes further improvements much more feasible.

For more background on the change, see #36224.

#4-7, #dev-notes

Fine grained capabilities for taxonomy terms in 4.7

WordPress 4.7 introduces new capabilities for individual taxonomyTaxonomy A taxonomy is a way to group things together. In WordPress, some common taxonomies are category, link, tag, or post format. https://codex.wordpress.org/Taxonomies#Default_Taxonomies. terms, allowing developers to implement more fine grained control over management of terms (including categories, tags, and custom taxonomy terms). The new capabilities are 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. capabilities that ultimately map back to existing capabilities, so there is no change in behaviour for existing functionality and users.

New Singular Capabilities

  • edit_term
  • delete_term
  • assign_term

In the same way that capabilities such as edit_post and delete_user are used to check that a user can perform the action for a specific post or user, these new capabilities can be used to check that a user can perform the action for a specific term. If you’re currently checking the edit_terms, delete_terms, and assign_terms capabilities, you can switch these over to the singular form and include the term ID as the second parameter. Example:

if ( current_user_can( 'edit_term', $term_id ) ) {
	printf( '<a href="%s">Edit This</a>', get_edit_term_link( $term_id ) );
}

The new capabilities are meta capabilities — which means they ultimately map back to the existing manage_categories capability by default, or whichever capability has been specified in the corresponding capability arguments when registering the taxonomy. The actual required capabilities can be filtered using the map_meta_cap 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. like so:

add_filter( 'map_meta_cap', function( $required_caps, $cap, $user_id, $args ) {
	switch ( $cap ) {

		case 'delete_term':
			$term_id = $args[0];
			// Prevent a "protected" term from being deleted:
			if ( get_term_meta( $term_id, 'protected', true ) ) {
				$required_caps[] = 'do_not_allow';
			}
			break;

		case 'edit_term':
			// Introduce some anarchy:
			if ( rand( 1, 6 ) == 3 ) {
				$required_caps[] = 'do_not_allow';
			}
			break;

	}

	return $required_caps;
}, 10, 4 );

Separate Capabilities for Tags and Categories

A related change is that the post tagtag A directory in Subversion. WordPress uses tags to store a single snapshot of a version (3.6, 3.6.1, etc.), the common convention of tags in version control systems. (Not to be confused with post tags.) taxonomy now uses separate capabilities from the categoryCategory The 'category' taxonomy lets you group posts / content together that share a common bond. Categories are pre-defined and broad ranging. taxonomy by default (it previously used the same capabilities as the category taxonomy). This doesn’t alter existing behaviour (custom or otherwise), but it does mean that you can target capabilities for tags separately from categories. Example:

add_filter( 'map_meta_cap', function( $required_caps, $cap, $user_id, $args ) {
	switch ( $cap ) {

		case 'manage_post_tags':
		case 'edit_post_tags':
		case 'delete_post_tags':
		case 'assign_post_tags':
			// Allow Authors to manage tags:
			$required_caps = array(
				'publish_posts',
			);
			break;

	}

	return $required_caps;
}, 10, 4 );

See TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress. ticketticket Created for both bug reports and feature development on the bug tracker. #35614 for more information.

#4-7, #dev-notes