Defensive Data Design

As we go into a world where agents will be doing more things, how can we make the defaults as safe (and secure) as possible? Some points to consider:

  • Putting something in the trashTrash Trash in WordPress is like the Recycle Bin on your PC or Trash in your Macintosh computer. Users with the proper permission level (administrators and editors) have the ability to delete a post, page, and/or comments. When you delete the item, it is moved to the trash folder where it will remain for 30 days. should be easy; deleting should be very hard.
  • Proposing a draft should be easy; publishing to the world should be hard.
  • Changes should be reversible and visible wherever possible.
  • How can we show the data behind the object in a way that makes it more legible and understandable, not less?
  • Error messages should be how you’d describe it to a friend. And have a copy-to-clipboard next to it, so people can put into search/AI/whatever.
  • If you error out, say the why not just the what.
  • Fail gracefully, in a way that minimizes harm or exposure.
  • Assume everything (networking / formatting / structure) is not just unreliable, but actually hostile.
  • Use the space you have, don’t make people click when you could have shown them something directly.
  • Plain language, make it obvious, natural. (And also fun.)

Introducing name and informational tool tips in WordPress 7.1

WordPress 7.1 comes with two new functions for rendering tool tips and informational help in coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress.. Similar tool tips already exist in the editor, but were not available in the rest of the administration. This change brings improved 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) for icon-only controls and situations where further explanation of a user interface element is needed.

Inf #51006, two new functions were added: wp_get_tooltip() and wp_get_toggletip().

The first function, wp_get_tooltip(), provides name visibility for buttons or links are otherwise only represented as icons. The second, wp_get_toggletip(), adds a button that can be triggered to view extended information about related controls.

In 7.1, each function has one relevant implementation. wp_get_tooltip() has been implemented on post 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 controls (move up, move down, and show/hide) to make those accessible names visible. wp_get_toggletip() has been added on the main login screen to provide an extended description of what the “Remember Me” checkbox does.

Enqueuing Required scripts and styles

The required CSSCSS Cascading Style Sheets. for tooltips is loaded globally, but the 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 is only loaded by default where post meta boxes are used and in the login screen.

You can enqueue required CSS on the front-end using:

wp_enqueue_style( 'wp-tooltip' );

You can enqueue required JavaScript as needed using:

wp_enqueue_script( 'wp-tooltip' );

Accessibility Notes

Both of these functions exist to provide support for gaps in accessibility support. It is always preferable for interface controls to have visible, persistent text labels, and it is also preferable to user interfaces to be structured so that additional help information is not required or is provided as visible descriptive text associated with the relevant input field.

However, we understand that space can be at a premium in complex web interfaces, and it is not always feasible to provide text labels for all controls. It is also a common case that explanatory text does not directly relate to one specific input, so that the aria-describedby solution is not practical. Using these functions can help make sure that the information provided for users is as accessible as possible within an interface that is imposing other limitations.

Tooltips

The function for adding tooltips is wp_get_tooltip(), and is only intended to provide visibility for controls that do not have a visible accessible name. For accessibility, it is still always the more accessible choice for a control to have persistently visible text.

Parameters for wp_get_tooltip()

  • string $content Plain-text tool tip content.
  • array $args An array of optional arguments for this tooltip.
    • string $id A unique ID for the popover element containing the tooltip. Default is a generated unique ID.
    • string $button Existing button or a markup. Used instead of a generated button.
    • string $label Unused for tool tips, but documented as part of the helper function.
    • string $close_label Unused for tool tips, but documented as part of the helper function.
    • string $icon A dashicons icon class for the toggle button. Default is dashicons-editor-help.
    • string $class Additional classes.

No additional arguments are required for wp_get_tooltip(); it will render a button with a tooltip control when only passed the $content argument. The $button parameter is available to make it easier to pass existing controls into the rendering button, and will be processed using the WP_HTML_Tag_Processor to add required attributes.

Parameters for wp_get_toggletip()

The function for adding information help text is intended for exposing additional help information.

  • string $content Plain-text tool tip content.
  • array $args An array of arguments for this tooltip.
    • string $id A unique ID for the popover element containing the tooltip. Default is a generated unique ID.
    • string $button Existing button or a markup. Used instead of a generated button.
    • string $label Accessible label for the toggle button. Default ‘Help’, matching the default icon. Ignored for tooltips.
    • string $close_label Accessible label for the close button. Default ‘Close’.
    • string $icon A dashicons icon class for the toggle button. Default is dashicons-editor-help.
    • string $class Additional classes.

Example Usages

Generate a button with a tooltip using a menu icon.

wp_get_tooltip(
    __( 'Show/Hide Menu', 'my-text-domain' ),
    array(
        'icon' => 'dashicons-menu',
    )
);

Output

