Skip to content

Register entity actions in the server #83537

Description

@jorgefilipecosta

Part of #61084 and #81228.
Similar to #76544, but for actions.

Right now, actions can only be registered and removed in JavaScript, using registerEntityAction and unregisterEntityAction, and only on the Gutenberg plugin. The actions each post type has are computed on the client by registerPostTypeSchema, which uses three resolvers (post type, canUser, and current theme) for information the server already has.

This issue proposes a plan so plugins can add and remove the actions of an entity from PHP. The actions show on the DataViews lists of both site editors and on the editor inspector.

Actions are defined in a registry and placed with the view config.

As #83368 does for fields, PHP has the plain data of an action (id, label, isPrimary, supportsBulk, context, modal options), and a script module has the parts PHP cannot serialize (callback, RenderModal, isEligible, function labels).

Which actions an entity has, and their order, comes from a new actions key in the view config, as #77343 prototyped. So plugins use the same merge(), replace(), set() and remove() calls they use for fields:

add_action( 'actions_api_init', function ( $registry ) {
	$registry->register(
		array(
			array(
				'id'           => 'acme/feature',
				'label'        => __( 'Feature', 'acme' ),
				'supportsBulk' => true,
			),
		),
		'acme/product-actions'
	);
} );

add_filter( 'get_entity_view_config_posttype_product', function ( $data ) {
	$data->merge(
		array(
			'actions' => array(
				'acme/feature',
				array( 'id' => 'move-to-trash', 'isPrimary' => true ),
			),
		),
		1
	);
	$data->remove( array( 'actions' => array( 'duplicate-post' ) ), 1 );
	return $data;
} );

The script module exports the JavaScript parts keyed by action id:

export default {
	'acme/feature': {
		isEligible: ( item ) => item.status === 'publish',
		async callback( items, { registry, onActionPerformed } ) {
			// Feature the items using registry.dispatch( 'core' ).
			onActionPerformed?.( items );
		},
	},
};

GET /wp/v2/actions?kind=postType&name=product returns the actions placed on that entity in order, each definition merged with its view config entry, plus the script modules to import. It has the same shape as /wp/v2/fields, so both can share the client loader.

I considered using only the registry to decide which actions an entity has, as, unlike fields, users can't show or hide actions. But that would give plugins different calls for actions and fields, so I opted for placing actions with the view config.

The work is divided into four PRs/stages.

PR 1: Move the core actions into a script module.

  • Moves the 13 actions registerPostTypeSchema registers (view-post, view-post-revisions, duplicate-post, duplicate-template-part, duplicate-pattern, rename-post, order-pages, export-pattern, restore, reset-post, delete-post, move-to-trash, permanently-delete) into a @wordpress/fields/server-actions script module keyed by action id, the equivalent of server-fields on Fields API #83368.
  • Adds the module as a dynamic module_dependencies entry of wp-editor, and makes sure the extensible site editor routes have it on their import map.
  • Makes registerPostTypeSchema import the module. No PHP and no behavior change.

PR 2: Add the actions key to the view config and move the core action list to the server.

  • Adds actions to the view config keys and REST schema.
  • Builds the core default list in the base config next to form, porting get_actions() from View Config: Serve supported action IDs from the REST controller #77343 and updating it to trunk. Only actions that can apply are placed, e.g.: reset-post is not placed on pages.
  • Places set-as-homepage and set-as-posts-page on pages for users with manage_options. In paralell to this effort we will work on Register set-as-homepage and set-as-posts-page as entity actions #76668 using option C isEligible needs access to the registry.
  • Makes registerPostTypeSchema read the list from the view config and removes its gating.
  • Makes the inspector request context: 'single', so an entry can target only the lists or only the inspector.
  • Stops placing move-to-trash on attachments and makes permanently-delete accept them, so the v2 post list no longer needs its attachment tweaks.

After this PR, plugins can remove, reorder, and change core actions from PHP.

PR 3: Add the actions registry and the /wp/v2/actions endpoint.

  • Adds the actions_api_init action, passing a registry with register( $actions, $script_module ) and unregister( $ids ).
  • Registers the core actions at priority 0, moving their plain data from the module to PHP.
  • Adds GET /wp/v2/actions?kind&name and makes the client use it with the loader from Fields API #83368. The loader should wait for all the imports and merge in the order the server lists the modules, so replacing the behavior of an action is deterministic.
  • Adds an e2e test plugin that adds, changes, and removes actions, and adds a core action to a custom post type.

After this PR, plugins can add new actions from PHP.

PR/Stage 4: Core readiness / tweaks.

  • Preloads the endpoint on both editors and the extensible site editor routes.
  • Documents the actions key and the registry next to the view config reference, including that the filter only exists per entity and that merging a bare id over an entry with keys replaces the entry.
  • Fixes the stale registerPostTypeActions entry on docs/private-apis.md.
  • Decides if registerEntityAction stays plugin-only.
  • Backports to wordpress-develop with a dev note.

Some notes:

  • Actions that navigate inside the app keep using onActionPerformed on each host.
  • Only the postType kind is covered. The view config also supports taxonomies and root entities, but nothing shows entity actions for them yet.
  • WordPress 7.1 ignores unknown view config keys with a notice, so plugins supporting 7.1 should check in_array( 'actions', $data::CONFIG_KEYS, true ) first.

cc: @oandregal, @ntsekouras as people inside the fields API, view config, and actions work.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

[Feature] DataViewsWork surrounding upgrading and evolving views in the site editor and beyond[Feature] ExtensibilityThe ability to extend blocks or the editing experience[Type] Tracking IssueTactical breakdown of efforts across the codebase and/or tied to Overview issues.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions