<!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>[56819] trunk: REST API: Fix issue with Template and Template Part Revision/Autosave REST API controllers.</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/56819">56819</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/56819","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>spacedmonkey</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2023-10-10 14:03:03 +0000 (Tue, 10 Oct 2023)</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'>REST API: Fix issue with Template and Template Part Revision/Autosave REST API controllers. 

The Template and Template Part REST API controllers have unique characteristics compared to other post type REST API controllers. They do not rely on integer IDs to reference objects; instead, they use a combination of the theme name and slug of the template, like 'twentytwentyfour//home.' Consequently, when the post types template and template part were introduced in <a href="https://core.trac.wordpress.org/changeset/52062">[52062]</a>, it led to the registration of REST API endpoints for autosaves and revisions with invalid URL structures.

In this commit, we introduce new functionality to enable custom autosave and revisions endpoints to be registered at the post type level. Similar to the 'rest_controller_class' parameter, developers can now define 'revisions_rest_controller' and 'autosave_rest_controller.' This empowers developers to create custom controllers for these functionalities. Additionally, we introduce a 'late_route_registration' parameter, which proves helpful when dealing with custom URL patterns and regex pattern matching issues.
This commit registers new classes for template and template part autosave and revisions controllers, differentiating them from standard controllers in the following ways:
* The response shape now matches that of the template controller.
* Permission checks align with the template controller.
* A custom URL pattern is introduced to support slug-based identification of templates.

Furthermore, we've updated the utility function '_build_block_template_result_from_post' to support passing revision post objects. This enhancement ensures compatibility with the custom revisions controller.