<span class="wp-tooltip wp-is-tooltip">
    <button class="wp-tooltip__toggle" type="button" aria-label="Show/Hide Menu">
        <span class="dashicons dashicons-menu" aria-hidden="true"></span>
    </button>
    <span popover="hint" id="wp-tooltip-1" class="wp-tooltip__bubble" role="tooltip">
        <span id="wp-tooltip-1-text" class="wp-tooltip__text">Show/Hide Menu</span>
    </span>
</span>

Note: All generic markup uses span elements, to ensure that the control can be validly inserted inside any element. Using div or other sectioning elements would disallow usage within a paragraph.

Generate a button with a toggle tip.

wp_get_toggletip(
    __( 'Selecting "Remember Me" reduces the number of times you&#8217;ll be asked to log in using this device. To keep your account secure, use this option only on your personal devices.', 'my-text-domain' ),
    array(
        'id'    => 'rememberme-help-toggletip',
        'label' => 'Learn More',
        'icon'  => 'dashicons-welcome-learn-more',
    )
);

Output

<span class="wp-tooltip wp-is-toggletip">
    <button aria-haspopup="dialog" class="wp-tooltip__toggle" popovertarget="rememberme-help-toggletip" type="button" aria-label="Learn More">
        <span class="dashicons dashicons-welcome-learn-more" aria-hidden="true"></span>
    </button>
    <span popover="auto" id="rememberme-help-toggletip" class="wp-tooltip__bubble" role="dialog" aria-label="Learn More" tabindex="-1" autofocus="">
        <span id="rememberme-help-toggletip-text" class="wp-tooltip__text">Selecting "Remember Me" reduces the number of times you’ll be asked to log in using this device. To keep your account secure, use this option only on your personal devices.</span>
        <button type="button" class="wp-tooltip__close" popovertarget="rememberme-help-toggletip" popovertargetaction="hide" aria-label="Close">
            <span class="dashicons dashicons-no-alt" aria-hidden="true"></span>
        </button>
    </span>
</span>

Reviewed by @afercia, Props @wongjn

#7-1, #dev-notes, #dev-notes-7-1

Post list tables row headers changed

The column used as the primary list table th with scope="row" was moved from the first column, containing the selection checkbox, to the second column, containing the post title and row actions in #32892.

This change significantly improves 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) for post list tables by ensuring that the naming used for screen readers to identify the current row refers consistently to the name of the relevant post, rather than referring to a selection checkbox that may not be present if the post is not currently available for editing.

This is expected to have an impact on extenders who assign custom styles or use 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 to register events using the th.check-column selector or similar selectors that expect a checkbox inside the th in a posts row.

This is also potentially going to impact extenders who target row actions or post titles with the expectation they are inside a td.

Additionally, the CSSCSS Cascading Style Sheets. for collapsed table cells in the responsive view port has been updated to use flex layout.

What you may need to change

CSS and JSJS JavaScript, a web scripting language typically executed in the browser. Often used for advanced user interfaces and behaviors. selectors targeting check columns should look for selectors targeting th.check-column or th input[type="checkbox"].

CSS and JS selectors targeting titles or row actions should look for selectors targeting td.title, td.column-title, td.page-title, td.column-primary, td .row-title, td .post-state, td .row-actions, or other similar selection patterns.

Retaining both td and th selectors will support retaining compatibility with versions of WordPress prior to 7.1.

Before and After

Code Before Change

<tr>
  <th scope="row" class="check-column">
    <input type="checkbox" name="post[]" value="123">
  </th>
  <td class="title column-title column-primary page-title">
    <a class="row-title" href="...">Hello world!</a>
  </td>
  <td class="author column-author">admin</td>
</tr>

The th is in the first column, followed by a td with the column title.

Code After Change

<tr>
  <td class="check-column">
    <input type="checkbox" name="post[]" value="123">
  </td>
  <th scope="row" class="title column-title column-primary page-title" aria-label="Hello world!">
    <a class="row-title" href="...">Hello world!</a>
  </th>
  <td class="author column-author">admin</td>
</tr>

The th is in the second column, with an aria-label containing the post title. The check-column is now a td.

Reviewed by @wildworks.

#7-1, #dev-notes, #dev-notes-7-1

Iframed Editor Changes in WordPress 7.1

Previous posts on this topic:

Overview

For several releases, WordPress has been moving its editors into an iframeiframe iFrame is an acronym for an inline frame. An iFrame is used inside a webpage to load another HTML document and render it. This HTML document may also contain JavaScript and/or CSS which is loaded at the time when iframe tag is parsed by the user’s browser., starting with the template editor back in 5.8. In WordPress 7.1, the post editor takes the final step: it is now always iframed.

If you want the full reasoning behind the move, see Benefits of the iframe editor.

Current Situation

Every editor except the post editor — the site editor, the template editor, and all blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. template, and device previews — has been iframed unconditionally for some time. The post editor was the exception, and until now, whether it was iframed depended on the environment: whether the GutenbergGutenberg The Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. https://wordpress.org/gutenberg/ 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. was active and the blocks in use.

  • In WordPress 7.0, the decision was based on the block 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. versions of the blocks actually inserted in the post. If every inserted block was API version 3 or higher, the post editor was iframed; if any lower-version block was present, the iframe was dropped to preserve compatibility.
  • The Gutenberg plugin has been ahead of coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. here: since Gutenberg 22.6, when the plugin is active the post editor is forced to be iframed regardless of the theme type or the block API versions in use, so that compatibility issues surface early and can be reported before the change reaches core.

This conditional behavior kept older blocks working, but it also meant the post editor could switch between iframed and non-iframed modes depending on a post’s content.

What’s changing in WordPress 7.1

Starting in WordPress 7.1, the post editor is always iframed, regardless of the theme type, the block API versions of the registered blocks, or the block API versions of the blocks in the content.

Starting in WordPress 7.1, the post editor is always iframed, regardless of the theme type, the block API versions of the registered blocks, or the block API versions of the blocks in the content. The site editor, template editor, and device previews have been iframed for a long time, so much of this ground is already well tested. Even so, please test that your custom blocks — and any plugins that extend blocks — work correctly in the fully iframed post editor. See WordPress/gutenberg#74042 for more details.

The site editor, template editor, and device previews have been iframed for a long time, so much of this ground is already well tested. Even so, please test that your custom blocks — and any plugins that extend blocks — work correctly in the fully iframed post editor.

See WordPress/gutenberg#74042 for more details.

What should block developers do?

Most blocks already work in the iframed editor without any changes. The issues that do come up almost always trace back to the same root cause: the iframe has its own document and window, separate from the adminadmin (and super admin) page where editor scripts run. Code that reaches for the global document or window to touch the editor canvas will be looking at the wrong document.

The usual fixes are:

  • Get the canvas document from an element inside it, via ownerDocument and its defaultView, rather than the global document/window.
  • Use useRefEffect to attach and clean up event listeners on canvas elements.

For the full list of things to watch for and how to resolve them, see Technical considerations for the iframe editor.

Props to @tyxla for review.

#7-1, #dev-notes, #dev-notes-7-1

JSON Schema preparation for client compatibility in WordPress 7.1

WordPress 7.1 introduces a shared JSONJSON JSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. It is used primarily to transmit data between a server and web application, as an alternative to XML. Schema preparation layer for schemas exposed to REST clients, frontend applications, and AI tools.

WordPress accepts several internal schema conventions that are useful during server-side validation but are not portable JSON Schema draft-04. Passing these schemas directly to external validators could cause validation errors or expose PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher callbacks and other server-only implementation details.

The wp_prepare_json_schema_for_client() function

The new wp_prepare_json_schema_for_client() function converts a WordPress schema into a portable, client-facing representation before it is exposed to REST clients, frontend applications, AI tools, or other external consumers.

Most developers do not need to take action. CoreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. now applies this preparation automatically to:

  • Abilities 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. schemas exposed through REST responses.
  • Ability input schemas converted into AI Client function declarations.
$prepared_schema = wp_prepare_json_schema_for_client( $schema );

This keeps the schemas used by clients (REST clients, 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 applications, and AI tools) consistent. See ticketticket Created for both bug reports and feature development on the bug tracker. #64955 and changeset [62591].

Compatibility guidance

This change is primarily automatic. Existing ability registration and execution code does not need to call the new function.

Call wp_prepare_json_schema_for_client() directly when 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. exposes a WordPress-style schema outside the server-side PHP validation boundary, for example through:

  • a custom REST endpoint;
  • a JavaScript configuration object;
  • an MCP tool declaration;
  • an AI function declaration; or
  • another external schema consumer.

Do not replace the schema stored by an ability with the prepared version. Keep the canonical WordPress schema for server-side use and prepare a copy only when sending it to a client.

Choosing a schema profile

This new function accepts a schema and an optional schema profile:

/** 
 * Prepares a JSON Schema for clients.
 * 
 * @param array<string, mixed> $schema         The schema array.
 * @param string               $schema_profile Optional. Name of the schema
 *                                             profile whose keywords should be
 *                                             preserved. Default 'draft-04'.
 * @return array<string, mixed> The prepared schema.
 */
wp_prepare_json_schema_for_client(
	array $schema,
	string $schema_profile = 'draft-04'
): array

WordPress provides two schema profiles out of the box:

  • draft-04 is the default. Use it when publishing a standalone schema to general-purpose clients, including Ability metadata, frontend validators, MCP integrations, and AI tooling. It preserves the broader JSON Schema Draft 4 vocabulary, including composition and reference keywords such as $ref, definitions, allOf, not, dependencies, and additionalItems.
  • rest-api uses the narrower keyword set supported by WordPress 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/ route schemas. Use it when preparing a schema that must follow the same conventions as a REST route’s argument or response schema.
