Skip to content

Commit

Permalink
Implement core template loader overrides to rely on wp_template posts…
Browse files Browse the repository at this point in the history
… instead.
  • Loading branch information
felixarntz committed Sep 27, 2019
1 parent 394da9e commit 4c93d15
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@

require dirname( __FILE__ ) . '/blocks.php';
require dirname( __FILE__ ) . '/templates.php';
require dirname( __FILE__ ) . '/template-loader.php';
require dirname( __FILE__ ) . '/client-assets.php';
require dirname( __FILE__ ) . '/demo.php';
require dirname( __FILE__ ) . '/widgets.php';
Expand Down
23 changes: 23 additions & 0 deletions lib/template-canvas.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php
/**
* Template canvas file to render the current 'wp_template'.
*
* @package gutenberg
*/

?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>" />
<?php wp_head(); ?>
</head>

<body <?php body_class(); ?>>
<?php wp_body_open(); ?>

<?php gutenberg_render_the_template(); ?>

<?php wp_footer(); ?>
</body>
</html>
162 changes: 162 additions & 0 deletions lib/template-loader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<?php
/**
* Block template loader functions.
*
* @package gutenberg
*/

/**
* 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;
}

/**
* Array of all overrideable default template types.
*
* @see get_query_template
*
* @var array
*/
$template_types = array(
'index',
'404',
'archive',
'author',
'category',
'tag',
'taxonomy',
'date',
// Skip 'embed' for now because it is not a regular template type.
'home',
'frontpage',
'privacypolicy',
'page',
'search',
'single',
'singular',
'attachment',
);
foreach ( $template_types as $template_type ) {
add_filter( $template_type . '_template', 'gutenberg_override_query_template', 20, 3 );
}

add_filter( 'template_include', 'gutenberg_find_template', 20 );
}
add_action( 'wp_loaded', 'gutenberg_add_template_loader_filters' );

/**
* Filters into the "{$type}_template" hooks to record the current template hierarchy.
*
* The method returns an empty result for every template so that a 'wp_template' post
* is used instead.
*
* @see gutenberg_find_template
*
* @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.
*/
function gutenberg_override_query_template( $template, $type, array $templates = array() ) {
global $_wp_current_template_hierarchy;

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

return '';
}

/**
* 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_find_template( $template_file ) {
global $_wp_current_template_post, $_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;
}

$slugs = array_map(
'gutenberg_strip_php_suffix',
$_wp_current_template_hierarchy
);

// Find most specific 'wp_template' post matching the hierarchy.
$template_query = new WP_Query(
array(
'post_type' => 'wp_template',
'post_status' => 'publish',
'post_name__in' => $slugs,
'orderby' => 'post_name__in',
'posts_per_page' => 1,
)
);

if ( $template_query->have_posts() ) {
$template_posts = $template_query->get_posts();
$_wp_current_template_post = $template_posts[0];
}

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

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

/**
* Renders the markup for the current template.
*/
function gutenberg_render_the_template() {
global $_wp_current_template_post;

if ( ! $_wp_current_template_post || 'wp_template' !== $_wp_current_template_post->post_type ) {
echo '<h1>' . esc_html__( 'No matching template found', 'gutenberg' ) . '</h1>';
return;
}

$content = $_wp_current_template_post->post_content;

$content = do_blocks( $content );
$content = wptexturize( $content );
$content = str_replace( ']]>', ']]&gt;', $content );

// Wrap block template in .wp-site-blocks to allow for specific descendant styles
// (e.g. `.wp-site-blocks > *`).
echo '<div class="wp-site-blocks">';
echo $content; // phpcs:ignore WordPress.Security.EscapeOutput
echo '</div>';
}

/**
* Renders a 'viewport' meta tag.
*
* This is hooked into {@see 'wp_head'} to decouple its output from the default template canvas.
*/
function gutenberg_viewport_meta_tag() {
echo '<meta name="viewport" content="width=device-width, initial-scale=1" />' . "\n";
}

/**
* Strips .php suffix from template file names.
*
* @access private
*
* @param string $template_file Template file name.
* @return string Template file name without extension.
*/
function gutenberg_strip_php_suffix( $template_file ) {
return preg_replace( '/\.php$/', '', $template_file );
}
29 changes: 29 additions & 0 deletions lib/templates.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,32 @@ function gutenberg_grant_template_caps( array $allcaps ) {
return $allcaps;
}
add_filter( 'user_has_cap', 'gutenberg_grant_template_caps' );

/**
* Filters capabilities to prevent deletion of the 'wp_template' post with slug 'index'.
*
* Similar to today's themes, this template should always exist.
*
* @param array $caps Array of the user's capabilities.
* @param string $cap Capability name.
* @param int $user_id The user ID.
* @param array $args Adds the context to the cap. Typically the object ID.
* @return array Filtered $caps.
*/
function gutenberg_prevent_index_template_deletion( $caps, $cap, $user_id, $args ) {
if ( 'delete_post' !== $cap || ! isset( $args[0] ) ) {
return $caps;
}

$post = get_post( $args[0] );
if ( ! $post || 'wp_template' !== $post->post_type ) {
return $caps;
}

if ( 'index' === $post->post_name ) {
$caps[] = 'do_not_allow';
}

return $caps;
}
add_filter( 'map_meta_cap', 'gutenberg_prevent_index_template_deletion', 10, 4 );

0 comments on commit 4c93d15

Please sign in to comment.