Skip to content
Snippets Groups Projects
Commit 0c98c5b4 authored by Nikolay Lobachev's avatar Nikolay Lobachev
Browse files

Issue #3398067 by LOBsTerr: Add option to remove group content when user join the group

parent 5d5984fa
Branches 9.0.x
No related tags found
1 merge request!29Issue #3327576 by LOBsTerr, dxvargas: Delete request if the user becomes a...
Showing
with 127 additions and 55 deletions
group_relation.config.remove_group_membership_request:
type: 'boolean'
label: 'Remove a group membership request, when user join the group.'
views.field.approve_membership_request: views.field.approve_membership_request:
type: views.field.entity_link type: views.field.entity_link
label: 'Approve group request membership' label: 'Approve group request membership'
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
* Contains hooks for grequest module. * Contains hooks for grequest module.
*/ */
use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\grequest\Plugin\Group\Relation\GroupMembershipRequest;
use Drupal\group\Entity\GroupInterface; use Drupal\group\Entity\GroupInterface;
use Drupal\group\Entity\GroupRelationshipInterface; use Drupal\group\Entity\GroupRelationshipInterface;
...@@ -39,14 +38,14 @@ function grequest_menu_local_tasks_alter(&$data, $route_name) { ...@@ -39,14 +38,14 @@ function grequest_menu_local_tasks_alter(&$data, $route_name) {
* Implements hook_entity_delete(). * Implements hook_entity_delete().
*/ */
function grequest_group_relationship_delete(GroupRelationshipInterface $group_relationship) { function grequest_group_relationship_delete(GroupRelationshipInterface $group_relationship) {
_grequest_delete_if_matching_membership($group_relationship); _grequest_delete_group_membership_request($group_relationship);
} }
/** /**
* Implements hook_entity_insert(). * Implements hook_entity_insert().
*/ */
function grequest_group_relationship_insert(GroupRelationshipInterface $group_relationship) { function grequest_group_relationship_insert(GroupRelationshipInterface $group_relationship) {
_grequest_delete_if_matching_membership($group_relationship, TRUE); _grequest_delete_group_membership_request($group_relationship, TRUE);
} }
/** /**
...@@ -57,10 +56,10 @@ function grequest_group_relationship_insert(GroupRelationshipInterface $group_re ...@@ -57,10 +56,10 @@ function grequest_group_relationship_insert(GroupRelationshipInterface $group_re
* *
* @param \Drupal\group\Entity\GroupRelationshipInterface $group_relationship * @param \Drupal\group\Entity\GroupRelationshipInterface $group_relationship
* Group content. * Group content.
* @param $check_is_pending * @param $check_settings
* Check or not the status of group membership request is pending. * Check if plugin settings to remove group membership requests is enabled.
*/ */
function _grequest_delete_if_matching_membership(GroupRelationshipInterface $group_relationship, $check_is_pending = FALSE) { function _grequest_delete_group_membership_request(GroupRelationshipInterface $group_relationship, $check_settings = FALSE) {
if (empty($group_relationship->getEntity())) { if (empty($group_relationship->getEntity())) {
return; return;
} }
...@@ -68,9 +67,9 @@ function _grequest_delete_if_matching_membership(GroupRelationshipInterface $gro ...@@ -68,9 +67,9 @@ function _grequest_delete_if_matching_membership(GroupRelationshipInterface $gro
if ($group_relationship->getPlugin()->getPluginId() === 'group_membership') { if ($group_relationship->getPlugin()->getPluginId() === 'group_membership') {
$membership_request = \Drupal::service('grequest.membership_request_manager')->getMembershipRequest($group_relationship->getEntity(), $group_relationship->getGroup()); $membership_request = \Drupal::service('grequest.membership_request_manager')->getMembershipRequest($group_relationship->getEntity(), $group_relationship->getGroup());
if (!empty($membership_request)) { if (!empty($membership_request)) {
// We want to delete group membership request only if it is pending, in // We want to delete group membership request only if the settings
// other cases we will keep it. // for removal is enabled.
if ($check_is_pending && $membership_request->get(GroupMembershipRequest::STATUS_FIELD)->value != GroupMembershipRequest::REQUEST_PENDING) { if ($check_settings && !$membership_request->getPlugin()->getConfiguration()['remove_group_membership_request']) {
return; return;
} }
$membership_request->delete(); $membership_request->delete();
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
* Implements hook_views_data(). * Implements hook_views_data().
*/ */
function grequest_views_data() { function grequest_views_data() {
$data = [];
$data['groups']['request_membership'] = [ $data['groups']['request_membership'] = [
'title' => t('Request Membership'), 'title' => t('Request Membership'),
'help' => t('Provides a link to request membership to the group.'), 'help' => t('Provides a link to request membership to the group.'),
......
...@@ -41,7 +41,7 @@ class PendingGroupMembershipRequestAccessChecker implements AccessInterface { ...@@ -41,7 +41,7 @@ class PendingGroupMembershipRequestAccessChecker implements AccessInterface {
!empty($route_contexts['group_relationship']) && !empty($route_contexts['group_relationship']) &&
($group_membership_request = $route_match->getParameter('group_relationship')) ($group_membership_request = $route_match->getParameter('group_relationship'))
) { ) {
if (!empty($group_membership_request) && $group_membership_request->get(GroupMembershipRequest::STATUS_FIELD)->value == GroupMembershipRequest::REQUEST_PENDING) { if ($group_membership_request->get(GroupMembershipRequest::STATUS_FIELD)->value == GroupMembershipRequest::REQUEST_PENDING) {
return AccessResult::allowed(); return AccessResult::allowed();
} }
} }
......
...@@ -55,20 +55,20 @@ class MembershipRequestManager { ...@@ -55,20 +55,20 @@ class MembershipRequestManager {
* Group relationship or NULL. * Group relationship or NULL.
*/ */
public function getMembershipRequest(AccountInterface $user, GroupInterface $group) { public function getMembershipRequest(AccountInterface $user, GroupInterface $group) {
$group_type = $group->getGroupType();
if (!$group_type->hasPlugin('group_membership_request')) {
return NULL;
}
// If no responsible group relationship types were found, we return nothing. // If no responsible group relationship types were found, we return nothing.
$group_relationship_type_storage = $this->entityTypeManager->getStorage('group_relationship_type'); $group_membership_requests = $this->entityTypeManager->getStorage('group_relationship')->loadByProperties([
$group_relationship_types = $group_relationship_type_storage->loadByPluginId('group_membership_request'); 'type' => $this->entityTypeManager->getStorage('group_relationship_type')->getRelationshipTypeId($group_type->id(), 'group_membership_request'),
if (!empty($group_relationship_types)) { 'entity_id' => $user->id(),
$group_relationship_storage = $this->entityTypeManager->getStorage('group_relationship'); 'gid' => $group->id(),
$group_membership_requests = $group_relationship_storage->loadByProperties([ ]);
'type' => array_keys($group_relationship_types),
'entity_id' => $user->id(),
'gid' => $group->id(),
]);
if (!empty($group_membership_requests)) { if (!empty($group_membership_requests)) {
return reset($group_membership_requests); return reset($group_membership_requests);
}
} }
return NULL; return NULL;
...@@ -146,16 +146,20 @@ class MembershipRequestManager { ...@@ -146,16 +146,20 @@ class MembershipRequestManager {
* Group membership request group relationship. * Group membership request group relationship.
*/ */
public function create(GroupInterface $group, UserInterface $user) { public function create(GroupInterface $group, UserInterface $user) {
$group_type = $group->getGroupType();
if (!$group_type->hasPlugin('group_membership_request')) {
return NULL;
}
if ($group->getMember($user)) { if ($group->getMember($user)) {
throw new \Exception('This user is already a member of the group'); throw new \Exception('This user is already a member of the group');
} }
$plugin_id = 'group_membership_request'; $plugin_id = 'group_membership_request';
$relationship_type_storage = $this->entityTypeManager->getStorage('group_relationship_type'); $relationship_type_storage = $this->entityTypeManager->getStorage('group_relationship_type');
$group_type_id = $group->getGroupType()->id();
$group_relationship = GroupRelationship::create([ $group_relationship = GroupRelationship::create([
'type' => $relationship_type_storage->getRelationshipTypeId($group_type_id, $plugin_id), 'type' => $relationship_type_storage->getRelationshipTypeId($group_type->id(), $plugin_id),
'gid' => $group->id(), 'gid' => $group->id(),
'entity_id' => $user->id(), 'entity_id' => $user->id(),
GroupMembershipRequest::STATUS_FIELD => GroupMembershipRequest::REQUEST_NEW, GroupMembershipRequest::STATUS_FIELD => GroupMembershipRequest::REQUEST_NEW,
......
...@@ -12,7 +12,7 @@ use Drupal\group\Entity\GroupRelationshipInterface; ...@@ -12,7 +12,7 @@ use Drupal\group\Entity\GroupRelationshipInterface;
* @Action( * @Action(
* id = "grequest_approve", * id = "grequest_approve",
* label = @Translation("Approve membership request"), * label = @Translation("Approve membership request"),
* type = "group_content", * type = "group_relationship",
* confirm = TRUE, * confirm = TRUE,
* ) * )
*/ */
......
...@@ -16,6 +16,11 @@ use Symfony\Component\DependencyInjection\ContainerInterface; ...@@ -16,6 +16,11 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*/ */
abstract class MembershipRequestActionBase extends ActionBase implements ContainerFactoryPluginInterface { abstract class MembershipRequestActionBase extends ActionBase implements ContainerFactoryPluginInterface {
/**
* Membership request manager.
*
* @var \Drupal\grequest\MembershipRequestManager
*/
protected MembershipRequestManager $membershipRequestManager; protected MembershipRequestManager $membershipRequestManager;
/** /**
...@@ -48,8 +53,7 @@ abstract class MembershipRequestActionBase extends ActionBase implements Contain ...@@ -48,8 +53,7 @@ abstract class MembershipRequestActionBase extends ActionBase implements Contain
* {@inheritdoc} * {@inheritdoc}
*/ */
public function access($entity, AccountInterface $account = NULL, $return_as_object = FALSE) { public function access($entity, AccountInterface $account = NULL, $return_as_object = FALSE) {
$group = $entity->getGroup(); $access = $entity->getGroup()->hasPermission('administer membership requests', $account);
$access = $group->hasPermission('administer membership requests', $account);
$result = $access ? AccessResult::allowed() : AccessResult::forbidden(); $result = $access ? AccessResult::allowed() : AccessResult::forbidden();
return $return_as_object ? $result : $result->isAllowed(); return $return_as_object ? $result : $result->isAllowed();
} }
......
...@@ -12,7 +12,7 @@ use Drupal\group\Entity\GroupRelationshipInterface; ...@@ -12,7 +12,7 @@ use Drupal\group\Entity\GroupRelationshipInterface;
* @Action( * @Action(
* id = "grequest_reject", * id = "grequest_reject",
* label = @Translation("Reject membership request"), * label = @Translation("Reject membership request"),
* type = "group_content", * type = "group_relationship",
* confirm = TRUE, * confirm = TRUE,
* ) * )
*/ */
......
...@@ -70,6 +70,7 @@ class GroupMembershipRequest extends GroupRelationBase { ...@@ -70,6 +70,7 @@ class GroupMembershipRequest extends GroupRelationBase {
public function defaultConfiguration() { public function defaultConfiguration() {
$config = parent::defaultConfiguration(); $config = parent::defaultConfiguration();
$config['entity_cardinality'] = 1; $config['entity_cardinality'] = 1;
$config['remove_group_membership_request'] = FALSE;
return $config; return $config;
} }
...@@ -79,6 +80,12 @@ class GroupMembershipRequest extends GroupRelationBase { ...@@ -79,6 +80,12 @@ class GroupMembershipRequest extends GroupRelationBase {
public function buildConfigurationForm(array $form, FormStateInterface $form_state) { public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state); $form = parent::buildConfigurationForm($form, $form_state);
$form['remove_group_membership_request'] = [
'#type' => 'checkbox',
'#title' => $this->t('Remove a group membership request, when user join the group.'),
'#default_value' => $this->getConfiguration()['remove_group_membership_request'] ?? FALSE,
];
// Disable the entity cardinality field as the functionality of this module // Disable the entity cardinality field as the functionality of this module
// relies on a cardinality of 1. We don't just hide it, though, to keep a UI // relies on a cardinality of 1. We don't just hide it, though, to keep a UI
// that's consistent with other group relations. // that's consistent with other group relations.
......
...@@ -36,14 +36,17 @@ class GroupMembershipRequestOperationProvider implements OperationProviderInterf ...@@ -36,14 +36,17 @@ class GroupMembershipRequestOperationProvider implements OperationProviderInterf
*/ */
public function getGroupOperations(GroupInterface $group) { public function getGroupOperations(GroupInterface $group) {
$operations = $this->parent->getGroupOperations($group); $operations = $this->parent->getGroupOperations($group);
$entity_instances = $this->getRelationships($group);
$url = $group->toUrl('group-request-membership'); $url = $group->toUrl('group-request-membership');
if ($url->access($this->currentUser()) && count($entity_instances) == 0) { if ($url->access($this->currentUser())) {
$operations['group-request-membership'] = [ $entity_instances = $this->getRelationships($group);
'title' => $this->t('Request group membership'), if (count($entity_instances) == 0) {
'url' => $url, $operations['group-request-membership'] = [
'weight' => 99, 'title' => $this->t('Request group membership'),
]; 'url' => $url,
'weight' => 99,
];
}
} }
// @todo With the new VariationCache, we can use the above context. // @todo With the new VariationCache, we can use the above context.
...@@ -62,7 +65,7 @@ class GroupMembershipRequestOperationProvider implements OperationProviderInterf ...@@ -62,7 +65,7 @@ class GroupMembershipRequestOperationProvider implements OperationProviderInterf
* List of group relationships. * List of group relationships.
*/ */
protected function getRelationships(GroupInterface $group) { protected function getRelationships(GroupInterface $group) {
// @todo: replace with getRelationshipsByEntity. // We can use loadByEntityAndGroup, but for this we need load user entity.
// @see https://www.drupal.org/project/group/issues/3310605 // @see https://www.drupal.org/project/group/issues/3310605
$properties = [ $properties = [
'entity_id' => $this->currentUser()->id(), 'entity_id' => $this->currentUser()->id(),
......
...@@ -63,9 +63,13 @@ class GroupMembershipRequestPostInstall implements PostInstallInterface { ...@@ -63,9 +63,13 @@ class GroupMembershipRequestPostInstall implements PostInstallInterface {
$relationship_type_id = $relationship_type->id(); $relationship_type_id = $relationship_type->id();
$field_config_storage = $this->entityTypeManager->getStorage('field_config');
$field_storage_config_storage = $this->entityTypeManager->getStorage('field_storage_config');
$entity_view_display_storage = $this->entityTypeManager->getStorage('entity_view_display');
// Add Status field. // Add Status field.
FieldConfig::create([ $field_config_storage->create([
'field_storage' => FieldStorageConfig::loadByName('group_relationship', GroupMembershipRequest::STATUS_FIELD), 'field_storage' => $field_storage_config_storage->load('group_relationship.' . GroupMembershipRequest::STATUS_FIELD),
'bundle' => $relationship_type_id, 'bundle' => $relationship_type_id,
'label' => $this->t('Request status'), 'label' => $this->t('Request status'),
'required' => TRUE, 'required' => TRUE,
...@@ -77,8 +81,8 @@ class GroupMembershipRequestPostInstall implements PostInstallInterface { ...@@ -77,8 +81,8 @@ class GroupMembershipRequestPostInstall implements PostInstallInterface {
// Add "Updated by" field, to save reference to // Add "Updated by" field, to save reference to
// user who approved/denied request. // user who approved/denied request.
FieldConfig::create([ $field_config_storage->create([
'field_storage' => FieldStorageConfig::loadByName('group_relationship', 'grequest_updated_by'), 'field_storage' => $field_storage_config_storage->load('group_relationship.grequest_updated_by'),
'bundle' => $relationship_type_id, 'bundle' => $relationship_type_id,
'label' => $this->t('Approved/Rejected by'), 'label' => $this->t('Approved/Rejected by'),
'settings' => [ 'settings' => [
...@@ -90,8 +94,8 @@ class GroupMembershipRequestPostInstall implements PostInstallInterface { ...@@ -90,8 +94,8 @@ class GroupMembershipRequestPostInstall implements PostInstallInterface {
// Build the 'default' display ID for both the entity form and view mode. // Build the 'default' display ID for both the entity form and view mode.
$default_display_id = "group_relationship.$relationship_type_id.default"; $default_display_id = "group_relationship.$relationship_type_id.default";
// Build or retrieve the 'default' view mode. // Build or retrieve the 'default' view mode.
if (!$view_display = EntityViewDisplay::load($default_display_id)) { if (!$view_display = $entity_view_display_storage->load($default_display_id)) {
$view_display = EntityViewDisplay::create([ $view_display = $entity_view_display_storage->create([
'targetEntityType' => 'group_relationship', 'targetEntityType' => 'group_relationship',
'bundle' => $relationship_type_id, 'bundle' => $relationship_type_id,
'mode' => 'default', 'mode' => 'default',
......
...@@ -90,7 +90,7 @@ class GroupMembershipRequestFormTest extends GroupBrowserTestBase { ...@@ -90,7 +90,7 @@ class GroupMembershipRequestFormTest extends GroupBrowserTestBase {
'status' => TRUE, 'status' => TRUE,
])->setComponent('field_test_text', ['type' => 'text_textfield'])->enable()->save(); ])->setComponent('field_test_text', ['type' => 'text_textfield'])->enable()->save();
// Add permissions to the creator of the group. // Add permissions to members.
$this->createGroupRole([ $this->createGroupRole([
'group_type' => $group_type->id(), 'group_type' => $group_type->id(),
'scope' => PermissionScopeInterface::INSIDER_ID, 'scope' => PermissionScopeInterface::INSIDER_ID,
...@@ -125,6 +125,10 @@ class GroupMembershipRequestFormTest extends GroupBrowserTestBase { ...@@ -125,6 +125,10 @@ class GroupMembershipRequestFormTest extends GroupBrowserTestBase {
'scope' => PermissionScopeInterface::INDIVIDUAL_ID, 'scope' => PermissionScopeInterface::INDIVIDUAL_ID,
]); ]);
$manager_account = $this->createUser();
$this->group->addMember($manager_account);
$this->drupalLogin($manager_account);
$this->drupalGet("/group/{$this->group->id()}/content/{$group_membership_request->id()}/approve-membership"); $this->drupalGet("/group/{$this->group->id()}/content/{$group_membership_request->id()}/approve-membership");
$this->assertSession()->statusCodeEquals(200); $this->assertSession()->statusCodeEquals(200);
...@@ -154,6 +158,10 @@ class GroupMembershipRequestFormTest extends GroupBrowserTestBase { ...@@ -154,6 +158,10 @@ class GroupMembershipRequestFormTest extends GroupBrowserTestBase {
$group_membership_request = $this->membershipRequestManager->create($this->group, $account); $group_membership_request = $this->membershipRequestManager->create($this->group, $account);
$group_membership_request->save(); $group_membership_request->save();
$manager_account = $this->createUser();
$this->group->addMember($manager_account);
$this->drupalLogin($manager_account);
$this->drupalGet("/group/{$this->group->id()}/content/{$group_membership_request->id()}/reject-membership"); $this->drupalGet("/group/{$this->group->id()}/content/{$group_membership_request->id()}/reject-membership");
$this->assertSession()->statusCodeEquals(200); $this->assertSession()->statusCodeEquals(200);
......
...@@ -37,6 +37,13 @@ class GroupMembershipRequestTest extends GroupKernelTestBase { ...@@ -37,6 +37,13 @@ class GroupMembershipRequestTest extends GroupKernelTestBase {
*/ */
protected $groupRelationshipTypeStorage; protected $groupRelationshipTypeStorage;
/**
* The group relationship type for group membership request.
*
* @var \Drupal\group\Entity\GroupRelationshipTypeInterface
*/
protected $groupRelationshipType;
/** /**
* Modules to enable. * Modules to enable.
* *
...@@ -68,8 +75,14 @@ class GroupMembershipRequestTest extends GroupKernelTestBase { ...@@ -68,8 +75,14 @@ class GroupMembershipRequestTest extends GroupKernelTestBase {
$group_type = $this->createGroupType(); $group_type = $this->createGroupType();
$this->group = $this->createGroup(['type' => $group_type->id()]); $this->group = $this->createGroup(['type' => $group_type->id()]);
// Enable group membership request group relationship plugin. // Enable group membership request group relationship plugin.
$config = [
'group_cardinality' => 0,
'entity_cardinality' => 1,
'remove_group_membership_request' => FALSE,
];
$this->groupRelationshipTypeStorage = $this->entityTypeManager->getStorage('group_relationship_type'); $this->groupRelationshipTypeStorage = $this->entityTypeManager->getStorage('group_relationship_type');
$this->groupRelationshipTypeStorage->save($this->groupRelationshipTypeStorage->createFromPlugin($group_type, 'group_membership_request')); $this->groupRelationshipType = $this->groupRelationshipTypeStorage->createFromPlugin($group_type, 'group_membership_request', $config);
$this->groupRelationshipType->save();
} }
/** /**
...@@ -181,12 +194,13 @@ class GroupMembershipRequestTest extends GroupKernelTestBase { ...@@ -181,12 +194,13 @@ class GroupMembershipRequestTest extends GroupKernelTestBase {
} }
/** /**
* Test the user is accessible after request creation. * Test the request is removed when membership is removed.
*/ */
public function testUserAccessibility() { public function testMembershipRemovalWhenMembershipRemoved() {
$account = $this->createUser(); $account = $this->createUser();
$this->group->addMember($account); $this->group->addMember($account);
$this->group->removeMember($account); $this->group->removeMember($account);
$membership_request = $this->membershipRequestManager->getMembershipRequest($account, $this->group); $membership_request = $this->membershipRequestManager->getMembershipRequest($account, $this->group);
...@@ -210,21 +224,45 @@ class GroupMembershipRequestTest extends GroupKernelTestBase { ...@@ -210,21 +224,45 @@ class GroupMembershipRequestTest extends GroupKernelTestBase {
} }
/** /**
* Test deletion of group membership request, when we insert group membership. * Test group membership removal with disabled settings.
*/ */
public function testGroupMembershipInsertion() { public function testRequestRemovalWithDisabledSettings() {
$account = $this->createUser(); $account = $this->createUser();
// We want to be sure that status is still pending. // Add first group membership request.
$group_membership_request = $this->createRequestMembership($account); $group_membership_request = $this->membershipRequestManager->create($this->group, $account);
$this->assertEquals($group_membership_request->get(GroupMembershipRequest::STATUS_FIELD)->value, GroupMembershipRequest::REQUEST_PENDING); $group_membership_request->save();
// if the group membership request is still pending, and we added // Add the user as member.
// a member using other means.
$this->group->addMember($account); $this->group->addMember($account);
$membership_request = $this->membershipRequestManager->getMembershipRequest($account, $this->group); // Since removal is disabled we should see find group membership request.
$this->assertNull($membership_request); $group_membership_request = $this->membershipRequestManager->getMembershipRequest($account, $this->group);
$this->assertNotNull($group_membership_request);
}
/**
* Test group membership removal with enabled settings.
*/
public function testRequestRemovalWithEnabledSettings() {
$config = [
'group_cardinality' => 0,
'entity_cardinality' => 1,
'remove_group_membership_request' => TRUE,
];
$this->groupRelationshipType->updatePlugin($config);
$account = $this->createUser();
// Add first group membership request.
$group_membership_request = $this->membershipRequestManager->create($this->group, $account);
$group_membership_request->save();
// Add the user as member.
$this->group->addMember($account);
// Since removal is disabled we should see find group membership request.
$group_membership_request = $this->membershipRequestManager->getMembershipRequest($account, $this->group);
$this->assertNull($group_membership_request);
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment