Fine grained capabilities for taxonomy terms in 4.7

WordPress 4.7 introduces new capabilities for individual taxonomy terms, allowing developers to implement more fine grained control over management of terms (including categories, tags, and custom taxonomy terms). The new capabilities are meta 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 filter 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 tag taxonomy now uses separate capabilities from the category 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 Trac ticket #35614 for more information.

#4-7, #dev-notes

Feature Project Proposal: Notifications API

Most of the situations where WordPress sends an outgoing email can be classified as a notification. X just happened on your website and you should be aware of it.

Back when WordPress was a youngster, the only way to reliably notify a user was via email. In 2016 we have many more options, including push notifications to mobile platforms, desktop notifications to browsers, messages to chat apps, endless services via webhooks, SMS messages, or even notifications in the WordPress admin area. The list goes on. For many users, email is no longer the optimal delivery mechanism for ephemeral notifications.

To that end, let’s think about replacing wp_mail() with a modern API that allows developers to route notifications to services other than email, allow them to better modify notifications and the way in which they’re formatted, and allow them to do so without stepping on each others’ toes.

The current lack of a notifications API (or even an email sending API) can be easily summed up:

Problem: Plugin A wants to provide HTML emails, and Plugin B wants to send emails via an email delivery service such as Mandrill. Plugin C wants to disregard emails and send Slack notifications. It’s very difficult for these to co-exist.

Notification Destinations

There are only two types of destination for a notification in WordPress. Most notifications are actually notifications to a user account that get delivered via email because it’s the only contact information available for every user account. The remaining notifications are explicitly notifications to an email address rather than a user account (or not yet attached to a user account), such as when a user signs up for a blog and needs to click a confirmation link before their user account gets created.

With this in mind, you might be able to imagine a notification class in WordPress core that defaults to delivering notifications via email, but which can be extended by a plugin on a per-user and per-notification basis to deliver notifications via any of the means listed above. WordPress core would support delivery via email and provide the API that allows plugins to implement delivery via other means.

With a well-designed API, multiple plugins can co-exist in order to deliver different notifications via different mechanisms, format email notifications as HTML, easily disable notifications, change the delivery mechanism for email notifications, or provide a UI for users to manage their notification preferences and destinations.

Planning a Notifications API

I’d like to begin work on a feature project with the intent of designing and implementing such an API. I’d like to get input from authors of existing plugins that provide functionality such as delivering notifications via a service other than email, that override the default email delivery mechanism, or that implement extra notifications (such as e-commerce sale notifications), in order that the API can be backwards compatible and that we can get some plugin implementations built during the API’s development.

I already have some technical ideas regarding how the API might look, but I’m conscious that such an API needs to be well-designed before anyone jumps into writing code. Maybe we can even try some test-driven development? Who knows.

In addition, consultation and involvement with the team that are working on the two-factor authentication feature project is important as it implements several delivery mechanisms for 2FA codes that could potentially be made simpler with a notifications API.

Get Involved

Feedback is most welcome in the comments. Would you like to help out? Yes? Great. Let’s plan a date and time for a meeting in Slack and go from there.

Finally, a reminder that feature projects are not tied to a specific release of WordPress. This won’t make 4.7. It would be great if it was mature enough for 4.8, but we won’t be aiming for a particular release just yet.

#feature-plugins, #notifications

Reminder: HTTPS meeting tomorrow

The weekly meetings about HTTPS improvements in core will resume tomorrow at 16:00 UTC (Friday, September 30, 2016, 16:00 UTC) in the #core-http channel on Slack. See you there!

#https

HTTPS Working Group

In WordPress 4.4 and 4.5, various pieces of work were done to improve HTTPS support in core, but not much has been tackled since then. To address this, I’m going to re-start the weekly chats in the #core-http channel in Slack. Fridays late afternoon UTC/GMT are good for me — does this work for other people who are interested in helping with HTTPS issues?

Although the HTTPS improvements are always ongoing and not tied to a particular release, it would be great to get some improvements into 4.7.

