Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/wp-admin/site-editor.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ static function ( $classes ) {
array( rest_get_route_for_post_type_items( 'attachment' ), 'OPTIONS' ),
array( rest_get_route_for_post_type_items( 'page' ), 'OPTIONS' ),
'/wp/v2/types?context=view',
'/wp/v2/wp_registered_template?context=edit',
'/wp/v2/types/wp_template?context=edit',
'/wp/v2/types/wp_template_part?context=edit',
'/wp/v2/templates?context=edit&per_page=-1',
Expand Down Expand Up @@ -244,7 +245,9 @@ static function ( $classes ) {
);
}
}
} else {
} elseif ( isset( $_GET['p'] ) && '/' !== $_GET['p'] ) {
// Only prefetch for the root. If we preload it for all pages and it's not
// used it won't be possible to invalidate.
$preload_paths[] = '/wp/v2/templates/lookup?slug=front-page';
$preload_paths[] = '/wp/v2/templates/lookup?slug=home';
}
Expand Down
111 changes: 107 additions & 4 deletions src/wp-includes/block-template-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -1074,6 +1074,46 @@ function _build_block_template_result_from_post( $post ) {
return $template;
}

function get_registered_block_templates( $query ) {
$template_files = _get_block_templates_files( 'wp_template', $query );
$query_result = array();

// _get_block_templates_files seems broken, it does not obey the query.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we mark this somehow to see later if that function needs fixing?

if ( isset( $query['slug__in'] ) && is_array( $query['slug__in'] ) ) {
$template_files = array_filter(
$template_files,
function ( $template_file ) use ( $query ) {
return in_array( $template_file['slug'], $query['slug__in'], true );
}
);
}

foreach ( $template_files as $template_file ) {
$query_result[] = _build_block_template_result_from_file( $template_file, 'wp_template' );
}

// Add templates registered through 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 );

// Templates added by PHP filter also count as registered templates.
/** This filter is documented in wp-includes/block-template-utils.php */
return apply_filters( 'get_block_templates', $query_result, $query, 'wp_template' );
}