Props spacedmonkey, revgeorge, andraganescu, hellofromTonya, antonvlasenko, kadamwhite, ironprogrammer, costdev, mukesh27, timothyblynjacobs, adamsilverstein. 
Fixes 56922.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesblocktemplateutilsphp">trunk/src/wp-includes/block-template-utils.php</a></li>
<li><a href="#trunksrcwpincludesclasswpposttypephp">trunk/src/wp-includes/class-wp-post-type.php</a></li>
<li><a href="#trunksrcwpincludespostphp">trunk/src/wp-includes/post.php</a></li>
<li><a href="#trunksrcwpincludesrestapiendpointsclasswprestautosavescontrollerphp">trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.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="#trunksrcwpincludesrestapiphp">trunk/src/wp-includes/rest-api.php</a></li>
<li><a href="#trunksrcwpsettingsphp">trunk/src/wp-settings.php</a></li>
<li><a href="#trunktestsphpunittestspostwpPostTypephp">trunk/tests/phpunit/tests/post/wpPostType.php</a></li>
<li><a href="#trunktestsphpunittestsrestapirestschemasetupphp">trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php</a></li>
<li><a href="#trunktestsqunitfixtureswpapigeneratedjs">trunk/tests/qunit/fixtures/wp-api-generated.js</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesrestapiendpointsclasswpresttemplateautosavescontrollerphp">trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-template-autosaves-controller.php</a></li>
<li><a href="#trunksrcwpincludesrestapiendpointsclasswpresttemplaterevisionscontrollerphp">trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-template-revisions-controller.php</a></li>
<li><a href="#trunktestsphpunittestsrestapiwpRestTemplateAutosavesControllerphp">trunk/tests/phpunit/tests/rest-api/wpRestTemplateAutosavesController.php</a></li>
<li><a href="#trunktestsphpunittestsrestapiwpRestTemplateRevisionsControllerphp">trunk/tests/phpunit/tests/rest-api/wpRestTemplateRevisionsController.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludesblocktemplateutilsphp"></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/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    2023-10-10 13:19:42 UTC (rev 56818)
+++ trunk/src/wp-includes/block-template-utils.php      2023-10-10 14:03:03 UTC (rev 56819)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -724,6 +724,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 5.9.0
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 6.3.0 Added `modified` property to template objects.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @since 6.4.0 Added support for a revision post to be passed to this function.
</ins><span class="cx" style="display: block; padding: 0 10px">  * @access private
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @param WP_Post $post Template post.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -731,8 +732,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> function _build_block_template_result_from_post( $post ) {
</span><span class="cx" style="display: block; padding: 0 10px">        $default_template_types = get_default_block_template_types();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $terms                  = get_the_terms( $post, 'wp_theme' );
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        $post_id = wp_is_post_revision( $post );
+       if ( ! $post_id ) {
+               $post_id = $post;
+       }
+       $parent_post = get_post( $post_id );
+
+       $terms = get_the_terms( $parent_post, 'wp_theme' );
+
</ins><span class="cx" style="display: block; padding: 0 10px">         if ( is_wp_error( $terms ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                return $terms;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -745,12 +753,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">        $template_file  = _get_block_template_file( $post->post_type, $post->post_name );
</span><span class="cx" style="display: block; padding: 0 10px">        $has_theme_file = get_stylesheet() === $theme && null !== $template_file;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $origin           = get_post_meta( $post->ID, 'origin', true );
-       $is_wp_suggestion = get_post_meta( $post->ID, 'is_wp_suggestion', true );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $origin           = get_post_meta( $parent_post->ID, 'origin', true );
+       $is_wp_suggestion = get_post_meta( $parent_post->ID, 'is_wp_suggestion', true );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        $template                 = new WP_Block_Template();
</span><span class="cx" style="display: block; padding: 0 10px">        $template->wp_id          = $post->ID;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $template->id             = $theme . '//' . $post->post_name;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $template->id             = $theme . '//' . $parent_post->post_name;
</ins><span class="cx" style="display: block; padding: 0 10px">         $template->theme          = $theme;
</span><span class="cx" style="display: block; padding: 0 10px">        $template->content        = $post->post_content;
</span><span class="cx" style="display: block; padding: 0 10px">        $template->slug           = $post->post_name;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -765,16 +773,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">        $template->author         = $post->post_author;
</span><span class="cx" style="display: block; padding: 0 10px">        $template->modified       = $post->post_modified;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        if ( 'wp_template' === $post->post_type && $has_theme_file && isset( $template_file['postTypes'] ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( 'wp_template' === $parent_post->post_type && $has_theme_file && isset( $template_file['postTypes'] ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 $template->post_types = $template_file['postTypes'];
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        if ( 'wp_template' === $post->post_type && isset( $default_template_types[ $template->slug ] ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( 'wp_template' === $parent_post->post_type && isset( $default_template_types[ $template->slug ] ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 $template->is_custom = false;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        if ( 'wp_template_part' === $post->post_type ) {
-               $type_terms = get_the_terms( $post, 'wp_template_part_area' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( 'wp_template_part' === $parent_post->post_type ) {
+               $type_terms = get_the_terms( $parent_post, 'wp_template_part_area' );
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( ! is_wp_error( $type_terms ) && false !== $type_terms ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $template->area = $type_terms[0]->name;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -781,7 +789,7 @@
</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">        // Check for a block template without a description and title or with a title equal to the slug.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        if ( 'wp_template' === $post->post_type && empty( $template->description ) && ( empty( $template->title ) || $template->title === $template->slug ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( 'wp_template' === $parent_post->post_type && empty( $template->description ) && ( empty( $template->title ) || $template->title === $template->slug ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 $matches = array();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Check for a block template for a single author, page, post, tag, category, custom post type, or custom taxonomy.
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpposttypephp"></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-post-type.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-post-type.php      2023-10-10 13:19:42 UTC (rev 56818)
+++ trunk/src/wp-includes/class-wp-post-type.php        2023-10-10 14:03:03 UTC (rev 56819)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -397,6 +397,54 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public $rest_controller;
</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">+         * The controller for this post type's revisions REST API endpoints.
+        *
+        * Custom controllers must extend WP_REST_Controller.
+        *
+        * @since 6.4.0
+        * @var string|bool $revisions_rest_controller_class
+        */
+       public $revisions_rest_controller_class;
+
+       /**
+        * The controller instance for this post type's revisions REST API endpoints.
+        *
+        * Lazily computed. Should be accessed using {@see WP_Post_Type::get_revisions_rest_controller()}.
+        *
+        * @since 6.4.0
+        * @var WP_REST_Controller $revisions_rest_controller
+        */
+       public $revisions_rest_controller;
+
+       /**
+        * The controller for this post type's autosave REST API endpoints.
+        *
+        * Custom controllers must extend WP_REST_Controller.
+        *
+        * @since 6.4.0
+        * @var string|bool $autosave_rest_controller_class
+        */
+       public $autosave_rest_controller_class;
+
+       /**
+        * The controller instance for this post type's autosave REST API endpoints.
+        *
+        * Lazily computed. Should be accessed using {@see WP_Post_Type::get_autosave_rest_controller()}.
+        *
+        * @since 6.4.0
+        * @var WP_REST_Controller $autosave_rest_controller
+        */
+       public $autosave_rest_controller;
+
+       /**
+        * A flag to register the post type REST API controller after its associated autosave / revisions controllers, instead of before. Registration order affects route matching priority.
+        *
+        * @since 6.4.0
+        * @var bool $late_route_registration
+        */
+       public $late_route_registration;
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Constructor.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * See the register_post_type() function for accepted arguments for `$args`.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -455,6 +503,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 *  - `register_page_post_type_args`
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><span class="cx" style="display: block; padding: 0 10px">                 * @since 6.0.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 * @since 6.4.0 Added `late_route_registration`, `autosave_rest_controller_class` and `revisions_rest_controller_class` arguments.
</ins><span class="cx" style="display: block; padding: 0 10px">                  *
</span><span class="cx" style="display: block; padding: 0 10px">                 * @param array  $args      Array of arguments for registering a post type.
</span><span class="cx" style="display: block; padding: 0 10px">                 *                          See the register_post_type() function for accepted arguments.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -466,37 +515,40 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Args prefixed with an underscore are reserved for internal use.
</span><span class="cx" style="display: block; padding: 0 10px">                $defaults = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'labels'                => array(),
-                       'description'           => '',
-                       'public'                => false,
-                       'hierarchical'          => false,
-                       'exclude_from_search'   => null,
-                       'publicly_queryable'    => null,
-                       'show_ui'               => null,
-                       'show_in_menu'          => null,
-                       'show_in_nav_menus'     => null,
-                       'show_in_admin_bar'     => null,
-                       'menu_position'         => null,
-                       'menu_icon'             => null,
-                       'capability_type'       => 'post',
-                       'capabilities'          => array(),
-                       'map_meta_cap'          => null,
-                       'supports'              => array(),
-                       'register_meta_box_cb'  => null,
-                       'taxonomies'            => array(),
-                       'has_archive'           => false,
-                       'rewrite'               => true,
-                       'query_var'             => true,
-                       'can_export'            => true,
-                       'delete_with_user'      => null,
-                       'show_in_rest'          => false,
-                       'rest_base'             => false,
-                       'rest_namespace'        => false,
-                       'rest_controller_class' => false,
-                       'template'              => array(),
-                       'template_lock'         => false,
-                       '_builtin'              => false,
-                       '_edit_link'            => 'post.php?post=%d',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'labels'                          => array(),
+                       'description'                     => '',
+                       'public'                          => false,
+                       'hierarchical'                    => false,
+                       'exclude_from_search'             => null,
+                       'publicly_queryable'              => null,
+                       'show_ui'                         => null,
+                       'show_in_menu'                    => null,
+                       'show_in_nav_menus'               => null,
+                       'show_in_admin_bar'               => null,
+                       'menu_position'                   => null,
+                       'menu_icon'                       => null,
+                       'capability_type'                 => 'post',
+                       'capabilities'                    => array(),
+                       'map_meta_cap'                    => null,
+                       'supports'                        => array(),
+                       'register_meta_box_cb'            => null,
+                       'taxonomies'                      => array(),
+                       'has_archive'                     => false,
+                       'rewrite'                         => true,
+                       'query_var'                       => true,
+                       'can_export'                      => true,
+                       'delete_with_user'                => null,
+                       'show_in_rest'                    => false,
+                       'rest_base'                       => false,
+                       'rest_namespace'                  => false,
+                       'rest_controller_class'           => false,
+                       'autosave_rest_controller_class'  => false,
+                       'revisions_rest_controller_class' => false,
+                       'late_route_registration'         => false,
+                       'template'                        => array(),
+                       'template_lock'                   => false,
+                       '_builtin'                        => false,
+                       '_edit_link'                      => 'post.php?post=%d',
</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">                $args = array_merge( $defaults, $args );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -817,6 +869,85 @@
</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">+         * Gets the REST API revisions controller for this post type.
+        *
+        * Will only instantiate the controller class once per request.
+        *
+        * @since 6.4.0
+        *
+        * @return WP_REST_Controller|null The controller instance, or null if the post type
+        *                                 is set not to show in rest.
+        */
+       public function get_revisions_rest_controller() {
+               if ( ! $this->show_in_rest ) {
+                       return null;
+               }
+
+               if ( ! post_type_supports( $this->name, 'revisions' ) ) {
+                       return null;
+               }
+
+               $class = $this->revisions_rest_controller_class ? $this->revisions_rest_controller_class : WP_REST_Revisions_Controller::class;
+               if ( ! class_exists( $class ) ) {
+                       return null;
+               }
+
+               if ( ! is_subclass_of( $class, WP_REST_Controller::class ) ) {
+                       return null;
+               }
+
+               if ( ! $this->revisions_rest_controller ) {
+                       $this->revisions_rest_controller = new $class( $this->name );
+               }
+
+               if ( ! ( $this->revisions_rest_controller instanceof $class ) ) {
+                       return null;
+               }
+
+               return $this->revisions_rest_controller;
+       }
+
+       /**
+        * Gets the REST API autosave controller for this post type.
+        *
+        * Will only instantiate the controller class once per request.
+        *
+        * @since 6.4.0
+        *
+        * @return WP_REST_Controller|null The controller instance, or null if the post type
+        *                                 is set not to show in rest.
+        */
+       public function get_autosave_rest_controller() {
+               if ( ! $this->show_in_rest ) {
+                       return null;
+               }
+
+               if ( 'attachment' === $this->name ) {
+                       return null;
+               }
+
+               $class = $this->autosave_rest_controller_class ? $this->autosave_rest_controller_class : WP_REST_Autosaves_Controller::class;
+
+               if ( ! class_exists( $class ) ) {
+                       return null;
+               }
+
+               if ( ! is_subclass_of( $class, WP_REST_Controller::class ) ) {
+                       return null;
+               }
+
+               if ( ! $this->autosave_rest_controller ) {
+                       $this->autosave_rest_controller = new $class( $this->name );
+               }
+
+               if ( ! ( $this->autosave_rest_controller instanceof $class ) ) {
+                       return null;
+               }
+
+               return $this->autosave_rest_controller;
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Returns the default labels for post types.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 6.0.0
</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    2023-10-10 13:19:42 UTC (rev 56818)
+++ trunk/src/wp-includes/post.php      2023-10-10 14:03:03 UTC (rev 56819)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -346,7 +346,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        register_post_type(
</span><span class="cx" style="display: block; padding: 0 10px">                'wp_template',
</span><span class="cx" style="display: block; padding: 0 10px">                array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'labels'                => array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'labels'                          => array(
</ins><span class="cx" style="display: block; padding: 0 10px">                                 'name'                  => _x( 'Templates', 'post type general name' ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'singular_name'         => _x( 'Template', 'post type singular name' ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'add_new'               => __( 'Add New Template' ),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -366,19 +366,22 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'items_list_navigation' => __( 'Templates list navigation' ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'items_list'            => __( 'Templates list' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'description'           => __( 'Templates to include in your theme.' ),
-                       'public'                => false,
-                       '_builtin'              => true, /* internal use only. don't use this when registering your own post type. */
-                       '_edit_link'            => $template_edit_link, /* internal use only. don't use this when registering your own post type. */
-                       'has_archive'           => false,
-                       'show_ui'               => false,
-                       'show_in_menu'          => false,
-                       'show_in_rest'          => true,
-                       'rewrite'               => false,
-                       'rest_base'             => 'templates',
-                       'rest_controller_class' => 'WP_REST_Templates_Controller',
-                       'capability_type'       => array( 'template', 'templates' ),
-                       'capabilities'          => array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'description'                     => __( 'Templates to include in your theme.' ),
+                       'public'                          => false,
+                       '_builtin'                        => true, /* internal use only. don't use this when registering your own post type. */
+                       '_edit_link'                      => $template_edit_link, /* internal use only. don't use this when registering your own post type. */
+                       'has_archive'                     => false,
+                       'show_ui'                         => false,
+                       'show_in_menu'                    => false,
+                       'show_in_rest'                    => true,
+                       'rewrite'                         => false,
+                       'rest_base'                       => 'templates',
+                       'rest_controller_class'           => 'WP_REST_Templates_Controller',
+                       'autosave_rest_controller_class'  => 'WP_REST_Template_Autosaves_Controller',
+                       'revisions_rest_controller_class' => 'WP_REST_Template_Revisions_Controller',
+                       'late_route_registration'         => true,
+                       'capability_type'                 => array( 'template', 'templates' ),
+                       'capabilities'                    => array(
</ins><span class="cx" style="display: block; padding: 0 10px">                                 'create_posts'           => 'edit_theme_options',
</span><span class="cx" style="display: block; padding: 0 10px">                                'delete_posts'           => 'edit_theme_options',
</span><span class="cx" style="display: block; padding: 0 10px">                                'delete_others_posts'    => 'edit_theme_options',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -392,8 +395,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'read'                   => 'edit_theme_options',
</span><span class="cx" style="display: block; padding: 0 10px">                                'read_private_posts'     => 'edit_theme_options',
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'map_meta_cap'          => true,
-                       'supports'              => array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'map_meta_cap'                    => true,
+                       'supports'                        => array(
</ins><span class="cx" style="display: block; padding: 0 10px">                                 'title',
</span><span class="cx" style="display: block; padding: 0 10px">                                'slug',
</span><span class="cx" style="display: block; padding: 0 10px">                                'excerpt',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -407,7 +410,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        register_post_type(
</span><span class="cx" style="display: block; padding: 0 10px">                'wp_template_part',
</span><span class="cx" style="display: block; padding: 0 10px">                array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'labels'                => array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'labels'                          => array(
</ins><span class="cx" style="display: block; padding: 0 10px">                                 'name'                  => _x( 'Template Parts', 'post type general name' ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'singular_name'         => _x( 'Template Part', 'post type singular name' ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'add_new'               => __( 'Add New Template Part' ),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -427,19 +430,22 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'items_list_navigation' => __( 'Template parts list navigation' ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'items_list'            => __( 'Template parts list' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'description'           => __( 'Template parts to include in your templates.' ),
-                       'public'                => false,
-                       '_builtin'              => true, /* internal use only. don't use this when registering your own post type. */
-                       '_edit_link'            => $template_edit_link, /* internal use only. don't use this when registering your own post type. */
-                       'has_archive'           => false,
-                       'show_ui'               => false,
-                       'show_in_menu'          => false,
-                       'show_in_rest'          => true,
-                       'rewrite'               => false,
-                       'rest_base'             => 'template-parts',
-                       'rest_controller_class' => 'WP_REST_Templates_Controller',
-                       'map_meta_cap'          => true,
-                       'capabilities'          => array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'description'                     => __( 'Template parts to include in your templates.' ),
+                       'public'                          => false,
+                       '_builtin'                        => true, /* internal use only. don't use this when registering your own post type. */
+                       '_edit_link'                      => $template_edit_link, /* internal use only. don't use this when registering your own post type. */
+                       'has_archive'                     => false,
+                       'show_ui'                         => false,
+                       'show_in_menu'                    => false,
+                       'show_in_rest'                    => true,
+                       'rewrite'                         => false,
+                       'rest_base'                       => 'template-parts',
+                       'rest_controller_class'           => 'WP_REST_Templates_Controller',
+                       'autosave_rest_controller_class'  => 'WP_REST_Template_Autosaves_Controller',
+                       'revisions_rest_controller_class' => 'WP_REST_Template_Revisions_Controller',
+                       'late_route_registration'         => true,
+                       'map_meta_cap'                    => true,
+                       'capabilities'                    => array(
</ins><span class="cx" style="display: block; padding: 0 10px">                                 'create_posts'           => 'edit_theme_options',
</span><span class="cx" style="display: block; padding: 0 10px">                                'delete_posts'           => 'edit_theme_options',
</span><span class="cx" style="display: block; padding: 0 10px">                                'delete_others_posts'    => 'edit_theme_options',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -453,7 +459,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'read'                   => 'edit_theme_options',
</span><span class="cx" style="display: block; padding: 0 10px">                                'read_private_posts'     => 'edit_theme_options',
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'supports'              => array(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'supports'                        => array(
</ins><span class="cx" style="display: block; padding: 0 10px">                                 'title',
</span><span class="cx" style="display: block; padding: 0 10px">                                'slug',
</span><span class="cx" style="display: block; padding: 0 10px">                                'excerpt',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1575,85 +1581,88 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * @param array|string $args {
</span><span class="cx" style="display: block; padding: 0 10px">  *     Array or string of arguments for registering a post type.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- *     @type string       $label                 Name of the post type shown in the menu. Usually plural.
- *                                               Default is value of $labels['name'].
- *     @type string[]     $labels                An array of labels for this post type. If not set, post
- *                                               labels are inherited for non-hierarchical types and page
- *                                               labels for hierarchical ones. See get_post_type_labels() for a full
- *                                               list of supported labels.
- *     @type string       $description           A short descriptive summary of what the post type is.
- *                                               Default empty.
- *     @type bool         $public                Whether a post type is intended for use publicly either via
- *                                               the admin interface or by front-end users. While the default
- *                                               settings of $exclude_from_search, $publicly_queryable, $show_ui,
- *                                               and $show_in_nav_menus are inherited from $public, each does not
- *                                               rely on this relationship and controls a very specific intention.
- *                                               Default false.
- *     @type bool         $hierarchical          Whether the post type is hierarchical (e.g. page). Default false.
- *     @type bool         $exclude_from_search   Whether to exclude posts with this post type from front end search
- *                                               results. Default is the opposite value of $public.
- *     @type bool         $publicly_queryable    Whether queries can be performed on the front end for the post type
- *                                               as part of parse_request(). Endpoints would include:
- *                                               * ?post_type={post_type_key}
- *                                               * ?{post_type_key}={single_post_slug}
- *                                               * ?{post_type_query_var}={single_post_slug}
- *                                               If not set, the default is inherited from $public.
- *     @type bool         $show_ui               Whether to generate and allow a UI for managing this post type in the
- *                                               admin. Default is value of $public.
- *     @type bool|string  $show_in_menu          Where to show the post type in the admin menu. To work, $show_ui
- *                                               must be true. If true, the post type is shown in its own top level
- *                                               menu. If false, no menu is shown. If a string of an existing top
- *                                               level menu ('tools.php' or 'edit.php?post_type=page', for example), the
- *                                               post type will be placed as a sub-menu of that.
- *                                               Default is value of $show_ui.
- *     @type bool         $show_in_nav_menus     Makes this post type available for selection in navigation menus.
- *                                               Default is value of $public.
- *     @type bool         $show_in_admin_bar     Makes this post type available via the admin bar. Default is value
- *                                               of $show_in_menu.
- *     @type bool         $show_in_rest          Whether to include the post type in the REST API. Set this to true
- *                                               for the post type to be available in the block editor.
- *     @type string       $rest_base             To change the base URL of REST API route. Default is $post_type.
- *     @type string       $rest_namespace        To change the namespace URL of REST API route. Default is wp/v2.
- *     @type string       $rest_controller_class REST API controller class name. Default is 'WP_REST_Posts_Controller'.
- *     @type int          $menu_position         The position in the menu order the post type should appear. To work,
- *                                               $show_in_menu must be true. Default null (at the bottom).
- *     @type string       $menu_icon             The URL to the icon to be used for this menu. Pass a base64-encoded
- *                                               SVG using a data URI, which will be colored to match the color scheme
- *                                               -- this should begin with 'data:image/svg+xml;base64,'. Pass the name
- *                                               of a Dashicons helper class to use a font icon, e.g.
- *                                               'dashicons-chart-pie'. Pass 'none' to leave div.wp-menu-image empty
- *                                               so an icon can be added via CSS. Defaults to use the posts icon.
- *     @type string|array $capability_type       The string to use to build the read, edit, and delete capabilities.
- *                                               May be passed as an array to allow for alternative plurals when using
- *                                               this argument as a base to construct the capabilities, e.g.
- *                                               array('story', 'stories'). Default 'post'.
- *     @type string[]     $capabilities          Array of capabilities for this post type. $capability_type is used
- *                                               as a base to construct capabilities by default.
- *                                               See get_post_type_capabilities().
- *     @type bool         $map_meta_cap          Whether to use the internal default meta capability handling.
- *                                               Default false.
- *     @type array        $supports              Core feature(s) the post type supports. Serves as an alias for calling
- *                                               add_post_type_support() directly. Core features include 'title',
- *                                               'editor', 'comments', 'revisions', 'trackbacks', 'author', 'excerpt',
- *                                               'page-attributes', 'thumbnail', 'custom-fields', and 'post-formats'.
- *                                               Additionally, the 'revisions' feature dictates whether the post type
- *                                               will store revisions, and the 'comments' feature dictates whether the
- *                                               comments count will show on the edit screen. A feature can also be
- *                                               specified as an array of arguments to provide additional information
- *                                               about supporting that feature.
- *                                               Example: `array( 'my_feature', array( 'field' => 'value' ) )`.
- *                                               Default is an array containing 'title' and 'editor'.
- *     @type callable     $register_meta_box_cb  Provide a callback function that sets up the meta boxes for the
- *                                               edit form. Do remove_meta_box() and add_meta_box() calls in the
- *                                               callback. Default null.
- *     @type string[]     $taxonomies            An array of taxonomy identifiers that will be registered for the
- *                                               post type. Taxonomies can be registered later with register_taxonomy()
- *                                               or register_taxonomy_for_object_type().
- *                                               Default empty array.
- *     @type bool|string  $has_archive           Whether there should be post type archives, or if a string, the
- *                                               archive slug to use. Will generate the proper rewrite rules if
- *                                               $rewrite is enabled. Default false.
- *     @type bool|array   $rewrite               {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ *     @type string       $label                           Name of the post type shown in the menu. Usually plural.
+ *                                                         Default is value of $labels['name'].
+ *     @type string[]     $labels                          An array of labels for this post type. If not set, post
+ *                                                         labels are inherited for non-hierarchical types and page
+ *                                                         labels for hierarchical ones. See get_post_type_labels() for a full
+ *                                                         list of supported labels.
+ *     @type string       $description                     A short descriptive summary of what the post type is.
+ *                                                         Default empty.
+ *     @type bool         $public                          Whether a post type is intended for use publicly either via
+ *                                                         the admin interface or by front-end users. While the default
+ *                                                         settings of $exclude_from_search, $publicly_queryable, $show_ui,
+ *                                                         and $show_in_nav_menus are inherited from $public, each does not
+ *                                                         rely on this relationship and controls a very specific intention.
+ *                                                         Default false.
+ *     @type bool         $hierarchical                    Whether the post type is hierarchical (e.g. page). Default false.
+ *     @type bool         $exclude_from_search             Whether to exclude posts with this post type from front end search
+ *                                                         results. Default is the opposite value of $public.
+ *     @type bool         $publicly_queryable              Whether queries can be performed on the front end for the post type
+ *                                                         as part of parse_request(). Endpoints would include:
+ *                                                          * ?post_type={post_type_key}
+ *                                                          * ?{post_type_key}={single_post_slug}
+ *                                                          * ?{post_type_query_var}={single_post_slug}
+ *                                                         If not set, the default is inherited from $public.
+ *     @type bool         $show_ui                         Whether to generate and allow a UI for managing this post type in the
+ *                                                         admin. Default is value of $public.
+ *     @type bool|string  $show_in_menu                    Where to show the post type in the admin menu. To work, $show_ui
+ *                                                         must be true. If true, the post type is shown in its own top level
+ *                                                         menu. If false, no menu is shown. If a string of an existing top
+ *                                                         level menu ('tools.php' or 'edit.php?post_type=page', for example), the
+ *                                                         post type will be placed as a sub-menu of that.
+ *                                                         Default is value of $show_ui.
+ *     @type bool         $show_in_nav_menus               Makes this post type available for selection in navigation menus.
+ *                                                         Default is value of $public.
+ *     @type bool         $show_in_admin_bar               Makes this post type available via the admin bar. Default is value
+ *                                                         of $show_in_menu.
+ *     @type bool         $show_in_rest                    Whether to include the post type in the REST API. Set this to true
+ *                                                         for the post type to be available in the block editor.
+ *     @type string       $rest_base                       To change the base URL of REST API route. Default is $post_type.
+ *     @type string       $rest_namespace                  To change the namespace URL of REST API route. Default is wp/v2.
+ *     @type string       $rest_controller_class           REST API controller class name. Default is 'WP_REST_Posts_Controller'.
+ *     @type string|bool  $autosave_rest_controller_class  REST API controller class name. Default is 'WP_REST_Autosaves_Controller'.
+ *     @type string|bool  $revisions_rest_controller_class REST API controller class name. Default is 'WP_REST_Revisions_Controller'.
+ *     @type bool         $late_route_registration         A flag to direct the REST API controllers for autosave / revisions should be registered before/after the post type controller.
+ *     @type int          $menu_position                   The position in the menu order the post type should appear. To work,
+ *                                                         $show_in_menu must be true. Default null (at the bottom).
+ *     @type string       $menu_icon                       The URL to the icon to be used for this menu. Pass a base64-encoded
+ *                                                         SVG using a data URI, which will be colored to match the color scheme
+ *                                                         -- this should begin with 'data:image/svg+xml;base64,'. Pass the name
+ *                                                         of a Dashicons helper class to use a font icon, e.g.
+ *                                                        'dashicons-chart-pie'. Pass 'none' to leave div.wp-menu-image empty
+ *                                                         so an icon can be added via CSS. Defaults to use the posts icon.
+ *     @type string|array $capability_type                 The string to use to build the read, edit, and delete capabilities.
+ *                                                         May be passed as an array to allow for alternative plurals when using
+ *                                                         this argument as a base to construct the capabilities, e.g.
+ *                                                         array('story', 'stories'). Default 'post'.
+ *     @type string[]     $capabilities                    Array of capabilities for this post type. $capability_type is used
+ *                                                         as a base to construct capabilities by default.
+ *                                                         See get_post_type_capabilities().
+ *     @type bool         $map_meta_cap                    Whether to use the internal default meta capability handling.
+ *                                                         Default false.
+ *     @type array        $supports                        Core feature(s) the post type supports. Serves as an alias for calling
+ *                                                         add_post_type_support() directly. Core features include 'title',
+ *                                                         'editor', 'comments', 'revisions', 'trackbacks', 'author', 'excerpt',
+ *                                                         'page-attributes', 'thumbnail', 'custom-fields', and 'post-formats'.
+ *                                                         Additionally, the 'revisions' feature dictates whether the post type
+ *                                                         will store revisions, and the 'comments' feature dictates whether the
+ *                                                         comments count will show on the edit screen. A feature can also be
+ *                                                         specified as an array of arguments to provide additional information
+ *                                                         about supporting that feature.
+ *                                                         Example: `array( 'my_feature', array( 'field' => 'value' ) )`.
+ *                                                         Default is an array containing 'title' and 'editor'.
+ *     @type callable     $register_meta_box_cb            Provide a callback function that sets up the meta boxes for the
+ *                                                         edit form. Do remove_meta_box() and add_meta_box() calls in the
+ *                                                         callback. Default null.
+ *     @type string[]     $taxonomies                      An array of taxonomy identifiers that will be registered for the
+ *                                                         post type. Taxonomies can be registered later with register_taxonomy()
+ *                                                         or register_taxonomy_for_object_type().
+ *                                                         Default empty array.
+ *     @type bool|string  $has_archive                     Whether there should be post type archives, or if a string, the
+ *                                                         archive slug to use. Will generate the proper rewrite rules if
+ *                                                         $rewrite is enabled. Default false.
+ *     @type bool|array   $rewrite                         {
</ins><span class="cx" style="display: block; padding: 0 10px">  *         Triggers the handling of rewrites for this post type. To prevent rewrite, set to false.
</span><span class="cx" style="display: block; padding: 0 10px">  *         Defaults to true, using $post_type as slug. To specify rewrite rules, an array can be
</span><span class="cx" style="display: block; padding: 0 10px">  *         passed with any of these keys:
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1668,32 +1677,32 @@
</span><span class="cx" style="display: block; padding: 0 10px">  *                                  inherits from $permalink_epmask. If not specified and permalink_epmask
</span><span class="cx" style="display: block; padding: 0 10px">  *                                  is not set, defaults to EP_PERMALINK.
</span><span class="cx" style="display: block; padding: 0 10px">  *     }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- *     @type string|bool  $query_var             Sets the query_var key for this post type. Defaults to $post_type
- *                                               key. If false, a post type cannot be loaded at
- *                                               ?{query_var}={post_slug}. If specified as a string, the query
- *                                               ?{query_var_string}={post_slug} will be valid.
- *     @type bool         $can_export            Whether to allow this post type to be exported. Default true.
- *     @type bool         $delete_with_user      Whether to delete posts of this type when deleting a user.
- *                                               * If true, posts of this type belonging to the user will be moved
- *                                                 to Trash when the user is deleted.
- *                                               * If false, posts of this type belonging to the user will *not*
- *                                                 be trashed or deleted.
- *                                               * If not set (the default), posts are trashed if post type supports
- *                                                 the 'author' feature. Otherwise posts are not trashed or deleted.
- *                                               Default null.
- *     @type array        $template              Array of blocks to use as the default initial state for an editor
- *                                               session. Each item should be an array containing block name and
- *                                               optional attributes. Default empty array.
- *     @type string|false $template_lock         Whether the block template should be locked if $template is set.
- *                                               * If set to 'all', the user is unable to insert new blocks,
- *                                                 move existing blocks and delete blocks.
- *                                               * If set to 'insert', the user is able to move existing blocks
- *                                                 but is unable to insert new blocks and delete blocks.
- *                                               Default false.
- *     @type bool         $_builtin              FOR INTERNAL USE ONLY! True if this post type is a native or
- *                                               "built-in" post_type. Default false.
- *     @type string       $_edit_link            FOR INTERNAL USE ONLY! URL segment to use for edit link of
- *                                               this post type. Default 'post.php?post=%d'.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ *     @type string|bool  $query_var                      Sets the query_var key for this post type. Defaults to $post_type
+ *                                                        key. If false, a post type cannot be loaded at
+ *                                                        ?{query_var}={post_slug}. If specified as a string, the query
+ *                                                        ?{query_var_string}={post_slug} will be valid.
+ *     @type bool         $can_export                     Whether to allow this post type to be exported. Default true.
+ *     @type bool         $delete_with_user               Whether to delete posts of this type when deleting a user.
+ *                                                          * If true, posts of this type belonging to the user will be moved
+ *                                                            to Trash when the user is deleted.
+ *                                                          * If false, posts of this type belonging to the user will *not*
+ *                                                            be trashed or deleted.
+ *                                                          * If not set (the default), posts are trashed if post type supports
+ *                                                            the 'author' feature. Otherwise posts are not trashed or deleted.
+ *                                                        Default null.
+ *     @type array        $template                       Array of blocks to use as the default initial state for an editor
+ *                                                        session. Each item should be an array containing block name and
+ *                                                        optional attributes. Default empty array.
+ *     @type string|false $template_lock                  Whether the block template should be locked if $template is set.
+ *                                                        * If set to 'all', the user is unable to insert new blocks,
+ *                                                          move existing blocks and delete blocks.
+ *                                                       * If set to 'insert', the user is able to move existing blocks
+ *                                                         but is unable to insert new blocks and delete blocks.
+ *                                                         Default false.
+ *     @type bool         $_builtin                     FOR INTERNAL USE ONLY! True if this post type is a native or
+ *                                                      "built-in" post_type. Default false.
+ *     @type string       $_edit_link                   FOR INTERNAL USE ONLY! URL segment to use for edit link of
+ *                                                      this post type. Default 'post.php?post=%d'.
</ins><span class="cx" style="display: block; padding: 0 10px">  * }
</span><span class="cx" style="display: block; padding: 0 10px">  * @return WP_Post_Type|WP_Error The registered post type object on success,
</span><span class="cx" style="display: block; padding: 0 10px">  *                               WP_Error object on failure.
</span></span></pre></div>
<a id="trunksrcwpincludesrestapiendpointsclasswprestautosavescontrollerphp"></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/rest-api/endpoints/class-wp-rest-autosaves-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-autosaves-controller.php   2023-10-10 13:19:42 UTC (rev 56818)
+++ trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php     2023-10-10 14:03:03 UTC (rev 56819)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -65,8 +65,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $parent_controller = new WP_REST_Posts_Controller( $parent_post_type );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->parent_controller    = $parent_controller;
-               $this->revisions_controller = new WP_REST_Revisions_Controller( $parent_post_type );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->parent_controller = $parent_controller;
+
+               $revisions_controller = $post_type_object->get_revisions_rest_controller();
+               if ( ! $revisions_controller ) {
+                       $revisions_controller = new WP_REST_Revisions_Controller( $parent_post_type );
+               }
+               $this->revisions_controller = $revisions_controller;
</ins><span class="cx" style="display: block; padding: 0 10px">                 $this->rest_base            = 'autosaves';
</span><span class="cx" style="display: block; padding: 0 10px">                $this->parent_base          = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
</span><span class="cx" style="display: block; padding: 0 10px">                $this->namespace            = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -205,11 +210,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function create_item( $request ) {
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( ! defined( 'DOING_AUTOSAVE' ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( ! defined( 'WP_RUN_CORE_TESTS' ) && ! defined( 'DOING_AUTOSAVE' ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         define( 'DOING_AUTOSAVE', true );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $post = get_post( $request['id'] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $post = $this->get_parent( $request['id'] );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( is_wp_error( $post ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        return $post;
</span></span></pre></div>
<a id="trunksrcwpincludesrestapiendpointsclasswpresttemplateautosavescontrollerphp"></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-template-autosaves-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-template-autosaves-controller.php                          (rev 0)
+++ trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-template-autosaves-controller.php    2023-10-10 14:03:03 UTC (rev 56819)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,276 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * REST API: WP_REST_Template_Autosaves_Controller class.
+ *
+ * @package WordPress
+ * @subpackage REST_API
+ * @since 6.4.0
+ */
+
+/**
+ * Core class used to access template autosaves via the REST API.
+ *
+ * @since 6.4.0
+ *
+ * @see WP_REST_Autosaves_Controller
+ */
+class WP_REST_Template_Autosaves_Controller extends WP_REST_Autosaves_Controller {
+       /**
+        * Parent post type.
+        *
+        * @since 6.4.0
+        * @var string
+        */
+       private $parent_post_type;
+
+       /**
+        * Parent post controller.
+        *
+        * @since 6.4.0
+        * @var WP_REST_Controller
+        */
+       private $parent_controller;
+
+       /**
+        * Revision controller.
+        *
+        * @since 6.4.0
+        * @var WP_REST_Revisions_Controller
+        */
+       private $revisions_controller;
+
+       /**
+        * The base of the parent controller's route.
+        *
+        * @since 6.4.0
+        * @var string
+        */
+       private $parent_base;
+
+       /**
+        * Constructor.
+        *
+        * @since 6.4.0
+        *
+        * @param string $parent_post_type Post type of the parent.
+        */
+       public function __construct( $parent_post_type ) {
+               parent::__construct( $parent_post_type );
+               $this->parent_post_type = $parent_post_type;
+               $post_type_object       = get_post_type_object( $parent_post_type );
+               $parent_controller      = $post_type_object->get_rest_controller();
+
+               if ( ! $parent_controller ) {
+                       $parent_controller = new WP_REST_Templates_Controller( $parent_post_type );
+               }
+
+               $this->parent_controller = $parent_controller;
+
+               $revisions_controller = $post_type_object->get_revisions_rest_controller();
+               if ( ! $revisions_controller ) {
+                       $revisions_controller = new WP_REST_Revisions_Controller( $parent_post_type );
+               }
+               $this->revisions_controller = $revisions_controller;
+               $this->rest_base            = 'autosaves';
+               $this->parent_base          = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
+               $this->namespace            = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2';
+       }
+
+       /**
+        * Registers the routes for autosaves.
+        *
+        * @since 6.4.0
+        *
+        * @see register_rest_route()
+        */
+       public function register_routes() {
+               register_rest_route(
+                       $this->namespace,
+                       sprintf(
+                               '/%s/(?P<id>%s%s)/%s',
+                               $this->parent_base,
+                               /*
+                                * Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
+                                * Excludes invalid directory name characters: `/:<>*?"|`.
+                                */
+                               '([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
+                               // Matches the template name.
+                               '[\/\w%-]+',
+                               $this->rest_base
+                       ),
+                       array(
+                               'args'   => array(
+                                       'id' => array(
+                                               'description'       => __( 'The id of a template' ),
+                                               'type'              => 'string',
+                                               'sanitize_callback' => array( $this->parent_controller, '_sanitize_template_id' ),
+                                       ),
+                               ),
+                               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->parent_controller->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
+                               ),
+                               'schema' => array( $this, 'get_public_item_schema' ),
+                       )
+               );
+
+               register_rest_route(
+                       $this->namespace,
+                       sprintf(
+                               '/%s/(?P<parent>%s%s)/%s/%s',
+                               $this->parent_base,
+                               /*
+                                * Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
+                                * Excludes invalid directory name characters: `/:<>*?"|`.
+                                */
+                               '([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
+                               // Matches the template name.
+                               '[\/\w%-]+',
+                               $this->rest_base,
+                               '(?P<id>[\d]+)'
+                       ),
+                       array(
+                               'args'   => array(
+                                       'parent' => array(
+                                               'description'       => __( 'The id of a template' ),
+                                               'type'              => 'string',
+                                               'sanitize_callback' => array( $this->parent_controller, '_sanitize_template_id' ),
+                                       ),
+                                       'id'     => array(
+                                               'description' => __( 'The ID for the autosave.' ),
+                                               'type'        => 'integer',
+                                       ),
+                               ),
+                               array(
+                                       'methods'             => WP_REST_Server::READABLE,
+                                       'callback'            => array( $this, 'get_item' ),
+                                       'permission_callback' => array( $this->revisions_controller, 'get_item_permissions_check' ),
+                                       'args'                => array(
+                                               'context' => $this->get_context_param( array( 'default' => 'view' ) ),
+                                       ),
+                               ),
+                               'schema' => array( $this, 'get_public_item_schema' ),
+                       )
+               );
+       }
+
+       /**
+        * Prepares the item for the REST response.
+        *
+        * @since 6.4.0
+        *
+        * @param WP_Post         $item    Post revision object.
+        * @param WP_REST_Request $request Request object.
+        * @return WP_REST_Response Response object.
+        */
+       public function prepare_item_for_response( $item, $request ) {
+               $template = _build_block_template_result_from_post( $item );
+               $response = $this->parent_controller->prepare_item_for_response( $template, $request );
+
+               $fields = $this->get_fields_for_response( $request );
+               $data   = $response->get_data();
+
+               if ( in_array( 'parent', $fields, true ) ) {
+                       $data['parent'] = (int) $item->post_parent;
+               }
+
+               $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
+               $data    = $this->filter_response_by_context( $data, $context );
+
+               // Wrap the data in a response object.
+               $response = new WP_REST_Response( $data );
+
+               if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
+                       $links = $this->prepare_links( $template );
+                       $response->add_links( $links );
+               }
+
+               return $response;
+       }
+
+       /**
+        * Gets the autosave, if the ID is valid.
+        *
+        * @since 6.4.0
+        *
+        * @param WP_REST_Request $request Full details about the request.
+        * @return WP_Post|WP_Error Autosave post object if ID is valid, WP_Error otherwise.
+        */
+       public function get_item( $request ) {
+               $parent = $this->get_parent( $request['parent'] );
+               if ( is_wp_error( $parent ) ) {
+                       return $parent;
+               }
+
+               $autosave = wp_get_post_autosave( $parent->ID );
+
+               if ( ! $autosave ) {
+                       return new WP_Error(
+                               'rest_post_no_autosave',
+                               __( 'There is no autosave revision for this template.' ),
+                               array( 'status' => 404 )
+                       );
+               }
+
+               $response = $this->prepare_item_for_response( $autosave, $request );
+               return $response;
+       }
+
+       /**
+        * Get the parent post.
+        *
+        * @since 6.4.0
+        *
+        * @param int $parent_id Supplied ID.
+        * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
+        */
+       protected function get_parent( $parent_id ) {
+               return $this->revisions_controller->get_parent( $parent_id );
+       }
+
+       /**
+        * Prepares links for the request.
+        *
+        * @since 6.4.0
+        *
+        * @param WP_Block_Template $template Template.
+        * @return array Links for the given post.
+        */
+       protected function prepare_links( $template ) {
+               $links = array(
+                       'self'   => array(
+                               'href' => rest_url( sprintf( '/%s/%s/%s/%s/%d', $this->namespace, $this->parent_base, $template->id, $this->rest_base, $template->wp_id ) ),
+                       ),
+                       'parent' => array(
+                               'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->parent_base, $template->id ) ),
+                       ),
+               );
+
+               return $links;
+       }
+
+       /**
+        * Retrieves the autosave's schema, conforming to JSON Schema.
+        *
+        * @since 6.4.0
+        *
+        * @return array Item schema data.
+        */
+       public function get_item_schema() {
+               if ( $this->schema ) {
+                       return $this->add_additional_fields_schema( $this->schema );
+               }
+
+               $this->schema = $this->revisions_controller->get_item_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-template-autosaves-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="trunksrcwpincludesrestapiendpointsclasswpresttemplaterevisionscontrollerphp"></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-template-revisions-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-template-revisions-controller.php                          (rev 0)
+++ trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-template-revisions-controller.php    2023-10-10 14:03:03 UTC (rev 56819)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,297 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * REST API: WP_REST_Template_Revisions_Controller class
+ *
+ * @package WordPress
+ * @subpackage REST_API
+ * @since 6.4.0
+ */
+
+/**
+ * Core class used to access template revisions via the REST API.
+ *
+ * @since 6.4.0
+ *
+ * @see WP_REST_Controller
+ */
+class WP_REST_Template_Revisions_Controller extends WP_REST_Revisions_Controller {
+       /**
+        * Parent post type.
+        *
+        * @since 6.4.0
+        * @var string
+        */
+       private $parent_post_type;
+
+       /**
+        * Parent controller.
+        *
+        * @since 6.4.0
+        * @var WP_REST_Controller
+        */
+       private $parent_controller;
+
+       /**
+        * The base of the parent controller's route.
+        *
+        * @since 6.4.0
+        * @var string
+        */
+       private $parent_base;
+
+       /**
+        * Constructor.
+        *
+        * @since 6.4.0
+        *
+        * @param string $parent_post_type Post type of the parent.
+        */
+       public function __construct( $parent_post_type ) {
+               parent::__construct( $parent_post_type );
+               $this->parent_post_type = $parent_post_type;
+               $post_type_object       = get_post_type_object( $parent_post_type );
+               $parent_controller      = $post_type_object->get_rest_controller();
+
+               if ( ! $parent_controller ) {
+                       $parent_controller = new WP_REST_Templates_Controller( $parent_post_type );
+               }
+
+               $this->parent_controller = $parent_controller;
+               $this->rest_base         = 'revisions';
+               $this->parent_base       = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
+               $this->namespace         = ! empty( $post_type_object->rest_namespace ) ? $post_type_object->rest_namespace : 'wp/v2';
+       }
+
+       /**
+        * Registers the routes for revisions based on post types supporting revisions.
+        *
+        * @since 6.4.0
+        *
+        * @see register_rest_route()
+        */
+       public function register_routes() {
+
+               register_rest_route(
+                       $this->namespace,
+                       sprintf(
+                               '/%s/(?P<parent>%s%s)/%s',
+                               $this->parent_base,
+                               /*
+                                * Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
+                                * Excludes invalid directory name characters: `/:<>*?"|`.
+                                */
+                               '([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
+                               // Matches the template name.
+                               '[\/\w%-]+',
+                               $this->rest_base
+                       ),
+                       array(
+                               'args'   => array(
+                                       'parent' => array(
+                                               'description'       => __( 'The id of a template' ),
+                                               'type'              => 'string',
+                                               'sanitize_callback' => array( $this->parent_controller, '_sanitize_template_id' ),
+                                       ),
+                               ),
+                               array(
+                                       'methods'             => WP_REST_Server::READABLE,
+                                       'callback'            => array( $this, 'get_items' ),
+                                       'permission_callback' => array( $this, 'get_items_permissions_check' ),
+                                       'args'                => $this->get_collection_params(),
+                               ),
+                               'schema' => array( $this, 'get_public_item_schema' ),
+                       )
+               );
+
+               register_rest_route(
+                       $this->namespace,
+                       sprintf(
+                               '/%s/(?P<parent>%s%s)/%s/%s',
+                               $this->parent_base,
+                               /*
+                                * Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
+                                * Excludes invalid directory name characters: `/:<>*?"|`.
+                                */
+                               '([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
+                               // Matches the template name.
+                               '[\/\w%-]+',
+                               $this->rest_base,
+                               '(?P<id>[\d]+)'
+                       ),
+                       array(
+                               'args'   => array(
+                                       'parent' => array(
+                                               'description'       => __( 'The id of a template' ),
+                                               'type'              => 'string',
+                                               'sanitize_callback' => array( $this->parent_controller, '_sanitize_template_id' ),
+                                       ),
+                                       'id'     => array(
+                                               'description' => __( 'Unique identifier for the revision.' ),
+                                               'type'        => 'integer',
+                                       ),
+                               ),
+                               array(
+                                       'methods'             => WP_REST_Server::READABLE,
+                                       'callback'            => array( $this, 'get_item' ),
+                                       'permission_callback' => array( $this, 'get_item_permissions_check' ),
+                                       'args'                => array(
+                                               'context' => $this->get_context_param( array( 'default' => 'view' ) ),
+                                       ),
+                               ),
+                               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' => __( 'Required to be true, as revisions do not support trashing.' ),
+                                               ),
+                                       ),
+                               ),
+                               'schema' => array( $this, 'get_public_item_schema' ),
+                       )
+               );
+       }
+
+       /**
+        * Gets the parent post, if the ID is valid.
+        *
+        * @since 6.4.0
+        *
+        * @param int $parent_post_id Supplied ID.
+        * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise.
+        */
+       protected function get_parent( $parent_post_id ) {
+               $template = get_block_template( $parent_post_id, $this->parent_post_type );
+
+               if ( ! $template ) {
+                       return new WP_Error(
+                               'rest_post_invalid_parent',
+                               __( 'Invalid template parent ID.' ),
+                               array( 'status' => 404 )
+                       );
+               }
+
+               return get_post( $template->wp_id );
+       }
+
+       /**
+        * Prepares the item for the REST response.
+        *
+        * @since 6.4.0
+        *
+        * @param WP_Post         $item    Post revision object.
+        * @param WP_REST_Request $request Request object.
+        * @return WP_REST_Response Response object.
+        */
+       public function prepare_item_for_response( $item, $request ) {
+               $template = _build_block_template_result_from_post( $item );
+               $response = $this->parent_controller->prepare_item_for_response( $template, $request );
+
+               $fields = $this->get_fields_for_response( $request );
+               $data   = $response->get_data();
+
+               if ( in_array( 'parent', $fields, true ) ) {
+                       $data['parent'] = (int) $item->post_parent;
+               }
+
+               $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
+               $data    = $this->filter_response_by_context( $data, $context );
+
+               // Wrap the data in a response object.
+               $response = new WP_REST_Response( $data );
+
+               if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
+                       $links = $this->prepare_links( $template );
+                       $response->add_links( $links );
+               }
+
+               return $response;
+       }
+
+       /**
+        * Checks if a given request has access to delete a revision.
+        *
+        * @since 6.4.0
+        *
+        * @param WP_REST_Request $request Full details about the request.
+        * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
+        */
+       public function delete_item_permissions_check( $request ) {
+               $parent = $this->get_parent( $request['parent'] );
+               if ( is_wp_error( $parent ) ) {
+                       return $parent;
+               }
+
+               if ( ! current_user_can( 'delete_post', $parent->ID ) ) {
+                       return new WP_Error(
+                               'rest_cannot_delete',
+                               __( 'Sorry, you are not allowed to delete revisions of this post.' ),
+                               array( 'status' => rest_authorization_required_code() )
+                       );
+               }
+
+               $revision = $this->get_revision( $request['id'] );
+               if ( is_wp_error( $revision ) ) {
+                       return $revision;
+               }
+
+               if ( ! current_user_can( 'edit_theme_options' ) ) {
+                       return new WP_Error(
+                               'rest_cannot_delete',
+                               __( 'Sorry, you are not allowed to delete this revision.' ),
+                               array( 'status' => rest_authorization_required_code() )
+                       );
+               }
+
+               return true;
+       }
+
+       /**
+        * Prepares links for the request.
+        *
+        * @since 6.4.0
+        *
+        * @param WP_Block_Template $template Template.
+        * @return array Links for the given post.
+        */
+       protected function prepare_links( $template ) {
+               $links = array(
+                       'self'   => array(
+                               'href' => rest_url( sprintf( '/%s/%s/%s/%s/%d', $this->namespace, $this->parent_base, $template->id, $this->rest_base, $template->wp_id ) ),
+                       ),
+                       'parent' => array(
+                               'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->parent_base, $template->id ) ),
+                       ),
+               );
+
+               return $links;
+       }
+
+       /**
+        * Retrieves the item's schema, conforming to JSON Schema.
+        *
+        * @since 6.4.0
+        *
+        * @return array Item schema data.
+        */
+       public function get_item_schema() {
+               if ( $this->schema ) {
+                       return $this->add_additional_fields_schema( $this->schema );
+               }
+
+               $schema = $this->parent_controller->get_item_schema();
+
+               $schema['properties']['parent'] = array(
+                       'description' => __( 'The ID for the parent of the revision.' ),
+                       'type'        => 'integer',
+                       'context'     => array( 'view', 'edit', 'embed' ),
+               );
+
+               $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-template-revisions-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="trunksrcwpincludesrestapiendpointsclasswpresttemplatescontrollerphp"></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/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   2023-10-10 13:19:42 UTC (rev 56818)
+++ trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php     2023-10-10 14:03:03 UTC (rev 56819)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -760,7 +760,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        protected function prepare_links( $id ) {
</span><span class="cx" style="display: block; padding: 0 10px">                $links = array(
</span><span class="cx" style="display: block; padding: 0 10px">                        'self'       => array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'href' => rest_url( rest_get_route_for_post( $id ) ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $id ) ),
</ins><span class="cx" style="display: block; padding: 0 10px">                         ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'collection' => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'href' => rest_url( rest_get_route_for_post_type_items( $this->post_type ) ),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -770,6 +770,27 @@
</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 ( post_type_supports( $this->post_type, 'revisions' ) ) {
+                       $template = get_block_template( $id, $this->post_type );
+                       if ( $template instanceof WP_Block_Template && ! empty( $template->wp_id ) ) {
+                               $revisions       = wp_get_latest_revision_id_and_total_count( $template->wp_id );
+                               $revisions_count = ! is_wp_error( $revisions ) ? $revisions['count'] : 0;
+                               $revisions_base  = sprintf( '/%s/%s/%s/revisions', $this->namespace, $this->rest_base, $id );
+
+                               $links['version-history'] = array(
+                                       'href'  => rest_url( $revisions_base ),
+                                       'count' => $revisions_count,
+                               );
+
+                               if ( $revisions_count > 0 ) {
+                                       $links['predecessor-version'] = array(
+                                               'href' => rest_url( $revisions_base . '/' . $revisions['latest_id'] ),
+                                               'id'   => $revisions['latest_id'],
+                                       );
+                               }
+                       }
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 return $links;
</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="trunksrcwpincludesrestapiphp"></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/rest-api.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/rest-api.php        2023-10-10 13:19:42 UTC (rev 56818)
+++ trunk/src/wp-includes/rest-api.php  2023-10-10 14:03:03 UTC (rev 56819)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -241,17 +241,23 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        continue;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $controller->register_routes();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( ! $post_type->late_route_registration ) {
+                       $controller->register_routes();
+               }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( post_type_supports( $post_type->name, 'revisions' ) ) {
-                       $revisions_controller = new WP_REST_Revisions_Controller( $post_type->name );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $revisions_controller = $post_type->get_revisions_rest_controller();
+               if ( $revisions_controller ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         $revisions_controller->register_routes();
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( 'attachment' !== $post_type->name ) {
-                       $autosaves_controller = new WP_REST_Autosaves_Controller( $post_type->name );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $autosaves_controller = $post_type->get_autosave_rest_controller();
+               if ( $autosaves_controller ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         $autosaves_controller->register_routes();
</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 ( $post_type->late_route_registration ) {
+                       $controller->register_routes();
+               }
</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">        // Post types.
</span></span></pre></div>
<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 2023-10-10 13:19:42 UTC (rev 56818)
+++ trunk/src/wp-settings.php   2023-10-10 14:03:03 UTC (rev 56819)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -273,7 +273,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-post-types-controller.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-post-statuses-controller.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-revisions-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-template-revisions-controller.php';
</ins><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-autosaves-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-template-autosaves-controller.php';
</ins><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-taxonomies-controller.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-terms-controller.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-menu-items-controller.php';
</span></span></pre></div>
<a id="trunktestsphpunittestspostwpPostTypephp"></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/post/wpPostType.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/post/wpPostType.php     2023-10-10 13:19:42 UTC (rev 56818)
+++ trunk/tests/phpunit/tests/post/wpPostType.php       2023-10-10 14:03:03 UTC (rev 56819)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -229,4 +229,214 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertSame( 3, $action->get_call_count() );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       /**
+        * @ticket 56922
+        *
+        * @dataProvider data_should_have_correct_custom_revisions_and_autosaves_controllers_properties
+        *
+        * @covers WP_Post_Type::set_props
+        *
+        * @param string      $property_name           Property name.
+        * @param string      $property_value          Property value.
+        * @param string|bool $expected_property_value Expected property value.
+        */
+       public function test_should_have_correct_custom_revisions_and_autosaves_controllers_properties( $property_name, $property_value, $expected_property_value ) {
+               $properties = null === $property_value ? array() : array( $property_name => $property_value );
+
+               $post_type = new WP_Post_Type( 'test_post_type', $properties );
+
+               $this->assertObjectHasProperty( $property_name, $post_type, "The WP_Post_Type object does not have the expected {$property_name} property." );
+               $this->assertSame(
+                       $expected_property_value,
+                       $post_type->$property_name,
+                       sprintf( 'Expected the property "%s" to have the %s value.', $property_name, var_export( $expected_property_value, true ) )
+               );
+       }
+
+       /**
+        * Data provider for test_should_allow_to_set_custom_revisions_and_autosaves_controllers_properties.
+        *
+        * @return array[] Arguments {
+        *     @type string $property_name           Property name.
+        *     @type string $property_value          Property value.
+        *     @type string|bool $expected_property_value Expected property value.
+        * }
+        */
+       public function data_should_have_correct_custom_revisions_and_autosaves_controllers_properties() {
+               return array(
+                       'autosave_rest_controller_class property'  => array(
+                               'autosave_rest_controller_class',
+                               'My_Custom_Template_Autosaves_Controller',
+                               'My_Custom_Template_Autosaves_Controller',
+                       ),
+                       'autosave_rest_controller_class property (null value)' => array(
+                               'autosave_rest_controller_class',
+                               null,
+                               false,
+                       ),
+                       'revisions_rest_controller_class property' => array(
+                               'revisions_rest_controller_class',
+                               'My_Custom_Template_Revisions_Controller',
+                               'My_Custom_Template_Revisions_Controller',
+                       ),
+                       'revisions_rest_controller_class property (null value)' => array(
+                               'revisions_rest_controller_class',
+                               null,
+                               false,
+                       ),
+               );
+       }
+
+       /**
+        * @ticket 56922
+        *
+        * @covers WP_Post_Type::get_revisions_rest_controller
+        *
+        * @dataProvider data_get_revisions_rest_controller_should_return_correct_values
+        *
+        * @param bool        $show_in_rest                    Enables "show_in_rest" support.
+        * @param bool        $supports_revisions              Enables revisions support.
+        * @param string|bool $revisions_rest_controller_class Custom revisions REST controller class.
+        * @param string|null $expected_value                  Expected value.
+        */
+       public function test_get_revisions_rest_controller_should_return_correct_values( $show_in_rest, $supports_revisions, $revisions_rest_controller_class, $expected_value ) {
+               $post_type  = 'test_post_type';
+               $properties = array(
+                       'show_in_rest'                    => $show_in_rest,
+                       'supports'                        => $supports_revisions ? array( 'revisions' ) : array(),
+                       'revisions_rest_controller_class' => $revisions_rest_controller_class,
+               );
+               register_post_type( $post_type, $properties );
+               $post_type = get_post_type_object( $post_type );
+
+               $controller = $post_type->get_revisions_rest_controller();
+               if ( $expected_value ) {
+                       $this->assertInstanceOf( $expected_value, $controller );
+
+                       return;
+               }
+
+               $this->assertSame( $expected_value, $controller );
+       }
+
+       /**
+        * Data provider for test_get_revisions_rest_controller_should_return_correct_values.
+        *
+        * @return array[] Arguments {
+        *     @type bool             $show_in_rest                    Enables "show_in_rest" support.
+        *     @type bool             $supports_revisions              Enables revisions support.
+        *     @type string|bool      $revisions_rest_controller_class Custom revisions REST controller class.
+        *     @type string|null      $expected_value                  Expected value.
+        * }
+        */
+       public function data_get_revisions_rest_controller_should_return_correct_values() {
+               return array(
+                       'disable show_in_rest'                => array(
+                               false,
+                               false,
+                               false,
+                               null,
+                       ),
+                       'disable revisions support'           => array(
+                               true,
+                               false,
+                               false,
+                               null,
+                       ),
+                       'default rest revisions controller'   => array(
+                               true,
+                               true,
+                               false,
+                               WP_REST_Revisions_Controller::class,
+                       ),
+                       'incorrect rest revisions controller' => array(
+                               true,
+                               true,
+                               stdClass::class,
+                               null,
+                       ),
+                       'correct rest revisions controller'   => array(
+                               true,
+                               true,
+                               WP_REST_Template_Revisions_Controller::class,
+                               WP_REST_Template_Revisions_Controller::class,
+                       ),
+               );
+       }
+
+       /**
+        * @ticket 56922
+        *
+        * @covers WP_Post_Type::get_autosave_rest_controller
+        *
+        * @dataProvider data_get_autosave_rest_controller_should_return_correct_values
+        *
+        * @param bool        $show_in_rest                   Enables "show_in_rest" support.
+        * @param string      $post_type                      Post type.
+        * @param string|bool $autosave_rest_controller_class Custom autosave REST controller class.
+        * @param string|null $expected_value                 Expected value.
+        */
+       public function test_get_autosave_rest_controller_should_return_correct_values( $show_in_rest, $post_type, $autosave_rest_controller_class, $expected_value ) {
+               $properties = array(
+                       'show_in_rest'                   => $show_in_rest,
+                       'autosave_rest_controller_class' => $autosave_rest_controller_class,
+               );
+               register_post_type( $post_type, $properties );
+               $post_type = get_post_type_object( $post_type );
+
+               $controller = $post_type->get_autosave_rest_controller();
+               if ( $expected_value ) {
+                       $this->assertInstanceOf( $expected_value, $controller );
+
+                       return;
+               }
+
+               $this->assertSame( $expected_value, $controller );
+       }
+
+       /**
+        * Data provider for test_get_autosave_rest_controller_should_return_correct_values.
+        *
+        * @return array[] Arguments {
+        *     @type bool        $show_in_rest                   Enables "show_in_rest" support.
+        *     @type string      $post_type                      Post type.
+        *     @type string|bool $autosave_rest_controller_class Custom autosave REST controller class.
+        *     @type string|null $expected_value                 Expected value.
+        * }
+        */
+       public function data_get_autosave_rest_controller_should_return_correct_values() {
+               return array(
+                       'disable show_in_rest'               => array(
+                               false,
+                               'attachment',
+                               false,
+                               null,
+                       ),
+                       'invalid post type'                  => array(
+                               true,
+                               'attachment',
+                               false,
+                               null,
+                       ),
+                       'default rest autosave controller'   => array(
+                               true,
+                               'test_post_type',
+                               false,
+                               WP_REST_Autosaves_Controller::class,
+                       ),
+                       'incorrect rest autosave controller' => array(
+                               true,
+                               'test_post_type',
+                               stdClass::class,
+                               null,
+                       ),
+                       'correct rest autosave controller'   => array(
+                               true,
+                               'test_post_type',
+                               WP_REST_Template_Autosaves_Controller::class,
+                               WP_REST_Template_Autosaves_Controller::class,
+                       ),
+               );
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<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  2023-10-10 13:19:42 UTC (rev 56818)
+++ trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php    2023-10-10 14:03:03 UTC (rev 56819)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -144,18 +144,18 @@
</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><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/template-parts',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        '/wp/v2/template-parts/(?P<id>[\d]+)/autosaves',
</del><span class="cx" style="display: block; padding: 0 10px">                         '/wp/v2/template-parts/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        '/wp/v2/template-parts/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)',
-                       '/wp/v2/template-parts/(?P<parent>[\d]+)/revisions',
-                       '/wp/v2/template-parts/(?P<parent>[\d]+)/revisions/(?P<id>[\d]+)',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 '/wp/v2/template-parts/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)/autosaves',
+                       '/wp/v2/template-parts/(?P<parent>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)/autosaves/(?P<id>[\d]+)',
+                       '/wp/v2/template-parts/(?P<parent>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)/revisions',
+                       '/wp/v2/template-parts/(?P<parent>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)/revisions/(?P<id>[\d]+)',
</ins><span class="cx" style="display: block; padding: 0 10px">                         '/wp/v2/template-parts/lookup',
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp/v2/templates',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        '/wp/v2/templates/(?P<id>[\d]+)/autosaves',
</del><span class="cx" style="display: block; padding: 0 10px">                         '/wp/v2/templates/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        '/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]+)',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 '/wp/v2/templates/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)/autosaves',
+                       '/wp/v2/templates/(?P<parent>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)/autosaves/(?P<id>[\d]+)',
+                       '/wp/v2/templates/(?P<parent>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)/revisions',
+                       '/wp/v2/templates/(?P<parent>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)/revisions/(?P<id>[\d]+)',
</ins><span class="cx" style="display: block; padding: 0 10px">                         '/wp/v2/templates/lookup',
</span><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>[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
</span></span></pre></div>
<a id="trunktestsphpunittestsrestapiwpRestTemplateAutosavesControllerphp"></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/wpRestTemplateAutosavesController.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/rest-api/wpRestTemplateAutosavesController.php                          (rev 0)
+++ trunk/tests/phpunit/tests/rest-api/wpRestTemplateAutosavesController.php    2023-10-10 14:03:03 UTC (rev 56819)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,413 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Unit tests covering WP_REST_Template_Autosaves_Controller functionality.
+ *
+ * @package WordPress
+ * @subpackage REST API
+ *
+ * @group restapi
+ */
+class Tests_REST_wpRestTemplateAutosavesController extends WP_Test_REST_Controller_Testcase {
+
+       /**
+        * @var string
+        */
+       const TEST_THEME = 'block-theme';
+
+       /**
+        * @var string
+        */
+       const TEMPLATE_NAME = 'my_template';
+
+       /**
+        * @var string
+        */
+       const PARENT_POST_TYPE = 'wp_template';
+
+       /**
+        * Admin user ID.
+        *
+        * @since 6.4.0
+        *
+        * @var int
+        */
+       private static $admin_id;
+
+       /**
+        * Contributor user ID.
+        *
+        * @since 6.4.0
+        *
+        * @var int
+        */
+       private static $contributor_id;
+
+       /**
+        * Template post.
+        *
+        * @since 6.4.0
+        *
+        * @var WP_Post
+        */
+       private static $template_post;
+
+       /**
+        * Create fake data before our tests run.
+        *
+        * @param WP_UnitTest_Factory $factory Helper that lets us create fake data.
+        */
+       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
+               self::$contributor_id = $factory->user->create(
+                       array(
+                               'role' => 'contributor',
+                       )
+               );
+
+               self::$admin_id = $factory->user->create(
+                       array(
+                               'role' => 'administrator',
+                       )
+               );
+               wp_set_current_user( self::$admin_id );
+
+               // Set up template post.
+               self::$template_post = $factory->post->create_and_get(
+                       array(
+                               'post_type'    => self::PARENT_POST_TYPE,
+                               'post_name'    => self::TEMPLATE_NAME,
+                               'post_title'   => 'My Template',
+                               'post_content' => 'Content',
+                               'post_excerpt' => 'Description of my template',
+                               'tax_input'    => array(
+                                       'wp_theme' => array(
+                                               self::TEST_THEME,
+                                       ),
+                               ),
+                       )
+               );
+               wp_set_post_terms( self::$template_post->ID, self::TEST_THEME, 'wp_theme' );
+       }
+
+       /**
+        * @covers WP_REST_Template_Autosaves_Controller::register_routes
+        * @ticket 56922
+        */
+       public function test_register_routes() {
+               $routes = rest_get_server()->get_routes();
+               $this->assertArrayHasKey(
+                       '/wp/v2/templates/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)/autosaves',
+                       $routes,
+                       'Template autosaves route does not exist.'
+               );
+               $this->assertArrayHasKey(
+                       '/wp/v2/templates/(?P<parent>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)/autosaves/(?P<id>[\d]+)',
+                       $routes,
+                       'Single template autosave based on the given ID route does not exist.'
+               );
+               $this->assertArrayHasKey(
+                       '/wp/v2/template-parts/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)/autosaves',
+                       $routes,
+                       'Template part autosaves route does not exist.'
+               );
+               $this->assertArrayHasKey(
+                       '/wp/v2/template-parts/(?P<parent>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)/autosaves/(?P<id>[\d]+)',
+                       $routes,
+                       'Single template part autosave based on the given ID route does not exist.'
+               );
+       }
+
+       /**
+        * @covers WP_REST_Template_Autosaves_Controller::get_context_param
+        * @ticket 56922
+        */
+       public function test_context_param() {
+               // Collection.
+               $request  = new WP_REST_Request( 'OPTIONS', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/autosaves' );
+               $response = rest_get_server()->dispatch( $request );
+               $data     = $response->get_data();
+
+               // Collection.
+               $this->assertCount(
+                       2,
+                       $data['endpoints'],
+                       'Failed to assert that the collection autosave endpoints count is 2.'
+               );
+               $this->assertSame(
+                       'view',
+                       $data['endpoints'][0]['args']['context']['default'],
+                       'Failed to assert that the default context for the GET collection endpoint is "view".'
+               );
+               $this->assertSame(
+                       array( 'view', 'embed', 'edit' ),
+                       $data['endpoints'][0]['args']['context']['enum'],
+                       "Failed to assert that the enum values for the GET collection endpoint are 'view', 'embed', and 'edit'."
+               );
+
+               // Single.
+               $request  = new WP_REST_Request( 'OPTIONS', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/autosaves/1' );
+               $response = rest_get_server()->dispatch( $request );
+               $data     = $response->get_data();
+               $this->assertCount(
+                       1,
+                       $data['endpoints'],
+                       'Failed to assert that the single autosave endpoints count is 1.'
+               );
+               $this->assertSame(
+                       'view',
+                       $data['endpoints'][0]['args']['context']['default'],
+                       'Failed to assert that the default context for the single autosave endpoint is "view".'
+               );
+               $this->assertSame(
+                       array( 'view', 'embed', 'edit' ),
+                       $data['endpoints'][0]['args']['context']['enum'],
+                       "Failed to assert that the enum values for the single autosave endpoint are 'view', 'embed', and 'edit'."
+               );
+       }
+
+       /**
+        * @covers WP_REST_Template_Autosaves_Controller::get_items
+        * @ticket 56922
+        */
+       public function test_get_items() {
+               wp_set_current_user( self::$admin_id );
+               $autosave_post_id = wp_create_post_autosave(
+                       array(
+                               'post_content' => 'Autosave content.',
+                               'post_ID'      => self::$template_post->ID,
+                               'post_type'    => self::PARENT_POST_TYPE,
+                       )
+               );
+
+               $request   = new WP_REST_Request(
+                       'GET',
+                       '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/autosaves'
+               );
+               $response  = rest_get_server()->dispatch( $request );
+               $autosaves = $response->get_data();
+
+               $this->assertCount(
+                       1,
+                       $autosaves,
+                       'Failed asserting that the response data contains exactly 1 item.'
+               );
+
+               $this->assertSame(
+                       $autosave_post_id,
+                       $autosaves[0]['wp_id'],
+                       'Failed asserting that the ID of the autosave matches the expected autosave post ID.'
+               );
+               $this->assertSame(
+                       self::$template_post->ID,
+                       $autosaves[0]['parent'],
+                       'Failed asserting that the parent ID of the autosave matches the template post ID.'
+               );
+               $this->assertSame(
+                       'Autosave content.',
+                       $autosaves[0]['content']['raw'],
+                       'Failed asserting that the content of the autosave is "Autosave content.".'
+               );
+       }
+
+       /**
+        * @covers WP_REST_Template_Autosaves_Controller::get_item
+        * @ticket 56922
+        */
+       public function test_get_item() {
+               wp_set_current_user( self::$admin_id );
+
+               $autosave_post_id = wp_create_post_autosave(
+                       array(
+                               'post_content' => 'Autosave content.',
+                               'post_ID'      => self::$template_post->ID,
+                               'post_type'    => self::PARENT_POST_TYPE,
+                       )
+               );
+
+               $request  = new WP_REST_Request( 'GET', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/autosaves/' . $autosave_post_id );
+               $response = rest_get_server()->dispatch( $request );
+               $autosave = $response->get_data();
+
+               $this->assertIsArray( $autosave, 'Failed asserting that the autosave is an array.' );
+               $this->assertSame(
+                       $autosave_post_id,
+                       $autosave['wp_id'],
+                       "Failed asserting that the autosave id is the same as $autosave_post_id."
+               );
+               $this->assertSame(
+                       self::$template_post->ID,
+                       $autosave['parent'],
+                       sprintf(
+                               'Failed asserting that the parent id of the autosave is the same as %s.',
+                               self::$template_post->ID
+                       )
+               );
+       }
+
+       /**
+        * @covers WP_REST_Template_Autosaves_Controller::prepare_item_for_response
+        * @ticket 56922
+        */
+       public function test_prepare_item() {
+               wp_set_current_user( self::$admin_id );
+               $autosave_post_id = wp_create_post_autosave(
+                       array(
+                               'post_content' => 'Autosave content.',
+                               'post_ID'      => self::$template_post->ID,
+                               'post_type'    => self::PARENT_POST_TYPE,
+                       )
+               );
+               $autosave_db_post = get_post( $autosave_post_id );
+               $template_id      = self::TEST_THEME . '//' . self::TEMPLATE_NAME;
+               $request          = new WP_REST_Request( 'GET', '/wp/v2/templates/' . $template_id . '/autosaves/' . $autosave_db_post->ID );
+               $controller       = new WP_REST_Template_Autosaves_Controller( self::PARENT_POST_TYPE );
+               $response         = $controller->prepare_item_for_response( $autosave_db_post, $request );
+               $this->assertInstanceOf(
+                       WP_REST_Response::class,
+                       $response,
+                       'Failed asserting that the response object is an instance of WP_REST_Response.'
+               );
+
+               $autosave = $response->get_data();
+               $this->assertIsArray( $autosave, 'Failed asserting that the autosave is an array.' );
+               $this->assertSame(
+                       $autosave_db_post->ID,
+                       $autosave['wp_id'],
+                       "Failed asserting that the autosave id is the same as $autosave_db_post->ID."
+               );
+               $this->assertSame(
+                       self::$template_post->ID,
+                       $autosave['parent'],
+                       sprintf(
+                               'Failed asserting that the parent id of the autosave is the same as %s.',
+                               self::$template_post->ID
+                       )
+               );
+
+               $links = $response->get_links();
+               $this->assertIsArray( $links, 'Failed asserting that the links are an array.' );
+
+               $this->assertStringEndsWith(
+                       $template_id . '/autosaves/' . $autosave_db_post->ID,
+                       $links['self'][0]['href'],
+                       "Failed asserting that the self link ends with $template_id . '/autosaves/' . $autosave_db_post->ID."
+               );
+
+               $this->assertStringEndsWith(
+                       $template_id,
+                       $links['parent'][0]['href'],
+                       "Failed asserting that the parent link ends with %$template_id."
+               );
+       }
+
+       /**
+        * @covers WP_REST_Template_Autosaves_Controller::get_item_schema
+        * @ticket 56922
+        */
+       public function test_get_item_schema() {
+               $request  = new WP_REST_Request( 'OPTIONS', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/autosaves' );
+               $response = rest_get_server()->dispatch( $request );
+               $data     = $response->get_data();
+
+               $properties = $data['schema']['properties'];
+
+               $this->assertCount( 16, $properties );
+               $this->assertArrayHasKey( 'id', $properties, 'ID key should exist in properties.' );
+               $this->assertArrayHasKey( 'slug', $properties, 'Slug key should exist in properties.' );
+               $this->assertArrayHasKey( 'theme', $properties, 'Theme key should exist in properties.' );
+               $this->assertArrayHasKey( 'source', $properties, 'Source key should exist in properties.' );
+               $this->assertArrayHasKey( 'origin', $properties, 'Origin key should exist in properties.' );
+               $this->assertArrayHasKey( 'content', $properties, 'Content key should exist in properties.' );
+               $this->assertArrayHasKey( 'title', $properties, 'Title key should exist in properties.' );
+               $this->assertArrayHasKey( 'description', $properties, 'description key should exist in properties.' );
+               $this->assertArrayHasKey( 'status', $properties, 'status key should exist in properties.' );
+               $this->assertArrayHasKey( 'wp_id', $properties, 'wp_id key should exist in properties.' );
+               $this->assertArrayHasKey( 'has_theme_file', $properties, 'has_theme_file key should exist in properties.' );
+               $this->assertArrayHasKey( 'author', $properties, 'author key should exist in properties.' );
+               $this->assertArrayHasKey( 'modified', $properties, 'modified key should exist in properties.' );
+               $this->assertArrayHasKey( 'is_custom', $properties, 'is_custom key should exist in properties.' );
+               $this->assertArrayHasKey( 'parent', $properties, 'Parent key should exist in properties.' );
+       }
+
+       /**
+        * @covers WP_REST_Template_Autosaves_Controller::create_item
+        * @ticket 56922
+        */
+       public function test_create_item() {
+               wp_set_current_user( self::$admin_id );
+
+               $template_id = self::TEST_THEME . '/' . self::TEMPLATE_NAME;
+               $request     = new WP_REST_Request( 'POST', '/wp/v2/templates/' . $template_id . '/autosaves' );
+               $request->add_header( 'Content-Type', 'application/x-www-form-urlencoded' );
+
+               $request_parameters = array(
+                       'title'   => 'Post Title',
+                       'content' => 'Post content',
+                       'excerpt' => 'Post excerpt',
+                       'name'    => 'test',
+                       'id'      => $template_id,
+               );
+
+               $request->set_body_params( $request_parameters );
+               $response = rest_get_server()->dispatch( $request );
+
+               $this->assertNotWPError( $response, 'The response from this request should not return a WP_Error object' );
+               $response = rest_ensure_response( $response );
+               $data     = $response->get_data();
+
+               $this->assertArrayHasKey( 'content', $data, 'Response should contain a key called content' );
+               $this->assertSame( $request_parameters['content'], $data['content']['raw'], 'Response data should match for field content' );
+
+               $this->assertArrayHasKey( 'title', $data, 'Response should contain a key called title' );
+               $this->assertSame( $request_parameters['title'], $data['title']['raw'], 'Response data should match for field title' );
+       }
+
+       /**
+        * @covers WP_REST_Template_Autosaves_Controller::delete_item
+        * @ticket 56922
+        */
+       public function test_create_item_incorrect_permission() {
+               wp_set_current_user( self::$contributor_id );
+               $template_id = self::TEST_THEME . '/' . self::TEMPLATE_NAME;
+               $request     = new WP_REST_Request( 'POST', '/wp/v2/templates/' . $template_id . '/autosaves' );
+               $response    = rest_get_server()->dispatch( $request );
+               $this->assertErrorResponse( 'rest_cannot_manage_templates', $response, WP_Http::FORBIDDEN );
+       }
+
+       /**
+        * @covers WP_REST_Template_Autosaves_Controller::delete_item
+        * @ticket 56922
+        */
+       public function test_create_item_no_permission() {
+               wp_set_current_user( 0 );
+               $template_id = self::TEST_THEME . '/' . self::TEMPLATE_NAME;
+               $request     = new WP_REST_Request( 'POST', '/wp/v2/templates/' . $template_id . '/autosaves' );
+               $response    = rest_get_server()->dispatch( $request );
+               $this->assertErrorResponse( 'rest_cannot_manage_templates', $response, WP_Http::UNAUTHORIZED );
+       }
+
+       /**
+        * @coversNothing
+        * @ticket 56922
+        */
+       public function test_update_item() {
+               $this->markTestSkipped(
+                       sprintf(
+                               "The '%s' controller doesn't currently support the ability to update template autosaves.",
+                               WP_REST_Template_Autosaves_Controller::class
+                       )
+               );
+       }
+
+       /**
+        * @coversNothing
+        * @ticket 56922
+        */
+       public function test_delete_item() {
+               $this->markTestSkipped(
+                       sprintf(
+                               "The '%s' controller doesn't currently support the ability to delete template autosaves.",
+                               WP_REST_Template_Autosaves_Controller::class
+                       )
+               );
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/tests/rest-api/wpRestTemplateAutosavesController.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="trunktestsphpunittestsrestapiwpRestTemplateRevisionsControllerphp"></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/wpRestTemplateRevisionsController.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/rest-api/wpRestTemplateRevisionsController.php                          (rev 0)
+++ trunk/tests/phpunit/tests/rest-api/wpRestTemplateRevisionsController.php    2023-10-10 14:03:03 UTC (rev 56819)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,511 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Unit tests covering WP_REST_Template_Revisions_Controller functionality.
+ *
+ * @package WordPress
+ * @subpackage REST API
+ *
+ * @group restapi
+ */
+class Tests_REST_wpRestTemplateRevisionsController extends WP_Test_REST_Controller_Testcase {
+
+       /**
+        * @var string
+        */
+       const TEST_THEME = 'block-theme';
+
+       /**
+        * @var string
+        */
+       const TEMPLATE_NAME = 'my_template';
+
+       /**
+        * @var string
+        */
+       const PARENT_POST_TYPE = 'wp_template';
+
+       /**
+        * Admin user ID.
+        *
+        * @since 6.4.0
+        *
+        * @var int
+        */
+       private static $admin_id;
+
+       /**
+        * Contributor user ID.
+        *
+        * @since 6.4.0
+        *
+        * @var int
+        */
+       private static $contributor_id;
+
+       /**
+        * Template post.
+        *
+        * @since 6.4.0
+        *
+        * @var WP_Post
+        */
+       private static $template_post;
+
+       /**
+        * @var array
+        */
+       private static $revisions = array();
+
+       /**
+        * Create fake data before our tests run.
+        *
+        * @param WP_UnitTest_Factory $factory Helper that lets us create fake data.
+        */
+       public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
+               self::$admin_id = $factory->user->create(
+                       array(
+                               'role' => 'administrator',
+                       )
+               );
+               wp_set_current_user( self::$admin_id );
+
+               self::$contributor_id = $factory->user->create(
+                       array(
+                               'role' => 'contributor',
+                       )
+               );
+
+               // Set up template post.
+               self::$template_post = $factory->post->create_and_get(
+                       array(
+                               'post_type'    => self::PARENT_POST_TYPE,
+                               'post_name'    => self::TEMPLATE_NAME,
+                               'post_title'   => 'My Template',
+                               'post_content' => 'Content',
+                               'post_excerpt' => 'Description of my template',
+                               'tax_input'    => array(
+                                       'wp_theme' => array(
+                                               self::TEST_THEME,
+                                       ),
+                               ),
+                       )
+               );
+               wp_set_post_terms( self::$template_post->ID, self::TEST_THEME, 'wp_theme' );
+
+               // Update post to create a new revisions.
+               self::$revisions[] = _wp_put_post_revision(
+                       array(
+                               'ID'           => self::$template_post->ID,
+                               'post_content' => 'Content revision #2',
+                       )
+               );
+
+               // Update post to create a new revisions.
+               self::$revisions[] = _wp_put_post_revision(
+                       array(
+                               'ID'           => self::$template_post->ID,
+                               'post_content' => 'Content revision #3',
+                       )
+               );
+
+               // Update post to create a new revisions.
+               self::$revisions[] = _wp_put_post_revision(
+                       array(
+                               'ID'           => self::$template_post->ID,
+                               'post_content' => 'Content revision #4',
+                       )
+               );
+
+               // Update post to create a new revisions.
+               self::$revisions[] = _wp_put_post_revision(
+                       array(
+                               'ID'           => self::$template_post->ID,
+                               'post_content' => 'Content revision #5',
+                       )
+               );
+       }
+
+       /**
+        * Remove revisions when tests are complete.
+        */
+       public static function wpTearDownAfterClass() {
+               // Also deletes revisions.
+               foreach ( self::$revisions as $revision ) {
+                       wp_delete_post( $revision, true );
+               }
+       }
+
+       /**
+        * @covers WP_REST_Template_Revisions_Controller::register_routes
+        * @ticket 56922
+        */
+       public function test_register_routes() {
+               $routes = rest_get_server()->get_routes();
+               $this->assertArrayHasKey(
+                       '/wp/v2/templates/(?P<parent>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)/revisions',
+                       $routes,
+                       'Template revisions route does not exist.'
+               );
+               $this->assertArrayHasKey(
+                       '/wp/v2/templates/(?P<parent>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)/revisions/(?P<id>[\d]+)',
+                       $routes,
+                       'Single template revision based on the given ID route does not exist.'
+               );
+               $this->assertArrayHasKey(
+                       '/wp/v2/template-parts/(?P<parent>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)/revisions',
+                       $routes,
+                       'Template part revisions route does not exist.'
+               );
+               $this->assertArrayHasKey(
+                       '/wp/v2/template-parts/(?P<parent>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)/revisions/(?P<id>[\d]+)',
+                       $routes,
+                       'Single template part revision based on the given ID route does not exist.'
+               );
+       }
+
+       /**
+        * @covers WP_REST_Template_Revisions_Controller::get_context_param
+        * @ticket 56922
+        */
+       public function test_context_param() {
+               // Collection.
+               $request  = new WP_REST_Request( 'OPTIONS', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/revisions' );
+               $response = rest_get_server()->dispatch( $request );
+               $data     = $response->get_data();
+               $this->assertSame(
+                       'view',
+                       $data['endpoints'][0]['args']['context']['default'],
+                       'Failed to assert that the default context for the collection endpoint is "view".'
+               );
+               $this->assertSame(
+                       array( 'view', 'embed', 'edit' ),
+                       $data['endpoints'][0]['args']['context']['enum'],
+                       'Failed to assert correct enum values for the collection endpoint.'
+               );
+
+               // Single.
+               $request  = new WP_REST_Request( 'OPTIONS', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/revisions/1' );
+               $response = rest_get_server()->dispatch( $request );
+               $data     = $response->get_data();
+               $this->assertCount(
+                       2,
+                       $data['endpoints'],
+                       'Failed to assert that the single revision endpoint count is 2.'
+               );
+               $this->assertSame(
+                       'view',
+                       $data['endpoints'][0]['args']['context']['default'],
+                       'Failed to assert that the default context for the single revision endpoint is "view".'
+               );
+               $this->assertSame(
+                       array( 'view', 'embed', 'edit' ),
+                       $data['endpoints'][0]['args']['context']['enum'],
+                       'Failed to assert correct enum values for the single revision endpoint.'
+               );
+       }
+
+       /**
+        * @covers WP_REST_Template_Revisions_Controller::get_items
+        * @ticket 56922
+        */
+       public function test_get_items() {
+               wp_set_current_user( self::$admin_id );
+               $request   = new WP_REST_Request(
+                       'GET',
+                       '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/revisions'
+               );
+               $response  = rest_get_server()->dispatch( $request );
+               $revisions = $response->get_data();
+
+               $this->assertCount(
+                       4,
+                       $revisions,
+                       'Failed asserting that the response data contains exactly 4 items.'
+               );
+
+               $this->assertSame(
+                       self::$template_post->ID,
+                       $revisions[0]['parent'],
+                       'Failed asserting that the parent ID of the revision matches the template post ID.'
+               );
+               $this->assertSame(
+                       'Content revision #5',
+                       $revisions[0]['content']['raw'],
+                       'Failed asserting that the content of the revision is "Content revision #5".'
+               );
+
+               $this->assertSame(
+                       self::$template_post->ID,
+                       $revisions[1]['parent'],
+                       'Failed asserting that the parent ID of the revision matches the template post ID.'
+               );
+               $this->assertSame(
+                       'Content revision #4',
+                       $revisions[1]['content']['raw'],
+                       'Failed asserting that the content of the revision is "Content revision #4".'
+               );
+
+               $this->assertSame(
+                       self::$template_post->ID,
+                       $revisions[2]['parent'],
+                       'Failed asserting that the parent ID of the revision matches the template post ID.'
+               );
+               $this->assertSame(
+                       'Content revision #3',
+                       $revisions[2]['content']['raw'],
+                       'Failed asserting that the content of the revision is "Content revision #3".'
+               );
+
+               $this->assertSame(
+                       self::$template_post->ID,
+                       $revisions[3]['parent'],
+                       'Failed asserting that the parent ID of the revision matches the template post ID.'
+               );
+               $this->assertSame(
+                       'Content revision #2',
+                       $revisions[3]['content']['raw'],
+                       'Failed asserting that the content of the revision is "Content revision #2".'
+               );
+       }
+
+
+       /**
+        * @covers WP_REST_Template_Revisions_Controller::get_items_permissions_check
+        * @ticket 56922
+        */
+       public function test_get_items_endpoint_should_return_unauthorized_https_status_code_for_unauthorized_request() {
+               wp_set_current_user( 0 );
+               $request  = new WP_REST_Request( 'GET', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/revisions' );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertErrorResponse( 'rest_cannot_read', $response, WP_Http::UNAUTHORIZED );
+       }
+
+       /**
+        * @covers WP_REST_Template_Revisions_Controller::get_items_permissions_check
+        * @ticket 56922
+        */
+       public function test_get_items_endpoint_should_return_forbidden_https_status_code_for_users_with_insufficient_permissions() {
+               wp_set_current_user( self::$contributor_id );
+               $request  = new WP_REST_Request( 'GET', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/revisions' );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertErrorResponse( 'rest_cannot_read', $response, WP_Http::FORBIDDEN );
+       }
+
+       /**
+        * @covers WP_REST_Template_Revisions_Controller::get_item
+        * @ticket 56922
+        */
+       public function test_get_item() {
+               wp_set_current_user( self::$admin_id );
+
+               $revisions   = wp_get_post_revisions( self::$template_post, array( 'fields' => 'ids' ) );
+               $revision_id = array_shift( $revisions );
+
+               $request  = new WP_REST_Request( 'GET', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/revisions/' . $revision_id );
+               $response = rest_get_server()->dispatch( $request );
+               $revision = $response->get_data();
+
+               $this->assertIsArray( $revision, 'Failed asserting that the revision is an array.' );
+               $this->assertSame(
+                       $revision_id,
+                       $revision['wp_id'],
+                       "Failed asserting that the revision id is the same as $revision_id"
+               );
+               $this->assertSame(
+                       self::$template_post->ID,
+                       $revision['parent'],
+                       sprintf(
+                               'Failed asserting that the parent id of the revision is the same as %s.',
+                               self::$template_post->ID
+                       )
+               );
+       }
+
+       /**
+        * @covers WP_REST_Template_Revisions_Controller::get_item
+        * @ticket 56922
+        */
+       public function test_get_item_not_found() {
+               wp_set_current_user( self::$admin_id );
+
+               $revisions   = wp_get_post_revisions( self::$template_post, array( 'fields' => 'ids' ) );
+               $revision_id = array_shift( $revisions );
+
+               $request  = new WP_REST_Request( 'GET', '/wp/v2/templates/invalid//parent/revisions/' . $revision_id );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertErrorResponse( 'rest_post_invalid_parent', $response, WP_Http::NOT_FOUND );
+       }
+
+       /**
+        * @covers WP_REST_Template_Revisions_Controller::prepare_item_for_response
+        * @ticket 56922
+        */
+       public function test_prepare_item() {
+               $revisions   = wp_get_post_revisions( self::$template_post, array( 'fields' => 'ids' ) );
+               $revision_id = array_shift( $revisions );
+               $post        = get_post( $revision_id );
+               $request     = new WP_REST_Request( 'GET', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/revisions/' . $revision_id );
+               $controller  = new WP_REST_Template_Revisions_Controller( self::PARENT_POST_TYPE );
+               $response    = $controller->prepare_item_for_response( $post, $request );
+               $this->assertInstanceOf(
+                       WP_REST_Response::class,
+                       $response,
+                       'Failed asserting that the response object is an instance of WP_REST_Response.'
+               );
+
+               $revision = $response->get_data();
+               $this->assertIsArray( $revision, 'Failed asserting that the revision is an array.' );
+               $this->assertSame(
+                       $revision_id,
+                       $revision['wp_id'],
+                       "Failed asserting that the revision id is the same as $revision_id."
+               );
+               $this->assertSame(
+                       self::$template_post->ID,
+                       $revision['parent'],
+                       sprintf(
+                               'Failed asserting that the parent id of the revision is the same as %s.',
+                               self::$template_post->ID
+                       )
+               );
+
+               $links = $response->get_links();
+               $this->assertIsArray( $links, 'Failed asserting that the links are an array.' );
+
+               $this->assertStringEndsWith(
+                       self::TEST_THEME . '//' . self::TEMPLATE_NAME . '/revisions/' . $revision_id,
+                       $links['self'][0]['href'],
+                       sprintf(
+                               'Failed asserting that the self link ends with %s.',
+                               self::TEST_THEME . '//' . self::TEMPLATE_NAME . '/revisions/' . $revision_id
+                       )
+               );
+
+               $this->assertStringEndsWith(
+                       self::TEST_THEME . '//' . self::TEMPLATE_NAME,
+                       $links['parent'][0]['href'],
+                       sprintf(
+                               'Failed asserting that the parent link ends with %s.',
+                               self::TEST_THEME . '//' . self::TEMPLATE_NAME
+                       )
+               );
+       }
+
+       /**
+        * @covers WP_REST_Template_Revisions_Controller::get_item_schema
+        * @ticket 56922
+        */
+       public function test_get_item_schema() {
+               $request    = new WP_REST_Request( 'OPTIONS', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/revisions' );
+               $response   = rest_get_server()->dispatch( $request );
+               $data       = $response->get_data();
+               $properties = $data['schema']['properties'];
+
+               $this->assertCount( 16, $properties );
+               $this->assertArrayHasKey( 'id', $properties, 'ID key should exist in properties.' );
+               $this->assertArrayHasKey( 'slug', $properties, 'Slug key should exist in properties.' );
+               $this->assertArrayHasKey( 'theme', $properties, 'Theme key should exist in properties.' );
+               $this->assertArrayHasKey( 'source', $properties, 'Source key should exist in properties.' );
+               $this->assertArrayHasKey( 'origin', $properties, 'Origin key should exist in properties.' );
+               $this->assertArrayHasKey( 'content', $properties, 'Content key should exist in properties.' );
+               $this->assertArrayHasKey( 'title', $properties, 'Title key should exist in properties.' );
+               $this->assertArrayHasKey( 'description', $properties, 'description key should exist in properties.' );
+               $this->assertArrayHasKey( 'status', $properties, 'status key should exist in properties.' );
+               $this->assertArrayHasKey( 'wp_id', $properties, 'wp_id key should exist in properties.' );
+               $this->assertArrayHasKey( 'has_theme_file', $properties, 'has_theme_file key should exist in properties.' );
+               $this->assertArrayHasKey( 'author', $properties, 'author key should exist in properties.' );
+               $this->assertArrayHasKey( 'modified', $properties, 'modified key should exist in properties.' );
+               $this->assertArrayHasKey( 'is_custom', $properties, 'is_custom key should exist in properties.' );
+               $this->assertArrayHasKey( 'parent', $properties, 'Parent key should exist in properties.' );
+       }
+
+       /**
+        * @coversNothing
+        * @ticket 56922
+        */
+       public function test_create_item() {
+               $this->markTestSkipped(
+                       sprintf(
+                               "The '%s' controller doesn't currently support the ability to create template revisions.",
+                               WP_REST_Template_Revisions_Controller::class
+                       )
+               );
+       }
+
+       /**
+        * @coversNothing
+        * @ticket 56922
+        */
+       public function test_update_item() {
+               $this->markTestSkipped(
+                       sprintf(
+                               "The '%s' controller doesn't currently support the ability to update template revisions.",
+                               WP_REST_Template_Revisions_Controller::class
+                       )
+               );
+       }
+
+       /**
+        * @covers WP_REST_Templates_Controller::delete_item
+        * @ticket 56922
+        */
+       public function test_delete_item() {
+               wp_set_current_user( self::$admin_id );
+
+               $revision_id       = _wp_put_post_revision( self::$template_post );
+               self::$revisions[] = $revision_id;
+
+               $request = new WP_REST_Request( 'DELETE', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/revisions/' . $revision_id );
+               $request->set_param( 'force', true );
+               $response = rest_get_server()->dispatch( $request );
+
+               $this->assertSame( 200, $response->get_status(), 'Failed asserting that the response status is 200.' );
+               $this->assertNull( get_post( $revision_id ), 'Failed asserting that the post with the given revision ID is deleted.' );
+       }
+
+       /**
+        * @covers WP_REST_Templates_Controller::delete_item
+        * @ticket 56922
+        */
+       public function test_delete_item_incorrect_permission() {
+               wp_set_current_user( self::$contributor_id );
+               $revision_id       = _wp_put_post_revision( self::$template_post );
+               self::$revisions[] = $revision_id;
+
+               $request = new WP_REST_Request( 'DELETE', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/revisions/' . $revision_id );
+               $request->set_param( 'force', true );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertErrorResponse( 'rest_cannot_delete', $response, WP_Http::FORBIDDEN );
+       }
+
+       /**
+        * @covers WP_REST_Templates_Controller::delete_item
+        * @ticket 56922
+        */
+       public function test_delete_item_no_permission() {
+               wp_set_current_user( 0 );
+               $revision_id       = _wp_put_post_revision( self::$template_post );
+               self::$revisions[] = $revision_id;
+
+               $request = new WP_REST_Request( 'DELETE', '/wp/v2/templates/' . self::TEST_THEME . '/' . self::TEMPLATE_NAME . '/revisions/' . $revision_id );
+               $request->set_param( 'force', true );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertErrorResponse( 'rest_cannot_delete', $response, WP_Http::UNAUTHORIZED );
+       }
+
+       /**
+        * @covers WP_REST_Template_Revisions_Controller::get_item
+        * @ticket 56922
+        */
+       public function test_delete_item_not_found() {
+               wp_set_current_user( self::$admin_id );
+
+               $revision_id       = _wp_put_post_revision( self::$template_post );
+               self::$revisions[] = $revision_id;
+
+               $request = new WP_REST_Request( 'DELETE', '/wp/v2/templates/invalid//parent/revisions/' . $revision_id );
+               $request->set_param( 'force', true );
+               $response = rest_get_server()->dispatch( $request );
+               $this->assertErrorResponse( 'rest_post_invalid_parent', $response, WP_Http::NOT_FOUND );
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/tests/rest-api/wpRestTemplateRevisionsController.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="trunktestsqunitfixtureswpapigeneratedjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/qunit/fixtures/wp-api-generated.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/qunit/fixtures/wp-api-generated.js    2023-10-10 13:19:42 UTC (rev 56818)
+++ trunk/tests/qunit/fixtures/wp-api-generated.js      2023-10-10 14:03:03 UTC (rev 56819)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2707,762 +2707,6 @@
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "/wp/v2/media": {
-            "namespace": "wp/v2",
-            "methods": [
-                "GET",
-                "POST"
-            ],
-            "endpoints": [
-                {
-                    "methods": [
-                        "GET"
-                    ],
-                    "args": {
-                        "context": {
-                            "description": "Scope under which the request is made; determines fields present in response.",
-                            "type": "string",
-                            "enum": [
-                                "view",
-                                "embed",
-                                "edit"
-                            ],
-                            "default": "view",
-                            "required": false
-                        },
-                        "page": {
-                            "description": "Current page of the collection.",
-                            "type": "integer",
-                            "default": 1,
-                            "minimum": 1,
-                            "required": false
-                        },
-                        "per_page": {
-                            "description": "Maximum number of items to be returned in result set.",
-                            "type": "integer",
-                            "default": 10,
-                            "minimum": 1,
-                            "maximum": 100,
-                            "required": false
-                        },
-                        "search": {
-                            "description": "Limit results to those matching a string.",
-                            "type": "string",
-                            "required": false
-                        },
-                        "after": {
-                            "description": "Limit response to posts published after a given ISO8601 compliant date.",
-                            "type": "string",
-                            "format": "date-time",
-                            "required": false
-                        },
-                        "modified_after": {
-                            "description": "Limit response to posts modified after a given ISO8601 compliant date.",
-                            "type": "string",
-                            "format": "date-time",
-                            "required": false
-                        },
-                        "author": {
-                            "description": "Limit result set to posts assigned to specific authors.",
-                            "type": "array",
-                            "items": {
-                                "type": "integer"
-                            },
-                            "default": [],
-                            "required": false
-                        },
-                        "author_exclude": {
-                            "description": "Ensure result set excludes posts assigned to specific authors.",
-                            "type": "array",
-                            "items": {
-                                "type": "integer"
-                            },
-                            "default": [],
-                            "required": false
-                        },
-                        "before": {
-                            "description": "Limit response to posts published before a given ISO8601 compliant date.",
-                            "type": "string",
-                            "format": "date-time",
-                            "required": false
-                        },
-                        "modified_before": {
-                            "description": "Limit response to posts modified before a given ISO8601 compliant date.",
-                            "type": "string",
-                            "format": "date-time",
-                            "required": false
-                        },
-                        "exclude": {
-                            "description": "Ensure result set excludes specific IDs.",
-                            "type": "array",
-                            "items": {
-                                "type": "integer"
-                            },
-                            "default": [],
-                            "required": false
-                        },
-                        "include": {
-                            "description": "Limit result set to specific IDs.",
-                            "type": "array",
-                            "items": {
-                                "type": "integer"
-                            },
-                            "default": [],
-                            "required": false
-                        },
-                        "offset": {
-                            "description": "Offset the result set by a specific number of items.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "order": {
-                            "description": "Order sort attribute ascending or descending.",
-                            "type": "string",
-                            "default": "desc",
-                            "enum": [
-                                "asc",
-                                "desc"
-                            ],
-                            "required": false
-                        },
-                        "orderby": {
-                            "description": "Sort collection by post attribute.",
-                            "type": "string",
-                            "default": "date",
-                            "enum": [
-                                "author",
-                                "date",
-                                "id",
-                                "include",
-                                "modified",
-                                "parent",
-                                "relevance",
-                                "slug",
-                                "include_slugs",
-                                "title"
-                            ],
-                            "required": false
-                        },
-                        "parent": {
-                            "description": "Limit result set to items with particular parent IDs.",
-                            "type": "array",
-                            "items": {
-                                "type": "integer"
-                            },
-                            "default": [],
-                            "required": false
-                        },
-                        "parent_exclude": {
-                            "description": "Limit result set to all items except those of a particular parent ID.",
-                            "type": "array",
-                            "items": {
-                                "type": "integer"
-                            },
-                            "default": [],
-                            "required": false
-                        },
-                        "search_columns": {
-                            "default": [],
-                            "description": "Array of column names to be searched.",
-                            "type": "array",
-                            "items": {
-                                "enum": [
-                                    "post_title",
-                                    "post_content",
-                                    "post_excerpt"
-                                ],
-                                "type": "string"
-                            },
-                            "required": false
-                        },
-                        "slug": {
-                            "description": "Limit result set to posts with one or more specific slugs.",
-                            "type": "array",
-                            "items": {
-                                "type": "string"
-                            },
-                            "required": false
-                        },
-                        "status": {
-                            "default": "inherit",
-                            "description": "Limit result set to posts assigned one or more statuses.",
-                            "type": "array",
-                            "items": {
-                                "enum": [
-                                    "inherit",
-                                    "private",
-                                    "trash"
-                                ],
-                                "type": "string"
-                            },
-                            "required": false
-                        },
-                        "media_type": {
-                            "default": null,
-                            "description": "Limit result set to attachments of a particular media type.",
-                            "type": "string",
-                            "enum": [
-                                "image",
-                                "video",
-                                "text",
-                                "application",
-                                "audio"
-                            ],
-                            "required": false
-                        },
-                        "mime_type": {
-                            "default": null,
-                            "description": "Limit result set to attachments of a particular MIME type.",
-                            "type": "string",
-                            "required": false
-                        }
-                    }
-                },
-                {
-                    "methods": [
-                        "POST"
-                    ],
-                    "args": {
-                        "date": {
-                            "description": "The date the post was published, in the site's timezone.",
-                            "type": [
-                                "string",
-                                "null"
-                            ],
-                            "format": "date-time",
-                            "required": false
-                        },
-                        "date_gmt": {
-                            "description": "The date the post was published, as GMT.",
-                            "type": [
-                                "string",
-                                "null"
-                            ],
-                            "format": "date-time",
-                            "required": false
-                        },
-                        "slug": {
-                            "description": "An alphanumeric identifier for the post unique to its type.",
-                            "type": "string",
-                            "required": false
-                        },
-                        "status": {
-                            "description": "A named status for the post.",
-                            "type": "string",
-                            "enum": [
-                                "publish",
-                                "future",
-                                "draft",
-                                "pending",
-                                "private"
-                            ],
-                            "required": false
-                        },
-                        "title": {
-                            "description": "The title for the post.",
-                            "type": "object",
-                            "properties": {
-                                "raw": {
-                                    "description": "Title for the post, as it exists in the database.",
-                                    "type": "string",
-                                    "context": [
-                                        "edit"
-                                    ]
-                                },
-                                "rendered": {
-                                    "description": "HTML title for the post, transformed for display.",
-                                    "type": "string",
-                                    "context": [
-                                        "view",
-                                        "edit",
-                                        "embed"
-                                    ],
-                                    "readonly": true
-                                }
-                            },
-                            "required": false
-                        },
-                        "author": {
-                            "description": "The ID for the author of the post.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "comment_status": {
-                            "description": "Whether or not comments are open on the post.",
-                            "type": "string",
-                            "enum": [
-                                "open",
-                                "closed"
-                            ],
-                            "required": false
-                        },
-                        "ping_status": {
-                            "description": "Whether or not the post can be pinged.",
-                            "type": "string",
-                            "enum": [
-                                "open",
-                                "closed"
-                            ],
-                            "required": false
-                        },
-                        "meta": {
-                            "description": "Meta fields.",
-                            "type": "object",
-                            "properties": [],
-                            "required": false
-                        },
-                        "template": {
-                            "description": "The theme file to use to display the post.",
-                            "type": "string",
-                            "required": false
-                        },
-                        "alt_text": {
-                            "description": "Alternative text to display when attachment is not displayed.",
-                            "type": "string",
-                            "required": false
-                        },
-                        "caption": {
-                            "description": "The attachment caption.",
-                            "type": "object",
-                            "properties": {
-                                "raw": {
-                                    "description": "Caption for the attachment, as it exists in the database.",
-                                    "type": "string",
-                                    "context": [
-                                        "edit"
-                                    ]
-                                },
-                                "rendered": {
-                                    "description": "HTML caption for the attachment, transformed for display.",
-                                    "type": "string",
-                                    "context": [
-                                        "view",
-                                        "edit",
-                                        "embed"
-                                    ],
-                                    "readonly": true
-                                }
-                            },
-                            "required": false
-                        },
-                        "description": {
-                            "description": "The attachment description.",
-                            "type": "object",
-                            "properties": {
-                                "raw": {
-                                    "description": "Description for the attachment, as it exists in the database.",
-                                    "type": "string",
-                                    "context": [
-                                        "edit"
-                                    ]
-                                },
-                                "rendered": {
-                                    "description": "HTML description for the attachment, transformed for display.",
-                                    "type": "string",
-                                    "context": [
-                                        "view",
-                                        "edit"
-                                    ],
-                                    "readonly": true
-                                }
-                            },
-                            "required": false
-                        },
-                        "post": {
-                            "description": "The ID for the associated post of the attachment.",
-                            "type": "integer",
-                            "required": false
-                        }
-                    }
-                }
-            ],
-            "_links": {
-                "self": "http://example.org/index.php?rest_route=/wp/v2/media"
-            }
-        },
-        "/wp/v2/media/(?P<id>[\\d]+)": {
-            "namespace": "wp/v2",
-            "methods": [
-                "GET",
-                "POST",
-                "PUT",
-                "PATCH",
-                "DELETE"
-            ],
-            "endpoints": [
-                {
-                    "methods": [
-                        "GET"
-                    ],
-                    "args": {
-                        "id": {
-                            "description": "Unique identifier for the post.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "context": {
-                            "description": "Scope under which the request is made; determines fields present in response.",
-                            "type": "string",
-                            "enum": [
-                                "view",
-                                "embed",
-                                "edit"
-                            ],
-                            "default": "view",
-                            "required": false
-                        }
-                    }
-                },
-                {
-                    "methods": [
-                        "POST",
-                        "PUT",
-                        "PATCH"
-                    ],
-                    "args": {
-                        "id": {
-                            "description": "Unique identifier for the post.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "date": {
-                            "description": "The date the post was published, in the site's timezone.",
-                            "type": [
-                                "string",
-                                "null"
-                            ],
-                            "format": "date-time",
-                            "required": false
-                        },
-                        "date_gmt": {
-                            "description": "The date the post was published, as GMT.",
-                            "type": [
-                                "string",
-                                "null"
-                            ],
-                            "format": "date-time",
-                            "required": false
-                        },
-                        "slug": {
-                            "description": "An alphanumeric identifier for the post unique to its type.",
-                            "type": "string",
-                            "required": false
-                        },
-                        "status": {
-                            "description": "A named status for the post.",
-                            "type": "string",
-                            "enum": [
-                                "publish",
-                                "future",
-                                "draft",
-                                "pending",
-                                "private"
-                            ],
-                            "required": false
-                        },
-                        "title": {
-                            "description": "The title for the post.",
-                            "type": "object",
-                            "properties": {
-                                "raw": {
-                                    "description": "Title for the post, as it exists in the database.",
-                                    "type": "string",
-                                    "context": [
-                                        "edit"
-                                    ]
-                                },
-                                "rendered": {
-                                    "description": "HTML title for the post, transformed for display.",
-                                    "type": "string",
-                                    "context": [
-                                        "view",
-                                        "edit",
-                                        "embed"
-                                    ],
-                                    "readonly": true
-                                }
-                            },
-                            "required": false
-                        },
-                        "author": {
-                            "description": "The ID for the author of the post.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "comment_status": {
-                            "description": "Whether or not comments are open on the post.",
-                            "type": "string",
-                            "enum": [
-                                "open",
-                                "closed"
-                            ],
-                            "required": false
-                        },
-                        "ping_status": {
-                            "description": "Whether or not the post can be pinged.",
-                            "type": "string",
-                            "enum": [
-                                "open",
-                                "closed"
-                            ],
-                            "required": false
-                        },
-                        "meta": {
-                            "description": "Meta fields.",
-                            "type": "object",
-                            "properties": [],
-                            "required": false
-                        },
-                        "template": {
-                            "description": "The theme file to use to display the post.",
-                            "type": "string",
-                            "required": false
-                        },
-                        "alt_text": {
-                            "description": "Alternative text to display when attachment is not displayed.",
-                            "type": "string",
-                            "required": false
-                        },
-                        "caption": {
-                            "description": "The attachment caption.",
-                            "type": "object",
-                            "properties": {
-                                "raw": {
-                                    "description": "Caption for the attachment, as it exists in the database.",
-                                    "type": "string",
-                                    "context": [
-                                        "edit"
-                                    ]
-                                },
-                                "rendered": {
-                                    "description": "HTML caption for the attachment, transformed for display.",
-                                    "type": "string",
-                                    "context": [
-                                        "view",
-                                        "edit",
-                                        "embed"
-                                    ],
-                                    "readonly": true
-                                }
-                            },
-                            "required": false
-                        },
-                        "description": {
-                            "description": "The attachment description.",
-                            "type": "object",
-                            "properties": {
-                                "raw": {
-                                    "description": "Description for the attachment, as it exists in the database.",
-                                    "type": "string",
-                                    "context": [
-                                        "edit"
-                                    ]
-                                },
-                                "rendered": {
-                                    "description": "HTML description for the attachment, transformed for display.",
-                                    "type": "string",
-                                    "context": [
-                                        "view",
-                                        "edit"
-                                    ],
-                                    "readonly": true
-                                }
-                            },
-                            "required": false
-                        },
-                        "post": {
-                            "description": "The ID for the associated post of the attachment.",
-                            "type": "integer",
-                            "required": false
-                        }
-                    }
-                },
-                {
-                    "methods": [
-                        "DELETE"
-                    ],
-                    "args": {
-                        "id": {
-                            "description": "Unique identifier for the post.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "force": {
-                            "type": "boolean",
-                            "default": false,
-                            "description": "Whether to bypass Trash and force deletion.",
-                            "required": false
-                        }
-                    }
-                }
-            ]
-        },
-        "/wp/v2/media/(?P<id>[\\d]+)/post-process": {
-            "namespace": "wp/v2",
-            "methods": [
-                "POST"
-            ],
-            "endpoints": [
-                {
-                    "methods": [
-                        "POST"
-                    ],
-                    "args": {
-                        "id": {
-                            "description": "Unique identifier for the attachment.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "action": {
-                            "type": "string",
-                            "enum": [
-                                "create-image-subsizes"
-                            ],
-                            "required": true
-                        }
-                    }
-                }
-            ]
-        },
-        "/wp/v2/media/(?P<id>[\\d]+)/edit": {
-            "namespace": "wp/v2",
-            "methods": [
-                "POST"
-            ],
-            "endpoints": [
-                {
-                    "methods": [
-                        "POST"
-                    ],
-                    "args": {
-                        "src": {
-                            "description": "URL to the edited image file.",
-                            "type": "string",
-                            "format": "uri",
-                            "required": true
-                        },
-                        "modifiers": {
-                            "description": "Array of image edits.",
-                            "type": "array",
-                            "minItems": 1,
-                            "items": {
-                                "description": "Image edit.",
-                                "type": "object",
-                                "required": [
-                                    "type",
-                                    "args"
-                                ],
-                                "oneOf": [
-                                    {
-                                        "title": "Rotation",
-                                        "properties": {
-                                            "type": {
-                                                "description": "Rotation type.",
-                                                "type": "string",
-                                                "enum": [
-                                                    "rotate"
-                                                ]
-                                            },
-                                            "args": {
-                                                "description": "Rotation arguments.",
-                                                "type": "object",
-                                                "required": [
-                                                    "angle"
-                                                ],
-                                                "properties": {
-                                                    "angle": {
-                                                        "description": "Angle to rotate clockwise in degrees.",
-                                                        "type": "number"
-                                                    }
-                                                }
-                                            }
-                                        }
-                                    },
-                                    {
-                                        "title": "Crop",
-                                        "properties": {
-                                            "type": {
-                                                "description": "Crop type.",
-                                                "type": "string",
-                                                "enum": [
-                                                    "crop"
-                                                ]
-                                            },
-                                            "args": {
-                                                "description": "Crop arguments.",
-                                                "type": "object",
-                                                "required": [
-                                                    "left",
-                                                    "top",
-                                                    "width",
-                                                    "height"
-                                                ],
-                                                "properties": {
-                                                    "left": {
-                                                        "description": "Horizontal position from the left to begin the crop as a percentage of the image width.",
-                                                        "type": "number"
-                                                    },
-                                                    "top": {
-                                                        "description": "Vertical position from the top to begin the crop as a percentage of the image height.",
-                                                        "type": "number"
-                                                    },
-                                                    "width": {
-                                                        "description": "Width of the crop as a percentage of the image width.",
-                                                        "type": "number"
-                                                    },
-                                                    "height": {
-                                                        "description": "Height of the crop as a percentage of the image height.",
-                                                        "type": "number"
-                                                    }
-                                                }
-                                            }
-                                        }
-                                    }
-                                ]
-                            },
-                            "required": false
-                        },
-                        "rotation": {
-                            "description": "The amount to rotate the image clockwise in degrees. DEPRECATED: Use `modifiers` instead.",
-                            "type": "integer",
-                            "minimum": 0,
-                            "exclusiveMinimum": true,
-                            "maximum": 360,
-                            "exclusiveMaximum": true,
-                            "required": false
-                        },
-                        "x": {
-                            "description": "As a percentage of the image, the x position to start the crop from. DEPRECATED: Use `modifiers` instead.",
-                            "type": "number",
-                            "minimum": 0,
-                            "maximum": 100,
-                            "required": false
-                        },
-                        "y": {
-                            "description": "As a percentage of the image, the y position to start the crop from. DEPRECATED: Use `modifiers` instead.",
-                            "type": "number",
-                            "minimum": 0,
-                            "maximum": 100,
-                            "required": false
-                        },
-                        "width": {
-                            "description": "As a percentage of the image, the width to crop the image to. DEPRECATED: Use `modifiers` instead.",
-                            "type": "number",
-                            "minimum": 0,
-                            "maximum": 100,
-                            "required": false
-                        },
-                        "height": {
-                            "description": "As a percentage of the image, the height to crop the image to. DEPRECATED: Use `modifiers` instead.",
-                            "type": "number",
-                            "minimum": 0,
-                            "maximum": 100,
-                            "required": false
-                        }
-                    }
-                }
-            ]
-        },
</del><span class="cx" style="display: block; padding: 0 10px">         "/wp/v2/menu-items": {
</span><span class="cx" style="display: block; padding: 0 10px">             "namespace": "wp/v2",
</span><span class="cx" style="display: block; padding: 0 10px">             "methods": [
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5184,6 +4428,338 @@
</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">+        "/wp/v2/templates/(?P<parent>([^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)[\\/\\w%-]+)/revisions": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "parent": {
+                            "description": "The id of a template",
+                            "type": "string",
+                            "required": false
+                        },
+                        "context": {
+                            "description": "Scope under which the request is made; determines fields present in response.",
+                            "type": "string",
+                            "enum": [
+                                "view",
+                                "embed",
+                                "edit"
+                            ],
+                            "default": "view",
+                            "required": false
+                        },
+                        "page": {
+                            "description": "Current page of the collection.",
+                            "type": "integer",
+                            "default": 1,
+                            "minimum": 1,
+                            "required": false
+                        },
+                        "per_page": {
+                            "description": "Maximum number of items to be returned in result set.",
+                            "type": "integer",
+                            "minimum": 1,
+                            "maximum": 100,
+                            "required": false
+                        },
+                        "search": {
+                            "description": "Limit results to those matching a string.",
+                            "type": "string",
+                            "required": false
+                        },
+                        "exclude": {
+                            "description": "Ensure result set excludes specific IDs.",
+                            "type": "array",
+                            "items": {
+                                "type": "integer"
+                            },
+                            "default": [],
+                            "required": false
+                        },
+                        "include": {
+                            "description": "Limit result set to specific IDs.",
+                            "type": "array",
+                            "items": {
+                                "type": "integer"
+                            },
+                            "default": [],
+                            "required": false
+                        },
+                        "offset": {
+                            "description": "Offset the result set by a specific number of items.",
+                            "type": "integer",
+                            "required": false
+                        },
+                        "order": {
+                            "description": "Order sort attribute ascending or descending.",
+                            "type": "string",
+                            "default": "desc",
+                            "enum": [
+                                "asc",
+                                "desc"
+                            ],
+                            "required": false
+                        },
+                        "orderby": {
+                            "description": "Sort collection by object attribute.",
+                            "type": "string",
+                            "default": "date",
+                            "enum": [
+                                "date",
+                                "id",
+                                "include",
+                                "relevance",
+                                "slug",
+                                "include_slugs",
+                                "title"
+                            ],
+                            "required": false
+                        }
+                    }
+                }
+            ]
+        },
+        "/wp/v2/templates/(?P<parent>([^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)[\\/\\w%-]+)/revisions/(?P<id>[\\d]+)": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET",
+                "DELETE"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "parent": {
+                            "description": "The id of a template",
+                            "type": "string",
+                            "required": false
+                        },
+                        "id": {
+                            "description": "Unique identifier for the revision.",
+                            "type": "integer",
+                            "required": false
+                        },
+                        "context": {
+                            "description": "Scope under which the request is made; determines fields present in response.",
+                            "type": "string",
+                            "enum": [
+                                "view",
+                                "embed",
+                                "edit"
+                            ],
+                            "default": "view",
+                            "required": false
+                        }
+                    }
+                },
+                {
+                    "methods": [
+                        "DELETE"
+                    ],
+                    "args": {
+                        "parent": {
+                            "description": "The id of a template",
+                            "type": "string",
+                            "required": false
+                        },
+                        "id": {
+                            "description": "Unique identifier for the revision.",
+                            "type": "integer",
+                            "required": false
+                        },
+                        "force": {
+                            "type": "boolean",
+                            "default": false,
+                            "description": "Required to be true, as revisions do not support trashing.",
+                            "required": false
+                        }
+                    }
+                }
+            ]
+        },
+        "/wp/v2/templates/(?P<id>([^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)[\\/\\w%-]+)/autosaves": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET",
+                "POST"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "id": {
+                            "description": "The id of a template",
+                            "type": "string",
+                            "required": false
+                        },
+                        "context": {
+                            "description": "Scope under which the request is made; determines fields present in response.",
+                            "type": "string",
+                            "enum": [
+                                "view",
+                                "embed",
+                                "edit"
+                            ],
+                            "default": "view",
+                            "required": false
+                        }
+                    }
+                },
+                {
+                    "methods": [
+                        "POST"
+                    ],
+                    "args": {
+                        "id": {
+                            "description": "The id of a template",
+                            "type": "string",
+                            "required": false
+                        },
+                        "slug": {
+                            "description": "Unique slug identifying the template.",
+                            "type": "string",
+                            "minLength": 1,
+                            "pattern": "[a-zA-Z0-9_\\%-]+",
+                            "required": false
+                        },
+                        "theme": {
+                            "description": "Theme identifier for the template.",
+                            "type": "string",
+                            "required": false
+                        },
+                        "type": {
+                            "description": "Type of template.",
+                            "type": "string",
+                            "required": false
+                        },
+                        "content": {
+                            "description": "Content of template.",
+                            "type": [
+                                "object",
+                                "string"
+                            ],
+                            "properties": {
+                                "raw": {
+                                    "description": "Content for the template, as it exists in the database.",
+                                    "type": "string",
+                                    "context": [
+                                        "view",
+                                        "edit"
+                                    ]
+                                },
+                                "block_version": {
+                                    "description": "Version of the content block format used by the template.",
+                                    "type": "integer",
+                                    "context": [
+                                        "edit"
+                                    ],
+                                    "readonly": true
+                                }
+                            },
+                            "required": false
+                        },
+                        "title": {
+                            "description": "Title of template.",
+                            "type": [
+                                "object",
+                                "string"
+                            ],
+                            "properties": {
+                                "raw": {
+                                    "description": "Title for the template, as it exists in the database.",
+                                    "type": "string",
+                                    "context": [
+                                        "view",
+                                        "edit",
+                                        "embed"
+                                    ]
+                                },
+                                "rendered": {
+                                    "description": "HTML title for the template, transformed for display.",
+                                    "type": "string",
+                                    "context": [
+                                        "view",
+                                        "edit",
+                                        "embed"
+                                    ],
+                                    "readonly": true
+                                }
+                            },
+                            "required": false
+                        },
+                        "description": {
+                            "description": "Description of template.",
+                            "type": "string",
+                            "required": false
+                        },
+                        "status": {
+                            "description": "Status of template.",
+                            "type": "string",
+                            "enum": [
+                                "publish",
+                                "future",
+                                "draft",
+                                "pending",
+                                "private"
+                            ],
+                            "required": false
+                        },
+                        "author": {
+                            "description": "The ID for the author of the template.",
+                            "type": "integer",
+                            "required": false
+                        }
+                    }
+                }
+            ]
+        },
+        "/wp/v2/templates/(?P<parent>([^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)[\\/\\w%-]+)/autosaves/(?P<id>[\\d]+)": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "parent": {
+                            "description": "The id of a template",
+                            "type": "string",
+                            "required": false
+                        },
+                        "id": {
+                            "description": "The ID for the autosave.",
+                            "type": "integer",
+                            "required": false
+                        },
+                        "context": {
+                            "description": "Scope under which the request is made; determines fields present in response.",
+                            "type": "string",
+                            "enum": [
+                                "view",
+                                "embed",
+                                "edit"
+                            ],
+                            "default": "view",
+                            "required": false
+                        }
+                    }
+                }
+            ]
+        },
</ins><span class="cx" style="display: block; padding: 0 10px">         "/wp/v2/templates": {
</span><span class="cx" style="display: block; padding: 0 10px">             "namespace": "wp/v2",
</span><span class="cx" style="display: block; padding: 0 10px">             "methods": [
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5536,7 +5112,7 @@
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "/wp/v2/templates/(?P<parent>[\\d]+)/revisions": {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "/wp/v2/template-parts/(?P<parent>([^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)[\\/\\w%-]+)/revisions": {
</ins><span class="cx" style="display: block; padding: 0 10px">             "namespace": "wp/v2",
</span><span class="cx" style="display: block; padding: 0 10px">             "methods": [
</span><span class="cx" style="display: block; padding: 0 10px">                 "GET"
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5548,8 +5124,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                     ],
</span><span class="cx" style="display: block; padding: 0 10px">                     "args": {
</span><span class="cx" style="display: block; padding: 0 10px">                         "parent": {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                            "description": "The ID for the parent of the revision.",
-                            "type": "integer",
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                            "description": "The id of a template",
+                            "type": "string",
</ins><span class="cx" style="display: block; padding: 0 10px">                             "required": false
</span><span class="cx" style="display: block; padding: 0 10px">                         },
</span><span class="cx" style="display: block; padding: 0 10px">                         "context": {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5634,7 +5210,7 @@
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "/wp/v2/templates/(?P<parent>[\\d]+)/revisions/(?P<id>[\\d]+)": {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "/wp/v2/template-parts/(?P<parent>([^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)[\\/\\w%-]+)/revisions/(?P<id>[\\d]+)": {
</ins><span class="cx" style="display: block; padding: 0 10px">             "namespace": "wp/v2",
</span><span class="cx" style="display: block; padding: 0 10px">             "methods": [
</span><span class="cx" style="display: block; padding: 0 10px">                 "GET",
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5647,8 +5223,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                     ],
</span><span class="cx" style="display: block; padding: 0 10px">                     "args": {
</span><span class="cx" style="display: block; padding: 0 10px">                         "parent": {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                            "description": "The ID for the parent of the revision.",
-                            "type": "integer",
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                            "description": "The id of a template",
+                            "type": "string",
</ins><span class="cx" style="display: block; padding: 0 10px">                             "required": false
</span><span class="cx" style="display: block; padding: 0 10px">                         },
</span><span class="cx" style="display: block; padding: 0 10px">                         "id": {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5675,8 +5251,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                     ],
</span><span class="cx" style="display: block; padding: 0 10px">                     "args": {
</span><span class="cx" style="display: block; padding: 0 10px">                         "parent": {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                            "description": "The ID for the parent of the revision.",
-                            "type": "integer",
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                            "description": "The id of a template",
+                            "type": "string",
</ins><span class="cx" style="display: block; padding: 0 10px">                             "required": false
</span><span class="cx" style="display: block; padding: 0 10px">                         },
</span><span class="cx" style="display: block; padding: 0 10px">                         "id": {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5694,7 +5270,7 @@
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "/wp/v2/templates/(?P<id>[\\d]+)/autosaves": {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "/wp/v2/template-parts/(?P<id>([^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)[\\/\\w%-]+)/autosaves": {
</ins><span class="cx" style="display: block; padding: 0 10px">             "namespace": "wp/v2",
</span><span class="cx" style="display: block; padding: 0 10px">             "methods": [
</span><span class="cx" style="display: block; padding: 0 10px">                 "GET",
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5706,9 +5282,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                         "GET"
</span><span class="cx" style="display: block; padding: 0 10px">                     ],
</span><span class="cx" style="display: block; padding: 0 10px">                     "args": {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        "parent": {
-                            "description": "The ID for the parent of the autosave.",
-                            "type": "integer",
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        "id": {
+                            "description": "The id of a template",
+                            "type": "string",
</ins><span class="cx" style="display: block; padding: 0 10px">                             "required": false
</span><span class="cx" style="display: block; padding: 0 10px">                         },
</span><span class="cx" style="display: block; padding: 0 10px">                         "context": {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5729,9 +5305,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                         "POST"
</span><span class="cx" style="display: block; padding: 0 10px">                     ],
</span><span class="cx" style="display: block; padding: 0 10px">                     "args": {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        "parent": {
-                            "description": "The ID for the parent of the autosave.",
-                            "type": "integer",
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        "id": {
+                            "description": "The id of a template",
+                            "type": "string",
</ins><span class="cx" style="display: block; padding: 0 10px">                             "required": false
</span><span class="cx" style="display: block; padding: 0 10px">                         },
</span><span class="cx" style="display: block; padding: 0 10px">                         "slug": {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5827,12 +5403,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">                             "description": "The ID for the author of the template.",
</span><span class="cx" style="display: block; padding: 0 10px">                             "type": "integer",
</span><span class="cx" style="display: block; padding: 0 10px">                             "required": false
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        },
+                        "area": {
+                            "description": "Where the template part is intended for use (header, footer, etc.)",
+                            "type": "string",
+                            "required": 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 class="cx" style="display: block; padding: 0 10px">             ]
</span><span class="cx" style="display: block; padding: 0 10px">         },
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "/wp/v2/templates/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)": {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        "/wp/v2/template-parts/(?P<parent>([^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)[\\/\\w%-]+)/autosaves/(?P<id>[\\d]+)": {
</ins><span class="cx" style="display: block; padding: 0 10px">             "namespace": "wp/v2",
</span><span class="cx" style="display: block; padding: 0 10px">             "methods": [
</span><span class="cx" style="display: block; padding: 0 10px">                 "GET"
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5844,8 +5425,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                     ],
</span><span class="cx" style="display: block; padding: 0 10px">                     "args": {
</span><span class="cx" style="display: block; padding: 0 10px">                         "parent": {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                            "description": "The ID for the parent of the autosave.",
-                            "type": "integer",
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                            "description": "The id of a template",
+                            "type": "string",
</ins><span class="cx" style="display: block; padding: 0 10px">                             "required": false
</span><span class="cx" style="display: block; padding: 0 10px">                         },
</span><span class="cx" style="display: block; padding: 0 10px">                         "id": {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -6230,343 +5811,6 @@
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        "/wp/v2/template-parts/(?P<parent>[\\d]+)/revisions": {
-            "namespace": "wp/v2",
-            "methods": [
-                "GET"
-            ],
-            "endpoints": [
-                {
-                    "methods": [
-                        "GET"
-                    ],
-                    "args": {
-                        "parent": {
-                            "description": "The ID for the parent of the revision.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "context": {
-                            "description": "Scope under which the request is made; determines fields present in response.",
-                            "type": "string",
-                            "enum": [
-                                "view",
-                                "embed",
-                                "edit"
-                            ],
-                            "default": "view",
-                            "required": false
-                        },
-                        "page": {
-                            "description": "Current page of the collection.",
-                            "type": "integer",
-                            "default": 1,
-                            "minimum": 1,
-                            "required": false
-                        },
-                        "per_page": {
-                            "description": "Maximum number of items to be returned in result set.",
-                            "type": "integer",
-                            "minimum": 1,
-                            "maximum": 100,
-                            "required": false
-                        },
-                        "search": {
-                            "description": "Limit results to those matching a string.",
-                            "type": "string",
-                            "required": false
-                        },
-                        "exclude": {
-                            "description": "Ensure result set excludes specific IDs.",
-                            "type": "array",
-                            "items": {
-                                "type": "integer"
-                            },
-                            "default": [],
-                            "required": false
-                        },
-                        "include": {
-                            "description": "Limit result set to specific IDs.",
-                            "type": "array",
-                            "items": {
-                                "type": "integer"
-                            },
-                            "default": [],
-                            "required": false
-                        },
-                        "offset": {
-                            "description": "Offset the result set by a specific number of items.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "order": {
-                            "description": "Order sort attribute ascending or descending.",
-                            "type": "string",
-                            "default": "desc",
-                            "enum": [
-                                "asc",
-                                "desc"
-                            ],
-                            "required": false
-                        },
-                        "orderby": {
-                            "description": "Sort collection by object attribute.",
-                            "type": "string",
-                            "default": "date",
-                            "enum": [
-                                "date",
-                                "id",
-                                "include",
-                                "relevance",
-                                "slug",
-                                "include_slugs",
-                                "title"
-                            ],
-                            "required": false
-                        }
-                    }
-                }
-            ]
-        },
-        "/wp/v2/template-parts/(?P<parent>[\\d]+)/revisions/(?P<id>[\\d]+)": {
-            "namespace": "wp/v2",
-            "methods": [
-                "GET",
-                "DELETE"
-            ],
-            "endpoints": [
-                {
-                    "methods": [
-                        "GET"
-                    ],
-                    "args": {
-                        "parent": {
-                            "description": "The ID for the parent of the revision.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "id": {
-                            "description": "Unique identifier for the revision.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "context": {
-                            "description": "Scope under which the request is made; determines fields present in response.",
-                            "type": "string",
-                            "enum": [
-                                "view",
-                                "embed",
-                                "edit"
-                            ],
-                            "default": "view",
-                            "required": false
-                        }
-                    }
-                },
-                {
-                    "methods": [
-                        "DELETE"
-                    ],
-                    "args": {
-                        "parent": {
-                            "description": "The ID for the parent of the revision.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "id": {
-                            "description": "Unique identifier for the revision.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "force": {
-                            "type": "boolean",
-                            "default": false,
-                            "description": "Required to be true, as revisions do not support trashing.",
-                            "required": false
-                        }
-                    }
-                }
-            ]
-        },
-        "/wp/v2/template-parts/(?P<id>[\\d]+)/autosaves": {
-            "namespace": "wp/v2",
-            "methods": [
-                "GET",
-                "POST"
-            ],
-            "endpoints": [
-                {
-                    "methods": [
-                        "GET"
-                    ],
-                    "args": {
-                        "parent": {
-                            "description": "The ID for the parent of the autosave.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "context": {
-                            "description": "Scope under which the request is made; determines fields present in response.",
-                            "type": "string",
-                            "enum": [
-                                "view",
-                                "embed",
-                                "edit"
-                            ],
-                            "default": "view",
-                            "required": false
-                        }
-                    }
-                },
-                {
-                    "methods": [
-                        "POST"
-                    ],
-                    "args": {
-                        "parent": {
-                            "description": "The ID for the parent of the autosave.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "slug": {
-                            "description": "Unique slug identifying the template.",
-                            "type": "string",
-                            "minLength": 1,
-                            "pattern": "[a-zA-Z0-9_\\%-]+",
-                            "required": false
-                        },
-                        "theme": {
-                            "description": "Theme identifier for the template.",
-                            "type": "string",
-                            "required": false
-                        },
-                        "type": {
-                            "description": "Type of template.",
-                            "type": "string",
-                            "required": false
-                        },
-                        "content": {
-                            "description": "Content of template.",
-                            "type": [
-                                "object",
-                                "string"
-                            ],
-                            "properties": {
-                                "raw": {
-                                    "description": "Content for the template, as it exists in the database.",
-                                    "type": "string",
-                                    "context": [
-                                        "view",
-                                        "edit"
-                                    ]
-                                },
-                                "block_version": {
-                                    "description": "Version of the content block format used by the template.",
-                                    "type": "integer",
-                                    "context": [
-                                        "edit"
-                                    ],
-                                    "readonly": true
-                                }
-                            },
-                            "required": false
-                        },
-                        "title": {
-                            "description": "Title of template.",
-                            "type": [
-                                "object",
-                                "string"
-                            ],
-                            "properties": {
-                                "raw": {
-                                    "description": "Title for the template, as it exists in the database.",
-                                    "type": "string",
-                                    "context": [
-                                        "view",
-                                        "edit",
-                                        "embed"
-                                    ]
-                                },
-                                "rendered": {
-                                    "description": "HTML title for the template, transformed for display.",
-                                    "type": "string",
-                                    "context": [
-                                        "view",
-                                        "edit",
-                                        "embed"
-                                    ],
-                                    "readonly": true
-                                }
-                            },
-                            "required": false
-                        },
-                        "description": {
-                            "description": "Description of template.",
-                            "type": "string",
-                            "required": false
-                        },
-                        "status": {
-                            "description": "Status of template.",
-                            "type": "string",
-                            "enum": [
-                                "publish",
-                                "future",
-                                "draft",
-                                "pending",
-                                "private"
-                            ],
-                            "required": false
-                        },
-                        "author": {
-                            "description": "The ID for the author of the template.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "area": {
-                            "description": "Where the template part is intended for use (header, footer, etc.)",
-                            "type": "string",
-                            "required": false
-                        }
-                    }
-                }
-            ]
-        },
-        "/wp/v2/template-parts/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)": {
-            "namespace": "wp/v2",
-            "methods": [
-                "GET"
-            ],
-            "endpoints": [
-                {
-                    "methods": [
-                        "GET"
-                    ],
-                    "args": {
-                        "parent": {
-                            "description": "The ID for the parent of the autosave.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "id": {
-                            "description": "The ID for the autosave.",
-                            "type": "integer",
-                            "required": false
-                        },
-                        "context": {
-                            "description": "Scope under which the request is made; determines fields present in response.",
-                            "type": "string",
-                            "enum": [
-                                "view",
-                                "embed",
-                                "edit"
-                            ],
-                            "default": "view",
-                            "required": false
-                        }
-                    }
-                }
-            ]
-        },
</del><span class="cx" style="display: block; padding: 0 10px">         "/wp/v2/navigation": {
</span><span class="cx" style="display: block; padding: 0 10px">             "namespace": "wp/v2",
</span><span class="cx" style="display: block; padding: 0 10px">             "methods": [
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -7416,6 +6660,762 @@
</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">+        "/wp/v2/media": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET",
+                "POST"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "context": {
+                            "description": "Scope under which the request is made; determines fields present in response.",
+                            "type": "string",
+                            "enum": [
+                                "view",
+                                "embed",
+                                "edit"
+                            ],
+                            "default": "view",
+                            "required": false
+                        },
+                        "page": {
+                            "description": "Current page of the collection.",
+                            "type": "integer",
+                            "default": 1,
+                            "minimum": 1,
+                            "required": false
+                        },
+                        "per_page": {
+                            "description": "Maximum number of items to be returned in result set.",
+                            "type": "integer",
+                            "default": 10,
+                            "minimum": 1,
+                            "maximum": 100,
+                            "required": false
+                        },
+                        "search": {
+                            "description": "Limit results to those matching a string.",
+                            "type": "string",
+                            "required": false
+                        },
+                        "after": {
+                            "description": "Limit response to posts published after a given ISO8601 compliant date.",
+                            "type": "string",
+                            "format": "date-time",
+                            "required": false
+                        },
+                        "modified_after": {
+                            "description": "Limit response to posts modified after a given ISO8601 compliant date.",
+                            "type": "string",
+                            "format": "date-time",
+                            "required": false
+                        },
+                        "author": {
+                            "description": "Limit result set to posts assigned to specific authors.",
+                            "type": "array",
+                            "items": {
+                                "type": "integer"
+                            },
+                            "default": [],
+                            "required": false
+                        },
+                        "author_exclude": {
+                            "description": "Ensure result set excludes posts assigned to specific authors.",
+                            "type": "array",
+                            "items": {
+                                "type": "integer"
+                            },
+                            "default": [],
+                            "required": false
+                        },
+                        "before": {
+                            "description": "Limit response to posts published before a given ISO8601 compliant date.",
+                            "type": "string",
+                            "format": "date-time",
+                            "required": false
+                        },
+                        "modified_before": {
+                            "description": "Limit response to posts modified before a given ISO8601 compliant date.",
+                            "type": "string",
+                            "format": "date-time",
+                            "required": false
+                        },
+                        "exclude": {
+                            "description": "Ensure result set excludes specific IDs.",
+                            "type": "array",
+                            "items": {
+                                "type": "integer"
+                            },
+                            "default": [],
+                            "required": false
+                        },
+                        "include": {
+                            "description": "Limit result set to specific IDs.",
+                            "type": "array",
+                            "items": {
+                                "type": "integer"
+                            },
+                            "default": [],
+                            "required": false
+                        },
+                        "offset": {
+                            "description": "Offset the result set by a specific number of items.",
+                            "type": "integer",
+                            "required": false
+                        },
+                        "order": {
+                            "description": "Order sort attribute ascending or descending.",
+                            "type": "string",
+                            "default": "desc",
+                            "enum": [
+                                "asc",
+                                "desc"
+                            ],
+                            "required": false
+                        },
+                        "orderby": {
+                            "description": "Sort collection by post attribute.",
+                            "type": "string",
+                            "default": "date",
+                            "enum": [
+                                "author",
+                                "date",
+                                "id",
+                                "include",
+                                "modified",
+                                "parent",
+                                "relevance",
+                                "slug",
+                                "include_slugs",
+                                "title"
+                            ],
+                            "required": false
+                        },
+                        "parent": {
+                            "description": "Limit result set to items with particular parent IDs.",
+                            "type": "array",
+                            "items": {
+                                "type": "integer"
+                            },
+                            "default": [],
+                            "required": false
+                        },
+                        "parent_exclude": {
+                            "description": "Limit result set to all items except those of a particular parent ID.",
+                            "type": "array",
+                            "items": {
+                                "type": "integer"
+                            },
+                            "default": [],
+                            "required": false
+                        },
+                        "search_columns": {
+                            "default": [],
+                            "description": "Array of column names to be searched.",
+                            "type": "array",
+                            "items": {
+                                "enum": [
+                                    "post_title",
+                                    "post_content",
+                                    "post_excerpt"
+                                ],
+                                "type": "string"
+                            },
+                            "required": false
+                        },
+                        "slug": {
+                            "description": "Limit result set to posts with one or more specific slugs.",
+                            "type": "array",
+                            "items": {
+                                "type": "string"
+                            },
+                            "required": false
+                        },
+                        "status": {
+                            "default": "inherit",
+                            "description": "Limit result set to posts assigned one or more statuses.",
+                            "type": "array",
+                            "items": {
+                                "enum": [
+                                    "inherit",
+                                    "private",
+                                    "trash"
+                                ],
+                                "type": "string"
+                            },
+                            "required": false
+                        },
+                        "media_type": {
+                            "default": null,
+                            "description": "Limit result set to attachments of a particular media type.",
+                            "type": "string",
+                            "enum": [
+                                "image",
+                                "video",
+                                "text",
+                                "application",
+                                "audio"
+                            ],
+                            "required": false
+                        },
+                        "mime_type": {
+                            "default": null,
+                            "description": "Limit result set to attachments of a particular MIME type.",
+                            "type": "string",
+                            "required": false
+                        }
+                    }
+                },
+                {
+                    "methods": [
+                        "POST"
+                    ],
+                    "args": {
+                        "date": {
+                            "description": "The date the post was published, in the site's timezone.",
+                            "type": [
+                                "string",
+                                "null"
+                            ],
+                            "format": "date-time",
+                            "required": false
+                        },
+                        "date_gmt": {
+                            "description": "The date the post was published, as GMT.",
+                            "type": [
+                                "string",
+                                "null"
+                            ],
+                            "format": "date-time",
+                            "required": false
+                        },
+                        "slug": {
+                            "description": "An alphanumeric identifier for the post unique to its type.",
+                            "type": "string",
+                            "required": false
+                        },
+                        "status": {
+                            "description": "A named status for the post.",
+                            "type": "string",
+                            "enum": [
+                                "publish",
+                                "future",
+                                "draft",
+                                "pending",
+                                "private"
+                            ],
+                            "required": false
+                        },
+                        "title": {
+                            "description": "The title for the post.",
+                            "type": "object",
+                            "properties": {
+                                "raw": {
+                                    "description": "Title for the post, as it exists in the database.",
+                                    "type": "string",
+                                    "context": [
+                                        "edit"
+                                    ]
+                                },
+                                "rendered": {
+                                    "description": "HTML title for the post, transformed for display.",
+                                    "type": "string",
+                                    "context": [
+                                        "view",
+                                        "edit",
+                                        "embed"
+                                    ],
+                                    "readonly": true
+                                }
+                            },
+                            "required": false
+                        },
+                        "author": {
+                            "description": "The ID for the author of the post.",
+                            "type": "integer",
+                            "required": false
+                        },
+                        "comment_status": {
+                            "description": "Whether or not comments are open on the post.",
+                            "type": "string",
+                            "enum": [
+                                "open",
+                                "closed"
+                            ],
+                            "required": false
+                        },
+                        "ping_status": {
+                            "description": "Whether or not the post can be pinged.",
+                            "type": "string",
+                            "enum": [
+                                "open",
+                                "closed"
+                            ],
+                            "required": false
+                        },
+                        "meta": {
+                            "description": "Meta fields.",
+                            "type": "object",
+                            "properties": [],
+                            "required": false
+                        },
+                        "template": {
+                            "description": "The theme file to use to display the post.",
+                            "type": "string",
+                            "required": false
+                        },
+                        "alt_text": {
+                            "description": "Alternative text to display when attachment is not displayed.",
+                            "type": "string",
+                            "required": false
+                        },
+                        "caption": {
+                            "description": "The attachment caption.",
+                            "type": "object",
+                            "properties": {
+                                "raw": {
+                                    "description": "Caption for the attachment, as it exists in the database.",
+                                    "type": "string",
+                                    "context": [
+                                        "edit"
+                                    ]
+                                },
+                                "rendered": {
+                                    "description": "HTML caption for the attachment, transformed for display.",
+                                    "type": "string",
+                                    "context": [
+                                        "view",
+                                        "edit",
+                                        "embed"
+                                    ],
+                                    "readonly": true
+                                }
+                            },
+                            "required": false
+                        },
+                        "description": {
+                            "description": "The attachment description.",
+                            "type": "object",
+                            "properties": {
+                                "raw": {
+                                    "description": "Description for the attachment, as it exists in the database.",
+                                    "type": "string",
+                                    "context": [
+                                        "edit"
+                                    ]
+                                },
+                                "rendered": {
+                                    "description": "HTML description for the attachment, transformed for display.",
+                                    "type": "string",
+                                    "context": [
+                                        "view",
+                                        "edit"
+                                    ],
+                                    "readonly": true
+                                }
+                            },
+                            "required": false
+                        },
+                        "post": {
+                            "description": "The ID for the associated post of the attachment.",
+                            "type": "integer",
+                            "required": false
+                        }
+                    }
+                }
+            ],
+            "_links": {
+                "self": "http://example.org/index.php?rest_route=/wp/v2/media"
+            }
+        },
+        "/wp/v2/media/(?P<id>[\\d]+)": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET",
+                "POST",
+                "PUT",
+                "PATCH",
+                "DELETE"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "id": {
+                            "description": "Unique identifier for the post.",
+                            "type": "integer",
+                            "required": false
+                        },
+                        "context": {
+                            "description": "Scope under which the request is made; determines fields present in response.",
+                            "type": "string",
+                            "enum": [
+                                "view",
+                                "embed",
+                                "edit"
+                            ],
+                            "default": "view",
+                            "required": false
+                        }
+                    }
+                },
+                {
+                    "methods": [
+                        "POST",
+                        "PUT",
+                        "PATCH"
+                    ],
+                    "args": {
+                        "id": {
+                            "description": "Unique identifier for the post.",
+                            "type": "integer",
+                            "required": false
+                        },
+                        "date": {
+                            "description": "The date the post was published, in the site's timezone.",
+                            "type": [
+                                "string",
+                                "null"
+                            ],
+                            "format": "date-time",
+                            "required": false
+                        },
+                        "date_gmt": {
+                            "description": "The date the post was published, as GMT.",
+                            "type": [
+                                "string",
+                                "null"
+                            ],
+                            "format": "date-time",
+                            "required": false
+                        },
+                        "slug": {
+                            "description": "An alphanumeric identifier for the post unique to its type.",
+                            "type": "string",
+                            "required": false
+                        },
+                        "status": {
+                            "description": "A named status for the post.",
+                            "type": "string",
+                            "enum": [
+                                "publish",
+                                "future",
+                                "draft",
+                                "pending",
+                                "private"
+                            ],
+                            "required": false
+                        },
+                        "title": {
+                            "description": "The title for the post.",
+                            "type": "object",
+                            "properties": {
+                                "raw": {
+                                    "description": "Title for the post, as it exists in the database.",
+                                    "type": "string",
+                                    "context": [
+                                        "edit"
+                                    ]
+                                },
+                                "rendered": {
+                                    "description": "HTML title for the post, transformed for display.",
+                                    "type": "string",
+                                    "context": [
+                                        "view",
+                                        "edit",
+                                        "embed"
+                                    ],
+                                    "readonly": true
+                                }
+                            },
+                            "required": false
+                        },
+                        "author": {
+                            "description": "The ID for the author of the post.",
+                            "type": "integer",
+                            "required": false
+                        },
+                        "comment_status": {
+                            "description": "Whether or not comments are open on the post.",
+                            "type": "string",
+                            "enum": [
+                                "open",
+                                "closed"
+                            ],
+                            "required": false
+                        },
+                        "ping_status": {
+                            "description": "Whether or not the post can be pinged.",
+                            "type": "string",
+                            "enum": [
+                                "open",
+                                "closed"
+                            ],
+                            "required": false
+                        },
+                        "meta": {
+                            "description": "Meta fields.",
+                            "type": "object",
+                            "properties": [],
+                            "required": false
+                        },
+                        "template": {
+                            "description": "The theme file to use to display the post.",
+                            "type": "string",
+                            "required": false
+                        },
+                        "alt_text": {
+                            "description": "Alternative text to display when attachment is not displayed.",
+                            "type": "string",
+                            "required": false
+                        },
+                        "caption": {
+                            "description": "The attachment caption.",
+                            "type": "object",
+                            "properties": {
+                                "raw": {
+                                    "description": "Caption for the attachment, as it exists in the database.",
+                                    "type": "string",
+                                    "context": [
+                                        "edit"
+                                    ]
+                                },
+                                "rendered": {
+                                    "description": "HTML caption for the attachment, transformed for display.",
+                                    "type": "string",
+                                    "context": [
+                                        "view",
+                                        "edit",
+                                        "embed"
+                                    ],
+                                    "readonly": true
+                                }
+                            },
+                            "required": false
+                        },
+                        "description": {
+                            "description": "The attachment description.",
+                            "type": "object",
+                            "properties": {
+                                "raw": {
+                                    "description": "Description for the attachment, as it exists in the database.",
+                                    "type": "string",
+                                    "context": [
+                                        "edit"
+                                    ]
+                                },
+                                "rendered": {
+                                    "description": "HTML description for the attachment, transformed for display.",
+                                    "type": "string",
+                                    "context": [
+                                        "view",
+                                        "edit"
+                                    ],
+                                    "readonly": true
+                                }
+                            },
+                            "required": false
+                        },
+                        "post": {
+                            "description": "The ID for the associated post of the attachment.",
+                            "type": "integer",
+                            "required": false
+                        }
+                    }
+                },
+                {
+                    "methods": [
+                        "DELETE"
+                    ],
+                    "args": {
+                        "id": {
+                            "description": "Unique identifier for the post.",
+                            "type": "integer",
+                            "required": false
+                        },
+                        "force": {
+                            "type": "boolean",
+                            "default": false,
+                            "description": "Whether to bypass Trash and force deletion.",
+                            "required": false
+                        }
+                    }
+                }
+            ]
+        },
+        "/wp/v2/media/(?P<id>[\\d]+)/post-process": {
+            "namespace": "wp/v2",
+            "methods": [
+                "POST"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "POST"
+                    ],
+                    "args": {
+                        "id": {
+                            "description": "Unique identifier for the attachment.",
+                            "type": "integer",
+                            "required": false
+                        },
+                        "action": {
+                            "type": "string",
+                            "enum": [
+                                "create-image-subsizes"
+                            ],
+                            "required": true
+                        }
+                    }
+                }
+            ]
+        },
+        "/wp/v2/media/(?P<id>[\\d]+)/edit": {
+            "namespace": "wp/v2",
+            "methods": [
+                "POST"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "POST"
+                    ],
+                    "args": {
+                        "src": {
+                            "description": "URL to the edited image file.",
+                            "type": "string",
+                            "format": "uri",
+                            "required": true
+                        },
+                        "modifiers": {
+                            "description": "Array of image edits.",
+                            "type": "array",
+                            "minItems": 1,
+                            "items": {
+                                "description": "Image edit.",
+                                "type": "object",
+                                "required": [
+                                    "type",
+                                    "args"
+                                ],
+                                "oneOf": [
+                                    {
+                                        "title": "Rotation",
+                                        "properties": {
+                                            "type": {
+                                                "description": "Rotation type.",
+                                                "type": "string",
+                                                "enum": [
+                                                    "rotate"
+                                                ]
+                                            },
+                                            "args": {
+                                                "description": "Rotation arguments.",
+                                                "type": "object",
+                                                "required": [
+                                                    "angle"
+                                                ],
+                                                "properties": {
+                                                    "angle": {
+                                                        "description": "Angle to rotate clockwise in degrees.",
+                                                        "type": "number"
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    },
+                                    {
+                                        "title": "Crop",
+                                        "properties": {
+                                            "type": {
+                                                "description": "Crop type.",
+                                                "type": "string",
+                                                "enum": [
+                                                    "crop"
+                                                ]
+                                            },
+                                            "args": {
+                                                "description": "Crop arguments.",
+                                                "type": "object",
+                                                "required": [
+                                                    "left",
+                                                    "top",
+                                                    "width",
+                                                    "height"
+                                                ],
+                                                "properties": {
+                                                    "left": {
+                                                        "description": "Horizontal position from the left to begin the crop as a percentage of the image width.",
+                                                        "type": "number"
+                                                    },
+                                                    "top": {
+                                                        "description": "Vertical position from the top to begin the crop as a percentage of the image height.",
+                                                        "type": "number"
+                                                    },
+                                                    "width": {
+                                                        "description": "Width of the crop as a percentage of the image width.",
+                                                        "type": "number"
+                                                    },
+                                                    "height": {
+                                                        "description": "Height of the crop as a percentage of the image height.",
+                                                        "type": "number"
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }
+                                ]
+                            },
+                            "required": false
+                        },
+                        "rotation": {
+                            "description": "The amount to rotate the image clockwise in degrees. DEPRECATED: Use `modifiers` instead.",
+                            "type": "integer",
+                            "minimum": 0,
+                            "exclusiveMinimum": true,
+                            "maximum": 360,
+                            "exclusiveMaximum": true,
+                            "required": false
+                        },
+                        "x": {
+                            "description": "As a percentage of the image, the x position to start the crop from. DEPRECATED: Use `modifiers` instead.",
+                            "type": "number",
+                            "minimum": 0,
+                            "maximum": 100,
+                            "required": false
+                        },
+                        "y": {
+                            "description": "As a percentage of the image, the y position to start the crop from. DEPRECATED: Use `modifiers` instead.",
+                            "type": "number",
+                            "minimum": 0,
+                            "maximum": 100,
+                            "required": false
+                        },
+                        "width": {
+                            "description": "As a percentage of the image, the width to crop the image to. DEPRECATED: Use `modifiers` instead.",
+                            "type": "number",
+                            "minimum": 0,
+                            "maximum": 100,
+                            "required": false
+                        },
+                        "height": {
+                            "description": "As a percentage of the image, the height to crop the image to. DEPRECATED: Use `modifiers` instead.",
+                            "type": "number",
+                            "minimum": 0,
+                            "maximum": 100,
+                            "required": false
+                        }
+                    }
+                }
+            ]
+        },
</ins><span class="cx" style="display: block; padding: 0 10px">         "/wp/v2/types": {
</span><span class="cx" style="display: block; padding: 0 10px">             "namespace": "wp/v2",
</span><span class="cx" style="display: block; padding: 0 10px">             "methods": [
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -8780,12 +8780,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                 "enum": {
</span><span class="cx" style="display: block; padding: 0 10px">                                     "post": "post",
</span><span class="cx" style="display: block; padding: 0 10px">                                     "page": "page",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                    "attachment": "attachment",
</del><span class="cx" style="display: block; padding: 0 10px">                                     "nav_menu_item": "nav_menu_item",
</span><span class="cx" style="display: block; padding: 0 10px">                                     "wp_block": "wp_block",
</span><span class="cx" style="display: block; padding: 0 10px">                                     "wp_template": "wp_template",
</span><span class="cx" style="display: block; padding: 0 10px">                                     "wp_template_part": "wp_template_part",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                    "wp_navigation": "wp_navigation"
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                    "wp_navigation": "wp_navigation",
+                                    "attachment": "attachment"
</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">                             "required": false
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -12352,36 +12352,6 @@
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-    "attachment": {
-        "description": "",
-        "hierarchical": false,
-        "has_archive": false,
-        "name": "Media",
-        "slug": "attachment",
-        "icon": "dashicons-admin-media",
-        "taxonomies": [],
-        "rest_base": "media",
-        "rest_namespace": "wp/v2",
-        "_links": {
-            "collection": [
-                {
-                    "href": "http://example.org/index.php?rest_route=/wp/v2/types"
-                }
-            ],
-            "wp:items": [
-                {
-                    "href": "http://example.org/index.php?rest_route=/wp/v2/media"
-                }
-            ],
-            "curies": [
-                {
-                    "name": "wp",
-                    "href": "https://api.w.org/{rel}",
-                    "templated": true
-                }
-            ]
-        }
-    },
</del><span class="cx" style="display: block; padding: 0 10px">     "nav_menu_item": {
</span><span class="cx" style="display: block; padding: 0 10px">         "description": "",
</span><span class="cx" style="display: block; padding: 0 10px">         "hierarchical": false,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -12535,6 +12505,36 @@
</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">+    },
+    "attachment": {
+        "description": "",
+        "hierarchical": false,
+        "has_archive": false,
+        "name": "Media",
+        "slug": "attachment",
+        "icon": "dashicons-admin-media",
+        "taxonomies": [],
+        "rest_base": "media",
+        "rest_namespace": "wp/v2",
+        "_links": {
+            "collection": [
+                {
+                    "href": "http://example.org/index.php?rest_route=/wp/v2/types"
+                }
+            ],
+            "wp:items": [
+                {
+                    "href": "http://example.org/index.php?rest_route=/wp/v2/media"
+                }
+            ],
+            "curies": [
+                {
+                    "name": "wp",
+                    "href": "https://api.w.org/{rel}",
+                    "templated": true
+                }
+            ]
+        }
</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>
</div>

</body>
</html>