PluginProbe
Gutenberg / 12.1.0
Gutenberg v12.1.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 / full-site-editing / template-loader.php

template-loader.php in Gutenberg 12.1.0, at lib/full-site-editing/template-loader.php

294 lines 9.3 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 ( ! gutenberg_supports_block_templates() ) {
13 return;
14 }
15
16 foreach ( gutenberg_get_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_override_query_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_override_query_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 hiearchy 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 );
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 remove_action( 'wp_head', '_block_template_render_title_tag', 1 );
101 add_action( 'wp_head', 'gutenberg_render_title_tag', 1 ); // ...and make it unconditional.
102
103 // This file will be included instead of the theme's template file.
104 return gutenberg_dir_path() . 'lib/template-canvas.php';
105 }
106
107 /**
108 * Return the correct 'wp_template' to render for the request template type.
109 *
110 * Accepts an optional $template_hierarchy argument as a hint.
111 *
112 * @param string $template_type The current template type.
113 * @param string[] $template_hierarchy (optional) The current template hierarchy, ordered by priority.
114 * @return null|WP_Block_Template A block template if found. Null if not.
115 */
116 function gutenberg_resolve_template( $template_type, $template_hierarchy ) {
117 if ( ! $template_type ) {
118 return null;
119 }
120
121 if ( empty( $template_hierarchy ) ) {
122 $template_hierarchy = array( $template_type );
123 }
124
125 $slugs = array_map(
126 'gutenberg_strip_php_suffix',
127 $template_hierarchy
128 );
129
130 // Find all potential templates 'wp_template' post matching the hierarchy.
131 $query = array(
132 'theme' => wp_get_theme()->get_stylesheet(),
133 'slug__in' => $slugs,
134 );
135 $templates = gutenberg_get_block_templates( $query );
136
137 // Order these templates per slug priority.
138 // Build map of template slugs to their priority in the current hierarchy.
139 $slug_priorities = array_flip( $slugs );
140
141 usort(
142 $templates,
143 function ( $template_a, $template_b ) use ( $slug_priorities ) {
144 return $slug_priorities[ $template_a->slug ] - $slug_priorities[ $template_b->slug ];
145 }
146 );
147
148 return count( $templates ) ? $templates[0] : null;
149 }
150
151 /**
152 * Displays title tag with content, regardless of whether theme has title-tag support.
153 *
154 * @see _wp_render_title_tag()
155 */
156 function gutenberg_render_title_tag() {
157 echo '<title>' . wp_get_document_title() . '</title>' . "\n";
158 }
159
160 /**
161 * Returns the markup for the current template.
162 */
163 function gutenberg_get_the_template_html() {
164 global $_wp_current_template_content;
165 global $wp_embed;
166
167 if ( ! $_wp_current_template_content ) {
168 if ( is_user_logged_in() ) {
169 return '<h1>' . esc_html__( 'No matching template found', 'gutenberg' ) . '</h1>';
170 }
171 return;
172 }
173
174 $content = $wp_embed->run_shortcode( $_wp_current_template_content );
175 $content = $wp_embed->autoembed( $content );
176 $content = do_blocks( $content );
177 $content = wptexturize( $content );
178 $content = wp_filter_content_tags( $content );
179 $content = str_replace( ']]>', ']]&gt;', $content );
180
181 // Wrap block template in .wp-site-blocks to allow for specific descendant styles
182 // (e.g. `.wp-site-blocks > *`).
183 return '<div class="wp-site-blocks">' . $content . '</div>';
184 }
185
186 /**
187 * Renders the markup for the current template.
188 */
189 function gutenberg_render_the_template() {
190 echo gutenberg_get_the_template_html(); // phpcs:ignore WordPress.Security.EscapeOutput
191 }
192
193 /**
194 * Renders a 'viewport' meta tag.
195 *
196 * This is hooked into {@see 'wp_head'} to decouple its output from the default template canvas.
197 */
198 function gutenberg_viewport_meta_tag() {
199 echo '<meta name="viewport" content="width=device-width, initial-scale=1" />' . "\n";
200 }
201
202 /**
203 * Strips .php suffix from template file names.
204 *
205 * @access private
206 *
207 * @param string $template_file Template file name.
208 * @return string Template file name without extension.
209 */
210 function gutenberg_strip_php_suffix( $template_file ) {
211 return preg_replace( '/\.(php|html)$/', '', $template_file );
212 }
213
214 /**
215 * Removes post details from block context when rendering a block template.
216 *
217 * @param array $context Default context.
218 *
219 * @return array Filtered context.
220 */
221 function gutenberg_template_render_without_post_block_context( $context ) {
222 /*
223 * When loading a template or template part directly and not through a page
224 * that resolves it, the top-level post ID and type context get set to that
225 * of the template part. Templates are just the structure of a site, and
226 * they should not be available as post context because blocks like Post
227 * Content would recurse infinitely.
228 */
229 if ( isset( $context['postType'] ) &&
230 ( 'wp_template' === $context['postType'] || 'wp_template_part' === $context['postType'] ) ) {
231 unset( $context['postId'] );
232 unset( $context['postType'] );
233 }
234
235 return $context;
236 }
237 add_filter( 'render_block_context', 'gutenberg_template_render_without_post_block_context' );
238
239
240 /**
241 * Sets the current WP_Query to return auto-draft posts.
242 *
243 * The auto-draft status indicates a new post, so allow the the WP_Query instance to
244 * return an auto-draft post for template resolution when editing a new post.
245 *
246 * @param WP_Query $wp_query Current WP_Query instance, passed by reference.
247 * @return void
248 */
249 function gutenberg_resolve_template_for_new_post( $wp_query ) {
250 remove_filter( 'pre_get_posts', 'gutenberg_resolve_template_for_new_post' );
251
252 // Pages.
253 $page_id = isset( $wp_query->query['page_id'] ) ? $wp_query->query['page_id'] : null;
254
255 // Posts, including custom post types.
256 $p = isset( $wp_query->query['p'] ) ? $wp_query->query['p'] : null;
257
258 $post_id = $page_id ? $page_id : $p;
259 $post = get_post( $post_id );
260
261 if (
262 $post &&
263 'auto-draft' === $post->post_status &&
264 current_user_can( 'edit_post', $post->ID )
265 ) {
266 $wp_query->set( 'post_status', 'auto-draft' );
267 }
268 }
269
270 /**
271 * Redirect the edit links for templates to the site editor.
272 *
273 * @param string $link The original link.
274 * @param int $post_id The custom post id.
275 */
276 function gutenberg_get_edit_template_link( $link, $post_id ) {
277 $post = get_post( $post_id );
278
279 if ( ! in_array( $post->post_type, array( 'wp_template', 'wp_template_part' ), true ) ) {
280 return $link;
281 }
282
283 $template = _build_block_template_result_from_post( $post );
284
285 if ( is_wp_error( $template ) ) {
286 return $link;
287 }
288
289 $edit_link = 'themes.php?page=gutenberg-edit-site&postId=%1$s&postType=%2$s';
290
291 return admin_url( sprintf( $edit_link, urlencode( $template->id ), $template->type ) );
292 }
293 add_filter( 'get_edit_post_link', 'gutenberg_get_edit_template_link', 10, 2 );
294