From 99f8e3e6dbd9da3c2124d2b875caaf147f95f22b Mon Sep 17 00:00:00 2001 From: Daniel Wehner <daniel@tag1consulting.com> Date: Tue, 8 Dec 2015 17:21:58 +0100 Subject: [PATCH] Add a revision view controller --- entity.services.yml | 7 ++ .../EntityRevisionRouteAccessChecker.php | 4 +- src/Controller/RevisionController.php | 36 ++++++++++ src/Routing/RevisionRouteProvider.php | 66 +++++++++++++++++ tests/Kernel/RevisionBasicUITest.php | 70 +++++++++++++++++++ .../src/Entity/EntityWithRevisionRoutes.php | 58 +++++++++++++++ 6 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 src/Controller/RevisionController.php create mode 100644 src/Routing/RevisionRouteProvider.php create mode 100644 tests/Kernel/RevisionBasicUITest.php create mode 100644 tests/modules/entity_module_test/src/Entity/EntityWithRevisionRoutes.php diff --git a/entity.services.yml b/entity.services.yml index 0440b03..e105738 100644 --- a/entity.services.yml +++ b/entity.services.yml @@ -9,3 +9,10 @@ services: class: Drupal\entity\RouteEnhancer\EntityRevisionRouteEnhancer tags: - { name: route_enhancer, priority: 20 } + + + access_checker.entity_revision: + class: \Drupal\entity\Access\EntityRevisionRouteAccessChecker + arguments: ['@entity_type.manager'] + tags: + - { name: access_check, applies_to: _entity_access_revision, needs_request: TRUE } diff --git a/src/Access/EntityRevisionRouteAccessChecker.php b/src/Access/EntityRevisionRouteAccessChecker.php index 797aab4..8a4184f 100644 --- a/src/Access/EntityRevisionRouteAccessChecker.php +++ b/src/Access/EntityRevisionRouteAccessChecker.php @@ -14,6 +14,7 @@ use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\RevisionableInterface; use Drupal\Core\Routing\Access\AccessInterface; use Drupal\Core\Session\AccountInterface; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; /** @@ -46,7 +47,8 @@ class EntityRevisionRouteAccessChecker implements AccessInterface { /** * {@inheritdoc} */ - public function access(Route $route, AccountInterface $account, RevisionableInterface $_entity_revision) { + public function access(Route $route, AccountInterface $account, Request $request) { + $_entity_revision = $request->attributes->get('_entity_revision'); $operation = $route->getRequirement('_entity_access_revision'); list(, $operation) = explode('.', $operation, 2); return AccessResult::allowedIf($_entity_revision && $this->checkAccess($_entity_revision, $account, $operation))->cachePerPermissions(); diff --git a/src/Controller/RevisionController.php b/src/Controller/RevisionController.php new file mode 100644 index 0000000..b85ee83 --- /dev/null +++ b/src/Controller/RevisionController.php @@ -0,0 +1,36 @@ +<?php + +/** + * @file + * Contains \Drupal\entity\Controller\RevisionController. + */ + +namespace Drupal\entity\Controller; + +use Drupal\Core\Entity\Controller\EntityViewController; +use Drupal\Core\Entity\EntityInterface; + +/** + * Provides some controllers related with entity revisions. + */ +class RevisionController extends EntityViewController { + + /** + * Provides a page to render a single entity revision. + * + * @param \Drupal\Core\Entity\EntityInterface $_entity_revision + * The Entity to be rendered. Note this variable is named $_entity_revision + * rather than $entity to prevent collisions with other named placeholders + * in the route. + * @param string $view_mode + * (optional) The view mode that should be used to display the entity. + * Defaults to 'full'. + * + * @return array + * A render array. + */ + public function view(EntityInterface $_entity_revision, $view_mode = 'full') { + return parent::view($_entity_revision, $view_mode); + } + +} diff --git a/src/Routing/RevisionRouteProvider.php b/src/Routing/RevisionRouteProvider.php new file mode 100644 index 0000000..71adea8 --- /dev/null +++ b/src/Routing/RevisionRouteProvider.php @@ -0,0 +1,66 @@ +<?php + +/** + * @file + * Contains \Drupal\entity\Routing\RevisionRouteProvider. + */ + +namespace Drupal\entity\Routing; + +use Drupal\Core\Entity\EntityTypeInterface; +use Drupal\Core\Entity\Routing\EntityRouteProviderInterface; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * Provides revision routes. + */ +class RevisionRouteProvider implements EntityRouteProviderInterface { + + /** + * {@inheritdoc} + */ + public function getRoutes(EntityTypeInterface $entity_type) { + $collection = new RouteCollection(); + $entity_type_id = $entity_type->id(); + + if ($view_route = $this->getRevisionViewRoute($entity_type)) { + $collection->add("entity.$entity_type_id.revision", $view_route); + } + + return $collection; + } + + /** + * Gets the entity revision view route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getRevisionViewRoute(EntityTypeInterface $entity_type) { + if ($entity_type->hasLinkTemplate('revision')) { + $entity_type_id = $entity_type->id(); + $route = new Route($entity_type->getLinkTemplate('revision')); + $route->addDefaults([ + '_controller' => '\Drupal\entity\Controller\RevisionController::view', + '_title_callback' => '\Drupal\Core\Entity\Controller\EntityController::title', + ]); + $route->addRequirements([ + '_entity_access_revision' => "$entity_type_id.view", + ]); + $route->setOption('parameters', [ + $entity_type->id() => [ + 'type' => 'entity:' . $entity_type->id(), + ], + $entity_type->id() . '_revision' => [ + 'type' => 'entity_revision:' . $entity_type->id(), + ], + ]); + return $route; + } + } + +} diff --git a/tests/Kernel/RevisionBasicUITest.php b/tests/Kernel/RevisionBasicUITest.php new file mode 100644 index 0000000..08aa99e --- /dev/null +++ b/tests/Kernel/RevisionBasicUITest.php @@ -0,0 +1,70 @@ +<?php + +/** + * @file + * Contains \Drupal\Tests\entity\Kernel\RevisionBasicUITest. + */ + +namespace Drupal\Tests\entity\Kernel; + +use Drupal\entity_module_test\Entity\EntityWithRevisionRoutes; +use Drupal\KernelTests\KernelTestBase; +use Drupal\user\Entity\Role; +use Drupal\user\Entity\User; +use Symfony\Component\HttpFoundation\Request; + +/** + * @group entity + */ +class RevisionBasicUITest extends KernelTestBase { + + /** + * {@inheritdoc} + */ + public static $modules = ['entity_module_test', 'system', 'user', 'entity']; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $this->installEntitySchema('user'); + $this->installEntitySchema('entity_test__rev_routes'); + $this->installSchema('system', 'router'); + + \Drupal::service('router.builder')->rebuild(); + } + + public function testRevisionView() { + $entity = EntityWithRevisionRoutes::create([]); + $entity->save(); + + $revision = clone $entity; + $revision->setNewRevision(TRUE); + $revision->isDefaultRevision(FALSE); + $revision->save(); + + /** @var \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel */ + $http_kernel = \Drupal::service('http_kernel'); + $request = Request::create($revision->url('revision')); + $response = $http_kernel->handle($request); + $this->assertEquals(403, $response->getStatusCode()); + + $role = Role::create(['id' => 'test_role']); + $role->grantPermission('view all entity_test__rev_routes revisions'); + $role->grantPermission('administer entity_test__revision_routes'); + $role->save(); + + $user = User::create([ + 'name' => 'Test user', + ]); + $user->addRole($role->id()); + \Drupal::service('account_switcher')->switchTo($user); + + $request = Request::create($revision->url('revision')); + $response = $http_kernel->handle($request); + $this->assertEquals(200, $response->getStatusCode()); + } + +} diff --git a/tests/modules/entity_module_test/src/Entity/EntityWithRevisionRoutes.php b/tests/modules/entity_module_test/src/Entity/EntityWithRevisionRoutes.php new file mode 100644 index 0000000..0638e3f --- /dev/null +++ b/tests/modules/entity_module_test/src/Entity/EntityWithRevisionRoutes.php @@ -0,0 +1,58 @@ +<?php + +/** + * @file + * Contains \Drupal\entity_module_test\Entity\EntityWithRevisionRoutes. + */ + +namespace Drupal\entity_module_test\Entity; + +use Drupal\Core\Entity\ContentEntityBase; +use Drupal\Core\Entity\EntityTypeInterface; +use Drupal\entity\EntityKeysFieldsTrait; +use Drupal\entity\Revision\EntityRevisionLogTrait; + +/** + * @ContentEntityType( + * id = "entity_test__rev_routes", + * label = @Translation("Entity test with revision routes"), + * handlers = { + * "storage" = "\Drupal\Core\Entity\Sql\SqlContentEntityStorage", + * "route_provider" = { + * "revision" = "\Drupal\entity\Routing\RevisionRouteProvider", + * }, + * }, + * base_table = "entity_test__revision_routes", + * data_table = "entity_test__revision_routes__field_data", + * revision_table = "entity_test__revision_routes__revision", + * revision_data_table = "entity_test__revision_routes__field_revision", + * translatable = TRUE, + * revisionable = TRUE, + * admin_permission = "administer entity_test__revision_routes", + * entity_keys = { + * "id" = "id", + * "revision" = "vid", + * "langcode" = "langcode", + * }, + * links = { + * "revision" = "/entity_test__rev_routes/{entity_test__rev_routes}/revisions/{entity_test__rev_routes_revision}/view", + * } + * ) + */ +class EntityWithRevisionRoutes extends ContentEntityBase { + + use EntityRevisionLogTrait; + use EntityKeysFieldsTrait; + + /** + * {@inheritdoc} + */ + public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { + $fields = []; + + $fields += static::entityKeysBaseFieldDefinitions($entity_type); + + return $fields; + } + +} -- GitLab