[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Block template loader functions. 4 * 5 * @package WordPress 6 */ 7 8 /** 9 * Adds necessary hooks to resolve '_wp-find-template' requests. 10 * 11 * @access private 12 * @since 5.9.0 13 */ 14 function _add_template_loader_filters() { 15 if ( isset( $_GET['_wp-find-template'] ) && current_theme_supports( 'block-templates' ) ) { 16 add_action( 'pre_get_posts', '_resolve_template_for_new_post' ); 17 } 18 } 19 20 /** 21 * Finds a block template with equal or higher specificity than a given PHP template file. 22 * 23 * Internally, this communicates the block content that needs to be used by the template canvas through a global variable. 24 * 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. 27 * 28 * @global string $_wp_current_template_content 29 * @global string $_wp_current_template_id 30 * 31 * @param string $template Path to the template. See locate_template(). 32 * @param string $type Sanitized filename without extension. 33 * @param string[] $templates A list of template candidates, in descending order of priority. 34 * @return string The path to the Site Editor template canvas file, or the fallback PHP template. 35 */ 36 function locate_block_template( $template, $type, array $templates ) { 37 global $_wp_current_template_content, $_wp_current_template_id; 38 39 if ( ! current_theme_supports( 'block-templates' ) ) { 40 return $template; 41 } 42 43 if ( $template ) { 44 /* 45 * locate_template() has found a PHP template at the path specified by $template. 46 * That means that we have a fallback candidate if we cannot find a block template 47 * with higher specificity. 48 * 49 * Thus, before looking for matching block themes, we shorten our list of candidate 50 * templates accordingly. 51 */ 52 53 // Locate the index of $template (without the theme directory path) in $templates. 54 $relative_template_path = str_replace( 55 array( get_stylesheet_directory() . '/', get_template_directory() . '/' ), 56 '', 57 $template 58 ); 59 $index = array_search( $relative_template_path, $templates, true ); 60 61 // If the template hierarchy algorithm has successfully located a PHP template file, 62 // we will only consider block templates with higher or equal specificity. 63 $templates = array_slice( $templates, 0, $index + 1 ); 64 } 65 66 $block_template = resolve_block_template( $type, $templates, $template ); 67 68 if ( $block_template ) { 69 $_wp_current_template_id = $block_template->id; 70 71 if ( empty( $block_template->content ) && is_user_logged_in() ) { 72 $_wp_current_template_content = 73 sprintf( 74 /* translators: %s: Template title */ 75 __( 'Empty template: %s' ), 76 $block_template->title 77 ); 78 } elseif ( ! empty( $block_template->content ) ) { 79 $_wp_current_template_content = $block_template->content; 80 } 81 if ( isset( $_GET['_wp-find-template'] ) ) { 82 wp_send_json_success( $block_template ); 83 } 84 } else { 85 if ( $template ) { 86 return $template; 87 } 88 89 if ( 'index' === $type ) { 90 if ( isset( $_GET['_wp-find-template'] ) ) { 91 wp_send_json_error( array( 'message' => __( 'No matching template found.' ) ) ); 92 } 93 } else { 94 return ''; // So that the template loader keeps looking for templates. 95 } 96 } 97 98 // Add hooks for template canvas. 99 // Add viewport meta tag. 100 add_action( 'wp_head', '_block_template_viewport_meta_tag', 0 ); 101 102 // Render title tag with content, regardless of whether theme has title-tag support. 103 remove_action( 'wp_head', '_wp_render_title_tag', 1 ); // Remove conditional title tag rendering... 104 add_action( 'wp_head', '_block_template_render_title_tag', 1 ); // ...and make it unconditional. 105 106 // This file will be included instead of the theme's template file. 107 return ABSPATH . WPINC . '/template-canvas.php'; 108 } 109 110 /** 111 * Returns the correct 'wp_template' to render for the request template type. 112 * 113 * @access private 114 * @since 5.8.0 115 * @since 5.9.0 Added the `$fallback_template` parameter. 116 * 117 * @param string $template_type The current template type. 118 * @param string[] $template_hierarchy The current template hierarchy, ordered by priority. 119 * @param string $fallback_template A PHP fallback template to use if no matching block template is found. 120 * @return WP_Block_Template|null template A template object, or null if none could be found. 121 */ 122 function resolve_block_template( $template_type, $template_hierarchy, $fallback_template ) { 123 if ( ! $template_type ) { 124 return null; 125 } 126 127 if ( empty( $template_hierarchy ) ) { 128 $template_hierarchy = array( $template_type ); 129 } 130 131 $slugs = array_map( 132 '_strip_template_file_suffix', 133 $template_hierarchy 134 ); 135 136 // Find all potential templates 'wp_template' post matching the hierarchy. 137 $query = array( 138 'slug__in' => $slugs, 139 ); 140 $templates = get_block_templates( $query ); 141 142 // Order these templates per slug priority. 143 // Build map of template slugs to their priority in the current hierarchy. 144 $slug_priorities = array_flip( $slugs ); 145 146 usort( 147 $templates, 148 static function ( $template_a, $template_b ) use ( $slug_priorities ) { 149 return $slug_priorities[ $template_a->slug ] - $slug_priorities[ $template_b->slug ]; 150 } 151 ); 152 153 $theme_base_path = get_stylesheet_directory() . DIRECTORY_SEPARATOR; 154 $parent_theme_base_path = get_template_directory() . DIRECTORY_SEPARATOR; 155 156 // Is the active theme a child theme, and is the PHP fallback template part of it? 157 if ( 158 str_starts_with( $fallback_template, $theme_base_path ) && 159 ! str_contains( $fallback_template, $parent_theme_base_path ) 160 ) { 161 $fallback_template_slug = substr( 162 $fallback_template, 163 // Starting position of slug. 164 strpos( $fallback_template, $theme_base_path ) + strlen( $theme_base_path ), 165 // Remove '.php' suffix. 166 -4 167 ); 168 169 // Is our candidate block template's slug identical to our PHP fallback template's? 170 if ( 171 count( $templates ) && 172 $fallback_template_slug === $templates[0]->slug && 173 'theme' === $templates[0]->source 174 ) { 175 // Unfortunately, we cannot trust $templates[0]->theme, since it will always 176 // be set to the active theme's slug by _build_block_template_result_from_file(), 177 // even if the block template is really coming from the active theme's parent. 178 // (The reason for this is that we want it to be associated with the active theme 179 // -- not its parent -- once we edit it and store it to the DB as a wp_template CPT.) 180 // Instead, we use _get_block_template_file() to locate the block template file. 181 $template_file = _get_block_template_file( 'wp_template', $fallback_template_slug ); 182 if ( $template_file && get_template() === $template_file['theme'] ) { 183 // The block template is part of the parent theme, so we 184 // have to give precedence to the child theme's PHP template. 185 array_shift( $templates ); 186 } 187 } 188 } 189 190 return count( $templates ) ? $templates[0] : null; 191 } 192 193 /** 194 * Displays title tag with content, regardless of whether theme has title-tag support. 195 * 196 * @access private 197 * @since 5.8.0 198 * 199 * @see _wp_render_title_tag() 200 */ 201 function _block_template_render_title_tag() { 202 echo '<title>' . wp_get_document_title() . '</title>' . "\n"; 203 } 204 205 /** 206 * Returns the markup for the current template. 207 * 208 * @access private 209 * @since 5.8.0 210 * 211 * @global string $_wp_current_template_id 212 * @global string $_wp_current_template_content 213 * @global WP_Embed $wp_embed WordPress Embed object. 214 * @global WP_Query $wp_query WordPress Query object. 215 * 216 * @return string Block template markup. 217 */ 218 function get_the_block_template_html() { 219 global $_wp_current_template_id, $_wp_current_template_content, $wp_embed, $wp_query; 220 221 if ( ! $_wp_current_template_content ) { 222 if ( is_user_logged_in() ) { 223 return '<h1>' . esc_html__( 'No matching template found' ) . '</h1>'; 224 } 225 return; 226 } 227 228 $content = $wp_embed->run_shortcode( $_wp_current_template_content ); 229 $content = $wp_embed->autoembed( $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 266 $content = wptexturize( $content ); 267 $content = convert_smilies( $content ); 268 $content = wp_filter_content_tags( $content, 'template' ); 269 $content = str_replace( ']]>', ']]>', $content ); 270 271 // Wrap block template in .wp-site-blocks to allow for specific descendant styles 272 // (e.g. `.wp-site-blocks > *`). 273 return '<div class="wp-site-blocks">' . $content . '</div>'; 274 } 275 276 /** 277 * Renders a 'viewport' meta tag. 278 * 279 * This is hooked into {@see 'wp_head'} to decouple its output from the default template canvas. 280 * 281 * @access private 282 * @since 5.8.0 283 */ 284 function _block_template_viewport_meta_tag() { 285 echo '<meta name="viewport" content="width=device-width, initial-scale=1" />' . "\n"; 286 } 287 288 /** 289 * Strips .php or .html suffix from template file names. 290 * 291 * @access private 292 * @since 5.8.0 293 * 294 * @param string $template_file Template file name. 295 * @return string Template file name without extension. 296 */ 297 function _strip_template_file_suffix( $template_file ) { 298 return preg_replace( '/\.(php|html)$/', '', $template_file ); 299 } 300 301 /** 302 * Removes post details from block context when rendering a block template. 303 * 304 * @access private 305 * @since 5.8.0 306 * 307 * @param array $context Default context. 308 * 309 * @return array Filtered context. 310 */ 311 function _block_template_render_without_post_block_context( $context ) { 312 /* 313 * When loading a template directly and not through a page that resolves it, 314 * the top-level post ID and type context get set to that of the template. 315 * Templates are just the structure of a site, and they should not be available 316 * as post context because blocks like Post Content would recurse infinitely. 317 */ 318 if ( isset( $context['postType'] ) && 'wp_template' === $context['postType'] ) { 319 unset( $context['postId'] ); 320 unset( $context['postType'] ); 321 } 322 323 return $context; 324 } 325 326 /** 327 * Sets the current WP_Query to return auto-draft posts. 328 * 329 * The auto-draft status indicates a new post, so allow the the WP_Query instance to 330 * return an auto-draft post for template resolution when editing a new post. 331 * 332 * @access private 333 * @since 5.9.0 334 * 335 * @param WP_Query $wp_query Current WP_Query instance, passed by reference. 336 */ 337 function _resolve_template_for_new_post( $wp_query ) { 338 if ( ! $wp_query->is_main_query() ) { 339 return; 340 } 341 342 remove_filter( 'pre_get_posts', '_resolve_template_for_new_post' ); 343 344 // Pages. 345 $page_id = isset( $wp_query->query['page_id'] ) ? $wp_query->query['page_id'] : null; 346 347 // Posts, including custom post types. 348 $p = isset( $wp_query->query['p'] ) ? $wp_query->query['p'] : null; 349 350 $post_id = $page_id ? $page_id : $p; 351 $post = get_post( $post_id ); 352 353 if ( 354 $post && 355 'auto-draft' === $post->post_status && 356 current_user_can( 'edit_post', $post->ID ) 357 ) { 358 $wp_query->set( 'post_status', 'auto-draft' ); 359 } 360 } 361 362 /** 363 * Register a block template. 364 * 365 * @since 6.7.0 366 * 367 * @param string $template_name Template name in the form of `plugin_uri//template_name`. 368 * @param array|string $args { 369 * @type string $title Optional. Title of the template as it will be shown in the Site Editor 370 * and other UI elements. 371 * @type string $description Optional. Description of the template as it will be shown in the Site 372 * Editor. 373 * @type string $content Optional. Default content of the template that will be used when the 374 * template is rendered or edited in the editor. 375 * @type string[] $post_types Optional. Array of post types to which the template should be available. 376 * @type string $plugin Optional. Slug of the plugin that registers the template. 377 * } 378 * @return WP_Block_Template|WP_Error The registered template object on success, WP_Error object on failure. 379 */ 380 function register_block_template( $template_name, $args = array() ) { 381 return WP_Block_Templates_Registry::get_instance()->register( $template_name, $args ); 382 } 383 384 /** 385 * Unregister a block template. 386 * 387 * @since 6.7.0 388 * 389 * @param string $template_name Template name in the form of `plugin_uri//template_name`. 390 * @return WP_Block_Template|WP_Error The unregistered template object on success, WP_Error object on failure or if the 391 * template doesn't exist. 392 */ 393 function unregister_block_template( $template_name ) { 394 return WP_Block_Templates_Registry::get_instance()->unregister( $template_name ); 395 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Thu Nov 21 08:20:01 2024 | Cross-referenced by PHPXref |