Skip to content

Commit 6461e4f

Browse files
[#438] Team invitation (#474)
* wip for team invitation * [#438] Implement events * Implement team_invitation event subscriber * Add email notification for team invitation * Update config * Redirect users after declining a team invitation * Validate existing members and pending invitations when inviting members * Remove the cancel status * Add permissions to delete team invitation * Add install and update hooks * Add team_invitation_administer permission to team admin * Fix update hooks * Fix update hooks * Install view config * Use administer team invitation instead of delete * Update form redirect route for accept and decline * Add created date to team_invitation * Implement team notifier service * Implement team_invitation expiry * Fix source for view config * Handle expired invitation * Fix status messages for forms * Fix schema for team_invitation_expiry_days * Fix phpcs warnings * Update config * Do not deprecate userStorage * Update cancel text for accept and decline form * Update the team name in the template * Replace delete with revoke * Fix phpcs deprecation warning * Update success message for UI test * Replace team_administer_invitation with team_manage_members * Add owner to team invitation * Update config * Use configurable team label * Update team invitation view * Add cache per user to access control handler * Remove add members form test from UiTest Co-authored-by: Arlina Espinoza <[email protected]>
1 parent bf99f73 commit 6461e4f

File tree

45 files changed

+4242
-106
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+4242
-106
lines changed

modules/apigee_edge_teams/apigee_edge_teams.info.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ type: module
66
core_version_requirement: ^8 || ^9
77

88
dependencies:
9-
- apigee_edge:apigee_edge
9+
- apigee_edge:apigee_edge
10+
- drupal:views
1011

1112
configure: apigee_edge_teams.settings.team
1213

modules/apigee_edge_teams/apigee_edge_teams.install

+63
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
*/
2020

2121
use Apigee\Edge\Utility\OrganizationFeatures;
22+
use Drupal\Core\Config\FileStorage;
23+
use Drupal\user\RoleInterface;
2224

2325
/**
2426
* @file
@@ -60,6 +62,20 @@ function apigee_edge_teams_requirements($phase) {
6062
return $requirements;
6163
}
6264

65+
/**
66+
* Implements hook_install().
67+
*/
68+
function apigee_edge_teams_install() {
69+
if (\Drupal::moduleHandler()->moduleExists('user')) {
70+
$authenticated_user_permissions = [
71+
'accept own team invitation',
72+
'decline own team invitation',
73+
];
74+
user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, $authenticated_user_permissions);
75+
}
76+
}
77+
78+
6379
/**
6480
* Assign "add_api_key", "revoke_api_key" and "edit_api_products" permissions to team administrators.
6581
*/
@@ -76,3 +92,50 @@ function apigee_edge_teams_update_8701() {
7692
$admin_role = $storage->load($role);
7793
$storage->changePermissions($role, $admin_role->getPermissions() + array_combine($api_key_permissions, $api_key_permissions));
7894
}
95+
96+
/**
97+
* Install team_invitation entity, dependencies and config.
98+
*/
99+
function apigee_edge_teams_update_8702() {
100+
// Install 'team_invitation' entity.
101+
\Drupal::entityTypeManager()->clearCachedDefinitions();
102+
$entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
103+
$entity_definition_update_manager->installEntityType(\Drupal::entityTypeManager()->getDefinition('team_invitation'));
104+
105+
// Install 'views' module.
106+
\Drupal::service('module_installer')->install(['views']);
107+
108+
// Install new config.
109+
$settings_to_sync = [
110+
'team_invitation_expiry_days',
111+
'team_invitation_email_existing',
112+
'team_invitation_email_new',
113+
];
114+
/** @var \Drupal\Core\Config\StorageInterface $config_storage */
115+
$config_storage = \Drupal::service('config.storage');
116+
$module_path = drupal_get_path('module', 'apigee_edge_teams');
117+
$source = new FileStorage("$module_path/config/install");
118+
$new_team_settings = $source->read('apigee_edge_teams.team_settings');
119+
$team_settings = $config_storage->read('apigee_edge_teams.team_settings');
120+
foreach ($settings_to_sync as $setting) {
121+
$team_settings[$setting] = $new_team_settings[$setting];
122+
}
123+
$config_storage->write('apigee_edge_teams.team_settings', $team_settings);
124+
125+
$optional = new FileStorage("$module_path/config/optional");
126+
$team_invitations_view = $optional->read('views.view.team_invitations');
127+
$config_storage->write('views.view.team_invitations', $team_invitations_view);
128+
}
129+
130+
/**
131+
* Assign accept and decline team invitations permissions to authenticated users.
132+
*/
133+
function apigee_edge_teams_update_8703() {
134+
if (\Drupal::moduleHandler()->moduleExists('user')) {
135+
$authenticated_user_permissions = [
136+
'accept own team invitation',
137+
'decline own team invitation',
138+
];
139+
user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, $authenticated_user_permissions);
140+
}
141+
}

modules/apigee_edge_teams/apigee_edge_teams.links.action.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
apigee_edge_teams.team.add_members:
22
route_name: entity.team.add_members
3-
title: 'Add members'
3+
title: 'Invite members'
44
appears_on:
55
- 'entity.team.members'
66

modules/apigee_edge_teams/apigee_edge_teams.links.task.yml

+5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ apigee_edge_teams.settings.team.cache:
2525
title: 'Caching'
2626
base_route: apigee_edge_teams.settings.team
2727

28+
apigee_edge_teams.settings.team.team_invitation:
29+
route_name: apigee_edge_teams.settings.team.team_invitation
30+
title: 'Team invitation'
31+
base_route: apigee_edge_teams.settings.team
32+
2833
apigee_edge_teams.settings.team_app.alias:
2934
route_name: apigee_edge_teams.settings.team_app
3035
title: 'Alias'

modules/apigee_edge_teams/apigee_edge_teams.module

+35
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
*/
2121

2222
use Drupal\apigee_edge\Exception\DeveloperDoesNotExistException;
23+
use Drupal\apigee_edge_teams\Entity\TeamInvitationInterface;
2324
use Drupal\apigee_edge_teams\Entity\TeamRoleInterface;
25+
use Drupal\Component\Render\PlainTextOutput;
2426
use Drupal\Core\Access\AccessResult;
2527
use Drupal\Core\Breadcrumb\Breadcrumb;
2628
use Drupal\Core\Entity\EntityInterface;
@@ -241,3 +243,36 @@ function apigee_edge_teams_help($route_name, RouteMatchInterface $route_match) {
241243
]) . '</p>';
242244
}
243245
}
246+
247+
/**
248+
* Implements hook_mail().
249+
*/
250+
function apigee_edge_teams_mail($key, &$message, $params) {
251+
$token_service = \Drupal::token();
252+
$config = \Drupal::config('apigee_edge_teams.team_settings');
253+
254+
switch ($key) {
255+
case 'team_invitation_created':
256+
$template = empty($params['user']) ? 'new' : 'existing';
257+
$message['subject'] = PlainTextOutput::renderFromHtml($token_service->replace($config->get("team_invitation_email_$template.subject"), $params));
258+
$message['body'][] = $token_service->replace($config->get("team_invitation_email_$template.body"), $params);
259+
break;
260+
}
261+
}
262+
263+
/**
264+
* Implements hook_cron().
265+
*/
266+
function apigee_edge_teams_cron() {
267+
/** @var \Drupal\apigee_edge_teams\Entity\Storage\TeamInvitationStorageInterface $storage */
268+
$storage = Drupal::entityTypeManager()->getStorage('team_invitation');
269+
$team_invitations = $storage->getInvitationsToExpire();
270+
if (!count($team_invitations)) {
271+
return;
272+
}
273+
274+
// Update status for expired invitations.
275+
foreach ($team_invitations as $team_invitation) {
276+
$team_invitation->setStatus(TeamInvitationInterface::STATUS_EXPIRED)->save();
277+
}
278+
}

modules/apigee_edge_teams/apigee_edge_teams.routing.yml

+8
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ apigee_edge_teams.settings.team.permissions:
3131
requirements:
3232
_permission: 'administer team'
3333

34+
apigee_edge_teams.settings.team.team_invitation:
35+
path: '/admin/config/apigee-edge/team-settings/invitation'
36+
defaults:
37+
_form: '\Drupal\apigee_edge_teams\Form\TeamInvitationForm'
38+
_title: 'Team invitation'
39+
requirements:
40+
_permission: 'administer team_invitation'
41+
3442
apigee_edge_teams.settings.team_app:
3543
path: '/admin/config/apigee-edge/app-settings/team-apps'
3644
defaults:

modules/apigee_edge_teams/apigee_edge_teams.services.yml

+10
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,16 @@ services:
8383
tags:
8484
- { name: event_subscriber }
8585