If you run a WordPress site over HTTPS only, support is very good and there are very few issues to contend with. If you’re running a multisite network on HTTPS there are a few small issues when adding new sites. However, the main HTTPS issues in core come from:

  • Enforcing the HTTPS scheme on assets (such as embedded images in post content, and enqueued JS and CSS).
  • Enforcing the HTTPS scheme on links, redirects, and canonical URLs.
  • Migrating an existing HTTP site to HTTPS.
  • Running a site that uses a mixture of HTTP and HTTPS.

The first two points — avoiding mixed content on HTTPS sites — need to be solved via an opt-in system (either via constants or filters) because enforcing these can cause issues with sites that run proxies (for example Cloudflare’s Universal SSL). Overall though, this ought to be a fairly straight forward set of enhancements to implement.

The third point is a potentially complex one which will need a lot of discussion and some ideas putting forward. How can core make life easier for a site owner who wishes to switch their site from HTTP to HTTPS? Should it be a case of being able to change the scheme in the URL on the General Settings screen or is there too much risk of breakage? What else can be done post-migration to aid the site owner, or will the opt-in enhancements for avoiding mixed content be enough?

The last point is one that, going forward, should be generally discouraged, however it needs to continue to be supported for multisite networks that use domain mapping and can’t serve every domain over HTTPS.

There’s an https keyword on Trac which has been applied to tickets that concern HTTPS issues. We’ll start going through this list in next week’s chat.

Here’s a bunch of further considerations that need to be taken into account while working on HTTPS issues:

  • Differing schemes, domains, and ports in the siteurl and home options.
  • Domain mapping
  • force_ssl_admin() usage
  • Self signed certs
  • No public access to admin URLs
  • Different HTTPS domain on front end (!)
  • HTTP site optionally available over HTTPS

Here’s a list of items that should be considered for enforcing over HTTPS:

  • Enqueued JS and CSS.
  • Post content, images, js, CSS, iframes,srcset, oembeds, forms.
  • How about other fields such as term descriptions, user bios, etc.
  • Force https links. Links to the current site.
  • Force https link in nav menus.
  • Force https redirects and/or canonical.
  • Force HSTS. (Probably not.)
  • Force https rest api endpoint.
  • Force https XML RPC.
  • Set https-only on cookies.

Let me know in the comments if you’d like to help out and if Fridays are good for the meeting time!

#https

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 theme 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 CSS or JavaScript 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 URL 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 Filter

#14310

This new dynamically-named filter allows the complete template hierarchy of a given request type to be filtered by a plugin 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 category archive based on the value of its term meta 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 ticket: #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.

Alert: 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.

#4-7, #dev-notes

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

HTTPS discussion meeting this Wednesday

In recent releases of WordPress there have been various improvements made to support for sites running on HTTPS. While support is currently very good, it’s still too easy to end up with mixed content on a site (HTTP content embedded within an HTTPS page), and especially so when migrating an existing site from HTTP to HTTPS.

There will be a discussion meeting in the #core-http Slack channel on Wednesday, January 27, 2016 at 2000 UTC. This is one hour before the regular weekly meeting in #core. I’d like to discuss three topics:

  1. Implementing an (opt-in) method of forcing a site to use HTTPS.
    • What should this cover? (Embedded content, enqueued scripts/styles, links, redirects)
    • How should it be implemented? (eg. filter/constant/automatic)
  2. Defaulting to HTTPS for new installs when it’s available.
    • Only applies when setting up a site over HTTP and it’s available over HTTPS.
    • Need to communicate clearly to the user what this implies, with option to toggle.
  3. Aiding in switching an existing site from HTTP to HTTPS.
    • Migrating existing embedded content.
    • Should this be a feature plugin?

If you’re interested in helping out with any of the above, or with HTTPS improvements in general, join us on Wednesday.

Further reading: the https tag on Core Trac.

#https, #meeting

Additional labels for custom post types and custom taxonomies

In WordPress 4.3 and 4.4, additional labels have been made available for custom post types and custom taxonomies. These get passed in via the labels argument when using register_post_type() and register_taxonomy().

New post type labels in 4.3:

  • featured_image – Overrides the “Featured Image” phrase for this post type. See #19257.
  • set_featured_image – Overrides the “Set featured image” phrase for this post type. See #19257.
  • remove_featured_image – Overrides the “Remove featured image” phrase for this post type. See #19257.
  • use_featured_image – Overrides the “Use as featured image” phrase for this post type. See #19257.

