| @@ -8,208 +8,154 @@ | ||
| 8 | 8 | /** |
| 9 | 9 | * Adds necessary filters to use 'wp_template' posts instead of theme template files. |
| 10 | 10 | */ |
| 11 | 11 | function gutenberg_add_template_loader_filters() { |
| 12 | - if ( ! post_type_exists( 'wp_template' ) ) { | |
| 12 | + if ( ! gutenberg_is_fse_theme() ) { | |
| 13 | 13 | return; |
| 14 | 14 | } |
| 15 | 15 | |
| 16 | - /** | |
| 17 | - * Array of all overrideable default template types. | |
| 18 | - * | |
| 19 | - * @see get_query_template | |
| 20 | - * | |
| 21 | - * @var array | |
| 22 | - */ | |
| 23 | - $template_types = array( | |
| 24 | - 'index', | |
| 25 | - '404', | |
| 26 | - 'archive', | |
| 27 | - 'author', | |
| 28 | - 'category', | |
| 29 | - 'tag', | |
| 30 | - 'taxonomy', | |
| 31 | - 'date', | |
| 32 | - // Skip 'embed' for now because it is not a regular template type. | |
| 33 | - 'home', | |
| 34 | - 'frontpage', | |
| 35 | - 'privacypolicy', | |
| 36 | - 'page', | |
| 37 | - 'search', | |
| 38 | - 'single', | |
| 39 | - 'singular', | |
| 40 | - 'attachment', | |
| 41 | - ); | |
| 42 | - foreach ( $template_types as $template_type ) { | |
| 43 | - add_filter( $template_type . '_template', 'gutenberg_override_query_template', 20, 3 ); | |
| 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 ); | |
| 44 | 21 | } |
| 45 | - | |
| 46 | - add_filter( 'template_include', 'gutenberg_find_template', 20 ); | |
| 47 | 22 | } |
| 48 | 23 | add_action( 'wp_loaded', 'gutenberg_add_template_loader_filters' ); |
| 49 | 24 | |
| 50 | 25 | /** |
| 51 | - * Filters into the "{$type}_template" hooks to record the current template hierarchy. | |
| 26 | + * Get the template hierarchy for a given template type. | |
| 52 | 27 | * |
| 53 | - * The method returns an empty result for every template so that a 'wp_template' post | |
| 54 | - * is used instead. | |
| 28 | + * Internally, this filters into the "{$type}_template_hierarchy" hook to record the type-specific template hierarchy. | |
| 55 | 29 | * |
| 56 | - * @see gutenberg_find_template | |
| 30 | + * @param string $template_type A template type. | |
| 31 | + * @return string[] A list of template candidates, in descending order of priority. | |
| 32 | + */ | |
| 33 | +function get_template_hierarchy( $template_type ) { | |
| 34 | + if ( ! in_array( $template_type, gutenberg_get_template_type_slugs(), true ) ) { | |
| 35 | + return array(); | |
| 36 | + } | |
| 37 | + | |
| 38 | + $get_template_function = 'get_' . str_replace( '-', '_', $template_type ) . '_template'; // front-page -> get_front_page_template. | |
| 39 | + $template_hierarchy_filter = str_replace( '-', '', $template_type ) . '_template_hierarchy'; // front-page -> frontpage_template_hierarchy. | |
| 40 | + | |
| 41 | + $result = array(); | |
| 42 | + $template_hierarchy_filter_function = function( $templates ) use ( &$result ) { | |
| 43 | + $result = $templates; | |
| 44 | + return $templates; | |
| 45 | + }; | |
| 46 | + | |
| 47 | + add_filter( $template_hierarchy_filter, $template_hierarchy_filter_function, 20, 1 ); | |
| 48 | + call_user_func( $get_template_function ); // This invokes template_hierarchy_filter. | |
| 49 | + remove_filter( $template_hierarchy_filter, $template_hierarchy_filter_function, 20 ); | |
| 50 | + | |
| 51 | + return $result; | |
| 52 | +} | |
| 53 | + | |
| 54 | +/** | |
| 55 | + * Filters into the "{$type}_template" hooks to redirect them to the Full Site Editing template canvas. | |
| 57 | 56 | * |
| 57 | + * Internally, this communicates the block content that needs to be used by the template canvas through a global variable. | |
| 58 | + * | |
| 58 | 59 | * @param string $template Path to the template. See locate_template(). |
| 59 | 60 | * @param string $type Sanitized filename without extension. |
| 60 | 61 | * @param array $templates A list of template candidates, in descending order of priority. |
| 61 | - * @return string Empty string to ensure template file is considered not found. | |
| 62 | + * @return string The path to the Full Site Editing template canvas file. | |
| 62 | 63 | */ |
| 63 | 64 | function gutenberg_override_query_template( $template, $type, array $templates = array() ) { |
| 64 | - global $_wp_current_template_hierarchy; | |
| 65 | + global $_wp_current_template_content; | |
| 66 | + $current_template = gutenberg_resolve_template( $type, $templates ); | |
| 65 | 67 | |
| 66 | - if ( ! is_array( $_wp_current_template_hierarchy ) ) { | |
| 67 | - $_wp_current_template_hierarchy = $templates; | |
| 68 | + if ( $current_template ) { | |
| 69 | + $_wp_current_template_content = empty( $current_template->post_content ) ? __( 'Empty template.', 'gutenberg' ) : $current_template->post_content; | |
| 70 | + | |
| 71 | + if ( isset( $_GET['_wp-find-template'] ) ) { | |
| 72 | + wp_send_json_success( $current_template ); | |
| 73 | + } | |
| 68 | 74 | } else { |
| 69 | - $_wp_current_template_hierarchy = array_merge( $_wp_current_template_hierarchy, $templates ); | |
| 75 | + if ( 'index' === $type ) { | |
| 76 | + if ( isset( $_GET['_wp-find-template'] ) ) { | |
| 77 | + wp_send_json_error( array( 'message' => __( 'No matching template found.', 'gutenberg' ) ) ); | |
| 78 | + } | |
| 79 | + } else { | |
| 80 | + return false; // So that the template loader keeps looking for templates. | |
| 81 | + } | |
| 70 | 82 | } |
| 71 | 83 | |
| 72 | - return ''; | |
| 84 | + // Add hooks for template canvas. | |
| 85 | + // Add viewport meta tag. | |
| 86 | + add_action( 'wp_head', 'gutenberg_viewport_meta_tag', 0 ); | |
| 87 | + | |
| 88 | + // Render title tag with content, regardless of whether theme has title-tag support. | |
| 89 | + remove_action( 'wp_head', '_wp_render_title_tag', 1 ); // Remove conditional title tag rendering... | |
| 90 | + add_action( 'wp_head', 'gutenberg_render_title_tag', 1 ); // ...and make it unconditional. | |
| 91 | + | |
| 92 | + // This file will be included instead of the theme's template file. | |
| 93 | + return gutenberg_dir_path() . 'lib/template-canvas.php'; | |
| 73 | 94 | } |
| 74 | 95 | |
| 75 | 96 | /** |
| 76 | - * Recursively traverses a block tree, creating auto drafts | |
| 77 | - * for any encountered template parts without a fixed post. | |
| 97 | + * Return the correct 'wp_template' to render fot the request template type. | |
| 78 | 98 | * |
| 79 | - * @access private | |
| 99 | + * Accepts an optional $template_hierarchy argument as a hint. | |
| 80 | 100 | * |
| 81 | - * @param array $block The root block to start traversing from. | |
| 101 | + * @param string $template_type The current template type. | |
| 102 | + * @param string[] $template_hierarchy (optional) The current template hierarchy, ordered by priority. | |
| 103 | + * @return null|array { | |
| 104 | + * @type WP_Post|null template_post A template post object, or null if none could be found. | |
| 105 | + * @type int[] A list of template parts IDs for the template. | |
| 106 | + * } | |
| 82 | 107 | */ |
| 83 | -function create_auto_draft_for_template_part_block( $block ) { | |
| 84 | - if ( 'core/template-part' === $block['blockName'] && ! isset( $block['attrs']['id'] ) ) { | |
| 85 | - $template_part_file_path = | |
| 86 | - get_stylesheet_directory() . '/block-template-parts/' . $block['attrs']['slug'] . '.html'; | |
| 87 | - if ( ! file_exists( $template_part_file_path ) ) { | |
| 88 | - return; | |
| 89 | - } | |
| 90 | - wp_insert_post( | |
| 91 | - array( | |
| 92 | - 'post_content' => file_get_contents( $template_part_file_path ), | |
| 93 | - 'post_title' => ucfirst( $block['attrs']['slug'] ), | |
| 94 | - 'post_status' => 'auto-draft', | |
| 95 | - 'post_type' => 'wp_template_part', | |
| 96 | - 'post_name' => $block['attrs']['slug'], | |
| 97 | - 'meta_input' => array( | |
| 98 | - 'theme' => $block['attrs']['theme'], | |
| 99 | - ), | |
| 100 | - ) | |
| 101 | - ); | |
| 108 | +function gutenberg_resolve_template( $template_type, $template_hierarchy = array() ) { | |
| 109 | + if ( ! $template_type ) { | |
| 110 | + return null; | |
| 102 | 111 | } |
| 103 | 112 | |
| 104 | - foreach ( $block['innerBlocks'] as $inner_block ) { | |
| 105 | - create_auto_draft_for_template_part_block( $inner_block ); | |
| 113 | + if ( empty( $template_hierarchy ) ) { | |
| 114 | + if ( 'index' === $template_type ) { | |
| 115 | + $template_hierarchy = get_template_hierarchy( 'index' ); | |
| 116 | + } else { | |
| 117 | + $template_hierarchy = array_merge( get_template_hierarchy( $template_type ), get_template_hierarchy( 'index' ) ); | |
| 118 | + } | |
| 106 | 119 | } |
| 107 | -} | |
| 108 | 120 | |
| 109 | -/** | |
| 110 | - * Find the correct 'wp_template' post for the current hierarchy and return the path | |
| 111 | - * to the canvas file that will render it. | |
| 112 | - * | |
| 113 | - * @param string $template_file Original template file. Will be overridden. | |
| 114 | - * @return string Path to the canvas file to include. | |
| 115 | - */ | |
| 116 | -function gutenberg_find_template( $template_file ) { | |
| 117 | - global $_wp_current_template_id, $_wp_current_template_content, $_wp_current_template_hierarchy; | |
| 118 | - | |
| 119 | - // Bail if no relevant template hierarchy was determined, or if the template file | |
| 120 | - // was overridden another way. | |
| 121 | - if ( ! $_wp_current_template_hierarchy || $template_file ) { | |
| 122 | - return $template_file; | |
| 123 | - } | |
| 124 | - | |
| 125 | 121 | $slugs = array_map( |
| 126 | 122 | 'gutenberg_strip_php_suffix', |
| 127 | - $_wp_current_template_hierarchy | |
| 123 | + $template_hierarchy | |
| 128 | 124 | ); |
| 129 | 125 | |
| 130 | - // Find most specific 'wp_template' post matching the hierarchy. | |
| 126 | + // Find all potential templates 'wp_template' post matching the hierarchy. | |
| 131 | 127 | $template_query = new WP_Query( |
| 132 | 128 | array( |
| 133 | 129 | 'post_type' => 'wp_template', |
| 134 | - 'post_status' => 'publish', | |
| 130 | + 'post_status' => array( 'publish', 'auto-draft' ), | |
| 135 | 131 | 'post_name__in' => $slugs, |
| 136 | 132 | 'orderby' => 'post_name__in', |
| 137 | - 'posts_per_page' => 1, | |
| 133 | + 'posts_per_page' => -1, | |
| 134 | + 'no_found_rows' => true, | |
| 135 | + 'tax_query' => array( | |
| 136 | + array( | |
| 137 | + 'taxonomy' => 'wp_theme', | |
| 138 | + 'field' => 'slug', | |
| 139 | + 'terms' => wp_get_theme()->get_stylesheet(), | |
| 140 | + ), | |
| 141 | + ), | |
| 138 | 142 | ) |
| 139 | 143 | ); |
| 144 | + $templates = $template_query->get_posts(); | |
| 140 | 145 | |
| 141 | - $template_posts = $template_query->get_posts(); | |
| 142 | - $current_template_post = array_shift( $template_posts ); | |
| 143 | - | |
| 146 | + // Order these templates per slug priority. | |
| 144 | 147 | // Build map of template slugs to their priority in the current hierarchy. |
| 145 | 148 | $slug_priorities = array_flip( $slugs ); |
| 146 | 149 | |
| 147 | - // See if there is a theme block template with higher priority than the resolved template post. | |
| 148 | - $higher_priority_block_template_path = null; | |
| 149 | - $higher_priority_block_template_priority = PHP_INT_MAX; | |
| 150 | - $block_template_files = glob( get_stylesheet_directory() . '/block-templates/*.html' ) ?: array(); | |
| 151 | - if ( is_child_theme() ) { | |
| 152 | - $block_template_files = array_merge( $block_template_files, glob( get_template_directory() . '/block-templates/*.html' ) ?: array() ); | |
| 153 | - } | |
| 154 | - if ( gutenberg_is_experiment_enabled( 'gutenberg-full-site-editing-demo' ) ) { | |
| 155 | - $block_template_files = array_merge( $block_template_files, glob( dirname( __FILE__ ) . '/demo-block-templates/*.html' ) ?: array() ); | |
| 156 | - } | |
| 157 | - foreach ( $block_template_files as $path ) { | |
| 158 | - if ( ! isset( $slug_priorities[ basename( $path, '.html' ) ] ) ) { | |
| 159 | - continue; | |
| 150 | + usort( | |
| 151 | + $templates, | |
| 152 | + function ( $template_a, $template_b ) use ( $slug_priorities ) { | |
| 153 | + return $slug_priorities[ $template_a->post_name ] - $slug_priorities[ $template_b->post_name ]; | |
| 160 | 154 | } |
| 161 | - $theme_block_template_priority = $slug_priorities[ basename( $path, '.html' ) ]; | |
| 162 | - if ( | |
| 163 | - $theme_block_template_priority < $higher_priority_block_template_priority && | |
| 164 | - ( empty( $current_template_post ) || $theme_block_template_priority < $slug_priorities[ $current_template_post->post_name ] ) | |
| 165 | - ) { | |
| 166 | - $higher_priority_block_template_path = $path; | |
| 167 | - $higher_priority_block_template_priority = $theme_block_template_priority; | |
| 168 | - } | |
| 169 | - } | |
| 155 | + ); | |
| 170 | 156 | |
| 171 | - // If there is, use it instead. | |
| 172 | - if ( isset( $higher_priority_block_template_path ) ) { | |
| 173 | - $post_name = basename( $higher_priority_block_template_path, '.html' ); | |
| 174 | - $current_template_post = array( | |
| 175 | - 'post_content' => file_get_contents( $higher_priority_block_template_path ), | |
| 176 | - 'post_title' => ucfirst( $post_name ), | |
| 177 | - 'post_status' => 'auto-draft', | |
| 178 | - 'post_type' => 'wp_template', | |
| 179 | - 'post_name' => $post_name, | |
| 180 | - ); | |
| 181 | - if ( is_admin() ) { | |
| 182 | - // Only create auto-draft of block template for editing | |
| 183 | - // in admin screens, similarly to how we do it for new | |
| 184 | - // posts in the editor. | |
| 185 | - $current_template_post = get_post( | |
| 186 | - wp_insert_post( $current_template_post ) | |
| 187 | - ); | |
| 188 | - } else { | |
| 189 | - $current_template_post = new WP_Post( | |
| 190 | - (object) $current_template_post | |
| 191 | - ); | |
| 192 | - } | |
| 193 | - } | |
| 194 | - | |
| 195 | - if ( $current_template_post ) { | |
| 196 | - if ( is_admin() ) { | |
| 197 | - foreach ( parse_blocks( $current_template_post->post_content ) as $block ) { | |
| 198 | - create_auto_draft_for_template_part_block( $block ); | |
| 199 | - } | |
| 200 | - } | |
| 201 | - $_wp_current_template_id = $current_template_post->ID; | |
| 202 | - $_wp_current_template_content = $current_template_post->post_content; | |
| 203 | - } | |
| 204 | - | |
| 205 | - // Add extra hooks for template canvas. | |
| 206 | - add_action( 'wp_head', 'gutenberg_viewport_meta_tag', 0 ); | |
| 207 | - remove_action( 'wp_head', '_wp_render_title_tag', 1 ); | |
| 208 | - add_action( 'wp_head', 'gutenberg_render_title_tag', 1 ); | |
| 209 | - | |
| 210 | - // This file will be included instead of the theme's template file. | |
| 211 | - return gutenberg_dir_path() . 'lib/template-canvas.php'; | |
| 157 | + return count( $templates ) ? $templates[0] : null; | |
| 212 | 158 | } |
| 213 | 159 | |
| 214 | 160 | /** |
| 215 | 161 | * Displays title tag with content, regardless of whether theme has title-tag support. |
| @@ -235,9 +181,13 @@ | ||
| 235 | 181 | $content = $wp_embed->run_shortcode( $_wp_current_template_content ); |
| 236 | 182 | $content = $wp_embed->autoembed( $content ); |
| 237 | 183 | $content = do_blocks( $content ); |
| 238 | 184 | $content = wptexturize( $content ); |
| 239 | - $content = wp_make_content_images_responsive( $content ); | |
| 185 | + if ( function_exists( 'wp_filter_content_tags' ) ) { | |
| 186 | + $content = wp_filter_content_tags( $content ); | |
| 187 | + } else { | |
| 188 | + $content = wp_make_content_images_responsive( $content ); | |
| 189 | + } | |
| 240 | 190 | $content = str_replace( ']]>', ']]>', $content ); |
| 241 | 191 | |
| 242 | 192 | // Wrap block template in .wp-site-blocks to allow for specific descendant styles |
| 243 | 193 | // (e.g. `.wp-site-blocks > *`). |
| @@ -263,28 +213,31 @@ | ||
| 263 | 213 | * @param string $template_file Template file name. |
| 264 | 214 | * @return string Template file name without extension. |
| 265 | 215 | */ |
| 266 | 216 | function gutenberg_strip_php_suffix( $template_file ) { |
| 267 | - return preg_replace( '/\.php$/', '', $template_file ); | |
| 217 | + return preg_replace( '/\.(php|html)$/', '', $template_file ); | |
| 268 | 218 | } |
| 269 | 219 | |
| 270 | 220 | /** |
| 271 | - * Extends default editor settings to enable template and template part editing. | |
| 221 | + * Removes post details from block context when rendering a block template. | |
| 272 | 222 | * |
| 273 | - * @param array $settings Default editor settings. | |
| 223 | + * @param array $context Default context. | |
| 274 | 224 | * |
| 275 | - * @return array Filtered editor settings. | |
| 225 | + * @return array Filtered context. | |
| 276 | 226 | */ |
| 277 | -function gutenberg_template_loader_filter_block_editor_settings( $settings ) { | |
| 278 | - if ( ! post_type_exists( 'wp_template' ) || ! post_type_exists( 'wp_template_part' ) ) { | |
| 279 | - return $settings; | |
| 227 | +function gutenberg_template_render_without_post_block_context( $context ) { | |
| 228 | + /* | |
| 229 | + * When loading a template or template part directly and not through a page | |
| 230 | + * that resolves it, the top-level post ID and type context get set to that | |
| 231 | + * of the template part. Templates are just the structure of a site, and | |
| 232 | + * they should not be available as post context because blocks like Post | |
| 233 | + * Content would recurse infinitely. | |
| 234 | + */ | |
| 235 | + if ( isset( $context['postType'] ) && | |
| 236 | + ( 'wp_template' === $context['postType'] || 'wp_template_part' === $context['postType'] ) ) { | |
| 237 | + unset( $context['postId'] ); | |
| 238 | + unset( $context['postType'] ); | |
| 280 | 239 | } |
| 281 | 240 | |
| 282 | - // Create template part auto-drafts for the edited post. | |
| 283 | - foreach ( parse_blocks( get_post()->post_content ) as $block ) { | |
| 284 | - create_auto_draft_for_template_part_block( $block ); | |
| 285 | - } | |
| 286 | - | |
| 287 | - // TODO: Set editing mode and current template ID for editing modes support. | |
| 288 | - return $settings; | |
| 241 | + return $context; | |
| 289 | 242 | } |
| 290 | -add_filter( 'block_editor_settings', 'gutenberg_template_loader_filter_block_editor_settings' ); | |
| 243 | +add_filter( 'render_block_context', 'gutenberg_template_render_without_post_block_context' ); | |