Skip to content

Commit

Permalink
Template Loader: Introduce get_template_hierarchy(), drop gutenberg_t…
Browse files Browse the repository at this point in the history
…emplate_include_filter() (#21980)

Previously, the template loader made gratuitous use of filters and globals in order to communicate data from one function to another. Specifically:

1. The `gutenberg_override_query_template` filter was added to `${type}_template`. It served two purposed:
   1. It recorded the current template hierarchy in the `_wp_current_template_hierarchy` global.
   1. It returned an empty string to indicate that no old-school theme template should be rendered.
2. The `gutenberg_template_include_filter` filter was added to `template_include`. That filter determined the relevant template post from `_wp_current_template_hierarchy`, and used it to render the template via the template canvas.
3. In order to populate the `$settings` variable (which is passed to the client), `edit-site-page.php` iterated over all template getters, calling each one of them, in order to trigger the `${type}_template` filters (and thus, `gutenberg_override_query_template`), to obtain the template hierarchies, and subsequently, the relevant template IDs. During each iteration, care had to be taken to evaluate and then reset the relevant globals.

Due to the implicit nature of globals and filters, this is harder to follow and reason about than a call stack of functions with explicit arguments and return values.

Hence, this PR makes the following changes:
- Introduce a new `get_template_hierachy()` function that determines the template hierarchy for a given template type. This encapsulates adding and removing a filter (which is still required) so it doesn't have to leak a global, as the previous approach did.
- Use that function to get the template hierarchy in `edit-site-page.php` (see item 3. above), getting rid of most globals involved in that file.
- Now that determining the template hierarchy doesn't need to be a concern of `gutenberg_override_query_template` anymore (see item 1.1 above), re-purpose it to actually render the template canvas, cutting out the now-superfluous `gutenberg_template_include_filter` (item 2.), which is thus removed. This still requires two globals (`_wp_current_template_id` and `_wp_current_template_content`), to communicate the template that needs to be rendered to the canvas. However, their use is much more limited.

This PR still does not touch the `_wp_current_template_part_ids` global, in order to ensure that the auto-draft saving behavior isn't changed. I'm planning to work on this in another follow-up.

This is a follow-up to #21959, and another preparatory step for https://github.com/WordPress/gutenberg/pull/21877/files#r416075231.

Co-authored-by: Enrique Piqueras <[email protected]>
  • Loading branch information
ockham and epiqueras committed May 5, 2020
1 parent cce63bb commit 464026a
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 98 deletions.
50 changes: 15 additions & 35 deletions lib/edit-site-page.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ function gutenberg_get_editor_styles() {
*/
function gutenberg_edit_site_init( $hook ) {
global
$_wp_current_template_hierarchy,
$_wp_current_template_part_ids,
$current_screen;

Expand Down Expand Up @@ -146,50 +145,31 @@ function gutenberg_edit_site_init( $hook ) {
$settings['fontSizes'] = $font_sizes;
}

// Get all templates by triggering `./template-loader.php`'s logic.
$template_getters = array(
'get_embed_template',
'get_404_template',
'get_search_template',
'get_home_template',
'get_privacy_policy_template',
'get_post_type_archive_template',
'get_taxonomy_template',
'get_attachment_template',
'get_single_template',
'get_page_template',
'get_singular_template',
'get_category_template',
'get_tag_template',
'get_author_template',
'get_date_template',
'get_archive_template',
);
$template_ids = array();
$template_part_ids = array();
foreach ( $template_getters as $template_getter ) {
call_user_func( $template_getter ); // This sets $_wp_current_template_hierarchy.
foreach ( get_template_types() as $template_type ) {
// Skip 'embed' for now because it is not a regular template type.
// Skip 'index' because it's a fallback that we handle differently.
if ( in_array( $template_type, array( 'embed', 'index' ), true ) ) {
continue;
}

$current_template_post = gutenberg_find_template_post( $_wp_current_template_hierarchy );
$template_hierarchy = array_merge( get_template_hierachy( $template_type ), get_template_hierachy( 'index' ) );
$current_template_post = gutenberg_find_template_post( $template_hierarchy );
if ( isset( $current_template_post ) ) {
$template_ids[ $current_template_post->post_name ] = $current_template_post->ID;
}
if ( isset( $_wp_current_template_part_ids ) ) {
$template_part_ids = $template_part_ids + $_wp_current_template_part_ids;
}

$_wp_current_template_hierarchy = null;
$_wp_current_template_part_ids = null;
}
get_front_page_template();
get_index_template();
$current_template_post = gutenberg_find_template_post( $_wp_current_template_hierarchy );
$template_ids[ $current_template_post->post_name ] = $current_template_post->ID;
if ( isset( $_wp_current_template_part_ids ) ) {
$template_part_ids = $template_part_ids + $_wp_current_template_part_ids;
$_wp_current_template_part_ids = null;
}
$settings['templateId'] = $current_template_post->ID;
$settings['homeTemplateId'] = $current_template_post->ID;

$current_template_id = $template_ids['front-page'];

$settings['templateId'] = $current_template_id;
$settings['homeTemplateId'] = $current_template_id;
$settings['templateType'] = 'wp_template';
$settings['templateIds'] = array_values( $template_ids );
$settings['templatePartIds'] = array_values( $template_part_ids );
Expand All @@ -207,7 +187,7 @@ function gutenberg_edit_site_init( $hook ) {
'/wp/v2/types?context=edit',
'/wp/v2/taxonomies?per_page=-1&context=edit',
'/wp/v2/themes?status=active',
sprintf( '/wp/v2/templates/%s?context=edit', $current_template_post->ID ),
sprintf( '/wp/v2/templates/%s?context=edit', $current_template_id ),
array( '/wp/v2/media', 'OPTIONS' ),
);
$preload_data = array_reduce(
Expand Down
134 changes: 71 additions & 63 deletions lib/template-loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,14 @@
*/

/**
* Adds necessary filters to use 'wp_template' posts instead of theme template files.
* Return a list of all overrideable default template types.
*
* @see get_query_template
*
* @return string[] List of all overrideable default template types.
*/
function gutenberg_add_template_loader_filters() {
if ( ! post_type_exists( 'wp_template' ) ) {
return;
}

/**
* Array of all overrideable default template types.
*
* @see get_query_template
*
* @var array
*/
$template_types = array(
function get_template_types() {
return array(
'index',
'404',
'archive',
Expand All @@ -29,47 +22,94 @@ function gutenberg_add_template_loader_filters() {
'tag',
'taxonomy',
'date',
// Skip 'embed' for now because it is not a regular template type.
'embed',
'home',
'frontpage',
'privacypolicy',
'front-page',
'privacy-policy',
'page',
'search',
'single',
'singular',
'attachment',
);
foreach ( $template_types as $template_type ) {
add_filter( $template_type . '_template', 'gutenberg_override_query_template', 20, 3 );
}

/**
* Adds necessary filters to use 'wp_template' posts instead of theme template files.
*/
function gutenberg_add_template_loader_filters() {
if ( ! post_type_exists( 'wp_template' ) ) {
return;
}

add_filter( 'template_include', 'gutenberg_template_include_filter', 20 );
foreach ( get_template_types() as $template_type ) {
if ( 'embed' === $template_type ) { // Skip 'embed' for now because it is not a regular template type.
continue;
}
add_filter( str_replace( '-', '', $template_type ) . '_template', 'gutenberg_override_query_template', 20, 3 );
}
}
add_action( 'wp_loaded', 'gutenberg_add_template_loader_filters' );

/**
* Filters into the "{$type}_template" hooks to record the current template hierarchy.
* Get the template hierarchy for a given template type.
*
* The method returns an empty result for every template so that a 'wp_template' post
* is used instead.
* Internally, this filters into the "{$type}_template_hierarchy" hook to record the type-specific template hierarchy.
*
* @param string $template_type A template type.
* @return string[] A list of template candidates, in descending order of priority.
*/
function get_template_hierachy( $template_type ) {
if ( ! in_array( $template_type, get_template_types(), true ) ) {
return array();
}

$get_template_function = 'get_' . str_replace( '-', '_', $template_type ) . '_template'; // front-page -> get_front_page_template.
$template_hierarchy_filter = str_replace( '-', '', $template_type ) . '_template_hierarchy'; // front-page -> frontpage_template_hierarchy.

$result = array();
$template_hierarchy_filter_function = function( $templates ) use ( &$result ) {
$result = $templates;
return $templates;
};

add_filter( $template_hierarchy_filter, $template_hierarchy_filter_function, 20, 1 );
call_user_func( $get_template_function ); // This invokes template_hierarchy_filter.
remove_filter( $template_hierarchy_filter, $template_hierarchy_filter_function, 20 );

return $result;
}

/**
* Filters into the "{$type}_template" hooks to redirect them to the Full Site Editing template canvas.
*
* @see gutenberg_template_include_filter
* Internally, this communicates the block content that needs to be used by the template canvas through a global variable.
*
* @param string $template Path to the template. See locate_template().
* @param string $type Sanitized filename without extension.
* @param array $templates A list of template candidates, in descending order of priority.
* @return string Empty string to ensure template file is considered not found.
* @return string The path to the Full Site Editing template canvas file.
*/
function gutenberg_override_query_template( $template, $type, array $templates = array() ) {
global $_wp_current_template_hierarchy;
global $_wp_current_template_id, $_wp_current_template_content;

if ( ! is_array( $_wp_current_template_hierarchy ) ) {
$_wp_current_template_hierarchy = $templates;
} else {
$_wp_current_template_hierarchy = array_merge( $_wp_current_template_hierarchy, $templates );
$current_template_post = gutenberg_find_template_post( $templates );

if ( $current_template_post ) {
$_wp_current_template_id = $current_template_post->ID;
$_wp_current_template_content = empty( $current_template_post->post_content ) ? __( 'Empty template.', 'gutenberg' ) : $current_template_post->post_content;
}

return '';
// Add hooks for template canvas.
// Add viewport meta tag.
add_action( 'wp_head', 'gutenberg_viewport_meta_tag', 0 );

// Render title tag with content, regardless of whether theme has title-tag support.
remove_action( 'wp_head', '_wp_render_title_tag', 1 ); // Remove conditional title tag rendering...
add_action( 'wp_head', 'gutenberg_render_title_tag', 1 ); // ...and make it unconditional.

// This file will be included instead of the theme's template file.
return gutenberg_dir_path() . 'lib/template-canvas.php';
}

/**
Expand Down Expand Up @@ -152,38 +192,6 @@ function create_auto_draft_for_template_part_block( $block ) {
}
}

/**
* Find the correct 'wp_template' post for the current hierarchy and return the path
* to the canvas file that will render it.
*
* @param string $template_file Original template file. Will be overridden.
* @return string Path to the canvas file to include.
*/
function gutenberg_template_include_filter( $template_file ) {
global $_wp_current_template_id, $_wp_current_template_content, $_wp_current_template_hierarchy;

// Bail if no relevant template hierarchy was determined, or if the template file
// was overridden another way.
if ( ! $_wp_current_template_hierarchy || $template_file ) {
return $template_file;
}

$current_template_post = gutenberg_find_template_post( $_wp_current_template_hierarchy );

if ( $current_template_post ) {
$_wp_current_template_id = $current_template_post->ID;
$_wp_current_template_content = empty( $current_template_post->post_content ) ? __( 'Empty template.', 'gutenberg' ) : $current_template_post->post_content;
}

// Add extra hooks for template canvas.
add_action( 'wp_head', 'gutenberg_viewport_meta_tag', 0 );
remove_action( 'wp_head', '_wp_render_title_tag', 1 );
add_action( 'wp_head', 'gutenberg_render_title_tag', 1 );

// This file will be included instead of the theme's template file.
return gutenberg_dir_path() . 'lib/template-canvas.php';
}

/**
* Return the correct 'wp_template' post for the current template hierarchy.
*
Expand Down

0 comments on commit 464026a

Please sign in to comment.