// General client-facing or Ability schema.
$prepared_schema = wp_prepare_json_schema_for_client( $schema );

// Schema intended to match WordPress REST API conventions.
$prepared_rest_schema = wp_prepare_json_schema_for_client(
	$schema,
	'rest-api'
);

Both profiles produce JSON Schema Draft 4 output. The difference is the set of keywords retained in the prepared schema.

What is JSON Schema Draft 4?

“Draft 4” refers to the fourth published draft of the JSON Schema specification, which defines a JSON-based contract for describing and validating JSON data. See the JSON Schema Draft 4 core specification for its terminology and behaviour.

Schema transformations

Preparation is recursive and applies to nested object properties, array items, composition keywords, definitions, dependencies, and other subschemas.

Required properties use Draft 4 syntax

WordPress schemas may mark individual properties as required:

'properties' => array(
	'title' => array(
		'type'     => 'string',
		'required' => true,
	),
)

The prepared schema moves those property names into the containing object’s Draft 4 required array:

'required' => array( 'title' ),

The property-level boolean is then removed.

If the object already has a valid required array, that array takes precedence over property-level boolean values. A property-level required => false is removed without creating an empty required array.

A boolean required value on a scalar schema is also removed because it has no Draft 4 equivalent.

This preparation only affects schemas sent to clients. It does not change the server-side behaviour of rest_validate_value_from_schema() or WP_Ability::validate_input().

Example of transforming schema:

$schema = array(
	'type'       => 'object',
	'properties' => array(
		'title'   => array(
			'type'              => 'string',
			'required'          => true,
			'sanitize_callback' => 'sanitize_text_field',
		),
		'content' => array(
			'type'              => 'string',
			'validate_callback' => 'is_string',
		),
	),
);

$prepared_schema = wp_prepare_json_schema_for_client( $schema );

The prepared schema is equivalent to:

array(
	'type'       => 'object',
	'required'   => array( 'title' ),
	'properties' => array(
		'title'   => array(
			'type' => 'string',
		),
		'content' => array(
			'type' => 'string',
		),
	),
);

The original $schema is not modified.

Server-only keywords are removed

PHP callbacks and other WordPress-specific keywords cannot be represented meaningfully in JSON. The preparation process removes unsupported keywords, including:

  • sanitize_callback
  • validate_callback
  • arg_options

These keywords are removed recursively, including when they appear inside:

  • properties
  • patternProperties
  • definitions
  • dependencies
  • items
  • additionalItems
  • additionalProperties
  • anyOf
  • oneOf
  • allOf
  • not

The callbacks remain available in the original server-side schema. They are removed only from its client-facing representation.

Ability authors should also note that validate_callback and sanitize_callback are not executed by the Abilities API’s runtime validation. Custom ability validation should use the wp_ability_validate_input and wp_ability_validate_output filters introduced in WordPress 7.1.

Empty object defaults are represented as objects

In PHP, an empty array serializes to [], even when its schema declares an object:

array(
	'type'    => 'object',
	'default' => array(),
)

For client-facing schemas, the empty default is prepared so that JSON serialization produces an object:

{
	"type": "object",
	"default": {}
}

This prevents client validators from rejecting the default because its serialized type does not match the declared object type.

Allowed keywords 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.

wp_prepare_json_schema_for_client() uses wp_get_json_schema_allowed_keywords() to decide which keywords to preserve.

The broader draft-04 profile can preserve composition and documentation keywords such as:

  • $ref
  • definitions
  • allOf
  • not
  • dependencies
  • additionalItems

Preserving a keyword means that it may be included in the client-facing schema. It does not mean that WordPress validates or sanitizes values against that keyword on the server.

The allowed keyword list is filterable:

add_filter(
	'wp_json_schema_allowed_keywords',
	function ( $keywords, $schema_profile ) {
		if ( 'draft-04' === $schema_profile ) {
			$keywords[] = 'x-example-keyword';
		}

		return $keywords;
	},
	10,
	2
);

Custom keywords should only be exposed when the receiving clients are known to understand them.

Abilities REST API changes

WordPress now prepares an ability’s input and output schemas before including them in Abilities REST API responses.

For abilities registered with REST visibility, consumers of endpoints under:

/wp-json/wp-abilities/v1/abilities

receive portable schemas instead of the original WordPress-internal schema arrays.

This is an output-boundary transformation:

  • WP_Ability::get_input_schema() and WP_Ability::get_output_schema() continue to return the original schemas to server-side PHP.
  • REST responses contain prepared versions.
  • Ability execution and server-side validation remain unchanged.

