diff --git a/entity.services.yml b/entity.services.yml index 0440b03b1ac091ed38f90c291cb49514a439bd39..3abb91e40048fddbdf3521862065d017feb4732a 100644 --- a/entity.services.yml +++ b/entity.services.yml @@ -9,3 +9,9 @@ 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 797aab4e441c4072ddfe2698d01414b9c44c0c1a..8a4184f6c1c76d49da0b2c6c872671ca88e33476 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 0000000000000000000000000000000000000000..b85ee8329d8465caf9e316ee1b2a98fbb4234db2 --- /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 0000000000000000000000000000000000000000..f9e6576542eb80bf2b6df6028acf64f239e8a77e --- /dev/null +++ b/src/Routing/RevisionRouteProvider.php @@ -0,0 +1,65 @@ +<?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/EntityRevisionLogTraitTest.php b/tests/Kernel/EntityRevisionLogTraitTest.php index 75786360f0913261eb54a740f6d4811d4e3ba8b4..d2c7a87adabbabd5d7098629c6eb605bc12575b1 100644 --- a/tests/Kernel/EntityRevisionLogTraitTest.php +++ b/tests/Kernel/EntityRevisionLogTraitTest.php @@ -7,7 +7,7 @@ namespace Drupal\Tests\entity\Kernel; -use Drupal\entity_module_test\Entity\EntityWithRevisionLog; +use Drupal\entity_module_test\Entity\EnhancedEntity; use Drupal\KernelTests\KernelTestBase; use Drupal\user\Entity\User; @@ -42,7 +42,7 @@ class EntityRevisionLogTraitTest extends KernelTestBase { $user2->save(); /** @var \Drupal\entity\Revision\EntityRevisionLogInterface $entity */ - $entity = EntityWithRevisionLog::create([ + $entity = EnhancedEntity::create([ 'revision_user' => $user->id(), 'revision_created' => 1447941735, 'revision_log_message' => 'Test message', diff --git a/tests/Kernel/RevisionBasicUITest.php b/tests/Kernel/RevisionBasicUITest.php new file mode 100644 index 0000000000000000000000000000000000000000..e19fdefae5f3f86b8047cc58cb6c0b20ecff8028 --- /dev/null +++ b/tests/Kernel/RevisionBasicUITest.php @@ -0,0 +1,75 @@ +<?php + +/** + * @file + * Contains \Drupal\Tests\entity\Kernel\RevisionBasicUITest. + */ + +namespace Drupal\Tests\entity\Kernel; + +use Drupal\entity_module_test\Entity\EnhancedEntity; +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_enhanced'); + $this->installSchema('system', 'router'); + + \Drupal::service('router.builder')->rebuild(); + } + + public function testRevisionView() { + $entity = EnhancedEntity::create([ + 'name' => 'rev 1', + ]); + $entity->save(); + + $revision = clone $entity; + $revision->name->value = 'rev 2'; + $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_enhanced revisions'); + $role->grantPermission('administer entity_test_enhanced'); + $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()); + $this->assertNotContains('rev 1', $response->getContent()); + $this->assertContains('rev 2', $response->getContent()); + } + +} diff --git a/tests/modules/entity_module_test/src/Entity/EnhancedEntity.php b/tests/modules/entity_module_test/src/Entity/EnhancedEntity.php new file mode 100644 index 0000000000000000000000000000000000000000..9cefdace06b340505575e0b7b63c857b49a607be --- /dev/null +++ b/tests/modules/entity_module_test/src/Entity/EnhancedEntity.php @@ -0,0 +1,71 @@ +<?php + +/** + * @file + * Contains \Drupal\entity_module_test\Entity\EnhancedEntity. + */ + +namespace Drupal\entity_module_test\Entity; + +use Drupal\Core\Entity\ContentEntityBase; +use Drupal\Core\Entity\EntityTypeInterface; +use Drupal\Core\Field\BaseFieldDefinition; +use Drupal\entity\EntityKeysFieldsTrait; +use Drupal\entity\Revision\EntityRevisionLogTrait; + +/** + * Provides a test entity which uses all the capabilities of entity module. + * + * @ContentEntityType( + * id = "entity_test_enhanced", + * label = @Translation("Entity test with enhancements"), + * handlers = { + * "storage" = "\Drupal\Core\Entity\Sql\SqlContentEntityStorage", + * "route_provider" = { + * "revision" = "\Drupal\entity\Routing\RevisionRouteProvider", + * }, + * }, + * base_table = "entity_test_enhanced", + * data_table = "entity_test_enhanced_field_data", + * revision_table = "entity_test_enhanced_revision", + * revision_data_table = "entity_test_enhanced_field_revision", + * translatable = TRUE, + * revisionable = TRUE, + * admin_permission = "administer entity_test_enhanced", + * entity_keys = { + * "id" = "id", + * "revision" = "vid", + * "langcode" = "langcode", + * }, + * links = { + * "revision" = "/entity_test_enhanced/{entity_test_enhanced}/revisions/{entity_test_enhanced_revision}/view", + * } + * ) + */ +class EnhancedEntity extends ContentEntityBase { + + use EntityRevisionLogTrait; + use EntityKeysFieldsTrait; + + /** + * {@inheritdoc} + */ + public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { + $fields = []; + + $fields += static::entityKeysBaseFieldDefinitions($entity_type); + $fields += static::entityRevisionLogBaseFieldDefinitions(); + + $fields['name'] = BaseFieldDefinition::create('string') + ->setLabel('Name') + ->setRevisionable(TRUE) + ->setDisplayOptions('view', [ + 'label' => 'hidden', + 'type' => 'string', + 'weight' => -5, + ]); + + return $fields; + } + +} diff --git a/tests/modules/entity_module_test/src/Entity/EntityWithRevisionLog.php b/tests/modules/entity_module_test/src/Entity/EntityWithRevisionLog.php deleted file mode 100644 index d44f34b623a6ae422bf56a07e93d8d5078dcb7b4..0000000000000000000000000000000000000000 --- a/tests/modules/entity_module_test/src/Entity/EntityWithRevisionLog.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\entity_module_test\Entity\EntityWithRevisionLog. - */ - -namespace Drupal\entity_module_test\Entity; - -use Drupal\Core\Entity\ContentEntityBase; -use Drupal\Core\Entity\EntityTypeInterface; -use Drupal\Core\Field\BaseFieldDefinition; -use Drupal\entity\EntityKeysFieldsTrait; -use Drupal\entity\Revision\EntityRevisionLogTrait; - -/** - * @ContentEntityType( - * id = "entity_test__revision_log", - * label = @Translation("Entity test with revision log"), - * handlers = { - * "storage" = "\Drupal\Core\Entity\Sql\SqlContentEntityStorage", - * }, - * translatable = TRUE, - * revisionable = TRUE, - * entity_keys = { - * "id" = "id", - * "revision" = "vid", - * "langcode" = "langcode", - * } - * ) - */ -class EntityWithRevisionLog extends ContentEntityBase { - - use EntityRevisionLogTrait; - use EntityKeysFieldsTrait; - - /** - * {@inheritdoc} - */ - public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { - $fields = []; - - $fields += static::entityKeysBaseFieldDefinitions($entity_type); - $fields += static::entityRevisionLogBaseFieldDefinitions(); - - return $fields; - } - -}