mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-21 10:16:13 +02:00
Merge pull request #2974 from cisagov/nl/2769-OrgMemberPage-InviteANewMember
#2769: Org Member Page - Invite A New Member - [NL]
This commit is contained in:
commit
0f3ae4ffff
9 changed files with 541 additions and 39 deletions
|
@ -20,6 +20,8 @@
|
|||
"http://localhost:8080/request/anything_else/",
|
||||
"http://localhost:8080/request/requirements/",
|
||||
"http://localhost:8080/request/finished/",
|
||||
"http://localhost:8080/user-profile/"
|
||||
"http://localhost:8080/user-profile/",
|
||||
"http://localhost:8080/members/",
|
||||
"http://localhost:8080/members/new-member"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -297,28 +297,56 @@ function clearValidators(el) {
|
|||
* radio button is false (hides this element if true)
|
||||
* **/
|
||||
function HookupYesNoListener(radioButtonName, elementIdToShowIfYes, elementIdToShowIfNo) {
|
||||
HookupRadioTogglerListener(radioButtonName, {
|
||||
'True': elementIdToShowIfYes,
|
||||
'False': elementIdToShowIfNo
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hookup listeners for radio togglers in form fields.
|
||||
*
|
||||
* Parameters:
|
||||
* - radioButtonName: The "name=" value for the radio buttons being used as togglers
|
||||
* - valueToElementMap: An object where keys are the values of the radio buttons,
|
||||
* and values are the corresponding DOM element IDs to show. All other elements will be hidden.
|
||||
*
|
||||
* Usage Example:
|
||||
* Assuming you have radio buttons with values 'option1', 'option2', and 'option3',
|
||||
* and corresponding DOM IDs 'section1', 'section2', 'section3'.
|
||||
*
|
||||
* HookupValueBasedListener('exampleRadioGroup', {
|
||||
* 'option1': 'section1',
|
||||
* 'option2': 'section2',
|
||||
* 'option3': 'section3'
|
||||
* });
|
||||
**/
|
||||
function HookupRadioTogglerListener(radioButtonName, valueToElementMap) {
|
||||
// Get the radio buttons
|
||||
let radioButtons = document.querySelectorAll('input[name="'+radioButtonName+'"]');
|
||||
|
||||
function handleRadioButtonChange() {
|
||||
// Check the value of the selected radio button
|
||||
// Attempt to find the radio button element that is checked
|
||||
let radioButtonChecked = document.querySelector('input[name="'+radioButtonName+'"]:checked');
|
||||
// Extract the list of all element IDs from the valueToElementMap
|
||||
let allElementIds = Object.values(valueToElementMap);
|
||||
|
||||
// Check if the element exists before accessing its value
|
||||
function handleRadioButtonChange() {
|
||||
// Find the checked radio button
|
||||
let radioButtonChecked = document.querySelector('input[name="'+radioButtonName+'"]:checked');
|
||||
let selectedValue = radioButtonChecked ? radioButtonChecked.value : null;
|
||||
|
||||
switch (selectedValue) {
|
||||
case 'True':
|
||||
toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 1);
|
||||
break;
|
||||
// Hide all elements by default
|
||||
allElementIds.forEach(function (elementId) {
|
||||
let element = document.getElementById(elementId);
|
||||
if (element) {
|
||||
hideElement(element);
|
||||
}
|
||||
});
|
||||
|
||||
case 'False':
|
||||
toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 2);
|
||||
break;
|
||||
|
||||
default:
|
||||
toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 0);
|
||||
// Show the relevant element for the selected value
|
||||
if (selectedValue && valueToElementMap[selectedValue]) {
|
||||
let elementToShow = document.getElementById(valueToElementMap[selectedValue]);
|
||||
if (elementToShow) {
|
||||
showElement(elementToShow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -328,11 +356,12 @@ function HookupYesNoListener(radioButtonName, elementIdToShowIfYes, elementIdToS
|
|||
radioButton.addEventListener('change', handleRadioButtonChange);
|
||||
});
|
||||
|
||||
// initialize
|
||||
// Initialize by checking the current state
|
||||
handleRadioButtonChange();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// A generic display none/block toggle function that takes an integer param to indicate how the elements toggle
|
||||
function toggleTwoDomElements(ele1, ele2, index) {
|
||||
let element1 = document.getElementById(ele1);
|
||||
|
@ -912,6 +941,18 @@ function setupUrbanizationToggle(stateTerritoryField) {
|
|||
HookupYesNoListener("additional_details-has_anything_else_text",'anything-else', null)
|
||||
})();
|
||||
|
||||
|
||||
/**
|
||||
* An IIFE that listens to the yes/no radio buttons on the anything else form and toggles form field visibility accordingly
|
||||
*
|
||||
*/
|
||||
(function newMemberFormListener() {
|
||||
HookupRadioTogglerListener('member_access_level', {
|
||||
'admin': 'new-member-admin-permissions',
|
||||
'basic': 'new-member-basic-permissions'
|
||||
});
|
||||
})();
|
||||
|
||||
/**
|
||||
* An IIFE that disables the delete buttons on nameserver forms on page load if < 3 forms
|
||||
*
|
||||
|
|
|
@ -120,6 +120,11 @@ urlpatterns = [
|
|||
# views.PortfolioNoMembersView.as_view(),
|
||||
# name="no-portfolio-members",
|
||||
# ),
|
||||
path(
|
||||
"members/new-member/",
|
||||
views.NewMemberView.as_view(),
|
||||
name="new-member",
|
||||
),
|
||||
path(
|
||||
"requests/",
|
||||
views.PortfolioDomainRequestsView.as_view(),
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import logging
|
||||
from django import forms
|
||||
from django.core.validators import RegexValidator
|
||||
from django.core.validators import MaxLengthValidator
|
||||
|
||||
from registrar.models import (
|
||||
PortfolioInvitation,
|
||||
|
@ -10,6 +11,7 @@ from registrar.models import (
|
|||
DomainInformation,
|
||||
Portfolio,
|
||||
SeniorOfficial,
|
||||
User,
|
||||
)
|
||||
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
|
||||
|
||||
|
@ -160,3 +162,112 @@ class PortfolioInvitedMemberForm(forms.ModelForm):
|
|||
"roles",
|
||||
"additional_permissions",
|
||||
]
|
||||
|
||||
|
||||
class NewMemberForm(forms.ModelForm):
|
||||
member_access_level = forms.ChoiceField(
|
||||
label="Select permission",
|
||||
choices=[("admin", "Admin Access"), ("basic", "Basic Access")],
|
||||
widget=forms.RadioSelect(attrs={"class": "usa-radio__input usa-radio__input--tile"}),
|
||||
required=True,
|
||||
error_messages={
|
||||
"required": "Member access level is required",
|
||||
},
|
||||
)
|
||||
admin_org_domain_request_permissions = forms.ChoiceField(
|
||||
label="Select permission",
|
||||
choices=[("view_only", "View all requests"), ("view_and_create", "View all requests plus create requests")],
|
||||
widget=forms.RadioSelect,
|
||||
required=True,
|
||||
error_messages={
|
||||
"required": "Admin domain request permission is required",
|
||||
},
|
||||
)
|
||||
admin_org_members_permissions = forms.ChoiceField(
|
||||
label="Select permission",
|
||||
choices=[("view_only", "View all members"), ("view_and_create", "View all members plus manage members")],
|
||||
widget=forms.RadioSelect,
|
||||
required=True,
|
||||
error_messages={
|
||||
"required": "Admin member permission is required",
|
||||
},
|
||||
)
|
||||
basic_org_domain_request_permissions = forms.ChoiceField(
|
||||
label="Select permission",
|
||||
choices=[
|
||||
("view_only", "View all requests"),
|
||||
("view_and_create", "View all requests plus create requests"),
|
||||
("no_access", "No access"),
|
||||
],
|
||||
widget=forms.RadioSelect,
|
||||
required=True,
|
||||
error_messages={
|
||||
"required": "Basic member permission is required",
|
||||
},
|
||||
)
|
||||
|
||||
email = forms.EmailField(
|
||||
label="Enter the email of the member you'd like to invite",
|
||||
max_length=None,
|
||||
error_messages={
|
||||
"invalid": ("Enter an email address in the required format, like name@example.com."),
|
||||
"required": ("Enter an email address in the required format, like name@example.com."),
|
||||
},
|
||||
validators=[
|
||||
MaxLengthValidator(
|
||||
320,
|
||||
message="Response must be less than 320 characters.",
|
||||
)
|
||||
],
|
||||
required=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ["email"]
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
|
||||
# Lowercase the value of the 'email' field
|
||||
email_value = cleaned_data.get("email")
|
||||
if email_value:
|
||||
cleaned_data["email"] = email_value.lower()
|
||||
|
||||
##########################################
|
||||
# TODO: future ticket
|
||||
# (invite new member)
|
||||
##########################################
|
||||
# Check for an existing user (if there isn't any, send an invite)
|
||||
# if email_value:
|
||||
# try:
|
||||
# existingUser = User.objects.get(email=email_value)
|
||||
# except User.DoesNotExist:
|
||||
# raise forms.ValidationError("User with this email does not exist.")
|
||||
|
||||
member_access_level = cleaned_data.get("member_access_level")
|
||||
|
||||
# Intercept the error messages so that we don't validate hidden inputs
|
||||
if not member_access_level:
|
||||
# If no member access level has been selected, delete error messages
|
||||
# for all hidden inputs (which is everything except the e-mail input
|
||||
# and member access selection)
|
||||
for field in self.fields:
|
||||
if field in self.errors and field != "email" and field != "member_access_level":
|
||||
del self.errors[field]
|
||||
return cleaned_data
|
||||
|
||||
basic_dom_req_error = "basic_org_domain_request_permissions"
|
||||
admin_dom_req_error = "admin_org_domain_request_permissions"
|
||||
admin_member_error = "admin_org_members_permissions"
|
||||
|
||||
if member_access_level == "admin" and basic_dom_req_error in self.errors:
|
||||
# remove the error messages pertaining to basic permission inputs
|
||||
del self.errors[basic_dom_req_error]
|
||||
elif member_access_level == "basic":
|
||||
# remove the error messages pertaining to admin permission inputs
|
||||
if admin_dom_req_error in self.errors:
|
||||
del self.errors[admin_dom_req_error]
|
||||
if admin_member_error in self.errors:
|
||||
del self.errors[admin_member_error]
|
||||
return cleaned_data
|
||||
|
|
|
@ -425,3 +425,70 @@ class DomainInformation(TimeStampedModel):
|
|||
return self.domain.get_state_display()
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def converted_organization_name(self):
|
||||
if self.portfolio:
|
||||
return self.portfolio.organization_name
|
||||
return self.organization_name
|
||||
|
||||
# ----- Portfolio Properties -----
|
||||
@property
|
||||
def converted_generic_org_type(self):
|
||||
if self.portfolio:
|
||||
return self.portfolio.organization_type
|
||||
return self.generic_org_type
|
||||
|
||||
@property
|
||||
def converted_federal_agency(self):
|
||||
if self.portfolio:
|
||||
return self.portfolio.federal_agency
|
||||
return self.federal_agency
|
||||
|
||||
@property
|
||||
def converted_federal_type(self):
|
||||
if self.portfolio:
|
||||
return self.portfolio.federal_type
|
||||
return self.federal_type
|
||||
|
||||
@property
|
||||
def converted_senior_official(self):
|
||||
if self.portfolio:
|
||||
return self.portfolio.senior_official
|
||||
return self.senior_official
|
||||
|
||||
@property
|
||||
def converted_address_line1(self):
|
||||
if self.portfolio:
|
||||
return self.portfolio.address_line1
|
||||
return self.address_line1
|
||||
|
||||
@property
|
||||
def converted_address_line2(self):
|
||||
if self.portfolio:
|
||||
return self.portfolio.address_line2
|
||||
return self.address_line2
|
||||
|
||||
@property
|
||||
def converted_city(self):
|
||||
if self.portfolio:
|
||||
return self.portfolio.city
|
||||
return self.city
|
||||
|
||||
@property
|
||||
def converted_state_territory(self):
|
||||
if self.portfolio:
|
||||
return self.portfolio.state_territory
|
||||
return self.state_territory
|
||||
|
||||
@property
|
||||
def converted_zipcode(self):
|
||||
if self.portfolio:
|
||||
return self.portfolio.zipcode
|
||||
return self.zipcode
|
||||
|
||||
@property
|
||||
def converted_urbanization(self):
|
||||
if self.portfolio:
|
||||
return self.portfolio.urbanization
|
||||
return self.urbanization
|
||||
|
|
|
@ -95,7 +95,7 @@
|
|||
|
||||
{% if has_organization_members_flag %}
|
||||
<li class="usa-nav__primary-item">
|
||||
<a href="/members/" class="usa-nav-link {% if path|is_members_subpage %} usa-current{% endif %}">
|
||||
<a href="{% url 'members' %}" class="usa-nav-link {% if path|is_members_subpage %} usa-current{% endif %}">
|
||||
Members
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
{% if has_edit_members_portfolio_permission %}
|
||||
<div class="mobile:grid-col-12 tablet:grid-col-6">
|
||||
<p class="float-right-tablet tablet:margin-y-0">
|
||||
<a href="#" class="usa-button"
|
||||
<a href="{% url 'new-member' %}" class="usa-button"
|
||||
>
|
||||
Add a new member
|
||||
</a>
|
||||
|
|
117
src/registrar/templates/portfolio_members_add_new.html
Normal file
117
src/registrar/templates/portfolio_members_add_new.html
Normal file
|
@ -0,0 +1,117 @@
|
|||
{% extends 'portfolio_base.html' %}
|
||||
{% load static url_helpers %}
|
||||
{% load field_helpers %}
|
||||
|
||||
{% block title %} Members | New Member {% endblock %}
|
||||
|
||||
{% block wrapper_class %}
|
||||
{{ block.super }} dashboard--grey-1
|
||||
{% endblock %}
|
||||
|
||||
{% block portfolio_content %}
|
||||
|
||||
<!-- Form mesages -->
|
||||
{% include "includes/form_errors.html" with form=form %}
|
||||
{% block messages %}
|
||||
{% include "includes/form_messages.html" %}
|
||||
{% endblock messages%}
|
||||
|
||||
<!-- Navigation breadcrumbs -->
|
||||
<nav class="usa-breadcrumb padding-top-0" aria-label="Domain request breadcrumb">
|
||||
<ol class="usa-breadcrumb__list">
|
||||
<li class="usa-breadcrumb__list-item">
|
||||
<a href="{% url 'members' %}" class="usa-breadcrumb__link"><span>Members</span></a>
|
||||
</li>
|
||||
<li class="usa-breadcrumb__list-item usa-current" aria-current="page">
|
||||
<span>Add a new member</span>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<!-- Page header -->
|
||||
{% block new_member_header %}
|
||||
<h1>Add a new member</h1>
|
||||
{% endblock new_member_header %}
|
||||
|
||||
{% include "includes/required_fields.html" %}
|
||||
|
||||
<form class="usa-form usa-form--large" method="post" novalidate>
|
||||
<fieldset class="usa-fieldset margin-top-2">
|
||||
<legend>
|
||||
<h2>Email</h2>
|
||||
</legend>
|
||||
<!-- Member email -->
|
||||
{% csrf_token %}
|
||||
{% with group_classes="usa-form-editable usa-form-editable--no-border padding-top-0" %}
|
||||
{% input_with_errors form.email %}
|
||||
{% endwith %}
|
||||
</fieldset>
|
||||
|
||||
<!-- Member access radio buttons (Toggles other sections) -->
|
||||
<fieldset class="usa-fieldset margin-top-2">
|
||||
<legend>
|
||||
<h2>Member Access</h2>
|
||||
</legend>
|
||||
|
||||
<em>Select the level of access for this member. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
|
||||
{% with group_classes="usa-form-editable usa-form-editable--no-border padding-top-0" %}
|
||||
<div class="usa-radio">
|
||||
{% for radio in form.member_access_level %}
|
||||
{{ radio.tag }}
|
||||
<label class="usa-radio__label usa-legend" for="{{ radio.id_for_label }}">
|
||||
{{ radio.choice_label }}
|
||||
<p class="margin-0 margin-top-2">
|
||||
{% if radio.choice_label == "Admin Access" %}
|
||||
Grants this member access to the organization-wide information on domains, domain requests, and members. Domain management can be assigned separately.
|
||||
{% else %}
|
||||
Grants this member access to the organization. They can be given extra permissions to view all organization domain requests and submit domain requests on behalf of the organization. Basic access members can’t view all members of an organization or manage them. Domain management can be assigned separately.
|
||||
{% endif %}
|
||||
</p>
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endwith %}
|
||||
|
||||
</fieldset>
|
||||
|
||||
<!-- Admin access form -->
|
||||
<div id="new-member-admin-permissions" class="margin-top-2">
|
||||
<h2>Admin access permissions</h2>
|
||||
<p>Member permissions available for admin-level acccess.</p>
|
||||
|
||||
<h3 class="margin-bottom-0">Organization domain requests</h3>
|
||||
{% with group_classes="usa-form-editable usa-form-editable--no-border padding-top-0" %}
|
||||
{% input_with_errors form.admin_org_domain_request_permissions %}
|
||||
{% endwith %}
|
||||
|
||||
<h3 class="margin-bottom-0 margin-top-3">Organization members</h3>
|
||||
{% with group_classes="usa-form-editable usa-form-editable--no-border padding-top-0" %}
|
||||
{% input_with_errors form.admin_org_members_permissions %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
||||
<!-- Basic access form -->
|
||||
<div id="new-member-basic-permissions" class="margin-top-2">
|
||||
<h2>Basic member permissions</h2>
|
||||
<p>Member permissions available for basic-level access</p>
|
||||
{% input_with_errors form.basic_org_domain_request_permissions %}
|
||||
</div>
|
||||
|
||||
<!-- Submit/cancel buttons -->
|
||||
<div class="margin-top-3">
|
||||
<a
|
||||
type="button"
|
||||
href="{% url 'members' %}"
|
||||
class="usa-button usa-button--outline"
|
||||
name="btn-cancel-click"
|
||||
aria-label="Cancel adding new member"
|
||||
>Cancel
|
||||
</a>
|
||||
<button type="submit" class="usa-button">Invite Member</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock portfolio_content%}
|
||||
|
||||
|
|
@ -1,14 +1,9 @@
|
|||
import logging
|
||||
from django.http import Http404
|
||||
from django.shortcuts import render
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse
|
||||
from django.contrib import messages
|
||||
from registrar.forms.portfolio import (
|
||||
PortfolioInvitedMemberForm,
|
||||
PortfolioMemberForm,
|
||||
PortfolioOrgAddressForm,
|
||||
PortfolioSeniorOfficialForm,
|
||||
)
|
||||
from registrar.forms import portfolio as portfolioForms
|
||||
from registrar.models import Portfolio, User
|
||||
from registrar.models.portfolio_invitation import PortfolioInvitation
|
||||
from registrar.models.user_portfolio_permission import UserPortfolioPermission
|
||||
|
@ -25,7 +20,6 @@ from registrar.views.utility.permission_views import (
|
|||
)
|
||||
from django.views.generic import View
|
||||
from django.views.generic.edit import FormMixin
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -51,15 +45,6 @@ class PortfolioDomainRequestsView(PortfolioDomainRequestsPermissionView, View):
|
|||
return render(request, "portfolio_requests.html")
|
||||
|
||||
|
||||
class PortfolioMembersView(PortfolioMembersPermissionView, View):
|
||||
|
||||
template_name = "portfolio_members.html"
|
||||
|
||||
def get(self, request):
|
||||
"""Add additional context data to the template."""
|
||||
return render(request, "portfolio_members.html")
|
||||
|
||||
|
||||
class PortfolioMemberView(PortfolioMemberPermissionView, View):
|
||||
|
||||
template_name = "portfolio_member.html"
|
||||
|
@ -101,7 +86,7 @@ class PortfolioMemberView(PortfolioMemberPermissionView, View):
|
|||
class PortfolioMemberEditView(PortfolioMemberEditPermissionView, View):
|
||||
|
||||
template_name = "portfolio_member_permissions.html"
|
||||
form_class = PortfolioMemberForm
|
||||
form_class = portfolioForms.PortfolioMemberForm
|
||||
|
||||
def get(self, request, pk):
|
||||
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=pk)
|
||||
|
@ -197,7 +182,7 @@ class PortfolioInvitedMemberView(PortfolioMemberPermissionView, View):
|
|||
class PortfolioInvitedMemberEditView(PortfolioMemberEditPermissionView, View):
|
||||
|
||||
template_name = "portfolio_member_permissions.html"
|
||||
form_class = PortfolioInvitedMemberForm
|
||||
form_class = portfolioForms.PortfolioInvitedMemberForm
|
||||
|
||||
def get(self, request, pk):
|
||||
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=pk)
|
||||
|
@ -310,7 +295,7 @@ class PortfolioOrganizationView(PortfolioBasePermissionView, FormMixin):
|
|||
|
||||
model = Portfolio
|
||||
template_name = "portfolio_organization.html"
|
||||
form_class = PortfolioOrgAddressForm
|
||||
form_class = portfolioForms.PortfolioOrgAddressForm
|
||||
context_object_name = "portfolio"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
@ -373,7 +358,7 @@ class PortfolioSeniorOfficialView(PortfolioBasePermissionView, FormMixin):
|
|||
|
||||
model = Portfolio
|
||||
template_name = "portfolio_senior_official.html"
|
||||
form_class = PortfolioSeniorOfficialForm
|
||||
form_class = portfolioForms.PortfolioSeniorOfficialForm
|
||||
context_object_name = "portfolio"
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
|
@ -394,3 +379,177 @@ class PortfolioSeniorOfficialView(PortfolioBasePermissionView, FormMixin):
|
|||
self.object = self.get_object()
|
||||
form = self.get_form()
|
||||
return self.render_to_response(self.get_context_data(form=form))
|
||||
|
||||
|
||||
class PortfolioMembersView(PortfolioMembersPermissionView, View):
|
||||
|
||||
template_name = "portfolio_members.html"
|
||||
|
||||
def get(self, request):
|
||||
"""Add additional context data to the template."""
|
||||
return render(request, "portfolio_members.html")
|
||||
|
||||
|
||||
class NewMemberView(PortfolioMembersPermissionView, FormMixin):
|
||||
|
||||
template_name = "portfolio_members_add_new.html"
|
||||
form_class = portfolioForms.NewMemberForm
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
"""Get the portfolio object based on the session."""
|
||||
portfolio = self.request.session.get("portfolio")
|
||||
if portfolio is None:
|
||||
raise Http404("No organization found for this user")
|
||||
return portfolio
|
||||
|
||||
def get_form_kwargs(self):
|
||||
"""Include the instance in the form kwargs."""
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs["instance"] = self.get_object()
|
||||
return kwargs
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""Handle GET requests to display the form."""
|
||||
self.object = self.get_object()
|
||||
form = self.get_form()
|
||||
return self.render_to_response(self.get_context_data(form=form))
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""Handle POST requests to process form submission."""
|
||||
self.object = self.get_object()
|
||||
form = self.get_form()
|
||||
if form.is_valid():
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
|
||||
def form_invalid(self, form):
|
||||
"""Handle the case when the form is invalid."""
|
||||
return self.render_to_response(self.get_context_data(form=form))
|
||||
|
||||
def get_success_url(self):
|
||||
"""Redirect to members table."""
|
||||
return reverse("members")
|
||||
|
||||
##########################################
|
||||
# TODO: future ticket #2854
|
||||
# (save/invite new member)
|
||||
##########################################
|
||||
|
||||
# def _send_domain_invitation_email(self, email: str, requestor: User, add_success=True):
|
||||
# """Performs the sending of the member invitation email
|
||||
# email: string- email to send to
|
||||
# add_success: bool- default True indicates:
|
||||
# adding a success message to the view if the email sending succeeds
|
||||
|
||||
# raises EmailSendingError
|
||||
# """
|
||||
|
||||
# # Set a default email address to send to for staff
|
||||
# requestor_email = settings.DEFAULT_FROM_EMAIL
|
||||
|
||||
# # Check if the email requestor has a valid email address
|
||||
# if not requestor.is_staff and requestor.email is not None and requestor.email.strip() != "":
|
||||
# requestor_email = requestor.email
|
||||
# elif not requestor.is_staff:
|
||||
# messages.error(self.request, "Can't send invitation email. No email is associated with your account.")
|
||||
# logger.error(
|
||||
# f"Can't send email to '{email}' on domain '{self.object}'."
|
||||
# f"No email exists for the requestor '{requestor.username}'.",
|
||||
# exc_info=True,
|
||||
# )
|
||||
# return None
|
||||
|
||||
# # Check to see if an invite has already been sent
|
||||
# try:
|
||||
# invite = MemberInvitation.objects.get(email=email, domain=self.object)
|
||||
# # check if the invite has already been accepted
|
||||
# if invite.status == MemberInvitation.MemberInvitationStatus.RETRIEVED:
|
||||
# add_success = False
|
||||
# messages.warning(
|
||||
# self.request,
|
||||
# f"{email} is already a manager for this domain.",
|
||||
# )
|
||||
# else:
|
||||
# add_success = False
|
||||
# # else if it has been sent but not accepted
|
||||
# messages.warning(self.request, f"{email} has already been invited to this domain")
|
||||
# except Exception:
|
||||
# logger.error("An error occured")
|
||||
|
||||
# try:
|
||||
# send_templated_email(
|
||||
# "emails/member_invitation.txt",
|
||||
# "emails/member_invitation_subject.txt",
|
||||
# to_address=email,
|
||||
# context={
|
||||
# "portfolio": self.object,
|
||||
# "requestor_email": requestor_email,
|
||||
# },
|
||||
# )
|
||||
# except EmailSendingError as exc:
|
||||
# logger.warn(
|
||||
# "Could not sent email invitation to %s for domain %s",
|
||||
# email,
|
||||
# self.object,
|
||||
# exc_info=True,
|
||||
# )
|
||||
# raise EmailSendingError("Could not send email invitation.") from exc
|
||||
# else:
|
||||
# if add_success:
|
||||
# messages.success(self.request, f"{email} has been invited to this domain.")
|
||||
|
||||
# def _make_invitation(self, email_address: str, requestor: User):
|
||||
# """Make a Member invitation for this email and redirect with a message."""
|
||||
# try:
|
||||
# self._send_member_invitation_email(email=email_address, requestor=requestor)
|
||||
# except EmailSendingError:
|
||||
# messages.warning(self.request, "Could not send email invitation.")
|
||||
# else:
|
||||
# # (NOTE: only create a MemberInvitation if the e-mail sends correctly)
|
||||
# MemberInvitation.objects.get_or_create(email=email_address, domain=self.object)
|
||||
# return redirect(self.get_success_url())
|
||||
|
||||
# def form_valid(self, form):
|
||||
|
||||
# """Add the specified user as a member
|
||||
# for this portfolio.
|
||||
# Throws EmailSendingError."""
|
||||
# requested_email = form.cleaned_data["email"]
|
||||
# requestor = self.request.user
|
||||
# # look up a user with that email
|
||||
# try:
|
||||
# requested_user = User.objects.get(email=requested_email)
|
||||
# except User.DoesNotExist:
|
||||
# # no matching user, go make an invitation
|
||||
# return self._make_invitation(requested_email, requestor)
|
||||
# else:
|
||||
# # if user already exists then just send an email
|
||||
# try:
|
||||
# self._send_member_invitation_email(requested_email, requestor, add_success=False)
|
||||
# except EmailSendingError:
|
||||
# logger.warn(
|
||||
# "Could not send email invitation (EmailSendingError)",
|
||||
# self.object,
|
||||
# exc_info=True,
|
||||
# )
|
||||
# messages.warning(self.request, "Could not send email invitation.")
|
||||
# except Exception:
|
||||
# logger.warn(
|
||||
# "Could not send email invitation (Other Exception)",
|
||||
# self.object,
|
||||
# exc_info=True,
|
||||
# )
|
||||
# messages.warning(self.request, "Could not send email invitation.")
|
||||
|
||||
# try:
|
||||
# UserPortfolioPermission.objects.create(
|
||||
# user=requested_user,
|
||||
# portfolio=self.object,
|
||||
# role=UserDomainRole.Roles.MANAGER,
|
||||
# )
|
||||
# except IntegrityError:
|
||||
# messages.warning(self.request, f"{requested_email} is already a member of this portfolio")
|
||||
# else:
|
||||
# messages.success(self.request, f"Added user {requested_email}.")
|
||||
# return redirect(self.get_success_url())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue