Loading core/core.link_relation_types.yml +6 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,12 @@ delete-multiple-form: revision: uri: https://drupal.org/link-relations/revision description: A particular version of this resource. revision-revert-form: uri: https://drupal.org/link-relations/revision-revert-form description: A form where a particular version of this resource can be reverted. revision-delete-form: uri: https://drupal.org/link-relations/revision-delete-form description: A form where a particular version of this resource can be deleted. create: uri: https://drupal.org/link-relations/create description: A REST resource URL where a resource of this type can be created. Loading core/lib/Drupal/Core/Entity/Controller/VersionHistoryController.php +5 −4 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ use Drupal\Core\Entity\RevisionableStorageInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Link; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Entity\RevisionLogInterface; Loading Loading @@ -155,10 +156,10 @@ protected function getRevisionDescription(RevisionableInterface $revision): arra $linkText = $revision->access('view label') ? $revision->label() : $this->t('- Restricted access -'); } $revisionViewLink = $revision->toLink($linkText, 'revision'); $context['revision'] = $revisionViewLink->getUrl()->access() ? $revisionViewLink->toString() : (string) $revisionViewLink->getText(); $url = $revision->hasLinkTemplate('revision') ? $revision->toUrl('revision') : NULL; $context['revision'] = $url && $url->access() ? Link::fromTextAndUrl($linkText, $url)->toString() : (string) $linkText; $context['message'] = $revision instanceof RevisionLogInterface ? [ '#markup' => $revision->getRevisionLogMessage(), '#allowed_tags' => Xss::getHtmlTagList(), Loading core/modules/block_content/block_content.install +24 −0 Original line number Diff line number Diff line Loading @@ -5,9 +5,33 @@ * Install, update and uninstall functions for the block_content module. */ use Drupal\Core\Entity\Form\RevisionDeleteForm; use Drupal\Core\Entity\Form\RevisionRevertForm; use Drupal\Core\Entity\Routing\RevisionHtmlRouteProvider; use Drupal\Core\StringTranslation\TranslatableMarkup; /** * Implements hook_update_last_removed(). */ function block_content_update_last_removed() { return 8600; } /** * Update entity definition to handle revision routes. */ function block_content_update_10100(&$sandbox = NULL): TranslatableMarkup { $entityDefinitionUpdateManager = \Drupal::entityDefinitionUpdateManager(); $definition = $entityDefinitionUpdateManager->getEntityType('block_content'); $routeProviders = $definition->get('route_provider'); $routeProviders['revision'] = RevisionHtmlRouteProvider::class; $definition ->setFormClass('revision-delete', RevisionDeleteForm::class) ->setFormClass('revision-revert', RevisionRevertForm::class) ->set('route_provider', $routeProviders) ->setLinkTemplate('revision-delete-form', '/block/{block_content}/revision/{block_content_revision}/delete') ->setLinkTemplate('revision-revert-form', '/block/{block_content}/revision/{block_content_revision}/revert') ->setLinkTemplate('version-history', '/block/{block_content}/revisions'); $entityDefinitionUpdateManager->updateEntityType($definition); return \t('Added revision routes to Custom block entity type.'); } core/modules/block_content/src/BlockContentAccessControlHandler.php +27 −14 Original line number Diff line number Diff line Loading @@ -54,21 +54,34 @@ public static function createInstance(ContainerInterface $container, EntityTypeI * {@inheritdoc} */ protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) { assert($entity instanceof BlockContentInterface); $bundle = $entity->bundle(); $forbidIfNotDefaultAndLatest = fn () => AccessResult::forbiddenIf($entity->isDefaultRevision() && $entity->isLatestRevision()); $access = match ($operation) { // 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()) 'view' => AccessResult::allowedIf($entity->isPublished()) ->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); } ->orIf(AccessResult::allowedIfHasPermission($account, 'edit any ' . $bundle . ' block content')), 'update' => AccessResult::allowedIfHasPermission($account, 'administer blocks') ->orIf(AccessResult::allowedIfHasPermission($account, 'edit any ' . $bundle . ' block content')), // Revisions. 'view all revisions' => AccessResult::allowedIfHasPermissions($account, [ 'administer blocks', 'view any ' . $bundle . ' block content history', ], 'OR'), 'revert' => AccessResult::allowedIfHasPermissions($account, [ 'administer blocks', 'revert any ' . $bundle . ' block content revisions', ], 'OR')->orIf($forbidIfNotDefaultAndLatest()), 'delete revision' => AccessResult::allowedIfHasPermissions($account, [ 'administer blocks', 'delete any ' . $bundle . ' block content revisions', ], 'OR')->orIf($forbidIfNotDefaultAndLatest()), default => 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. Loading core/modules/block_content/src/BlockContentPermissions.php +34 −2 Original line number Diff line number Diff line Loading @@ -3,17 +3,40 @@ namespace Drupal\block_content; use Drupal\block_content\Entity\BlockContentType; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\BundlePermissionHandlerTrait; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provide dynamic permissions for blocks of different types. */ class BlockContentPermissions { class BlockContentPermissions implements ContainerInjectionInterface { use StringTranslationTrait; use BundlePermissionHandlerTrait; /** * Constructs a BlockContentPermissions instance. * * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager * Entity type manager. */ public function __construct( protected EntityTypeManagerInterface $entityTypeManager, ) { } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('entity_type.manager'), ); } /** * Build permissions for each block type. * Loading @@ -21,7 +44,7 @@ class BlockContentPermissions { * The block type permissions. */ public function blockTypePermissions() { return $this->generatePermissions(BlockContentType::loadMultiple(), [$this, 'buildPermissions']); return $this->generatePermissions($this->entityTypeManager->getStorage('block_content_type')->loadMultiple(), [$this, 'buildPermissions']); } /** Loading @@ -40,6 +63,15 @@ protected function buildPermissions(BlockContentType $type) { "edit any $type_id block content" => [ 'title' => $this->t('%type_name: Edit any block content', $type_params), ], "view any $type_id block content history" => [ 'title' => $this->t('%type_name: View any block content history pages', $type_params), ], "revert any $type_id block content revisions" => [ 'title' => $this->t('%type_name: Revert any block content revisions', $type_params), ], "delete any $type_id block content revisions" => [ 'title' => $this->t('%type_name: Delete any block content revisions', $type_params), ], ]; } Loading Loading
core/core.link_relation_types.yml +6 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,12 @@ delete-multiple-form: revision: uri: https://drupal.org/link-relations/revision description: A particular version of this resource. revision-revert-form: uri: https://drupal.org/link-relations/revision-revert-form description: A form where a particular version of this resource can be reverted. revision-delete-form: uri: https://drupal.org/link-relations/revision-delete-form description: A form where a particular version of this resource can be deleted. create: uri: https://drupal.org/link-relations/create description: A REST resource URL where a resource of this type can be created. Loading
core/lib/Drupal/Core/Entity/Controller/VersionHistoryController.php +5 −4 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ use Drupal\Core\Entity\RevisionableStorageInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Link; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Entity\RevisionLogInterface; Loading Loading @@ -155,10 +156,10 @@ protected function getRevisionDescription(RevisionableInterface $revision): arra $linkText = $revision->access('view label') ? $revision->label() : $this->t('- Restricted access -'); } $revisionViewLink = $revision->toLink($linkText, 'revision'); $context['revision'] = $revisionViewLink->getUrl()->access() ? $revisionViewLink->toString() : (string) $revisionViewLink->getText(); $url = $revision->hasLinkTemplate('revision') ? $revision->toUrl('revision') : NULL; $context['revision'] = $url && $url->access() ? Link::fromTextAndUrl($linkText, $url)->toString() : (string) $linkText; $context['message'] = $revision instanceof RevisionLogInterface ? [ '#markup' => $revision->getRevisionLogMessage(), '#allowed_tags' => Xss::getHtmlTagList(), Loading
core/modules/block_content/block_content.install +24 −0 Original line number Diff line number Diff line Loading @@ -5,9 +5,33 @@ * Install, update and uninstall functions for the block_content module. */ use Drupal\Core\Entity\Form\RevisionDeleteForm; use Drupal\Core\Entity\Form\RevisionRevertForm; use Drupal\Core\Entity\Routing\RevisionHtmlRouteProvider; use Drupal\Core\StringTranslation\TranslatableMarkup; /** * Implements hook_update_last_removed(). */ function block_content_update_last_removed() { return 8600; } /** * Update entity definition to handle revision routes. */ function block_content_update_10100(&$sandbox = NULL): TranslatableMarkup { $entityDefinitionUpdateManager = \Drupal::entityDefinitionUpdateManager(); $definition = $entityDefinitionUpdateManager->getEntityType('block_content'); $routeProviders = $definition->get('route_provider'); $routeProviders['revision'] = RevisionHtmlRouteProvider::class; $definition ->setFormClass('revision-delete', RevisionDeleteForm::class) ->setFormClass('revision-revert', RevisionRevertForm::class) ->set('route_provider', $routeProviders) ->setLinkTemplate('revision-delete-form', '/block/{block_content}/revision/{block_content_revision}/delete') ->setLinkTemplate('revision-revert-form', '/block/{block_content}/revision/{block_content_revision}/revert') ->setLinkTemplate('version-history', '/block/{block_content}/revisions'); $entityDefinitionUpdateManager->updateEntityType($definition); return \t('Added revision routes to Custom block entity type.'); }
core/modules/block_content/src/BlockContentAccessControlHandler.php +27 −14 Original line number Diff line number Diff line Loading @@ -54,21 +54,34 @@ public static function createInstance(ContainerInterface $container, EntityTypeI * {@inheritdoc} */ protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) { assert($entity instanceof BlockContentInterface); $bundle = $entity->bundle(); $forbidIfNotDefaultAndLatest = fn () => AccessResult::forbiddenIf($entity->isDefaultRevision() && $entity->isLatestRevision()); $access = match ($operation) { // 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()) 'view' => AccessResult::allowedIf($entity->isPublished()) ->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); } ->orIf(AccessResult::allowedIfHasPermission($account, 'edit any ' . $bundle . ' block content')), 'update' => AccessResult::allowedIfHasPermission($account, 'administer blocks') ->orIf(AccessResult::allowedIfHasPermission($account, 'edit any ' . $bundle . ' block content')), // Revisions. 'view all revisions' => AccessResult::allowedIfHasPermissions($account, [ 'administer blocks', 'view any ' . $bundle . ' block content history', ], 'OR'), 'revert' => AccessResult::allowedIfHasPermissions($account, [ 'administer blocks', 'revert any ' . $bundle . ' block content revisions', ], 'OR')->orIf($forbidIfNotDefaultAndLatest()), 'delete revision' => AccessResult::allowedIfHasPermissions($account, [ 'administer blocks', 'delete any ' . $bundle . ' block content revisions', ], 'OR')->orIf($forbidIfNotDefaultAndLatest()), default => 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. Loading
core/modules/block_content/src/BlockContentPermissions.php +34 −2 Original line number Diff line number Diff line Loading @@ -3,17 +3,40 @@ namespace Drupal\block_content; use Drupal\block_content\Entity\BlockContentType; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\BundlePermissionHandlerTrait; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provide dynamic permissions for blocks of different types. */ class BlockContentPermissions { class BlockContentPermissions implements ContainerInjectionInterface { use StringTranslationTrait; use BundlePermissionHandlerTrait; /** * Constructs a BlockContentPermissions instance. * * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager * Entity type manager. */ public function __construct( protected EntityTypeManagerInterface $entityTypeManager, ) { } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('entity_type.manager'), ); } /** * Build permissions for each block type. * Loading @@ -21,7 +44,7 @@ class BlockContentPermissions { * The block type permissions. */ public function blockTypePermissions() { return $this->generatePermissions(BlockContentType::loadMultiple(), [$this, 'buildPermissions']); return $this->generatePermissions($this->entityTypeManager->getStorage('block_content_type')->loadMultiple(), [$this, 'buildPermissions']); } /** Loading @@ -40,6 +63,15 @@ protected function buildPermissions(BlockContentType $type) { "edit any $type_id block content" => [ 'title' => $this->t('%type_name: Edit any block content', $type_params), ], "view any $type_id block content history" => [ 'title' => $this->t('%type_name: View any block content history pages', $type_params), ], "revert any $type_id block content revisions" => [ 'title' => $this->t('%type_name: Revert any block content revisions', $type_params), ], "delete any $type_id block content revisions" => [ 'title' => $this->t('%type_name: Delete any block content revisions', $type_params), ], ]; } Loading