/**
* Retrieves a list of unified template objects based on a query.
*
Expand Down Expand Up @@ -1152,6 +1192,8 @@ function get_block_templates( $query = array(), $template_type = 'wp_template' )
$wp_query_args['post_status'] = 'publish';
}

$active_templates = get_option( 'active_templates', array() );

$template_query = new WP_Query( $wp_query_args );
$query_result = array();
foreach ( $template_query->posts as $post ) {
Expand All @@ -1173,7 +1215,14 @@ function get_block_templates( $query = array(), $template_type = 'wp_template' )
continue;
}

$query_result[] = $template;
if ( $template->is_custom || isset( $query['wp_id'] ) ) {
// Custom templates don't need to be activated, leave them be.
// Also don't filter out templates when querying by wp_id.
$query_result[] = $template;
} elseif ( isset( $active_templates[ $template->slug ] ) && $active_templates[ $template->slug ] === $post->ID ) {
// Only include active templates.
$query_result[] = $template;
}
}

if ( ! isset( $query['wp_id'] ) ) {
Expand Down Expand Up @@ -1296,7 +1345,25 @@ function get_block_template( $id, $template_type = 'wp_template' ) {
return null;
}
list( $theme, $slug ) = $parts;
$wp_query_args = array(

$active_templates = get_option( 'active_templates', array() );

if ( ! empty( $active_templates[ $slug ] ) ) {
if ( is_int( $active_templates[ $slug ] ) ) {
$post = get_post( $active_templates[ $slug ] );
if ( $post && 'publish' === $post->post_status ) {
$template = _build_block_template_result_from_post( $post );

if ( ! is_wp_error( $template ) && $theme === $template->theme ) {
return $template;
}
}
} elseif ( false === $active_templates[ $slug ] ) {
return null;
}
Comment on lines +1361 to +1363
Copy link

@justlevine justlevine Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this nested correctly? We can only get here via the ! empty( $active_templates[ #slug ] ) on L1351 (maybe we want an isset there? )

}

$wp_query_args = array(
'post_name__in' => array( $slug ),
'post_type' => $template_type,
'post_status' => array( 'auto-draft', 'draft', 'publish', 'trash' ),
Expand All @@ -1310,12 +1377,18 @@ function get_block_template( $id, $template_type = 'wp_template' ) {
),
),
);
$template_query = new WP_Query( $wp_query_args );
$posts = $template_query->posts;
$template_query = new WP_Query( $wp_query_args );
$posts = $template_query->posts;

if ( count( $posts ) > 0 ) {
$template = _build_block_template_result_from_post( $posts[0] );

// Custom templates don't need to be activated, so if it's a custom
// template, return it.
if ( ! is_wp_error( $template ) && $template->is_custom ) {
return $template;
}

if ( ! is_wp_error( $template ) ) {
return $template;
}
Expand Down Expand Up @@ -1779,3 +1852,33 @@ function inject_ignored_hooked_blocks_metadata_attributes( $changes, $deprecated

return $changes;
}

function wp_assign_new_template_to_theme( $changes, $request ) {
// Do not run this for templates created through the old enpoint.
$template = $request['id'] ? get_block_template( $request['id'], 'wp_template' ) : null;
if ( $template ) {
return $changes;
}
if ( ! isset( $changes->tax_input ) ) {
$changes->tax_input = array();
}
$changes->tax_input['wp_theme'] = isset( $request['theme'] ) ? $request['theme'] : get_stylesheet();
// All new templates saved will receive meta so we can distinguish between
// templates created the old way as edits and templates created the new way.
if ( ! isset( $changes->meta_input ) ) {
$changes->meta_input = array();
}
$changes->meta_input['is_inactive_by_default'] = true;
return $changes;
}

function wp_maybe_activate_template( $post_id ) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aside from documenting, we should audit these new functions to determine which ones to make private.

$post = get_post( $post_id );
$is_inactive_by_default = get_post_meta( $post_id, 'is_inactive_by_default', true );
if ( $is_inactive_by_default ) {
return;
}
$active_templates = get_option( 'active_templates', array() );
$active_templates[ $post->post_name ] = $post->ID;
update_option( 'active_templates', $active_templates );
}
72 changes: 69 additions & 3 deletions src/wp-includes/block-template.php
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,77 @@ function resolve_block_template( $template_type, $template_hierarchy, $fallback_
$template_hierarchy
);

// Find all potential templates 'wp_template' post matching the hierarchy.
$object = get_queried_object();
$specific_template = $object ? get_page_template_slug( $object ) : null;
$active_templates = (array) get_option( 'active_templates', array() );

// Remove templates slugs that are deactivated, except if it's the specific
// template or index.
$slugs = array_filter(
$slugs,
function ( $slug ) use ( $specific_template, $active_templates ) {
$should_ignore = $slug === $specific_template || 'index' === $slug;
return $should_ignore || ( ! isset( $active_templates[ $slug ] ) || false !== $active_templates[ $slug ] );
}
);

// We expect one template for each slug. Use the active template if it is
// set and exists. Otherwise use the static template.
$templates = array();
$remaining_slugs = array();

foreach ( $slugs as $slug ) {
if ( $slug === $specific_template || empty( $active_templates[ $slug ] ) ) {
$remaining_slugs[] = $slug;
continue;
}

// TODO: it need to be possible to set a static template as active.
$post = get_post( $active_templates[ $slug ] );
if ( ! $post || 'publish' !== $post->post_status ) {
$remaining_slugs[] = $slug;
continue;
}

$template = _build_block_template_result_from_post( $post );

// Ensure the active templates are associated with the active theme.
// See _build_block_template_object_from_post_object.
if ( get_stylesheet() !== $template->theme ) {
$remaining_slugs[] = $slug;
continue;
}

$templates[] = $template;
}

// Apply the filter to the active templates for backward compatibility.
/** This filter is documented in wp-includes/block-template-utils.php */
if ( ! empty( $templates ) ) {
$templates = apply_filters(
'get_block_templates',
$templates,
array(
'slug__in' => array_map(
function ( $template ) {
return $template->slug;
},
$templates
),
),
'wp_template'
);
}

