Verified Commit 97e5542c authored by Alex Pott's avatar Alex Pott
Browse files

Issue #2809291 by wengerk, aleevas, acbramley, jenlampton, douggreen,...

Issue #2809291 by wengerk, aleevas, acbramley, jenlampton, douggreen, larowlan, mikemiles86, luismagr, raman.b, paulocs, guptahemant, ridhimaabrol24, leslieg, benjifisher, ifrik, catch, yoroy, tim.plunkett, angelamnr, Berdir, xjm, alexpott: Add "edit block $type" permissions
parent 80759ce8
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
permission_callbacks:
  - \Drupal\block_content\BlockContentPermissions::blockTypePermissions
+10 −1
Original line number Diff line number Diff line
@@ -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);
+46 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\block_content;

use Drupal\block_content\Entity\BlockContentType;
use Drupal\Core\Entity\BundlePermissionHandlerTrait;
use Drupal\Core\StringTranslation\StringTranslationTrait;

/**
 * Provide dynamic permissions for blocks of different types.
 */
class BlockContentPermissions {

  use StringTranslationTrait;
  use BundlePermissionHandlerTrait;

  /**
   * Build permissions for each block type.
   *
   * @return array
   *   The block type permissions.
   */
  public function blockTypePermissions() {
    return $this->generatePermissions(BlockContentType::loadMultiple(), [$this, 'buildPermissions']);
  }

  /**
   * 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 buildPermissions(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),
      ],
    ];
  }

}
+95 −1
Original line number Diff line number Diff line
@@ -61,7 +61,15 @@ protected function setUp(): void {
    $this->installEntitySchema('user');
    $this->installEntitySchema('block_content');

    // Create a block content type.
    // Create a basic block content type.
    $block_content_type = BlockContentType::create([
      'id' => 'basic',
      'label' => 'A basic block type',
      'description' => "Provides a block type that is basic.",
    ]);
    $block_content_type->save();

    // Create a square block content type.
    $block_content_type = BlockContentType::create([
      'id' => 'square',
      'label' => 'A square block type',
@@ -184,6 +192,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 +216,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 +331,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;
  }

+86 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\Tests\block_content\Kernel;

use Drupal\KernelTests\KernelTestBase;
use Drupal\block_content\Entity\BlockContentType;

/**
 * Tests the permissions of content blocks.
 *
 * @coversDefaultClass \Drupal\block_content\BlockContentPermissions
 *
 * @group block_content
 */
class BlockContentPermissionsTest extends KernelTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'block',
    'block_content',
    'block_content_test',
    'system',
    'user',
  ];

  /**
   * The permission handler.
   *
   * @var \Drupal\user\PermissionHandlerInterface
   */
  protected $permissionHandler;

  /**
   * {@inheritdoc}
   */
  public function setUp(): void {
    parent::setUp();
    $this->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(
      '<em class="placeholder">A basic block type</em>: 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(
      '<em class="placeholder">A square block type</em>: Edit any block content',
      $permissions['edit any square block content']['title']->render()
    );
  }

}
Loading