Developers comparing a schema retrieved directly from a WP_Ability object with one returned through REST may therefore see intentional differences.

AI Client integration

When the WordPress AI Client converts abilities into function declarations, it now prepares each ability’s input schema first:

$input_schema = wp_prepare_json_schema_for_client(
	$ability->get_input_schema()
);

This prevents WordPress-only schema keywords and nonportable required conventions from being passed directly into AI function declarations.

The helper prepares a portable Draft 4 schema; it is not a provider-specific compiler. Individual AI providers may support a smaller schema vocabulary or impose additional requirements. Provider-specific adaptation may therefore still occur elsewhere in the integration.

Follow-up

Provider-specific adaptation is tracked in WordPress/php-ai-client#256: each provider should be able to override the input schema for its own API requirements, which differ across providers and evolve over time.

  • #64955 — Add schema compiler for AI tool calling compatibility
  • Changeset [62591] — Abilities API: Reuse JSON Schema client preparation
  • Changeset [62549] — REST API: Add a shared helper for JSON Schema allowed keywords.
  • Changeset [62449] — Abilities API: Normalize required schema shape for REST responses 

Props to @jorbin for peer review and suggested improvements.

#abilities-api, #7-1, #dev-notes, #dev-notes-7-1, #rest-api

Abilities API improvements in WordPress 7.1

WordPress 7.1 expands the Abilities 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. with custom validation 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., a new invocation lifecycle action, richer user information, selective field responses, and more consistent schemas for the coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. abilities introduced in WordPress 6.9.

Custom input and output validation

WP_Ability validates input and output against each ability’s JSONJSON JSON, or JavaScript Object Notation, is a minimal, readable format for structuring data. It is used primarily to transmit data between a server and web application, as an alternative to XML. Schema. WordPress 7.1 adds two filters that let extenders supplement that validation:

  • wp_ability_validate_input
  • wp_ability_validate_output

Each 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. receives the existing validation result, the value being validated, and the ability name:

/**
 * @param true|WP_Error $is_valid Validation result.
 * @param mixed         $value    Input or output value.
 * @param string        $name     Ability name.
 */

For example, 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. can enforce a rule that cannot be expressed by the JSON Schema implementation used by WordPress:

add_filter(
	'wp_ability_validate_input',
	function ( $is_valid, $input, $ability_name ) {
		if ( 'my-plugin/send-message' !== $ability_name ) {
			return $is_valid;
		}

		// Preserve errors produced by the default schema validation.
		if ( is_wp_error( $is_valid ) ) {
			return $is_valid;
		}

		if (
			! is_array( $input )
			|| empty( $input['recipient'] )
			|| ! str_ends_with( $input['recipient'], '@example.com' )
		) {
			return new WP_Error(
				'invalid_recipient',
				__( 'The recipient must use the example.com domain.', 'my-plugin' )
			);
		}

		return true;
	},
	10,
	3
);

Output can be validated in the same way:

add_filter(
	'wp_ability_validate_output',
	function ( $is_valid, $output, $ability_name ) {
		if ( 'my-plugin/send-message' !== $ability_name ) {
			return $is_valid;
		}

		if ( is_wp_error( $is_valid ) ) {
			return $is_valid;
		}

		if ( ! is_array( $output ) || empty( $output['message_id'] ) ) {
			return new WP_Error(
				'invalid_message_output',
				__( 'The ability did not return a message ID.', 'my-plugin' )
			);
		}

		return true;
	},
	10,
	3
);

Callbacks should return true when the value is valid or a WP_Error describing why it is invalidinvalid A resolution on the bug tracker (and generally common in software development, sometimes also notabug) that indicates the ticket is not a bug, is a support request, or is generally invalid.. Returning false also fails validation, but WordPress converts it to a generic WP_Error. The filters operate on the validation result rather than the schema, allowing plugins to augment an existing error or reject data that otherwise passed JSON Schema validation.

REST-style validate_callback and sanitize_callback schema keywords are not executed by the Abilities API. Custom runtime validation should use these new filters. See ticketticket Created for both bug reports and feature development on the bug tracker. #64311.

Observing every ability invocation

WordPress 7.1 adds the wp_ability_invoked action at the beginning of WP_Ability::execute():

do_action( 'wp_ability_invoked', $this->name, $input, $this );

The action fires before input normalization, validation, permission checks, and the wp_pre_execute_ability short-circuit filter. Consequently, it runs for every invocation, including calls that:

  • contain invalid input;
  • fail their permission check;
  • are short-circuited;
  • return a cached result;
  • require approval; or
  • proceed to the execution callback.

This makes the action suitable for auditing, telemetry, tracing, and invocation accounting:

