diff --git a/.secrets.baseline b/.secrets.baseline index 22f467a5..bc5bea51 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "^.secrets.baseline$", "lines": null }, - "generated_at": "2019-10-02T14:53:58Z", + "generated_at": "2019-10-02T23:24:50Z", "plugins_used": [ { "base64_limit": 4.5, @@ -194,7 +194,7 @@ "hashed_secret": "e4f14805dfd1e6af030359090c535e149e6b4207", "is_secret": false, "is_verified": false, - "line_number": 638, + "line_number": 651, "type": "Hex High Entropy String" } ] diff --git a/atst/domain/invitations.py b/atst/domain/invitations.py index 6b68db92..fb889aec 100644 --- a/atst/domain/invitations.py +++ b/atst/domain/invitations.py @@ -117,22 +117,21 @@ class BaseInvitations(object): return cls._update_status(invite, InvitationStatus.REVOKED) @classmethod - def resend(cls, inviter, token): + def resend(cls, inviter, token, user_info=None): previous_invitation = cls._get(token) cls._update_status(previous_invitation, InvitationStatus.REVOKED) - return cls.create( - inviter, - previous_invitation.role, - { + if not user_info: + user_info = { "email": previous_invitation.email, "dod_id": previous_invitation.dod_id, "first_name": previous_invitation.first_name, "last_name": previous_invitation.last_name, - "phone_number": previous_invitation.last_name, - }, - commit=True, - ) + "phone_number": previous_invitation.phone_number, + "phone_ext": previous_invitation.phone_ext, + } + + return cls.create(inviter, previous_invitation.role, user_info, commit=True) class PortfolioInvitations(BaseInvitations): diff --git a/atst/routes/applications/settings.py b/atst/routes/applications/settings.py index 29efb1de..18b92603 100644 --- a/atst/routes/applications/settings.py +++ b/atst/routes/applications/settings.py @@ -12,6 +12,7 @@ from atst.domain.invitations import ApplicationInvitations from atst.forms.application_member import NewForm as NewMemberForm, UpdateMemberForm from atst.forms.application import NameAndDescriptionForm, EditEnvironmentForm from atst.forms.data import ENV_ROLE_NO_ACCESS as NO_ACCESS +from atst.forms.member import NewForm as MemberForm from atst.domain.authz.decorator import user_can_access_decorator as user_can from atst.models.permissions import Permissions from atst.domain.permission_sets import PermissionSets @@ -95,6 +96,13 @@ def get_members_data(application): form = UpdateMemberForm( environment_roles=env_roles_form_data, **permission_sets ) + update_invite_form = None + + if member.latest_invitation and member.latest_invitation.can_resend: + update_invite_form = MemberForm(obj=member.latest_invitation) + else: + update_invite_form = MemberForm() + members_data.append( { "role_id": member.id, @@ -103,6 +111,7 @@ def get_members_data(application): "environment_roles": environment_roles, "role_status": member.status.value, "form": form, + "update_invite_form": update_invite_form, } ) @@ -419,3 +428,46 @@ def revoke_invite(application_id, application_role_id): _anchor="application-members", ) ) + + +@applications_bp.route( + "/applications//members//resend_invite", + methods=["POST"], +) +@user_can(Permissions.EDIT_APPLICATION_MEMBER, message="resend application invitation") +def resend_invite(application_id, application_role_id): + app_role = ApplicationRoles.get_by_id(application_role_id) + invite = app_role.latest_invitation + form = MemberForm(http_request.form) + + if form.validate(): + new_invite = ApplicationInvitations.resend( + g.current_user, invite.token, form.data + ) + + send_application_invitation( + invitee_email=new_invite.email, + inviter_name=g.current_user.full_name, + token=new_invite.token, + ) + + flash( + "application_invite_resent", + user_name=new_invite.user_name, + application_name=app_role.application.name, + ) + else: + flash( + "application_invite_error", + user_name=app_role.user_name, + application_name=g.application.name, + ) + + return redirect( + url_for( + "applications.settings", + application_id=application_id, + fragment="application-members", + _anchor="application-members", + ) + ) diff --git a/atst/utils/flash.py b/atst/utils/flash.py index 613b55bf..623306d5 100644 --- a/atst/utils/flash.py +++ b/atst/utils/flash.py @@ -39,6 +39,11 @@ MESSAGES = { "message_template": "There was an error processing the invitation for {{ user_name }} from {{ application_name }}", "category": "error", }, + "application_invite_resent": { + "title_template": "Application invitation revoked", + "message_template": "You have successfully resent the invite for {{ user_name }} from {{ application_name }}", + "category": "success", + }, "application_invite_revoked": { "title_template": "Application invitation revoked", "message_template": "You have successfully revoked the invite for {{ user_name }} from {{ application_name }}", diff --git a/templates/applications/fragments/member_form_fields.html b/templates/applications/fragments/member_form_fields.html new file mode 100644 index 00000000..96c6966b --- /dev/null +++ b/templates/applications/fragments/member_form_fields.html @@ -0,0 +1,71 @@ +{% from "components/checkbox_input.html" import CheckboxInput %} +{% from "components/text_input.html" import TextInput %} +{% from "components/phone_input.html" import PhoneInput %} + +{% macro PermsFields(form, new=False, member_role_id=None) %} +

{{ "portfolios.applications.members.form.project_perms" | translate }}

+
+ {% if new %} + {% set team_mgmt = form.perms_team_mgmt.name %} + {% set env_mgmt = form.perms_env_mgmt.name %} + {% set del_env = form.perms_del_env.name %} + {% else %} + {% set team_mgmt = "perms_team_mgmt-{}".format(member_role_id) %} + {% set env_mgmt = "perms_env_mgmt-{}".format(member_role_id) %} + {% set del_env = "perms_del_env-{}".format(member_role_id) %} + {% endif %} + + {{ CheckboxInput(form.perms_team_mgmt, classes="input__inline-fields", key=team_mgmt, id=team_mgmt, optional=True) }} + {{ CheckboxInput(form.perms_env_mgmt, classes="input__inline-fields", key=env_mgmt, id=env_mgmt, optional=True) }} + {{ CheckboxInput(form.perms_del_env, classes="input__inline-fields", key=del_env, id=del_env, optional=True) }} +
+
+

{{ "portfolios.applications.members.form.env_access" | translate }}

+
+ {% for environment_data in form.environment_roles %} + +
+
+
+
+ +
+ {{ environment_data.environment_name.data }} +
+
+
+
+ {{ environment_data.role(**{"v-model": "value"}) }} +
+
+
+
+
+ {{ environment_data.environment_id() }} +
+ {% endfor %} +
+{% endmacro %} + +{% macro InfoFields(member_form) %} +
+ {{ TextInput(member_form.first_name, validation='requiredField', optional=False) }} +
+
+ {{ TextInput(member_form.last_name, validation='requiredField', optional=False) }} +
+
+ {{ TextInput(member_form.email, validation='email', optional=False) }} +
+
+ {{ PhoneInput(member_form.phone_number, member_form.phone_ext)}} +
+
+ {{ TextInput(member_form.dod_id, validation='dodId', optional=False) }} +
+ How do I find the DoD ID? +{% endmacro %} diff --git a/templates/applications/fragments/member_perms_form_fields.html b/templates/applications/fragments/member_perms_form_fields.html deleted file mode 100644 index c0261ed3..00000000 --- a/templates/applications/fragments/member_perms_form_fields.html +++ /dev/null @@ -1,52 +0,0 @@ -{% from "components/checkbox_input.html" import CheckboxInput %} - -{% macro MemberPermsFields(form, new=False, member_role_id=None) %} -
-

{{ "portfolios.applications.members.form.project_perms" | translate }}

-
- {% if new %} - {% set team_mgmt = form.perms_team_mgmt.name %} - {% set env_mgmt = form.perms_env_mgmt.name %} - {% set del_env = form.perms_del_env.name %} - {% else %} - {% set team_mgmt = "perms_team_mgmt-{}".format(member_role_id) %} - {% set env_mgmt = "perms_env_mgmt-{}".format(member_role_id) %} - {% set del_env = "perms_del_env-{}".format(member_role_id) %} - {% endif %} - - {{ CheckboxInput(form.perms_team_mgmt, classes="input__inline-fields", key=team_mgmt, id=team_mgmt, optional=True) }} - {{ CheckboxInput(form.perms_env_mgmt, classes="input__inline-fields", key=env_mgmt, id=env_mgmt, optional=True) }} - {{ CheckboxInput(form.perms_del_env, classes="input__inline-fields", key=del_env, id=del_env, optional=True) }} -
-
-

{{ "portfolios.applications.members.form.env_access" | translate }}

-
- {% for environment_data in form.environment_roles %} - -
-
-
-
- -
- {{ environment_data.environment_name.data }} -
-
-
-
- {{ environment_data.role(**{"v-model": "value"}) }} -
-
-
-
-
- {{ environment_data.environment_id() }} -
- {% endfor %} -
-
-{% endmacro %} diff --git a/templates/applications/fragments/new_member_modal_content.html b/templates/applications/fragments/new_member_modal_content.html index bc3bd4d2..94bbc64e 100644 --- a/templates/applications/fragments/new_member_modal_content.html +++ b/templates/applications/fragments/new_member_modal_content.html @@ -1,8 +1,5 @@ {% from "components/icon.html" import Icon %} -{% from "components/text_input.html" import TextInput %} -{% from "components/checkbox_input.html" import CheckboxInput %} -{% from "components/phone_input.html" import PhoneInput %} -{% from "applications/fragments/member_perms_form_fields.html" import MemberPermsFields %} +{% import "applications/fragments/member_form_fields.html" as member_fields %} {% macro MemberFormTemplate(title, next_button, previous=True) %}