Skip to content

Commit

Permalink
Merge pull request #2529 from WordPress/update/server-schemas
Browse files Browse the repository at this point in the history
Block API: Support and bootstrap server-registered block attribute schemas
  • Loading branch information
aduth authored Sep 1, 2017
2 parents 28adfcd + 15ac75c commit 7a05235
Show file tree
Hide file tree
Showing 14 changed files with 215 additions and 49 deletions.
38 changes: 38 additions & 0 deletions bin/get-server-block-attributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env php
<?php
/**
* Generates server-registered block attributes, writing to standard output.
*
* @package gutenberg-build
*/

$attributes = array();

/**
* Register a block type. Substitute for core API in lieu of loading full
* WordPress context.
*
* @param string $name Block type name including namespace.
* @param array $args {
* Optional. Array of block type arguments. Any arguments may be defined, however the
* ones described below are supported by default. Default empty array.
*
* @type callable $render_callback Callback used to render blocks of this block type.
* @type array $attributes Block attributes mapping, property name to schema.
* }
*/
function register_block_type( $name, $args = array() ) {
if ( ! isset( $args['attributes'] ) ) {
return;
}

global $attributes;
$attributes[ $name ] = $args['attributes'];
}

// Register server-side code for individual blocks.
foreach ( glob( dirname( dirname( __FILE__ ) ) . '/blocks/library/*/index.php' ) as $block_logic ) {
require_once $block_logic;
}

echo json_encode( $attributes, JSON_PRETTY_PRINT );
11 changes: 8 additions & 3 deletions blocks/api/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/**
* External dependencies
*/
import { isFunction, some } from 'lodash';
import { get, isFunction, some } from 'lodash';

/**
* WordPress dependencies
Expand Down Expand Up @@ -92,8 +92,13 @@ export function registerBlockType( name, settings ) {
);
return;
}
const block = Object.assign( { name }, settings );
blocks[ name ] = block;

const block = blocks[ name ] = {
name,
attributes: get( window._wpBlocksAttributes, name ),
...settings,
};

return block;
}

Expand Down
18 changes: 18 additions & 0 deletions blocks/api/test/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe( 'blocks', () => {
} );
setUnknownTypeHandlerName( undefined );
setDefaultBlockName( undefined );
window._wpBlocksAttributes = {};
console.error = error;
} );

Expand Down Expand Up @@ -109,6 +110,23 @@ describe( 'blocks', () => {
expect( block ).toBeUndefined();
} );

it( 'should default to browser-initialized global attributes', () => {
const attributes = { ok: { type: 'boolean' } };
window._wpBlocksAttributes = {
'core/test-block-with-attributes': attributes,
};

const blockType = { settingName: 'settingValue', save: noop, category: 'common' };
registerBlockType( 'core/test-block-with-attributes', blockType );
expect( getBlockType( 'core/test-block-with-attributes' ) ).toEqual( {
name: 'core/test-block-with-attributes',
settingName: 'settingValue',
save: noop,
category: 'common',
attributes,
} );
} );

it( 'should store a copy of block type', () => {
const blockType = { settingName: 'settingValue', save: noop, category: 'common' };
registerBlockType( 'core/test-block-with-settings', blockType );
Expand Down
22 changes: 0 additions & 22 deletions blocks/library/latest-posts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,28 +36,6 @@ registerBlockType( 'core/latest-posts', {

keywords: [ __( 'recent posts' ) ],

attributes: {
postsToShow: {
type: 'number',
default: 5,
},
displayPostDate: {
type: 'boolean',
default: false,
},
layout: {
type: 'string',
default: 'list',
},
columns: {
type: 'number',
default: 3,
},
align: {
type: 'string',
},
},

getEditWrapperProps( attributes ) {
const { align } = attributes;
if ( 'left' === align || 'right' === align || 'wide' === align || 'full' === align ) {
Expand Down
46 changes: 25 additions & 21 deletions blocks/library/latest-posts/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,8 @@
* @return string Returns the post content with latest posts added.
*/
function gutenberg_render_block_core_latest_posts( $attributes ) {
$posts_to_show = 5;

if ( array_key_exists( 'postsToShow', $attributes ) ) {

// Basic attribute validation.
if (
is_numeric( $attributes['postsToShow'] ) &&
$attributes['postsToShow'] > 0 &&
$attributes['postsToShow'] < 100
) {
$posts_to_show = intval( $attributes['postsToShow'] );
}
}

$align = 'center';
if ( isset( $attributes['align'] ) && in_array( $attributes['align'], array( 'left', 'right', 'wide', 'full' ), true ) ) {
$align = $attributes['align'];
}

$recent_posts = wp_get_recent_posts( array(
'numberposts' => $posts_to_show,
'numberposts' => $attributes['postsToShow'],
'post_status' => 'publish',
) );

Expand Down Expand Up @@ -63,7 +44,7 @@ function gutenberg_render_block_core_latest_posts( $attributes ) {
$list_items_markup .= "</li>\n";
}

$class = "wp-block-latest-posts align{$align}";
$class = "wp-block-latest-posts align{$attributes['align']}";
if ( isset( $attributes['layout'] ) && 'grid' === $attributes['layout'] ) {
$class .= ' is-grid';
}
Expand All @@ -82,5 +63,28 @@ function gutenberg_render_block_core_latest_posts( $attributes ) {
}

register_block_type( 'core/latest-posts', array(
'attributes' => array(
'postsToShow' => array(
'type' => 'number',
'default' => 5,
),
'displayPostDate' => array(
'type' => 'boolean',
'default' => false,
),
'layout' => array(
'type' => 'string',
'default' => 'list',
),
'columns' => array(
'type' => 'number',
'default' => 3,
),
'align' => array(
'type' => 'string',
'default' => 'center',
),
),

'render_callback' => 'gutenberg_render_block_core_latest_posts',
) );
3 changes: 2 additions & 1 deletion blocks/test/fixtures/core__latest-posts.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"postsToShow": 5,
"displayPostDate": false,
"layout": "list",
"columns": 3
"columns": 3,
"align": "center"
}
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"postsToShow": 5,
"displayPostDate": true,
"layout": "list",
"columns": 3
"columns": 3,
"align": "center"
}
}
]
2 changes: 2 additions & 0 deletions blocks/test/full-content.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ function normalizeParsedBlocks( blocks ) {

describe( 'full post content fixture', () => {
beforeAll( () => {
window._wpBlocksAttributes = require( './server-attributes.json' );

// Register all blocks.
require( 'blocks' );
} );
Expand Down
24 changes: 24 additions & 0 deletions blocks/test/server-attributes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"core\/latest-posts": {
"postsToShow": {
"type": "number",
"default": 5
},
"displayPostDate": {
"type": "boolean",
"default": false
},
"layout": {
"type": "string",
"default": "list"
},
"columns": {
"type": "number",
"default": 3
},
"align": {
"type": "string",
"default": "center"
}
}
}
1 change: 1 addition & 0 deletions lib/class-wp-block-type-registry.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ final class WP_Block_Type_Registry {
* ones described below are supported by default. Default empty array.
*
* @type callable $render_callback Callback used to render blocks of this block type.
* @type array $attributes Block attributes mapping, property name to schema.
* }
* @return WP_Block_Type|false The registered block type on success, or false on failure.
*/
Expand Down
45 changes: 45 additions & 0 deletions lib/class-wp-block-type.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ class WP_Block_Type {
*/
public $render_callback;

/**
* Block type attributes property schemas.
*
* @since 0.10.0
* @access public
* @var array
*/
public $attributes;

/**
* Constructor.
*
Expand Down Expand Up @@ -71,9 +80,45 @@ public function render( $attributes = array(), $content = null ) {
return $content;
}

$attributes = $this->prepare_attributes_for_render( $attributes );

return call_user_func( $this->render_callback, $attributes, $content );
}

/**
* Validates attributes against the current block schema, populating
* defaulted and missing values, and omitting unknown attributes.
*
* @param array $attributes Original block attributes.
* @return array Prepared block attributes.
*/
public function prepare_attributes_for_render( $attributes ) {
if ( ! isset( $this->attributes ) ) {
return $attributes;
}

$prepared_attributes = array();

foreach ( $this->attributes as $attribute_name => $schema ) {
$value = null;

if ( isset( $attributes[ $attribute_name ] ) ) {
$is_valid = rest_validate_value_from_schema( $attributes[ $attribute_name ], $schema );
if ( ! is_wp_error( $is_valid ) ) {
$value = $attributes[ $attribute_name ];
}
}

if ( is_null( $value ) && isset( $schema['default'] ) ) {
$value = $schema['default'];
}

$prepared_attributes[ $attribute_name ] = $value;
}

return $prepared_attributes;
}

/**
* Sets block type properties.
*
Expand Down
10 changes: 10 additions & 0 deletions lib/client-assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,16 @@ function gutenberg_editor_scripts_and_styles( $hook ) {
'before'
);

// Preload server-registered block schemas.
$block_registry = WP_Block_Type_Registry::get_instance();
$schemas = array();
foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) {
if ( isset( $block_type->attributes ) ) {
$schemas[ $block_name ] = $block_type->attributes;
}
}
wp_localize_script( 'wp-blocks', '_wpBlocksAttributes', $schemas );

// Initialize the editor.
$gutenberg_theme_support = get_theme_support( 'gutenberg' );
$color_palette = gutenberg_color_palette();
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@
"test": "npm run lint && npm run test-unit",
"ci": "concurrently \"npm run lint && npm run build\" \"npm run test-unit:coverage-ci\"",
"fixtures:clean": "rimraf \"blocks/test/fixtures/*.+(json|serialized.html)\"",
"fixtures:generate": "cross-env GENERATE_MISSING_FIXTURES=y npm run test-unit",
"fixtures:server-attributes": "./bin/get-server-block-attributes.php > blocks/test/server-attributes.json",
"fixtures:generate": "npm run fixtures:server-attributes && cross-env GENERATE_MISSING_FIXTURES=y npm run test-unit",
"fixtures:regenerate": "npm run fixtures:clean && npm run fixtures:generate",
"package-plugin": "./bin/build-plugin-zip.sh",
"docs-start": "./docutron/bin/cli.js start",
Expand Down
38 changes: 38 additions & 0 deletions phpunit/class-block-type-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,44 @@ function test_render_without_callback() {
$this->assertSame( $content, $output );
}

function test_prepare_attributes() {
$attributes = array(
'correct' => 'include',
'wrongType' => 5,
'wrongTypeDefaulted' => 5,
/* missingDefaulted */
'undefined' => 'omit',
);

$block_type = new WP_Block_Type( 'core/dummy', array(
'attributes' => array(
'correct' => array(
'type' => 'string',
),
'wrongType' => array(
'type' => 'string',
),
'wrongTypeDefaulted' => array(
'type' => 'string',
'default' => 'defaulted',
),
'missingDefaulted' => array(
'type' => 'string',
'default' => 'define',
),
),
) );

$prepared_attributes = $block_type->prepare_attributes_for_render( $attributes );

$this->assertEquals( array(
'correct' => 'include',
'wrongType' => null,
'wrongTypeDefaulted' => 'defaulted',
'missingDefaulted' => 'define',
), $prepared_attributes );
}

function render_dummy_block( $attributes ) {
return json_encode( $attributes );
}
Expand Down

0 comments on commit 7a05235

Please sign in to comment.