mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-23 19:20:47 +02:00
Merge branch 'main' into nmb/nameservers
This commit is contained in:
commit
d60fca1c7f
19 changed files with 899 additions and 44 deletions
4
.github/workflows/deploy-sandbox.yaml
vendored
4
.github/workflows/deploy-sandbox.yaml
vendored
|
@ -40,7 +40,7 @@ jobs:
|
|||
docker compose run node npx gulp compile
|
||||
- name: Collect static assets
|
||||
working-directory: ./src
|
||||
run: docker compose run app python manage.py collectstatic
|
||||
run: docker compose run app python manage.py collectstatic --no-input
|
||||
- name: Deploy to cloud.gov sandbox
|
||||
uses: 18f/cg-deploy-action@main
|
||||
env:
|
||||
|
@ -69,4 +69,4 @@ jobs:
|
|||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '🥳 Successfully deployed to developer sandbox **[${{ env.ENVIRONMENT }}](https://getgov-${{ env.ENVIRONMENT }}.app.cloud.gov/)**.'
|
||||
})
|
||||
})
|
||||
|
|
|
@ -49,7 +49,7 @@ class ViewsTest(TestCase):
|
|||
# assert
|
||||
self.assertEqual(response.status_code, 500)
|
||||
self.assertTemplateUsed(response, "500.html")
|
||||
self.assertIn("Server Error", response.content.decode("utf-8"))
|
||||
self.assertIn("server error", response.content.decode("utf-8"))
|
||||
|
||||
def test_login_callback_reads_next(self, mock_client):
|
||||
# setup
|
||||
|
|
|
@ -55,6 +55,7 @@ admin.site.register(models.UserDomainRole, AuditedAdmin)
|
|||
admin.site.register(models.Contact, AuditedAdmin)
|
||||
admin.site.register(models.DomainInvitation, AuditedAdmin)
|
||||
admin.site.register(models.DomainApplication, AuditedAdmin)
|
||||
admin.site.register(models.DomainInformation, AuditedAdmin)
|
||||
admin.site.register(models.Domain, AuditedAdmin)
|
||||
admin.site.register(models.Host, MyHostAdmin)
|
||||
admin.site.register(models.Nameserver, MyHostAdmin)
|
||||
|
|
|
@ -417,6 +417,10 @@ footer {
|
|||
color: color('primary');
|
||||
}
|
||||
|
||||
.usa-identifier__logo {
|
||||
height: units(7);
|
||||
}
|
||||
|
||||
abbr[title] {
|
||||
// workaround for underlining abbr element
|
||||
border-bottom: none;
|
||||
|
|
|
@ -44,6 +44,21 @@ class UserFixture:
|
|||
"first_name": "Neil",
|
||||
"last_name": "Martinsen-Burrell",
|
||||
},
|
||||
{
|
||||
"username": "7185e6cd-d3c8-4adc-90a3-ceddba71d24f",
|
||||
"first_name": "Jon",
|
||||
"last_name": "Roberts",
|
||||
},
|
||||
{
|
||||
"username": "5f283494-31bd-49b5-b024-a7e7cae00848",
|
||||
"first_name": "Rachid",
|
||||
"last_name": "Mrad",
|
||||
},
|
||||
{
|
||||
"username": "eb2214cd-fc0c-48c0-9dbd-bc4cd6820c74",
|
||||
"first_name": "Alysia",
|
||||
"last_name": "Broddrick",
|
||||
},
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
|
273
src/registrar/migrations/0018_domaininformation.py
Normal file
273
src/registrar/migrations/0018_domaininformation.py
Normal file
|
@ -0,0 +1,273 @@
|
|||
# Generated by Django 4.1.6 on 2023-05-08 15:30
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("registrar", "0017_alter_domainapplication_status_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="DomainInformation",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"organization_type",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
(
|
||||
"federal",
|
||||
"Federal: an agency of the U.S. government's executive, legislative, or judicial branches",
|
||||
),
|
||||
(
|
||||
"interstate",
|
||||
"Interstate: an organization of two or more states",
|
||||
),
|
||||
(
|
||||
"state_or_territory",
|
||||
"State or territory: one of the 50 U.S. states, the District of Columbia, American Samoa, Guam, Northern Mariana Islands, Puerto Rico, or the U.S. Virgin Islands",
|
||||
),
|
||||
(
|
||||
"tribal",
|
||||
"Tribal: a tribal government recognized by the federal or a state government",
|
||||
),
|
||||
("county", "County: a county, parish, or borough"),
|
||||
("city", "City: a city, town, township, village, etc."),
|
||||
(
|
||||
"special_district",
|
||||
"Special district: an independent organization within a single state",
|
||||
),
|
||||
(
|
||||
"school_district",
|
||||
"School district: a school district that is not part of a local government",
|
||||
),
|
||||
],
|
||||
help_text="Type of Organization",
|
||||
max_length=255,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"federally_recognized_tribe",
|
||||
models.BooleanField(
|
||||
help_text="Is the tribe federally recognized", null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"state_recognized_tribe",
|
||||
models.BooleanField(
|
||||
help_text="Is the tribe recognized by a state", null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"tribe_name",
|
||||
models.TextField(blank=True, help_text="Name of tribe", null=True),
|
||||
),
|
||||
(
|
||||
"federal_agency",
|
||||
models.TextField(blank=True, help_text="Federal agency", null=True),
|
||||
),
|
||||
(
|
||||
"federal_type",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("executive", "Executive"),
|
||||
("judicial", "Judicial"),
|
||||
("legislative", "Legislative"),
|
||||
],
|
||||
help_text="Federal government branch",
|
||||
max_length=50,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_election_board",
|
||||
models.BooleanField(
|
||||
blank=True,
|
||||
help_text="Is your organization an election office?",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"organization_name",
|
||||
models.TextField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text="Organization name",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"address_line1",
|
||||
models.TextField(blank=True, help_text="Street address", null=True),
|
||||
),
|
||||
(
|
||||
"address_line2",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
help_text="Street address line 2",
|
||||
max_length=15,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
("city", models.TextField(blank=True, help_text="City", null=True)),
|
||||
(
|
||||
"state_territory",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
help_text="State, territory, or military post",
|
||||
max_length=2,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"zipcode",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text="Zip code",
|
||||
max_length=10,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"urbanization",
|
||||
models.TextField(
|
||||
blank=True,
|
||||
help_text="Urbanization (Puerto Rico only)",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"type_of_work",
|
||||
models.TextField(
|
||||
blank=True,
|
||||
help_text="Type of work of the organization",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"more_organization_information",
|
||||
models.TextField(
|
||||
blank=True,
|
||||
help_text="Further information about the government organization",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"purpose",
|
||||
models.TextField(
|
||||
blank=True, help_text="Purpose of your domain", null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"no_other_contacts_rationale",
|
||||
models.TextField(
|
||||
blank=True,
|
||||
help_text="Reason for listing no additional contacts",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"anything_else",
|
||||
models.TextField(
|
||||
blank=True, help_text="Anything else we should know?", null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_policy_acknowledged",
|
||||
models.BooleanField(
|
||||
blank=True,
|
||||
help_text="Acknowledged .gov acceptable use policy",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"security_email",
|
||||
models.EmailField(
|
||||
blank=True,
|
||||
help_text="Security email for public use",
|
||||
max_length=320,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"authorizing_official",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="information_authorizing_official",
|
||||
to="registrar.contact",
|
||||
),
|
||||
),
|
||||
(
|
||||
"creator",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="information_created",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"domain",
|
||||
models.OneToOneField(
|
||||
blank=True,
|
||||
help_text="Domain to which this information belongs",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="domain_info",
|
||||
to="registrar.domain",
|
||||
),
|
||||
),
|
||||
(
|
||||
"domain_application",
|
||||
models.OneToOneField(
|
||||
blank=True,
|
||||
help_text="Associated domain application",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="domainapplication_info",
|
||||
to="registrar.domainapplication",
|
||||
),
|
||||
),
|
||||
(
|
||||
"other_contacts",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
related_name="contact_applications_information",
|
||||
to="registrar.contact",
|
||||
),
|
||||
),
|
||||
(
|
||||
"submitter",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="submitted_applications_information",
|
||||
to="registrar.contact",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name_plural": "Domain Information",
|
||||
},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,47 @@
|
|||
# Generated by Django 4.1.6 on 2023-05-09 19:50
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("registrar", "0018_domaininformation"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="domainapplication",
|
||||
name="organization_type",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
(
|
||||
"federal",
|
||||
"Federal: an agency of the U.S. government's executive, legislative, or judicial branches",
|
||||
),
|
||||
("interstate", "Interstate: an organization of two or more states"),
|
||||
(
|
||||
"state_or_territory",
|
||||
"State or territory: one of the 50 U.S. states, the District of Columbia, American Samoa, Guam, Northern Mariana Islands, Puerto Rico, or the U.S. Virgin Islands",
|
||||
),
|
||||
(
|
||||
"tribal",
|
||||
"Tribal: a tribal government recognized by the federal or a state government",
|
||||
),
|
||||
("county", "County: a county, parish, or borough"),
|
||||
("city", "City: a city, town, township, village, etc."),
|
||||
(
|
||||
"special_district",
|
||||
"Special district: an independent organization within a single state",
|
||||
),
|
||||
(
|
||||
"school_district",
|
||||
"School district: a school district that is not part of a local government",
|
||||
),
|
||||
],
|
||||
help_text="Type of organization",
|
||||
max_length=255,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -2,6 +2,7 @@ from auditlog.registry import auditlog # type: ignore
|
|||
|
||||
from .contact import Contact
|
||||
from .domain_application import DomainApplication
|
||||
from .domain_information import DomainInformation
|
||||
from .domain import Domain
|
||||
from .host_ip import HostIP
|
||||
from .host import Host
|
||||
|
@ -15,6 +16,7 @@ from .website import Website
|
|||
__all__ = [
|
||||
"Contact",
|
||||
"DomainApplication",
|
||||
"DomainInformation",
|
||||
"Domain",
|
||||
"DomainInvitation",
|
||||
"HostIP",
|
||||
|
|
|
@ -9,7 +9,7 @@ from django_fsm import FSMField, transition # type: ignore
|
|||
|
||||
from .utility.time_stamped_model import TimeStampedModel
|
||||
from ..utility.email import send_templated_email, EmailSendingError
|
||||
|
||||
from itertools import chain
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -520,6 +520,10 @@ class DomainApplication(TimeStampedModel):
|
|||
Domain = apps.get_model("registrar.Domain")
|
||||
created_domain, _ = Domain.objects.get_or_create(name=self.requested_domain)
|
||||
|
||||
# copy the information from domainapplication into domaininformation
|
||||
DomainInformation = apps.get_model("registrar.DomainInformation")
|
||||
DomainInformation.create_from_da(self)
|
||||
|
||||
# create the permission for the user
|
||||
UserDomainRole = apps.get_model("registrar.UserDomainRole")
|
||||
UserDomainRole.objects.get_or_create(
|
||||
|
@ -577,3 +581,26 @@ class DomainApplication(TimeStampedModel):
|
|||
if self.organization_type == DomainApplication.OrganizationChoices.FEDERAL:
|
||||
return True
|
||||
return False
|
||||
|
||||
def to_dict(self):
|
||||
"""This is to process to_dict for Domain Information, making it friendly
|
||||
to "copy" it
|
||||
|
||||
More information can be found at this- (This used #5)
|
||||
https://stackoverflow.com/questions/21925671/convert-django-model-object-to-dict-with-all-of-the-fields-intact/29088221#29088221
|
||||
""" # noqa 590
|
||||
opts = self._meta
|
||||
data = {}
|
||||
for field in chain(opts.concrete_fields, opts.private_fields):
|
||||
if field.get_internal_type() in ("ForeignKey", "OneToOneField"):
|
||||
# get the related instance of the FK value
|
||||
fk_id = field.value_from_object(self)
|
||||
if fk_id:
|
||||
data[field.name] = field.related_model.objects.get(id=fk_id)
|
||||
else:
|
||||
data[field.name] = None
|
||||
else:
|
||||
data[field.name] = field.value_from_object(self)
|
||||
for field in opts.many_to_many:
|
||||
data[field.name] = field.value_from_object(self)
|
||||
return data
|
||||
|
|
250
src/registrar/models/domain_information.py
Normal file
250
src/registrar/models/domain_information.py
Normal file
|
@ -0,0 +1,250 @@
|
|||
from __future__ import annotations
|
||||
from .domain_application import DomainApplication
|
||||
from .utility.time_stamped_model import TimeStampedModel
|
||||
|
||||
import logging
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DomainInformation(TimeStampedModel):
|
||||
|
||||
"""A registrant's domain information for that domain, exported from
|
||||
DomainApplication. We use these field from DomainApplication with few exceptation
|
||||
which are 'removed' via pop at the bottom of this file. Most of design for domain
|
||||
management's user information are based on application, but we cannot change
|
||||
the application once approved, so copying them that way we can make changes
|
||||
after its approved. Most fields here are copied from Application."""
|
||||
|
||||
StateTerritoryChoices = DomainApplication.StateTerritoryChoices
|
||||
|
||||
OrganizationChoices = DomainApplication.OrganizationChoices
|
||||
|
||||
BranchChoices = DomainApplication.BranchChoices
|
||||
|
||||
AGENCY_CHOICES = DomainApplication.AGENCY_CHOICES
|
||||
|
||||
# This is the application user who created this application. The contact
|
||||
# information that they gave is in the `submitter` field
|
||||
creator = models.ForeignKey(
|
||||
"registrar.User",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="information_created",
|
||||
)
|
||||
|
||||
domain_application = models.OneToOneField(
|
||||
"registrar.DomainApplication",
|
||||
on_delete=models.PROTECT,
|
||||
blank=True,
|
||||
null=True,
|
||||
related_name="domainapplication_info",
|
||||
help_text="Associated domain application",
|
||||
unique=True,
|
||||
)
|
||||
|
||||
# ##### data fields from the initial form #####
|
||||
organization_type = models.CharField(
|
||||
max_length=255,
|
||||
choices=OrganizationChoices.choices,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Type of Organization",
|
||||
)
|
||||
|
||||
federally_recognized_tribe = models.BooleanField(
|
||||
null=True,
|
||||
help_text="Is the tribe federally recognized",
|
||||
)
|
||||
|
||||
state_recognized_tribe = models.BooleanField(
|
||||
null=True,
|
||||
help_text="Is the tribe recognized by a state",
|
||||
)
|
||||
|
||||
tribe_name = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Name of tribe",
|
||||
)
|
||||
|
||||
federal_agency = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Federal agency",
|
||||
)
|
||||
|
||||
federal_type = models.CharField(
|
||||
max_length=50,
|
||||
choices=BranchChoices.choices,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Federal government branch",
|
||||
)
|
||||
|
||||
is_election_board = models.BooleanField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Is your organization an election office?",
|
||||
)
|
||||
|
||||
organization_name = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Organization name",
|
||||
db_index=True,
|
||||
)
|
||||
address_line1 = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Street address",
|
||||
)
|
||||
address_line2 = models.CharField(
|
||||
max_length=15,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Street address line 2",
|
||||
)
|
||||
city = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="City",
|
||||
)
|
||||
state_territory = models.CharField(
|
||||
max_length=2,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="State, territory, or military post",
|
||||
)
|
||||
zipcode = models.CharField(
|
||||
max_length=10,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Zip code",
|
||||
db_index=True,
|
||||
)
|
||||
urbanization = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Urbanization (Puerto Rico only)",
|
||||
)
|
||||
|
||||
type_of_work = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Type of work of the organization",
|
||||
)
|
||||
|
||||
more_organization_information = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Further information about the government organization",
|
||||
)
|
||||
|
||||
authorizing_official = models.ForeignKey(
|
||||
"registrar.Contact",
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="information_authorizing_official",
|
||||
on_delete=models.PROTECT,
|
||||
)
|
||||
|
||||
domain = models.OneToOneField(
|
||||
"registrar.Domain",
|
||||
on_delete=models.PROTECT,
|
||||
blank=True,
|
||||
null=True,
|
||||
# Access this information via Domain as "domain.domain_info"
|
||||
related_name="domain_info",
|
||||
help_text="Domain to which this information belongs",
|
||||
)
|
||||
|
||||
# This is the contact information provided by the applicant. The
|
||||
# application user who created it is in the `creator` field.
|
||||
submitter = models.ForeignKey(
|
||||
"registrar.Contact",
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="submitted_applications_information",
|
||||
on_delete=models.PROTECT,
|
||||
)
|
||||
|
||||
purpose = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Purpose of your domain",
|
||||
)
|
||||
|
||||
other_contacts = models.ManyToManyField(
|
||||
"registrar.Contact",
|
||||
blank=True,
|
||||
related_name="contact_applications_information",
|
||||
)
|
||||
|
||||
no_other_contacts_rationale = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Reason for listing no additional contacts",
|
||||
)
|
||||
|
||||
anything_else = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Anything else we should know?",
|
||||
)
|
||||
|
||||
is_policy_acknowledged = models.BooleanField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Acknowledged .gov acceptable use policy",
|
||||
)
|
||||
security_email = models.EmailField(
|
||||
max_length=320,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Security email for public use",
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
try:
|
||||
if self.domain and self.domain.name:
|
||||
return self.domain.name
|
||||
else:
|
||||
return f"domain info set up and created by {self.creator}"
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
@classmethod
|
||||
def create_from_da(cls, domain_application):
|
||||
"""Takes in a DomainApplication dict and converts it into DomainInformation"""
|
||||
da_dict = domain_application.to_dict()
|
||||
# remove the id so one can be assinged on creation
|
||||
da_id = da_dict.pop("id")
|
||||
# check if we have a record that corresponds with the domain
|
||||
# application, if so short circuit the create
|
||||
domain_info = cls.objects.filter(domain_application__id=da_id).first()
|
||||
if domain_info:
|
||||
return domain_info
|
||||
# the following information below is not needed in the domain information:
|
||||
da_dict.pop("status")
|
||||
da_dict.pop("current_websites")
|
||||
da_dict.pop("investigator")
|
||||
da_dict.pop("alternative_domains")
|
||||
# use the requested_domain to create information for this domain
|
||||
da_dict["domain"] = da_dict.pop("requested_domain")
|
||||
other_contacts = da_dict.pop("other_contacts")
|
||||
domain_info = cls(**da_dict)
|
||||
domain_info.domain_application = domain_application
|
||||
# Save so the object now have PK
|
||||
# (needed to process the manytomany below before, first)
|
||||
domain_info.save()
|
||||
|
||||
# Process the remaining "many to many" stuff
|
||||
domain_info.other_contacts.add(*other_contacts)
|
||||
domain_info.save()
|
||||
return domain_info
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = "Domain Information"
|
20
src/registrar/public/img/registrar/dotgov_401_illo.svg
Normal file
20
src/registrar/public/img/registrar/dotgov_401_illo.svg
Normal file
|
@ -0,0 +1,20 @@
|
|||
<svg width="404" height="409" viewBox="0 0 404 409" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M291.707 328.743C240.024 358.583 133.444 374.87 78.8899 280.379C14.3648 168.618 78.2559 99.3488 140.956 63.1491C203.655 26.9495 296.801 80.4848 337.226 150.503C377.652 220.522 343.391 298.903 291.707 328.743Z" fill="#F5F8FA"/>
|
||||
<circle cx="276.88" cy="130.594" r="8" transform="rotate(135 276.88 130.594)" fill="#7AA5C1"/>
|
||||
<circle cx="288.196" cy="119.279" r="8" transform="rotate(135 288.196 119.279)" fill="#7AA5C1"/>
|
||||
<circle cx="231.626" cy="175.849" r="8" transform="rotate(135 231.626 175.849)" fill="#7AA5C1"/>
|
||||
<circle cx="186.371" cy="221.104" r="8" transform="rotate(135 186.371 221.104)" fill="#7AA5C1"/>
|
||||
<circle cx="242.939" cy="164.535" r="8" transform="rotate(135 242.939 164.535)" fill="#7AA5C1"/>
|
||||
<circle cx="197.686" cy="209.788" r="8" transform="rotate(135 197.686 209.788)" fill="#7AA5C1"/>
|
||||
<circle cx="220.312" cy="187.163" r="8" transform="rotate(135 220.312 187.163)" fill="#7AA5C1"/>
|
||||
<circle cx="175.057" cy="232.417" r="8" transform="rotate(135 175.057 232.417)" fill="#7AA5C1"/>
|
||||
<circle cx="163.743" cy="243.731" r="8" transform="rotate(135 163.743 243.731)" fill="#7AA5C1"/>
|
||||
<circle cx="152.43" cy="255.045" r="8" transform="rotate(135 152.43 255.045)" fill="#7AA5C1"/>
|
||||
<circle cx="141.116" cy="266.358" r="8" transform="rotate(135 141.116 266.358)" fill="#7AA5C1"/>
|
||||
<circle cx="129.802" cy="277.672" r="8" transform="rotate(135 129.802 277.672)" fill="#7AA5C1"/>
|
||||
<circle cx="118.489" cy="288.986" r="8" transform="rotate(135 118.489 288.986)" fill="#7AA5C1"/>
|
||||
<circle cx="254.253" cy="153.221" r="8" transform="rotate(135 254.253 153.221)" fill="#7AA5C1"/>
|
||||
<circle cx="265.566" cy="141.908" r="8" transform="rotate(135 265.566 141.908)" fill="#7AA5C1"/>
|
||||
<circle cx="208.998" cy="198.476" r="8" transform="rotate(135 208.998 198.476)" fill="#7AA5C1"/>
|
||||
<circle cx="203.342" cy="203.999" r="120.001" stroke="#7AA5C1" stroke-width="16"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
18
src/registrar/public/img/registrar/dotgov_404_illo.svg
Normal file
18
src/registrar/public/img/registrar/dotgov_404_illo.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 53 KiB |
59
src/registrar/public/img/registrar/dotgov_500_illo.svg
Normal file
59
src/registrar/public/img/registrar/dotgov_500_illo.svg
Normal file
|
@ -0,0 +1,59 @@
|
|||
<svg width="409" height="214" viewBox="0 0 409 214" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M366.004 90.4612C372.017 135.603 322.608 199.102 196.168 205.902C-32.9139 218.22 19.0655 18.8457 205.511 8.81994C299.204 3.78172 359.99 45.3195 366.004 90.4612Z" fill="#F5F8FA"/>
|
||||
<circle cx="213.873" cy="37.4943" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="212.214" cy="58.4272" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="231.089" cy="66.2297" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="235.451" cy="85.568" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="252.535" cy="99.7787" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="273.981" cy="115.121" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="372.072" cy="182.568" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="402.423" cy="189.642" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="220.441" cy="99.6379" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="231.089" cy="118.336" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="262.722" cy="143.568" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="248.722" cy="125.568" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="288.537" cy="138.04" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="300.051" cy="160.304" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="338.722" cy="170.568" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="349.914" cy="189.575" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="330.21" cy="189.575" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="316.722" cy="173.568" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="305.722" cy="190.568" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="287.722" cy="184.568" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="269.894" cy="183.007" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="250.19" cy="189.575" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="220.722" cy="192.568" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="233.722" cy="173.568" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="252.722" cy="165.568" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="180.203" cy="189.575" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="161.329" cy="196.143" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="140.795" cy="189.575" r="6.56803" fill="#7AA5C1"/>
|
||||
<path d="M122.733 189.575C122.733 193.203 119.793 196.143 116.165 196.143C112.538 196.143 109.597 193.203 109.597 189.575C109.597 185.948 112.538 183.007 116.165 183.007C119.793 183.007 122.733 185.948 122.733 189.575Z" fill="#7AA5C1"/>
|
||||
<circle cx="91.5343" cy="189.642" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="57.8852" cy="185.432" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="198.722" cy="195.568" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="193.34" cy="70.2917" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="98.9323" cy="156.735" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="25.247" cy="189.642" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="7.26164" cy="190.584" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="76.7592" cy="173.44" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="129.722" cy="172.568" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="147.722" cy="164.568" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="164.722" cy="173.568" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="174.465" cy="150.167" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="186.771" cy="169.871" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="198.266" cy="150.167" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="232.747" cy="151.176" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="273.722" cy="162.568" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="210.589" cy="163.303" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="204.834" cy="124.904" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="193.34" cy="108.553" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="171.181" cy="119.345" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="160.499" cy="138.04" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="139.153" cy="144.608" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="170.351" cy="95.4167" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="151.477" cy="115.121" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="125.204" cy="137.031" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="112.881" cy="163.303" r="6.56803" fill="#7AA5C1"/>
|
||||
<circle cx="204.834" cy="86.6427" r="6.56803" fill="#7AA5C1"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.9 KiB |
|
@ -1,28 +1,45 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block title %}{% translate "Unauthorized" %}{% endblock %}
|
||||
{% block title %}{% translate "Unauthorized | " %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" class="grid-container">
|
||||
<h1>{% translate "Unauthorized" %}</h1>
|
||||
<div class="grid-row grow-gap">
|
||||
<div class="tablet:grid-col-6 usa-prose margin-bottom-3">
|
||||
<h1>
|
||||
{% translate "You are not authorized to view this page" %}
|
||||
</h1>
|
||||
<h2>
|
||||
{% translate "Status 401" %}
|
||||
</h2>
|
||||
|
||||
{% if friendly_message %}
|
||||
<p>{{ friendly_message }}</p>
|
||||
{% else %}
|
||||
<p>{% translate "Authorization failed." %}</p>
|
||||
{% endif %}
|
||||
|
||||
<p><a href="{% url 'login' %}">
|
||||
{% translate "Would you like to try logging in again?" %}
|
||||
</a></p>
|
||||
{% if friendly_message %}
|
||||
<p>{{ friendly_message }}</p>
|
||||
{% else %}
|
||||
<p>{% translate "Authorization failed." %}</p>
|
||||
{% endif %}
|
||||
<p>
|
||||
You must be an authorized user and need to be signed in to view this page.
|
||||
Would you like to <a href="{% url 'login' %}"> try logging in again?</a>
|
||||
</p>
|
||||
<p>
|
||||
If you would like help with this error <a href="https://federalist-877ab29f-16f6-4f12-961c-96cf064cf070.sites.pages.cloud.gov/site/cisagov/getgov-home/contact/"> contact us </a>
|
||||
</p>
|
||||
|
||||
{% if log_identifier %}
|
||||
<p>Here's a unique identifier for this error.</p>
|
||||
<blockquote>{{ log_identifier }}</blockquote>
|
||||
<p>{% translate "Please include it if you contact us." %}</p>
|
||||
{% endif %}
|
||||
|
||||
TODO: Content team to create a "how to contact us" footer for the error pages
|
||||
{% if log_identifier %}
|
||||
<p>Here's a unique identifier for this error.</p>
|
||||
<p class="text-semibold">{{ log_identifier }}</p>
|
||||
<p>{% translate "Please include it if you contact us." %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="tablet:grid-col-4">
|
||||
<img
|
||||
src="{% static 'img/registrar/dotgov_401_illo.svg' %}"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
|
45
src/registrar/templates/403.html
Normal file
45
src/registrar/templates/403.html
Normal file
|
@ -0,0 +1,45 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block title %}{% translate "Forbidden | " %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" class="grid-container">
|
||||
<div class="grid-row grow-gap">
|
||||
<div class="tablet:grid-col-6 usa-prose margin-bottom-3">
|
||||
<h1>
|
||||
{% translate "You do not have the right permissions to view this page." %}
|
||||
</h1>
|
||||
<h2>
|
||||
{% translate "Status 403" %}
|
||||
</h2>
|
||||
|
||||
|
||||
{% if friendly_message %}
|
||||
<p>{{ friendly_message }}</p>
|
||||
{% else %}
|
||||
<p>{% translate "Forbidden." %}</p>
|
||||
{% endif %}
|
||||
<p>
|
||||
You must be an authorized user and need to be signed in to view this page.
|
||||
Would you like to <a href="{% url 'login' %}"> try logging in again?</a>
|
||||
</p>
|
||||
<p>
|
||||
If you would like help with this error <a href="https://federalist-877ab29f-16f6-4f12-961c-96cf064cf070.sites.pages.cloud.gov/site/cisagov/getgov-home/contact/"> contact us </a>
|
||||
</p>
|
||||
|
||||
{% if log_identifier %}
|
||||
<p>Here's a unique identifier for this error.</p>
|
||||
<p class="text-semibold">{{ log_identifier }}</p>
|
||||
<p>{% translate "Please include it if you contact us." %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="tablet:grid-col-4">
|
||||
<img
|
||||
src="{% static 'img/registrar/dotgov_401_illo.svg' %}"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
|
@ -1,15 +1,31 @@
|
|||
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block title %}{% translate "Page not found" %}{% endblock %}
|
||||
{% block title %}{% translate "Page not found | " %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" class="grid-container">
|
||||
<div class="grid-row grid-gap">
|
||||
<div class="tablet:grid-col-6 usa-prose margin-bottom-3">
|
||||
<h1>
|
||||
{% translate "We couldn’t find that page" %}
|
||||
</h1>
|
||||
<h2>
|
||||
{% translate "Status 404" %}
|
||||
</h2>
|
||||
|
||||
<h1>{% translate "Page not found" %}</h1>
|
||||
<p> Try going to the <a href="/">homepage</a>. If you can’t find what you’re looking for, <a href= "https://federalist-877ab29f-16f6-4f12-961c-96cf064cf070.sites.pages.cloud.gov/site/cisagov/getgov-home/contact/">contact us.</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="tablet:grid-col-4">
|
||||
<img
|
||||
src="{% static 'img/registrar/dotgov_404_illo.svg' %}"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>{% translate "The requested page could not be found." %}</p>
|
||||
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,24 +1,39 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block title %}{% translate "Server error" %}{% endblock %}
|
||||
{% block title %}{% translate "Server error | " %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" class="grid-container">
|
||||
<h1>{% translate "Server Error" %}</h1>
|
||||
<div class="grid-row grid-gap">
|
||||
<div class="tablet:grid-col-6 usa-prose margin-bottom-3">
|
||||
<h1>
|
||||
{% translate "We're having some trouble" %}
|
||||
</h1>
|
||||
<h2>
|
||||
{% translate "Status 500 – server error" %}
|
||||
</h2>
|
||||
{% if friendly_message %}
|
||||
<p>{{ friendly_message }}</p>
|
||||
{% else %}
|
||||
<p>
|
||||
Sorry! Try waiting a few minutes and then reloading the page.
|
||||
<a href="https://federalist-877ab29f-16f6-4f12-961c-96cf064cf070.sites.pages.cloud.gov/site/cisagov/getgov-home/contact/"> Contact us </a> if you need help.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if friendly_message %}
|
||||
<p>{{ friendly_message }}</p>
|
||||
{% else %}
|
||||
<p>{% translate "An internal server error occurred." %}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if log_identifier %}
|
||||
<p>Here's a unique identifier for this error.</p>
|
||||
<blockquote>{{ log_identifier }}</blockquote>
|
||||
<p>{% translate "Please include it if you contact us." %}</p>
|
||||
{% endif %}
|
||||
|
||||
TODO: Content team to create a "how to contact us" footer for the error pages
|
||||
{% if log_identifier %}
|
||||
<p>Here's a unique identifier for this error.</p>
|
||||
<p class="text-semibold">{{ log_identifier }}</p>
|
||||
<p>{% translate "Please include it if you contact us." %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="tablet:grid-col-4 flex-align-self-end">
|
||||
<img
|
||||
src="{%static 'img/registrar/dotgov_500_illo.svg' %}"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
src="{% static 'img/CISA_logo.png' %}"
|
||||
alt="CISA logo"
|
||||
role="img"
|
||||
width="48px"
|
||||
width="56px"
|
||||
/></a>
|
||||
</div>
|
||||
<section
|
||||
|
|
|
@ -4,6 +4,7 @@ from django.db.utils import IntegrityError
|
|||
from registrar.models import (
|
||||
Contact,
|
||||
DomainApplication,
|
||||
DomainInformation,
|
||||
User,
|
||||
Website,
|
||||
Domain,
|
||||
|
@ -63,6 +64,33 @@ class TestDomainApplication(TestCase):
|
|||
application.other_contacts.add(contact)
|
||||
application.save()
|
||||
|
||||
def test_domain_info(self):
|
||||
"""Can create domain info with all fields."""
|
||||
user, _ = User.objects.get_or_create()
|
||||
contact = Contact.objects.create()
|
||||
domain, _ = Domain.objects.get_or_create(name="igorville.gov")
|
||||
information = DomainInformation.objects.create(
|
||||
creator=user,
|
||||
organization_type=DomainInformation.OrganizationChoices.FEDERAL,
|
||||
federal_type=DomainInformation.BranchChoices.EXECUTIVE,
|
||||
is_election_board=False,
|
||||
organization_name="Test",
|
||||
address_line1="100 Main St.",
|
||||
address_line2="APT 1A",
|
||||
state_territory="CA",
|
||||
zipcode="12345-6789",
|
||||
authorizing_official=contact,
|
||||
submitter=contact,
|
||||
purpose="Igorville rules!",
|
||||
anything_else="All of Igorville loves the dotgov program.",
|
||||
is_policy_acknowledged=True,
|
||||
domain=domain,
|
||||
)
|
||||
information.other_contacts.add(contact)
|
||||
information.save()
|
||||
self.assertEqual(information.domain.id, domain.id)
|
||||
self.assertEqual(information.id, domain.domain_info.id)
|
||||
|
||||
def test_status_fsm_submit_fail(self):
|
||||
user, _ = User.objects.get_or_create()
|
||||
application = DomainApplication.objects.create(creator=user)
|
||||
|
@ -166,6 +194,24 @@ class TestPermissions(TestCase):
|
|||
self.assertTrue(UserDomainRole.objects.get(user=user, domain=domain))
|
||||
|
||||
|
||||
class TestDomainInfo(TestCase):
|
||||
|
||||
"""Test creation of Domain Information when approved."""
|
||||
|
||||
def test_approval_creates_info(self):
|
||||
domain, _ = Domain.objects.get_or_create(name="igorville.gov")
|
||||
user, _ = User.objects.get_or_create()
|
||||
application = DomainApplication.objects.create(
|
||||
creator=user, requested_domain=domain
|
||||
)
|
||||
# skip using the submit method
|
||||
application.status = DomainApplication.SUBMITTED
|
||||
application.approve()
|
||||
|
||||
# should be an information present for this domain
|
||||
self.assertTrue(DomainInformation.objects.get(domain=domain))
|
||||
|
||||
|
||||
class TestInvitations(TestCase):
|
||||
|
||||
"""Test the retrieval of invitations."""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue