diff --git a/core/modules/block_content/block_content.permissions.yml b/core/modules/block_content/block_content.permissions.yml
new file mode 100644
index 0000000000..18bcddd199
--- /dev/null
+++ b/core/modules/block_content/block_content.permissions.yml
@@ -0,0 +1,2 @@
+permission_callbacks:
+ - \Drupal\block_content\BlockContentPermissions::blockTypePermissions
diff --git a/core/modules/block_content/src/BlockContentAccessControlHandler.php b/core/modules/block_content/src/BlockContentAccessControlHandler.php
index 0c84190997..e19ceaefee 100644
--- a/core/modules/block_content/src/BlockContentAccessControlHandler.php
+++ b/core/modules/block_content/src/BlockContentAccessControlHandler.php
@@ -54,13 +54,22 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
+ // Allow view and update access to user with the 'edit any (type) block content'
+ // permission or the 'administer blocks' permission.
+ $edit_any_permission = 'edit any ' . $entity->bundle() . ' block content';
if ($operation === 'view') {
$access = AccessResult::allowedIf($entity->isPublished())
- ->orIf(AccessResult::allowedIfHasPermission($account, 'administer blocks'));
+ ->orIf(AccessResult::allowedIfHasPermission($account, 'administer blocks'))
+ ->orIf(AccessResult::allowedIfHasPermission($account, $edit_any_permission));
+ }
+ elseif ($operation === 'update') {
+ $access = AccessResult::allowedIfHasPermission($account, 'administer blocks')
+ ->orIf(AccessResult::allowedIfHasPermission($account, $edit_any_permission));
}
else {
$access = parent::checkAccess($entity, $operation, $account);
}
+
// Add the entity as a cacheable dependency because access will at least be
// determined by whether the block is reusable.
$access->addCacheableDependency($entity);
diff --git a/core/modules/block_content/src/BlockContentPermissions.php b/core/modules/block_content/src/BlockContentPermissions.php
new file mode 100644
index 0000000000..ee63dda0d7
--- /dev/null
+++ b/core/modules/block_content/src/BlockContentPermissions.php
@@ -0,0 +1,50 @@
+buildPermission($type);
+ }
+
+ return $perms;
+ }
+
+ /**
+ * Return all the permissions available for a custom block type.
+ *
+ * @param \Drupal\block_content\Entity\BlockContentType $type
+ * The block type.
+ *
+ * @return array
+ * Permissions available for the given block type.
+ */
+ protected function buildPermission(BlockContentType $type) {
+ $type_id = $type->id();
+ $type_params = ['%type_name' => $type->label()];
+ return [
+ "edit any $type_id block content" => [
+ 'title' => $this->t('%type_name: Edit any block content', $type_params),
+ ],
+ ];
+ }
+
+}
diff --git a/core/modules/block_content/tests/src/Kernel/BlockContentAccessHandlerTest.php b/core/modules/block_content/tests/src/Kernel/BlockContentAccessHandlerTest.php
index 0f7e66c5c6..788d1d4c42 100644
--- a/core/modules/block_content/tests/src/Kernel/BlockContentAccessHandlerTest.php
+++ b/core/modules/block_content/tests/src/Kernel/BlockContentAccessHandlerTest.php
@@ -184,6 +184,22 @@ public function providerTestAccess() {
NULL,
'allowed',
],
+ 'view:unpublished:reusable:per-block-editor:basic' => [
+ 'view',
+ FALSE,
+ TRUE,
+ ['edit any basic block content'],
+ NULL,
+ 'neutral',
+ ],
+ 'view:unpublished:reusable:per-block-editor:square' => [
+ 'view',
+ FALSE,
+ TRUE,
+ ['edit any square block content'],
+ NULL,
+ 'allowed',
+ ],
'view:published:reusable:admin' => [
'view',
TRUE,
@@ -192,6 +208,22 @@ public function providerTestAccess() {
NULL,
'allowed',
],
+ 'view:published:reusable:per-block-editor:basic' => [
+ 'view',
+ TRUE,
+ TRUE,
+ ['edit any basic block content'],
+ NULL,
+ 'allowed',
+ ],
+ 'view:published:reusable:per-block-editor:square' => [
+ 'view',
+ TRUE,
+ TRUE,
+ ['edit any square block content'],
+ NULL,
+ 'allowed',
+ ],
'view:published:non_reusable' => [
'view',
TRUE,
@@ -291,8 +323,62 @@ public function providerTestAccess() {
'forbidden',
'forbidden',
],
+ $operation . ':unpublished:reusable:per-block-editor:basic' => [
+ $operation,
+ FALSE,
+ TRUE,
+ ['edit any basic block content'],
+ NULL,
+ 'neutral',
+ ],
+ $operation . ':published:reusable:per-block-editor:basic' => [
+ $operation,
+ TRUE,
+ TRUE,
+ ['edit any basic block content'],
+ NULL,
+ 'neutral',
+ ],
];
}
+
+ $cases += [
+ 'update:unpublished:reusable:per-block-editor:square' => [
+ 'update',
+ FALSE,
+ TRUE,
+ ['edit any square block content'],
+ NULL,
+ 'allowed',
+ ],
+ 'update:published:reusable:per-block-editor:square' => [
+ 'update',
+ TRUE,
+ TRUE,
+ ['edit any square block content'],
+ NULL,
+ 'allowed',
+ ],
+ ];
+
+ $cases += [
+ 'delete:unpublished:reusable:per-block-editor:square' => [
+ 'delete',
+ FALSE,
+ TRUE,
+ ['edit any square block content'],
+ NULL,
+ 'neutral',
+ ],
+ 'delete:published:reusable:per-block-editor:square' => [
+ 'delete',
+ TRUE,
+ TRUE,
+ ['edit any square block content'],
+ NULL,
+ 'neutral',
+ ],
+ ];
return $cases;
}
diff --git a/core/modules/block_content/tests/src/Kernel/BlockContentPermissionsTest.php b/core/modules/block_content/tests/src/Kernel/BlockContentPermissionsTest.php
new file mode 100644
index 0000000000..eba2cf4d1c
--- /dev/null
+++ b/core/modules/block_content/tests/src/Kernel/BlockContentPermissionsTest.php
@@ -0,0 +1,86 @@
+installSchema('system', ['sequences']);
+ $this->installEntitySchema('user');
+ $this->installEntitySchema('block_content');
+
+ $this->permissionHandler = $this->container->get('user.permissions');
+ }
+
+ /**
+ * @covers ::blockTypePermissions
+ */
+ public function testDynamicPermissions() {
+ $permissions = $this->permissionHandler->getPermissions();
+ $this->assertArrayNotHasKey('edit any basic block content', $permissions, 'The per-block-type permission does not exist.');
+ $this->assertArrayNotHasKey('edit any square block content', $permissions, 'The per-block-type permission does not exist.');
+
+ // Create a basic block content type.
+ BlockContentType::create([
+ 'id' => 'basic',
+ 'label' => 'A basic block type',
+ 'description' => 'Provides a basic block type',
+ ])->save();
+
+ // Create a square block content type.
+ BlockContentType::create([
+ 'id' => 'square',
+ 'label' => 'A square block type',
+ 'description' => 'Provides a block type that is square',
+ ])->save();
+
+ $permissions = $this->permissionHandler->getPermissions();
+
+ // Assert the basic permission has been created.
+ $this->assertArrayHasKey('edit any basic block content', $permissions, 'The per-block-type permission exists.');
+ $this->assertEquals(
+ 'A basic block type: Edit any block content',
+ $permissions['edit any basic block content']['title']->render()
+ );
+
+ // Assert the square permission has been created.
+ $this->assertArrayHasKey('edit any square block content', $permissions, 'The per-block-type permission exists.');
+ $this->assertEquals(
+ 'A square block type: Edit any block content',
+ $permissions['edit any square block content']['title']->render()
+ );
+ }
+
+}