add_action(
	'wp_ability_invoked',
	function ( $ability_name, $input, $ability ) {
		// Avoid storing sensitive input without appropriate filtering.
		do_action(
			'my_plugin_record_ability_invocation',
			array(
				'ability'  => $ability_name,
				'timestamp' => time(),
			)
		);
	},
	10,
	3
);

The action receives raw, unnormalized input. Plugins should therefore avoid logging input indiscriminately, because it may contain credentials, personal information, or other sensitive data.

The existing wp_before_execute_ability and wp_after_execute_ability actions also receive the corresponding WP_Ability instance as an additional final argument. Existing callbacks continue to work unchanged. To receive the new argument, update the callback signature and $accepted_args. See ticket #65248.

Expanded core/get-user-info responses

The core/get-user-info ability now returns five additional profile fields for the current authenticated user:

  • first_name
  • last_name
  • nickname
  • description
  • user_url

When no input is supplied, the response contains all supported properties:

{
	"id": 1,
	"display_name": "Jane Doe",
	"user_nicename": "jane-doe",
	"user_login": "jane",
	"roles": [ "administrator" ],
	"locale": "en_US",
	"first_name": "Jane",
	"last_name": "Doe",
	"nickname": "Jane",
	"description": "Site administrator and contributor.",
	"user_url": "https://example.com"
}

The roles property is now normalized with array_values() so that it is consistently encoded as a JSON array, regardless of its PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher array keys.

Callers can also use the new optional fields input property to request a subset of the response:

$ability = wp_get_ability( 'core/get-user-info' );

$result = $ability->execute(
	array(
		'fields' => array(
			'display_name',
			'first_name',
			'last_name',
		),
	)
);

The resulting value contains only the requested properties:

{
	"display_name": "Jane Doe",
	"first_name": "Jane",
	"last_name": "Doe"
}

Supported field names are declared through an enum in the input schema. Passing an unknown name causes execution to return an ability_invalid_input error before the ability callback runs.

The permission requirement remains unchanged: the requester must be logged in. See ticket #65234 and changeset [62419].

Consistent schemas across core abilities

The following core abilities now follow the same schema conventions:

  • core/get-site-info
  • core/get-user-info
  • core/get-environment-info

Every output property declares a translatable, Title Case title and a description. This provides better metadata to REST, MCP, WebMCP, AI, and other programmatic clients that use the schema to present or select ability fields.

core/get-environment-info now supports the same optional fields input as the other two abilities:

$ability = wp_get_ability( 'core/get-environment-info' );

$result = $ability->execute(
	array(
		'fields' => array( 'php_version' ),
	)
);

Unknown field names are rejected through schema validation.

In addition, core/get-user-info is now exposed through 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/. Like the other core abilities, it declares the new public 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. flag introduced in WordPress 7.1. Authenticated clients can discover it through:

/wp-json/wp-abilities/v1/abilities

The exact ordered set of input field names and output properties is covered by registration tests. Developers extending or consuming these core abilities should use the schemas for discovery rather than assuming a fixed response shape. See ticket #65355.

Typed input for REST ability runs

REST requests that run an ability over GET or DELETE deliver every query string value as a string, and a comma-separated list as a single string. In previous releases, the ability received this input as-is: an integer arrived as "10", a boolean as "true", and strict comparisons inside the callback silently failed unless the ability hand-rolled its own casting.

WordPress 7.1 coerces the input of a run request to the types declared in the ability’s input_schema before the ability runs. The coercion is registered as the input argument’s sanitize_callback, so the permission callback and the execute callback both receive natively typed input from the same request object:

GET /wp-json/wp-abilities/v1/abilities/my-plugin/list-items/run
    ?input[limit]=10&input[featured]=true&input[ids]=1,2,3

With an input schema declaring limit as an integer, featured as a boolean, and ids as an array of integers, the callbacks now receive:

array(
	'limit'    => 10,
	'featured' => true,
	'ids'      => array( 1, 2, 3 ),
)

Coercion never changes what validation accepts. Input is coerced only when validate_input() already accepts it, so invalid input reaches validation untouched and returns the same ability_invalid_input error as before.

Props to @benjamin_zekavica and @annezazu for peer review, and to @jorgefilipecosta for review and collaboration.

#abilities-api, #7-1, #dev-notes, #dev-notes-7-1, #rest-api

Design System Theming in WordPress 7.1

WordPress 7.1 will include foundational support for theming design aspects of adminadmin (and super admin) interface components, including color, roundness, and cursor pointers for interactive controls. The background and purpose behind these new features were shared previously in Merge Proposal: Design System Theming. This post aims to provide information about what is included with WordPress 7.1.

Design Tokens in the wp-theme stylesheet

