wp/wp-includes/block-template.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
child 22 8c2e4d02f4ef
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
     4  *
     4  *
     5  * @package WordPress
     5  * @package WordPress
     6  */
     6  */
     7 
     7 
     8 /**
     8 /**
     9  * Adds necessary filters to use 'wp_template' posts instead of theme template files.
     9  * Adds necessary hooks to resolve '_wp-find-template' requests.
    10  *
    10  *
    11  * @access private
    11  * @access private
    12  * @since 5.9.0
    12  * @since 5.9.0
    13  */
    13  */
    14 function _add_template_loader_filters() {
    14 function _add_template_loader_filters() {
    15 	if ( ! current_theme_supports( 'block-templates' ) ) {
    15 	if ( isset( $_GET['_wp-find-template'] ) && current_theme_supports( 'block-templates' ) ) {
    16 		return;
    16 		add_action( 'pre_get_posts', '_resolve_template_for_new_post' );
    17 	}
    17 	}
    18 
    18 }
    19 	$template_types = array_keys( get_default_block_template_types() );
    19 
    20 	foreach ( $template_types as $template_type ) {
    20 /**
    21 		// Skip 'embed' for now because it is not a regular template type.
    21  * Finds a block template with equal or higher specificity than a given PHP template file.
    22 		if ( 'embed' === $template_type ) {
       
    23 			continue;
       
    24 		}
       
    25 		add_filter( str_replace( '-', '', $template_type ) . '_template', 'locate_block_template', 20, 3 );
       
    26 	}
       
    27 
       
    28 	// Request to resolve a template.
       
    29 	if ( isset( $_GET['_wp-find-template'] ) ) {
       
    30 		add_filter( 'pre_get_posts', '_resolve_template_for_new_post' );
       
    31 	}
       
    32 }
       
    33 
       
    34 /**
       
    35  * Find a block template with equal or higher specificity than a given PHP template file.
       
    36  *
    22  *
    37  * Internally, this communicates the block content that needs to be used by the template canvas through a global variable.
    23  * Internally, this communicates the block content that needs to be used by the template canvas through a global variable.
    38  *
    24  *
    39  * @since 5.8.0
    25  * @since 5.8.0
       
    26  * @since 6.3.0 Added `$_wp_current_template_id` global for editing of current template directly from the admin bar.
    40  *
    27  *
    41  * @global string $_wp_current_template_content
    28  * @global string $_wp_current_template_content
       
    29  * @global string $_wp_current_template_id
    42  *
    30  *
    43  * @param string   $template  Path to the template. See locate_template().
    31  * @param string   $template  Path to the template. See locate_template().
    44  * @param string   $type      Sanitized filename without extension.
    32  * @param string   $type      Sanitized filename without extension.
    45  * @param string[] $templates A list of template candidates, in descending order of priority.
    33  * @param string[] $templates A list of template candidates, in descending order of priority.
    46  * @return string The path to the Full Site Editing template canvas file, or the fallback PHP template.
    34  * @return string The path to the Site Editor template canvas file, or the fallback PHP template.
    47  */
    35  */
    48 function locate_block_template( $template, $type, array $templates ) {
    36 function locate_block_template( $template, $type, array $templates ) {
    49 	global $_wp_current_template_content;
    37 	global $_wp_current_template_content, $_wp_current_template_id;
    50 
    38 
    51 	if ( ! current_theme_supports( 'block-templates' ) ) {
    39 	if ( ! current_theme_supports( 'block-templates' ) ) {
    52 		return $template;
    40 		return $template;
    53 	}
    41 	}
    54 
    42 
    76 	}
    64 	}
    77 
    65 
    78 	$block_template = resolve_block_template( $type, $templates, $template );
    66 	$block_template = resolve_block_template( $type, $templates, $template );
    79 
    67 
    80 	if ( $block_template ) {
    68 	if ( $block_template ) {
       
    69 		$_wp_current_template_id = $block_template->id;
       
    70 
    81 		if ( empty( $block_template->content ) && is_user_logged_in() ) {
    71 		if ( empty( $block_template->content ) && is_user_logged_in() ) {
    82 			$_wp_current_template_content =
    72 			$_wp_current_template_content =
    83 			sprintf(
    73 			sprintf(
    84 				/* translators: %s: Template title */
    74 				/* translators: %s: Template title */
    85 				__( 'Empty template: %s' ),
    75 				__( 'Empty template: %s' ),
   116 	// This file will be included instead of the theme's template file.
   106 	// This file will be included instead of the theme's template file.
   117 	return ABSPATH . WPINC . '/template-canvas.php';
   107 	return ABSPATH . WPINC . '/template-canvas.php';
   118 }
   108 }
   119 
   109 
   120 /**
   110 /**
   121  * Return the correct 'wp_template' to render for the request template type.
   111  * Returns the correct 'wp_template' to render for the request template type.
   122  *
   112  *
   123  * @access private
   113  * @access private
   124  * @since 5.8.0
   114  * @since 5.8.0
   125  * @since 5.9.0 Added the `$fallback_template` parameter.
   115  * @since 5.9.0 Added the `$fallback_template` parameter.
   126  *
   116  *
   143 		$template_hierarchy
   133 		$template_hierarchy
   144 	);
   134 	);
   145 
   135 
   146 	// Find all potential templates 'wp_template' post matching the hierarchy.
   136 	// Find all potential templates 'wp_template' post matching the hierarchy.
   147 	$query     = array(
   137 	$query     = array(
   148 		'theme'    => wp_get_theme()->get_stylesheet(),
       
   149 		'slug__in' => $slugs,
   138 		'slug__in' => $slugs,
   150 	);
   139 	);
   151 	$templates = get_block_templates( $query );
   140 	$templates = get_block_templates( $query );
   152 
   141 
   153 	// Order these templates per slug priority.
   142 	// Order these templates per slug priority.
   164 	$theme_base_path        = get_stylesheet_directory() . DIRECTORY_SEPARATOR;
   153 	$theme_base_path        = get_stylesheet_directory() . DIRECTORY_SEPARATOR;
   165 	$parent_theme_base_path = get_template_directory() . DIRECTORY_SEPARATOR;
   154 	$parent_theme_base_path = get_template_directory() . DIRECTORY_SEPARATOR;
   166 
   155 
   167 	// Is the active theme a child theme, and is the PHP fallback template part of it?
   156 	// Is the active theme a child theme, and is the PHP fallback template part of it?
   168 	if (
   157 	if (
   169 		strpos( $fallback_template, $theme_base_path ) === 0 &&
   158 		str_starts_with( $fallback_template, $theme_base_path ) &&
   170 		strpos( $fallback_template, $parent_theme_base_path ) === false
   159 		! str_contains( $fallback_template, $parent_theme_base_path )
   171 	) {
   160 	) {
   172 		$fallback_template_slug = substr(
   161 		$fallback_template_slug = substr(
   173 			$fallback_template,
   162 			$fallback_template,
   174 			// Starting position of slug.
   163 			// Starting position of slug.
   175 			strpos( $fallback_template, $theme_base_path ) + strlen( $theme_base_path ),
   164 			strpos( $fallback_template, $theme_base_path ) + strlen( $theme_base_path ),
   217  * Returns the markup for the current template.
   206  * Returns the markup for the current template.
   218  *
   207  *
   219  * @access private
   208  * @access private
   220  * @since 5.8.0
   209  * @since 5.8.0
   221  *
   210  *
       
   211  * @global string   $_wp_current_template_id
   222  * @global string   $_wp_current_template_content
   212  * @global string   $_wp_current_template_content
   223  * @global WP_Embed $wp_embed
   213  * @global WP_Embed $wp_embed                     WordPress Embed object.
       
   214  * @global WP_Query $wp_query                     WordPress Query object.
   224  *
   215  *
   225  * @return string Block template markup.
   216  * @return string Block template markup.
   226  */
   217  */
   227 function get_the_block_template_html() {
   218 function get_the_block_template_html() {
   228 	global $_wp_current_template_content;
   219 	global $_wp_current_template_id, $_wp_current_template_content, $wp_embed, $wp_query;
   229 	global $wp_embed;
       
   230 
   220 
   231 	if ( ! $_wp_current_template_content ) {
   221 	if ( ! $_wp_current_template_content ) {
   232 		if ( is_user_logged_in() ) {
   222 		if ( is_user_logged_in() ) {
   233 			return '<h1>' . esc_html__( 'No matching template found' ) . '</h1>';
   223 			return '<h1>' . esc_html__( 'No matching template found' ) . '</h1>';
   234 		}
   224 		}
   235 		return;
   225 		return;
   236 	}
   226 	}
   237 
   227 
   238 	$content = $wp_embed->run_shortcode( $_wp_current_template_content );
   228 	$content = $wp_embed->run_shortcode( $_wp_current_template_content );
   239 	$content = $wp_embed->autoembed( $content );
   229 	$content = $wp_embed->autoembed( $content );
   240 	$content = do_blocks( $content );
   230 	$content = shortcode_unautop( $content );
       
   231 	$content = do_shortcode( $content );
       
   232 
       
   233 	/*
       
   234 	 * Most block themes omit the `core/query` and `core/post-template` blocks in their singular content templates.
       
   235 	 * While this technically still works since singular content templates are always for only one post, it results in
       
   236 	 * the main query loop never being entered which causes bugs in core and the plugin ecosystem.
       
   237 	 *
       
   238 	 * The workaround below ensures that the loop is started even for those singular templates. The while loop will by
       
   239 	 * definition only go through a single iteration, i.e. `do_blocks()` is only called once. Additional safeguard
       
   240 	 * checks are included to ensure the main query loop has not been tampered with and really only encompasses a
       
   241 	 * single post.
       
   242 	 *
       
   243 	 * Even if the block template contained a `core/query` and `core/post-template` block referencing the main query
       
   244 	 * loop, it would not cause errors since it would use a cloned instance and go through the same loop of a single
       
   245 	 * post, within the actual main query loop.
       
   246 	 *
       
   247 	 * This special logic should be skipped if the current template does not come from the current theme, in which case
       
   248 	 * it has been injected by a plugin by hijacking the block template loader mechanism. In that case, entirely custom
       
   249 	 * logic may be applied which is unpredictable and therefore safer to omit this special handling on.
       
   250 	 */
       
   251 	if (
       
   252 		$_wp_current_template_id &&
       
   253 		str_starts_with( $_wp_current_template_id, get_stylesheet() . '//' ) &&
       
   254 		is_singular() &&
       
   255 		1 === $wp_query->post_count &&
       
   256 		have_posts()
       
   257 	) {
       
   258 		while ( have_posts() ) {
       
   259 			the_post();
       
   260 			$content = do_blocks( $content );
       
   261 		}
       
   262 	} else {
       
   263 		$content = do_blocks( $content );
       
   264 	}
       
   265 
   241 	$content = wptexturize( $content );
   266 	$content = wptexturize( $content );
   242 	$content = convert_smilies( $content );
   267 	$content = convert_smilies( $content );
   243 	$content = shortcode_unautop( $content );
   268 	$content = wp_filter_content_tags( $content, 'template' );
   244 	$content = wp_filter_content_tags( $content );
       
   245 	$content = do_shortcode( $content );
       
   246 	$content = str_replace( ']]>', ']]&gt;', $content );
   269 	$content = str_replace( ']]>', ']]&gt;', $content );
   247 
   270 
   248 	// Wrap block template in .wp-site-blocks to allow for specific descendant styles
   271 	// Wrap block template in .wp-site-blocks to allow for specific descendant styles
   249 	// (e.g. `.wp-site-blocks > *`).
   272 	// (e.g. `.wp-site-blocks > *`).
   250 	return '<div class="wp-site-blocks">' . $content . '</div>';
   273 	return '<div class="wp-site-blocks">' . $content . '</div>';
   333 		current_user_can( 'edit_post', $post->ID )
   356 		current_user_can( 'edit_post', $post->ID )
   334 	) {
   357 	) {
   335 		$wp_query->set( 'post_status', 'auto-draft' );
   358 		$wp_query->set( 'post_status', 'auto-draft' );
   336 	}
   359 	}
   337 }
   360 }
   338 
       
   339 /**
       
   340  * Returns the correct template for the site's home page.
       
   341  *
       
   342  * @access private
       
   343  * @since 6.0.0
       
   344  *
       
   345  * @return array|null A template object, or null if none could be found.
       
   346  */
       
   347 function _resolve_home_block_template() {
       
   348 	$show_on_front = get_option( 'show_on_front' );
       
   349 	$front_page_id = get_option( 'page_on_front' );
       
   350 
       
   351 	if ( 'page' === $show_on_front && $front_page_id ) {
       
   352 		return array(
       
   353 			'postType' => 'page',
       
   354 			'postId'   => $front_page_id,
       
   355 		);
       
   356 	}
       
   357 
       
   358 	$hierarchy = array( 'front-page', 'home', 'index' );
       
   359 	$template  = resolve_block_template( 'home', $hierarchy, '' );
       
   360 
       
   361 	if ( ! $template ) {
       
   362 		return null;
       
   363 	}
       
   364 
       
   365 	return array(
       
   366 		'postType' => 'wp_template',
       
   367 		'postId'   => $template->id,
       
   368 	);
       
   369 }