diff --git a/core/includes/entity.api.php b/core/includes/entity.api.php index e6e1f7c..c5713e6 100644 --- a/core/includes/entity.api.php +++ b/core/includes/entity.api.php @@ -11,6 +11,86 @@ */ /** + * Checks entity operation access. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity to check access to. + * @param string $operation + * The operation that is to be performed on $entity. + * @param \Drupal\Core\Session\AccountInterface $account + * The account trying to access the entity. + * @param string $langcode + * The code of the language $entity is accessed in. + * + * @return bool|null + * A boolean to explicitly allow or deny access, or NULL to neither allow nor + * deny access. + * + * @see \Drupal\Core\Entity\EntityAccessController + */ +function hook_entity_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account, $langcode) { + return NULL; +} + +/** + * Checks entity operation access for a specific entity type. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity to check access to. + * @param string $operation + * The operation that is to be performed on $entity. + * @param \Drupal\Core\Session\AccountInterface $account + * The account trying to access the entity. + * @param string $langcode + * The code of the language $entity is accessed in. + * + * @return bool|null + * A boolean to explicitly allow or deny access, or NULL to neither allow nor + * deny access. + * + * @see \Drupal\Core\Entity\EntityAccessController + */ +function hook_ENTITY_TYPE_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account, $langcode) { + return NULL; +} + +/** + * Checks entity create access. + * + * @param \Drupal\Core\Session\AccountInterface $account + * The account trying to access the entity. + * @param string $langcode + * The code of the language $entity is accessed in. + * + * @return bool|null + * A boolean to explicitly allow or deny access, or NULL to neither allow nor + * deny access. + * + * @see \Drupal\Core\Entity\EntityAccessController + */ +function hook_entity_create_access(\Drupal\Core\Session\AccountInterface $account, $langcode) { + return NULL; +} + +/** + * Checks entity create access for a specific entity type. + * + * @param \Drupal\Core\Session\AccountInterface $account + * The account trying to access the entity. + * @param string $langcode + * The code of the language $entity is accessed in. + * + * @return bool|null + * A boolean to explicitly allow or deny access, or NULL to neither allow nor + * deny access. + * + * @see \Drupal\Core\Entity\EntityAccessController + */ +function hook_ENTITY_TYPE_create_access(\Drupal\Core\Session\AccountInterface $account, $langcode) { + return NULL; +} + +/** * Add to entity type definitions. * * Modules may implement this hook to add information to defined entity types. diff --git a/core/lib/Drupal/Core/Entity/EntityAccessController.php b/core/lib/Drupal/Core/Entity/EntityAccessController.php index 47573c2..5583aec 100644 --- a/core/lib/Drupal/Core/Entity/EntityAccessController.php +++ b/core/lib/Drupal/Core/Entity/EntityAccessController.php @@ -44,7 +44,7 @@ class EntityAccessController implements EntityAccessControllerInterface { * The entity type of the access controller instance. */ public function __construct($entity_type) { - $this->entity_type = $entity_type; + $this->entityType = $entity_type; } /** @@ -58,15 +58,19 @@ public function access(EntityInterface $entity, $operation, $langcode = Language return $access; } - // Invoke hook_entity_access(), hook results take precedence over overridden - // implementations of EntityAccessController::checkAccess(). Entities - // that have checks that need to be done before the hook is invoked should - // do so by overridding this method. + // Invoke hook_entity_access() and hook_ENTITY_TYPE_access(). Hook results + // take precedence over overridden implementations of + // EntityAccessController::checkAccess(). Entities that have checks that + // need to be done before the hook is invoked should do so by overriding + // this method. // We grant access to the entity if both of these conditions are met: // - No modules say to deny access. // - At least one module says to grant access. - $access = $this->moduleHandler->invokeAll($entity->entityType() . '_access', array($entity, $operation, $account, $langcode)); + $access = array_merge( + $this->moduleHandler->invokeAll('entity_access', array($entity, $operation, $account, $langcode)), + $this->moduleHandler->invokeAll($entity->entityType() . '_access', array($entity, $operation, $account, $langcode)) + ); if (($return = $this->processAccessHookResults($access)) === NULL) { // No module had an opinion about the access, so let's the access @@ -196,15 +200,19 @@ public function createAccess($entity_bundle = NULL, AccountInterface $account = return $access; } - // Invoke hook_entity_access(), hook results take precedence over overridden - // implementations of EntityAccessController::checkAccess(). Entities - // that have checks that need to be done before the hook is invoked should - // do so by overridding this method. + // Invoke hook_entity_create_access() and hook_ENTITY_TYPE_create_access(). + // Hook results take precedence over overridden implementations of + // EntityAccessController::checkAccess(). Entities that have checks that + // need to be done before the hook is invoked should do so by overriding + // this method. // We grant access to the entity if both of these conditions are met: // - No modules say to deny access. // - At least one module says to grant access. - $access = $this->moduleHandler->invokeAll($this->entity_type . '_create_access', array($account, $context['langcode'])); + $access = array_merge( + $this->moduleHandler->invokeAll('entity_create_access', array($account, $context['langcode'])), + $this->moduleHandler->invokeAll($this->entityType . '_create_access', array($account, $context['langcode'])) + ); if (($return = $this->processAccessHookResults($access)) === NULL) { // No module had an opinion about the access, so let's the access diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php index be02e7f..2fd0d2d 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php @@ -136,4 +136,24 @@ function testEntityTranslationAccess() { 'view' => TRUE, ), $translation); } + + /** + * Tests hook invocations. + */ + protected function testHooks() { + $state = $this->container->get('state'); + $entity = entity_create('entity_test', array( + 'name' => 'test', + )); + + // Test hook_entity_create_access() and hook_ENTITY_TYPE_create_access(). + $entity->access('create'); + $this->assertEqual($state->get('entity_test_entity_create_access'), TRUE); + $this->assertEqual($state->get('entity_test_entity_test_create_access'), TRUE); + + // Test hook_entity_access() and hook_ENTITY_TYPE_access(). + $entity->access('view'); + $this->assertEqual($state->get('entity_test_entity_access'), TRUE); + $this->assertEqual($state->get('entity_test_entity_test_access'), TRUE); + } } diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module index 1c81419..e06eab4 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.module +++ b/core/modules/system/tests/modules/entity_test/entity_test.module @@ -6,6 +6,7 @@ */ use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Session\AccountInterface; use Drupal\entity\Entity\EntityFormDisplay; use Drupal\field\Entity\Field; use Drupal\field\Entity\FieldInstance; @@ -510,3 +511,31 @@ function entity_test_entity_prepare_view($entity_type, array $entities, array $d } } } + +/** + * Implements hook_entity_access(). + */ +function entity_test_entity_access(EntityInterface $entity, $operation, AccountInterface $account, $langcode) { + \Drupal::state()->set('entity_test_entity_access', TRUE); +} + +/** + * Implements hook_ENTITY_TYPE_access(). + */ +function entity_test_entity_test_access(EntityInterface $entity, $operation, AccountInterface $account, $langcode) { + \Drupal::state()->set('entity_test_entity_test_access', TRUE); +} + +/** + * Implements hook_entity_create_access(). + */ +function entity_test_entity_create_access(AccountInterface $account, $langcode) { + \Drupal::state()->set('entity_test_entity_create_access', TRUE); +} + +/** + * Implements hook_ENTITY_TYPE_create_access(). + */ +function entity_test_entity_test_create_access(AccountInterface $account, $langcode) { + \Drupal::state()->set('entity_test_entity_test_create_access', TRUE); +}