New post type labels in 4.4:

  • archives – The post type archive label used in nav menus. Default “Post Archives”. See #16075.
  • insert_into_item – Overrides the “Insert into post”/”Insert into page” phrase (used when inserting media into a post). See #33616.
  • uploaded_to_this_item – Overrides the “Uploaded to this post”/”Uploaded to this page” phrase (used when viewing media attached to a post). See #33616.
  • filter_items_list – Screen reader text for the filter links heading on the post type listing screen. Default “Filter posts list”/”Filter pages list”. See #32147.
  • items_list_navigation – Screen reader text for the pagination heading on the post type listing screen. Default “Posts list navigation”/”Pages list navigation”. See #32147.
  • items_list – Screen reader text for the items list heading on the post type listing screen. Default “Posts list”/”Pages list”. See #32147.

New taxonomy labels in 4.3:

  • no_terms – Used when indicating that there are no terms in the given taxonomy associated with an object. Default “No tags”/”No categories”. See #32150.

New taxonomy labels in 4.4:

  • items_list_navigation – Screen reader text for the pagination heading on the term listing screen. Default “Tags list navigation”/”Categories list navigation”. See #32147.
  • items_list – Screen reader text for the items list heading on the term listing screen. Default “Tags list”/”Categories list”. See #32147.

See the documentation for get_post_type_labels() and get_taxonomy_labels() for the full list of available labels.

Tweaks to user searching and management

A few improvements have been made to user searching and user management in WordPress 4.3 and the upcoming 4.4. Here’s an overview:

  • 4.3: Performing a search on the Users screen now searches the user’s username, email address, display name, nicename, and URL, instead of just their username and nicename. See #27304
  • 4.4: Performing a search on the Network Admin -> Users screen previously required the use of a * wildcard character at the beginning and/or end of the search term, otherwise the search required an exact match. This is no longer the case, so finding users on Multisite is no longer frustrating and inexplicably dysfunctional. This, combined with the changes in 4.3, means searching for a phrase such as “@gmail.com” now works as you would expect. See #32913
  • 4.4: It’s now possible to filter the Users screen by users who have no role (in addition to being able to filter the screen by individual roles), if there are such users. See #22993
  • 4.4: Users with multiple roles (it’s possible to programatically give a user multiple roles, although this isn’t possible via the UI) are now shown as having multiple roles on the Users screen. This helps avoid obfuscation of a user’s roles. If your plugin facilitates the assignment of multiple roles to an individual user, you should test it against trunk and look at using the new get_role_list filter introduced in [34963] if necessary. See #22959

Any other improvements you think could be made? Leave a comment.

#4-3, #4-4

add_rewrite_rule() accepts an array of query vars in WordPress 4.4

A small change to add_rewrite_rule() in [34708] means that in the upcoming WordPress 4.4 an array can be passed as the second parameter instead of a query string.

Previously:

add_rewrite_rule( 'foo/([^/]+)/bar/([^/]+)/?$', 'index.php?post_type=foo&name=$matches[1]&taxonomy=bar&term=$matches[2]' );

Now:

add_rewrite_rule( 'foo/([^/]+)/bar/([^/]+)/?$', array(
	'post_type' => 'foo',
	'name'      => '$matches[1]',
	'taxonomy'  => 'bar',
	'term'      => '$matches[2]',
) );

While this isn’t the biggest change in the world, it makes this parameter much easier on the eyes.

(Note that your $matches variables need to remain in single quotes!)

#dev-notes

single-{post_type}-{post_name}.php: New theme template in WordPress 4.4

A new theme template has been added to the theme hierarchy as of r34800: single-{post_type}-{post_name}.php.  This template follows the rules of is_single() and is used for a single post or custom post type. It’s most useful for targeting a specific post in a custom post type, and brings consistency to the templates available for pages and taxonomies. It comes in the hierarchy before single.php and single-{post_type}.php.

Don’t forget, too, that in WordPress 4.3 singular.php was introduced and the hierarchy of attachment templates was fixed.

#dev-notes, #templates, #themes