A new wp-theme stylesheet will be registered by default and is available as a dependency for plugins. When enqueued, the stylesheet includes a full set of semantic design tokens formatted as CSS custom properties (variables) that can be referenced in WordPress or 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. styles that are building user interfaces for the wp-admin dashboard. Design tokens can be used in place of hard-coded values to keep styles in sync across related UIUI User interface components, for aspects such as color, typography, spacing, and more. When paired with the ThemeProvider, design tokens automatically inherit aspects of color and roundness for that area of a page.

Typically, design tokens would be used in the implementation of common WordPress UI components, and a plugin would use those components instead of interfacing with the design tokens directly in their own styles. But the design tokens are generally available for both WordPress-internal styling as well as a plugin’s unique use-cases so that styles can be applied consistently.

The following example demonstrates a hypothetical component for a simple card layout. In the blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. editor or site editor, you should use the Card React component instead.

.card {
	background-color: var(--wpds-color-background-surface-neutral-strong);
	color: var(--wpds-color-foreground-content-neutral);
	border: var(--wpds-border-width-xs) solid var(--wpds-color-stroke-surface-neutral-weak);
	border-radius: var(--wpds-border-radius-lg);
	padding: var(--wpds-dimension-padding-2xl);
}
Screenshot of the card component described in the caption
An example card component inheriting ThemeProvider settings with a light gray background
Screenshot of the card component described in the caption
An example card component inheriting ThemeProvider settings with a dark blue background and a more pronounced border radius

To learn more about design tokens, refer to the “Design Tokens” section of the @wordpress/theme package documentation.

For more information about the available token options, a design system token reference describes the structure, purpose, and name of each token.

Design tokens are used as the basis for all components in the @wordpress/ui React component library, and is used in applying the user’s preferred color scheme to the Site Editor in WordPress 7.1. As a foundational piece, this will be expanded in subsequent WordPress releases to apply to more of the admin interface, which is detailed in the merge proposal.

ThemeProvider ReactReact React is a JavaScript library that makes it easy to reason about, construct, and maintain stateless and stateful user interfaces. https://reactjs.org component in the wp-theme script handle

A new wp-theme 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 script handle will be registered by default and is available as a dependency for plugins. The wp-theme script provides a single ThemeProvider React component that can be used to wrap content in an area of the page to override the default design token values that are provide from the wp-theme stylesheet.

import { ThemeProvider } from '@wordpress/theme';
import { Card } from '@wordpress/ui';

function Application() {
	return (
		<ThemeProvider
			color={ { primary: '#3858e9', background: '#11004d' } }
			cornerRadius="pronounced"
		>
			<Card.Root>
				<Card.Content>
					WordPress is designed for everyone. We believe great
					software should work with minimum set up,
					emphasizing accessibility, performance, security,
					and ease of use.
				</Card.Content>
			</Card.Root>
		</ThemeProvider>
	);
}

Given a pair of primary and background seed color values, the component internally generates a color ramp which is visually harmonious and provides contrast between elements, such as between foreground and background, or between background and border. While the color algorithm aims for accessible contrast targets, it is not possible to guarantee for every color combination, so due diligence is still required to verify its outputs for a given set of colors.

ThemeProvider currently includes five options for customizing the behavior of a themed section of a page:

