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

Blocks: add block registration to package #17167

Merged
merged 13 commits into from
Sep 25, 2020
150 changes: 149 additions & 1 deletion packages/blocks/src/class-blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,159 @@

namespace Automattic\Jetpack;

use Jetpack_Gutenberg;

/**
* Register and manage blocks within a plugin. Used to manage block registration, enqueues, and more.
*
* @since 9.0.0
*/
class Blocks {
/**
* Wrapper function to safely register a Gutenberg block type
*
* @see register_block_type
*
* @since 9.0.0
*
* @param string $slug Slug of the block.
* @param array $args {
* Arguments that are passed into register_block_type.
* See register_block_type for full list of arguments.
* Can also include 2 extra arguments not currently supported by register_block_type.
*
* @type array $version_requirements Array containing required Gutenberg version and, if known, the WordPress version that was released with this minimum version.
jeherve marked this conversation as resolved.
Show resolved Hide resolved
* @type bool $plan_check Should we check for a specific plan before registering the block.
*
jeherve marked this conversation as resolved.
Show resolved Hide resolved
* @return WP_Block_Type|false The registered block type on success, or false on failure.
*/
public static function jetpack_register_block( $slug, $args = array() ) {
if ( 0 !== strpos( $slug, 'jetpack/' ) && ! strpos( $slug, '/' ) ) {
_doing_it_wrong( 'jetpack_register_block', 'Prefix the block with jetpack/ ', 'Jetpack 9.0.0' );
$slug = 'jetpack/' . $slug;
}

if (
isset( $args['version_requirements'] )
&& ! self::is_gutenberg_version_available( $args['version_requirements'], $slug )
) {
return false;
}

// Checking whether block is registered to ensure it isn't registered twice.
if ( self::is_registered( $slug ) ) {
return false;
}

$feature_name = self::remove_extension_prefix( $slug );

// This is only useful in Jetpack.
if ( class_exists( Jetpack_Gutenberg::class ) ) {
// If the block is dynamic, and a Jetpack block, wrap the render_callback to check availability.
if ( ! empty( $args['plan_check'] ) ) {
if ( isset( $args['render_callback'] ) ) {
$args['render_callback'] = Jetpack_Gutenberg::get_render_callback_with_availability_check( $feature_name, $args['render_callback'] );
}
$method_name = 'set_availability_for_plan';
} else {
$method_name = 'set_extension_available';
}

add_action(
'jetpack_register_gutenberg_extensions',
function() use ( $feature_name, $method_name ) {
call_user_func( array( 'Jetpack_Gutenberg', $method_name ), $feature_name );
}
);
}
return register_block_type( $slug, $args );
}

/**
* Check if an extension/block is already registered
*
* @since 9.0.0
*
* @param string $slug Name of extension/block to check.
*
* @return bool
*/
public static function is_registered( $slug ) {
return \WP_Block_Type_Registry::get_instance()->is_registered( $slug );
}

/**
* Remove the 'jetpack/' or jetpack-' prefix from an extension name
*
* @since 9.0.0
*
* @param string $extension_name The extension name.
*
* @return string The unprefixed extension name.
*/
public static function remove_extension_prefix( $extension_name ) {
if ( 0 === strpos( $extension_name, 'jetpack/' ) || 0 === strpos( $extension_name, 'jetpack-' ) ) {
return substr( $extension_name, strlen( 'jetpack/' ) );
}
return $extension_name;
}

/**
* Check to see if a minimum version of Gutenberg is available. Because a Gutenberg version is not available in
* php if the Gutenberg plugin is not installed, if we know which minimum WP release has the required version we can
* optionally fall back to that.
*
* @since 9.0.0
*
* @param array $version_requirements An array containing the required Gutenberg version and, if known, the WordPress version that was released with this minimum version.
jeherve marked this conversation as resolved.
Show resolved Hide resolved
* @param string $slug The slug of the block or plugin that has the Gutenberg version requirement.
*
* @return boolean True if the version of Gutenberg required by the block or plugin is available.
*/
public static function is_gutenberg_version_available( $version_requirements, $slug ) {
global $wp_version;

// Bail if we don't at least have the Gutenberg version requirement, the WP version is optional.
if ( empty( $version_requirements['gutenberg'] ) ) {
return false;
}

// If running a local dev build of Gutenberg plugin GUTENBERG_DEVELOPMENT_MODE is set so assume correct version.
if ( defined( 'GUTENBERG_DEVELOPMENT_MODE' ) && GUTENBERG_DEVELOPMENT_MODE ) {
return true;
}

$version_available = false;

// If running a production build of the Gutenberg plugin then GUTENBERG_VERSION is set, otherwise if WP version
// with required version of Gutenberg is known check that.
if ( defined( 'GUTENBERG_VERSION' ) ) {
$version_available = version_compare( GUTENBERG_VERSION, $version_requirements['gutenberg'], '>=' );
} elseif ( ! empty( $version_requirements['wp'] ) ) {
$version_available = version_compare( $wp_version, $version_requirements['wp'], '>=' );
}

if (
! $version_available
&& class_exists( Jetpack_Gutenberg::class ) // This is only useful in Jetpack.
) {
Jetpack_Gutenberg::set_extension_unavailable(
$slug,
'incorrect_gutenberg_version',
array(
'required_feature' => $slug,
'required_version' => $version_requirements,
'current_version' => array(
'wp' => $wp_version,
'gutenberg' => defined( 'GUTENBERG_VERSION' ) ? GUTENBERG_VERSION : null,
),
)
);
}

return $version_available;
}

/**
* Get CSS classes for a block.
*
Expand Down Expand Up @@ -62,6 +209,8 @@ public static function classes( $slug, $attr, $extra = array() ) {
/**
* Does the page return AMP content.
*
* @since 9.0.0
*
* @return bool $is_amp_request Are we on an AMP view.
*/
public static function is_amp_request() {
Expand All @@ -71,4 +220,3 @@ public static function is_amp_request() {
return apply_filters( 'jetpack_is_amp_request', $is_amp_request );
}
}

130 changes: 130 additions & 0 deletions packages/blocks/tests/php/test-blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,33 @@
* Class Test_Blocks
*/
class Test_Blocks extends TestCase {
/**
* Test block name.
*
* @var string
*/
public $block_name = 'jetpack/apple';

/**
* Setup runs before each test.
*/
public function setUp() {
parent::setUp();

// Register a test block.
Blocks::jetpack_register_block( $this->block_name );
}

/**
* Teardown runs after each test.
*/
public function tearDown() {
parent::tearDown();

// Unregister the test Jetpack block we may have created for our tests.
unregister_block_type( $this->block_name );
}

/**
* Test the different inputs and matching output for Classes.
*
Expand Down Expand Up @@ -84,4 +111,107 @@ public function test_is_amp_request() {
public function test_is_not_amp_request() {
$this->assertFalse( Blocks::is_amp_request() );
}

/**
* Test WordPress and Gutenberg version requirements.
*
* @covers Automattic\Jetpack\Blocks::is_gutenberg_version_available
*/
public function test_returns_false_if_core_wp_version_less_than_minimum() {
$version_gated = Blocks::is_gutenberg_version_available(
array(
'wp' => '999999',
'gutenberg' => '999999',
),
'gated_block'
);
$this->assertFalse( false, $version_gated );
}

/**
* Test WordPress and Gutenberg version requirements.
*
* @covers Automattic\Jetpack\Blocks::is_gutenberg_version_available
*/
public function test_returns_true_if_core_wp_version_greater_or_equal_to_minimum() {
$version_gated = Blocks::is_gutenberg_version_available(
array(
'wp' => '1',
'gutenberg' => '999999',
),
'ungated_block'
);
$this->assertTrue( true, $version_gated );
}

/**
* Testing removing the Jetpack prefix from a block slug.
*
* @covers Automattic\Jetpack\Blocks::remove_extension_prefix
*
* @dataProvider get_extension_name_provider
*
* @param string $extension_slug Block / Extension name.
* @param string $expected_short_slug Extension name without Jetpack prefix.
*/
public function test_remove_extension_prefix( $extension_slug, $expected_short_slug ) {
$short_slug = Blocks::remove_extension_prefix( $extension_slug );

$this->assertEquals( $expected_short_slug, $short_slug );
}

/**
* Get different possible block names.
*
* Data provider for test_remove_extension_prefix.
*/
public function get_extension_name_provider() {
return array(
'not_jetpack' => array(
'woocommerce/product-best-sellers',
'woocommerce/product-best-sellers',
),
'jetpack_dash' => array(
'jetpack/shortlinks',
'shortlinks',
),
'jetpack_hyphen' => array(
'jetpack-shortlinks',
'shortlinks',
),
);
}

/**
* Test to ensure that an extension is returned as registered.
*
* @covers Automattic\Jetpack\Blocks::is_registered
*/
public function test_is_extension_registered() {
// Test for the block that is registered for all tests here.
$this->assertTrue( Blocks::is_registered( $this->block_name ) );
// Test for a non-existing block.
$this->assertFalse( Blocks::is_registered( 'foo/bar' ) );
}

/**
* Ensure blocks cannot be registered twice.
*
* @covers Automattic\Jetpack\Blocks::jetpack_register_block
*/
public function test_jetpack_register_block_twice() {
$result = Blocks::jetpack_register_block( $this->block_name );
$this->assertFalse( $result );
}

/**
* Test to ensure blocks without a Jetpack prefix are registered, but with a jetpack prefix.
*
* @expectedIncorrectUsage Automattic\Jetpack\Blocks::jetpack_register_block
* @covers Automattic\Jetpack\Blocks::jetpack_register_block
*/
public function test_jetpack_register_block_without_jetpack() {
$result = Blocks::jetpack_register_block( 'doing-it-wrong' );
$this->assertEquals( 'jetpack/doing-it-wrong', $result->name );
}
}