mirror of
https://github.com/gosticks/wordpress-develop.git
synced 2025-10-16 12:05:38 +00:00
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 [52062], 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. git-svn-id: https://develop.svn.wordpress.org/trunk@56819 602fd350-edb4-49c9-b593-d223f7449a82
512 lines
16 KiB
PHP
512 lines
16 KiB
PHP
<?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 );
|
|
}
|
|
}
|