diff --git a/gutenberg.php b/gutenberg.php
index 307d349f0cf283..d420337390d9ea 100644
--- a/gutenberg.php
+++ b/gutenberg.php
@@ -264,8 +264,15 @@ function gutenberg_add_edit_link( $actions, $post ) {
$title = _draft_or_post_title( $post->ID );
if ( 'wp_block' === $post->post_type ) {
- unset( $actions['edit'] );
unset( $actions['inline hide-if-no-js'] );
+
+ // Export uses block raw content, which is only returned from the post
+ // REST endpoint via `context=edit`, requiring edit capability.
+ $post_type = get_post_type_object( $post->post_type );
+ if ( ! current_user_can( $post_type->cap->edit_post, $post->ID ) ) {
+ return $actions;
+ }
+
$actions['export'] = sprintf(
'',
$post->ID,
diff --git a/lib/class-wp-rest-blocks-controller.php b/lib/class-wp-rest-blocks-controller.php
index 75f69afe917598..9689820c7494be 100644
--- a/lib/class-wp-rest-blocks-controller.php
+++ b/lib/class-wp-rest-blocks-controller.php
@@ -33,91 +33,4 @@ public function check_read_permission( $post ) {
return parent::check_read_permission( $post );
}
-
- /**
- * Handle a DELETE request.
- *
- * @since 1.10.0
- *
- * @param WP_REST_Request $request Full details about the request.
- * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
- */
- public function delete_item( $request ) {
- // Always hard-delete a block.
- $request->set_param( 'force', true );
-
- return parent::delete_item( $request );
- }
-
- /**
- * Given an update or create request, build the post object that is saved to
- * the database.
- *
- * @since 1.10.0
- *
- * @param WP_REST_Request $request Request object.
- * @return stdClass|WP_Error Post object or WP_Error.
- */
- public function prepare_item_for_database( $request ) {
- $prepared_post = parent::prepare_item_for_database( $request );
-
- // Force blocks to always be published.
- $prepared_post->post_status = 'publish';
-
- return $prepared_post;
- }
-
- /**
- * Given a block from the database, build the array that is returned from an
- * API response.
- *
- * @since 1.10.0
- *
- * @param WP_Post $post Post object that backs the block.
- * @param WP_REST_Request $request Request object.
- * @return WP_REST_Response Response object.
- */
- public function prepare_item_for_response( $post, $request ) {
- $data = array(
- 'id' => $post->ID,
- 'title' => $post->post_title,
- 'content' => $post->post_content,
- );
-
- $response = rest_ensure_response( $data );
-
- return apply_filters( "rest_prepare_{$this->post_type}", $response, $post, $request );
- }
-
- /**
- * Builds the block's schema, conforming to JSON Schema.
- *
- * @since 1.10.0
- *
- * @return array Item schema data.
- */
- public function get_item_schema() {
- return array(
- '$schema' => 'http://json-schema.org/schema#',
- 'title' => $this->post_type,
- 'type' => 'object',
- 'properties' => array(
- 'id' => array(
- 'description' => __( 'Unique identifier for the block.', 'gutenberg' ),
- 'type' => 'integer',
- 'readonly' => true,
- ),
- 'title' => array(
- 'description' => __( 'The block’s title.', 'gutenberg' ),
- 'type' => 'string',
- 'required' => true,
- ),
- 'content' => array(
- 'description' => __( 'The block’s HTML content.', 'gutenberg' ),
- 'type' => 'string',
- 'required' => true,
- ),
- ),
- );
- }
}
diff --git a/lib/register.php b/lib/register.php
index 1fc63f8c22d6de..8a5c02777cdc66 100644
--- a/lib/register.php
+++ b/lib/register.php
@@ -457,7 +457,10 @@ function gutenberg_register_post_types() {
'create_posts' => 'create_blocks',
),
'map_meta_cap' => true,
- 'supports' => false,
+ 'supports' => array(
+ 'title',
+ 'editor',
+ ),
)
);
diff --git a/packages/editor/src/store/effects/reusable-blocks.js b/packages/editor/src/store/effects/reusable-blocks.js
index d1cb3834693283..a895d9eae615f8 100644
--- a/packages/editor/src/store/effects/reusable-blocks.js
+++ b/packages/editor/src/store/effects/reusable-blocks.js
@@ -36,6 +36,7 @@ import {
getBlocks,
getBlocksByClientId,
} from '../selectors';
+import { getPostRawValue } from '../reducer';
/**
* Module Constants
@@ -61,26 +62,25 @@ export const fetchReusableBlocks = async ( action, store ) => {
let result;
if ( id ) {
- result = apiFetch( { path: `/wp/v2/${ postType.rest_base }/${ id }` } );
+ result = apiFetch( { path: `/wp/v2/${ postType.rest_base }/${ id }?context=edit` } );
} else {
- result = apiFetch( { path: `/wp/v2/${ postType.rest_base }?per_page=-1` } );
+ result = apiFetch( { path: `/wp/v2/${ postType.rest_base }?per_page=-1&context=edit` } );
}
try {
const reusableBlockOrBlocks = await result;
dispatch( receiveReusableBlocksAction( map(
castArray( reusableBlockOrBlocks ),
- ( reusableBlock ) => {
- const parsedBlocks = parse( reusableBlock.content );
- if ( parsedBlocks.length === 1 ) {
- return {
- reusableBlock,
- parsedBlock: parsedBlocks[ 0 ],
- };
- }
+ ( post ) => {
+ const parsedBlocks = parse( post.content.raw );
return {
- reusableBlock,
- parsedBlock: createBlock( 'core/template', {}, parsedBlocks ),
+ reusableBlock: {
+ id: post.id,
+ title: getPostRawValue( post.title ),
+ },
+ parsedBlock: parsedBlocks.length === 1 ?
+ parsedBlocks[ 0 ] :
+ createBlock( 'core/template', {}, parsedBlocks ),
};
}
) ) );
@@ -119,7 +119,7 @@ export const saveReusableBlocks = async ( action, store ) => {
const reusableBlock = getBlock( state, clientId );
const content = serialize( reusableBlock.name === 'core/template' ? reusableBlock.innerBlocks : reusableBlock );
- const data = isTemporary ? { title, content } : { id, title, content };
+ const data = isTemporary ? { title, content, status: 'publish' } : { id, title, content, status: 'publish' };
const path = isTemporary ? `/wp/v2/${ postType.rest_base }` : `/wp/v2/${ postType.rest_base }/${ id }`;
const method = isTemporary ? 'POST' : 'PUT';
@@ -184,7 +184,10 @@ export const deleteReusableBlocks = async ( action, store ) => {
] ) );
try {
- await apiFetch( { path: `/wp/v2/${ postType.rest_base }/${ id }`, method: 'DELETE' } );
+ await apiFetch( {
+ path: `/wp/v2/${ postType.rest_base }/${ id }`,
+ method: 'DELETE',
+ } );
dispatch( {
type: 'DELETE_REUSABLE_BLOCK_SUCCESS',
id,
diff --git a/packages/editor/src/store/effects/test/reusable-blocks.js b/packages/editor/src/store/effects/test/reusable-blocks.js
index 2ff4081a650a79..dcfe687f744129 100644
--- a/packages/editor/src/store/effects/test/reusable-blocks.js
+++ b/packages/editor/src/store/effects/test/reusable-blocks.js
@@ -70,8 +70,12 @@ describe( 'reusable blocks effects', () => {
const blockPromise = Promise.resolve( [
{
id: 123,
- title: 'My cool block',
- content: '',
+ title: {
+ raw: 'My cool block',
+ },
+ content: {
+ raw: '',
+ },
},
] );
const postTypePromise = Promise.resolve( {
@@ -97,7 +101,6 @@ describe( 'reusable blocks effects', () => {
reusableBlock: {
id: 123,
title: 'My cool block',
- content: '',
},
parsedBlock: expect.objectContaining( {
name: 'core/test-block',
@@ -115,8 +118,12 @@ describe( 'reusable blocks effects', () => {
it( 'should fetch a single reusable block', async () => {
const blockPromise = Promise.resolve( {
id: 123,
- title: 'My cool block',
- content: '',
+ title: {
+ raw: 'My cool block',
+ },
+ content: {
+ raw: '',
+ },
} );
const postTypePromise = Promise.resolve( {
slug: 'wp_block', rest_base: 'blocks',
@@ -141,7 +148,6 @@ describe( 'reusable blocks effects', () => {
reusableBlock: {
id: 123,
title: 'My cool block',
- content: '',
},
parsedBlock: expect.objectContaining( {
name: 'core/test-block',
diff --git a/packages/list-reusable-blocks/src/utils/export.js b/packages/list-reusable-blocks/src/utils/export.js
index e3ca3b8b07788e..a0d75bd427c1e7 100644
--- a/packages/list-reusable-blocks/src/utils/export.js
+++ b/packages/list-reusable-blocks/src/utils/export.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { pick, kebabCase } from 'lodash';
+import { kebabCase } from 'lodash';
/**
* WordPress dependencies
@@ -20,12 +20,15 @@ import { download } from './file';
*/
async function exportReusableBlock( id ) {
const postType = await apiFetch( { path: `/wp/v2/types/wp_block` } );
- const reusableBlock = await apiFetch( { path: `/wp/v2/${ postType.rest_base }/${ id }` } );
+ const post = await apiFetch( { path: `/wp/v2/${ postType.rest_base }/${ id }?context=edit` } );
+ const title = post.title.raw;
+ const content = post.content.raw;
const fileContent = JSON.stringify( {
__file: 'wp_block',
- ...pick( reusableBlock, [ 'title', 'content' ] ),
+ title,
+ content,
}, null, 2 );
- const fileName = kebabCase( reusableBlock.title ) + '.json';
+ const fileName = kebabCase( title ) + '.json';
download( fileName, fileContent, 'application/json' );
}
diff --git a/packages/list-reusable-blocks/src/utils/import.js b/packages/list-reusable-blocks/src/utils/import.js
index 6bf02048952840..e885cbb6b2b24c 100644
--- a/packages/list-reusable-blocks/src/utils/import.js
+++ b/packages/list-reusable-blocks/src/utils/import.js
@@ -42,6 +42,7 @@ async function importReusableBlock( file ) {
data: {
title: parsedContent.title,
content: parsedContent.content,
+ status: 'publish',
},
method: 'POST',
} );
diff --git a/phpunit/class-rest-blocks-controller-test.php b/phpunit/class-rest-blocks-controller-test.php
index 7b92f200a2afce..a5c58c6f49805a 100644
--- a/phpunit/class-rest-blocks-controller-test.php
+++ b/phpunit/class-rest-blocks-controller-test.php
@@ -8,7 +8,7 @@
/**
* Tests for WP_REST_Blocks_Controller.
*/
-class REST_Blocks_Controller_Test extends WP_Test_REST_Controller_Testcase {
+class REST_Blocks_Controller_Test extends WP_UnitTestCase {
/**
* Our fake block's post ID.
@@ -55,161 +55,6 @@ public static function wpTearDownAfterClass() {
self::delete_user( self::$user_id );
}
- /**
- * Check that our routes get set up properly.
- */
- public function test_register_routes() {
- $routes = rest_get_server()->get_routes();
-
- $this->assertArrayHasKey( '/wp/v2/blocks', $routes );
- $this->assertCount( 2, $routes['/wp/v2/blocks'] );
- $this->assertArrayHasKey( '/wp/v2/blocks/(?P[\d]+)', $routes );
- $this->assertCount( 3, $routes['/wp/v2/blocks/(?P[\d]+)'] );
- }
-
- /**
- * Check that we can GET a collection of blocks.
- */
- public function test_get_items() {
- wp_set_current_user( self::$user_id );
-
- $request = new WP_REST_Request( 'GET', '/wp/v2/blocks' );
- $response = rest_get_server()->dispatch( $request );
-
- $this->assertEquals( 200, $response->get_status() );
- $this->assertEquals(
- array(
- array(
- 'id' => self::$post_id,
- 'title' => 'My cool block',
- 'content' => 'Hello!
',
- ),
- ),
- $response->get_data()
- );
- }
-
- /**
- * Check that we can GET a single block.
- */
- public function test_get_item() {
- wp_set_current_user( self::$user_id );
-
- $request = new WP_REST_Request( 'GET', '/wp/v2/blocks/' . self::$post_id );
- $response = rest_get_server()->dispatch( $request );
-
- $this->assertEquals( 200, $response->get_status() );
- $this->assertEquals(
- array(
- 'id' => self::$post_id,
- 'title' => 'My cool block',
- 'content' => 'Hello!
',
- ),
- $response->get_data()
- );
- }
-
- /**
- * Check that we can POST to create a new block.
- */
- public function test_create_item() {
- wp_set_current_user( self::$user_id );
-
- $request = new WP_REST_Request( 'POST', '/wp/v2/blocks/' . self::$post_id );
- $request->set_body_params(
- array(
- 'title' => 'New cool block',
- 'content' => 'Wow!
',
- )
- );
-
- $response = rest_get_server()->dispatch( $request );
-
- $this->assertEquals( 200, $response->get_status() );
-
- $data = $response->get_data();
-
- $this->assertArrayHasKey( 'id', $data );
- $this->assertArrayHasKey( 'title', $data );
- $this->assertArrayHasKey( 'content', $data );
-
- $this->assertEquals( self::$post_id, $data['id'] );
- $this->assertEquals( 'New cool block', $data['title'] );
- $this->assertEquals( 'Wow!
', $data['content'] );
- }
-
- /**
- * Check that we can PUT to update a block.
- */
- public function test_update_item() {
- wp_set_current_user( self::$user_id );
-
- $request = new WP_REST_Request( 'PUT', '/wp/v2/blocks/' . self::$post_id );
- $request->set_body_params(
- array(
- 'title' => 'Updated cool block',
- 'content' => 'Nice!
',
- )
- );
-
- $response = rest_get_server()->dispatch( $request );
-
- $this->assertEquals( 200, $response->get_status() );
-
- $data = $response->get_data();
-
- $this->assertArrayHasKey( 'id', $data );
- $this->assertArrayHasKey( 'title', $data );
- $this->assertArrayHasKey( 'content', $data );
-
- $this->assertEquals( self::$post_id, $data['id'] );
- $this->assertEquals( 'Updated cool block', $data['title'] );
- $this->assertEquals( 'Nice!
', $data['content'] );
- }
-
- /**
- * Check that we can DELETE a block.
- */
- public function test_delete_item() {
- wp_set_current_user( self::$user_id );
-
- $request = new WP_REST_Request( 'DELETE', '/wp/v2/blocks/' . self::$post_id );
-
- $response = rest_get_server()->dispatch( $request );
-
- $this->assertEquals( 200, $response->get_status() );
-
- $data = $response->get_data();
-
- $this->assertArrayHasKey( 'deleted', $data );
- $this->assertArrayHasKey( 'previous', $data );
-
- $this->assertTrue( $data['deleted'] );
-
- $this->assertArrayHasKey( 'id', $data['previous'] );
- $this->assertArrayHasKey( 'title', $data['previous'] );
- $this->assertArrayHasKey( 'content', $data['previous'] );
-
- $this->assertEquals( self::$post_id, $data['previous']['id'] );
- $this->assertEquals( 'My cool block', $data['previous']['title'] );
- $this->assertEquals( 'Hello!
', $data['previous']['content'] );
- }
-
- /**
- * Check that we have defined a JSON schema.
- */
- public function test_get_item_schema() {
- $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/blocks' );
- $response = rest_get_server()->dispatch( $request );
- $data = $response->get_data();
- $properties = $data['schema']['properties'];
-
- $this->assertEquals( 3, count( $properties ) );
- $this->assertArrayHasKey( 'id', $properties );
- $this->assertArrayHasKey( 'title', $properties );
- $this->assertArrayHasKey( 'content', $properties );
- }
-
/**
* Test cases for test_capabilities().
*/
@@ -331,11 +176,4 @@ public function test_capabilities( $action, $role, $expected_status ) {
self::delete_user( $user_id );
}
}
-
- public function test_context_param() {
- $this->markTestSkipped( 'Controller doesn\'t implement get_context_param().' );
- }
- public function test_prepare_item() {
- $this->markTestSkipped( 'Controller doesn\'t implement prepare_item().' );
- }
}