Commit 38f2c297cf for wordpress.org
commit 38f2c297cf6c2895cdc931b78afcd4cd4b94a6dc
Author: oandregal <oandregal@git.wordpress.org>
Date: Fri Oct 23 07:32:55 2026 +0000
Editor: Fix edge cases in the entity actions and fields APIs.
`_wp_preload_entity_actions()` and `_wp_preload_entity_fields()` assumed
`$paths` was an array, so the editor crashed when an earlier
`block_editor_rest_api_preload_paths` callback returned `null`, `false`,
a string or an object. Empty values are now turned into an array so the
entity paths are still added. Other non-array values, and contexts that
are not a `WP_Block_Editor_Context`, are returned unchanged. `$context`
now defaults to `null`, so calling either function with one argument no
longer throws an `ArgumentCountError`.
Numeric script module ids are now handled as strings. PHP turned them into
integer array keys, so `wp_get_registered_entity_field_modules()` and
`wp_get_registered_entity_action_modules()` could miss them in a strict
comparison and `array_merge()` could renumber them. Both functions now
return a list of entries holding the module id and the ids of the fields
or actions it applies to. This is the shape the REST controllers already
returned, and they now use these results directly. The entity fields
endpoint now returns module ids as strings, matching its schema.
A field registered with the id `'0'` is no longer rejected as empty.
The code duplicated between the two APIs now lives in a new
`entity-utils.php` file:
- `_wp_add_entity_preload_paths()` builds the preload paths for both
preload callbacks.
- `_wp_add_entity_modules_to_editor_script()` adds the script modules to
the editor script for both `admin_footer` callbacks. It takes a callable
rather than the modules, so the registries are still only read on pages
that load the editor script.
Developed in https://github.com/WordPress/wordpress-develop/pull/14716.
Props oandregal, jorgefilipecosta.
See #66229, #66343.
Built from https://develop.svn.wordpress.org/trunk@64686
git-svn-id: http://core.svn.wordpress.org/trunk@63836 1a063a9b-81f0-0310-95a4-ce76da25c4cd
diff --git a/wp-includes/class-wp-entity-actions-registry.php b/wp-includes/class-wp-entity-actions-registry.php
index 1ee5e722cf..664819e078 100644
--- a/wp-includes/class-wp-entity-actions-registry.php
+++ b/wp-includes/class-wp-entity-actions-registry.php
@@ -404,17 +404,31 @@ class WP_Entity_Actions_Registry {
*
* @param string $kind The entity kind (e.g. `postType`).
* @param string $name The entity name (e.g. `page`).
- * @return array<string, string[]> The ids of the actions each module
- * applies to, keyed by module id, in
- * registration order. Empty when the
- * entity is invalid.
+ * @return array[] {
+ * The script modules, in registration order. Empty when the entity
+ * is invalid.
+ *
+ * @type array ...$0 {
+ * @type string $id The id of the script module.
+ * @type string[] $actions The ids of the actions the module
+ * applies to, in registration order.
+ * }
+ * }
*/
public function get_registered_action_modules( $kind, $name ) {
if ( ! $this->validate_entity( __METHOD__, $kind, $name ) ) {
return array();
}
$this->initialize();
- return $this->action_modules[ $kind ][ $name ] ?? array();
+ $modules = array();
+ foreach ( $this->action_modules[ $kind ][ $name ] ?? array() as $module => $action_ids ) {
+ $modules[] = array(
+ // A numeric module id is an integer as an array key.
+ 'id' => (string) $module,
+ 'actions' => $action_ids,
+ );
+ }
+ return $modules;
}
/**
diff --git a/wp-includes/class-wp-entity-fields-registry.php b/wp-includes/class-wp-entity-fields-registry.php
index e25f2e026a..864828ca1c 100644
--- a/wp-includes/class-wp-entity-fields-registry.php
+++ b/wp-includes/class-wp-entity-fields-registry.php
@@ -477,13 +477,27 @@ class WP_Entity_Fields_Registry {
*
* @param string $kind The entity kind (e.g. `postType`).
* @param string $name The entity name (e.g. `page`).
- * @return array<string, string[]> The ids of the fields each module
- * applies to, keyed by module id, in
- * registration order.
+ * @return array[] {
+ * The script modules, in registration order.
+ *
+ * @type array ...$0 {
+ * @type string $id The id of the script module.
+ * @type string[] $fields The ids of the fields the module applies
+ * to, in registration order.
+ * }
+ * }
*/
public function get_registered_field_modules( $kind, $name ) {
$this->initialize();
- return $this->field_modules[ $kind ][ $name ] ?? array();
+ $modules = array();
+ foreach ( $this->field_modules[ $kind ][ $name ] ?? array() as $module => $field_ids ) {
+ $modules[] = array(
+ // A numeric module id is an integer as an array key.
+ 'id' => (string) $module,
+ 'fields' => $field_ids,
+ );
+ }
+ return $modules;
}
/**
@@ -497,9 +511,25 @@ class WP_Entity_Fields_Registry {
*/
public function get_all_registered_field_modules() {
$this->initialize();
+ /*
+ * Keeps the module ids and drops the field ids they apply to, e.g.:
+ *
+ * array(
+ * 'postType' => array(
+ * 'page' => array( 'my-plugin/fields', '123' ),
+ * 'post' => array( 'my-plugin/fields' ),
+ * ),
+ * )
+ */
return array_map(
static function ( $entities ) {
- return array_map( 'array_keys', $entities );
+ return array_map(
+ static function ( $modules ) {
+ // A numeric module id is an integer as an array key.
+ return array_map( 'strval', array_keys( $modules ) );
+ },
+ $entities
+ );
},
$this->field_modules
);
@@ -731,7 +761,7 @@ class WP_Entity_Fields_Registry {
$valid = array();
$invalid = array();
foreach ( $fields as $position => $field ) {
- if ( is_array( $field ) && ! empty( $field['id'] ) && is_string( $field['id'] ) ) {
+ if ( is_array( $field ) && isset( $field['id'] ) && is_string( $field['id'] ) && '' !== $field['id'] ) {
$valid[] = $field;
} else {
$invalid[] = '#' . ( $position + 1 );
diff --git a/wp-includes/entity-actions.php b/wp-includes/entity-actions.php
index 426fd1d309..80aced696b 100644
--- a/wp-includes/entity-actions.php
+++ b/wp-includes/entity-actions.php
@@ -80,17 +80,22 @@ function wp_get_registered_entity_actions( $kind, $name ) {
}
/**
- * Returns the ids of the script modules registered for the given entity.
+ * Returns the script modules registered for the given entity.
*
* @since 7.2.0
*
* @param string $kind The entity kind (e.g. `postType`).
* @param string $name The entity name (e.g. `page`).
- * @return array<string, string[]> The ids of the actions each module
- * applies to, keyed by module id, in
- * registration order. Empty until the
- * `init` action has completed: a read
- * before then is refused.
+ * @return array[] {
+ * The script modules, in registration order. Empty until the `init`
+ * action has completed: a read before then is refused.
+ *
+ * @type array ...$0 {
+ * @type string $id The id of the script module.
+ * @type string[] $actions The ids of the actions the module applies
+ * to, in registration order.
+ * }
+ * }
*/
function wp_get_registered_entity_action_modules( $kind, $name ) {
return WP_Entity_Actions_Registry::get_instance()->get_registered_action_modules( $kind, $name );
@@ -114,78 +119,21 @@ function wp_get_all_registered_entity_action_modules() {
/**
* Declares the script modules of the entity actions as dependencies of the
- * editor script.
- *
- * The editor imports the script modules of an entity on demand, with a dynamic
- * `import()` the browser resolves through the import map of the page. Listing
- * the modules as dynamic `module_dependencies` of the `wp-editor` script adds
- * them to the import map of every page that loads the editor script: the post
- * editor, the site editor, and the pages booted by `@wordpress/boot`, whose
- * prerequisites depend on it. A dynamic dependency is only listed in the
- * import map; the module is fetched when it is imported.
- *
- * Reading the registry fires `wp_entity_actions_api_init`, so the actions and their
- * script modules are registered on demand. It only reads the registry on the
- * pages that load the editor script, not on every admin request (heartbeat,
- * Ajax, other screens). It runs on `admin_footer`, the last action before the
- * import map is printed that fires both in the admin template and in the pages
- * rendered outside it, which print the import map themselves. By then `init`
- * has completed, so the post types and supports the actions derive from are
- * final.
- * The registry only accepts registrations while the action fires, so the
- * modules declared here are the modules of every action the page can show.
+ * editor script, see _wp_add_entity_modules_to_editor_script().
+ *
+ * The actions are loaded by `usePostActions()` of the editor script, so every
+ * screen showing them loads it.
*
* The `admin_footer` action passes it no argument, so the global registry is
* used. In other scenarios (tests), the scripts are given directly.
*
* @since 7.2.0
+ * @access private
*
* @param WP_Scripts|null $scripts The scripts registry. Defaults to the global one.
*/
function _wp_add_entity_action_modules_to_editor_script( $scripts = null ) {
- if ( ! $scripts instanceof WP_Scripts ) {
- $scripts = wp_scripts();
- }
-
- // The actions are loaded by `usePostActions()` of the editor script, so
- // every screen showing them loads it.
- if ( ! $scripts->query( 'wp-editor', 'enqueued' ) && ! $scripts->query( 'wp-editor', 'done' ) ) {
- return;
- }
-
- $kinds = wp_get_all_registered_entity_action_modules();
- if ( empty( $kinds ) ) {
- return;
- }
-
- $dependencies = $scripts->get_data( 'wp-editor', 'module_dependencies' );
- $dependencies = is_array( $dependencies ) ? $dependencies : array();
-
- $declared = array();
- foreach ( $dependencies as $dependency ) {
- $declared[] = is_array( $dependency ) ? $dependency['id'] : $dependency;
- }
-
- $added = false;
- foreach ( $kinds as $entities ) {
- foreach ( $entities as $modules ) {
- foreach ( $modules as $module ) {
- if ( ! in_array( $module, $declared, true ) ) {
- $dependencies[] = array(
- 'id' => $module,
- 'import' => 'dynamic',
- );
- $declared[] = $module;
- $added = true;
- }
- }
- }
- }
-
- if ( $added ) {
- // `add_data()` replaces the value, hence the merge above.
- $scripts->add_data( 'wp-editor', 'module_dependencies', $dependencies );
- }
+ _wp_add_entity_modules_to_editor_script( $scripts, 'wp_get_all_registered_entity_action_modules' );
}
/**
@@ -201,34 +149,12 @@ function _wp_add_entity_action_modules_to_editor_script( $scripts = null ) {
* packages/core-data/src/resolvers.js in Gutenberg.
*
* @since 7.2.0
+ * @access private
*
- * @param array $paths REST API paths to preload.
- * @param WP_Block_Editor_Context $context Block editor context.
- * @return array Filtered preload paths.
+ * @param mixed $paths REST API paths to preload.
+ * @param mixed $context Optional. Block editor context. Default null.
+ * @return mixed Filtered preload paths. See _wp_add_entity_preload_paths().
*/
-function _wp_preload_entity_actions( $paths, $context ) {
- $post_types = array();
- if ( 'core/edit-post' === $context->name && isset( $context->post ) ) {
- $post_types[] = $context->post->post_type;
- } elseif ( 'core/edit-site' === $context->name ) {
- if ( isset( $context->post ) ) {
- $post_types[] = $context->post->post_type;
- }
- $post_types = array_merge( $post_types, _wp_get_site_editor_screen_post_types() );
- }
-
- foreach ( array_unique( $post_types ) as $post_type ) {
- if ( ! post_type_exists( $post_type ) ) {
- continue;
- }
- $paths[] = add_query_arg(
- array(
- 'kind' => 'postType',
- 'name' => $post_type,
- ),
- '/wp/v2/entity-actions'
- );
- }
-
- return $paths;
+function _wp_preload_entity_actions( $paths, $context = null ) {
+ return _wp_add_entity_preload_paths( $paths, $context, '/wp/v2/entity-actions' );
}
diff --git a/wp-includes/entity-fields.php b/wp-includes/entity-fields.php
index 779a32f7fe..c08bf519fc 100644
--- a/wp-includes/entity-fields.php
+++ b/wp-includes/entity-fields.php
@@ -111,16 +111,22 @@ function wp_get_registered_entity_fields( $kind, $name ) {
}
/**
- * Returns the ids of the script modules registered for the given entity.
+ * Returns the script modules registered for the given entity.
*
* @since 7.2.0
*
* @param string $kind The entity kind (e.g. `postType`).
* @param string $name The entity name (e.g. `page`).
- * @return array<string, string[]> The ids of the fields each module applies
- * to, keyed by module id, in registration
- * order. Empty until the `init` action has
- * completed: a read before then is refused.
+ * @return array[] {
+ * The script modules, in registration order. Empty until the `init`
+ * action has completed: a read before then is refused.
+ *
+ * @type array ...$0 {
+ * @type string $id The id of the script module.
+ * @type string[] $fields The ids of the fields the module applies to,
+ * in registration order.
+ * }
+ * }
*/
function wp_get_registered_entity_field_modules( $kind, $name ) {
return WP_Entity_Fields_Registry::get_instance()->get_registered_field_modules( $kind, $name );
@@ -144,25 +150,12 @@ function wp_get_all_registered_entity_field_modules() {
/**
* Declares the script modules of the entity fields as dependencies of the
- * editor script.
- *
- * The editor imports the script modules of an entity on demand, with a dynamic
- * `import()` the browser resolves through the import map of the page. Listing
- * the modules as dynamic `module_dependencies` of the `wp-editor` script adds
- * them to the import map of every page that loads the editor script: the post
- * editor, the site editor, and the pages booted by `@wordpress/boot`, whose
- * prerequisites depend on it. A dynamic dependency is only listed in the
- * import map; the module is fetched when it is imported.
- *
- * Reading the registry fires `wp_entity_fields_api_init`, so the fields and their
- * script modules are registered on demand. It only reads the registry on the
- * pages that load the editor script, not on every admin request (heartbeat,
- * Ajax, other screens). It runs on `admin_footer`, the last action before the
- * import map is printed that fires both in the admin template and in the pages
- * rendered outside it, which print the import map themselves. By then `init`
- * has completed, so the supports the default fields derive from are final.
- * The registry only accepts registrations while the action fires, so the
- * modules declared here are the modules of every field the page can show.
+ * editor script, see _wp_add_entity_modules_to_editor_script().
+ *
+ * The fields are loaded by the editor package, see
+ * packages/editor/src/dataviews/store/private-resolvers.ts in Gutenberg, which
+ * means any screen that wants to use this mechanism needs to load the editor
+ * script.
*
* It is hooked to `admin_footer` without arguments, so the global scripts
* registry is used. In other scenarios (tests), the scripts are given directly.
@@ -174,53 +167,7 @@ function wp_get_all_registered_entity_field_modules() {
* the global one.
*/
function _wp_add_entity_field_modules_to_editor_script( $scripts = null ) {
- if ( ! $scripts instanceof WP_Scripts ) {
- $scripts = wp_scripts();
- }
-
- /*
- * The fields are loaded by the editor package, see
- * packages/editor/src/dataviews/store/private-resolvers.ts in Gutenberg,
- * which means any screen that wants to use this mechanism needs to load
- * the editor script.
- */
- if ( ! $scripts->query( 'wp-editor', 'enqueued' ) && ! $scripts->query( 'wp-editor', 'done' ) ) {
- return;
- }
-
- $kinds = wp_get_all_registered_entity_field_modules();
- if ( empty( $kinds ) ) {
- return;
- }
-
- $dependencies = $scripts->get_data( 'wp-editor', 'module_dependencies' );
- $dependencies = is_array( $dependencies ) ? $dependencies : array();
-
- $declared = array();
- foreach ( $dependencies as $dependency ) {
- $declared[] = is_array( $dependency ) ? $dependency['id'] : $dependency;
- }
-
- $added = false;
- foreach ( $kinds as $entities ) {
- foreach ( $entities as $modules ) {
- foreach ( $modules as $module ) {
- if ( ! in_array( $module, $declared, true ) ) {
- $dependencies[] = array(
- 'id' => $module,
- 'import' => 'dynamic',
- );
- $declared[] = $module;
- $added = true;
- }
- }
- }
- }
-
- if ( $added ) {
- // `add_data()` replaces the value, hence the merge above.
- $scripts->add_data( 'wp-editor', 'module_dependencies', $dependencies );
- }
+ _wp_add_entity_modules_to_editor_script( $scripts, 'wp_get_all_registered_entity_field_modules' );
}
/**
@@ -255,73 +202,6 @@ function wp_register_entity_field_collection( $directory ) {
return WP_Entity_Fields_Registry::get_instance()->register_collection( $directory );
}
-/**
- * Returns the paths that serve the fields of the given post types, skipping
- * the post types that are not registered.
- *
- * A path must match the request of the `getFieldsConfig` core data resolver,
- * see packages/core-data/src/resolvers.js in Gutenberg.
- *
- * @since 7.2.0
- * @access private
- *
- * @param string[] $post_types The post types.
- * @return string[] One path of the `/wp/v2/entity-fields` route per post type,
- * in the order the post types are given, without duplicates.
- */
-function _wp_get_post_type_fields_preload_paths( $post_types ) {
- $paths = array();
- foreach ( array_unique( $post_types ) as $post_type ) {
- if ( ! post_type_exists( $post_type ) ) {
- continue;
- }
- $paths[] = add_query_arg(
- array(
- 'kind' => 'postType',
- 'name' => $post_type,
- ),
- '/wp/v2/entity-fields'
- );
- }
-
- return $paths;
-}
-
-/**
- * Returns the post types whose items the screen of the site editor being
- * loaded lists or edits, from its `p` query arg.
- *
- * The screens are the routes in
- * packages/edit-site/src/components/site-editor-routes in Gutenberg: the
- * lists and the editors of pages, templates, patterns, and template parts.
- * The patterns screen lists both patterns and template parts.
- *
- * @since 7.2.0
- * @access private
- *
- * @return string[] The post types, empty for any other screen.
- */
-function _wp_get_site_editor_screen_post_types() {
- // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Only picks what to preload.
- $path = isset( $_GET['p'] ) && is_string( $_GET['p'] ) ? sanitize_text_field( wp_unslash( $_GET['p'] ) ) : '';
- $segments = explode( '/', trim( $path, '/' ) );
-
- switch ( $segments[0] ) {
- case 'page':
- return array( 'page' );
- case 'template':
- case 'wp_template':
- return array( 'wp_template' );
- case 'pattern':
- return array( 'wp_block', 'wp_template_part' );
- case 'wp_block':
- return array( 'wp_block' );
- case 'wp_template_part':
- return array( 'wp_template_part' );
- }
- return array();
-}
-
/**
* Preloads the fields of the post types the editors list.
*
@@ -329,28 +209,17 @@ function _wp_get_site_editor_screen_post_types() {
* in packages/editor/src/dataviews/fields-loader/index.ts in Gutenberg. The
* post editor reads those of the edited post in the post summary of its
* inspector. The site editor reads those of the edited post, if any, and of
- * the post types of the screen it opens on.
+ * the post types of the screen it opens on. A path must match the request of
+ * the `getFieldsConfig` core data resolver, see
+ * packages/core-data/src/resolvers.js in Gutenberg.
*
* @since 7.2.0
* @access private
*
- * @param array $paths REST API paths to preload.
- * @param WP_Block_Editor_Context $context Block editor context.
- * @return array Filtered preload paths.
+ * @param mixed $paths REST API paths to preload.
+ * @param mixed $context Optional. Block editor context. Default null.
+ * @return mixed Filtered preload paths. See _wp_add_entity_preload_paths().
*/
-function _wp_preload_entity_fields( $paths, $context ) {
- $post_types = array();
- if ( 'core/edit-post' === $context->name && isset( $context->post ) ) {
- $post_types[] = $context->post->post_type;
- } elseif ( 'core/edit-site' === $context->name ) {
- if ( isset( $context->post ) ) {
- $post_types[] = $context->post->post_type;
- }
- $post_types = array_merge( $post_types, _wp_get_site_editor_screen_post_types() );
- }
-
- return array_merge(
- $paths,
- _wp_get_post_type_fields_preload_paths( $post_types )
- );
+function _wp_preload_entity_fields( $paths, $context = null ) {
+ return _wp_add_entity_preload_paths( $paths, $context, '/wp/v2/entity-fields' );
}
diff --git a/wp-includes/entity-utils.php b/wp-includes/entity-utils.php
new file mode 100644
index 0000000000..9f2489886b
--- /dev/null
+++ b/wp-includes/entity-utils.php
@@ -0,0 +1,156 @@
+<?php
+/**
+ * Utilities shared by the Entity Actions and Entity Fields APIs.
+ *
+ * @package WordPress
+ * @since 7.2.0
+ */
+
+/**
+ * Adds the paths of an entity route for the post types the editors list.
+ *
+ * The post editor lists the post type of the edited post. The site editor
+ * lists that of the edited post, if any, and those of the screen it opens on.
+ * Post types that are not registered are skipped, and each post type is added
+ * once.
+ *
+ * @since 7.2.0
+ * @access private
+ *
+ * @param mixed $paths REST API paths to preload.
+ * @param mixed $context Block editor context.
+ * @param string $route The entity route, such as `/wp/v2/entity-fields`.
+ * @return mixed The paths with one path per post type added. An empty value
+ * that is not an array becomes an array. Any other value that
+ * is not an array, or a context that is not a block editor
+ * context, returns `$paths` unchanged.
+ */
+function _wp_add_entity_preload_paths( $paths, $context, $route ) {
+ if ( empty( $paths ) && ! is_array( $paths ) ) {
+ $paths = array();
+ }
+
+ if ( ! is_array( $paths ) || ! $context instanceof WP_Block_Editor_Context ) {
+ return $paths;
+ }
+
+ $post_types = array();
+ if ( 'core/edit-post' === $context->name && isset( $context->post ) ) {
+ $post_types[] = $context->post->post_type;
+ } elseif ( 'core/edit-site' === $context->name ) {
+ if ( isset( $context->post ) ) {
+ $post_types[] = $context->post->post_type;
+ }
+
+ /*
+ * Adds the post types the screen lists or edits, from the first
+ * segment of its `p` query arg. The screens are the routes in
+ * packages/edit-site/src/components/site-editor-routes in Gutenberg.
+ * The patterns screen lists both patterns and template parts.
+ */
+ $screen_post_types = array(
+ 'page' => array( 'page' ),
+ 'template' => array( 'wp_template' ),
+ 'wp_template' => array( 'wp_template' ),
+ 'pattern' => array( 'wp_block', 'wp_template_part' ),
+ 'wp_block' => array( 'wp_block' ),
+ 'wp_template_part' => array( 'wp_template_part' ),
+ );
+
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Only picks what to preload.
+ $screen = isset( $_GET['p'] ) && is_string( $_GET['p'] ) ? sanitize_text_field( wp_unslash( $_GET['p'] ) ) : '';
+ $segments = explode( '/', trim( $screen, '/' ) );
+ if ( isset( $screen_post_types[ $segments[0] ] ) ) {
+ $post_types = array_merge( $post_types, $screen_post_types[ $segments[0] ] );
+ }
+ }
+
+ foreach ( array_unique( $post_types ) as $post_type ) {
+ if ( ! post_type_exists( $post_type ) ) {
+ continue;
+ }
+ $paths[] = add_query_arg(
+ array(
+ 'kind' => 'postType',
+ 'name' => $post_type,
+ ),
+ $route
+ );
+ }
+
+ return $paths;
+}
+
+/**
+ * Declares the script modules of an entity API as dependencies of the editor
+ * script.
+ *
+ * The editor imports the script modules of an entity on demand, with a dynamic
+ * `import()` the browser resolves through the import map of the page. Listing
+ * the modules as dynamic `module_dependencies` of the `wp-editor` script adds
+ * them to the import map of every page that loads the editor script: the post
+ * editor, the site editor, and the pages booted by `@wordpress/boot`, whose
+ * prerequisites depend on it. A dynamic dependency is only listed in the
+ * import map; the module is fetched when it is imported.
+ *
+ * The modules are read with `$get_modules`, which reads the registry of the
+ * API and so fires its init action, registering the entities and their script
+ * modules on demand. It is only called on the pages that load the editor
+ * script, not on every admin request (heartbeat, Ajax, other screens). The
+ * callers run on `admin_footer`, the last action before the import map is
+ * printed that fires both in the admin template and in the pages rendered
+ * outside it, which print the import map themselves. By then `init` has
+ * completed, so the post types and supports the registrations derive from are
+ * final. The registries only accept registrations while their action fires,
+ * so the modules declared here are those of everything the page can show.
+ *
+ * @since 7.2.0
+ * @access private
+ *
+ * @param WP_Scripts|null $scripts The scripts registry. Null for the global one.
+ * @param callable $get_modules Returns the module ids of the API, keyed
+ * by kind, then by name.
+ */
+function _wp_add_entity_modules_to_editor_script( $scripts, $get_modules ) {
+ if ( ! $scripts instanceof WP_Scripts ) {
+ $scripts = wp_scripts();
+ }
+
+ if ( ! $scripts->query( 'wp-editor', 'enqueued' ) && ! $scripts->query( 'wp-editor', 'done' ) ) {
+ return;
+ }
+
+ $kinds = call_user_func( $get_modules );
+ if ( empty( $kinds ) ) {
+ return;
+ }
+
+ $dependencies = $scripts->get_data( 'wp-editor', 'module_dependencies' );
+ $dependencies = is_array( $dependencies ) ? $dependencies : array();
+
+ $declared = array();
+ foreach ( $dependencies as $dependency ) {
+ $declared[] = is_array( $dependency ) ? $dependency['id'] : $dependency;
+ }
+
+ $added = false;
+ foreach ( $kinds as $entities ) {
+ foreach ( $entities as $modules ) {
+ foreach ( $modules as $module ) {
+ if ( ! in_array( $module, $declared, true ) ) {
+ $dependencies[] = array(
+ 'id' => $module,
+ 'import' => 'dynamic',
+ );
+ $declared[] = $module;
+ $added = true;
+ }
+ }
+ }
+ }
+
+ if ( $added ) {
+ // `add_data()` replaces the value, hence the merge above.
+ $scripts->add_data( 'wp-editor', 'module_dependencies', $dependencies );
+ }
+}
diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-entity-actions-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-entity-actions-controller.php
index d72cab8dc2..bf9ebcf895 100644
--- a/wp-includes/rest-api/endpoints/class-wp-rest-entity-actions-controller.php
+++ b/wp-includes/rest-api/endpoints/class-wp-rest-entity-actions-controller.php
@@ -197,20 +197,11 @@ class WP_REST_Entity_Actions_Controller extends WP_REST_Controller {
$kind = $request->get_param( 'kind' );
$name = $request->get_param( 'name' );
- $script_modules = array();
- foreach ( wp_get_registered_entity_action_modules( $kind, $name ) as $module => $action_ids ) {
- $script_modules[] = array(
- // A numeric module id is an integer as an array key.
- 'id' => (string) $module,
- 'actions' => array_values( $action_ids ),
- );
- }
-
$response = array(
'kind' => $kind,
'name' => $name,
'actions' => wp_get_registered_entity_actions( $kind, $name ),
- 'script_modules' => $script_modules,
+ 'script_modules' => wp_get_registered_entity_action_modules( $kind, $name ),
);
return rest_ensure_response( $response );
diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-entity-fields-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-entity-fields-controller.php
index e7674e2a80..c9738f2a7b 100644
--- a/wp-includes/rest-api/endpoints/class-wp-rest-entity-fields-controller.php
+++ b/wp-includes/rest-api/endpoints/class-wp-rest-entity-fields-controller.php
@@ -159,19 +159,11 @@ class WP_REST_Entity_Fields_Controller extends WP_REST_Controller {
$fields[] = wp_prepare_json_value_for_client( $field, $field_schema );
}
- $script_modules = array();
- foreach ( wp_get_registered_entity_field_modules( $kind, $name ) as $module => $field_ids ) {
- $script_modules[] = array(
- 'id' => $module,
- 'fields' => array_values( $field_ids ),
- );
- }
-
$response = array(
'kind' => $kind,
'name' => $name,
'fields' => $fields,
- 'script_modules' => $script_modules,
+ 'script_modules' => wp_get_registered_entity_field_modules( $kind, $name ),
);
return rest_ensure_response( $response );
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 9b005558e0..b106712edd 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
-$wp_version = '7.2-beta1-64685';
+$wp_version = '7.2-beta1-64686';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
diff --git a/wp-settings.php b/wp-settings.php
index 8bd37b43d6..cf8fb4d090 100644
--- a/wp-settings.php
+++ b/wp-settings.php
@@ -206,6 +206,7 @@ require ABSPATH . WPINC . '/theme-previews.php';
require ABSPATH . WPINC . '/template.php';
require ABSPATH . WPINC . '/class-wp-view-config-data.php';
require ABSPATH . WPINC . '/view-config.php';
+require ABSPATH . WPINC . '/entity-utils.php';
require ABSPATH . WPINC . '/class-wp-entity-actions-registry.php';
require ABSPATH . WPINC . '/entity-actions.php';
require ABSPATH . WPINC . '/class-wp-entity-fields-registry.php';