Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
76 changes: 54 additions & 22 deletions classes/class-blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,6 @@ public function __construct() {
// https://github.com/nk-crew/lazy-blocks/issues/247 .
add_filter( 'allowed_block_types_all', array( $this, 'allowed_block_types_all' ), 100, 2 );

// Custom post roles.
add_action( 'admin_init', array( $this, 'add_role_caps' ) );

// Additional elements in blocks list table.
add_filter( 'display_post_states', array( $this, 'display_post_states' ), 10, 2 );
add_filter( 'disable_months_dropdown', array( $this, 'disable_months_dropdown' ), 10, 2 );
Expand Down Expand Up @@ -296,32 +293,67 @@ public function allowed_block_types_all( $allowed_block_types, $editor_context )
}

/**
* Add Roles
* Get the Lazy Blocks role capability matrix.
*
* @return array
*/
public function add_role_caps() {
global $wp_roles;

if ( isset( $wp_roles ) ) {
$wp_roles->add_cap( 'administrator', 'edit_lazyblock' );
$wp_roles->add_cap( 'administrator', 'edit_lazyblocks' );
$wp_roles->add_cap( 'administrator', 'edit_other_lazyblocks' );
$wp_roles->add_cap( 'administrator', 'publish_lazyblocks' );
$wp_roles->add_cap( 'administrator', 'read_lazyblock' );
$wp_roles->add_cap( 'administrator', 'read_private_lazyblocks' );
$wp_roles->add_cap( 'administrator', 'delete_lazyblocks' );
$wp_roles->add_cap( 'administrator', 'delete_lazyblock' );
public function get_role_caps_matrix() {
return array(
'administrator' => array(
'edit_lazyblock',
'edit_lazyblocks',
'edit_other_lazyblocks',
'publish_lazyblocks',
'read_lazyblock',
'read_private_lazyblocks',
'delete_lazyblocks',
'delete_lazyblock',
),
'editor' => array(
'read_lazyblock',
'read_private_lazyblocks',
),
'author' => array(
'read_lazyblock',
'read_private_lazyblocks',
),
'contributor' => array(
'read_lazyblock',
'read_private_lazyblocks',
),
);
}

$wp_roles->add_cap( 'editor', 'read_lazyblock' );
$wp_roles->add_cap( 'editor', 'read_private_lazyblocks' );
/**
* Synchronize Lazy Blocks capabilities for built-in roles.
*
* @return void
*/
public function sync_role_caps() {
foreach ( $this->get_role_caps_matrix() as $role_name => $caps ) {
$role = get_role( $role_name );

$wp_roles->add_cap( 'author', 'read_lazyblock' );
$wp_roles->add_cap( 'author', 'read_private_lazyblocks' );
if ( ! $role ) {
continue;
}

$wp_roles->add_cap( 'contributor', 'read_lazyblock' );
$wp_roles->add_cap( 'contributor', 'read_private_lazyblocks' );
foreach ( $caps as $capability ) {
$role->add_cap( $capability );
}
}
}

/**
* Add Roles
*
* @deprecated Use sync_role_caps().
*
* @return void
*/
public function add_role_caps() {
$this->sync_role_caps();
}

/**
* Disable month dropdown.
*
Expand Down
15 changes: 15 additions & 0 deletions classes/class-migration.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ public function init() {
*/
public function get_migrations() {
return array(
array(
'version' => '4.3.0',
'cb' => array( $this, 'v_4_3_0_sync_role_caps' ),
),
array(
'version' => '2.5.0',
'cb' => array( $this, 'v_2_5_0' ),
Expand All @@ -68,6 +72,17 @@ public function get_migrations() {
);
}

/**
* Synchronize Lazy Blocks capabilities during the 4.3.0 upgrade path.
*
* @return void
*/
public function v_4_3_0_sync_role_caps() {
if ( function_exists( 'lazyblocks' ) && lazyblocks()->blocks() ) {
lazyblocks()->blocks()->sync_role_caps();
}
}

/**
* Convert old templates to new one.
*/
Expand Down
4 changes: 4 additions & 0 deletions lazy-blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ public function __construct() {
* Activation Hook
*/
public function activation_hook() {
if ( $this->blocks ) {
$this->blocks->sync_role_caps();
}

LazyBlocks_Dummy::add();
}

Expand Down
209 changes: 209 additions & 0 deletions tests/phpunit/CapabilitySyncTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
<?php
/**
* Tests for Lazy Blocks role capability synchronization lifecycle.
*/
class CapabilitySyncTest extends WP_UnitTestCase {
/**
* Original lzb_db_version option value.
*
* @var string
*/
private $original_db_version = '__missing__';

/**
* Original role capability state before each test.
*
* @var array
*/
private $original_role_caps = array();

/**
* Capability matrix expected for Lazy Blocks roles.
*
* @return array
*/
private function get_role_capability_matrix() {
return array(
'administrator' => array(
'edit_lazyblock',
'edit_lazyblocks',
'edit_other_lazyblocks',
'publish_lazyblocks',
'read_lazyblock',
'read_private_lazyblocks',
'delete_lazyblocks',
'delete_lazyblock',
),
'editor' => array(
'read_lazyblock',
'read_private_lazyblocks',
),
'author' => array(
'read_lazyblock',
'read_private_lazyblocks',
),
'contributor' => array(
'read_lazyblock',
'read_private_lazyblocks',
),
);
}

/**
* Capture the current role capability state so tests can restore it.
*/
private function capture_role_capabilities() {
foreach ( $this->get_role_capability_matrix() as $role_name => $caps ) {
$role = get_role( $role_name );

if ( ! $role ) {
continue;
}

foreach ( $caps as $capability ) {
$this->original_role_caps[ $role_name ][ $capability ] = $role->has_cap( $capability );
}
}
}

/**
* Restore the role capability state captured before each test.
*/
private function restore_role_capabilities() {
foreach ( $this->original_role_caps as $role_name => $caps ) {
$role = get_role( $role_name );

if ( ! $role ) {
continue;
}

foreach ( $caps as $capability => $has_capability ) {
if ( $has_capability ) {
$role->add_cap( $capability );
} else {
$role->remove_cap( $capability );
}
}
}
}

/**
* Remove the Lazy Blocks capabilities from all affected roles.
*/
private function remove_lazyblocks_capabilities() {
foreach ( $this->get_role_capability_matrix() as $role_name => $caps ) {
$role = get_role( $role_name );

if ( ! $role ) {
continue;
}

foreach ( $caps as $capability ) {
$role->remove_cap( $capability );
}
}
}

/**
* Assert the expected Lazy Blocks capability matrix is applied.
*/
private function assert_lazyblocks_capability_matrix_applied() {
foreach ( $this->get_role_capability_matrix() as $role_name => $caps ) {
$role = get_role( $role_name );

$this->assertNotNull( $role, sprintf( 'Role %s should exist.', $role_name ) );

foreach ( $caps as $capability ) {
$this->assertTrue(
$role->has_cap( $capability ),
sprintf( 'Role %s should have capability %s.', $role_name, $capability )
);
}
}
}

/**
* Get a migration instance without registering its constructor hooks.
*
* @return LazyBlocks_Migration
*/
private function create_migration_instance() {
$reflection = new ReflectionClass( 'LazyBlocks_Migration' );

return $reflection->newInstanceWithoutConstructor();
}

/**
* Set up test state.
*/
protected function setUp(): void {
parent::setUp();

$this->original_db_version = get_option( 'lzb_db_version', '__missing__' );
$this->capture_role_capabilities();
}

/**
* Restore test state.
*/
protected function tearDown(): void {
$this->restore_role_capabilities();

if ( '__missing__' === $this->original_db_version ) {
delete_option( 'lzb_db_version' );
} else {
update_option( 'lzb_db_version', $this->original_db_version );
}

parent::tearDown();
}

/**
* Capabilities should not be synchronized via admin_init.
*/
public function test_role_caps_are_not_synced_on_admin_init() {
$this->assertFalse(
has_action( 'admin_init', array( lazyblocks()->blocks(), 'add_role_caps' ) ),
'Role capability sync should not be hooked to admin_init.'
);
}

/**
* Existing installs should receive capabilities through the migration path.
*/
public function test_migration_syncs_role_caps_for_existing_installs() {
$this->remove_lazyblocks_capabilities();
update_option( 'lzb_db_version', '4.2.9' );

$migration = $this->create_migration_instance();
$migration->init();

$this->assert_lazyblocks_capability_matrix_applied();
$this->assertSame( LAZY_BLOCKS_VERSION, get_option( 'lzb_db_version' ) );
}

/**
* Fresh installs should receive capabilities when migrations run for the first time.
*/
public function test_migration_syncs_role_caps_for_fresh_installs() {
$this->remove_lazyblocks_capabilities();
delete_option( 'lzb_db_version' );

$migration = $this->create_migration_instance();
$migration->init();

$this->assert_lazyblocks_capability_matrix_applied();
$this->assertSame( LAZY_BLOCKS_VERSION, get_option( 'lzb_db_version' ) );
}

/**
* Fresh installs should receive capabilities during plugin activation.
*/
public function test_activation_hook_syncs_role_caps_for_fresh_installs() {
$this->remove_lazyblocks_capabilities();

lazyblocks()->activation_hook();

$this->assert_lazyblocks_capability_matrix_applied();
}
}
Loading