PropDescription
color.primaryThe primary seed color to use for the theme. Accepts a fully opaque sRGB-parseable string: a hex value (e.g. #3858e9), an rgb()/rgba() string, or a CSSCSS Cascading Style Sheets. named color (e.g. 'blue').
color.backgroundThe background seed color to use for the theme. Accepts a fully opaque sRGB-parseable string: a hex value (e.g. #f8f8f8), an rgb()/rgba() string, or a CSS named color (e.g. 'blue').
cursor.controlThe cursor style for interactive controls that are not links (e.g. buttons, checkboxes, and toggles). By default, it inherits from the parent ThemeProvider, and falls back to the prebuilt default (pointer).
cornerRadiusOverall roundness preset for the theme subtree: none (square corners), subtlemoderate, or pronounced (most rounded). By default, it inherits from the parent ThemeProvider, and falls back to the prebuilt default (subtle).
isRootWhen a ThemeProvider is the root provider, it will apply its theming settings also to the root document element (e.g. the html element). Render at most one root provider per document.

ThemeProvider can be used by plugin authors to express their unique brand identity while appearing consistent within the broader WordPress admin interface.

To learn more about using the ThemeProvider component, refer to the “Theme Provider” section of the @wordpress/theme package documentation.

Further Reading

  • Merge Proposal: Design System Theming (Make/CoreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. post)
  • Design System: Support for admin redesign (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 by the repository owner. https://github.com/ issue)
  • A themeable and scalable primitive system (GitHub issue)

Props to @wildworks and @tyxla for reviewing this post’s content.

#dev-notes, #dev-notes-7-1

Filtering Site Editor Screens in WordPress 7.1

This work is part of the iteration issue DataViews, DataForm, et. al. in WordPress 7.1 and it has been tracked at #76544. Check the documentation: filters, properties.

WordPress 7.1 introduces four new filters to configure Site Editor screens:

PageFilterFilter 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. name
Pagesget_entity_view_config_posttype_page
Templatesget_entity_view_config_posttype_wp_template
Partsget_entity_view_config_posttype_wp_template_part
Patternsget_entity_view_config_posttype_wp_block

Each filter can configure the DataViews and DataForm components that power those screens:

  • default_view: the default DataViews configuration (e.g., what fields are visible, what is the default sort order, etc.)
  • default_layouts: the default DataViews layouts (e.g., what layouts are available for the user to choose from)
  • view_list: the preconfigured views displayed in the sidebarSidebar A sidebar in WordPress is referred to a widget-ready area used by WordPress themes to display information that is not a part of the main content. It is not always a vertical column on the side. It can be a horizontal rectangle below or above the content area, footer, header, or any where in the theme. (e.g. “All”, “Published”, “Drafts”, etc.)
  • form: the DataForm configuration used for the Quick Edit form (e.g. which fields are displayed in the form and their order)

Filter callbacks receive an object with the config for the given entity with a set of methods to work with data (API). For example, this code will update the Pages screen by setting grid as the default layout, sort it by ascending title, and add a new visible field date:

function example_filter_page_view_config( $data ) {
	$patch = array(
        'default_view' => array(
            'type'   => 'grid',
            'sort'   => array(
                'field'     => 'title',
                'direction' => 'asc',
            ),
            'fields' => array( 'date' ),
        ),
    );
    $data->merge( $patch, 1 );

    return $data;
}
add_filter( 'get_entity_view_config_posttype_page', 'example_filter_page_view_config' );

Next steps

This mechanism is designed to grow in the future by creating filters for other entities and updating screens to use them. The goal is to have a single place of configuration for an entity that works across screens and components. An example of this work is the experiment to power the editor inspector (built with DataForm) with the data coming from these filters, consolidating Site Editor’s QuickEdit and the editor inspectors.

Future work also includes registration of fields that work across DataViews and DataForm.

Props to @ntsekouras and @priethor for review.

#dev-notes #dev-notes-7-1 #7-1

Leaner, steadier PHPUnit runs for upcoming releases

In recent years, the CI load across coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. WordPress 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 by the repository owner. https://github.com/ repos increased as the project evolved. This is normal for an active project.

But, the load can be especially high when running the full PHPUnit matrix at once on many PRs, such as during a release with many backportbackport A port is when code from one branch (or trunk) is merged into another branch or trunk. Some changes in WordPress point releases are the result of backporting code from trunk to the release branch. branches. Cases like this mean the number of jobs can strain the capacity of the system.

Ahead of 7.1, we’ve landed a round of trims and reliability fixes aimed at increasing capacity and running leaner while ensuring quality checks still run.

This CI work builds on the test-suite optimization work from many contributors in the past.

Two key changes

  1. Trimmed the PHPUnit matrix to boundary PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher versions (#12719 on 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., #12720 for 7.0). Full PHP coverage kept, redundant database combinations dropped. Per run: ~52% fewer jobs and ~54% fewer job-minutes.
  2. Fetch the GutenbergGutenberg The Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. https://wordpress.org/gutenberg/ build once per run (#12701) instead of once per job, plus bounded retries on Docker image pulls (#12703). Runs needing a rerun to go green roughly halved, from ~68% to ~36%.

Source: measured per run from the GitHub Actions 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. (job counts and durations, and rerun counts) across comparable runs before / after the changes.

Next up

  • Trim the 6.8 matrix (#12726).
  • Pilot a dedicated larger-runner pool for use during release windows to help with concurrency issues (more on that in a future update).

Note: The PHPUnit tests themselves could be further tuned. The trims above cut job count, not job duration—so we could give more attention to the tests themselves to find more efficiency.

Thanks to contributions from adrianmoldovanwp, barry, garyj, @johnbillion, @jonsurrell, @jorbin, lucasbustamante, and @mukesh27.

jQuery UI updated to 1.14.2 in WordPress 7.1

WordPress 7.1 includes an update to the latest stable version of jQuery UI, 1.14.2 from version 1.13.3. This update drops support for all versions of Internet Explorer & Edge Legacy which is inline with the WordPress Browser Support Policy.

This update sets jQuery.uiBackCompat to be equal to true to ensure that code written for the jQuery 1.11 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. continues to function as expected. Additionally, $.fn._form, $.ui.ie, $.ui.safeActiveElement, and $.ui.safeBlur have been removed. WordPress coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. does not use any of these functions, but you should check your code and update it accordingly if it does.

See [62747] for the specific changes and #62757 for more background information.

props @masteradhoc and @joedolson for review

#7-1, #dev-notes, #dev-notes-7-1, #external-libraries