block-template.php (7053B)
1 <?php 2 /** 3 * Block template loader functions. 4 * 5 * @package WordPress 6 */ 7 8 /** 9 * Find a block template with equal or higher specificity than a given PHP template file. 10 * 11 * Internally, this communicates the block content that needs to be used by the template canvas through a global variable. 12 * 13 * @since 5.8.0 14 * 15 * @global string $_wp_current_template_content 16 * 17 * @param string $template Path to the template. See locate_template(). 18 * @param string $type Sanitized filename without extension. 19 * @param array $templates A list of template candidates, in descending order of priority. 20 * @return string The path to the Full Site Editing template canvas file, or the fallback PHP template. 21 */ 22 function locate_block_template( $template, $type, array $templates ) { 23 global $_wp_current_template_content; 24 25 if ( $template ) { 26 /* 27 * locate_template() has found a PHP template at the path specified by $template. 28 * That means that we have a fallback candidate if we cannot find a block template 29 * with higher specificity. 30 * 31 * Thus, before looking for matching block themes, we shorten our list of candidate 32 * templates accordingly. 33 */ 34 35 // Locate the index of $template (without the theme directory path) in $templates. 36 $relative_template_path = str_replace( 37 array( get_stylesheet_directory() . '/', get_template_directory() . '/' ), 38 '', 39 $template 40 ); 41 $index = array_search( $relative_template_path, $templates, true ); 42 43 // If the template hiearchy algorithm has successfully located a PHP template file, 44 // we will only consider block templates with higher or equal specificity. 45 $templates = array_slice( $templates, 0, $index + 1 ); 46 } 47 48 $block_template = resolve_block_template( $type, $templates ); 49 50 if ( $block_template ) { 51 if ( empty( $block_template->content ) && is_user_logged_in() ) { 52 $_wp_current_template_content = 53 sprintf( 54 /* translators: %s: Template title */ 55 __( 'Empty template: %s' ), 56 $block_template->title 57 ); 58 } elseif ( ! empty( $block_template->content ) ) { 59 $_wp_current_template_content = $block_template->content; 60 } 61 if ( isset( $_GET['_wp-find-template'] ) ) { 62 wp_send_json_success( $block_template ); 63 } 64 } else { 65 if ( $template ) { 66 return $template; 67 } 68 69 if ( 'index' === $type ) { 70 if ( isset( $_GET['_wp-find-template'] ) ) { 71 wp_send_json_error( array( 'message' => __( 'No matching template found.' ) ) ); 72 } 73 } else { 74 return ''; // So that the template loader keeps looking for templates. 75 } 76 } 77 78 // Add hooks for template canvas. 79 // Add viewport meta tag. 80 add_action( 'wp_head', '_block_template_viewport_meta_tag', 0 ); 81 82 // Render title tag with content, regardless of whether theme has title-tag support. 83 remove_action( 'wp_head', '_wp_render_title_tag', 1 ); // Remove conditional title tag rendering... 84 add_action( 'wp_head', '_block_template_render_title_tag', 1 ); // ...and make it unconditional. 85 86 // This file will be included instead of the theme's template file. 87 return ABSPATH . WPINC . '/template-canvas.php'; 88 } 89 90 /** 91 * Return the correct 'wp_template' to render for the request template type. 92 * 93 * @access private 94 * @since 5.8.0 95 * 96 * @param string $template_type The current template type. 97 * @param string[] $template_hierarchy The current template hierarchy, ordered by priority. 98 * @return WP_Block_Template|null template A template object, or null if none could be found. 99 */ 100 function resolve_block_template( $template_type, $template_hierarchy ) { 101 if ( ! $template_type ) { 102 return null; 103 } 104 105 if ( empty( $template_hierarchy ) ) { 106 $template_hierarchy = array( $template_type ); 107 } 108 109 $slugs = array_map( 110 '_strip_template_file_suffix', 111 $template_hierarchy 112 ); 113 114 // Find all potential templates 'wp_template' post matching the hierarchy. 115 $query = array( 116 'theme' => wp_get_theme()->get_stylesheet(), 117 'slug__in' => $slugs, 118 ); 119 $templates = get_block_templates( $query ); 120 121 // Order these templates per slug priority. 122 // Build map of template slugs to their priority in the current hierarchy. 123 $slug_priorities = array_flip( $slugs ); 124 125 usort( 126 $templates, 127 function ( $template_a, $template_b ) use ( $slug_priorities ) { 128 return $slug_priorities[ $template_a->slug ] - $slug_priorities[ $template_b->slug ]; 129 } 130 ); 131 132 return count( $templates ) ? $templates[0] : null; 133 } 134 135 /** 136 * Displays title tag with content, regardless of whether theme has title-tag support. 137 * 138 * @access private 139 * @since 5.8.0 140 * 141 * @see _wp_render_title_tag() 142 */ 143 function _block_template_render_title_tag() { 144 echo '<title>' . wp_get_document_title() . '</title>' . "\n"; 145 } 146 147 /** 148 * Returns the markup for the current template. 149 * 150 * @access private 151 * @since 5.8.0 152 * 153 * @global string $_wp_current_template_content 154 * @global WP_Embed $wp_embed 155 * 156 * @return string Block template markup. 157 */ 158 function get_the_block_template_html() { 159 global $_wp_current_template_content; 160 global $wp_embed; 161 162 if ( ! $_wp_current_template_content ) { 163 if ( is_user_logged_in() ) { 164 return '<h1>' . esc_html__( 'No matching template found' ) . '</h1>'; 165 } 166 return; 167 } 168 169 $content = $wp_embed->run_shortcode( $_wp_current_template_content ); 170 $content = $wp_embed->autoembed( $content ); 171 $content = do_blocks( $content ); 172 $content = wptexturize( $content ); 173 $content = wp_filter_content_tags( $content ); 174 $content = str_replace( ']]>', ']]>', $content ); 175 176 // Wrap block template in .wp-site-blocks to allow for specific descendant styles 177 // (e.g. `.wp-site-blocks > *`). 178 return '<div class="wp-site-blocks">' . $content . '</div>'; 179 } 180 181 /** 182 * Renders a 'viewport' meta tag. 183 * 184 * This is hooked into {@see 'wp_head'} to decouple its output from the default template canvas. 185 * 186 * @access private 187 * @since 5.8.0 188 */ 189 function _block_template_viewport_meta_tag() { 190 echo '<meta name="viewport" content="width=device-width, initial-scale=1" />' . "\n"; 191 } 192 193 /** 194 * Strips .php or .html suffix from template file names. 195 * 196 * @access private 197 * @since 5.8.0 198 * 199 * @param string $template_file Template file name. 200 * @return string Template file name without extension. 201 */ 202 function _strip_template_file_suffix( $template_file ) { 203 return preg_replace( '/\.(php|html)$/', '', $template_file ); 204 } 205 206 /** 207 * Removes post details from block context when rendering a block template. 208 * 209 * @access private 210 * @since 5.8.0 211 * 212 * @param array $context Default context. 213 * 214 * @return array Filtered context. 215 */ 216 function _block_template_render_without_post_block_context( $context ) { 217 /* 218 * When loading a template directly and not through a page that resolves it, 219 * the top-level post ID and type context get set to that of the template. 220 * Templates are just the structure of a site, and they should not be available 221 * as post context because blocks like Post Content would recurse infinitely. 222 */ 223 if ( isset( $context['postType'] ) && 'wp_template' === $context['postType'] ) { 224 unset( $context['postId'] ); 225 unset( $context['postType'] ); 226 } 227 228 return $context; 229 }