Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[REST] Restore the missing double slash in the ID received by /templates #36881

Merged
merged 13 commits into from
Nov 26, 2021
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ public function register_routes() {
'permission_callback' => array( $this, 'get_item_permissions_check' ),
'args' => array(
'id' => array(
'description' => __( 'The id of a template', 'gutenberg' ),
'type' => 'string',
'description' => __( 'The id of a template', 'gutenberg' ),
'type' => 'string',
'sanitize_callback' => array( $this, '_sanitize_template_id' ),
),
),
),
Expand Down Expand Up @@ -116,6 +117,36 @@ protected function permissions_check() {
return true;
}

/**
* Requesting this endpoint for a template like "twentytwentytwo//home" requires using
* a path like /wp/v2/templates/twentytwentytwo//home. There are special cases when
* WordPress routing corrects the name to contain only a single slash like "twentytwentytwo/home".
*
* This method doubles the last slash if it's not already doubled. It relies on the template
* ID format {theme_name}//{template_slug} and the fact that slugs cannot contain slashes.
*
* See https://core.trac.wordpress.org/ticket/54507 for more context
*
* @param string $id Template ID.
* @return string Sanitized template ID.
*/
public function _sanitize_template_id( $id ) {
$last_slash_pos = strrpos( $id, '/' );
if ( false === $last_slash_pos ) {
return $id;
}

$is_double_slashed = substr( $id, $last_slash_pos - 1, 1 ) === '/';
if ( $is_double_slashed ) {
return $id;
}
return (
substr( $id, 0, $last_slash_pos )
. '/'
. substr( $id, $last_slash_pos )
);
}

/**
* Checks if a given request has access to read templates.
*
Expand Down
70 changes: 70 additions & 0 deletions phpunit/class-gutenberg-rest-template-controller-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,76 @@ public function test_get_item() {
);
}

/**
* Ticket 54507
*
* @dataProvider get_template_endpoint_urls
*/
public function test_get_item_works_with_a_single_slash( $endpoint_url ) {
wp_set_current_user( self::$admin_id );
$request = new WP_REST_Request( 'GET', $endpoint_url );
$response = rest_get_server()->dispatch( $request );

$data = $response->get_data();
unset( $data['content'] );
unset( $data['_links'] );

$this->assertEquals(
array(
'id' => 'tt1-blocks//index',
'theme' => 'tt1-blocks',
'slug' => 'index',
'title' => array(
'raw' => 'Index',
'rendered' => 'Index',
),
'description' => 'The default template used when no other template is available. This is a required template in WordPress.',
'status' => 'publish',
'source' => 'theme',
'type' => 'wp_template',
'wp_id' => null,
'has_theme_file' => true,
),
$data
);
}

/**
*
*/
public function get_template_endpoint_urls() {
return array(
array( '/wp/v2/templates/tt1-blocks/index' ),
array( '/wp/v2/templates/tt1-blocks//index' ),
);
}

/**
* Ticket 54507
*
* @dataProvider get_template_ids_to_sanitize
*/
public function test_sanitize_template_id( $input_id, $sanitized_id ) {
$endpoint = new Gutenberg_REST_Templates_Controller( 'wp_template' );
$this->assertEquals(
$sanitized_id,
$endpoint->_sanitize_template_id( $input_id )
);
}

/**
*
*/
public function get_template_ids_to_sanitize() {
return array(
array( 'tt1-blocks/index', 'tt1-blocks//index' ),
array( 'tt1-blocks//index', 'tt1-blocks//index' ),

array( 'theme-experiments/tt1-blocks/index', 'theme-experiments/tt1-blocks//index' ),
array( 'theme-experiments/tt1-blocks//index', 'theme-experiments/tt1-blocks//index' ),
);
}

public function test_create_item() {
wp_set_current_user( self::$admin_id );
$request = new WP_REST_Request( 'POST', '/wp/v2/templates' );
Expand Down