[ 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 * Renders a warning screen for empty block templates. 22 * 23 * @since 6.8.0 24 * 25 * @param WP_Block_Template $block_template The block template object. 26 * @return string The warning screen HTML. 27 */ 28 function wp_render_empty_block_template_warning( $block_template ) { 29 wp_enqueue_style( 'wp-empty-template-alert' ); 30 return sprintf( 31 /* translators: %1$s: Block template title. %2$s: Empty template warning message. %3$s: Edit template link. %4$s: Edit template button label. */ 32 '<div id="wp-empty-template-alert"> 33 <h2>%1$s</h2> 34 <p>%2$s</p> 35 <a href="%3$s" class="wp-element-button"> 36 %4$s 37 </a> 38 </div>', 39 esc_html( $block_template->title ), 40 __( 'This page is blank because the template is empty. You can reset or customize it in the Site Editor.' ), 41 get_edit_post_link( $block_template->wp_id, 'site-editor' ), 42 __( 'Edit template' ) 43 ); 44 } 45 46 /** 47 * Finds a block template with equal or higher specificity than a given PHP template file. 48 * 49 * Internally, this communicates the block content that needs to be used by the template canvas through a global variable. 50 * 51 * @since 5.8.0 52 * @since 6.3.0 Added `$_wp_current_template_id` global for editing of current template directly from the admin bar. 53 * 54 * @global string $_wp_current_template_content 55 * @global string $_wp_current_template_id 56 * 57 * @param string $template Path to the template. See locate_template(). 58 * @param string $type Sanitized filename without extension. 59 * @param string[] $templates A list of template candidates, in descending order of priority. 60 * @return string The path to the Site Editor template canvas file, or the fallback PHP template. 61 */ 62 function locate_block_template( $template, $type, array $templates ) { 63 global $_wp_current_template_content, $_wp_current_template_id; 64 65 if ( ! current_theme_supports( 'block-templates' ) ) { 66 return $template; 67 } 68 69 if ( $template ) { 70 /* 71 * locate_template() has found a PHP template at the path specified by $template. 72 * That means that we have a fallback candidate if we cannot find a block template 73 * with higher specificity. 74 * 75 * Thus, before looking for matching block themes, we shorten our list of candidate 76 * templates accordingly. 77 */ 78 79 // Locate the index of $template (without the theme directory path) in $templates. 80 $relative_template_path = str_replace( 81 array( get_stylesheet_directory() . '/', get_template_directory() . '/' ), 82 '', 83 $template 84 ); 85 $index = array_search( $relative_template_path, $templates, true ); 86 87 // If the template hierarchy algorithm has successfully located a PHP template file, 88 // we will only consider block templates with higher or equal specificity. 89 $templates = array_slice( $templates, 0, $index + 1 ); 90 } 91 92 $block_template = resolve_block_template( $type, $templates, $template ); 93 94 if ( $block_template ) { 95 $_wp_current_template_id = $block_template->id; 96 97 if ( empty( $block_template->content ) ) { 98 if ( is_user_logged_in() ) { 99 $_wp_current_template_content = wp_render_empty_block_template_warning( $block_template ); 100 } else { 101 if ( $block_template->has_theme_file ) { 102 // Show contents from theme template if user is not logged in. 103 $theme_template = _get_block_template_file( 'wp_template', $block_template->slug ); 104 $_wp_current_template_content = file_get_contents( $theme_template['path'] ); 105 } else { 106 $_wp_current_template_content = $block_template->content; 107 } 108 } 109 } elseif ( ! empty( $block_template->content ) ) { 110 $_wp_current_template_content = $block_template->content; 111 } 112 if ( isset( $_GET['_wp-find-template'] ) ) { 113 wp_send_json_success( $block_template ); 114 } 115 } else { 116 if ( $template ) { 117 return $template; 118 } 119 120 if ( 'index' === $type ) { 121 if ( isset( $_GET['_wp-find-template'] ) ) { 122 wp_send_json_error( array( 'message' => __( 'No matching template found.' ) ) ); 123 } 124 } else { 125 return ''; // So that the template loader keeps looking for templates. 126 } 127 } 128 129 // Add hooks for template canvas. 130 // Add viewport meta tag. 131 add_action( 'wp_head', '_block_template_viewport_meta_tag', 0 ); 132 133 // Render title tag with content, regardless of whether theme has title-tag support. 134 remove_action( 'wp_head', '_wp_render_title_tag', 1 ); // Remove conditional title tag rendering... 135 add_action( 'wp_head', '_block_template_render_title_tag', 1 ); // ...and make it unconditional. 136 137 // This file will be included instead of the theme's template file. 138 return ABSPATH . WPINC . '/template-canvas.php'; 139 } 140 141 /** 142 * Returns the correct 'wp_template' to render for the request template type. 143 * 144 * @access private 145 * @since 5.8.0 146 * @since 5.9.0 Added the `$fallback_template` parameter. 147 * 148 * @param string $template_type The current template type. 149 * @param string[] $template_hierarchy The current template hierarchy, ordered by priority. 150 * @param string $fallback_template A PHP fallback template to use if no matching block template is found. 151 * @return WP_Block_Template|null template A template object, or null if none could be found. 152 */ 153 function resolve_block_template( $template_type, $template_hierarchy, $fallback_template ) { 154 if ( ! $template_type ) { 155 return null; 156 } 157 158 if ( empty( $template_hierarchy ) ) { 159 $template_hierarchy = array( $template_type ); 160 } 161 162 $slugs = array_map( 163 '_strip_template_file_suffix', 164 $template_hierarchy 165 ); 166 167 // Find all potential templates 'wp_template' post matching the hierarchy. 168 $query = array( 169 'slug__in' => $slugs, 170 ); 171 $templates = get_block_templates( $query ); 172 173 // Order these templates per slug priority. 174 // Build map of template slugs to their priority in the current hierarchy. 175 $slug_priorities = array_flip( $slugs ); 176 177 usort( 178 $templates, 179 static function ( $template_a, $template_b ) use ( $slug_priorities ) { 180 return $slug_priorities[ $template_a->slug ] - $slug_priorities[ $template_b->slug ]; 181 } 182 ); 183 184 $theme_base_path = get_stylesheet_directory() . DIRECTORY_SEPARATOR; 185 $parent_theme_base_path = get_template_directory() . DIRECTORY_SEPARATOR; 186 187 // Is the active theme a child theme, and is the PHP fallback template part of it? 188 if ( 189 str_starts_with( $fallback_template, $theme_base_path ) && 190 ! str_contains( $fallback_template, $parent_theme_base_path ) 191 ) { 192 $fallback_template_slug = substr( 193 $fallback_template, 194 // Starting position of slug. 195 strpos( $fallback_template, $theme_base_path ) + strlen( $theme_base_path ), 196 // Remove '.php' suffix. 197 -4 198 ); 199 200 // Is our candidate block template's slug identical to our PHP fallback template's? 201 if ( 202 count( $templates ) && 203 $fallback_template_slug === $templates[0]->slug && 204 'theme' === $templates[0]->source 205 ) { 206 // Unfortunately, we cannot trust $templates[0]->theme, since it will always 207 // be set to the active theme's slug by _build_block_template_result_from_file(), 208 // even if the block template is really coming from the active theme's parent. 209 // (The reason for this is that we want it to be associated with the active theme 210 // -- not its parent -- once we edit it and store it to the DB as a wp_template CPT.) 211 // Instead, we use _get_block_template_file() to locate the block template file. 212 $template_file = _get_block_template_file( 'wp_template', $fallback_template_slug ); 213 if ( $template_file && get_template() === $template_file['theme'] ) { 214 // The block template is part of the parent theme, so we 215 // have to give precedence to the child theme's PHP template. 216 array_shift( $templates ); 217 } 218 } 219 } 220 221 return count( $templates ) ? $templates[0] : null; 222 } 223 224 /** 225 * Displays title tag with content, regardless of whether theme has title-tag support. 226 * 227 * @access private 228 * @since 5.8.0 229 * 230 * @see _wp_render_title_tag() 231 */ 232 function _block_template_render_title_tag() { 233 echo '<title>' . wp_get_document_title() . '</title>' . "\n"; 234 } 235 236 /** 237 * Returns the markup for the current template. 238 * 239 * @access private 240 * @since 5.8.0 241 * 242 * @global string $_wp_current_template_id 243 * @global string $_wp_current_template_content 244 * @global WP_Embed $wp_embed WordPress Embed object. 245 * @global WP_Query $wp_query WordPress Query object. 246 * 247 * @return string Block template markup. 248 */ 249 function get_the_block_template_html() { 250 global $_wp_current_template_id, $_wp_current_template_content, $wp_embed, $wp_query; 251 252 if ( ! $_wp_current_template_content ) { 253 if ( is_user_logged_in() ) { 254 return '<h1>' . esc_html__( 'No matching template found' ) . '</h1>'; 255 } 256 return; 257 } 258 259 $content = $wp_embed->run_shortcode( $_wp_current_template_content ); 260 $content = $wp_embed->autoembed( $content ); 261 $content = shortcode_unautop( $content ); 262 $content = do_shortcode( $content ); 263 264 /* 265 * Most block themes omit the `core/query` and `core/post-template` blocks in their singular content templates. 266 * While this technically still works since singular content templates are always for only one post, it results in 267 * the main query loop never being entered which causes bugs in core and the plugin ecosystem. 268 * 269 * The workaround below ensures that the loop is started even for those singular templates. The while loop will by 270 * definition only go through a single iteration, i.e. `do_blocks()` is only called once. Additional safeguard 271 * checks are included to ensure the main query loop has not been tampered with and really only encompasses a 272 * single post. 273 * 274 * Even if the block template contained a `core/query` and `core/post-template` block referencing the main query 275 * loop, it would not cause errors since it would use a cloned instance and go through the same loop of a single 276 * post, within the actual main query loop. 277 * 278 * This special logic should be skipped if the current template does not come from the current theme, in which case 279 * it has been injected by a plugin by hijacking the block template loader mechanism. In that case, entirely custom 280 * logic may be applied which is unpredictable and therefore safer to omit this special handling on. 281 */ 282 if ( 283 $_wp_current_template_id && 284 str_starts_with( $_wp_current_template_id, get_stylesheet() . '//' ) && 285 is_singular() && 286 1 === $wp_query->post_count && 287 have_posts() 288 ) { 289 while ( have_posts() ) { 290 the_post(); 291 $content = do_blocks( $content ); 292 } 293 } else { 294 $content = do_blocks( $content ); 295 } 296 297 $content = wptexturize( $content ); 298 $content = convert_smilies( $content ); 299 $content = wp_filter_content_tags( $content, 'template' ); 300 $content = str_replace( ']]>', ']]>', $content ); 301 302 // Wrap block template in .wp-site-blocks to allow for specific descendant styles 303 // (e.g. `.wp-site-blocks > *`). 304 return '<div class="wp-site-blocks">' . $content . '</div>'; 305 } 306 307 /** 308 * Renders a 'viewport' meta tag. 309 * 310 * This is hooked into {@see 'wp_head'} to decouple its output from the default template canvas. 311 * 312 * @access private 313 * @since 5.8.0 314 */ 315 function _block_template_viewport_meta_tag() { 316 echo '<meta name="viewport" content="width=device-width, initial-scale=1" />' . "\n"; 317 } 318 319 /** 320 * Strips .php or .html suffix from template file names. 321 * 322 * @access private 323 * @since 5.8.0 324 * 325 * @param string $template_file Template file name. 326 * @return string Template file name without extension. 327 */ 328 function _strip_template_file_suffix( $template_file ) { 329 return preg_replace( '/\.(php|html)$/', '', $template_file ); 330 } 331 332 /** 333 * Removes post details from block context when rendering a block template. 334 * 335 * @access private 336 * @since 5.8.0 337 * 338 * @param array $context Default context. 339 * 340 * @return array Filtered context. 341 */ 342 function _block_template_render_without_post_block_context( $context ) { 343 /* 344 * When loading a template directly and not through a page that resolves it, 345 * the top-level post ID and type context get set to that of the template. 346 * Templates are just the structure of a site, and they should not be available 347 * as post context because blocks like Post Content would recurse infinitely. 348 */ 349 if ( isset( $context['postType'] ) && 'wp_template' === $context['postType'] ) { 350 unset( $context['postId'] ); 351 unset( $context['postType'] ); 352 } 353 354 return $context; 355 } 356 357 /** 358 * Sets the current WP_Query to return auto-draft posts. 359 * 360 * The auto-draft status indicates a new post, so allow the the WP_Query instance to 361 * return an auto-draft post for template resolution when editing a new post. 362 * 363 * @access private 364 * @since 5.9.0 365 * 366 * @param WP_Query $wp_query Current WP_Query instance, passed by reference. 367 */ 368 function _resolve_template_for_new_post( $wp_query ) { 369 if ( ! $wp_query->is_main_query() ) { 370 return; 371 } 372 373 remove_filter( 'pre_get_posts', '_resolve_template_for_new_post' ); 374 375 // Pages. 376 $page_id = isset( $wp_query->query['page_id'] ) ? $wp_query->query['page_id'] : null; 377 378 // Posts, including custom post types. 379 $p = isset( $wp_query->query['p'] ) ? $wp_query->query['p'] : null; 380 381 $post_id = $page_id ? $page_id : $p; 382 $post = get_post( $post_id ); 383 384 if ( 385 $post && 386 'auto-draft' === $post->post_status && 387 current_user_can( 'edit_post', $post->ID ) 388 ) { 389 $wp_query->set( 'post_status', 'auto-draft' ); 390 } 391 } 392 393 /** 394 * Register a block template. 395 * 396 * @since 6.7.0 397 * 398 * @param string $template_name Template name in the form of `plugin_uri//template_name`. 399 * @param array|string $args { 400 * @type string $title Optional. Title of the template as it will be shown in the Site Editor 401 * and other UI elements. 402 * @type string $description Optional. Description of the template as it will be shown in the Site 403 * Editor. 404 * @type string $content Optional. Default content of the template that will be used when the 405 * template is rendered or edited in the editor. 406 * @type string[] $post_types Optional. Array of post types to which the template should be available. 407 * @type string $plugin Optional. Slug of the plugin that registers the template. 408 * } 409 * @return WP_Block_Template|WP_Error The registered template object on success, WP_Error object on failure. 410 */ 411 function register_block_template( $template_name, $args = array() ) { 412 return WP_Block_Templates_Registry::get_instance()->register( $template_name, $args ); 413 } 414 415 /** 416 * Unregister a block template. 417 * 418 * @since 6.7.0 419 * 420 * @param string $template_name Template name in the form of `plugin_uri//template_name`. 421 * @return WP_Block_Template|WP_Error The unregistered template object on success, WP_Error object on failure or if the 422 * template doesn't exist. 423 */ 424 function unregister_block_template( $template_name ) { 425 return WP_Block_Templates_Registry::get_instance()->unregister( $template_name ); 426 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Sun Dec 22 08:20:01 2024 | Cross-referenced by PHPXref |