// For any remaining slugs, use the static template.
$query = array(
'slug__in' => $slugs,
'slug__in' => $remaining_slugs,
);
$templates = get_block_templates( $query );
$templates = array_merge( $templates, get_registered_block_templates( $query ) );

if ( $specific_template ) {
$templates = array_merge( $templates, get_block_templates( array( 'slug__in' => array( $specific_template ) ) ) );
}

// Order these templates per slug priority.
// Build map of template slugs to their priority in the current hierarchy.
Expand Down
5 changes: 5 additions & 0 deletions src/wp-includes/default-filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,7 @@
// Block templates post type and rendering.
add_filter( 'render_block_context', '_block_template_render_without_post_block_context' );
add_filter( 'pre_wp_unique_post_slug', 'wp_filter_wp_template_unique_post_slug', 10, 5 );
add_action( 'save_post_wp_template', 'wp_maybe_activate_template' );
add_action( 'save_post_wp_template_part', 'wp_set_unique_slug_on_create_template_part' );
add_action( 'wp_enqueue_scripts', 'wp_enqueue_block_template_skip_link' );
add_action( 'wp_footer', 'the_block_template_skip_link' ); // Retained for backwards-compatibility. Unhooked by wp_enqueue_block_template_skip_link().
Expand Down Expand Up @@ -780,6 +781,10 @@
add_filter( 'rest_pre_insert_wp_template', 'inject_ignored_hooked_blocks_metadata_attributes' );
add_filter( 'rest_pre_insert_wp_template_part', 'inject_ignored_hooked_blocks_metadata_attributes' );

// Assign the wp_theme term to any newly created wp_template with the new endpoint.
// Must run before `inject_ignored_hooked_blocks_metadata_attributes`.
add_action( 'rest_pre_insert_wp_template', 'wp_assign_new_template_to_theme', 9, 2 );

// Update ignoredHookedBlocks postmeta for some post types.
add_filter( 'rest_pre_insert_page', 'update_ignored_hooked_blocks_postmeta' );
add_filter( 'rest_pre_insert_post', 'update_ignored_hooked_blocks_postmeta' );
Expand Down
19 changes: 19 additions & 0 deletions src/wp-includes/option.php
Original file line number Diff line number Diff line change
Expand Up @@ -2959,6 +2959,25 @@ function register_initial_settings() {
'description' => __( 'Allow people to submit comments on new posts.' ),
)
);

register_setting(
'reading',
'active_templates',
array(
'type' => 'object',
// Do not set the default value to an empty array! For some reason
// that will prevent the option from being set to an empty array.
'show_in_rest' => array(
'schema' => array(
'type' => 'object',
// Properties can be integers, strings, or false
// (deactivated).
'additionalProperties' => true,
),
),
'label' => 'Active Templates',
)
);
}

/**
Expand Down
19 changes: 15 additions & 4 deletions src/wp-includes/post.php
Original file line number Diff line number Diff line change
Expand Up @@ -398,10 +398,8 @@ function create_initial_post_types() {
'show_in_menu' => false,
'show_in_rest' => true,
'rewrite' => false,
'rest_base' => 'templates',
'rest_controller_class' => 'WP_REST_Templates_Controller',
'autosave_rest_controller_class' => 'WP_REST_Template_Autosaves_Controller',
'revisions_rest_controller_class' => 'WP_REST_Template_Revisions_Controller',
'rest_base' => 'wp_template',
'rest_controller_class' => 'WP_REST_Posts_Controller',
'late_route_registration' => true,
'capability_type' => array( 'template', 'templates' ),
'capabilities' => array(
Expand All @@ -426,6 +424,7 @@ function create_initial_post_types() {
'editor',
'revisions',
'author',
'custom-fields',
),
)
);
Expand Down Expand Up @@ -8629,4 +8628,16 @@ function wp_create_initial_post_meta() {
),
)
);

// Allow setting the is_wp_suggestion meta field, which partly determines if
// a template is a custom template.
register_post_meta(
'wp_template',
'is_wp_suggestion',
array(
'type' => 'boolean',
'show_in_rest' => true,
'single' => true,
)
);
}
Loading
Loading