Skip to content

Instantly share code, notes, and snippets.

@adamfranco
Last active November 10, 2023 21:23
Show Gist options
  • Save adamfranco/5814eba660cbda3c93b5253b28b325ab to your computer and use it in GitHub Desktop.
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
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
<?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);
}
}
@piridium
Copy link

Thank you! Your example of a service definition saved my day.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment