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

Issue #3484972: Add seperate group permissions for invitePerson and inviteBulk

parent bd1ce04b
Branches 4.0.x
Tags 4.0.0
1 merge request!47Issue #3484972: Add permission to handle bulk invitation, add administer user...
......@@ -5,9 +5,130 @@
* Install, update and uninstall functions for the ginvite module.
*/
use Drupal\group\Entity\GroupRole;
/**
* Implements hook_update_last_removed().
*/
function ginvite_update_last_removed() {
return 8003;
}
/**
* Provide bulk permissions if a user already has invite permissions.
*/
function ginvite_update_8004(&$sandbox) {
// Set up the batch by retrieving all of the group role IDs.
if (!isset($sandbox['progress'])) {
$sandbox['ids'] = \Drupal::service('entity_type.manager')
->getStorage('group_role')
->getQuery()
->accessCheck(FALSE)
->execute();
$sandbox['max'] = count($sandbox['ids']);
$sandbox['progress'] = 0;
}
$invite_permission = 'invite users to group';
$bulk_invite_permission = 'bulk invite users to group';
$administer_user_permission = 'administer members';
$administer_group_invitation_permission = 'administer group invitations';
// Try to update 25 group role entities at a time.
$ids = array_slice($sandbox['ids'], $sandbox['progress'], 25);
/** @var \Drupal\group\Entity\GroupRoleInterface $group_role */
foreach (GroupRole::loadMultiple($ids) as $group_role) {
$has_change = FALSE;
// Set bulk permission, if invite permission is found.
$permissions = $group_role->getPermissions();
if (in_array($invite_permission, $permissions)) {
$has_change = TRUE;
$group_role->grantPermission($bulk_invite_permission);
}
if (in_array($administer_user_permission, $permissions)) {
$has_change = TRUE;
$group_role->grantPermission($administer_group_invitation_permission);
}
if ($has_change) {
$group_role->save();
}
$sandbox['progress']++;
}
// Try to update the percentage but avoid division by zero.
$sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
// Show a status update for the current progress.
return t('Updated the permissions for @progress out of @max group role entities.', [
'@progress' => $sandbox['progress'],
'@max' => $sandbox['max'],
]);
}
/**
* Provide bulk permissions if a user already has invite permissions in group
* permissions.
*/
function ginvite_update_8005(&$sandbox) {
if (!\Drupal::moduleHandler()->moduleExists('group_permissions')) {
return;
}
$storage = \Drupal::service('entity_type.manager')->getStorage('group_permission');
// Set up the batch by retrieving all of the group permission IDs.
if (!isset($sandbox['progress'])) {
$sandbox['ids'] = $storage
->getQuery()
->accessCheck(FALSE)
->execute();
$sandbox['max'] = count($sandbox['ids']);
$sandbox['progress'] = 0;
}
$invite_permission = 'invite users to group';
$administer_user_permission = 'administer members';
$administer_group_invitation_permission = 'administer group invitations';
$bulk_invite_permission = 'bulk invite users to group';
// Try to update 25 group role entities at a time.
$ids = array_slice($sandbox['ids'], $sandbox['progress'], 25);
/** @var \Drupal\group_permissions\Entity\GroupPermission $group_permission */
foreach ($storage->loadMultiple($ids) as $group_permission) {
// Set bulk permission, if invite permission is found.
$custom_permissions = $group_permission->getPermissions();
$has_change = FALSE;
foreach ($custom_permissions as $role_id => $permissions) {
if (in_array($invite_permission, $permissions)) {
$has_change = TRUE;
$custom_permissions[$role_id][] = $bulk_invite_permission;
}
if (in_array($administer_user_permission, $permissions)) {
$has_change = TRUE;
$custom_permissions[$role_id][] = $administer_group_invitation_permission;
}
}
if ($has_change) {
$group_permission->setPermissions($custom_permissions);
$violations = $group_permission->validate();
if (count($violations) == 0) {
$group_permission->save();
}
}
$sandbox['progress']++;
}
// Try to update the percentage but avoid division by zero.
$sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
// Show a status update for the current progress.
return t('Updated the permissions for @progress out of @max group permission entities.', [
'@progress' => $sandbox['progress'],
'@max' => $sandbox['max'],
]);
}
......@@ -28,7 +28,7 @@ ginvite.invitation.bulk:
_form: '\Drupal\ginvite\Form\BulkGroupInvitation'
_title_callback: '\Drupal\ginvite\Controller\InvitationOperations::invitationTitle'
requirements:
_group_permission: 'invite users to group'
_group_permission: 'bulk invite users to group+administer members+administer group invitations'
_group_installed_content: 'group_invitation'
options:
parameters:
......@@ -41,7 +41,7 @@ ginvite.invitation.bulk.confirm:
_form: '\Drupal\ginvite\Form\BulkGroupInvitationConfirm'
_title_callback: '\Drupal\ginvite\Controller\InvitationOperations::invitationTitle'
requirements:
_group_permission: 'invite users to group'
_group_permission: 'bulk invite users to group+administer members+administer group invitations'
_group_installed_content: 'group_invitation'
options:
parameters:
......
......@@ -15,7 +15,7 @@ services:
ginvite.invitation_handler:
class: 'Drupal\ginvite\GroupInvitationHandler'
arguments: [ '@plugin.manager.mail', '@messenger' ]
arguments: [ '@plugin.manager.mail', '@messenger', '@language_manager' ]
# Specific group relation handlers.
group.relation_handler.operation_provider.group_invitation:
......
......@@ -142,10 +142,12 @@ class GinviteSubscriber implements EventSubscriberInterface {
}
$invited_user = $invitation->getUser();
$invited_user->activate();
$invited_user->save();
$this->messenger->addMessage($this->t('User %user unblocked as it comes from an invitation', ['%user' => $invited_user->getDisplayName()]));
$this->loggerFactory->get('ginvite')->notice($this->t('User %user unblocked as it comes from an invitation', ['%user' => $invited_user->getDisplayName()]));
if (!$invited_user->isActive()) {
$invited_user->activate();
$invited_user->save();
$this->messenger->addMessage($this->t('User %user unblocked as it comes from an invitation', ['%user' => $invited_user->getDisplayName()]));
$this->loggerFactory->get('ginvite')->notice($this->t('User %user unblocked as it comes from an invitation', ['%user' => $invited_user->getDisplayName()]));
}
}
/**
......
......@@ -2,6 +2,7 @@
namespace Drupal\ginvite;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Mail\MailManagerInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
......@@ -25,10 +26,13 @@ class GroupInvitationHandler implements GroupInvitationHandlerInterface {
* Mail manager service.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* Messenger service.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
*/
public function __construct(
protected MailManagerInterface $mailManager,
protected MessengerInterface $messenger
protected MessengerInterface $messenger,
protected LanguageManagerInterface $languageManager
) {
}
......@@ -90,12 +94,16 @@ class GroupInvitationHandler implements GroupInvitationHandlerInterface {
$group = $group_relationship->getGroup();
$from = $group_relationship->getEntity();
$langcode = $from->getPreferredLangcode();
if ($existing_user && !empty($group_relationship->getEntity())) {
$langcode = $group_relationship->getEntity()->getPreferredLangcode();
}
else {
$langcode = $this->languageManager->getDefaultLanguage()->getId();
}
$params = [
'user' => $from,
'group' => $group,
'group_content' => $group_relationship,
'group_relationship' => $group_relationship,
'existing_user' => $existing_user,
];
......
......@@ -51,13 +51,13 @@ class GroupInvitationAccessControl implements AccessControlInterface {
return $return_as_object ? AccessResult::neutral() : FALSE;
}
$invite_permission = 'invite users to group';
$has_invite_permission = $group_relationship->getGroup()->hasPermission($invite_permission, $account);
if (GroupAccessResult::allowedIfHasGroupPermissions($group_relationship->getGroup(), $account, $this->getInvitePermissions(), 'OR')->isAllowed()) {
return $return_as_object ? AccessResult::allowed() : FALSE;
}
if (!$has_invite_permission) {
if ($operation == 'view') {
// Check if the account is the owner.
$is_owner = $group_relationship->getEntityId() === $account->id();
if (!$is_owner && $operation == 'view') {
if ($group_relationship->getEntityId() !== $account->id()) {
return $return_as_object ? AccessResult::neutral() : FALSE;
}
}
......@@ -65,6 +65,20 @@ class GroupInvitationAccessControl implements AccessControlInterface {
return $this->parent->relationshipAccess($group_relationship, $operation, $account, $return_as_object);
}
/**
* Get invite permissions.
*
* @return string[]
* Permissions.
*/
protected function getInvitePermissions() {
return [
'invite users to group',
'administer members',
'administer group invitations',
];
}
/**
* Checks operation support across the entire decorator chain.
*
......
......@@ -53,6 +53,10 @@ class GroupInvitationPermissionProvider implements PermissionProviderInterface {
'title' => 'Invite users to group',
'description' => 'Allows users with permissions to invite new users to group.',
];
$permissions['bulk invite users to group'] = [
'title' => 'Invite users to group in bulk',
'description' => 'Allows users with permissions to invite new users to group using bulk form.',
];
$permissions['view group invitations'] = [
'title' => 'View group invitations',
'description' => 'Allows users with permissions view created invitations.',
......
......@@ -7,6 +7,7 @@ use Drupal\ginvite\Plugin\Group\Relation\GroupInvitation;
use Drupal\group\Entity\GroupRelationshipInterface;
use Drupal\group\PermissionScopeInterface;
use Drupal\Tests\group\Functional\GroupBrowserTestBase;
use Drupal\user\Entity\User;
use Drupal\user\RoleInterface;
/**
......@@ -23,16 +24,14 @@ class GroupInviteTest extends GroupBrowserTestBase {
*
* @var \Drupal\ginvite\GroupInvitationManager
*/
protected $invitationManager;
protected $groupInvitationManager;
/**
* {@inheritdoc}
* The group relationship type for group membership request.
*
* @var \Drupal\group\Entity\GroupRelationshipTypeInterface
*/
protected static $modules = [
'group',
'group_test_config',
'ginvite',
];
protected $groupRelationshipType;
/**
* The group we will use to test methods on.
......@@ -69,6 +68,15 @@ class GroupInviteTest extends GroupBrowserTestBase {
*/
protected $groupMemberRole;
/**
* {@inheritdoc}
*/
protected static $modules = [
'group',
'group_test_config',
'ginvite',
];
/**
* Gets the global (site) permissions for the group creator.
*
......@@ -98,7 +106,7 @@ class GroupInviteTest extends GroupBrowserTestBase {
'type' => 'default',
]);
$this->invitationManager = $this->container->get('ginvite.group_invitation_manager');
$this->groupInvitationManager = $this->container->get('ginvite.group_invitation_manager');
$this->account = $this->drupalCreateUser();
$this->group->addMember($this->account);
......@@ -128,6 +136,8 @@ class GroupInviteTest extends GroupBrowserTestBase {
$this->submitForm(['invitation_bypass_form' => 1], 'Save configuration');
$this->assertSession()->statusCodeEquals(200);
$this->groupRelationshipType = $this->entityTypeManager->getStorage('group_relationship_type')->load('default-group_invitation');
// Add permissions to invite users to members of the group.
$this->groupOutsiderRole = $this->createGroupRole([
'group_type' => $this->group->getGroupType()->id(),
......@@ -200,7 +210,7 @@ class GroupInviteTest extends GroupBrowserTestBase {
$this->drupalLogin($account);
$group_invitation = $this->invitationManager->createInvitation($this->group, $account->getEmail(), $account->id(), [$this->groupIndividualRole->id()]);
$group_invitation = $this->groupInvitationManager->createInvitation($this->group, $account->getEmail(), $account->id(), [$this->groupIndividualRole->id()]);
$group_invitation->save();
// Install and configure the Group Invitation plugin.
......@@ -218,7 +228,7 @@ class GroupInviteTest extends GroupBrowserTestBase {
$account = $this->drupalCreateUser();
$this->drupalLogin($account);
$group_invitation = $this->invitationManager->createInvitation($this->group, $account->getEmail(), $account->id());
$group_invitation = $this->groupInvitationManager->createInvitation($this->group, $account->getEmail(), $account->id());
$group_invitation->save();
$this->drupalGet("/ginvite/{$group_invitation->id()}/accept");
......@@ -228,7 +238,7 @@ class GroupInviteTest extends GroupBrowserTestBase {
$account = $this->drupalCreateUser();
$this->drupalLogin($account);
$group_invitation = $this->invitationManager->createInvitation($this->group, $account->getEmail(), $account->id());
$group_invitation = $this->groupInvitationManager->createInvitation($this->group, $account->getEmail(), $account->id());
$group_invitation->save();
$this->drupalGet("/ginvite/{$group_invitation->id()}/decline");
......@@ -242,7 +252,7 @@ class GroupInviteTest extends GroupBrowserTestBase {
$account = $this->drupalCreateUser();
$group_invitation = $this->invitationManager->createInvitation($this->group, $account->getEmail(), $account->id());
$group_invitation = $this->groupInvitationManager->createInvitation($this->group, $account->getEmail(), $account->id());
$group_invitation->save();
$not_owner_user = $this->drupalCreateUser();
......@@ -257,7 +267,7 @@ class GroupInviteTest extends GroupBrowserTestBase {
*/
public function testAccessNotPendingInvitation() {
$group_invitation = $this->invitationManager->createInvitation($this->group, $this->account->getEmail(), $this->account->id());
$group_invitation = $this->groupInvitationManager->createInvitation($this->group, $this->account->getEmail(), $this->account->id());
$group_invitation->save();
$account = $this->drupalCreateUser();
......@@ -267,7 +277,7 @@ class GroupInviteTest extends GroupBrowserTestBase {
// As not owner of invitation I can't accept or decline it.
$this->checkRoutesAccess($group_invitation, 403);
$group_invitation = $this->invitationManager->createInvitation($this->group, $this->account->getEmail(), $this->account->id());
$group_invitation = $this->groupInvitationManager->createInvitation($this->group, $this->account->getEmail(), $this->account->id());
$group_invitation->save();
$group_invitation->set('invitation_status', GroupInvitation::INVITATION_ACCEPTED)->save();
......@@ -286,17 +296,71 @@ class GroupInviteTest extends GroupBrowserTestBase {
$this->groupOutsiderRole->save();
$this->drupalGet("/group/{$this->group->id()}/invite-members");
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->statusCodeEquals(403);
$this->drupalGet("/group/{$this->group->id()}/invite-members/confirm");
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->statusCodeEquals(403);
$this->groupOutsiderRole->revokePermission('invite users to group');
$this->groupOutsiderRole->grantPermission('bulk invite users to group');
$this->groupOutsiderRole->save();
$this->drupalGet("/group/{$this->group->id()}/invite-members");
$this->assertSession()->statusCodeEquals(403);
$this->assertSession()->statusCodeEquals(200);
$this->drupalGet("/group/{$this->group->id()}/invite-members/confirm");
$this->assertSession()->statusCodeEquals(403);
$this->assertSession()->statusCodeEquals(200);
}
/**
* Unblock user during registration, if unblock_invitees option enabled.
*
* @param int $unblock_invitees
* Option unblock_invitees
* @param bool $user_status
* Current user status.
* @param $expected_user_status
* Expected user status.
*
* @dataProvider groupInvitationUserRegistrationData
*/
public function testUnlbockRegisteredUser($unblock_invitees, $user_status, $expected_user_status) {
$email = $this->randomMachineName() . '@domain.com';
// Enable unblock_invitees option.
$this->groupRelationshipType->updatePlugin(['unblock_invitees' => $unblock_invitees]);
$group_invitation = $this->groupInvitationManager->createInvitation($this->group, $email);
$group_invitation->save();
$account = $this->drupalCreateUser([], NULL, FALSE, [
'mail' => $email,
'status' => $user_status,
]);
// Reload account.
$user = User::load($account->id());
if ($expected_user_status) {
$this->assertTrue($user->isActive());
}
else {
$this->assertFalse($user->isActive());
}
}
/**
* Data provider for testUnlbockRegisteredUser().
*
* @return array
* Data to check unlock functionality.
*/
public function groupInvitationUserRegistrationData() {
return [
// Each array contains [email, unblock_invitees option, user status, expected user status].
[1, FALSE, TRUE],
[1, TRUE, TRUE],
[0, FALSE, FALSE],
[0, TRUE, TRUE],
];
}
}
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