<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[51003] trunk: Block Editor: Introduce block templates for classic themes.</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/51003">51003</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/51003","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>youknowriad</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2021-05-25 14:19:14 +0000 (Tue, 25 May 2021)</dd>
</dl>
<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Block Editor: Introduce block templates for classic themes.
With this patch, users will be able to create custom block based templates
and assign them to specific pages/posts.
Themes can also opt-out of this feature
Props bernhard-reiter, carlomanf.
Fixes <a href="https://core.trac.wordpress.org/ticket/53176">#53176</a>.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadmineditformblocksphp">trunk/src/wp-admin/edit-form-blocks.php</a></li>
<li><a href="#trunksrcwpincludesclasswpthemephp">trunk/src/wp-includes/class-wp-theme.php</a></li>
<li><a href="#trunksrcwpincludesdefaultfiltersphp">trunk/src/wp-includes/default-filters.php</a></li>
<li><a href="#trunksrcwpincludespostphp">trunk/src/wp-includes/post.php</a></li>
<li><a href="#trunksrcwpincludesscriptloaderphp">trunk/src/wp-includes/script-loader.php</a></li>
<li><a href="#trunksrcwpincludestaxonomyphp">trunk/src/wp-includes/taxonomy.php</a></li>
<li><a href="#trunksrcwpincludestemplatephp">trunk/src/wp-includes/template.php</a></li>
<li><a href="#trunksrcwpsettingsphp">trunk/src/wp-settings.php</a></li>
<li><a href="#trunktestsphpunittestsrestapirestschemasetupphp">trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php</a></li>
<li><a href="#trunktestsphpunitteststhemewpThemeJsonphp">trunk/tests/phpunit/tests/theme/wpThemeJson.php</a></li>
<li><a href="#trunktestsphpunitteststhemewpThemeJsonResolverphp">trunk/tests/phpunit/tests/theme/wpThemeJsonResolver.php</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesblocktemplateutilsphp">trunk/src/wp-includes/block-template-utils.php</a></li>
<li><a href="#trunksrcwpincludesblocktemplatephp">trunk/src/wp-includes/block-template.php</a></li>
<li><a href="#trunksrcwpincludesclasswpblocktemplatephp">trunk/src/wp-includes/class-wp-block-template.php</a></li>
<li><a href="#trunksrcwpincludesrestapiendpointsclasswpresttemplatescontrollerphp">trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php</a></li>
<li><a href="#trunksrcwpincludestemplatecanvasphp">trunk/src/wp-includes/template-canvas.php</a></li>
<li><a href="#trunksrcwpincludesthemetemplatesphp">trunk/src/wp-includes/theme-templates.php</a></li>
<li><a href="#trunktestsphpunittestsblocktemplateutilsphp">trunk/tests/phpunit/tests/block-template-utils.php</a></li>
<li><a href="#trunktestsphpunittestsblocktemplatephp">trunk/tests/phpunit/tests/block-template.php</a></li>
<li><a href="#trunktestsphpunittestsrestapiresttemplatescontrollerphp">trunk/tests/phpunit/tests/rest-api/rest-templates-controller.php</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadmineditformblocksphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/edit-form-blocks.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/edit-form-blocks.php 2021-05-25 13:54:12 UTC (rev 51002)
+++ trunk/src/wp-admin/edit-form-blocks.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -220,6 +220,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 'supportsLayout' => WP_Theme_JSON_Resolver::theme_has_support(),
</span><span class="cx" style="display: block; padding: 0 10px"> '__experimentalBlockPatterns' => WP_Block_Patterns_Registry::get_instance()->get_all_registered(),
</span><span class="cx" style="display: block; padding: 0 10px"> '__experimentalBlockPatternCategories' => WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered(),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ 'supportsTemplateMode' => current_theme_supports( 'block-templates' ),
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> // Whether or not to load the 'postcustom' meta box is stored as a user meta
</span><span class="cx" style="display: block; padding: 0 10px"> // field so that we're not always loading its assets.
</span></span></pre></div>
<a id="trunksrcwpincludesblocktemplateutilsphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/src/wp-includes/block-template-utils.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/block-template-utils.php (rev 0)
+++ trunk/src/wp-includes/block-template-utils.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,144 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Utilities used to fetch and create templates.
+ *
+ * @package WordPress
+ * @since 5.8.0
+ */
+
+/**
+ * Build a unified template object based a post Object.
+ *
+ * @access private
+ * @since 5.8.0
+ *
+ * @param WP_Post $post Template post.
+ *
+ * @return WP_Block_Template|WP_Error Template.
+ */
+function _build_template_result_from_post( $post ) {
+ $terms = get_the_terms( $post, 'wp_theme' );
+
+ if ( is_wp_error( $terms ) ) {
+ return $terms;
+ }
+
+ if ( ! $terms ) {
+ return new WP_Error( 'template_missing_theme', __( 'No theme is defined for this template.' ) );
+ }
+
+ $theme = $terms[0]->name;
+
+ $template = new WP_Block_Template();
+ $template->wp_id = $post->ID;
+ $template->id = $theme . '//' . $post->post_name;
+ $template->theme = $theme;
+ $template->content = $post->post_content;
+ $template->slug = $post->post_name;
+ $template->source = 'custom';
+ $template->type = $post->post_type;
+ $template->description = $post->post_excerpt;
+ $template->title = $post->post_title;
+ $template->status = $post->post_status;
+ $template->has_theme_file = false;
+
+ return $template;
+}
+
+/**
+ * Retrieves a list of unified template objects based on a query.
+ *
+ * @since 5.8.0
+ *
+ * @param array $query {
+ * Optional. Arguments to retrieve templates.
+ *
+ * @type array $slug__in List of slugs to include.
+ * @type int $wp_id Post ID of customized template.
+ * }
+ * @param string $template_type wp_template.
+ *
+ * @return array Templates.
+ */
+function get_block_templates( $query = array(), $template_type = 'wp_template' ) {
+ $wp_query_args = array(
+ 'post_status' => array( 'auto-draft', 'draft', 'publish' ),
+ 'post_type' => $template_type,
+ 'posts_per_page' => -1,
+ 'no_found_rows' => true,
+ 'tax_query' => array(
+ array(
+ 'taxonomy' => 'wp_theme',
+ 'field' => 'name',
+ 'terms' => wp_get_theme()->get_stylesheet(),
+ ),
+ ),
+ );
+
+ if ( isset( $query['slug__in'] ) ) {
+ $wp_query_args['post_name__in'] = $query['slug__in'];
+ }
+
+ // This is only needed for the regular templates CPT listing and editor.
+ if ( isset( $query['wp_id'] ) ) {
+ $wp_query_args['p'] = $query['wp_id'];
+ } else {
+ $wp_query_args['post_status'] = 'publish';
+ }
+
+ $template_query = new WP_Query( $wp_query_args );
+ $query_result = array();
+ foreach ( $template_query->get_posts() as $post ) {
+ $template = _build_template_result_from_post( $post );
+
+ if ( ! is_wp_error( $template ) ) {
+ $query_result[] = $template;
+ }
+ }
+
+ return $query_result;
+}
+
+/**
+ * Retrieves a single unified template object using its id.
+ *
+ * @since 5.8.0
+ *
+ * @param string $id Template unique identifier (example: theme_slug//template_slug).
+ * @param string $template_type wp_template.
+ *
+ * @return WP_Block_Template|null Template.
+ */
+function get_block_template( $id, $template_type = 'wp_template' ) {
+ $parts = explode( '//', $id, 2 );
+ if ( count( $parts ) < 2 ) {
+ return null;
+ }
+ list( $theme, $slug ) = $parts;
+ $wp_query_args = array(
+ 'post_name__in' => array( $slug ),
+ 'post_type' => $template_type,
+ 'post_status' => array( 'auto-draft', 'draft', 'publish', 'trash' ),
+ 'posts_per_page' => 1,
+ 'no_found_rows' => true,
+ 'tax_query' => array(
+ array(
+ 'taxonomy' => 'wp_theme',
+ 'field' => 'name',
+ 'terms' => $theme,
+ ),
+ ),
+ );
+ $template_query = new WP_Query( $wp_query_args );
+ $posts = $template_query->get_posts();
+
+ if ( count( $posts ) > 0 ) {
+ $template = _build_template_result_from_post( $posts[0] );
+
+ if ( ! is_wp_error( $template ) ) {
+ return $template;
+ }
+ }
+
+ return null;
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/src/wp-includes/block-template-utils.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunksrcwpincludesblocktemplatephp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/src/wp-includes/block-template.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/block-template.php (rev 0)
+++ trunk/src/wp-includes/block-template.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,228 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Block template loader functions.
+ *
+ * @package WordPress
+ */
+
+/**
+ * Find a block template with equal or higher specificity than a given PHP template file.
+ *
+ * Internally, this communicates the block content that needs to be used by the template canvas through a global variable.
+ *
+ * @since 5.8.0
+ *
+ * @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 The path to the Full Site Editing template canvas file, or the fallback PHP template.
+ */
+function locate_block_template( $template, $type, array $templates ) {
+ global $_wp_current_template_content;
+
+ if ( $template ) {
+ // locate_template() has found a PHP template at the path specified by $template.
+ // That means that we have a fallback candidate if we cannot find a block template
+ // with higher specificity.
+ // Thus, before looking for matching block themes, we shorten our list of candidate
+ // templates accordingly.
+
+ // Locate the index of $template (without the theme directory path) in $templates.
+ $relative_template_path = str_replace(
+ array( get_stylesheet_directory() . '/', get_template_directory() . '/' ),
+ '',
+ $template
+ );
+ $index = array_search( $relative_template_path, $templates, true );
+
+ // If the template hiearchy algorithm has successfully located a PHP template file,
+ // we will only consider block templates with higher or equal specificity.
+ $templates = array_slice( $templates, 0, $index + 1 );
+ }
+
+ $block_template = resolve_block_template( $type, $templates );
+
+ if ( $block_template ) {
+ if ( empty( $block_template->content ) && is_user_logged_in() ) {
+ $_wp_current_template_content =
+ sprintf(
+ /* translators: %s: Template title */
+ __( 'Empty template: %s' ),
+ $block_template->title
+ );
+ } elseif ( ! empty( $block_template->content ) ) {
+ $_wp_current_template_content = $block_template->content;
+ }
+ if ( isset( $_GET['_wp-find-template'] ) ) {
+ wp_send_json_success( $block_template );
+ }
+ } else {
+ if ( $template ) {
+ return $template;
+ }
+
+ if ( 'index' === $type ) {
+ if ( isset( $_GET['_wp-find-template'] ) ) {
+ wp_send_json_error( array( 'message' => __( 'No matching template found.' ) ) );
+ }
+ } else {
+ return ''; // So that the template loader keeps looking for templates.
+ }
+ }
+
+ // Add hooks for template canvas.
+ // Add viewport meta tag.
+ add_action( 'wp_head', '_block_template_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', '_block_template_render_title_tag', 1 ); // ...and make it unconditional.
+
+ // This file will be included instead of the theme's template file.
+ return ABSPATH . WPINC . '/template-canvas.php';
+}
+
+/**
+ * Return the correct 'wp_template' to render for the request template type.
+ *
+ * @access private
+ * @since 5.8.0
+ *
+ * Accepts an optional $template_hierarchy argument as a hint.
+ *
+ * @param string $template_type The current template type.
+ * @param string[] $template_hierarchy (optional) The current template hierarchy, ordered by priority.
+ * @return WP_Block_Template|null template A template object, or null if none could be found.
+ */
+function resolve_block_template( $template_type, $template_hierarchy ) {
+ if ( ! $template_type ) {
+ return null;
+ }
+
+ if ( empty( $template_hierarchy ) ) {
+ $template_hierarchy = array( $template_type );
+ }
+
+ $slugs = array_map(
+ '_strip_template_file_suffix',
+ $template_hierarchy
+ );
+
+ // Find all potential templates 'wp_template' post matching the hierarchy.
+ $query = array(
+ 'theme' => wp_get_theme()->get_stylesheet(),
+ 'slug__in' => $slugs,
+ );
+ $templates = get_block_templates( $query );
+
+ // Order these templates per slug priority.
+ // Build map of template slugs to their priority in the current hierarchy.
+ $slug_priorities = array_flip( $slugs );
+
+ usort(
+ $templates,
+ function ( $template_a, $template_b ) use ( $slug_priorities ) {
+ return $slug_priorities[ $template_a->slug ] - $slug_priorities[ $template_b->slug ];
+ }
+ );
+
+ return count( $templates ) ? $templates[0] : null;
+}
+
+/**
+ * Displays title tag with content, regardless of whether theme has title-tag support.
+ *
+ * @access private
+ * @since 5.8.0
+ *
+ * @see _wp_render_title_tag()
+ */
+function _block_template_render_title_tag() {
+ echo '<title>' . wp_get_document_title() . '</title>' . "\n";
+}
+
+/**
+ * Returns the markup for the current template.
+ *
+ * @access private
+ * @since 5.8.0
+ *
+ * @return string block tempate markup.
+ */
+function get_the_block_template_html() {
+ global $_wp_current_template_content;
+ global $wp_embed;
+
+ if ( ! $_wp_current_template_content ) {
+ if ( is_user_logged_in() ) {
+ return '<h1>' . esc_html__( 'No matching template found' ) . '</h1>';
+ }
+ return;
+ }
+
+ $content = $wp_embed->run_shortcode( $_wp_current_template_content );
+ $content = $wp_embed->autoembed( $content );
+ $content = do_blocks( $content );
+ $content = wptexturize( $content );
+ if ( function_exists( 'wp_filter_content_tags' ) ) {
+ $content = wp_filter_content_tags( $content );
+ } else {
+ $content = wp_make_content_images_responsive( $content );
+ }
+ $content = str_replace( ']]>', ']]>', $content );
+
+ // Wrap block template in .wp-site-blocks to allow for specific descendant styles
+ // (e.g. `.wp-site-blocks > *`).
+ return '<div class="wp-site-blocks">' . $content . '</div>';
+}
+
+/**
+ * Renders a 'viewport' meta tag.
+ *
+ * @access private
+ * @since 5.8.0
+ *
+ * This is hooked into {@see 'wp_head'} to decouple its output from the default template canvas.
+ */
+function _block_template_viewport_meta_tag() {
+ echo '<meta name="viewport" content="width=device-width, initial-scale=1" />' . "\n";
+}
+
+/**
+ * Strips .php or .html suffix from template file names.
+ *
+ * @access private
+ * @since 5.8.0
+ *
+ * @param string $template_file Template file name.
+ * @return string Template file name without extension.
+ */
+function _strip_template_file_suffix( $template_file ) {
+ return preg_replace( '/\.(php|html)$/', '', $template_file );
+}
+
+/**
+ * Removes post details from block context when rendering a block template.
+ *
+ * @access private
+ * @since 5.8.0
+ *
+ * @param array $context Default context.
+ *
+ * @return array Filtered context.
+ */
+function _block_template_render_without_post_block_context( $context ) {
+ /*
+ * When loading a template directly and not through a page
+ * that resolves it, the top-level post ID and type context get set to that
+ * of the template. Templates are just the structure of a site, and
+ * they should not be available as post context because blocks like Post
+ * Content would recurse infinitely.
+ */
+ if ( isset( $context['postType'] ) && 'wp_template' === $context['postType'] ) {
+ unset( $context['postId'] );
+ unset( $context['postType'] );
+ }
+
+ return $context;
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/src/wp-includes/block-template.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunksrcwpincludesclasswpblocktemplatephp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/src/wp-includes/class-wp-block-template.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-block-template.php (rev 0)
+++ trunk/src/wp-includes/class-wp-block-template.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,103 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Blocks API: WP_Block_Template class
+ *
+ * @package WordPress
+ * @since 5.8.0
+ */
+
+/**
+ * Class representing a block template.
+ *
+ * @since 5.8.0
+ */
+class WP_Block_Template {
+
+ /**
+ * Type: wp_template.
+ *
+ * @since 5.8.0
+ * @var string
+ */
+ public $type;
+
+ /**
+ * Theme.
+ *
+ * @since 5.8.0
+ * @var string
+ */
+ public $theme;
+
+ /**
+ * Template slug.
+ *
+ * @since 5.8.0
+ * @var string
+ */
+ public $slug;
+
+ /**
+ * Id.
+ *
+ * @since 5.8.0
+ * @var string
+ */
+ public $id;
+
+ /**
+ * Title.
+ *
+ * @since 5.8.0
+ * @var string
+ */
+ public $title = '';
+
+ /**
+ * Content.
+ *
+ * @since 5.8.0
+ * @var string
+ */
+ public $content = '';
+
+ /**
+ * Description.
+ *
+ * @since 5.8.0
+ * @var string
+ */
+ public $description = '';
+
+ /**
+ * Source of the content. `theme` and `custom` is used for now.
+ *
+ * @since 5.8.0
+ * @var string
+ */
+ public $source = 'theme';
+
+ /**
+ * Post Id.
+ *
+ * @since 5.8.0
+ * @var integer|null
+ */
+ public $wp_id;
+
+ /**
+ * Template Status.
+ *
+ * @since 5.8.0
+ * @var string
+ */
+ public $status;
+
+ /**
+ * Whether a template is, or is based upon, an existing template file.
+ *
+ * @since 5.8.0
+ * @var boolean
+ */
+ public $has_theme_file;
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/src/wp-includes/class-wp-block-template.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunksrcwpincludesclasswpthemephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-theme.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-theme.php 2021-05-25 13:54:12 UTC (rev 51002)
+++ trunk/src/wp-includes/class-wp-theme.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1182,6 +1182,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> * Returns the theme's post templates.
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @since 4.7.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @since 5.8.0 Include block templates.
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @return string[] Array of page templates, keyed by filename and post type,
</span><span class="cx" style="display: block; padding: 0 10px"> * with the value of the translated header name.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1219,6 +1220,15 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( current_theme_supports( 'block-templates' ) ) {
+ $block_templates = get_block_templates( array(), 'wp_template' );
+ foreach ( get_post_types( array( 'public' => true ) ) as $type ) {
+ foreach ( $block_templates as $block_template ) {
+ $post_templates[ $type ][ $block_template->slug ] = $block_template->title;
+ }
+ }
+ }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> $this->cache_add( 'post_templates', $post_templates );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span></span></pre></div>
<a id="trunksrcwpincludesdefaultfiltersphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/default-filters.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/default-filters.php 2021-05-25 13:54:12 UTC (rev 51002)
+++ trunk/src/wp-includes/default-filters.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -555,6 +555,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'wp_head', 'wp_maybe_inline_styles', 1 ); // Run for styles enqueued in <head>.
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'wp_footer', 'wp_maybe_inline_styles', 1 ); // Run for late-loaded styles in the footer.
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+add_action( 'admin_footer-post.php', 'wp_add_iframed_editor_assets_html' );
+add_action( 'admin_footer-post-new.php', 'wp_add_iframed_editor_assets_html' );
+
</ins><span class="cx" style="display: block; padding: 0 10px"> // Taxonomy.
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'init', 'create_initial_taxonomies', 0 ); // Highest priority.
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'change_locale', 'create_initial_taxonomies' );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -633,4 +636,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> add_filter( 'user_has_cap', 'wp_maybe_grant_resume_extensions_caps', 1 );
</span><span class="cx" style="display: block; padding: 0 10px"> add_filter( 'user_has_cap', 'wp_maybe_grant_site_health_caps', 1, 4 );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// Block Templates CPT and Rendering
+add_filter( 'render_block_context', '_block_template_render_without_post_block_context' );
+add_filter( 'pre_wp_unique_post_slug', 'wp_filter_wp_template_unique_post_slug', 10, 5 );
+add_action( 'wp_footer', 'the_block_template_skip_link' );
+
</ins><span class="cx" style="display: block; padding: 0 10px"> unset( $filter, $action );
</span></span></pre></div>
<a id="trunksrcwpincludespostphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/post.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/post.php 2021-05-25 13:54:12 UTC (rev 51002)
+++ trunk/src/wp-includes/post.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -310,6 +310,66 @@
</span><span class="cx" style="display: block; padding: 0 10px"> )
</span><span class="cx" style="display: block; padding: 0 10px"> );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ register_post_type(
+ 'wp_template',
+ array(
+ 'labels' => array(
+ 'name' => __( 'Templates' ),
+ 'singular_name' => __( 'Template' ),
+ 'menu_name' => _x( 'Templates', 'Admin Menu text' ),
+ 'add_new' => _x( 'Add New', 'Template' ),
+ 'add_new_item' => __( 'Add New Template' ),
+ 'new_item' => __( 'New Template' ),
+ 'edit_item' => __( 'Edit Template' ),
+ 'view_item' => __( 'View Template' ),
+ 'all_items' => __( 'All Templates' ),
+ 'search_items' => __( 'Search Templates' ),
+ 'parent_item_colon' => __( 'Parent Template:' ),
+ 'not_found' => __( 'No templates found.' ),
+ 'not_found_in_trash' => __( 'No templates found in Trash.' ),
+ 'archives' => __( 'Template archives' ),
+ 'insert_into_item' => __( 'Insert into template' ),
+ 'uploaded_to_this_item' => __( 'Uploaded to this template' ),
+ 'filter_items_list' => __( 'Filter templates list' ),
+ 'items_list_navigation' => __( 'Templates list navigation' ),
+ 'items_list' => __( 'Templates list' ),
+ ),
+ 'description' => __( 'Templates to include in your theme.' ),
+ 'public' => false,
+ 'has_archive' => false,
+ 'show_ui' => false,
+ 'show_in_menu' => false,
+ 'show_in_admin_bar' => false,
+ 'show_in_rest' => true,
+ 'rewrite' => false,
+ 'rest_base' => 'templates',
+ 'rest_controller_class' => 'WP_REST_Templates_Controller',
+ 'capability_type' => array( 'template', 'templates' ),
+ 'capabilities' => array(
+ 'create_posts' => 'edit_theme_options',
+ 'delete_posts' => 'edit_theme_options',
+ 'delete_others_posts' => 'edit_theme_options',
+ 'delete_private_posts' => 'edit_theme_options',
+ 'delete_published_posts' => 'edit_theme_options',
+ 'edit_posts' => 'edit_theme_options',
+ 'edit_others_posts' => 'edit_theme_options',
+ 'edit_private_posts' => 'edit_theme_options',
+ 'edit_published_posts' => 'edit_theme_options',
+ 'publish_posts' => 'edit_theme_options',
+ 'read' => 'edit_theme_options',
+ 'read_private_posts' => 'edit_theme_options',
+ ),
+ 'map_meta_cap' => true,
+ 'supports' => array(
+ 'title',
+ 'slug',
+ 'excerpt',
+ 'editor',
+ 'revisions',
+ ),
+ )
+ );
+
</ins><span class="cx" style="display: block; padding: 0 10px"> register_post_status(
</span><span class="cx" style="display: block; padding: 0 10px"> 'publish',
</span><span class="cx" style="display: block; padding: 0 10px"> array(
</span></span></pre></div>
<a id="trunksrcwpincludesrestapiendpointsclasswpresttemplatescontrollerphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php (rev 0)
+++ trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,603 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * REST API: WP_REST_Templates_Controller class
+ *
+ * @package WordPress
+ * @subpackage REST_API
+ * @since 5.8.0
+ */
+
+/**
+ * Base Templates REST API Controller.
+ *
+ * @since 5.8.0
+ *
+ * @see WP_REST_Controller
+ */
+class WP_REST_Templates_Controller extends WP_REST_Controller {
+
+ /**
+ * Post type.
+ *
+ * @since 5.8.0
+ * @var string
+ */
+ protected $post_type;
+
+ /**
+ * Constructor.
+ *
+ * @since 5.8.0
+ *
+ * @param string $post_type Post type.
+ */
+ public function __construct( $post_type ) {
+ $this->post_type = $post_type;
+ $this->namespace = 'wp/v2';
+ $obj = get_post_type_object( $post_type );
+ $this->rest_base = ! empty( $obj->rest_base ) ? $obj->rest_base : $obj->name;
+ }
+
+ /**
+ * Registers the controllers routes.
+ *
+ * @since 5.8.0
+ *
+ * @return void
+ */
+ public function register_routes() {
+ // Lists all templates.
+ register_rest_route(
+ $this->namespace,
+ '/' . $this->rest_base,
+ array(
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => array( $this, 'get_items' ),
+ 'permission_callback' => array( $this, 'get_items_permissions_check' ),
+ 'args' => $this->get_collection_params(),
+ ),
+ array(
+ 'methods' => WP_REST_Server::CREATABLE,
+ 'callback' => array( $this, 'create_item' ),
+ 'permission_callback' => array( $this, 'create_item_permissions_check' ),
+ 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
+ ),
+ 'schema' => array( $this, 'get_public_item_schema' ),
+ )
+ );
+
+ // Lists/updates a single template based on the given id.
+ register_rest_route(
+ $this->namespace,
+ '/' . $this->rest_base . '/(?P<id>[\/\w-]+)',
+ array(
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => array( $this, 'get_item' ),
+ 'permission_callback' => array( $this, 'get_item_permissions_check' ),
+ 'args' => array(
+ 'id' => array(
+ 'description' => __( 'The id of a template' ),
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => array( $this, 'update_item' ),
+ 'permission_callback' => array( $this, 'update_item_permissions_check' ),
+ 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
+ ),
+ array(
+ 'methods' => WP_REST_Server::DELETABLE,
+ 'callback' => array( $this, 'delete_item' ),
+ 'permission_callback' => array( $this, 'delete_item_permissions_check' ),
+ 'args' => array(
+ 'force' => array(
+ 'type' => 'boolean',
+ 'default' => false,
+ 'description' => __( 'Whether to bypass Trash and force deletion.' ),
+ ),
+ ),
+ ),
+ 'schema' => array( $this, 'get_public_item_schema' ),
+ )
+ );
+ }
+
+ /**
+ * Checks if the user has permissions to make the request.
+ *
+ * @since 5.8.0
+ *
+ * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
+ */
+ protected function permissions_check() {
+ // Verify if the current user has edit_theme_options capability.
+ // This capability is required to edit/view/delete templates.
+ if ( ! current_user_can( 'edit_theme_options' ) ) {
+ return new WP_Error(
+ 'rest_cannot_manage_templates',
+ __( 'Sorry, you are not allowed to access the templates on this site.' ),
+ array(
+ 'status' => rest_authorization_required_code(),
+ )
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks if a given request has access to read templates.
+ *
+ * @since 5.8.0
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
+ */
+ public function get_items_permissions_check( $request ) {
+ return $this->permissions_check( $request );
+ }
+
+ /**
+ * Returns a list of templates.
+ *
+ * @since 5.8.0
+ *
+ * @param WP_REST_Request $request The request instance.
+ *
+ * @return WP_REST_Response
+ */
+ public function get_items( $request ) {
+ $query = array();
+ if ( isset( $request['wp_id'] ) ) {
+ $query['wp_id'] = $request['wp_id'];
+ }
+ if ( isset( $request['area'] ) ) {
+ $query['area'] = $request['area'];
+ }
+ $templates = array();
+ foreach ( get_block_templates( $query, $this->post_type ) as $template ) {
+ $data = $this->prepare_item_for_response( $template, $request );
+ $templates[] = $this->prepare_response_for_collection( $data );
+ }
+
+ return rest_ensure_response( $templates );
+ }
+
+ /**
+ * Checks if a given request has access to read a single template.
+ *
+ * @since 5.8.0
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise.
+ */
+ public function get_item_permissions_check( $request ) {
+ return $this->permissions_check( $request );
+ }
+
+ /**
+ * Returns the given template
+ *
+ * @since 5.8.0
+ *
+ * @param WP_REST_Request $request The request instance.
+ *
+ * @return WP_REST_Response|WP_Error
+ */
+ public function get_item( $request ) {
+ $template = get_block_template( $request['id'], $this->post_type );
+
+ if ( ! $template ) {
+ return new WP_Error( 'rest_template_not_found', __( 'No templates exist with that id.' ), array( 'status' => 404 ) );
+ }
+
+ return $this->prepare_item_for_response( $template, $request );
+ }
+
+ /**
+ * Checks if a given request has access to write a single template.
+ *
+ * @since 5.8.0
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return true|WP_Error True if the request has write access for the item, WP_Error object otherwise.
+ */
+ public function update_item_permissions_check( $request ) {
+ return $this->permissions_check( $request );
+ }
+
+ /**
+ * Updates a single template.
+ *
+ * @since 5.8.0
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
+ */
+ public function update_item( $request ) {
+ $template = get_block_template( $request['id'], $this->post_type );
+ if ( ! $template ) {
+ return new WP_Error( 'rest_template_not_found', __( 'No templates exist with that id.' ), array( 'status' => 404 ) );
+ }
+
+ $changes = $this->prepare_item_for_database( $request );
+
+ if ( 'custom' === $template->source ) {
+ $result = wp_update_post( wp_slash( (array) $changes ), true );
+ } else {
+ $result = wp_insert_post( wp_slash( (array) $changes ), true );
+ }
+ if ( is_wp_error( $result ) ) {
+ return $result;
+ }
+
+ $template = get_block_template( $request['id'], $this->post_type );
+ $fields_update = $this->update_additional_fields_for_object( $template, $request );
+ if ( is_wp_error( $fields_update ) ) {
+ return $fields_update;
+ }
+
+ return $this->prepare_item_for_response(
+ get_block_template( $request['id'], $this->post_type ),
+ $request
+ );
+ }
+
+ /**
+ * Checks if a given request has access to create a template.
+ *
+ * @since 5.8.0
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
+ */
+ public function create_item_permissions_check( $request ) {
+ return $this->permissions_check( $request );
+ }
+
+ /**
+ * Creates a single template.
+ *
+ * @since 5.8.0
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
+ */
+ public function create_item( $request ) {
+ $changes = $this->prepare_item_for_database( $request );
+ $changes->post_name = $request['slug'];
+ $result = wp_insert_post( wp_slash( (array) $changes ), true );
+ if ( is_wp_error( $result ) ) {
+ return $result;
+ }
+ $posts = get_block_templates( array( 'wp_id' => $result ), $this->post_type );
+ if ( ! count( $posts ) ) {
+ return new WP_Error( 'rest_template_insert_error', __( 'No templates exist with that id.' ) );
+ }
+ $id = $posts[0]->id;
+ $template = get_block_template( $id, $this->post_type );
+ $fields_update = $this->update_additional_fields_for_object( $template, $request );
+ if ( is_wp_error( $fields_update ) ) {
+ return $fields_update;
+ }
+
+ return $this->prepare_item_for_response(
+ get_block_template( $id, $this->post_type ),
+ $request
+ );
+ }
+
+ /**
+ * Checks if a given request has access to delete a single template.
+ *
+ * @since 5.8.0
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return true|WP_Error True if the request has delete access for the item, WP_Error object otherwise.
+ */
+ public function delete_item_permissions_check( $request ) {
+ return $this->permissions_check( $request );
+ }
+
+ /**
+ * Deletes a single template.
+ *
+ * @since 5.8.0
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
+ */
+ public function delete_item( $request ) {
+ $template = get_block_template( $request['id'], $this->post_type );
+ if ( ! $template ) {
+ return new WP_Error( 'rest_template_not_found', __( 'No templates exist with that id.' ), array( 'status' => 404 ) );
+ }
+ if ( 'custom' !== $template->source ) {
+ return new WP_Error( 'rest_invalid_template', __( 'Templates based on theme files can\'t be removed.' ), array( 'status' => 400 ) );
+ }
+
+ $id = $template->wp_id;
+ $force = (bool) $request['force'];
+
+ // If we're forcing, then delete permanently.
+ if ( $force ) {
+ $previous = $this->prepare_item_for_response( $template, $request );
+ wp_delete_post( $id, true );
+ $response = new WP_REST_Response();
+ $response->set_data(
+ array(
+ 'deleted' => true,
+ 'previous' => $previous->get_data(),
+ )
+ );
+
+ return $response;
+ }
+
+ // Otherwise, only trash if we haven't already.
+ if ( 'trash' === $template->status ) {
+ return new WP_Error(
+ 'rest_template_already_trashed',
+ __( 'The template has already been deleted.' ),
+ array( 'status' => 410 )
+ );
+ }
+
+ wp_trash_post( $id );
+ $template->status = 'trash';
+ return $this->prepare_item_for_response( $template, $request );
+ }
+
+ /**
+ * Prepares a single template for create or update.
+ *
+ * @since 5.8.0
+ *
+ * @param WP_REST_Request $request Request object.
+ * @return stdClass Changes to pass to wp_update_post.
+ */
+ protected function prepare_item_for_database( $request ) {
+ $template = $request['id'] ? get_block_template( $request['id'], $this->post_type ) : null;
+ $changes = new stdClass();
+ if ( null === $template ) {
+ $changes->post_type = $this->post_type;
+ $changes->post_status = 'publish';
+ $changes->tax_input = array(
+ 'wp_theme' => isset( $request['theme'] ) ? $request['theme'] : wp_get_theme()->get_stylesheet(),
+ );
+ } elseif ( 'custom' !== $template->source ) {
+ $changes->post_name = $template->slug;
+ $changes->post_type = $this->post_type;
+ $changes->post_status = 'publish';
+ $changes->tax_input = array(
+ 'wp_theme' => $template->theme,
+ );
+ } else {
+ $changes->post_name = $template->slug;
+ $changes->ID = $template->wp_id;
+ $changes->post_status = 'publish';
+ }
+ if ( isset( $request['content'] ) ) {
+ $changes->post_content = $request['content'];
+ } elseif ( null !== $template && 'custom' !== $template->source ) {
+ $changes->post_content = $template->content;
+ }
+ if ( isset( $request['title'] ) ) {
+ $changes->post_title = $request['title'];
+ } elseif ( null !== $template && 'custom' !== $template->source ) {
+ $changes->post_title = $template->title;
+ }
+ if ( isset( $request['description'] ) ) {
+ $changes->post_excerpt = $request['description'];
+ } elseif ( null !== $template && 'custom' !== $template->source ) {
+ $changes->post_excerpt = $template->description;
+ }
+
+ return $changes;
+ }
+
+ /**
+ * Prepare a single template output for response
+ *
+ * @since 5.8.0
+ *
+ * @param WP_Block_Template $template Template instance.
+ * @param WP_REST_Request $request Request object.
+ *
+ * @return WP_REST_Response $data
+ */
+ public function prepare_item_for_response( $template, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $result = array(
+ 'id' => $template->id,
+ 'theme' => $template->theme,
+ 'content' => array( 'raw' => $template->content ),
+ 'slug' => $template->slug,
+ 'source' => $template->source,
+ 'type' => $template->type,
+ 'description' => $template->description,
+ 'title' => array(
+ 'raw' => $template->title,
+ 'rendered' => $template->title,
+ ),
+ 'status' => $template->status,
+ 'wp_id' => $template->wp_id,
+ 'has_theme_file' => $template->has_theme_file,
+ );
+
+ if ( 'wp_template_part' === $template->type ) {
+ $result['area'] = $template->area;
+ }
+
+ $result = $this->add_additional_fields_to_object( $result, $request );
+
+ $response = rest_ensure_response( $result );
+ $links = $this->prepare_links( $template->id );
+ $response->add_links( $links );
+ if ( ! empty( $links['self']['href'] ) ) {
+ $actions = $this->get_available_actions();
+ $self = $links['self']['href'];
+ foreach ( $actions as $rel ) {
+ $response->add_link( $rel, $self );
+ }
+ }
+
+ return $response;
+ }
+
+
+ /**
+ * Prepares links for the request.
+ *
+ * @since 5.8.0
+ *
+ * @param integer $id ID.
+ * @return array Links for the given post.
+ */
+ protected function prepare_links( $id ) {
+ $base = sprintf( '%s/%s', $this->namespace, $this->rest_base );
+
+ $links = array(
+ 'self' => array(
+ 'href' => rest_url( trailingslashit( $base ) . $id ),
+ ),
+ 'collection' => array(
+ 'href' => rest_url( $base ),
+ ),
+ 'about' => array(
+ 'href' => rest_url( 'wp/v2/types/' . $this->post_type ),
+ ),
+ );
+
+ return $links;
+ }
+
+ /**
+ * Get the link relations available for the post and current user.
+ *
+ * @since 5.8.0
+ *
+ * @return array List of link relations.
+ */
+ protected function get_available_actions() {
+ $rels = array();
+
+ $post_type = get_post_type_object( $this->post_type );
+
+ if ( current_user_can( $post_type->cap->publish_posts ) ) {
+ $rels[] = 'https://api.w.org/action-publish';
+ }
+
+ if ( current_user_can( 'unfiltered_html' ) ) {
+ $rels[] = 'https://api.w.org/action-unfiltered-html';
+ }
+
+ return $rels;
+ }
+
+ /**
+ * Retrieves the query params for the posts collection.
+ *
+ * @since 5.8.0
+ *
+ * @return array Collection parameters.
+ */
+ public function get_collection_params() {
+ return array(
+ 'context' => $this->get_context_param(),
+ 'wp_id' => array(
+ 'description' => __( 'Limit to the specified post id.' ),
+ 'type' => 'integer',
+ ),
+ );
+ }
+
+ /**
+ * Retrieves the block type' schema, conforming to JSON Schema.
+ *
+ * @since 5.8.0
+ *
+ * @return array Item schema data.
+ */
+ public function get_item_schema() {
+ if ( $this->schema ) {
+ return $this->add_additional_fields_schema( $this->schema );
+ }
+
+ $schema = array(
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
+ 'title' => $this->post_type,
+ 'type' => 'object',
+ 'properties' => array(
+ 'id' => array(
+ 'description' => __( 'ID of template.' ),
+ 'type' => 'string',
+ 'context' => array( 'embed', 'view', 'edit' ),
+ 'readonly' => true,
+ ),
+ 'slug' => array(
+ 'description' => __( 'Unique slug identifying the template.' ),
+ 'type' => 'string',
+ 'context' => array( 'embed', 'view', 'edit' ),
+ 'required' => true,
+ 'minLength' => 1,
+ 'pattern' => '[a-zA-Z_\-]+',
+ ),
+ 'theme' => array(
+ 'description' => __( 'Theme identifier for the template.' ),
+ 'type' => 'string',
+ 'context' => array( 'embed', 'view', 'edit' ),
+ ),
+ 'source' => array(
+ 'description' => __( 'Source of template' ),
+ 'type' => 'string',
+ 'context' => array( 'embed', 'view', 'edit' ),
+ 'readonly' => true,
+ ),
+ 'content' => array(
+ 'description' => __( 'Content of template.' ),
+ 'type' => array( 'object', 'string' ),
+ 'default' => '',
+ 'context' => array( 'embed', 'view', 'edit' ),
+ ),
+ 'title' => array(
+ 'description' => __( 'Title of template.' ),
+ 'type' => array( 'object', 'string' ),
+ 'default' => '',
+ 'context' => array( 'embed', 'view', 'edit' ),
+ ),
+ 'description' => array(
+ 'description' => __( 'Description of template.' ),
+ 'type' => 'string',
+ 'default' => '',
+ 'context' => array( 'embed', 'view', 'edit' ),
+ ),
+ 'status' => array(
+ 'description' => __( 'Status of template.' ),
+ 'type' => 'string',
+ 'default' => 'publish',
+ 'context' => array( 'embed', 'view', 'edit' ),
+ ),
+ 'wp_id' => array(
+ 'description' => __( 'Post ID.' ),
+ 'type' => 'integer',
+ 'context' => array( 'embed', 'view', 'edit' ),
+ 'readonly' => true,
+ ),
+ 'has_theme_file' => array(
+ 'description' => __( 'Theme file exists.' ),
+ 'type' => 'bool',
+ 'context' => array( 'embed', 'view', 'edit' ),
+ 'readonly' => true,
+ ),
+ ),
+ );
+
+ $this->schema = $schema;
+
+ return $this->add_additional_fields_schema( $this->schema );
+ }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunksrcwpincludesscriptloaderphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/script-loader.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/script-loader.php 2021-05-25 13:54:12 UTC (rev 51002)
+++ trunk/src/wp-includes/script-loader.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2662,3 +2662,65 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+/**
+ * Inject the block editor assets that need to be loaded into the editor's iframe as an inline script.
+ *
+ * @since 5.8.0
+ */
+function wp_add_iframed_editor_assets_html() {
+ $script_handles = array();
+ $style_handles = array(
+ 'wp-block-editor',
+ 'wp-block-library',
+ 'wp-block-library-theme',
+ 'wp-edit-blocks',
+ );
+
+ $block_registry = WP_Block_Type_Registry::get_instance();
+
+ foreach ( $block_registry->get_all_registered() as $block_type ) {
+ if ( ! empty( $block_type->style ) ) {
+ $style_handles[] = $block_type->style;
+ }
+
+ if ( ! empty( $block_type->editor_style ) ) {
+ $style_handles[] = $block_type->editor_style;
+ }
+
+ if ( ! empty( $block_type->script ) ) {
+ $script_handles[] = $block_type->script;
+ }
+ }
+
+ $style_handles = array_unique( $style_handles );
+ $done = wp_styles()->done;
+
+ ob_start();
+
+ wp_styles()->done = array();
+ wp_styles()->do_items( $style_handles );
+ wp_styles()->done = $done;
+
+ $styles = ob_get_clean();
+
+ $script_handles = array_unique( $script_handles );
+ $done = wp_scripts()->done;
+
+ ob_start();
+
+ wp_scripts()->done = array();
+ wp_scripts()->do_items( $script_handles );
+ wp_scripts()->done = $done;
+
+ $scripts = ob_get_clean();
+
+ $editor_assets = wp_json_encode(
+ array(
+ 'styles' => $styles,
+ 'scripts' => $scripts,
+ )
+ );
+
+ echo "<script>window.__editorAssets = $editor_assets</script>";
+}
</ins></span></pre></div>
<a id="trunksrcwpincludestaxonomyphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/taxonomy.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/taxonomy.php 2021-05-25 13:54:12 UTC (rev 51002)
+++ trunk/src/wp-includes/taxonomy.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -172,6 +172,25 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 'show_in_nav_menus' => current_theme_supports( 'post-formats' ),
</span><span class="cx" style="display: block; padding: 0 10px"> )
</span><span class="cx" style="display: block; padding: 0 10px"> );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ register_taxonomy(
+ 'wp_theme',
+ array( 'wp_template' ),
+ array(
+ 'public' => false,
+ 'hierarchical' => false,
+ 'labels' => array(
+ 'name' => __( 'Themes' ),
+ 'singular_name' => __( 'Theme' ),
+ ),
+ 'query_var' => false,
+ 'rewrite' => false,
+ 'show_ui' => false,
+ '_builtin' => true,
+ 'show_in_nav_menus' => false,
+ 'show_in_rest' => false,
+ )
+ );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span></span></pre></div>
<a id="trunksrcwpincludestemplatecanvasphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/src/wp-includes/template-canvas.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/template-canvas.php (rev 0)
+++ trunk/src/wp-includes/template-canvas.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,28 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Template canvas file to render the current 'wp_template'.
+ *
+ * @package WordPress
+ */
+
+/**
+ * Get the template HTML.
+ * This needs to run before <head> so that blocks can add scripts and styles in wp_head().
+ */
+$template_html = get_the_block_template_html();
+?>
+<!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 echo $template_html; // phpcs:ignore WordPress.Security.EscapeOutput ?>
+
+<?php wp_footer(); ?>
+</body>
+</html>
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/src/wp-includes/template-canvas.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunksrcwpincludestemplatephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/template.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/template.php 2021-05-25 13:54:12 UTC (rev 51002)
+++ trunk/src/wp-includes/template.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -63,6 +63,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $template = locate_template( $templates );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $template = locate_block_template( $template, $type, $templates );
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px"> * Filters the path of the queried template by type.
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span></span></pre></div>
<a id="trunksrcwpincludesthemetemplatesphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/src/wp-includes/theme-templates.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/theme-templates.php (rev 0)
+++ trunk/src/wp-includes/theme-templates.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,163 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Generates a unique slug for templates.
+ *
+ * @access private
+ * @since 5.8.0
+ *
+ * @param string $override_slug The filtered value of the slug (starts as `null` from apply_filter).
+ * @param string $slug The original/un-filtered slug (post_name).
+ * @param int $post_ID Post ID.
+ * @param string $post_status No uniqueness checks are made if the post is still draft or pending.
+ * @param string $post_type Post type.
+ * @return string The original, desired slug.
+ */
+function wp_filter_wp_template_unique_post_slug( $override_slug, $slug, $post_ID, $post_status, $post_type ) {
+ if ( 'wp_template' !== $post_type ) {
+ return $override_slug;
+ }
+
+ if ( ! $override_slug ) {
+ $override_slug = $slug;
+ }
+
+ // Template slugs must be unique within the same theme.
+ // TODO - Figure out how to update this to work for a multi-theme
+ // environment. Unfortunately using `get_the_terms` for the 'wp-theme'
+ // term does not work in the case of new entities since is too early in
+ // the process to have been saved to the entity. So for now we use the
+ // currently activated theme for creation.
+ $theme = wp_get_theme()->get_stylesheet();
+ $terms = get_the_terms( $post_ID, 'wp_theme' );
+ if ( $terms && ! is_wp_error( $terms ) ) {
+ $theme = $terms[0]->name;
+ }
+
+ $check_query_args = array(
+ 'post_name__in' => array( $override_slug ),
+ 'post_type' => $post_type,
+ 'posts_per_page' => 1,
+ 'no_found_rows' => true,
+ 'post__not_in' => array( $post_ID ),
+ 'tax_query' => array(
+ array(
+ 'taxonomy' => 'wp_theme',
+ 'field' => 'name',
+ 'terms' => $theme,
+ ),
+ ),
+ );
+ $check_query = new WP_Query( $check_query_args );
+ $posts = $check_query->get_posts();
+
+ if ( count( $posts ) > 0 ) {
+ $suffix = 2;
+ do {
+ $query_args = $check_query_args;
+ $alt_post_name = _truncate_post_slug( $override_slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
+ $query_args['post_name__in'] = array( $alt_post_name );
+ $query = new WP_Query( $query_args );
+ $suffix++;
+ } while ( count( $query->get_posts() ) > 0 );
+ $override_slug = $alt_post_name;
+ }
+
+ return $override_slug;
+}
+
+/**
+ * Print the skip-link script & styles.
+ *
+ * @access private
+ * @since 5.8.0
+ *
+ * @return void
+ */
+function the_block_template_skip_link() {
+
+ // Early exit if not an FSE theme.
+ if ( ! current_theme_supports( 'block-templates' ) ) {
+ return;
+ }
+ ?>
+
+ <?php
+ /**
+ * Print the skip-link styles.
+ */
+ ?>
+ <style id="skip-link-styles">
+ .skip-link.screen-reader-text {
+ border: 0;
+ clip: rect(1px,1px,1px,1px);
+ clip-path: inset(50%);
+ height: 1px;
+ margin: -1px;
+ overflow: hidden;
+ padding: 0;
+ position: absolute !important;
+ width: 1px;
+ word-wrap: normal !important;
+ }
+
+ .skip-link.screen-reader-text:focus {
+ background-color: #eee;
+ clip: auto !important;
+ clip-path: none;
+ color: #444;
+ display: block;
+ font-size: 1em;
+ height: auto;
+ left: 5px;
+ line-height: normal;
+ padding: 15px 23px 14px;
+ text-decoration: none;
+ top: 5px;
+ width: auto;
+ z-index: 100000;
+ }
+ </style>
+ <?php
+ /**
+ * Print the skip-link script.
+ */
+ ?>
+ <script>
+ ( function() {
+ var skipLinkTarget = document.querySelector( 'main' ),
+ parentEl,
+ skipLinkTargetID,
+ skipLink;
+
+ // Early exit if a skip-link target can't be located.
+ if ( ! skipLinkTarget ) {
+ return;
+ }
+
+ // Get the site wrapper.
+ // The skip-link will be injected in the beginning of it.
+ parentEl = document.querySelector( '.wp-site-blocks' ) || document.body,
+
+ // Get the skip-link target's ID, and generate one if it doesn't exist.
+ skipLinkTargetID = skipLinkTarget.id;
+ if ( ! skipLinkTargetID ) {
+ skipLinkTargetID = 'wp--skip-link--target';
+ skipLinkTarget.id = skipLinkTargetID;
+ }
+
+ // Create the skip link.
+ skipLink = document.createElement( 'a' );
+ skipLink.classList.add( 'skip-link', 'screen-reader-text' );
+ skipLink.href = '#' + skipLinkTargetID;
+ skipLink.innerHTML = '<?php esc_html_e( 'Skip to content' ); ?>';
+
+ // Inject the skip link.
+ parentEl.insertAdjacentElement( 'afterbegin', skipLink );
+ }() );
+ </script>
+ <?php
+}
+
+// By default, themes support block templates.
+add_theme_support( 'block-templates' );
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/src/wp-includes/theme-templates.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunksrcwpsettingsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-settings.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-settings.php 2021-05-25 13:54:12 UTC (rev 51002)
+++ trunk/src/wp-settings.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -170,6 +170,10 @@
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/class-wp-date-query.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/theme.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/class-wp-theme.php';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require ABSPATH . WPINC . '/class-wp-block-template.php';
+require ABSPATH . WPINC . '/block-template-utils.php';
+require ABSPATH . WPINC . '/block-template.php';
+require ABSPATH . WPINC . '/theme-templates.php';
</ins><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/template.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/https-detection.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/https-migration.php';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -268,6 +272,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-sidebars-controller.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-widget-types-controller.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-widgets-controller.php';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-templates-controller.php';
</ins><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-meta-fields.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-comment-meta-fields.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-post-meta-fields.php';
</span></span></pre></div>
<a id="trunktestsphpunittestsblocktemplateutilsphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/tests/block-template-utils.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/block-template-utils.php (rev 0)
+++ trunk/tests/phpunit/tests/block-template-utils.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,97 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Block_Template_Utils_Test class
+ *
+ * @package WordPress
+ */
+
+/**
+ * Tests for the Block Template Loader abstraction layer.
+ */
+class Block_Template_Utils_Test extends WP_UnitTestCase {
+ private static $post;
+
+ public static function wpSetUpBeforeClass() {
+ // Set up template post.
+ $args = array(
+ 'post_type' => 'wp_template',
+ 'post_name' => 'my_template',
+ 'post_title' => 'My Template',
+ 'post_content' => 'Content',
+ 'post_excerpt' => 'Description of my template',
+ 'tax_input' => array(
+ 'wp_theme' => array(
+ get_stylesheet(),
+ ),
+ ),
+ );
+ self::$post = self::factory()->post->create_and_get( $args );
+ wp_set_post_terms( self::$post->ID, get_stylesheet(), 'wp_theme' );
+ }
+
+ public static function wpTearDownAfterClass() {
+ wp_delete_post( self::$post->ID );
+ }
+
+ function test_build_template_result_from_post() {
+ $template = _build_template_result_from_post(
+ self::$post,
+ 'wp_template'
+ );
+
+ $this->assertNotWPError( $template );
+ $this->assertEquals( get_stylesheet() . '//my_template', $template->id );
+ $this->assertEquals( get_stylesheet(), $template->theme );
+ $this->assertEquals( 'my_template', $template->slug );
+ $this->assertEquals( 'publish', $template->status );
+ $this->assertEquals( 'custom', $template->source );
+ $this->assertEquals( 'My Template', $template->title );
+ $this->assertEquals( 'Description of my template', $template->description );
+ $this->assertEquals( 'wp_template', $template->type );
+ }
+
+ /**
+ * Should retrieve the template from the CPT.
+ */
+ function test_get_block_template_from_post() {
+ $id = get_stylesheet() . '//' . 'my_template';
+ $template = get_block_template( $id, 'wp_template' );
+ $this->assertEquals( $id, $template->id );
+ $this->assertEquals( get_stylesheet(), $template->theme );
+ $this->assertEquals( 'my_template', $template->slug );
+ $this->assertEquals( 'publish', $template->status );
+ $this->assertEquals( 'custom', $template->source );
+ $this->assertEquals( 'wp_template', $template->type );
+ }
+
+ /**
+ * Should retrieve block templates.
+ */
+ function test_get_block_templates() {
+ function get_template_ids( $templates ) {
+ return array_map(
+ function( $template ) {
+ return $template->id;
+ },
+ $templates
+ );
+ }
+
+ // All results.
+ $templates = get_block_templates( array(), 'wp_template' );
+ $template_ids = get_template_ids( $templates );
+
+ // Avoid testing the entire array because the theme might add/remove templates.
+ $this->assertContains( get_stylesheet() . '//' . 'my_template', $template_ids );
+
+ // Filter by slug.
+ $templates = get_block_templates( array( 'slug__in' => array( 'my_template' ) ), 'wp_template' );
+ $template_ids = get_template_ids( $templates );
+ $this->assertEquals( array( get_stylesheet() . '//' . 'my_template' ), $template_ids );
+
+ // Filter by CPT ID.
+ $templates = get_block_templates( array( 'wp_id' => self::$post->ID ), 'wp_template' );
+ $template_ids = get_template_ids( $templates );
+ $this->assertEquals( array( get_stylesheet() . '//' . 'my_template' ), $template_ids );
+ }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/tests/block-template-utils.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunktestsphpunittestsblocktemplatephp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/tests/block-template.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/block-template.php (rev 0)
+++ trunk/tests/phpunit/tests/block-template.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,87 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Block_Template_Test class
+ *
+ * @package WordPress
+ */
+
+/**
+ * Tests for the block template loading algorithm.
+ */
+class Block_Template_Test extends WP_UnitTestCase {
+ private static $post;
+
+ private static $template_canvas_path = ABSPATH . WPINC . '/template-canvas.php';
+
+ public static function wpSetUpBeforeClass() {
+ // Set up custom template post.
+ $args = array(
+ 'post_type' => 'wp_template',
+ 'post_name' => 'wp-custom-template-my-block-template',
+ 'post_title' => 'My Custom Block Template',
+ 'post_content' => 'Content',
+ 'post_excerpt' => 'Description of my block template',
+ 'tax_input' => array(
+ 'wp_theme' => array(
+ get_stylesheet(),
+ ),
+ ),
+ );
+ self::$post = self::factory()->post->create_and_get( $args );
+ wp_set_post_terms( self::$post->ID, get_stylesheet(), 'wp_theme' );
+ }
+
+ public static function wpTearDownAfterClass() {
+ wp_delete_post( self::$post->ID );
+ }
+
+ public function tearDown() {
+ global $_wp_current_template_content;
+ unset( $_wp_current_template_content );
+ }
+
+ /**
+ * Regression: https://github.com/WordPress/gutenberg/issues/31399.
+ */
+ function test_custom_page_php_template_takes_precedence_over_all_other_templates() {
+ $custom_page_template = 'templates/full-width.php';
+ $custom_page_template_path = get_stylesheet_directory() . '/' . $custom_page_template;
+ $type = 'page';
+ $templates = array(
+ $custom_page_template,
+ 'page-slug.php',
+ 'page-1.php',
+ 'page.php',
+ );
+ $resolved_template_path = locate_block_template( $custom_page_template_path, $type, $templates );
+ $this->assertEquals( $custom_page_template_path, $resolved_template_path );
+ }
+
+ /**
+ * Covers: https://github.com/WordPress/gutenberg/pull/30438.
+ */
+ function test_custom_page_block_template_takes_precedence_over_all_other_templates() {
+ global $_wp_current_template_content;
+
+ $custom_page_block_template = 'wp-custom-template-my-block-template';
+ $page_template_path = get_stylesheet_directory() . '/' . 'page.php';
+ $type = 'page';
+ $templates = array(
+ $custom_page_block_template,
+ 'page-slug.php',
+ 'page-1.php',
+ 'page.php',
+ );
+ $resolved_template_path = locate_block_template( $page_template_path, $type, $templates );
+ $this->assertEquals( self::$template_canvas_path, $resolved_template_path );
+ $this->assertEquals( self::$post->post_content, $_wp_current_template_content );
+ }
+
+ /**
+ * Regression: https://github.com/WordPress/gutenberg/issues/31652.
+ */
+ function test_template_remains_unchanged_if_templates_array_is_empty() {
+ $resolved_template_path = locate_block_template( '', 'search', array() );
+ $this->assertEquals( '', $resolved_template_path );
+ }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/tests/block-template.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunktestsphpunittestsrestapirestschemasetupphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php 2021-05-25 13:54:12 UTC (rev 51002)
+++ trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -129,6 +129,12 @@
</span><span class="cx" style="display: block; padding: 0 10px"> '/wp/v2/block-types/(?P<namespace>[a-zA-Z0-9_-]+)',
</span><span class="cx" style="display: block; padding: 0 10px"> '/wp/v2/block-types/(?P<namespace>[a-zA-Z0-9_-]+)/(?P<name>[a-zA-Z0-9_-]+)',
</span><span class="cx" style="display: block; padding: 0 10px"> '/wp/v2/settings',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ '/wp/v2/templates',
+ '/wp/v2/templates/(?P<id>[\/\w-]+)',
+ '/wp/v2/templates/(?P<id>[\d]+)/autosaves',
+ '/wp/v2/templates/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)',
+ '/wp/v2/templates/(?P<parent>[\d]+)/revisions',
+ '/wp/v2/templates/(?P<parent>[\d]+)/revisions/(?P<id>[\d]+)',
</ins><span class="cx" style="display: block; padding: 0 10px"> '/wp/v2/themes',
</span><span class="cx" style="display: block; padding: 0 10px"> '/wp/v2/themes/(?P<stylesheet>[\w-]+)',
</span><span class="cx" style="display: block; padding: 0 10px"> '/wp/v2/plugins',
</span></span></pre></div>
<a id="trunktestsphpunittestsrestapiresttemplatescontrollerphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/tests/rest-api/rest-templates-controller.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/rest-api/rest-templates-controller.php (rev 0)
+++ trunk/tests/phpunit/tests/rest-api/rest-templates-controller.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,197 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Unit tests covering the templates endpoint..
+ *
+ * @package WordPress
+ * @subpackage REST API
+ */
+
+class WP_REST_Template_Controller_Test extends WP_Test_REST_Controller_Testcase {
+ /**
+ * @var int
+ */
+ protected static $admin_id;
+ private static $post;
+
+ /**
+ * Create fake data before our tests run.
+ *
+ * @param WP_UnitTest_Factory $factory Helper that lets us create fake data.
+ */
+ public static function wpSetupBeforeClass( $factory ) {
+ self::$admin_id = $factory->user->create(
+ array(
+ 'role' => 'administrator',
+ )
+ );
+
+ // Set up template post.
+ $args = array(
+ 'post_type' => 'wp_template',
+ 'post_name' => 'my_template',
+ 'post_title' => 'My Template',
+ 'post_content' => 'Content',
+ 'post_excerpt' => 'Description of my template.',
+ 'tax_input' => array(
+ 'wp_theme' => array(
+ get_stylesheet(),
+ ),
+ ),
+ );
+ self::$post = self::factory()->post->create_and_get( $args );
+ wp_set_post_terms( self::$post->ID, get_stylesheet(), 'wp_theme' );
+ }
+
+ public static function wpTearDownAfterClass() {
+ wp_delete_post( self::$post->ID );
+ }
+
+
+ public function test_register_routes() {
+ $routes = rest_get_server()->get_routes();
+ $this->assertArrayHasKey( '/wp/v2/templates', $routes );
+ $this->assertArrayHasKey( '/wp/v2/templates/(?P<id>[\/\w-]+)', $routes );
+ }
+
+ public function test_context_param() {
+ // TODO: Implement test_context_param() method.
+ }
+
+ public function test_get_items() {
+ function find_and_normalize_template_by_id( $templates, $id ) {
+ foreach ( $templates as $template ) {
+ if ( $template['id'] === $id ) {
+ unset( $template['content'] );
+ unset( $template['_links'] );
+ return $template;
+ }
+ }
+
+ return null;
+ }
+
+ wp_set_current_user( 0 );
+ $request = new WP_REST_Request( 'GET', '/wp/v2/templates' );
+ $response = rest_get_server()->dispatch( $request );
+ $this->assertErrorResponse( 'rest_cannot_manage_templates', $response, 401 );
+
+ wp_set_current_user( self::$admin_id );
+ $request = new WP_REST_Request( 'GET', '/wp/v2/templates' );
+ $response = rest_get_server()->dispatch( $request );
+ $data = $response->get_data();
+
+ $this->assertEquals(
+ array(
+ 'id' => 'default//my_template',
+ 'theme' => 'default',
+ 'slug' => 'my_template',
+ 'title' => array(
+ 'raw' => 'My Template',
+ 'rendered' => 'My Template',
+ ),
+ 'description' => 'Description of my template.',
+ 'status' => 'publish',
+ 'source' => 'custom',
+ 'type' => 'wp_template',
+ 'wp_id' => self::$post->ID,
+ 'has_theme_file' => false,
+ ),
+ find_and_normalize_template_by_id( $data, 'default//my_template' )
+ );
+ }
+
+ public function test_get_item() {
+ wp_set_current_user( self::$admin_id );
+ $request = new WP_REST_Request( 'GET', '/wp/v2/templates/default//my_template' );
+ $response = rest_get_server()->dispatch( $request );
+ $data = $response->get_data();
+ unset( $data['content'] );
+ unset( $data['_links'] );
+
+ $this->assertEquals(
+ array(
+ 'id' => 'default//my_template',
+ 'theme' => 'default',
+ 'slug' => 'my_template',
+ 'title' => array(
+ 'raw' => 'My Template',
+ 'rendered' => 'My Template',
+ ),
+ 'description' => 'Description of my template.',
+ 'status' => 'publish',
+ 'source' => 'custom',
+ 'type' => 'wp_template',
+ 'wp_id' => self::$post->ID,
+ 'has_theme_file' => false,
+ ),
+ $data
+ );
+ }
+
+ public function test_create_item() {
+ wp_set_current_user( self::$admin_id );
+ $request = new WP_REST_Request( 'POST', '/wp/v2/templates' );
+ $request->set_body_params(
+ array(
+ 'slug' => 'my_custom_template',
+ 'title' => 'My Template',
+ 'description' => 'Just a description',
+ 'content' => 'Content',
+ )
+ );
+ $response = rest_get_server()->dispatch( $request );
+ $data = $response->get_data();
+ unset( $data['_links'] );
+ unset( $data['wp_id'] );
+
+ $this->assertEquals(
+ array(
+ 'id' => 'default//my_custom_template',
+ 'theme' => 'default',
+ 'slug' => 'my_custom_template',
+ 'title' => array(
+ 'raw' => 'My Template',
+ 'rendered' => 'My Template',
+ ),
+ 'description' => 'Just a description',
+ 'status' => 'publish',
+ 'source' => 'custom',
+ 'type' => 'wp_template',
+ 'content' => array(
+ 'raw' => 'Content',
+ ),
+ 'has_theme_file' => false,
+ ),
+ $data
+ );
+ }
+
+ public function test_update_item() {
+ wp_set_current_user( self::$admin_id );
+ $request = new WP_REST_Request( 'PUT', '/wp/v2/templates/default//my_template' );
+ $request->set_body_params(
+ array(
+ 'title' => 'My new Index Title',
+ )
+ );
+ $response = rest_get_server()->dispatch( $request );
+ $data = $response->get_data();
+ $this->assertEquals( 'My new Index Title', $data['title']['raw'] );
+ $this->assertEquals( 'custom', $data['source'] );
+ }
+
+ public function test_delete_item() {
+ wp_set_current_user( self::$admin_id );
+ $request = new WP_REST_Request( 'DELETE', '/wp/v2/templates/justrandom//template' );
+ $response = rest_get_server()->dispatch( $request );
+ $this->assertErrorResponse( 'rest_template_not_found', $response, 404 );
+ }
+
+ public function test_prepare_item() {
+ // TODO: Implement test_prepare_item() method.
+ }
+
+ public function test_get_item_schema() {
+ // TODO: Implement test_get_item_schema() method.
+ }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/tests/rest-api/rest-templates-controller.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunktestsphpunitteststhemewpThemeJsonphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/theme/wpThemeJson.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/theme/wpThemeJson.php 2021-05-25 13:54:12 UTC (rev 51002)
+++ trunk/tests/phpunit/tests/theme/wpThemeJson.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5,6 +5,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @package WordPress
</span><span class="cx" style="display: block; padding: 0 10px"> * @subpackage Theme
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ *
</ins><span class="cx" style="display: block; padding: 0 10px"> * @since 5.8.0
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @group themes
</span></span></pre></div>
<a id="trunktestsphpunitteststhemewpThemeJsonResolverphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/theme/wpThemeJsonResolver.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/theme/wpThemeJsonResolver.php 2021-05-25 13:54:12 UTC (rev 51002)
+++ trunk/tests/phpunit/tests/theme/wpThemeJsonResolver.php 2021-05-25 14:19:14 UTC (rev 51003)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5,6 +5,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @package WordPress
</span><span class="cx" style="display: block; padding: 0 10px"> * @subpackage Theme
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ *
</ins><span class="cx" style="display: block; padding: 0 10px"> * @since 5.8.0
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @group themes
</span></span></pre>
</div>
</div>
</body>
</html>