diff --git a/wp-includes/block-template-utils.php b/wp-includes/block-template-utils.php
index fb9076c42e..2900781172 100644
--- a/wp-includes/block-template-utils.php
+++ b/wp-includes/block-template-utils.php
@@ -592,6 +592,15 @@ function _build_block_template_result_from_file( $template_file, $template_type
$template->is_custom = true;
$template->modified = null;
+ if ( 'wp_template' === $template_type ) {
+ $registered_template = WP_Block_Templates_Registry::get_instance()->get_by_slug( $template_file['slug'] );
+ if ( $registered_template ) {
+ $template->plugin = $registered_template->plugin;
+ $template->title = empty( $template->title ) || $template->title === $template->slug ? $registered_template->title : $template->title;
+ $template->description = empty( $template->description ) ? $registered_template->description : $template->description;
+ }
+ }
+
if ( 'wp_template' === $template_type && isset( $default_template_types[ $template_file['slug'] ] ) ) {
$template->description = $default_template_types[ $template_file['slug'] ]['description'];
$template->title = $default_template_types[ $template_file['slug'] ]['title'];
@@ -1014,6 +1023,19 @@ function _build_block_template_result_from_post( $post ) {
}
}
+ if ( 'wp_template' === $post->post_type ) {
+ $registered_template = WP_Block_Templates_Registry::get_instance()->get_by_slug( $template->slug );
+ if ( $registered_template ) {
+ $template->plugin = $registered_template->plugin;
+ $template->origin =
+ 'theme' !== $template->origin && 'theme' !== $template->source ?
+ 'plugin' :
+ $template->origin;
+ $template->title = empty( $template->title ) || $template->title === $template->slug ? $registered_template->title : $template->title;
+ $template->description = empty( $template->description ) ? $registered_template->description : $template->description;
+ }
+ }
+
$hooked_blocks = get_hooked_blocks();
if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) {
$before_block_visitor = make_before_block_visitor( $hooked_blocks, $template, 'insert_hooked_blocks_and_set_ignored_hooked_blocks_metadata' );
@@ -1157,6 +1179,23 @@ function get_block_templates( $query = array(), $template_type = 'wp_template' )
foreach ( $template_files as $template_file ) {
$query_result[] = _build_block_template_result_from_file( $template_file, $template_type );
}
+
+ if ( 'wp_template' === $template_type ) {
+ // Add templates registered in the template registry. Filtering out the ones which have a theme file.
+ $registered_templates = WP_Block_Templates_Registry::get_instance()->get_by_query( $query );
+ $matching_registered_templates = array_filter(
+ $registered_templates,
+ function ( $registered_template ) use ( $template_files ) {
+ foreach ( $template_files as $template_file ) {
+ if ( $template_file['slug'] === $registered_template->slug ) {
+ return false;
+ }
+ }
+ return true;
+ }
+ );
+ $query_result = array_merge( $query_result, $matching_registered_templates );
+ }
}
/**
@@ -1287,18 +1326,17 @@ function get_block_file_template( $id, $template_type = 'wp_template' ) {
}
list( $theme, $slug ) = $parts;
- if ( get_stylesheet() !== $theme ) {
- /** This filter is documented in wp-includes/block-template-utils.php */
- return apply_filters( 'get_block_file_template', null, $id, $template_type );
+ if ( get_stylesheet() === $theme ) {
+ $template_file = _get_block_template_file( $template_type, $slug );
+ if ( null !== $template_file ) {
+ $block_template = _build_block_template_result_from_file( $template_file, $template_type );
+
+ /** This filter is documented in wp-includes/block-template-utils.php */
+ return apply_filters( 'get_block_file_template', $block_template, $id, $template_type );
+ }
}
- $template_file = _get_block_template_file( $template_type, $slug );
- if ( null === $template_file ) {
- /** This filter is documented in wp-includes/block-template-utils.php */
- return apply_filters( 'get_block_file_template', null, $id, $template_type );
- }
-
- $block_template = _build_block_template_result_from_file( $template_file, $template_type );
+ $block_template = WP_Block_Templates_Registry::get_instance()->get_by_slug( $slug );
/**
* Filters the block template object after it has been (potentially) fetched from the theme file.
@@ -1665,12 +1703,12 @@ function inject_ignored_hooked_blocks_metadata_attributes( $changes, $deprecated
);
}
- $content = get_comment_delimited_block_content(
+ $content = get_comment_delimited_block_content(
'core/template-part',
$attributes,
$changes->post_content
);
- $content = apply_block_hooks_to_content( $content, $template, 'set_ignored_hooked_blocks_metadata' );
+ $content = apply_block_hooks_to_content( $content, $template, 'set_ignored_hooked_blocks_metadata' );
$changes->post_content = remove_serialized_parent_block( $content );
$wrapper_block_markup = extract_serialized_parent_block( $content );
diff --git a/wp-includes/block-template.php b/wp-includes/block-template.php
index 0a4d85ac71..d42a74479c 100644
--- a/wp-includes/block-template.php
+++ b/wp-includes/block-template.php
@@ -358,3 +358,38 @@ function _resolve_template_for_new_post( $wp_query ) {
$wp_query->set( 'post_status', 'auto-draft' );
}
}
+
+/**
+ * Register a block template.
+ *
+ * @since 6.7.0
+ *
+ * @param string $template_name Template name in the form of `plugin_uri//template_name`.
+ * @param array|string $args {
+ * @type string $title Optional. Title of the template as it will be shown in the Site Editor
+ * and other UI elements.
+ * @type string $description Optional. Description of the template as it will be shown in the Site
+ * Editor.
+ * @type string $content Optional. Default content of the template that will be used when the
+ * template is rendered or edited in the editor.
+ * @type string[] $post_types Optional. Array of post types to which the template should be available.
+ * @type string $plugin Optional. Slug of the plugin that registers the template.
+ * }
+ * @return WP_Block_Template|WP_Error The registered template object on success, WP_Error object on failure.
+ */
+function wp_register_block_template( $template_name, $args = array() ) {
+ return WP_Block_Templates_Registry::get_instance()->register( $template_name, $args );
+}
+
+/**
+ * Unregister a block template.
+ *
+ * @since 6.7.0
+ *
+ * @param string $template_name Template name in the form of `plugin_uri//template_name`.
+ * @return WP_Block_Template|WP_Error The unregistered template object on success, WP_Error object on failure or if the
+ * template doesn't exist.
+ */
+function wp_unregister_block_template( $template_name ) {
+ return WP_Block_Templates_Registry::get_instance()->unregister( $template_name );
+}
diff --git a/wp-includes/blocks/media-text.php b/wp-includes/blocks/media-text.php
index 87be164a04..b65137b150 100644
--- a/wp-includes/blocks/media-text.php
+++ b/wp-includes/blocks/media-text.php
@@ -29,15 +29,32 @@ function render_block_core_media_text( $attributes, $content ) {
return $content;
}
+ $has_media_on_right = isset( $attributes['mediaPosition'] ) && 'right' === $attributes['mediaPosition'];
+ $image_fill = isset( $attributes['imageFill'] ) && $attributes['imageFill'];
+ $focal_point = isset( $attributes['focalPoint'] ) ? round( $attributes['focalPoint']['x'] * 100 ) . '% ' . round( $attributes['focalPoint']['y'] * 100 ) . '%' : '50% 50%';
+ $unique_id = 'wp-block-media-text__media-' . wp_unique_id();
+
+ $block_tag_processor = new WP_HTML_Tag_Processor( $content );
+ $block_query = array(
+ 'tag_name' => 'div',
+ 'class_name' => 'wp-block-media-text',
+ );
+
+ while ( $block_tag_processor->next_tag( $block_query ) ) {
+ if ( $image_fill ) {
+ // The markup below does not work with the deprecated `is-image-fill` class.
+ $block_tag_processor->remove_class( 'is-image-fill' );
+ $block_tag_processor->add_class( 'is-image-fill-element' );
+ }
+ }
+
+ $content = $block_tag_processor->get_updated_html();
+
$media_tag_processor = new WP_HTML_Tag_Processor( $content );
$wrapping_figure_query = array(
'tag_name' => 'figure',
'class_name' => 'wp-block-media-text__media',
);
- $has_media_on_right = isset( $attributes['mediaPosition'] ) && 'right' === $attributes['mediaPosition'];
- $image_fill = isset( $attributes['imageFill'] ) && $attributes['imageFill'];
- $focal_point = isset( $attributes['focalPoint'] ) ? round( $attributes['focalPoint']['x'] * 100 ) . '% ' . round( $attributes['focalPoint']['y'] * 100 ) . '%' : '50% 50%';
- $unique_id = 'wp-block-media-text__media-' . wp_unique_id();
if ( $has_media_on_right ) {
// Loop through all the figure tags and set a bookmark on the last figure tag.
@@ -46,59 +63,52 @@ function render_block_core_media_text( $attributes, $content ) {
}
if ( $media_tag_processor->has_bookmark( 'last_figure' ) ) {
$media_tag_processor->seek( 'last_figure' );
- if ( $image_fill ) {
- $media_tag_processor->set_attribute( 'style', 'background-image:url(' . esc_url( $current_featured_image ) . ');background-position:' . $focal_point . ';' );
- } else {
- // Insert a unique ID to identify the figure tag.
- $media_tag_processor->set_attribute( 'id', $unique_id );
- }
+ // Insert a unique ID to identify the figure tag.
+ $media_tag_processor->set_attribute( 'id', $unique_id );
}
} else {
if ( $media_tag_processor->next_tag( $wrapping_figure_query ) ) {
- if ( $image_fill ) {
- $media_tag_processor->set_attribute( 'style', 'background-image:url(' . esc_url( $current_featured_image ) . ');background-position:' . $focal_point . ';' );
- } else {
- // Insert a unique ID to identify the figure tag.
- $media_tag_processor->set_attribute( 'id', $unique_id );
- }
+ // Insert a unique ID to identify the figure tag.
+ $media_tag_processor->set_attribute( 'id', $unique_id );
}
}
$content = $media_tag_processor->get_updated_html();
- // If the image is not set to fill, add the image tag inside the figure tag,
- // and update the image attributes in order to display the featured image.
- if ( ! $image_fill ) {
- $media_size_slug = isset( $attributes['mediaSizeSlug'] ) ? $attributes['mediaSizeSlug'] : 'full';
- $image_tag = '
';
- $content = preg_replace(
- '/()/',
- '$1' . $image_tag,
- $content
- );
+ // Add the image tag inside the figure tag, and update the image attributes
+ // in order to display the featured image.
+ $media_size_slug = isset( $attributes['mediaSizeSlug'] ) ? $attributes['mediaSizeSlug'] : 'full';
+ $image_tag = '
';
+ $content = preg_replace(
+ '/()/',
+ '$1' . $image_tag,
+ $content
+ );
- $image_tag_processor = new WP_HTML_Tag_Processor( $content );
+ $image_tag_processor = new WP_HTML_Tag_Processor( $content );
+ if ( $image_tag_processor->next_tag(
+ array(
+ 'tag_name' => 'figure',
+ 'id' => $unique_id,
+ )
+ ) ) {
+ // The ID is only used to ensure that the correct figure tag is selected,
+ // and can now be removed.
+ $image_tag_processor->remove_attribute( 'id' );
if ( $image_tag_processor->next_tag(
array(
- 'tag_name' => 'figure',
- 'id' => $unique_id,
+ 'tag_name' => 'img',
+ 'class_name' => 'wp-block-media-text__featured_image',
)
) ) {
- // The ID is only used to ensure that the correct figure tag is selected,
- // and can now be removed.
- $image_tag_processor->remove_attribute( 'id' );
- if ( $image_tag_processor->next_tag(
- array(
- 'tag_name' => 'img',
- 'class_name' => 'wp-block-media-text__featured_image',
- )
- ) ) {
- $image_tag_processor->set_attribute( 'src', esc_url( $current_featured_image ) );
- $image_tag_processor->set_attribute( 'class', 'wp-image-' . get_post_thumbnail_id() . ' size-' . $media_size_slug );
- $image_tag_processor->set_attribute( 'alt', trim( strip_tags( get_post_meta( get_post_thumbnail_id(), '_wp_attachment_image_alt', true ) ) ) );
-
- $content = $image_tag_processor->get_updated_html();
+ $image_tag_processor->set_attribute( 'src', esc_url( $current_featured_image ) );
+ $image_tag_processor->set_attribute( 'class', 'wp-image-' . get_post_thumbnail_id() . ' size-' . $media_size_slug );
+ $image_tag_processor->set_attribute( 'alt', trim( strip_tags( get_post_meta( get_post_thumbnail_id(), '_wp_attachment_image_alt', true ) ) ) );
+ if ( $image_fill ) {
+ $image_tag_processor->set_attribute( 'style', 'object-position:' . $focal_point . ';' );
}
+
+ $content = $image_tag_processor->get_updated_html();
}
}
diff --git a/wp-includes/class-wp-block-template.php b/wp-includes/class-wp-block-template.php
index 8149935203..822302d4c4 100644
--- a/wp-includes/class-wp-block-template.php
+++ b/wp-includes/class-wp-block-template.php
@@ -131,6 +131,14 @@ class WP_Block_Template {
*/
public $author;
+ /**
+ * Plugin.
+ *
+ * @since 6.7.0
+ * @var string|null
+ */
+ public $plugin;
+
/**
* Post types.
*
diff --git a/wp-includes/class-wp-block-templates-registry.php b/wp-includes/class-wp-block-templates-registry.php
new file mode 100644
index 0000000000..368c517fc9
--- /dev/null
+++ b/wp-includes/class-wp-block-templates-registry.php
@@ -0,0 +1,256 @@
+ $instance` pairs.
+ *
+ * @since 6.7.0
+ * @var WP_Block_Template[] $registered_block_templates Registered templates.
+ */
+ private $registered_templates = array();
+
+ /**
+ * Container for the main instance of the class.
+ *
+ * @since 6.7.0
+ * @var WP_Block_Templates_Registry|null
+ */
+ private static $instance = null;
+
+ /**
+ * Registers a template.
+ *
+ * @since 6.7.0
+ *
+ * @param string $template_name Template name including namespace.
+ * @param array $args Optional. Array of template arguments.
+ * @return WP_Block_Template|WP_Error The registered template on success, or WP_Error on failure.
+ */
+ public function register( $template_name, $args = array() ) {
+
+ $template = null;
+
+ $error_message = '';
+ $error_code = '';
+
+ if ( ! is_string( $template_name ) ) {
+ $error_message = __( 'Template names must be strings.' );
+ $error_code = 'template_name_no_string';
+ } elseif ( preg_match( '/[A-Z]+/', $template_name ) ) {
+ $error_message = __( 'Template names must not contain uppercase characters.' );
+ $error_code = 'template_name_no_uppercase';
+ } elseif ( ! preg_match( '/^[a-z0-9-]+\/\/[a-z0-9-]+$/', $template_name ) ) {
+ $error_message = __( 'Template names must contain a namespace prefix. Example: my-plugin//my-custom-template' );
+ $error_code = 'template_no_prefix';
+ } elseif ( $this->is_registered( $template_name ) ) {
+ /* translators: %s: Template name. */
+ $error_message = sprintf( __( 'Template "%s" is already registered.' ), $template_name );
+ $error_code = 'template_already_registered';
+ }
+
+ if ( $error_message ) {
+ _doing_it_wrong(
+ __METHOD__,
+ $error_message,
+ '6.7.0'
+ );
+ return new WP_Error( $error_code, $error_message );
+ }
+
+ if ( ! $template ) {
+ $theme_name = get_stylesheet();
+ list( $plugin, $slug ) = explode( '//', $template_name );
+ $default_template_types = get_default_block_template_types();
+
+ $template = new WP_Block_Template();
+ $template->id = $theme_name . '//' . $slug;
+ $template->theme = $theme_name;
+ $template->plugin = $plugin;
+ $template->author = null;
+ $template->content = isset( $args['content'] ) ? $args['content'] : '';
+ $template->source = 'plugin';
+ $template->slug = $slug;
+ $template->type = 'wp_template';
+ $template->title = isset( $args['title'] ) ? $args['title'] : $template_name;
+ $template->description = isset( $args['description'] ) ? $args['description'] : '';
+ $template->status = 'publish';
+ $template->origin = 'plugin';
+ $template->is_custom = ! isset( $default_template_types[ $template_name ] );
+ $template->post_types = isset( $args['post_types'] ) ? $args['post_types'] : array();
+ }
+
+ $this->registered_templates[ $template_name ] = $template;
+
+ return $template;
+ }
+
+ /**
+ * Retrieves all registered templates.
+ *
+ * @since 6.7.0
+ *
+ * @return WP_Block_Template[] Associative array of `$template_name => $template` pairs.
+ */
+ public function get_all_registered() {
+ return $this->registered_templates;
+ }
+
+ /**
+ * Retrieves a registered template by its name.
+ *
+ * @since 6.7.0
+ *
+ * @param string $template_name Template name including namespace.
+ * @return WP_Block_Template|null The registered template, or null if it is not registered.
+ */
+ public function get_registered( $template_name ) {
+ if ( ! $this->is_registered( $template_name ) ) {
+ return null;
+ }
+
+ return $this->registered_templates[ $template_name ];
+ }
+
+ /**
+ * Retrieves a registered template by its slug.
+ *
+ * @since 6.7.0
+ *
+ * @param string $template_slug Slug of the template.
+ * @return WP_Block_Template|null The registered template, or null if it is not registered.
+ */
+ public function get_by_slug( $template_slug ) {
+ $all_templates = $this->get_all_registered();
+
+ if ( ! $all_templates ) {
+ return null;
+ }
+
+ foreach ( $all_templates as $template ) {
+ if ( $template->slug === $template_slug ) {
+ return $template;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieves registered templates matching a query.
+ *
+ * @since 6.7.0
+ *
+ * @param array $query {
+ * Arguments to retrieve templates. Optional, empty by default.
+ *
+ * @type string[] $slug__in List of slugs to include.
+ * @type string[] $slug__not_in List of slugs to skip.
+ * @type string $post_type Post type to get the templates for.
+ * }
+ * @return WP_Block_Template[] Associative array of `$template_name => $template` pairs.
+ */
+ public function get_by_query( $query = array() ) {
+ $all_templates = $this->get_all_registered();
+
+ if ( ! $all_templates ) {
+ return array();
+ }
+
+ $query = wp_parse_args(
+ $query,
+ array(
+ 'slug__in' => array(),
+ 'slug__not_in' => array(),
+ 'post_type' => '',
+ )
+ );
+ $slugs_to_include = $query['slug__in'];
+ $slugs_to_skip = $query['slug__not_in'];
+ $post_type = $query['post_type'];
+
+ $matching_templates = array();
+ foreach ( $all_templates as $template_name => $template ) {
+ if ( $slugs_to_include && ! in_array( $template->slug, $slugs_to_include, true ) ) {
+ continue;
+ }
+
+ if ( $slugs_to_skip && in_array( $template->slug, $slugs_to_skip, true ) ) {
+ continue;
+ }
+
+ if ( $post_type && ! in_array( $post_type, $template->post_types, true ) ) {
+ continue;
+ }
+
+ $matching_templates[ $template_name ] = $template;
+ }
+
+ return $matching_templates;
+ }
+
+ /**
+ * Checks if a template is registered.
+ *
+ * @since 6.7.0
+ *
+ * @param string $template_name Template name.
+ * @return bool True if the template is registered, false otherwise.
+ */
+ public function is_registered( $template_name ) {
+ return isset( $this->registered_templates[ $template_name ] );
+ }
+
+ /**
+ * Unregisters a template.
+ *
+ * @since 6.7.0
+ *
+ * @param string $template_name Template name including namespace.
+ * @return WP_Block_Template|WP_Error The unregistered template on success, or WP_Error on failure.
+ */
+ public function unregister( $template_name ) {
+ if ( ! $this->is_registered( $template_name ) ) {
+ _doing_it_wrong(
+ __METHOD__,
+ /* translators: %s: Template name. */
+ sprintf( __( 'Template "%s" is not registered.' ), $template_name ),
+ '6.7.0'
+ );
+ /* translators: %s: Template name. */
+ return new WP_Error( 'template_not_registered', __( 'Template "%s" is not registered.' ) );
+ }
+
+ $unregistered_template = $this->registered_templates[ $template_name ];
+ unset( $this->registered_templates[ $template_name ] );
+
+ return $unregistered_template;
+ }
+
+ /**
+ * Utility method to retrieve the main instance of the class.
+ *
+ * The instance will be created if it does not exist yet.
+ *
+ * @since 6.7.0
+ *
+ * @return WP_Block_Templates_Registry The main instance.
+ */
+ public static function get_instance() {
+ if ( null === self::$instance ) {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+}
diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php
index e2063a450d..43780fb4e6 100644
--- a/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php
+++ b/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php
@@ -326,7 +326,7 @@ class WP_REST_Templates_Controller extends WP_REST_Controller {
* @return WP_REST_Response|WP_Error
*/
public function get_item( $request ) {
- if ( isset( $request['source'] ) && 'theme' === $request['source'] ) {
+ if ( isset( $request['source'] ) && ( 'theme' === $request['source'] || 'plugin' === $request['source'] ) ) {
$template = get_block_file_template( $request['id'], $this->post_type );
} else {
$template = get_block_template( $request['id'], $this->post_type );
@@ -776,6 +776,13 @@ class WP_REST_Templates_Controller extends WP_REST_Controller {
$data['original_source'] = self::get_wp_templates_original_source_field( $template );
}
+ if ( rest_is_field_included( 'plugin', $fields ) ) {
+ $registered_template = WP_Block_Templates_Registry::get_instance()->get_by_slug( $template->slug );
+ if ( $registered_template ) {
+ $data['plugin'] = $registered_template->plugin;
+ }
+ }
+
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
$data = $this->add_additional_fields_to_object( $data, $request );
$data = $this->filter_response_by_context( $data, $context );
@@ -831,7 +838,7 @@ class WP_REST_Templates_Controller extends WP_REST_Controller {
}
// Added by plugin.
- if ( $template_object->has_theme_file && 'plugin' === $template_object->origin ) {
+ if ( 'plugin' === $template_object->origin ) {
return 'plugin';
}
@@ -865,9 +872,41 @@ class WP_REST_Templates_Controller extends WP_REST_Controller {
$theme_name = wp_get_theme( $template_object->theme )->get( 'Name' );
return empty( $theme_name ) ? $template_object->theme : $theme_name;
case 'plugin':
- $plugins = get_plugins();
- $plugin = $plugins[ plugin_basename( sanitize_text_field( $template_object->theme . '.php' ) ) ];
- return empty( $plugin['Name'] ) ? $template_object->theme : $plugin['Name'];
+ if ( ! function_exists( 'get_plugins' ) || ! function_exists( 'get_plugin_data' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
+ }
+ if ( isset( $template_object->plugin ) ) {
+ $plugins = wp_get_active_and_valid_plugins();
+
+ foreach ( $plugins as $plugin_file ) {
+ $plugin_basename = plugin_basename( $plugin_file );
+ // Split basename by '/' to get the plugin slug.
+ list( $plugin_slug, ) = explode( '/', $plugin_basename );
+
+ if ( $plugin_slug === $template_object->plugin ) {
+ $plugin_data = get_plugin_data( $plugin_file );
+
+ if ( ! empty( $plugin_data['Name'] ) ) {
+ return $plugin_data['Name'];
+ }
+
+ break;
+ }
+ }
+ }
+
+ /*
+ * Fall back to the theme name if the plugin is not defined. That's needed to keep backwards
+ * compatibility with templates that were registered before the plugin attribute was added.
+ */
+ $plugins = get_plugins();
+ $plugin_basename = plugin_basename( sanitize_text_field( $template_object->theme . '.php' ) );
+ if ( isset( $plugins[ $plugin_basename ] ) && isset( $plugins[ $plugin_basename ]['Name'] ) ) {
+ return $plugins[ $plugin_basename ]['Name'];
+ }
+ return isset( $template_object->plugin ) ?
+ $template_object->plugin :
+ $template_object->theme;
case 'site':
return get_bloginfo( 'name' );
case 'user':
@@ -1134,6 +1173,12 @@ class WP_REST_Templates_Controller extends WP_REST_Controller {
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
);
+ $schema['properties']['plugin'] = array(
+ 'type' => 'string',
+ 'description' => __( 'Plugin that registered the template.' ),
+ 'readonly' => true,
+ 'context' => array( 'view', 'edit', 'embed' ),
+ );
}
if ( 'wp_template_part' === $this->post_type ) {
diff --git a/wp-includes/version.php b/wp-includes/version.php
index af52bf6a99..04d5e724f1 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
-$wp_version = '6.7-alpha-59072';
+$wp_version = '6.7-alpha-59073';
/**
* 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 d3dfe5776e..4643892ada 100644
--- a/wp-settings.php
+++ b/wp-settings.php
@@ -192,6 +192,7 @@ require ABSPATH . WPINC . '/class-wp-theme-json-resolver.php';
require ABSPATH . WPINC . '/class-wp-duotone.php';
require ABSPATH . WPINC . '/global-styles-and-settings.php';
require ABSPATH . WPINC . '/class-wp-block-template.php';
+require ABSPATH . WPINC . '/class-wp-block-templates-registry.php';
require ABSPATH . WPINC . '/block-template-utils.php';
require ABSPATH . WPINC . '/block-template.php';
require ABSPATH . WPINC . '/theme-templates.php';