Last active
November 10, 2023 21:23
-
-
Save adamfranco/5814eba660cbda3c93b5253b28b325ab to your computer and use it in GitHub Desktop.
Extending the Drupal Group module with an AccessControl decorator for a node visibility field
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
services: | |
group.relation_handler_decorator.access_control.visibility_field: | |
class: 'Drupal\middlebury_course_hub\Plugin\Group\RelationHandler\VisibilityFieldAccessControl' | |
decorates: 'group.relation_handler.access_control' | |
decoration_priority: 500 | |
arguments: ['@group.relation_handler_decorator.access_control.visibility_field.inner'] | |
shared: false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Drupal\middlebury_course_hub\Plugin\Group\RelationHandler; | |
use Drupal\Core\Access\AccessResult; | |
use Drupal\Core\Entity\EntityInterface; | |
use Drupal\Core\Session\AccountInterface; | |
use Drupal\group\Plugin\Group\RelationHandler\AccessControlTrait; | |
use Drupal\group\Plugin\Group\RelationHandler\AccessControlInterface; | |
/** | |
* An access control handler that grants view access based on visibility field. | |
* | |
* In the Course Hub instructors can set a "visibility" field to make syllabus | |
* and resources visibile to just the "class" (group members), "institution" | |
* (all authenticated users), or "public" (anonymous) visitors. | |
* | |
* This Access Control handler looks at that field and opens up access if | |
* needed. | |
*/ | |
class VisibilityFieldAccessControl implements AccessControlInterface { | |
use AccessControlTrait; | |
/** | |
* Constructs a new VisibilityFieldAccessControl. | |
* | |
* @param \Drupal\group\Plugin\Group\RelationHandler\AccessControlInterface $parent | |
* The parent access control handler. | |
*/ | |
public function __construct(AccessControlInterface $parent) { | |
$this->parent = $parent; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function supportsOperation($operation, $target) { | |
// We know how to check view on entities. | |
if ($operation == 'view' && $target == 'entity') { | |
return TRUE; | |
} | |
// Yield to the handler we are decorating. Other access control checks will | |
// be passed through the chain of decorators for this handler. | |
return $this->parent->supportsOperation($operation, $target); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function entityAccess(EntityInterface $entity, $operation, AccountInterface $account, $return_as_object = FALSE) { | |
// For entities that have our CourseHub-specific visibility field, allow | |
// view access to syllabi and resources when the instructor choses | |
// "All Middlebury People" or "Public / Anyone in the world" as the value. | |
if ($operation == 'view' && $entity->hasField('field_visibility')) { | |
// If the visibility is set to public, allow all to view. | |
if ($entity->field_visibility->value == 'public') { | |
$result = AccessResult::allowed(); | |
} | |
// Allow authenticated users view-access when 'institution' is chosen. | |
// Note: This may be worth refactoring into a role check. | |
elseif ($entity->field_visibility->value == 'institution' && $account->isAuthenticated()) { | |
$result = AccessResult::allowed(); | |
} | |
// Otherwise, get the result from the handler we are decorating. | |
// Unless another decorator takes precedence, the Group module will check | |
// group membership and role permissions to provide a view access result. | |
else { | |
$result = $this->parent->entityAccess($entity, $operation, $account, TRUE); | |
} | |
// We need to add the entity as a dependency because its visibility | |
// field's value might change. | |
$result->addCacheableDependency($entity); | |
return $return_as_object ? $result : $result->isAllowed(); | |
} | |
// For other operations and entities without our visibility field, yield to | |
// the handler we are decorating and let it provide the result. | |
return $this->parent->entityAccess($entity, $operation, $account, $return_as_object); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you! Your example of a service definition saved my day.