86+
apigee_edge_teams.event_subscriber.team_invitation_event_subscriber:
87+
class: Drupal\apigee_edge_teams\EventSubscriber\TeamInvitationSubscriber
88+
arguments: ['@logger.channel.apigee_edge_teams', '@entity_type.manager', '@apigee_edge_teams.team_membership_manager', '@apigee_edge_teams.team_invitation_notifier.email']
89+
tags:
90+
- { name: event_subscriber }
91+
92+
apigee_edge_teams.team_invitation_notifier.email:
93+
class: Drupal\apigee_edge_teams\TeamInvitationNotifierEmail
94+
arguments: ['@plugin.manager.mail', '@language_manager']
95+
8696
route.subscriber.apigee_edge_teams.team_app_by_name:
8797
class: Drupal\apigee_edge_teams\Routing\TeamAppByNameRouteAlterSubscriber
8898
tags:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
3+
/**
4+
* @file
5+
* Implements tokens for apigee_edge_teams module.
6+
*/
7+
8+
/**
9+
* Copyright 2020 Google Inc.
10+
*
11+
* This program is free software; you can redistribute it and/or
12+
* modify it under the terms of the GNU General Public License
13+
* version 2 as published by the Free Software Foundation.
14+
*
15+
* This program is distributed in the hope that it will be useful,
16+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
* GNU General Public License for more details.
19+
*
20+
* You should have received a copy of the GNU General Public License
21+
* along with this program; if not, write to the Free Software
22+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23+
* MA 02110-1301, USA.
24+
*/
25+
26+
use Drupal\Core\Render\BubbleableMetadata;
27+
use Drupal\Core\Url;
28+
29+
/**
30+
* Implements hook_token_info_alter().
31+
*/
32+
function apigee_edge_teams_token_info_alter(&$info) {
33+
if (!empty($info['tokens']['team_invitation'])) {
34+
$info['tokens']['team_invitation']['team_name'] = [
35+
'name' => t('Team name'),
36+
'description' => t('The name of the team.'),
37+
'type' => 'team_invitation',
38+
];
39+
$info['tokens']['team_invitation']['url_accept'] = [
40+
'name' => t('Accept URL'),
41+
'description' => t('The invitation accept url.'),
42+
'type' => 'team_invitation',
43+
];
44+
$info['tokens']['team_invitation']['url_decline'] = [
45+
'name' => t('Decline URL'),
46+
'description' => t('The invitation decline url.'),
47+
'type' => 'team_invitation',
48+
];
49+
$info['tokens']['team_invitation']['url_register'] = [
50+
'name' => t('Registration URL'),
51+
'description' => t('A url to register an account with a redirect to accept the team invitation.'),
52+
'type' => 'team_invitation',
53+
];
54+
$info['tokens']['team_invitation']['expiry_days'] = [
55+
'name' => t('Expiry days'),
56+
'description' => t('The expiry days for the team invitation.'),
57+
'type' => 'team_invitation',
58+
];
59+
}
60+
}
61+
62+
/**
63+
* Implements hook_tokens().
64+
*/
65+
function apigee_edge_teams_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
66+
if (!isset($data['team_invitation'])) {
67+
return [];
68+
}
69+
70+
/** @var \Drupal\apigee_edge_teams\Entity\TeamInvitationInterface $team_invitation */
71+
$team_invitation = $data['team_invitation'];
72+
$team = $team_invitation->getTeam();
73+
$replacements = [];
74+
75+
foreach ($tokens as $field_name => $original) {
76+
switch ($field_name) {
77+
case "team_name":
78+
$replacements[$original] = $team->label();
79+
break;
80+
81+
case "url_accept":
82+
$replacements[$original] = $team_invitation->toUrl('accept-form', ['absolute' => TRUE])->setRouteParameter('team', $team->id())->toString();
83+
break;
84+
85+
case "url_decline":
86+
$replacements[$original] = $team_invitation->toUrl('decline-form', ['absolute' => TRUE])->setRouteParameter('team', $team->id())->toString();
87+
break;
88+
89+
case "url_register":
90+
$query = ['destination' => $team_invitation->toUrl('accept-form')->setRouteParameter('team', $team->id())->toString()];
91+
$replacements[$original] = Url::fromRoute('user.register', [], ['query' => $query, 'absolute' => TRUE])->toString();
92+
break;
93+
94+
case "expiry_days":
95+
$replacements[$original] = Drupal::config('apigee_edge_teams.team_settings')->get('team_invitation_expiry_days');
96+
break;
97+
}
98+
}
99+
100+
return $replacements;
101+
}

modules/apigee_edge_teams/config/install/apigee_edge_teams.team_settings.yml

+7
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,10 @@ cache_expiration: 900
55
non_member_team_apps_visible_api_products:
66
- public
77
- private
8+
team_invitation_expiry_days: 7
9+
team_invitation_email_existing:
10+
subject: '[[site:name]] You have been invited to join the [team_invitation:team_name] team'
11+
body: "Hi [user:display-name],\r\n\r\nYou have been invited to join the [team_invitation:team_name] team on [site:name] by [team_invitation:uid:entity:display-name].\r\n\r\nUse the following links to accept or decline the invitation:\r\n\r\n- Accept invitation: [team_invitation:url_accept]\r\n- Decline invitation: [team_invitation:url_decline]\r\n\r\nThis invitation will expire in [team_invitation:expiry_days] days.\r\n\r\n-- [site:name]"
12+
team_invitation_email_new:
13+
subject: '[[site:name]] You have been invited to join the [team_invitation:team_name] team'
14+
body: "Hi,\r\n\r\nYou have been invited to join the [team_invitation:team_name] team on [site:name] by [team_invitation:uid:entity:display-name].\r\n\r\nUse the link below to create an account and join the [team_invitation:team_name] team.\r\n\r\n[team_invitation:url_register]\r\n\r\nThis invitation will expire in [team_invitation:expiry_days] days.\r\n\r\n-- [site:name]"

0 commit comments

Comments
 (0)