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 API: Use posts endpoint for reusable blocks #10751

Merged
merged 7 commits into from
Oct 19, 2018
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
REST: Restore WP_REST_Blocks_Controller for permissions check
  • Loading branch information
aduth committed Oct 19, 2018
commit e3b127985e219837bab14ee0ab8a2b2e2c0aaf0b
36 changes: 36 additions & 0 deletions lib/class-wp-rest-blocks-controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
/**
* Reusable blocks REST API: WP_REST_Blocks_Controller class
*
* @package gutenberg
* @since 0.10.0
*/

/**
* Controller which provides a REST endpoint for Gutenberg to read, create,
* edit and delete reusable blocks. Blocks are stored as posts with the wp_block
* post type.
*
* @since 0.10.0
*
* @see WP_REST_Controller
*/
class WP_REST_Blocks_Controller extends WP_REST_Posts_Controller {
/**
* Checks if a block can be read.
*
* @since 2.1.0
*
* @param object $post Post object that backs the block.
* @return bool Whether the block can be read.
*/
public function check_read_permission( $post ) {
// Ensure that the user is logged in and has the read_blocks capability.
$post_type = get_post_type_object( $post->post_type );
if ( ! current_user_can( $post_type->cap->read_post, $post->ID ) ) {
return false;
}

return parent::check_read_permission( $post );
}
}
3 changes: 3 additions & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
@@ -12,6 +12,9 @@
// These files only need to be loaded if within a rest server instance
// which this class will exist if that is the case.
if ( class_exists( 'WP_REST_Controller' ) ) {
if ( ! class_exists( 'WP_REST_Blocks_Controller' ) ) {
require dirname( __FILE__ ) . '/class-wp-rest-blocks-controller.php';
}
if ( ! class_exists( 'WP_REST_Autosaves_Controller' ) ) {
require dirname( __FILE__ ) . '/class-wp-rest-autosaves-controller.php';
}
23 changes: 12 additions & 11 deletions lib/register.php
Original file line number Diff line number Diff line change
@@ -439,24 +439,25 @@ function gutenberg_register_post_types() {
register_post_type(
'wp_block',
array(
'labels' => array(
'labels' => array(
'name' => __( 'Blocks', 'gutenberg' ),
'singular_name' => __( 'Block', 'gutenberg' ),
'search_items' => __( 'Search Blocks', 'gutenberg' ),
),
'public' => false,
'show_ui' => true,
'show_in_menu' => false,
'rewrite' => false,
'show_in_rest' => true,
'rest_base' => 'blocks',
'capability_type' => 'block',
'capabilities' => array(
'public' => false,
'show_ui' => true,
'show_in_menu' => false,
'rewrite' => false,
'show_in_rest' => true,
'rest_base' => 'blocks',
'rest_controller_class' => 'WP_REST_Blocks_Controller',
'capability_type' => 'block',
'capabilities' => array(
'read' => 'read_blocks',
'create_posts' => 'create_blocks',
),
'map_meta_cap' => true,
'supports' => array(
'map_meta_cap' => true,
'supports' => array(
'title',
'editor',
),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the CPT now supports title and editor, we can go a step further and remove this line from gutenberg.php:267:

unset( $actions['edit'] );

This PR would then close #9964.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the CPT now supports title and editor, we can go a step further and remove this line from gutenberg.php:267:

Updated in c8ad73a

179 changes: 179 additions & 0 deletions phpunit/class-rest-blocks-controller-test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
<?php
/**
* WP_REST_Blocks_Controller tests
*
* @package gutenberg
*/

/**
* Tests for WP_REST_Blocks_Controller.
*/
class REST_Blocks_Controller_Test extends WP_UnitTestCase {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change might make someone somewhere upset, but implementing 9 abstract methods when the controller implements only check_read_permission is excessive, and would hint more at unreliability of tests for the extended posts controller.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implementing 9 abstract methods when the controller implements only check_read_permission is excessive, and would hint more at unreliability of tests for the extended posts controller.

Yeah, it's annoying. We can improve upon it at some point.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess another option could be extending WP_Test_REST_Posts_Controller, to inherit the tests implemented there we're assuming for our controller. Still lots of redundancy.

Or another way to signal "this is already tested elsewhere"? Extend WP_Test_REST_Controller_Testcase with the methods implemented as simple $this->skip with a message explaining?

(Not going to let this block merge)


/**
* Our fake block's post ID.
*
* @var int
*/
protected static $post_id;

/**
* Our fake user's ID.
*
* @var int
*/
protected static $user_id;

/**
* Create fake data before our tests run.
*
* @param WP_UnitTest_Factory $factory Helper that lets us create fake data.
*/
public static function wpSetUpBeforeClass( $factory ) {
self::$post_id = wp_insert_post(
array(
'post_type' => 'wp_block',
'post_status' => 'publish',
'post_title' => 'My cool block',
'post_content' => '<!-- wp:core/paragraph --><p>Hello!</p><!-- /wp:core/paragraph -->',
)
);

self::$user_id = $factory->user->create(
array(
'role' => 'editor',
)
);
}

/**
* Delete our fake data after our tests run.
*/
public static function wpTearDownAfterClass() {
wp_delete_post( self::$post_id );

self::delete_user( self::$user_id );
}

/**
* Test cases for test_capabilities().
*/
public function data_capabilities() {
return array(
array( 'create', 'editor', 201 ),
array( 'create', 'author', 201 ),
array( 'create', 'contributor', 403 ),
array( 'create', null, 401 ),

array( 'read', 'editor', 200 ),
array( 'read', 'author', 200 ),
array( 'read', 'contributor', 200 ),
array( 'read', null, 401 ),

array( 'update_delete_own', 'editor', 200 ),
array( 'update_delete_own', 'author', 200 ),
array( 'update_delete_own', 'contributor', 403 ),

array( 'update_delete_others', 'editor', 200 ),
array( 'update_delete_others', 'author', 403 ),
array( 'update_delete_others', 'contributor', 403 ),
array( 'update_delete_others', null, 401 ),
);
}

/**
* Exhaustively check that each role either can or cannot create, edit,
* update, and delete reusable blocks.
*
* @dataProvider data_capabilities
*/
public function test_capabilities( $action, $role, $expected_status ) {
if ( $role ) {
$user_id = $this->factory->user->create( array( 'role' => $role ) );
wp_set_current_user( $user_id );
} else {
wp_set_current_user( 0 );
}

switch ( $action ) {
case 'create':
$request = new WP_REST_Request( 'POST', '/wp/v2/blocks' );
$request->set_body_params(
array(
'title' => 'Test',
'content' => '<!-- wp:core/paragraph --><p>Test</p><!-- /wp:core/paragraph -->',
)
);

$response = rest_get_server()->dispatch( $request );
$this->assertEquals( $expected_status, $response->get_status() );

break;

case 'read':
$request = new WP_REST_Request( 'GET', '/wp/v2/blocks/' . self::$post_id );

$response = rest_get_server()->dispatch( $request );
$this->assertEquals( $expected_status, $response->get_status() );

break;

case 'update_delete_own':
$post_id = wp_insert_post(
array(
'post_type' => 'wp_block',
'post_status' => 'publish',
'post_title' => 'My cool block',
'post_content' => '<!-- wp:core/paragraph --><p>Hello!</p><!-- /wp:core/paragraph -->',
'post_author' => $user_id,
)
);

$request = new WP_REST_Request( 'PUT', '/wp/v2/blocks/' . $post_id );
$request->set_body_params(
array(
'title' => 'Test',
'content' => '<!-- wp:core/paragraph --><p>Test</p><!-- /wp:core/paragraph -->',
)
);

$response = rest_get_server()->dispatch( $request );
$this->assertEquals( $expected_status, $response->get_status() );

$request = new WP_REST_Request( 'DELETE', '/wp/v2/blocks/' . $post_id );

$response = rest_get_server()->dispatch( $request );
$this->assertEquals( $expected_status, $response->get_status() );

wp_delete_post( $post_id );

break;

case 'update_delete_others':
$request = new WP_REST_Request( 'PUT', '/wp/v2/blocks/' . self::$post_id );
$request->set_body_params(
array(
'title' => 'Test',
'content' => '<!-- wp:core/paragraph --><p>Test</p><!-- /wp:core/paragraph -->',
)
);

$response = rest_get_server()->dispatch( $request );
$this->assertEquals( $expected_status, $response->get_status() );

$request = new WP_REST_Request( 'DELETE', '/wp/v2/blocks/' . self::$post_id );

$response = rest_get_server()->dispatch( $request );
$this->assertEquals( $expected_status, $response->get_status() );

break;

default:
$this->fail( "'$action' is not a valid action." );
}

if ( isset( $user_id ) ) {
self::delete_user( $user_id );
}
}
}