diff --git a/modules/ggroup/ggroup.services.yml b/modules/ggroup/ggroup.services.yml index a53105c..96a5e18 100644 --- a/modules/ggroup/ggroup.services.yml +++ b/modules/ggroup/ggroup.services.yml @@ -14,6 +14,6 @@ services: arguments: ['@database'] ggroup.inherit_calculator: class: 'Drupal\ggroup\Access\InheritGroupPermissionCalculator' - arguments: ['@entity_type.manager', '@ggroup.group_hierarchy_manager'] + arguments: ['@entity_type.manager', '@ggroup.group_hierarchy_manager', '@group.membership_loader'] tags: - { name: group_permission_calculator, priority: -110 } diff --git a/modules/ggroup/src/Access/InheritGroupPermissionCalculator.php b/modules/ggroup/src/Access/InheritGroupPermissionCalculator.php index 22aed29..0b43b47 100644 --- a/modules/ggroup/src/Access/InheritGroupPermissionCalculator.php +++ b/modules/ggroup/src/Access/InheritGroupPermissionCalculator.php @@ -9,6 +9,9 @@ use Drupal\group\Access\GroupPermissionCalculatorBase; use Drupal\group\Access\RefinableCalculatedGroupPermissions; use Drupal\group\Access\CalculatedGroupPermissionsItem; use Drupal\group\Access\CalculatedGroupPermissionsItemInterface; +use Drupal\group\Entity\GroupContentType; +use Drupal\group\GroupMembershipLoader; +use Drupal\group_permissions\Entity\GroupPermission; /** * Calculates group permissions for an account. @@ -29,6 +32,13 @@ class InheritGroupPermissionCalculator extends GroupPermissionCalculatorBase { */ protected $entityTypeManager; + /** + * The group membership loader. + * + * @var \Drupal\group\GroupMembershipLoader + */ + protected $membershipLoader; + /** * Constructs a InheritGroupPermissionCalculator object. * @@ -36,10 +46,13 @@ class InheritGroupPermissionCalculator extends GroupPermissionCalculatorBase { * The entity type manager. * @param \Drupal\ggroup\GroupHierarchyManager $hierarchy_manager * The group hierarchy manager. + * @param \Drupal\group\GroupMembershipLoader $membership_loader + * The group membership loader. */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, GroupHierarchyManager $hierarchy_manager) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, GroupHierarchyManager $hierarchy_manager, GroupMembershipLoader $membership_loader) { $this->entityTypeManager = $entity_type_manager; $this->hierarchyManager = $hierarchy_manager; + $this->membershipLoader = $membership_loader; } /** @@ -52,9 +65,20 @@ class InheritGroupPermissionCalculator extends GroupPermissionCalculatorBase { $user = $this->entityTypeManager->getStorage('user')->load($account->id()); $calculated_permissions->addCacheableDependency($user); - $groups = $this->entityTypeManager->getStorage('group')->loadMultiple(); + foreach ($this->membershipLoader->loadByUser($account) as $group_membership) { + $group = $group_membership->getGroup(); + + // Add group content types as a cache dependency. + $plugins = $group->getGroupType()->getInstalledContentPlugins(); + foreach ($plugins as $plugin) { + if ($plugin->getEntityTypeId() == 'group') { + $group_content_types = GroupContentType::loadByContentPluginId($plugin->getPluginId()); + foreach ($group_content_types as $group_content_type) { + $calculated_permissions->addCacheableDependency($group_content_type); + } + } + } - foreach ($groups as $group) { $group_roles = $this->hierarchyManager->getInheritedGroupRoleIdsByUser($group, $account); $permission_sets = []; @@ -64,6 +88,7 @@ class InheritGroupPermissionCalculator extends GroupPermissionCalculatorBase { } $permissions = $permission_sets ? array_merge(...$permission_sets) : []; + $item = new CalculatedGroupPermissionsItem( CalculatedGroupPermissionsItemInterface::SCOPE_GROUP, $group->id(), @@ -72,18 +97,105 @@ class InheritGroupPermissionCalculator extends GroupPermissionCalculatorBase { $calculated_permissions->addItem($item); $calculated_permissions->addCacheableDependency($group); - } return $calculated_permissions; } public function calculateAnonymousPermissions() { - return parent::calculateAnonymousPermissions(); + $calculated_permissions = new RefinableCalculatedGroupPermissions(); + $calculated_permissions->addCacheContexts(['user']); + + // We have to select all the groups, because we need the mapping in + // both directions. + $groups = $this->entityTypeManager->getStorage('group')->loadMultiple(); + + foreach ($groups as $group) { + $permission_sets = []; + + // Add group content types as a cache dependency. + $plugins = $group->getGroupType()->getInstalledContentPlugins(); + foreach ($plugins as $plugin) { + if ($plugin->getEntityTypeId() == 'group') { + $group_content_types = GroupContentType::loadByContentPluginId($plugin->getPluginId()); + foreach ($group_content_types as $group_content_type) { + $calculated_permissions->addCacheableDependency($group_content_type); + } + } + } + + $group_roles = $this->hierarchyManager->getInheritedGroupAnonymousRoleIds($group, $groups); + + foreach ($group_roles as $group_role) { + $permission_sets[] = $group_role->getPermissions(); + $calculated_permissions->addCacheableDependency($group_role); + } + + $permissions = $permission_sets ? array_merge(...$permission_sets) : []; + + $item = new CalculatedGroupPermissionsItem( + CalculatedGroupPermissionsItemInterface::SCOPE_GROUP, + $group->id(), + $permissions + ); + + $calculated_permissions->addItem($item); + $calculated_permissions->addCacheableDependency($group); + } + + return $calculated_permissions; } public function calculateOutsiderPermissions(AccountInterface $account) { - return $this->calculateMemberPermissions($account); + $calculated_permissions = new RefinableCalculatedGroupPermissions(); + $calculated_permissions->addCacheContexts(['user']); + + $user = $this->entityTypeManager->getStorage('user')->load($account->id()); + $calculated_permissions->addCacheableDependency($user); + + // We have to select all the groups, because we need the mapping in + // both directions. + $groups = $this->entityTypeManager->getStorage('group')->loadMultiple(); + + foreach ($groups as $group) { + // We check only groups where the user is outsider. + if ($group->getMember($user)) { + continue; + } + + // Add group content types as a cache dependency. + $plugins = $group->getGroupType()->getInstalledContentPlugins(); + foreach ($plugins as $plugin) { + if ($plugin->getEntityTypeId() == 'group') { + $group_content_types = GroupContentType::loadByContentPluginId($plugin->getPluginId()); + foreach ($group_content_types as $group_content_type) { + $calculated_permissions->addCacheableDependency($group_content_type); + } + } + } + + $permission_sets = []; + + $group_roles = $this->hierarchyManager->getInheritedGroupOutsiderRoleIds($group, $user); + + foreach ($group_roles as $group_role) { + $permission_sets[] = $group_role->getPermissions(); + $calculated_permissions->addCacheableDependency($group_role); + } + + $permissions = $permission_sets ? array_merge(...$permission_sets) : []; + + $item = new CalculatedGroupPermissionsItem( + CalculatedGroupPermissionsItemInterface::SCOPE_GROUP, + $group->id(), + $permissions + ); + + $calculated_permissions->addItem($item); + $calculated_permissions->addCacheableDependency($group); + } + + return $calculated_permissions; } } diff --git a/modules/ggroup/src/GroupHierarchyManager.php b/modules/ggroup/src/GroupHierarchyManager.php index 70ecacc..25f37d7 100644 --- a/modules/ggroup/src/GroupHierarchyManager.php +++ b/modules/ggroup/src/GroupHierarchyManager.php @@ -5,6 +5,8 @@ namespace Drupal\ggroup; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Session\AccountInterface; use Drupal\ggroup\Graph\GroupGraphStorageInterface; +use Drupal\group\Entity\Group; +use Drupal\group\Entity\GroupContentType; use Drupal\group\Entity\GroupInterface; use Drupal\group\Entity\GroupContentInterface; use Drupal\group\GroupMembership; @@ -61,6 +63,15 @@ class GroupHierarchyManager implements GroupHierarchyManagerInterface { */ protected $userGroupRoles = []; + /** + * Static cache for all outsider roles of group type. + * + * A nested array with all outsider roles keyed by group type ID and role ID. + * + * @var array + */ + protected $groupTypeOutsiderRoles = []; + /** * Constructs a new GroupHierarchyManager. * @@ -195,6 +206,98 @@ class GroupHierarchyManager implements GroupHierarchyManagerInterface { $mapped_role_ids[] = array_intersect_key($role_map[$group_id][$membership_gid], array_flip($this->getMembershipRoles($membership))); } + + $mapped_role_ids = array_replace_recursive(...$mapped_role_ids); + + $this->userGroupRoles[$account_id][$group_id] = $this->entityTypeManager->getStorage('group_role')->loadMultiple(array_unique($mapped_role_ids)); + return $this->userGroupRoles[$account_id][$group_id]; + } + + /** + * {@inheritdoc} + */ + public function getInheritedGroupOutsiderRoleIds(GroupInterface $group, AccountInterface $account) { + + $account_id = $account->id(); + $group_id = $group->id(); + + if (isset($this->userGroupRoles[$account_id][$group_id])) { + return $this->userGroupRoles[$account_id][$group_id]; + } + + if (empty($this->userMemberships[$account_id])) { + $this->userMemberships[$account_id] = $this->membershipLoader->loadByUser($account); + } + + $role_map = $this->groupRoleInheritanceManager->getAllInheritedGroupRoleIds($group_id); + + $mapped_role_ids = [[]]; + foreach ($this->userMemberships[$account_id] as $membership) { + $membership_gid = $membership->getGroupContent()->gid->target_id; + $role_mapping = []; + + // Get all outsider roles. + $outsider_roles = $this->getOutsiderGroupRoles($membership->getGroupContent()->getGroup()); + if (!empty($role_map[$membership_gid][$group_id])) { + $role_mapping = array_intersect_key($role_map[$membership_gid][$group_id], $outsider_roles); + } + else if (!empty($role_map[$group_id][$membership_gid])) { + $role_mapping = array_intersect_key($role_map[$group_id][$membership_gid], $outsider_roles); + } + + $mapped_role_ids[] = $role_mapping; + } + + $mapped_role_ids = array_replace_recursive(...$mapped_role_ids); + + $this->userGroupRoles[$account_id][$group_id] = $this->entityTypeManager->getStorage('group_role')->loadMultiple(array_unique($mapped_role_ids)); + return $this->userGroupRoles[$account_id][$group_id]; + } + + /** + * Get outsider group type roles. + * + * @param Group $group + * Group. + * @return arrays + * Group type roles. + */ + protected function getOutsiderGroupRoles(Group $group) { + if (!isset($this->groupTypeOutsiderRoles[$group->getGroupType()->id()])) { + $storage = $this->entityTypeManager->getStorage('group_role'); + $outsider_roles = $storage->loadSynchronizedByGroupTypes([$group->getGroupType()->id()]); + $outsider_roles[$group->getGroupType()->getOutsiderRoleId()] = $group->getGroupType()->getOutsiderRole(); + $this->groupTypeOutsiderRoles[$group->getGroupType()->id()] = $outsider_roles; + } + return $this->groupTypeOutsiderRoles[$group->getGroupType()->id()]; + } + + /** + * {@inheritdoc} + */ + public function getInheritedGroupAnonymousRoleIds(GroupInterface $group, array $groups) { + // Anonymous user doesn't have id, but we want to cache it. + $account_id = 0; + $group_id = $group->id(); + + $role_map = $this->groupRoleInheritanceManager->getAllInheritedGroupRoleIds($group_id); + $mapped_role_ids = [[]]; + foreach ($groups as $group_item) { + $group_item_gid = $group_item->id(); + $role_mapping = []; + + $anonymous_role = [$group_item->getGroupType()->getAnonymousRoleId() => $group_item->getGroupType()->getAnonymousRole()]; + + if (!empty($role_map[$group_item_gid][$group_id])) { + $role_mapping = array_intersect_key($role_map[$group_item_gid][$group_id], $anonymous_role); + } + else if (!empty($role_map[$group_id][$group_item_gid])) { + $role_mapping = array_intersect_key($role_map[$group_id][$group_item_gid], $anonymous_role); + } + + $mapped_role_ids[] = $role_mapping; + } + $mapped_role_ids = array_replace_recursive(...$mapped_role_ids); $this->userGroupRoles[$account_id][$group_id] = $this->entityTypeManager->getStorage('group_role')->loadMultiple(array_unique($mapped_role_ids)); diff --git a/modules/ggroup/src/GroupHierarchyManagerInterface.php b/modules/ggroup/src/GroupHierarchyManagerInterface.php index 09f6b03..b83b97c 100644 --- a/modules/ggroup/src/GroupHierarchyManagerInterface.php +++ b/modules/ggroup/src/GroupHierarchyManagerInterface.php @@ -103,4 +103,8 @@ interface GroupHierarchyManagerInterface { */ public function getInheritedGroupRoleIdsByUser(GroupInterface $group, AccountInterface $account); + public function getInheritedGroupOutsiderRoleIds(GroupInterface $group, AccountInterface $account); + + public function getInheritedGroupAnonymousRoleIds(GroupInterface $group, array $groups); + } diff --git a/modules/ggroup/src/GroupRoleInheritance.php b/modules/ggroup/src/GroupRoleInheritance.php index a7a80ba..7aa3e7b 100644 --- a/modules/ggroup/src/GroupRoleInheritance.php +++ b/modules/ggroup/src/GroupRoleInheritance.php @@ -77,7 +77,7 @@ class GroupRoleInheritance implements GroupRoleInheritanceInterface { */ public function getAllInheritedGroupRoleIds($group_id) { if (!empty($this->roleMap[$group_id])) { - return $this->roleMap; + return $this->roleMap[$group_id]; } $cid = GroupRoleInheritanceInterface::ROLE_MAP_CID . ':' . $group_id; @@ -100,7 +100,7 @@ class GroupRoleInheritance implements GroupRoleInheritanceInterface { $cid = GroupRoleInheritanceInterface::ROLE_MAP_CID . ':' . $group_id; $this->cache->delete($cid); $this->roleMap[$group_id] = $this->build($group_id); - $this->cache->set($cid, $this->roleMap[$group_id], Cache::PERMANENT, ['group:' . $group_id]); + $this->cache->set($cid, $this->roleMap[$group_id], Cache::PERMANENT, ["group:$group_id"]); } /**