PluginProbe
Gutenberg / 12.6.0
Gutenberg v12.6.0
23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 7.4.0 All 402 releases
gutenberg / lib / compat / wordpress-5.9 / block-template.php

block-template.php in Gutenberg 12.6.0, at lib/compat/wordpress-5.9/block-template.php

308 lines 10.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Block template loader functions.
4 *
5 * @package gutenberg
6 */
7
8 /**
9 * Adds necessary filters to use 'wp_template' posts instead of theme template files.
10 */
11 function gutenberg_add_template_loader_filters() {
12 if ( ! current_theme_supports( 'block-templates' ) ) {
13 return;
14 }
15 $template_type_slugs = array_keys( get_default_block_template_types() );
16 foreach ( $template_type_slugs as $template_type ) {
17 if ( 'embed' === $template_type ) { // Skip 'embed' for now because it is not a regular template type.
18 continue;
19 }
20 add_filter( str_replace( '-', '', $template_type ) . '_template', 'gutenberg_locate_block_template', 20, 3 );
21 }
22
23 // Request to resolve a template.
24 if ( isset( $_GET['_wp-find-template'] ) ) {
25 add_filter( 'pre_get_posts', 'gutenberg_resolve_template_for_new_post' );
26 }
27 }
28
29 add_action( 'wp_loaded', 'gutenberg_add_template_loader_filters' );
30
31 /**
32 * Filters into the "{$type}_template" hooks to redirect them to the Full Site Editing template canvas.
33 *
34 * Internally, this communicates the block content that needs to be used by the template canvas through a global variable.
35 *
36 * @param string $template Path to the template. See locate_template().
37 * @param string $type Sanitized filename without extension.
38 * @param array $templates A list of template candidates, in descending order of priority.
39 * @return string The path to the Full Site Editing template canvas file.
40 */
41 function gutenberg_locate_block_template( $template, $type, array $templates ) {
42 global $_wp_current_template_content;
43
44 if ( $template ) {
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 // Thus, before looking for matching block themes, we shorten our list of candidate
49 // templates accordingly.
50
51 // Locate the index of $template (without the theme directory path) in $templates.
52 $relative_template_path = str_replace(
53 array( get_stylesheet_directory() . '/', get_template_directory() . '/' ),
54 '',
55 $template
56 );
57 $index = array_search( $relative_template_path, $templates, true );
58
59 // If the template hierarchy algorithm has successfully located a PHP template file,
60 // we will only consider block templates with higher or equal specificity.
61 $templates = array_slice( $templates, 0, $index + 1 );
62 }
63
64 $block_template = gutenberg_resolve_template( $type, $templates, $template );
65
66 if ( $block_template ) {
67 if ( empty( $block_template->content ) && is_user_logged_in() ) {
68 $_wp_current_template_content =
69 sprintf(
70 /* translators: %s: Template title */
71 __( 'Empty template: %s', 'gutenberg' ),
72 $block_template->title
73 );
74 } elseif ( ! empty( $block_template->content ) ) {
75 $_wp_current_template_content = $block_template->content;
76 }
77 if ( isset( $_GET['_wp-find-template'] ) ) {
78 wp_send_json_success( $block_template );
79 }
80 } else {
81 if ( $template ) {
82 return $template;
83 }
84
85 if ( 'index' === $type ) {
86 if ( isset( $_GET['_wp-find-template'] ) ) {
87 wp_send_json_error( array( 'message' => __( 'No matching template found.', 'gutenberg' ) ) );
88 }
89 } else {
90 return false; // So that the template loader keeps looking for templates.
91 }
92 }
93
94 // Add hooks for template canvas.
95 // Add viewport meta tag.
96 add_action( 'wp_head', 'gutenberg_viewport_meta_tag', 0 );
97
98 // Render title tag with content, regardless of whether theme has title-tag support.
99 remove_action( 'wp_head', '_wp_render_title_tag', 1 ); // Remove conditional title tag rendering...
100 // Override WP 5.8 title-tag support.
101 remove_action( 'wp_head', '_block_template_render_title_tag', 1 );
102 add_action( 'wp_head', 'gutenberg_render_title_tag', 1 ); // ...and make it unconditional.
103
104 // This file will be included instead of the theme's template file.
105 return gutenberg_dir_path() . 'lib/compat/wordpress-5.9/template-canvas.php';
106 }
107
108 /**
109 * Return the correct 'wp_template' to render for the request template type.
110 *
111 * Accepts an optional $template_hierarchy argument as a hint.
112 *
113 * @since 5.9.0 Added the `$fallback_template` parameter.
114 *
115 * @param string $template_type The current template type.
116 * @param string[] $template_hierarchy (optional) The current template hierarchy, ordered by priority.
117 * @param string $fallback_template A PHP fallback template to use if no matching block template is found.
118 * @return null|Gutenberg_Block_Template A block template if found. Null if not.
119 */
120 function gutenberg_resolve_template( $template_type, $template_hierarchy, $fallback_template ) {
121 if ( ! $template_type ) {
122 return null;
123 }
124
125 if ( empty( $template_hierarchy ) ) {
126 $template_hierarchy = array( $template_type );
127 }
128
129 $slugs = array_map(
130 'gutenberg_strip_template_file_suffix',
131 $template_hierarchy
132 );
133
134 // Find all potential templates 'wp_template' post matching the hierarchy.
135 $query = array(
136 'theme' => wp_get_theme()->get_stylesheet(),
137 'slug__in' => $slugs,
138 );
139 $templates = gutenberg_get_block_templates( $query );
140
141 // Order these templates per slug priority.
142 // Build map of template slugs to their priority in the current hierarchy.
143 $slug_priorities = array_flip( $slugs );
144
145 usort(
146 $templates,
147 function ( $template_a, $template_b ) use ( $slug_priorities ) {
148 return $slug_priorities[ $template_a->slug ] - $slug_priorities[ $template_b->slug ];
149 }
150 );
151
152 $theme_base_path = get_stylesheet_directory() . DIRECTORY_SEPARATOR;
153 $parent_theme_base_path = get_template_directory() . DIRECTORY_SEPARATOR;
154
155 // Is the current theme a child theme, and is the PHP fallback template part of it?
156 if (
157 strpos( $fallback_template, $theme_base_path ) === 0 &&
158 strpos( $fallback_template, $parent_theme_base_path ) === false
159 ) {
160 $fallback_template_slug = substr(
161 $fallback_template,
162 // Starting position of slug.
163 strpos( $fallback_template, $theme_base_path ) + strlen( $theme_base_path ),
164 // Remove '.php' suffix.
165 -4
166 );
167
168 // Is our candidate block template's slug identical to our PHP fallback template's?
169 if (
170 count( $templates ) &&
171 $fallback_template_slug === $templates[0]->slug &&
172 'theme' === $templates[0]->source
173 ) {
174 // Unfortunately, we cannot trust $templates[0]->theme, since it will always
175 // be set to the current theme's slug by _build_block_template_result_from_file(),
176 // even if the block template is really coming from the current theme's parent.
177 // (The reason for this is that we want it to be associated with the current theme
178 // -- not its parent -- once we edit it and store it to the DB as a wp_template CPT.)
179 // Instead, we use _get_block_template_file() to locate the block template file.
180 $template_file = _get_block_template_file( 'wp_template', $fallback_template_slug );
181 if ( $template_file && get_template() === $template_file['theme'] ) {
182 // The block template is part of the parent theme, so we
183 // have to give precedence to the child theme's PHP template.
184 array_shift( $templates );
185 }
186 }
187 }
188
189 return count( $templates ) ? $templates[0] : null;
190 }
191
192 /**
193 * Displays title tag with content, regardless of whether theme has title-tag support.
194 *
195 * @see _wp_render_title_tag()
196 */
197 function gutenberg_render_title_tag() {
198 echo '<title>' . wp_get_document_title() . '</title>' . "\n";
199 }
200
201 /**
202 * Returns the markup for the current template.
203 */
204 function gutenberg_get_the_template_html() {
205 global $_wp_current_template_content;
206 global $wp_embed;
207
208 if ( ! $_wp_current_template_content ) {
209 if ( is_user_logged_in() ) {
210 return '<h1>' . esc_html__( 'No matching template found', 'gutenberg' ) . '</h1>';
211 }
212 return;
213 }
214
215 $content = $wp_embed->run_shortcode( $_wp_current_template_content );
216 $content = $wp_embed->autoembed( $content );
217 $content = do_blocks( $content );
218 $content = wptexturize( $content );
219 $content = convert_smilies( $content );
220 $content = shortcode_unautop( $content );
221 $content = wp_filter_content_tags( $content );
222 $content = do_shortcode( $content );
223 $content = str_replace( ']]>', ']]&gt;', $content );
224
225 // Wrap block template in .wp-site-blocks to allow for specific descendant styles
226 // (e.g. `.wp-site-blocks > *`).
227 return '<div class="wp-site-blocks">' . $content . '</div>';
228 }
229
230 /**
231 * Renders a 'viewport' meta tag.
232 *
233 * This is hooked into {@see 'wp_head'} to decouple its output from the default template canvas.
234 */
235 function gutenberg_viewport_meta_tag() {
236 echo '<meta name="viewport" content="width=device-width, initial-scale=1" />' . "\n";
237 }
238
239 /**
240 * Strips .php suffix from template file names.
241 *
242 * @access private
243 *
244 * @param string $template_file Template file name.
245 * @return string Template file name without extension.
246 */
247 function gutenberg_strip_template_file_suffix( $template_file ) {
248 return preg_replace( '/\.(php|html)$/', '', $template_file );
249 }
250
251 /**
252 * Removes post details from block context when rendering a block template.
253 *
254 * @param array $context Default context.
255 *
256 * @return array Filtered context.
257 */
258 function gutenberg_template_render_without_post_block_context( $context ) {
259 /*
260 * When loading a template or template part directly and not through a page
261 * that resolves it, the top-level post ID and type context get set to that
262 * of the template part. Templates are just the structure of a site, and
263 * they should not be available as post context because blocks like Post
264 * Content would recurse infinitely.
265 */
266 if ( isset( $context['postType'] ) &&
267 ( 'wp_template' === $context['postType'] || 'wp_template_part' === $context['postType'] ) ) {
268 unset( $context['postId'] );
269 unset( $context['postType'] );
270 }
271
272 return $context;
273 }
274
275 // override WordPress 5.8 filters.
276 remove_filter( 'render_block_context', '_block_template_render_without_post_block_context' );
277 add_filter( 'render_block_context', 'gutenberg_template_render_without_post_block_context' );
278
279 /**
280 * Sets the current WP_Query to return auto-draft posts.
281 *
282 * The auto-draft status indicates a new post, so allow the the WP_Query instance to
283 * return an auto-draft post for template resolution when editing a new post.
284 *
285 * @param WP_Query $wp_query Current WP_Query instance, passed by reference.
286 * @return void
287 */
288 function gutenberg_resolve_template_for_new_post( $wp_query ) {
289 remove_filter( 'pre_get_posts', 'gutenberg_resolve_template_for_new_post' );
290
291 // Pages.
292 $page_id = isset( $wp_query->query['page_id'] ) ? $wp_query->query['page_id'] : null;
293
294 // Posts, including custom post types.
295 $p = isset( $wp_query->query['p'] ) ? $wp_query->query['p'] : null;
296
297 $post_id = $page_id ? $page_id : $p;
298 $post = get_post( $post_id );
299
300 if (
301 $post &&
302 'auto-draft' === $post->post_status &&
303 current_user_can( 'edit_post', $post->ID )
304 ) {
305 $wp_query->set( 'post_status', 'auto-draft' );
306 }
307 }
308