mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-14 21:44:08 +02:00
Initial architecture
This commit is contained in:
parent
8b41e70840
commit
75499337e0
12 changed files with 238 additions and 61 deletions
|
@ -104,7 +104,7 @@ urlpatterns = [
|
||||||
# We embed the current user ID here, but we have a permission check
|
# We embed the current user ID here, but we have a permission check
|
||||||
# that ensures the user is who they say they are.
|
# that ensures the user is who they say they are.
|
||||||
"finish-user-setup/<int:pk>",
|
"finish-user-setup/<int:pk>",
|
||||||
views.FinishContactProfileSetupView.as_view(),
|
views.ContactProfileSetupView.as_view(),
|
||||||
name="finish-contact-profile-setup",
|
name="finish-contact-profile-setup",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
|
|
|
@ -126,11 +126,11 @@ class UserFixture:
|
||||||
"last_name": "Osos-Analyst",
|
"last_name": "Osos-Analyst",
|
||||||
"email": "kosos@truss.works",
|
"email": "kosos@truss.works",
|
||||||
},
|
},
|
||||||
{
|
# {
|
||||||
"username": "2cc0cde8-8313-4a50-99d8-5882e71443e8",
|
# "username": "2cc0cde8-8313-4a50-99d8-5882e71443e8",
|
||||||
"first_name": "Zander-Analyst",
|
# "first_name": "Zander-Analyst",
|
||||||
"last_name": "Adkinson-Analyst",
|
# "last_name": "Adkinson-Analyst",
|
||||||
},
|
# },
|
||||||
{
|
{
|
||||||
"username": "57ab5847-7789-49fe-a2f9-21d38076d699",
|
"username": "57ab5847-7789-49fe-a2f9-21d38076d699",
|
||||||
"first_name": "Paul-Analyst",
|
"first_name": "Paul-Analyst",
|
||||||
|
|
42
src/registrar/forms/contact.py
Normal file
42
src/registrar/forms/contact.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
from django import forms
|
||||||
|
from phonenumber_field.modelfields import PhoneNumberField # type: ignore
|
||||||
|
from django.core.validators import MaxLengthValidator
|
||||||
|
|
||||||
|
|
||||||
|
class ContactForm(forms.Form):
|
||||||
|
"""Form for adding or editing a contact"""
|
||||||
|
|
||||||
|
first_name = forms.CharField(
|
||||||
|
label="First name / given name",
|
||||||
|
error_messages={"required": "Enter your first name / given name."},
|
||||||
|
)
|
||||||
|
middle_name = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
label="Middle name (optional)",
|
||||||
|
)
|
||||||
|
last_name = forms.CharField(
|
||||||
|
label="Last name / family name",
|
||||||
|
error_messages={"required": "Enter your last name / family name."},
|
||||||
|
)
|
||||||
|
title = forms.CharField(
|
||||||
|
label="Title or role in your organization",
|
||||||
|
error_messages={
|
||||||
|
"required": ("Enter your title or role in your organization (e.g., Chief Information Officer).")
|
||||||
|
},
|
||||||
|
)
|
||||||
|
email = forms.EmailField(
|
||||||
|
label="Email",
|
||||||
|
max_length=None,
|
||||||
|
error_messages={"invalid": ("Enter your email address in the required format, like name@example.com.")},
|
||||||
|
validators=[
|
||||||
|
MaxLengthValidator(
|
||||||
|
320,
|
||||||
|
message="Response must be less than 320 characters.",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
phone = PhoneNumberField(
|
||||||
|
label="Phone",
|
||||||
|
error_messages={"invalid": "Enter a valid 10-digit phone number.", "required": "Enter your phone number."},
|
||||||
|
)
|
||||||
|
|
|
@ -202,7 +202,7 @@ NameserverFormset = formset_factory(
|
||||||
validate_max=True,
|
validate_max=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# TODO - refactor, wait until daves PR
|
||||||
class ContactForm(forms.ModelForm):
|
class ContactForm(forms.ModelForm):
|
||||||
"""Form for updating contacts."""
|
"""Form for updating contacts."""
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ from registrar.forms.utility.wizard_form_helper import (
|
||||||
from registrar.models import Contact, DomainRequest, DraftDomain, Domain, FederalAgency
|
from registrar.models import Contact, DomainRequest, DraftDomain, Domain, FederalAgency
|
||||||
from registrar.templatetags.url_helpers import public_site_url
|
from registrar.templatetags.url_helpers import public_site_url
|
||||||
from registrar.utility.enums import ValidationReturnType
|
from registrar.utility.enums import ValidationReturnType
|
||||||
|
from registrar.forms import ContactForm
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -385,7 +386,7 @@ class PurposeForm(RegistrarForm):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class YourContactForm(RegistrarForm):
|
class YourContactForm(RegistrarForm, ContactForm):
|
||||||
JOIN = "submitter"
|
JOIN = "submitter"
|
||||||
|
|
||||||
def to_database(self, obj):
|
def to_database(self, obj):
|
||||||
|
@ -408,40 +409,6 @@ class YourContactForm(RegistrarForm):
|
||||||
contact = getattr(obj, "submitter", None)
|
contact = getattr(obj, "submitter", None)
|
||||||
return super().from_database(contact)
|
return super().from_database(contact)
|
||||||
|
|
||||||
first_name = forms.CharField(
|
|
||||||
label="First name / given name",
|
|
||||||
error_messages={"required": "Enter your first name / given name."},
|
|
||||||
)
|
|
||||||
middle_name = forms.CharField(
|
|
||||||
required=False,
|
|
||||||
label="Middle name (optional)",
|
|
||||||
)
|
|
||||||
last_name = forms.CharField(
|
|
||||||
label="Last name / family name",
|
|
||||||
error_messages={"required": "Enter your last name / family name."},
|
|
||||||
)
|
|
||||||
title = forms.CharField(
|
|
||||||
label="Title or role in your organization",
|
|
||||||
error_messages={
|
|
||||||
"required": ("Enter your title or role in your organization (e.g., Chief Information Officer).")
|
|
||||||
},
|
|
||||||
)
|
|
||||||
email = forms.EmailField(
|
|
||||||
label="Email",
|
|
||||||
max_length=None,
|
|
||||||
error_messages={"invalid": ("Enter your email address in the required format, like name@example.com.")},
|
|
||||||
validators=[
|
|
||||||
MaxLengthValidator(
|
|
||||||
320,
|
|
||||||
message="Response must be less than 320 characters.",
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
phone = PhoneNumberField(
|
|
||||||
label="Phone",
|
|
||||||
error_messages={"invalid": "Enter a valid 10-digit phone number.", "required": "Enter your phone number."},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class OtherContactsYesNoForm(BaseYesNoForm):
|
class OtherContactsYesNoForm(BaseYesNoForm):
|
||||||
"""The yes/no field for the OtherContacts form."""
|
"""The yes/no field for the OtherContacts form."""
|
||||||
|
|
|
@ -21,7 +21,12 @@ class RegistrarForm(forms.Form):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
kwargs.setdefault("label_suffix", "")
|
kwargs.setdefault("label_suffix", "")
|
||||||
# save a reference to a domain request object
|
# save a reference to a domain request object
|
||||||
|
if "domain_request" in kwargs:
|
||||||
self.domain_request = kwargs.pop("domain_request", None)
|
self.domain_request = kwargs.pop("domain_request", None)
|
||||||
|
|
||||||
|
if "contact" in kwargs:
|
||||||
|
self.contact = kwargs.pop("contact", None)
|
||||||
|
|
||||||
super(RegistrarForm, self).__init__(*args, **kwargs)
|
super(RegistrarForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def to_database(self, obj: DomainRequest | Contact):
|
def to_database(self, obj: DomainRequest | Contact):
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from .utility.time_stamped_model import TimeStampedModel
|
from .utility.time_stamped_model import TimeStampedModel
|
||||||
|
|
||||||
from phonenumber_field.modelfields import PhoneNumberField # type: ignore
|
from phonenumber_field.modelfields import PhoneNumberField # type: ignore
|
||||||
|
|
||||||
|
|
||||||
class Contact(TimeStampedModel):
|
class Contact(TimeStampedModel):
|
||||||
"""Contact information follows a similar pattern for each contact."""
|
"""Contact information follows a similar pattern for each contact."""
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,72 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load static url_helpers %}
|
{% load static form_helpers url_helpers field_helpers %}
|
||||||
{% block title %} Finish setting up your profile {% endblock %}
|
{% block title %} Finish setting up your profile {% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>TEST</h2>
|
<div class="grid-container">
|
||||||
|
<div class="grid-row grid-gap">
|
||||||
|
<div class="tablet:grid-col">
|
||||||
|
<main id="main-content" class="grid-container">
|
||||||
|
{% include "includes/form_messages.html" %}
|
||||||
|
{% comment %}
|
||||||
|
Repurposed from domain_request_form.html
|
||||||
|
{% endcomment %}
|
||||||
|
{% for outer in forms %}
|
||||||
|
{% if outer|isformset %}
|
||||||
|
{% for inner in outer.forms %}
|
||||||
|
{% include "includes/form_errors.html" with form=inner %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% include "includes/form_errors.html" with form=outer %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<h1>Finish setting up your profile</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
We <a href="#">require</a> that you maintain accurate contact information.
|
||||||
|
The details you provide will only be used to support the administration of .gov and won’t be made public.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>What contact information should we use to reach you?</h2>
|
||||||
|
<p>
|
||||||
|
Review the details below and update any required information.
|
||||||
|
Note that editing this information won’t affect your Login.gov account information.
|
||||||
|
</p>
|
||||||
|
{# TODO: maybe remove this? #}
|
||||||
|
<p>Required information is marked with an asterisk (*).</p>
|
||||||
|
<form id="step__{{steps.current}}" class="usa-form usa-form--large" method="post" novalidate>
|
||||||
|
{% csrf_token %}
|
||||||
|
<fieldset class="usa-fieldset">
|
||||||
|
<legend class="usa-sr-only">
|
||||||
|
Your contact information
|
||||||
|
</legend>
|
||||||
|
|
||||||
|
{% input_with_errors forms.0.first_name %}
|
||||||
|
|
||||||
|
{% input_with_errors forms.0.middle_name %}
|
||||||
|
|
||||||
|
{% input_with_errors forms.0.last_name %}
|
||||||
|
|
||||||
|
{% input_with_errors forms.0.title %}
|
||||||
|
|
||||||
|
{% input_with_errors forms.0.email %}
|
||||||
|
|
||||||
|
{% with add_class="usa-input--medium" %}
|
||||||
|
{% input_with_errors forms.0.phone %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
<div>
|
||||||
|
<button type="submit" name="submit_button" class="usa-button">
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{% block form_fields %}{% endblock %}
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
|
|
|
@ -14,5 +14,8 @@ from .domain import (
|
||||||
DomainInvitationDeleteView,
|
DomainInvitationDeleteView,
|
||||||
DomainDeleteUserView,
|
DomainDeleteUserView,
|
||||||
)
|
)
|
||||||
|
from .contact import (
|
||||||
|
ContactProfileSetupView,
|
||||||
|
)
|
||||||
from .health import *
|
from .health import *
|
||||||
from .index import *
|
from .index import *
|
||||||
|
|
107
src/registrar/views/contact.py
Normal file
107
src/registrar/views/contact.py
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
from registrar.forms.contact import ContactForm
|
||||||
|
from registrar.views.utility.permission_views import ContactPermissionView
|
||||||
|
from django.views.generic.edit import FormMixin
|
||||||
|
|
||||||
|
|
||||||
|
# TODO we can and probably should generalize this at this rate.
|
||||||
|
class BaseContactView(ContactPermissionView):
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
self._set_contact(request)
|
||||||
|
context = self.get_context_data(object=self.object)
|
||||||
|
return self.render_to_response(context)
|
||||||
|
|
||||||
|
# TODO - this deserves a small refactor
|
||||||
|
def _set_contact(self, request):
|
||||||
|
"""
|
||||||
|
get domain from session cache or from db and set
|
||||||
|
to self.object
|
||||||
|
set session to self for downstream functions to
|
||||||
|
update session cache
|
||||||
|
"""
|
||||||
|
self.session = request.session
|
||||||
|
|
||||||
|
contact_pk = "contact:" + str(self.kwargs.get("pk"))
|
||||||
|
cached_contact = self.session.get(contact_pk)
|
||||||
|
|
||||||
|
if cached_contact:
|
||||||
|
self.object = cached_contact
|
||||||
|
else:
|
||||||
|
self.object = self.get_object()
|
||||||
|
self._update_session_with_contact()
|
||||||
|
|
||||||
|
def _update_session_with_contact(self):
|
||||||
|
"""
|
||||||
|
Set contact pk in the session cache
|
||||||
|
"""
|
||||||
|
domain_pk = "contact:" + str(self.kwargs.get("pk"))
|
||||||
|
self.session[domain_pk] = self.object
|
||||||
|
|
||||||
|
|
||||||
|
class ContactFormBaseView(BaseContactView, FormMixin):
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
"""Form submission posts to this view.
|
||||||
|
|
||||||
|
This post method harmonizes using BaseContactView and FormMixin
|
||||||
|
"""
|
||||||
|
# Set the current contact object in cache
|
||||||
|
self._set_contact(request)
|
||||||
|
|
||||||
|
# Get the current form and validate it
|
||||||
|
form = self.get_form()
|
||||||
|
return self.check_form(form)
|
||||||
|
|
||||||
|
# TODO rename?
|
||||||
|
def check_form(self, form):
|
||||||
|
return self.form_valid(form) if form.is_valid() else self.form_invalid(form)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
# updates session cache with contact
|
||||||
|
self._update_session_with_contact()
|
||||||
|
|
||||||
|
# superclass has the redirect
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def form_invalid(self, form):
|
||||||
|
# updates session cache with contact
|
||||||
|
self._update_session_with_contact()
|
||||||
|
|
||||||
|
# superclass has the redirect
|
||||||
|
return super().form_invalid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class ContactProfileSetupView(ContactPermissionView):
|
||||||
|
"""This view forces the user into providing additional details that
|
||||||
|
we may have missed from Login.gov"""
|
||||||
|
template_name = "finish_contact_setup.html"
|
||||||
|
form_class = ContactForm
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
self._get_contact(request)
|
||||||
|
context = self.get_context_data(object=self.object)
|
||||||
|
return self.render_to_response(context)
|
||||||
|
|
||||||
|
def _get_contact(self, request):
|
||||||
|
"""
|
||||||
|
get domain from session cache or from db and set
|
||||||
|
to self.object
|
||||||
|
set session to self for downstream functions to
|
||||||
|
update session cache
|
||||||
|
"""
|
||||||
|
self.session = request.session
|
||||||
|
|
||||||
|
contact_pk = "contact:" + str(self.kwargs.get("pk"))
|
||||||
|
cached_contact = self.session.get(contact_pk)
|
||||||
|
|
||||||
|
if cached_contact:
|
||||||
|
self.object = cached_contact
|
||||||
|
else:
|
||||||
|
self.object = self.get_object()
|
||||||
|
self._set_session_contact_pk()
|
||||||
|
|
||||||
|
def _set_session_contact_pk(self):
|
||||||
|
"""
|
||||||
|
Set contact pk in the session cache
|
||||||
|
"""
|
||||||
|
domain_pk = "contact:" + str(self.kwargs.get("pk"))
|
||||||
|
self.session[domain_pk] = self.object
|
||||||
|
|
|
@ -14,7 +14,7 @@ from registrar.models.contact import Contact
|
||||||
from registrar.models.user import User
|
from registrar.models.user import User
|
||||||
from registrar.utility import StrEnum
|
from registrar.utility import StrEnum
|
||||||
from registrar.views.utility import StepsHelper
|
from registrar.views.utility import StepsHelper
|
||||||
from registrar.views.utility.permission_views import DomainRequestPermissionDeleteView, ContactPermissionView
|
from registrar.views.utility.permission_views import DomainRequestPermissionDeleteView
|
||||||
|
|
||||||
from .utility import (
|
from .utility import (
|
||||||
DomainRequestPermissionView,
|
DomainRequestPermissionView,
|
||||||
|
@ -820,9 +820,3 @@ class DomainRequestDeleteView(DomainRequestPermissionDeleteView):
|
||||||
duplicates = [item for item, count in object_dict.items() if count > 1]
|
duplicates = [item for item, count in object_dict.items() if count > 1]
|
||||||
return duplicates
|
return duplicates
|
||||||
|
|
||||||
|
|
||||||
class FinishContactProfileSetupView(ContactPermissionView):
|
|
||||||
"""This view forces the user into providing additional details that
|
|
||||||
we may have missed from Login.gov"""
|
|
||||||
template_name = "finish_contact_setup.html"
|
|
||||||
forms = [forms.YourContactForm]
|
|
||||||
|
|
|
@ -153,11 +153,7 @@ class ContactPermissionView(ContactPermission, DetailView, abc.ABC):
|
||||||
|
|
||||||
# DetailView property for what model this is viewing
|
# DetailView property for what model this is viewing
|
||||||
model = Contact
|
model = Contact
|
||||||
# variable name in template context for the model object
|
object: Contact
|
||||||
context_object_name = "Contact"
|
|
||||||
|
|
||||||
# Abstract property enforces NotImplementedError on an attribute.
|
# variable name in template context for the model object
|
||||||
@property
|
context_object_name = "contact"
|
||||||
@abc.abstractmethod
|
|
||||||
def template_name(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue