Merge branch 'main' into za/1676-require-investigator-da

This commit is contained in:
zandercymatics 2024-03-12 15:34:29 -06:00
commit 2df8713b96
No known key found for this signature in database
GPG key ID: FF4636ABEC9682B7
89 changed files with 2070 additions and 1881 deletions

View file

@ -22,7 +22,7 @@ body:
attributes: attributes:
label: Expected Behavior label: Expected Behavior
description: "Please add a concise description of the behavior you would expect if this issue were not occurring" description: "Please add a concise description of the behavior you would expect if this issue were not occurring"
placeholder: "Example: When submitting a new domain application, the request should be successful, OR if there is a problem with the user's application preventing submission, errors should be enumerated to the user" placeholder: "Example: When submitting a new domain request, the request should be successful, OR if there is a problem with the user's application preventing submission, errors should be enumerated to the user"
validations: validations:
required: true required: true
- type: textarea - type: textarea
@ -33,8 +33,8 @@ body:
How can the issue be reliably reproduced? Feel free to include screenshots or other supporting artifacts How can the issue be reliably reproduced? Feel free to include screenshots or other supporting artifacts
Example: Example:
1. In the test environment, fill out the application for a new domain 1. In the test environment, fill out the domain request for a new domain
2. Click the button to trigger a save/submit on the final page and complete the application 2. Click the button to trigger a save/submit on the final page and complete the domain request
3. See the error 3. See the error
value: | value: |
1. 1.

View file

@ -19,7 +19,7 @@ body:
Example: Example:
As an analyst As an analyst
I want the ability to approve a domain application I want the ability to approve a domain request
so that a request can be fulfilled and a new .gov domain can be provisioned so that a request can be fulfilled and a new .gov domain can be provisioned
value: | value: |
As a As a
@ -36,11 +36,11 @@ body:
Example: Example:
- Application sends an email when analysts approve domain requests - Application sends an email when analysts approve domain requests
- Domain application status is "approved" - Domain request status is "approved"
Example ("given, when, then" format): Example ("given, when, then" format):
Given that I am an analyst who has finished reviewing a domain application Given that I am an analyst who has finished reviewing a domain request
When I click to approve a domain application When I click to approve a domain request
Then the domain provisioning process should be initiated, and the applicant should receive an email update. Then the domain provisioning process should be initiated, and the applicant should receive an email update.
validations: validations:
required: true required: true

41
.github/workflows/test-deploy.yaml vendored Normal file
View file

@ -0,0 +1,41 @@
# This workflow is to for testing a change to our deploy structure and will be deleted when testing finishes
name: Deploy Main
run-name: Run deploy for ${{ github.event.inputs.environment }}
on:
workflow_dispatch:
inputs:
environment:
type: choice
description: Which environment should we run deploy for?
options:
- development
- backup
- ky
- es
- nl
- rh
- za
- gd
- rb
- ko
- ab
- rjm
- dk
jobs:
deploy:
runs-on: ubuntu-latest
env:
CF_USERNAME: CF_${{ github.event.inputs.environment }}_USERNAME
CF_PASSWORD: CF_${{ github.event.inputs.environment }}_PASSWORD
steps:
- name: Deploy to cloud.gov sandbox
uses: cloud-gov/cg-cli-tools@main
with:
cf_username: ${{ secrets[env.CF_USERNAME] }}
cf_password: ${{ secrets[env.CF_PASSWORD] }}
cf_org: cisa-dotgov
cf_space: ${{ github.event.inputs.environment }}
cf_command: "push -f ops/manifests/manifest-${{ github.event.inputs.environment }}.yaml --strategy rolling"

View file

@ -1,4 +1,4 @@
# 15. Use Django-FSM library for domain application state # 15. Use Django-FSM library for domain request state
Date: 2022-11-03 Date: 2022-11-03
@ -10,12 +10,12 @@ Accepted
The applications that registrants submit for domains move through a variety of The applications that registrants submit for domains move through a variety of
different states or stages as they are processed by CISA staff. Traditionally, different states or stages as they are processed by CISA staff. Traditionally,
there would be a “domain application” data model with a “status” field. The there would be a “domain request” data model with a “status” field. The
rules in the application code that control what changes are permitted to the rules in the application code that control what changes are permitted to the
statuses are called “domain logic”. statuses are called “domain logic”.
In a large piece of software, domain logic often spreads around the code base In a large piece of software, domain logic often spreads around the code base
because while handling a single request like “mark this application as because while handling a single request like “mark this domain request as
approved”, requirements can be enforced at many different points during the approved”, requirements can be enforced at many different points during the
process. process.
@ -28,7 +28,7 @@ states and can change states (or “transition”) according to fixed rules.
We will use the django-fsm library to represent the status of our domain We will use the django-fsm library to represent the status of our domain
registration applications as a finite state machine. The library allows us to registration applications as a finite state machine. The library allows us to
list what statuses are possible and describe which state transitions are list what statuses are possible and describe which state transitions are
possible (e.g. Can an approved application ever be marked as “in-process”?). possible (e.g. Can an approved domain request ever be marked as “in-process”?).
## Consequences ## Consequences

View file

@ -8,17 +8,17 @@ Accepted
## Context ## Context
The application form by which registrants apply for a .gov domain is presented over many pages. The domain request form by which registrants apply for a .gov domain is presented over many pages.
Because we use server-side rendering, each page of the application is a unique HTML page with form fields surrounded by a form tag. Because we use server-side rendering, each page of the domain request is a unique HTML page with form fields surrounded by a form tag.
Needing a way to coordinate state between the pages as a user fills in their application, we initially used the Form wizard from [django-formtools](https://django-formtools.readthedocs.io/en/latest/wizard.html). This eventually proved unworkable due to the lack of native ability to have more than one Django form object displayed on a single HTML page. Needing a way to coordinate state between the pages as a user fills in their domain request, we initially used the Form wizard from [django-formtools](https://django-formtools.readthedocs.io/en/latest/wizard.html). This eventually proved unworkable due to the lack of native ability to have more than one Django form object displayed on a single HTML page.
However, a significant portion of the user workflow had already been coded, so it seemed prudent to port some of the formtools logic into our codebase. However, a significant portion of the user workflow had already been coded, so it seemed prudent to port some of the formtools logic into our codebase.
## Decision ## Decision
To maintain each page of the domain application as its own Django view class, inheriting common code from a parent class. To maintain each page of the domain request as its own Django view class, inheriting common code from a parent class.
To maintain Django form and formset class in accordance with the Django models whose data they collect, independently of the pages on which they appear. To maintain Django form and formset class in accordance with the Django models whose data they collect, independently of the pages on which they appear.

View file

@ -9,7 +9,7 @@ Approved
## Context ## Context
Our application needs to be able to send email to applicants for various Our application needs to be able to send email to applicants for various
purposes including notifying them that their application has been submitted. purposes including notifying them that their domain request has been submitted.
We need infrastructure for programmatically sending email. Amazon Web Services We need infrastructure for programmatically sending email. Amazon Web Services
(AWS) provides the Simple Email Service (SES) that can do that. CISA can (AWS) provides the Simple Email Service (SES) that can do that. CISA can
provide access to AWS SES for our application. provide access to AWS SES for our application.

View file

@ -8,8 +8,7 @@ Accepted
## Context ## Context
CISA needs a way to perform administrative actions to manage the new get.gov application as well as the .gov domain CISA needs a way to perform administrative actions to manage the new get.gov application as well as the .gov domain requests submitted. Analysts need to be able to view, review, and approve domain requests. Other
application requests submitted. Analysts need to be able to view, review, and approve domain applications. Other
dashboard views, reports, searches (with filters and sorting) are also highly desired. dashboard views, reports, searches (with filters and sorting) are also highly desired.
## Decision ## Decision

View file

@ -8,13 +8,13 @@ Accepted
## Context ## Context
Historically, the .gov vendor managed initial identity verification and organizational affiliation for users that request a .gov domain. With the new registrar, _any user with a valid Login.gov account_ will be able to make a request. As a primary layer of abuse prevention (i.e., DDoSing the registry program with illegitimate requests), we need a way to stop new users from submitting multiple domain requests before they are known to the .gov registry. In this case, "known" means they have at least one approved domain application or existing domain. Historically, the .gov vendor managed initial identity verification and organizational affiliation for users that request a .gov domain. With the new registrar, _any user with a valid Login.gov account_ will be able to make a request. As a primary layer of abuse prevention (i.e., DDoSing the registry program with illegitimate requests), we need a way to stop new users from submitting multiple domain requests before they are known to the .gov registry. In this case, "known" means they have at least one approved domain request or existing domain.
## Considered Options ## Considered Options
**Option 1:** Users will not be able to submit any new applications if they have 0 prior approved applications OR prior registered .gov domains. We would add a page alert informing the user that they cannot submit their application because they have an application in one of these "3" statuses (Submitted, In Review or Action Needed). They would still be able to create and edit new applications, just not submit them. The benefits of this option are that it would allow users to have multiple applications essentially in "draft mode" that are queued up and ready for submission after they are permitted to submit. **Option 1:** Users will not be able to submit any new applications if they have 0 prior approved applications OR prior registered .gov domains. We would add a page alert informing the user that they cannot submit their application because they have a domain request in one of these "3" statuses (Submitted, In Review or Action Needed). They would still be able to create and edit new applications, just not submit them. The benefits of this option are that it would allow users to have multiple applications essentially in "draft mode" that are queued up and ready for submission after they are permitted to submit.
**Option 2:** Users will not be able to submit any new applications if they have 0 prior approved applications OR prior registered .gov domains. Additionally, we would remove the ability to edit any application with the started/withdrawn/rejected status, or start a new application. The benefit of this option is that a user would not be able to begin an action (submitting an application) that they are not allowed to complete. **Option 2:** Users will not be able to submit any new applications if they have 0 prior approved applications OR prior registered .gov domains. Additionally, we would remove the ability to edit any application with the started/withdrawn/rejected status, or start a new application. The benefit of this option is that a user would not be able to begin an action (submitting a domain request) that they are not allowed to complete.
## Decision ## Decision

View file

@ -18,13 +18,13 @@ Deployment_Node(aws, "AWS GovCloud", "Amazon Web Services Region") {
Deployment_Node(organization, "get.gov organization") { Deployment_Node(organization, "get.gov organization") {
Deployment_Node(sandbox, "sandbox space") { Deployment_Node(sandbox, "sandbox space") {
System_Boundary(dashboard_sandbox, "get.gov registrar") { System_Boundary(dashboard_sandbox, "get.gov registrar") {
Container(getgov_app_sandbox, "Registrar Application", "Python, Django", "Delivers static HTML/CSS and forms") Container(getgov_app_sandbox, "Registrar Domain Request", "Python, Django", "Delivers static HTML/CSS and forms")
ContainerDb(dashboard_db_sandbox, "sandbox PostgreSQL Database", "AWS RDS", "Stores agency information and reports") ContainerDb(dashboard_db_sandbox, "sandbox PostgreSQL Database", "AWS RDS", "Stores agency information and reports")
} }
} }
Deployment_Node(stable, "stable space") { Deployment_Node(stable, "stable space") {
System_Boundary(dashboard_stable, "get.gov registrar") { System_Boundary(dashboard_stable, "get.gov registrar") {
Container(getgov_app_stable, "Registrar Application", "Python, Django", "Delivers static HTML/CSS and forms") Container(getgov_app_stable, "Registrar Domain Request", "Python, Django", "Delivers static HTML/CSS and forms")
ContainerDb(dashboard_db_stable, "stable PostgreSQL Database", "AWS RDS", "Stores agency information and reports") ContainerDb(dashboard_db_stable, "stable PostgreSQL Database", "AWS RDS", "Stores agency information and reports")
} }
} }

View file

@ -3,9 +3,9 @@
This diagram connects the data models along with various workflow stages. This diagram connects the data models along with various workflow stages.
1. The applicant starts the process at `/request` interacting with the 1. The applicant starts the process at `/request` interacting with the
`DomainApplication` object. `DomainRequest` object.
2. The analyst approves the application using the `DomainApplication`'s 2. The analyst approves the domain request using the `DomainRequest`'s
`approve()` method which creates many related objects: `UserDomainRole`, `approve()` method which creates many related objects: `UserDomainRole`,
`Domain`, and `DomainInformation`. `Domain`, and `DomainInformation`.
@ -36,8 +36,8 @@ $ docker run -v $(pwd):$(pwd) -w $(pwd) -it plantuml/plantuml -tsvg model_timeli
allowmixing allowmixing
left to right direction left to right direction
class DomainApplication { class DomainRequest {
Application for a domain Request for a domain
-- --
creator (User) creator (User)
investigator (User) investigator (User)
@ -66,7 +66,7 @@ note left of User
<b>username</b> is the Login UUID <b>username</b> is the Login UUID
end note end note
DomainApplication -l- User : creator, investigator DomainRequest -l- User : creator, investigator
class Contact { class Contact {
Contact info for a person Contact info for a person
@ -80,7 +80,7 @@ class Contact {
-- --
} }
DomainApplication *-r-* Contact : authorizing_official, submitter, other_contacts DomainRequest *-r-* Contact : authorizing_official, submitter, other_contacts
class DraftDomain { class DraftDomain {
Requested domain Requested domain
@ -89,7 +89,7 @@ class DraftDomain {
-- --
} }
DomainApplication -l- DraftDomain : requested_domain DomainRequest -l- DraftDomain : requested_domain
class Domain { class Domain {
Approved domain Approved domain
@ -99,21 +99,21 @@ class Domain {
<b>EPP methods</b> <b>EPP methods</b>
} }
DomainApplication .right[#blue].> Domain : approve() DomainRequest .right[#blue].> Domain : approve()
class DomainInformation { class DomainInformation {
Registrar information on a domain Registrar information on a domain
-- --
domain (Domain) domain (Domain)
domain_application (DomainApplication) domain_request (DomainRequest)
security_email security_email
-- --
Request information... Request information...
} }
DomainInformation -- Domain DomainInformation -- Domain
DomainInformation -- DomainApplication DomainInformation -- DomainRequest
DomainApplication .[#blue].> DomainInformation : approve() DomainRequest .[#blue].> DomainInformation : approve()
class UserDomainRole { class UserDomainRole {
Permissions Permissions
@ -125,7 +125,7 @@ class UserDomainRole {
} }
UserDomainRole -- User UserDomainRole -- User
UserDomainRole -- Domain UserDomainRole -- Domain
DomainApplication .[#blue].> UserDomainRole : approve() DomainRequest .[#blue].> UserDomainRole : approve()
class DomainInvitation { class DomainInvitation {
Email invitations sent Email invitations sent
@ -139,10 +139,10 @@ DomainInvitation -- Domain
DomainInvitation .[#green].> UserDomainRole : User.on_each_login() DomainInvitation .[#green].> UserDomainRole : User.on_each_login()
actor applicant #Red actor applicant #Red
applicant -d-> DomainApplication : **/request** applicant -d-> DomainRequest : **/request**
actor analyst #Blue actor analyst #Blue
analyst -[#blue]-> DomainApplication : **approve()** analyst -[#blue]-> DomainRequest : **approve()**
actor user1 #Green actor user1 #Green
user1 -[#green]-> Domain : **/domain/<id>/nameservers** user1 -[#green]-> Domain : **/domain/<id>/nameservers**

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Before After
Before After

View file

@ -39,8 +39,8 @@ class "registrar.Contact <Registrar>" as registrar.Contact #d6f4e9 {
registrar.Contact -- registrar.User registrar.Contact -- registrar.User
class "registrar.DomainApplication <Registrar>" as registrar.DomainApplication #d6f4e9 { class "registrar.DomainRequest <Registrar>" as registrar.DomainRequest #d6f4e9 {
domain application domain request
-- --
+ id (BigAutoField) + id (BigAutoField)
+ created_at (DateTimeField) + created_at (DateTimeField)
@ -77,15 +77,15 @@ class "registrar.DomainApplication <Registrar>" as registrar.DomainApplication #
# other_contacts (ManyToManyField) # other_contacts (ManyToManyField)
-- --
} }
registrar.DomainApplication -- registrar.User registrar.DomainRequest -- registrar.User
registrar.DomainApplication -- registrar.User registrar.DomainRequest -- registrar.User
registrar.DomainApplication -- registrar.Contact registrar.DomainRequest -- registrar.Contact
registrar.DomainApplication -- registrar.DraftDomain registrar.DomainRequest -- registrar.DraftDomain
registrar.DomainApplication -- registrar.Domain registrar.DomainRequest -- registrar.Domain
registrar.DomainApplication -- registrar.Contact registrar.DomainRequest -- registrar.Contact
registrar.DomainApplication *--* registrar.Website registrar.DomainRequest *--* registrar.Website
registrar.DomainApplication *--* registrar.Website registrar.DomainRequest *--* registrar.Website
registrar.DomainApplication *--* registrar.Contact registrar.DomainRequest *--* registrar.Contact
class "registrar.DomainInformation <Registrar>" as registrar.DomainInformation #d6f4e9 { class "registrar.DomainInformation <Registrar>" as registrar.DomainInformation #d6f4e9 {
@ -95,7 +95,7 @@ class "registrar.DomainInformation <Registrar>" as registrar.DomainInformation #
+ created_at (DateTimeField) + created_at (DateTimeField)
+ updated_at (DateTimeField) + updated_at (DateTimeField)
~ creator (ForeignKey) ~ creator (ForeignKey)
~ domain_application (OneToOneField) ~ domain_request (OneToOneField)
+ organization_type (CharField) + organization_type (CharField)
+ federally_recognized_tribe (BooleanField) + federally_recognized_tribe (BooleanField)
+ state_recognized_tribe (BooleanField) + state_recognized_tribe (BooleanField)
@ -124,7 +124,7 @@ class "registrar.DomainInformation <Registrar>" as registrar.DomainInformation #
-- --
} }
registrar.DomainInformation -- registrar.User registrar.DomainInformation -- registrar.User
registrar.DomainInformation -- registrar.DomainApplication registrar.DomainInformation -- registrar.DomainRequest
registrar.DomainInformation -- registrar.Contact registrar.DomainInformation -- registrar.Contact
registrar.DomainInformation -- registrar.Domain registrar.DomainInformation -- registrar.Domain
registrar.DomainInformation -- registrar.Contact registrar.DomainInformation -- registrar.Contact

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Before After
Before After

View file

@ -78,7 +78,7 @@ Get the secrets from Cloud.gov by running `cf env getgov-YOURSANDBOX`. More info
The endpoint /admin can be used to view and manage site content, including but not limited to user information and the list of current applications in the database. To be able to view and use /admin locally: The endpoint /admin can be used to view and manage site content, including but not limited to user information and the list of current applications in the database. To be able to view and use /admin locally:
1. Login via login.gov 1. Login via login.gov
2. Go to the home page and make sure you can see the part where you can submit an application 2. Go to the home page and make sure you can see the part where you can submit a domain request
3. Go to /admin and it will tell you that UUID is not authorized, copy that UUID for use in 4 3. Go to /admin and it will tell you that UUID is not authorized, copy that UUID for use in 4
4. in src/registrar/fixtures_users.py add to the `ADMINS` list in that file by adding your UUID as your username along with your first and last name. See below: 4. in src/registrar/fixtures_users.py add to the `ADMINS` list in that file by adding your UUID as your username along with your first and last name. See below:
@ -93,14 +93,14 @@ The endpoint /admin can be used to view and manage site content, including but n
] ]
``` ```
5. In the browser, navigate to /admin. To verify that all is working correctly, under "domain applications" you should see fake domains with various fake statuses. 5. In the browser, navigate to /admin. To verify that all is working correctly, under "domain requests" you should see fake domains with various fake statuses.
6. Add an optional email key/value pair 6. Add an optional email key/value pair
### Adding an Analyst to /admin ### Adding an Analyst to /admin
Analysts are a variant of the admin role with limited permissions. The process for adding an Analyst is much the same as adding an admin: Analysts are a variant of the admin role with limited permissions. The process for adding an Analyst is much the same as adding an admin:
1. Login via login.gov (if you already exist as an admin, you will need to create a separate login.gov account for this: i.e. first.last+1@email.com) 1. Login via login.gov (if you already exist as an admin, you will need to create a separate login.gov account for this: i.e. first.last+1@email.com)
2. Go to the home page and make sure you can see the part where you can submit an application 2. Go to the home page and make sure you can see the part where you can submit a domain request
3. Go to /admin and it will tell you that UUID is not authorized, copy that UUID for use in 4 (this will be a different UUID than the one obtained from creating an admin) 3. Go to /admin and it will tell you that UUID is not authorized, copy that UUID for use in 4 (this will be a different UUID than the one obtained from creating an admin)
4. in src/registrar/fixtures_users.py add to the `STAFF` list in that file by adding your UUID as your username along with your first and last name. See below: 4. in src/registrar/fixtures_users.py add to the `STAFF` list in that file by adding your UUID as your username along with your first and last name. See below:
@ -145,7 +145,7 @@ You can change the logging verbosity, if needed. Do a web search for "django log
## Mock data ## Mock data
[load.py](../../src/registrar/management/commands/load.py) called from docker-compose (locally) and reset-db.yml (upper) loads the fixtures from [fixtures_user.py](../../src/registrar/fixtures_users.py) and [fixtures_applications.py](../../src/registrar/fixtures_applications.py), giving you some test data to play with while developing. [load.py](../../src/registrar/management/commands/load.py) called from docker-compose (locally) and reset-db.yml (upper) loads the fixtures from [fixtures_user.py](../../src/registrar/fixtures_users.py) and [fixtures_domain_requests.py](../../src/registrar/fixtures_domain_requests.py), giving you some test data to play with while developing.
See the [database-access README](./database-access.md) for information on how to pull data to update these fixtures. See the [database-access README](./database-access.md) for information on how to pull data to update these fixtures.

View file

@ -24,8 +24,8 @@
## Status Change Approved ## Status Change Approved
- Starting Location: Django Admin - Starting Location: Django Admin
- Workflow: Analyst Admin - Workflow: Analyst Admin
- Workflow Step: Click "Domain applications" -> Click an application in a status of "submitted", "In review", "rejected", or "ineligible" -> Click status dropdown -> (select "approved") -> click "Save" - Workflow Step: Click "domain requests" -> Click a domain request in a status of "submitted", "In review", "rejected", or "ineligible" -> Click status dropdown -> (select "approved") -> click "Save"
- Notes: Note that this will send an email to the submitter (email listed on Your Contact Information). To test this with your own email, you need to create an application, then set the status to "approved". This will send you an email. - Notes: Note that this will send an email to the submitter (email listed on Your Contact Information). To test this with your own email, you need to create a domain request, then set the status to "approved". This will send you an email.
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/status_change_approved.txt) - [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/status_change_approved.txt)
### Status Change Approved Subject ### Status Change Approved Subject
@ -35,8 +35,8 @@
## Status Change Rejected ## Status Change Rejected
- Starting Location: Django Admin - Starting Location: Django Admin
- Workflow: Analyst Admin - Workflow: Analyst Admin
- Workflow Step: Click "Domain applications" -> Click an application in a status of "In review", or "approved" -> Click status dropdown -> (select "rejected") -> click "Save" - Workflow Step: Click "domain requests" -> Click a domain request in a status of "In review", or "approved" -> Click status dropdown -> (select "rejected") -> click "Save"
- Notes: Note that this will send an email to the submitter (email listed on Your Contact Information). To test this with your own email, you need to create an application, then set the status to "in review" (and click save). Then, go back to the same application and set the status to "rejected". This will send you an email. - Notes: Note that this will send an email to the submitter (email listed on Your Contact Information). To test this with your own email, you need to create a domain request, then set the status to "in review" (and click save). Then, go back to the same application and set the status to "rejected". This will send you an email.
- [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/status_change_rejected.txt) - [Email Content](https://github.com/cisagov/manage.get.gov/blob/main/src/registrar/templates/emails/status_change_rejected.txt)
### Status Change Rejected Subject ### Status Change Rejected Subject

View file

@ -114,7 +114,7 @@ that can be used for specific tasks.
## Cloud.gov dashboard ## Cloud.gov dashboard
At <https://dashboard.fr.cloud.gov/applications> there is a list for all of the At <https://dashboard.fr.cloud.gov/applications> there is a list for all of the
applications that a Cloud.gov user has access to. Clicking on an application applications that a Cloud.gov user has access to. Clicking on a domain request
goes to a screen for that individual application, e.g. goes to a screen for that individual application, e.g.
<https://dashboard.fr.cloud.gov/applications/2oBn9LBurIXUNpfmtZCQTCHnxUM/53b88024-1492-46aa-8fb6-1429bdb35f95/summary>. <https://dashboard.fr.cloud.gov/applications/2oBn9LBurIXUNpfmtZCQTCHnxUM/53b88024-1492-46aa-8fb6-1429bdb35f95/summary>.
On that page is a left-hand link for "Log Stream" e.g. On that page is a left-hand link for "Log Stream" e.g.

View file

@ -4,7 +4,7 @@ applications:
buildpacks: buildpacks:
- python_buildpack - python_buildpack
path: ../../src path: ../../src
instances: 1 instances: 2
memory: 512M memory: 512M
stack: cflinuxfs4 stack: cflinuxfs4
timeout: 180 timeout: 180

View file

@ -4,7 +4,7 @@ applications:
buildpacks: buildpacks:
- python_buildpack - python_buildpack
path: ../../src path: ../../src
instances: 1 instances: 2
memory: 512M memory: 512M
stack: cflinuxfs4 stack: cflinuxfs4
timeout: 180 timeout: 180

View file

@ -14,7 +14,7 @@ from django.contrib.contenttypes.models import ContentType
from django.urls import reverse from django.urls import reverse
from dateutil.relativedelta import relativedelta # type: ignore from dateutil.relativedelta import relativedelta # type: ignore
from epplibwrapper.errors import ErrorCode, RegistryError from epplibwrapper.errors import ErrorCode, RegistryError
from registrar.models import Contact, Domain, DomainApplication, DraftDomain, User, Website from registrar.models import Contact, Domain, DomainRequest, DraftDomain, User, Website
from registrar.utility import csv_export from registrar.utility import csv_export
from registrar.utility.errors import FSMApplicationError, FSMErrorCodes from registrar.utility.errors import FSMApplicationError, FSMErrorCodes
from registrar.views.utility.mixins import OrderableFieldsMixin from registrar.views.utility.mixins import OrderableFieldsMixin
@ -70,12 +70,12 @@ class DomainInformationInlineForm(forms.ModelForm):
} }
class DomainApplicationAdminForm(forms.ModelForm): class DomainRequestAdminForm(forms.ModelForm):
"""Custom form to limit transitions to available transitions. """Custom form to limit transitions to available transitions.
This form utilizes the custom widget for its class's ManyToMany UIs.""" This form utilizes the custom widget for its class's ManyToMany UIs."""
class Meta: class Meta:
model = models.DomainApplication model = models.DomainRequest
fields = "__all__" fields = "__all__"
widgets = { widgets = {
"current_websites": NoAutocompleteFilteredSelectMultiple("current_websites", False), "current_websites": NoAutocompleteFilteredSelectMultiple("current_websites", False),
@ -86,29 +86,29 @@ class DomainApplicationAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
application = kwargs.get("instance") domain_request = kwargs.get("instance")
if application and application.pk: if domain_request and domain_request.pk:
current_state = application.status current_state = domain_request.status
# first option in status transitions is current state # first option in status transitions is current state
available_transitions = [(current_state, application.get_status_display())] available_transitions = [(current_state, domain_request.get_status_display())]
if application.investigator is not None: if domain_request.investigator is not None:
transitions = get_available_FIELD_transitions( transitions = get_available_FIELD_transitions(
application, models.DomainApplication._meta.get_field("status") domain_request, models.DomainRequest._meta.get_field("status")
) )
else: else:
transitions = self.get_custom_field_transitions( transitions = self.get_custom_field_transitions(
application, models.DomainApplication._meta.get_field("status") domain_request, models.DomainRequest._meta.get_field("status")
) )
for transition in transitions: for transition in transitions:
available_transitions.append((transition.target, transition.target.label)) available_transitions.append((transition.target, transition.target.label))
# only set the available transitions if the user is not restricted # only set the available transitions if the user is not restricted
# from editing the domain application; otherwise, the form will be # from editing the domain request; otherwise, the form will be
# readonly and the status field will not have a widget # readonly and the status field will not have a widget
if not application.creator.is_restricted(): if not domain_request.creator.is_restricted():
self.fields["status"].widget.choices = available_transitions self.fields["status"].widget.choices = available_transitions
def get_custom_field_transitions(self, instance, field): def get_custom_field_transitions(self, instance, field):
@ -291,8 +291,8 @@ class AdminSortFields:
"alternative_domains": (Website, "website"), "alternative_domains": (Website, "website"),
# == DraftDomain == # # == DraftDomain == #
"requested_domain": (DraftDomain, "name"), "requested_domain": (DraftDomain, "name"),
# == DomainApplication == # # == DomainRequest == #
"domain_application": (DomainApplication, "requested_domain__name"), "domain_request": (DomainRequest, "requested_domain__name"),
# == Domain == # # == Domain == #
"domain": (Domain, "name"), "domain": (Domain, "name"),
"approved_domain": (Domain, "name"), "approved_domain": (Domain, "name"),
@ -539,7 +539,7 @@ class MyUserAdmin(BaseUserAdmin):
def get_search_results(self, request, queryset, search_term): def get_search_results(self, request, queryset, search_term):
""" """
Override for get_search_results. This affects any upstream model using autocomplete_fields, Override for get_search_results. This affects any upstream model using autocomplete_fields,
such as DomainApplication. This is because autocomplete_fields uses an API call to fetch data, such as DomainRequest. This is because autocomplete_fields uses an API call to fetch data,
and this fetch comes from this method. and this fetch comes from this method.
""" """
# Custom filtering logic # Custom filtering logic
@ -553,13 +553,13 @@ class MyUserAdmin(BaseUserAdmin):
request_get = request.GET request_get = request.GET
# The request defines model name and field name. # The request defines model name and field name.
# For instance, model_name could be "DomainApplication" # For instance, model_name could be "DomainRequest"
# and field_name could be "investigator". # and field_name could be "investigator".
model_name = request_get.get("model_name", None) model_name = request_get.get("model_name", None)
field_name = request_get.get("field_name", None) field_name = request_get.get("field_name", None)
# Make sure we're only modifying requests from these models. # Make sure we're only modifying requests from these models.
models_to_target = {"domainapplication"} models_to_target = {"domainrequest"}
if model_name in models_to_target: if model_name in models_to_target:
# Define rules per field # Define rules per field
match field_name: match field_name:
@ -850,7 +850,7 @@ class DomainInformationAdmin(ListHeaderAdmin):
search_help_text = "Search by domain." search_help_text = "Search by domain."
fieldsets = [ fieldsets = [
(None, {"fields": ["creator", "domain_application", "notes"]}), (None, {"fields": ["creator", "domain_request", "notes"]}),
( (
"Type of organization", "Type of organization",
{ {
@ -901,7 +901,7 @@ class DomainInformationAdmin(ListHeaderAdmin):
"type_of_work", "type_of_work",
"more_organization_information", "more_organization_information",
"domain", "domain",
"domain_application", "domain_request",
"submitter", "submitter",
"no_other_contacts_rationale", "no_other_contacts_rationale",
"anything_else", "anything_else",
@ -914,7 +914,7 @@ class DomainInformationAdmin(ListHeaderAdmin):
autocomplete_fields = [ autocomplete_fields = [
"creator", "creator",
"domain_application", "domain_request",
"authorizing_official", "authorizing_official",
"domain", "domain",
"submitter", "submitter",
@ -939,10 +939,10 @@ class DomainInformationAdmin(ListHeaderAdmin):
return readonly_fields # Read-only fields for analysts return readonly_fields # Read-only fields for analysts
class DomainApplicationAdmin(ListHeaderAdmin): class DomainRequestAdmin(ListHeaderAdmin):
"""Custom domain applications admin class.""" """Custom domain requests admin class."""
form = DomainApplicationAdminForm form = DomainRequestAdminForm
class InvestigatorFilter(admin.SimpleListFilter): class InvestigatorFilter(admin.SimpleListFilter):
"""Custom investigator filter that only displays users with the manager role""" """Custom investigator filter that only displays users with the manager role"""
@ -957,7 +957,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
""" """
# Select all investigators that are staff, then order by name and email # Select all investigators that are staff, then order by name and email
privileged_users = ( privileged_users = (
DomainApplication.objects.select_related("investigator") DomainRequest.objects.select_related("investigator")
.filter(investigator__is_staff=True) .filter(investigator__is_staff=True)
.order_by("investigator__first_name", "investigator__last_name", "investigator__email") .order_by("investigator__first_name", "investigator__last_name", "investigator__email")
) )
@ -1138,7 +1138,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
messages.set_level(request, messages.ERROR) messages.set_level(request, messages.ERROR)
messages.error( messages.error(
request, request,
"Could not save DomainApplication. Something went wrong.", "Could not save DomainRequest. Something went wrong.",
) )
return None return None
@ -1150,7 +1150,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
messages.error( messages.error(
request, request,
"This action is not permitted for applications with a restricted creator.", "This action is not permitted for domain requests with a restricted creator.",
) )
return None return None
@ -1164,7 +1164,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
# == Handle non-status changes == # # == Handle non-status changes == #
# Get the original application from the database. # Get the original application from the database.
original_obj = models.DomainApplication.objects.get(pk=obj.pk) original_obj = models.DomainRequest.objects.get(pk=obj.pk)
if obj.status == original_obj.status: if obj.status == original_obj.status:
# If the status hasn't changed, let the base function take care of it # If the status hasn't changed, let the base function take care of it
return super().save_model(request, obj, form, change) return super().save_model(request, obj, form, change)
@ -1187,7 +1187,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
In the event that we should not status change, an error message In the event that we should not status change, an error message
will be displayed. will be displayed.
Returns a tuple: (obj: DomainApplication, should_proceed: bool) Returns a tuple: (obj: DomainRequest, should_proceed: bool)
""" """
should_proceed = True should_proceed = True
@ -1203,7 +1203,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
should_proceed = False should_proceed = False
return should_proceed return should_proceed
application_is_not_approved = obj.status != models.DomainApplication.ApplicationStatus.APPROVED application_is_not_approved = obj.status != models.DomainRequest.DomainRequestStatus.APPROVED
if application_is_not_approved and not obj.domain_is_not_active(): if application_is_not_approved and not obj.domain_is_not_active():
# If an admin tried to set an approved application to # If an admin tried to set an approved application to
# another status and the related domain is already # another status and the related domain is already
@ -1212,7 +1212,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
# shortcut or not as the rules are duplicated on the model, # shortcut or not as the rules are duplicated on the model,
# but the error would be an ugly Django error screen. # but the error would be an ugly Django error screen.
error_message = "This action is not permitted. The domain is already active." error_message = "This action is not permitted. The domain is already active."
elif obj.status == models.DomainApplication.ApplicationStatus.REJECTED and not obj.rejection_reason: elif obj.status == models.DomainRequest.DomainRequestStatus.REJECTED and not obj.rejection_reason:
# This condition should never be triggered. # This condition should never be triggered.
# The opposite of this condition is acceptable (rejected -> other status and rejection_reason) # The opposite of this condition is acceptable (rejected -> other status and rejection_reason)
# because we clean up the rejection reason in the transition in the model. # because we clean up the rejection reason in the transition in the model.
@ -1247,27 +1247,27 @@ class DomainApplicationAdmin(ListHeaderAdmin):
return (obj, should_proceed) return (obj, should_proceed)
def get_status_method_mapping(self, application): def get_status_method_mapping(self, domain_request):
"""Returns what method should be ran given an application object""" """Returns what method should be ran given an application object"""
# Define a per-object mapping # Define a per-object mapping
status_method_mapping = { status_method_mapping = {
models.DomainApplication.ApplicationStatus.STARTED: None, models.DomainRequest.DomainRequestStatus.STARTED: None,
models.DomainApplication.ApplicationStatus.SUBMITTED: application.submit, models.DomainRequest.DomainRequestStatus.SUBMITTED: domain_request.submit,
models.DomainApplication.ApplicationStatus.IN_REVIEW: application.in_review, models.DomainRequest.DomainRequestStatus.IN_REVIEW: domain_request.in_review,
models.DomainApplication.ApplicationStatus.ACTION_NEEDED: application.action_needed, models.DomainRequest.DomainRequestStatus.ACTION_NEEDED: domain_request.action_needed,
models.DomainApplication.ApplicationStatus.APPROVED: application.approve, models.DomainRequest.DomainRequestStatus.APPROVED: domain_request.approve,
models.DomainApplication.ApplicationStatus.WITHDRAWN: application.withdraw, models.DomainRequest.DomainRequestStatus.WITHDRAWN: domain_request.withdraw,
models.DomainApplication.ApplicationStatus.REJECTED: application.reject, models.DomainRequest.DomainRequestStatus.REJECTED: domain_request.reject,
models.DomainApplication.ApplicationStatus.INELIGIBLE: (application.reject_with_prejudice), models.DomainRequest.DomainRequestStatus.INELIGIBLE: (domain_request.reject_with_prejudice),
} }
# Grab the method # Grab the method
return status_method_mapping.get(application.status, None) return status_method_mapping.get(domain_request.status, None)
def get_readonly_fields(self, request, obj=None): def get_readonly_fields(self, request, obj=None):
"""Set the read-only state on form elements. """Set the read-only state on form elements.
We have 2 conditions that determine which fields are read-only: We have 2 conditions that determine which fields are read-only:
admin user permissions and the application creator's status, so admin user permissions and the domain request creator's status, so
we'll use the baseline readonly_fields and extend it as needed. we'll use the baseline readonly_fields and extend it as needed.
""" """
readonly_fields = list(self.readonly_fields) readonly_fields = list(self.readonly_fields)
@ -1292,7 +1292,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
if obj and obj.creator.status == models.User.RESTRICTED: if obj and obj.creator.status == models.User.RESTRICTED:
messages.warning( messages.warning(
request, request,
"Cannot edit an application with a restricted creator.", "Cannot edit a domain request with a restricted creator.",
) )
def change_view(self, request, object_id, form_url="", extra_context=None): def change_view(self, request, object_id, form_url="", extra_context=None):
@ -1336,7 +1336,7 @@ class DomainInformationInline(admin.StackedInline):
autocomplete_fields = [ autocomplete_fields = [
"creator", "creator",
"domain_application", "domain_request",
"authorizing_official", "authorizing_official",
"domain", "domain",
"submitter", "submitter",
@ -1846,6 +1846,6 @@ admin.site.register(models.DraftDomain, DraftDomainAdmin)
admin.site.register(models.Host, MyHostAdmin) admin.site.register(models.Host, MyHostAdmin)
admin.site.register(models.Website, WebsiteAdmin) admin.site.register(models.Website, WebsiteAdmin)
admin.site.register(models.PublicContact, AuditedAdmin) admin.site.register(models.PublicContact, AuditedAdmin)
admin.site.register(models.DomainApplication, DomainApplicationAdmin) admin.site.register(models.DomainRequest, DomainRequestAdmin)
admin.site.register(models.TransitionDomain, TransitionDomainAdmin) admin.site.register(models.TransitionDomain, TransitionDomainAdmin)
admin.site.register(models.VerifiedByStaff, VerifiedByStaffAdmin) admin.site.register(models.VerifiedByStaff, VerifiedByStaffAdmin)

View file

@ -135,6 +135,8 @@ html[data-theme="dark"] {
color: var(--primary-fg); color: var(--primary-fg);
} }
#branding h1, #branding h1,
h1, h2, h3, h1, h2, h3,
.module h2 { .module h2 {

View file

@ -13,18 +13,18 @@ from registrar import views
from registrar.views.admin_views import ExportData from registrar.views.admin_views import ExportData
from registrar.views.application import Step from registrar.views.domain_request import Step
from registrar.views.utility import always_404 from registrar.views.utility import always_404
from api.views import available, get_current_federal, get_current_full from api.views import available, get_current_federal, get_current_full
APPLICATION_NAMESPACE = views.ApplicationWizard.URL_NAMESPACE DOMAIN_REQUEST_NAMESPACE = views.DomainRequestWizard.URL_NAMESPACE
application_urls = [ domain_request_urls = [
path("", views.ApplicationWizard.as_view(), name=""), path("", views.DomainRequestWizard.as_view(), name=""),
path("finished/", views.Finished.as_view(), name="finished"), path("finished/", views.Finished.as_view(), name="finished"),
] ]
# dynamically generate the other application_urls # dynamically generate the other domain_request_urls
for step, view in [ for step, view in [
# add/remove steps here # add/remove steps here
(Step.ORGANIZATION_TYPE, views.OrganizationType), (Step.ORGANIZATION_TYPE, views.OrganizationType),
@ -43,7 +43,7 @@ for step, view in [
(Step.REQUIREMENTS, views.Requirements), (Step.REQUIREMENTS, views.Requirements),
(Step.REVIEW, views.Review), (Step.REVIEW, views.Review),
]: ]:
application_urls.append(path(f"{step}/", view.as_view(), name=step)) domain_request_urls.append(path(f"{step}/", view.as_view(), name=step))
urlpatterns = [ urlpatterns = [
@ -55,28 +55,28 @@ urlpatterns = [
path("export_data/", ExportData.as_view(), name="admin_export_data"), path("export_data/", ExportData.as_view(), name="admin_export_data"),
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
path( path(
"application/<id>/edit/", "domain-request/<id>/edit/",
views.ApplicationWizard.as_view(), views.DomainRequestWizard.as_view(),
name=views.ApplicationWizard.EDIT_URL_NAME, name=views.DomainRequestWizard.EDIT_URL_NAME,
), ),
path( path(
"application/<int:pk>", "domain-request/<int:pk>",
views.ApplicationStatus.as_view(), views.DomainRequestStatus.as_view(),
name="application-status", name="domain-request-status",
), ),
path( path(
"application/<int:pk>/withdraw", "domain-request/<int:pk>/withdraw",
views.ApplicationWithdrawConfirmation.as_view(), views.DomainRequestWithdrawConfirmation.as_view(),
name="application-withdraw-confirmation", name="domain-request-withdraw-confirmation",
), ),
path( path(
"application/<int:pk>/withdrawconfirmed", "domain-request/<int:pk>/withdrawconfirmed",
views.ApplicationWithdrawn.as_view(), views.DomainRequestWithdrawn.as_view(),
name="application-withdrawn", name="domain-request-withdrawn",
), ),
path("health", views.health, name="health"), path("health", views.health, name="health"),
path("openid/", include("djangooidc.urls")), path("openid/", include("djangooidc.urls")),
path("request/", include((application_urls, APPLICATION_NAMESPACE))), path("request/", include((domain_request_urls, DOMAIN_REQUEST_NAMESPACE))),
path("api/v1/available/", available, name="available"), path("api/v1/available/", available, name="available"),
path("api/v1/get-report/current-federal", get_current_federal, name="get-current-federal"), path("api/v1/get-report/current-federal", get_current_federal, name="get-current-federal"),
path("api/v1/get-report/current-full", get_current_full, name="get-current-full"), path("api/v1/get-report/current-full", get_current_full, name="get-current-full"),
@ -138,9 +138,9 @@ urlpatterns = [
name="invitation-delete", name="invitation-delete",
), ),
path( path(
"application/<int:pk>/delete", "domain-request/<int:pk>/delete",
views.DomainApplicationDeleteView.as_view(http_method_names=["post"]), views.DomainRequestDeleteView.as_view(http_method_names=["post"]),
name="application-delete", name="domain-request-delete",
), ),
path( path(
"domain/<int:pk>/users/<int:user_pk>/delete", "domain/<int:pk>/users/<int:user_pk>/delete",

View file

@ -1,10 +1,10 @@
import logging import logging
import random import random
from faker import Faker from faker import Faker
from django.db import transaction
from registrar.models import ( from registrar.models import (
User, User,
DomainApplication, DomainRequest,
DraftDomain, DraftDomain,
Contact, Contact,
Website, Website,
@ -14,9 +14,9 @@ fake = Faker()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class DomainApplicationFixture: class DomainRequestFixture:
""" """
Load domain applications into the database. Load domain requests into the database.
Make sure this class' `load` method is called from `handle` Make sure this class' `load` method is called from `handle`
in management/commands/load.py, then use `./manage.py load` in management/commands/load.py, then use `./manage.py load`
@ -49,27 +49,27 @@ class DomainApplicationFixture:
# }, # },
DA = [ DA = [
{ {
"status": DomainApplication.ApplicationStatus.STARTED, "status": DomainRequest.DomainRequestStatus.STARTED,
"organization_name": "Example - Finished but not submitted", "organization_name": "Example - Finished but not submitted",
}, },
{ {
"status": DomainApplication.ApplicationStatus.SUBMITTED, "status": DomainRequest.DomainRequestStatus.SUBMITTED,
"organization_name": "Example - Submitted but pending investigation", "organization_name": "Example - Submitted but pending investigation",
}, },
{ {
"status": DomainApplication.ApplicationStatus.IN_REVIEW, "status": DomainRequest.DomainRequestStatus.IN_REVIEW,
"organization_name": "Example - In investigation", "organization_name": "Example - In investigation",
}, },
{ {
"status": DomainApplication.ApplicationStatus.IN_REVIEW, "status": DomainRequest.DomainRequestStatus.IN_REVIEW,
"organization_name": "Example - Approved", "organization_name": "Example - Approved",
}, },
{ {
"status": DomainApplication.ApplicationStatus.WITHDRAWN, "status": DomainRequest.DomainRequestStatus.WITHDRAWN,
"organization_name": "Example - Withdrawn", "organization_name": "Example - Withdrawn",
}, },
{ {
"status": DomainApplication.ApplicationStatus.ACTION_NEEDED, "status": DomainRequest.DomainRequestStatus.ACTION_NEEDED,
"organization_name": "Example - Action needed", "organization_name": "Example - Action needed",
}, },
{ {
@ -94,7 +94,7 @@ class DomainApplicationFixture:
return f"{fake.slug()}.gov" return f"{fake.slug()}.gov"
@classmethod @classmethod
def _set_non_foreign_key_fields(cls, da: DomainApplication, app: dict): def _set_non_foreign_key_fields(cls, da: DomainRequest, app: dict):
"""Helper method used by `load`.""" """Helper method used by `load`."""
da.status = app["status"] if "status" in app else "started" da.status = app["status"] if "status" in app else "started"
da.organization_type = app["organization_type"] if "organization_type" in app else "federal" da.organization_type = app["organization_type"] if "organization_type" in app else "federal"
@ -102,7 +102,7 @@ class DomainApplicationFixture:
app["federal_agency"] app["federal_agency"]
if "federal_agency" in app if "federal_agency" in app
# Random choice of agency for selects, used as placeholders for testing. # Random choice of agency for selects, used as placeholders for testing.
else random.choice(DomainApplication.AGENCIES) # nosec else random.choice(DomainRequest.AGENCIES) # nosec
) )
da.submission_date = fake.date() da.submission_date = fake.date()
da.federal_type = ( da.federal_type = (
@ -121,7 +121,7 @@ class DomainApplicationFixture:
da.is_policy_acknowledged = app["is_policy_acknowledged"] if "is_policy_acknowledged" in app else True da.is_policy_acknowledged = app["is_policy_acknowledged"] if "is_policy_acknowledged" in app else True
@classmethod @classmethod
def _set_foreign_key_fields(cls, da: DomainApplication, app: dict, user: User): def _set_foreign_key_fields(cls, da: DomainRequest, app: dict, user: User):
"""Helper method used by `load`.""" """Helper method used by `load`."""
if not da.investigator: if not da.investigator:
da.investigator = User.objects.get(username=user.username) if "investigator" in app else None da.investigator = User.objects.get(username=user.username) if "investigator" in app else None
@ -145,7 +145,7 @@ class DomainApplicationFixture:
da.requested_domain = DraftDomain.objects.create(name=cls.fake_dot_gov()) da.requested_domain = DraftDomain.objects.create(name=cls.fake_dot_gov())
@classmethod @classmethod
def _set_many_to_many_relations(cls, da: DomainApplication, app: dict): def _set_many_to_many_relations(cls, da: DomainRequest, app: dict):
"""Helper method used by `load`.""" """Helper method used by `load`."""
if "other_contacts" in app: if "other_contacts" in app:
for contact in app["other_contacts"]: for contact in app["other_contacts"]:
@ -176,27 +176,19 @@ class DomainApplicationFixture:
@classmethod @classmethod
def load(cls): def load(cls):
"""Creates domain applications for each user in the database.""" """Creates domain requests for each user in the database."""
logger.info("Going to load %s domain applications" % len(cls.DA)) logger.info("Going to load %s domain requests" % len(cls.DA))
try: try:
users = list(User.objects.all()) # force evaluation to catch db errors users = list(User.objects.all()) # force evaluation to catch db errors
except Exception as e: except Exception as e:
logger.warning(e) logger.warning(e)
return return
# Lumped under .atomic to ensure we don't make redundant DB calls.
# This bundles them all together, and then saves it in a single call.
with transaction.atomic():
cls._create_applications(users)
@classmethod
def _create_applications(cls, users):
"""Creates DomainApplications given a list of users"""
for user in users: for user in users:
logger.debug("Loading domain applications for %s" % user) logger.debug("Loading domain requests for %s" % user)
for app in cls.DA: for app in cls.DA:
try: try:
da, _ = DomainApplication.objects.get_or_create( da, _ = DomainRequest.objects.get_or_create(
creator=user, creator=user,
organization_name=app["organization_name"], organization_name=app["organization_name"],
) )
@ -208,7 +200,7 @@ class DomainApplicationFixture:
logger.warning(e) logger.warning(e)
class DomainFixture(DomainApplicationFixture): class DomainFixture(DomainRequestFixture):
"""Create one domain and permissions on it for each user.""" """Create one domain and permissions on it for each user."""
@classmethod @classmethod
@ -219,30 +211,14 @@ class DomainFixture(DomainApplicationFixture):
logger.warning(e) logger.warning(e)
return return
# Lumped under .atomic to ensure we don't make redundant DB calls.
# This bundles them all together, and then saves it in a single call.
with transaction.atomic():
# approve each user associated with `in review` status domains
DomainFixture._approve_applications(users)
@staticmethod
def _approve_applications(users):
"""Approves all provided applications if they are in the state in_review"""
for user in users: for user in users:
application = DomainApplication.objects.filter( # approve one of each users in review status domains
creator=user, status=DomainApplication.ApplicationStatus.IN_REVIEW domain_request = DomainRequest.objects.filter(
creator=user, status=DomainRequest.DomainRequestStatus.IN_REVIEW
).last() ).last()
logger.debug(f"Approving {application} for {user}") logger.debug(f"Approving {domain_request} for {user}")
# We don't want fixtures sending out real emails to # We don't want fixtures sending out real emails to
# fake email addresses, so we just skip that and log it instead # fake email addresses, so we just skip that and log it instead
domain_request.approve(send_email=False)
# All approvals require an investigator, so if there is none, domain_request.save()
# assign one.
if application.investigator is None:
# All "users" in fixtures have admin perms per prior config.
# No need to check for that.
application.investigator = random.choice(users) # nosec
application.approve(send_email=False)
application.save()

View file

@ -1,4 +1,4 @@
from .application_wizard import * from .domain_request_wizard import *
from .domain import ( from .domain import (
DomainAddUserForm, DomainAddUserForm,
NameserverFormset, NameserverFormset,

View file

@ -10,7 +10,7 @@ from django.core.validators import RegexValidator, MaxLengthValidator
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.db.models.fields.related import ForeignObjectRel from django.db.models.fields.related import ForeignObjectRel
from registrar.models import Contact, DomainApplication, DraftDomain, Domain from registrar.models import Contact, DomainRequest, DraftDomain, Domain
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
@ -21,7 +21,7 @@ class RegistrarForm(forms.Form):
""" """
A common set of methods and configuration. A common set of methods and configuration.
The registrar's domain application is several pages of "steps". The registrar's domain request is several pages of "steps".
Each step is an HTML form containing one or more Django "forms". Each step is an HTML form containing one or more Django "forms".
Subclass this class to create new forms. Subclass this class to create new forms.
@ -29,11 +29,11 @@ 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 an application object # save a reference to a domain request object
self.application = kwargs.pop("application", None) self.domain_request = kwargs.pop("domain_request", None)
super(RegistrarForm, self).__init__(*args, **kwargs) super(RegistrarForm, self).__init__(*args, **kwargs)
def to_database(self, obj: DomainApplication | Contact): def to_database(self, obj: DomainRequest | Contact):
""" """
Adds this form's cleaned data to `obj` and saves `obj`. Adds this form's cleaned data to `obj` and saves `obj`.
@ -46,7 +46,7 @@ class RegistrarForm(forms.Form):
obj.save() obj.save()
@classmethod @classmethod
def from_database(cls, obj: DomainApplication | Contact | None): def from_database(cls, obj: DomainRequest | Contact | None):
"""Returns a dict of form field values gotten from `obj`.""" """Returns a dict of form field values gotten from `obj`."""
if obj is None: if obj is None:
return {} return {}
@ -61,8 +61,8 @@ class RegistrarFormSet(forms.BaseFormSet):
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
# save a reference to an application object # save a reference to an domain_request object
self.application = kwargs.pop("application", None) self.domain_request = kwargs.pop("domain_request", None)
super(RegistrarFormSet, self).__init__(*args, **kwargs) super(RegistrarFormSet, self).__init__(*args, **kwargs)
# quick workaround to ensure that the HTML `required` # quick workaround to ensure that the HTML `required`
# attribute shows up on required fields for any forms # attribute shows up on required fields for any forms
@ -85,7 +85,7 @@ class RegistrarFormSet(forms.BaseFormSet):
"""Code to run before an item in the formset is created in the database.""" """Code to run before an item in the formset is created in the database."""
return cleaned return cleaned
def to_database(self, obj: DomainApplication): def to_database(self, obj: DomainRequest):
""" """
Adds this form's cleaned data to `obj` and saves `obj`. Adds this form's cleaned data to `obj` and saves `obj`.
@ -97,7 +97,7 @@ class RegistrarFormSet(forms.BaseFormSet):
def _to_database( def _to_database(
self, self,
obj: DomainApplication, obj: DomainRequest,
join: str, join: str,
should_delete: Callable, should_delete: Callable,
pre_update: Callable, pre_update: Callable,
@ -137,14 +137,14 @@ class RegistrarFormSet(forms.BaseFormSet):
if should_delete(cleaned): if should_delete(cleaned):
if hasattr(db_obj, "has_more_than_one_join") and db_obj.has_more_than_one_join(related_name): if hasattr(db_obj, "has_more_than_one_join") and db_obj.has_more_than_one_join(related_name):
# Remove the specific relationship without deleting the object # Remove the specific relationship without deleting the object
getattr(db_obj, related_name).remove(self.application) getattr(db_obj, related_name).remove(self.domain_request)
else: else:
# If there are no other relationships, delete the object # If there are no other relationships, delete the object
db_obj.delete() db_obj.delete()
else: else:
if hasattr(db_obj, "has_more_than_one_join") and db_obj.has_more_than_one_join(related_name): if hasattr(db_obj, "has_more_than_one_join") and db_obj.has_more_than_one_join(related_name):
# create a new db_obj and disconnect existing one # create a new db_obj and disconnect existing one
getattr(db_obj, related_name).remove(self.application) getattr(db_obj, related_name).remove(self.domain_request)
kwargs = pre_create(db_obj, cleaned) kwargs = pre_create(db_obj, cleaned)
getattr(obj, join).create(**kwargs) getattr(obj, join).create(**kwargs)
else: else:
@ -163,15 +163,15 @@ class RegistrarFormSet(forms.BaseFormSet):
return query.values() return query.values()
@classmethod @classmethod
def from_database(cls, obj: DomainApplication, join: str, on_fetch: Callable): def from_database(cls, obj: DomainRequest, join: str, on_fetch: Callable):
"""Returns a dict of form field values gotten from `obj`.""" """Returns a dict of form field values gotten from `obj`."""
return on_fetch(getattr(obj, join).order_by("created_at")) # order matters return on_fetch(getattr(obj, join).order_by("created_at")) # order matters
class OrganizationTypeForm(RegistrarForm): class OrganizationTypeForm(RegistrarForm):
organization_type = forms.ChoiceField( organization_type = forms.ChoiceField(
# use the long names in the application form # use the long names in the domain request form
choices=DomainApplication.OrganizationChoicesVerbose.choices, choices=DomainRequest.OrganizationChoicesVerbose.choices,
widget=forms.RadioSelect, widget=forms.RadioSelect,
error_messages={"required": "Select the type of organization you represent."}, error_messages={"required": "Select the type of organization you represent."},
) )
@ -201,7 +201,7 @@ class TribalGovernmentForm(RegistrarForm):
# into a link. There should be no user-facing input in the # into a link. There should be no user-facing input in the
# HTML indicated here. # HTML indicated here.
mark_safe( # nosec mark_safe( # nosec
"You cant complete this application yet. " "You cant complete this domain request yet. "
"Only tribes recognized by the U.S. federal government " "Only tribes recognized by the U.S. federal government "
"or by a U.S. state government are eligible for .gov " "or by a U.S. state government are eligible for .gov "
'domains. Use our <a href="{}">contact form</a> to ' 'domains. Use our <a href="{}">contact form</a> to '
@ -215,7 +215,7 @@ class TribalGovernmentForm(RegistrarForm):
class OrganizationFederalForm(RegistrarForm): class OrganizationFederalForm(RegistrarForm):
federal_type = forms.ChoiceField( federal_type = forms.ChoiceField(
choices=DomainApplication.BranchChoices.choices, choices=DomainRequest.BranchChoices.choices,
widget=forms.RadioSelect, widget=forms.RadioSelect,
error_messages={"required": ("Select the part of the federal government your organization is in.")}, error_messages={"required": ("Select the part of the federal government your organization is in.")},
) )
@ -251,7 +251,7 @@ class OrganizationContactForm(RegistrarForm):
# it is a federal agency. Use clean to check programatically # it is a federal agency. Use clean to check programatically
# if it has been filled in when required. # if it has been filled in when required.
required=False, required=False,
choices=[("", "--Select--")] + DomainApplication.AGENCY_CHOICES, choices=[("", "--Select--")] + DomainRequest.AGENCY_CHOICES,
) )
organization_name = forms.CharField( organization_name = forms.CharField(
label="Organization name", label="Organization name",
@ -271,7 +271,7 @@ class OrganizationContactForm(RegistrarForm):
) )
state_territory = forms.ChoiceField( state_territory = forms.ChoiceField(
label="State, territory, or military post", label="State, territory, or military post",
choices=[("", "--Select--")] + DomainApplication.StateTerritoryChoices.choices, choices=[("", "--Select--")] + DomainRequest.StateTerritoryChoices.choices,
error_messages={ error_messages={
"required": ("Select the state, territory, or military post where your organization is located.") "required": ("Select the state, territory, or military post where your organization is located.")
}, },
@ -294,16 +294,16 @@ class OrganizationContactForm(RegistrarForm):
def clean_federal_agency(self): def clean_federal_agency(self):
"""Require something to be selected when this is a federal agency.""" """Require something to be selected when this is a federal agency."""
federal_agency = self.cleaned_data.get("federal_agency", None) federal_agency = self.cleaned_data.get("federal_agency", None)
# need the application object to know if this is federal # need the domain request object to know if this is federal
if self.application is None: if self.domain_request is None:
# hmm, no saved application object?, default require the agency # hmm, no saved domain request object?, default require the agency
if not federal_agency: if not federal_agency:
# no answer was selected # no answer was selected
raise forms.ValidationError( raise forms.ValidationError(
"Select the federal agency your organization is in.", "Select the federal agency your organization is in.",
code="required", code="required",
) )
if self.application.is_federal(): if self.domain_request.is_federal():
if not federal_agency: if not federal_agency:
# no answer was selected # no answer was selected
raise forms.ValidationError( raise forms.ValidationError(
@ -390,7 +390,7 @@ class BaseCurrentSitesFormSet(RegistrarFormSet):
website = cleaned.get("website", "") website = cleaned.get("website", "")
return website.strip() == "" return website.strip() == ""
def to_database(self, obj: DomainApplication): def to_database(self, obj: DomainRequest):
# If we want to test against multiple joins for a website object, replace the empty array # If we want to test against multiple joins for a website object, replace the empty array
# and change the JOIN in the models to allow for reverse references # and change the JOIN in the models to allow for reverse references
self._to_database(obj, self.JOIN, self.should_delete, self.pre_update, self.pre_create) self._to_database(obj, self.JOIN, self.should_delete, self.pre_update, self.pre_create)
@ -444,7 +444,7 @@ class BaseAlternativeDomainFormSet(RegistrarFormSet):
else: else:
return {} return {}
def to_database(self, obj: DomainApplication): def to_database(self, obj: DomainRequest):
# If we want to test against multiple joins for a website object, replace the empty array and # If we want to test against multiple joins for a website object, replace the empty array and
# change the JOIN in the models to allow for reverse references # change the JOIN in the models to allow for reverse references
self._to_database(obj, self.JOIN, self.should_delete, self.pre_update, self.pre_create) self._to_database(obj, self.JOIN, self.should_delete, self.pre_update, self.pre_create)
@ -530,7 +530,7 @@ class YourContactForm(RegistrarForm):
if not self.is_valid(): if not self.is_valid():
return return
contact = getattr(obj, "submitter", None) contact = getattr(obj, "submitter", None)
if contact is not None and not contact.has_more_than_one_join("submitted_applications"): if contact is not None and not contact.has_more_than_one_join("submitted_domain_requests"):
# if contact exists in the database and is not joined to other entities # if contact exists in the database and is not joined to other entities
super().to_database(contact) super().to_database(contact)
else: else:
@ -578,13 +578,13 @@ class OtherContactsYesNoForm(RegistrarForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""Extend the initialization of the form from RegistrarForm __init__""" """Extend the initialization of the form from RegistrarForm __init__"""
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# set the initial value based on attributes of application # set the initial value based on attributes of domain request
if self.application and self.application.has_other_contacts(): if self.domain_request and self.domain_request.has_other_contacts():
initial_value = True initial_value = True
elif self.application and self.application.has_rationale(): elif self.domain_request and self.domain_request.has_rationale():
initial_value = False initial_value = False
else: else:
# No pre-selection for new applications # No pre-selection for new domain requests
initial_value = None initial_value = None
self.fields["has_other_contacts"] = forms.TypedChoiceField( self.fields["has_other_contacts"] = forms.TypedChoiceField(
@ -687,7 +687,7 @@ class BaseOtherContactsFormSet(RegistrarFormSet):
this case, all forms in formset are marked for deletion. Both of these conditions this case, all forms in formset are marked for deletion. Both of these conditions
must co-exist. must co-exist.
Also, other_contacts have db relationships to multiple db objects. When attempting Also, other_contacts have db relationships to multiple db objects. When attempting
to delete an other_contact from an application, those db relationships must be to delete an other_contact from a domain request, those db relationships must be
tested and handled. tested and handled.
""" """
@ -701,7 +701,7 @@ class BaseOtherContactsFormSet(RegistrarFormSet):
Override __init__ for RegistrarFormSet. Override __init__ for RegistrarFormSet.
""" """
self.formset_data_marked_for_deletion = False self.formset_data_marked_for_deletion = False
self.application = kwargs.pop("application", None) self.domain_request = kwargs.pop("domain_request", None)
super(RegistrarFormSet, self).__init__(*args, **kwargs) super(RegistrarFormSet, self).__init__(*args, **kwargs)
# quick workaround to ensure that the HTML `required` # quick workaround to ensure that the HTML `required`
# attribute shows up on required fields for the first form # attribute shows up on required fields for the first form
@ -722,7 +722,7 @@ class BaseOtherContactsFormSet(RegistrarFormSet):
cleaned.pop("DELETE") cleaned.pop("DELETE")
return cleaned return cleaned
def to_database(self, obj: DomainApplication): def to_database(self, obj: DomainRequest):
self._to_database(obj, self.JOIN, self.should_delete, self.pre_update, self.pre_create) self._to_database(obj, self.JOIN, self.should_delete, self.pre_update, self.pre_create)
@classmethod @classmethod

View file

@ -5,7 +5,7 @@ from auditlog.context import disable_auditlog # type: ignore
from registrar.fixtures_users import UserFixture from registrar.fixtures_users import UserFixture
from registrar.fixtures_applications import DomainApplicationFixture, DomainFixture from registrar.fixtures_domain_requests import DomainRequestFixture, DomainFixture
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -16,6 +16,6 @@ class Command(BaseCommand):
# https://github.com/jazzband/django-auditlog/issues/17 # https://github.com/jazzband/django-auditlog/issues/17
with disable_auditlog(): with disable_auditlog():
UserFixture.load() UserFixture.load()
DomainApplicationFixture.load() DomainRequestFixture.load()
DomainFixture.load() DomainFixture.load()
logger.info("All fixtures loaded.") logger.info("All fixtures loaded.")

View file

@ -15,7 +15,7 @@ from registrar.management.commands.utility.terminal_helper import (
TerminalHelper, TerminalHelper,
) )
from registrar.models.contact import Contact from registrar.models.contact import Contact
from registrar.models.domain_application import DomainApplication from registrar.models.domain_request import DomainRequest
from registrar.models.domain_information import DomainInformation from registrar.models.domain_information import DomainInformation
from registrar.models.user import User from registrar.models.user import User
@ -817,9 +817,9 @@ class Command(BaseCommand):
raise Exception(f"Domain {existing_domain} wants to be added" "but doesn't exist in the DB") raise Exception(f"Domain {existing_domain} wants to be added" "but doesn't exist in the DB")
invitation.save() invitation.save()
valid_org_choices = [(name, value) for name, value in DomainApplication.OrganizationChoices.choices] valid_org_choices = [(name, value) for name, value in DomainRequest.OrganizationChoices.choices]
valid_fed_choices = [value for name, value in DomainApplication.BranchChoices.choices] valid_fed_choices = [value for name, value in DomainRequest.BranchChoices.choices]
valid_agency_choices = DomainApplication.AGENCIES valid_agency_choices = DomainRequest.AGENCIES
# ====================================================== # ======================================================
# ================= DOMAIN INFORMATION ================= # ================= DOMAIN INFORMATION =================
logger.info( logger.info(

View file

@ -0,0 +1,110 @@
# Generated by Django 4.2.10 on 2024-03-12 16:50
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("registrar", "0072_alter_publiccontact_fax_alter_publiccontact_voice"),
]
operations = [
migrations.AlterField(
model_name="domainapplication",
name="approved_domain",
field=models.OneToOneField(
blank=True,
help_text="The approved domain",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="domain_request",
to="registrar.domain",
),
),
migrations.AlterField(
model_name="domainapplication",
name="creator",
field=models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="domain_requests_created",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="domainapplication",
name="investigator",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="domain_requests_investigating",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AlterField(
model_name="domainapplication",
name="other_contacts",
field=models.ManyToManyField(
blank=True, related_name="contact_domain_requests", to="registrar.contact", verbose_name="contacts"
),
),
migrations.AlterField(
model_name="domainapplication",
name="requested_domain",
field=models.OneToOneField(
blank=True,
help_text="The requested domain",
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="domain_request",
to="registrar.draftdomain",
),
),
migrations.AlterField(
model_name="domainapplication",
name="submitter",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="submitted_domain_requests",
to="registrar.contact",
),
),
migrations.AlterField(
model_name="domaininformation",
name="domain_application",
field=models.OneToOneField(
blank=True,
help_text="Associated domain request",
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="DomainRequest_info",
to="registrar.domainapplication",
),
),
migrations.AlterField(
model_name="domaininformation",
name="other_contacts",
field=models.ManyToManyField(
blank=True,
related_name="contact_domain_requests_information",
to="registrar.contact",
verbose_name="contacts",
),
),
migrations.AlterField(
model_name="domaininformation",
name="submitter",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="submitted_domain_requests_information",
to="registrar.contact",
),
),
]

View file

@ -0,0 +1,22 @@
# Generated by Django 4.2.10 on 2024-03-12 16:53
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("registrar", "0073_alter_domainapplication_approved_domain_and_more"),
]
operations = [
migrations.RenameModel(
old_name="DomainApplication",
new_name="DomainRequest",
),
migrations.RenameField(
model_name="domaininformation",
old_name="domain_application",
new_name="domain_request",
),
]

View file

@ -0,0 +1,37 @@
# This migration creates the create_full_access_group and create_cisa_analyst_group groups
# It is dependent on 0035 (which populates ContentType and Permissions)
# If permissions on the groups need changing, edit CISA_ANALYST_GROUP_PERMISSIONS
# in the user_group model then:
# [NOT RECOMMENDED]
# step 1: docker-compose exec app ./manage.py migrate --fake registrar 0035_contenttypes_permissions
# step 2: docker-compose exec app ./manage.py migrate registrar 0036_create_groups
# step 3: fake run the latest migration in the migrations list
# [RECOMMENDED]
# Alternatively:
# step 1: duplicate the migration that loads data
# step 2: docker-compose exec app ./manage.py migrate
from django.db import migrations
from registrar.models import UserGroup
from typing import Any
# For linting: RunPython expects a function reference,
# so let's give it one
def create_groups(apps, schema_editor) -> Any:
UserGroup.create_cisa_analyst_group(apps, schema_editor)
UserGroup.create_full_access_group(apps, schema_editor)
class Migration(migrations.Migration):
dependencies = [
("registrar", "0074_rename_domainapplication_domainrequest_and_more"),
]
operations = [
migrations.RunPython(
create_groups,
reverse_code=migrations.RunPython.noop,
atomic=True,
),
]

View file

@ -1,6 +1,6 @@
from auditlog.registry import auditlog # type: ignore from auditlog.registry import auditlog # type: ignore
from .contact import Contact from .contact import Contact
from .domain_application import DomainApplication from .domain_request import DomainRequest
from .domain_information import DomainInformation from .domain_information import DomainInformation
from .domain import Domain from .domain import Domain
from .draft_domain import DraftDomain from .draft_domain import DraftDomain
@ -17,7 +17,7 @@ from .verified_by_staff import VerifiedByStaff
__all__ = [ __all__ = [
"Contact", "Contact",
"DomainApplication", "DomainRequest",
"DomainInformation", "DomainInformation",
"Domain", "Domain",
"DraftDomain", "DraftDomain",
@ -34,7 +34,7 @@ __all__ = [
] ]
auditlog.register(Contact) auditlog.register(Contact)
auditlog.register(DomainApplication) auditlog.register(DomainRequest)
auditlog.register(Domain) auditlog.register(Domain)
auditlog.register(DraftDomain) auditlog.register(DraftDomain)
auditlog.register(DomainInvitation) auditlog.register(DomainInvitation)

View file

@ -897,8 +897,8 @@ class Domain(TimeStampedModel, DomainHelper):
def security_contact(self, contact: PublicContact): def security_contact(self, contact: PublicContact):
"""makes the contact in the registry, """makes the contact in the registry,
for security the public contact should have the org or registrant information for security the public contact should have the org or registrant information
from domain information (not domain application) from domain information (not domain request)
and should have the security email from DomainApplication""" and should have the security email from DomainRequest"""
logger.info("making security contact in registry") logger.info("making security contact in registry")
self._set_singleton_contact(contact, expectedType=contact.ContactTypeChoices.SECURITY) self._set_singleton_contact(contact, expectedType=contact.ContactTypeChoices.SECURITY)

View file

@ -2,7 +2,7 @@ from __future__ import annotations
from django.db import transaction from django.db import transaction
from registrar.models.utility.domain_helper import DomainHelper from registrar.models.utility.domain_helper import DomainHelper
from .domain_application import DomainApplication from .domain_request import DomainRequest
from .utility.time_stamped_model import TimeStampedModel from .utility.time_stamped_model import TimeStampedModel
import logging import logging
@ -15,22 +15,22 @@ logger = logging.getLogger(__name__)
class DomainInformation(TimeStampedModel): class DomainInformation(TimeStampedModel):
"""A registrant's domain information for that domain, exported from """A registrant's domain information for that domain, exported from
DomainApplication. We use these field from DomainApplication with few exceptions DomainRequest. We use these field from DomainRequest with few exceptions
which are 'removed' via pop at the bottom of this file. Most of design for domain 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 management's user information are based on domain_request, but we cannot change
the application once approved, so copying them that way we can make changes the domain request once approved, so copying them that way we can make changes
after its approved. Most fields here are copied from Application.""" after its approved. Most fields here are copied from DomainRequest."""
StateTerritoryChoices = DomainApplication.StateTerritoryChoices StateTerritoryChoices = DomainRequest.StateTerritoryChoices
# use the short names in Django admin # use the short names in Django admin
OrganizationChoices = DomainApplication.OrganizationChoices OrganizationChoices = DomainRequest.OrganizationChoices
BranchChoices = DomainApplication.BranchChoices BranchChoices = DomainRequest.BranchChoices
AGENCY_CHOICES = DomainApplication.AGENCY_CHOICES AGENCY_CHOICES = DomainRequest.AGENCY_CHOICES
# This is the application user who created this application. The contact # This is the domain request user who created this domain request. The contact
# information that they gave is in the `submitter` field # information that they gave is in the `submitter` field
creator = models.ForeignKey( creator = models.ForeignKey(
"registrar.User", "registrar.User",
@ -38,13 +38,13 @@ class DomainInformation(TimeStampedModel):
related_name="information_created", related_name="information_created",
) )
domain_application = models.OneToOneField( domain_request = models.OneToOneField(
"registrar.DomainApplication", "registrar.DomainRequest",
on_delete=models.PROTECT, on_delete=models.PROTECT,
blank=True, blank=True,
null=True, null=True,
related_name="domainapplication_info", related_name="DomainRequest_info",
help_text="Associated domain application", help_text="Associated domain request",
unique=True, unique=True,
) )
@ -163,13 +163,13 @@ class DomainInformation(TimeStampedModel):
help_text="Domain to which this information belongs", help_text="Domain to which this information belongs",
) )
# This is the contact information provided by the applicant. The # This is the contact information provided by the domain requestor. The
# application user who created it is in the `creator` field. # user who created the domain request is in the `creator` field.
submitter = models.ForeignKey( submitter = models.ForeignKey(
"registrar.Contact", "registrar.Contact",
null=True, null=True,
blank=True, blank=True,
related_name="submitted_applications_information", related_name="submitted_domain_requests_information",
on_delete=models.PROTECT, on_delete=models.PROTECT,
) )
@ -182,7 +182,7 @@ class DomainInformation(TimeStampedModel):
other_contacts = models.ManyToManyField( other_contacts = models.ManyToManyField(
"registrar.Contact", "registrar.Contact",
blank=True, blank=True,
related_name="contact_applications_information", related_name="contact_domain_requests_information",
verbose_name="contacts", verbose_name="contacts",
) )
@ -220,25 +220,25 @@ class DomainInformation(TimeStampedModel):
return "" return ""
@classmethod @classmethod
def create_from_da(cls, domain_application: DomainApplication, domain=None): def create_from_da(cls, domain_request: DomainRequest, domain=None):
"""Takes in a DomainApplication and converts it into DomainInformation""" """Takes in a DomainRequest and converts it into DomainInformation"""
# Throw an error if we get None - we can't create something from nothing # Throw an error if we get None - we can't create something from nothing
if domain_application is None: if domain_request is None:
raise ValueError("The provided DomainApplication is None") raise ValueError("The provided DomainRequest is None")
# Throw an error if the da doesn't have an id # Throw an error if the da doesn't have an id
if not hasattr(domain_application, "id"): if not hasattr(domain_request, "id"):
raise ValueError("The provided DomainApplication has no id") raise ValueError("The provided DomainRequest has no id")
# check if we have a record that corresponds with the domain # check if we have a record that corresponds with the domain
# application, if so short circuit the create # domain_request, if so short circuit the create
existing_domain_info = cls.objects.filter(domain_application__id=domain_application.id).first() existing_domain_info = cls.objects.filter(domain_request__id=domain_request.id).first()
if existing_domain_info: if existing_domain_info:
return existing_domain_info return existing_domain_info
# Get the fields that exist on both DomainApplication and DomainInformation # Get the fields that exist on both DomainRequest and DomainInformation
common_fields = DomainHelper.get_common_fields(DomainApplication, DomainInformation) common_fields = DomainHelper.get_common_fields(DomainRequest, DomainInformation)
# Get a list of all many_to_many relations on DomainInformation (needs to be saved differently) # Get a list of all many_to_many relations on DomainInformation (needs to be saved differently)
info_many_to_many_fields = DomainInformation._get_many_to_many_fields() info_many_to_many_fields = DomainInformation._get_many_to_many_fields()
@ -249,11 +249,11 @@ class DomainInformation(TimeStampedModel):
for field in common_fields: for field in common_fields:
# If the field isn't many_to_many, populate the da_dict. # If the field isn't many_to_many, populate the da_dict.
# If it is, populate da_many_to_many_dict as we need to save this later. # If it is, populate da_many_to_many_dict as we need to save this later.
if hasattr(domain_application, field): if hasattr(domain_request, field):
if field not in info_many_to_many_fields: if field not in info_many_to_many_fields:
da_dict[field] = getattr(domain_application, field) da_dict[field] = getattr(domain_request, field)
else: else:
da_many_to_many_dict[field] = getattr(domain_application, field).all() da_many_to_many_dict[field] = getattr(domain_request, field).all()
# This will not happen in normal code flow, but having some redundancy doesn't hurt. # This will not happen in normal code flow, but having some redundancy doesn't hurt.
# da_dict should not have "id" under any circumstances. # da_dict should not have "id" under any circumstances.
@ -266,8 +266,8 @@ class DomainInformation(TimeStampedModel):
# Create a placeholder DomainInformation object # Create a placeholder DomainInformation object
domain_info = DomainInformation(**da_dict) domain_info = DomainInformation(**da_dict)
# Add the domain_application and domain fields # Add the domain_request and domain fields
domain_info.domain_application = domain_application domain_info.domain_request = domain_request
if domain: if domain:
domain_info.domain = domain domain_info.domain = domain

View file

@ -9,7 +9,7 @@ from django.db import models
from django_fsm import FSMField, transition # type: ignore from django_fsm import FSMField, transition # type: ignore
from django.utils import timezone from django.utils import timezone
from registrar.models.domain import Domain from registrar.models.domain import Domain
from registrar.utility.errors import FSMApplicationError, FSMErrorCodes from registrar.utility.errors import FSMdomain_requestError, FSMErrorCodes
from .utility.time_stamped_model import TimeStampedModel from .utility.time_stamped_model import TimeStampedModel
from ..utility.email import send_templated_email, EmailSendingError from ..utility.email import send_templated_email, EmailSendingError
@ -18,11 +18,11 @@ from itertools import chain
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class DomainApplication(TimeStampedModel): class DomainRequest(TimeStampedModel):
"""A registrant's application for a new domain.""" """A registrant's request for a new domain."""
# Constants for choice fields # Constants for choice fields
class ApplicationStatus(models.TextChoices): class DomainRequestStatus(models.TextChoices):
STARTED = "started", "Started" STARTED = "started", "Started"
SUBMITTED = "submitted", "Submitted" SUBMITTED = "submitted", "Submitted"
IN_REVIEW = "in review", "In review" IN_REVIEW = "in review", "In review"
@ -116,7 +116,7 @@ class DomainApplication(TimeStampedModel):
class OrganizationChoicesVerbose(models.TextChoices): class OrganizationChoicesVerbose(models.TextChoices):
""" """
Secondary organization choices Secondary organization choices
For use in the application form and on the templates For use in the domain_request form and on the templates
Keys need to match OrganizationChoices Keys need to match OrganizationChoices
""" """
@ -367,10 +367,10 @@ class DomainApplication(TimeStampedModel):
NAMING_REQUIREMENTS = "naming_not_met", "Naming requirements not met" NAMING_REQUIREMENTS = "naming_not_met", "Naming requirements not met"
OTHER = "other", "Other/Unspecified" OTHER = "other", "Other/Unspecified"
# #### Internal fields about the application ##### # #### Internal fields about the domain_request #####
status = FSMField( status = FSMField(
choices=ApplicationStatus.choices, # possible states as an array of constants choices=DomainRequestStatus.choices, # possible states as an array of constants
default=ApplicationStatus.STARTED, # sensible default default=DomainRequestStatus.STARTED, # sensible default
protected=False, # can change state directly, particularly in Django admin protected=False, # can change state directly, particularly in Django admin
) )
@ -380,12 +380,12 @@ class DomainApplication(TimeStampedModel):
blank=True, blank=True,
) )
# This is the application user who created this application. The contact # This is the domain_request user who created this domain_request. The contact
# information that they gave is in the `submitter` field # information that they gave is in the `submitter` field
creator = models.ForeignKey( creator = models.ForeignKey(
"registrar.User", "registrar.User",
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name="applications_created", related_name="domain_requests_created",
) )
investigator = models.ForeignKey( investigator = models.ForeignKey(
@ -393,7 +393,7 @@ class DomainApplication(TimeStampedModel):
null=True, null=True,
blank=True, blank=True,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
related_name="applications_investigating", related_name="domain_requests_investigating",
) )
# ##### data fields from the initial form ##### # ##### data fields from the initial form #####
@ -500,7 +500,7 @@ class DomainApplication(TimeStampedModel):
on_delete=models.PROTECT, on_delete=models.PROTECT,
) )
# "+" means no reverse relation to lookup applications from Website # "+" means no reverse relation to lookup domain_requests from Website
current_websites = models.ManyToManyField( current_websites = models.ManyToManyField(
"registrar.Website", "registrar.Website",
blank=True, blank=True,
@ -513,7 +513,7 @@ class DomainApplication(TimeStampedModel):
null=True, null=True,
blank=True, blank=True,
help_text="The approved domain", help_text="The approved domain",
related_name="domain_application", related_name="domain_domain_request",
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
) )
@ -522,7 +522,7 @@ class DomainApplication(TimeStampedModel):
null=True, null=True,
blank=True, blank=True,
help_text="The requested domain", help_text="The requested domain",
related_name="domain_application", related_name="domain_domain_request",
on_delete=models.PROTECT, on_delete=models.PROTECT,
) )
alternative_domains = models.ManyToManyField( alternative_domains = models.ManyToManyField(
@ -532,12 +532,12 @@ class DomainApplication(TimeStampedModel):
) )
# This is the contact information provided by the applicant. The # This is the contact information provided by the applicant. The
# application user who created it is in the `creator` field. # domain_request user who created it is in the `creator` field.
submitter = models.ForeignKey( submitter = models.ForeignKey(
"registrar.Contact", "registrar.Contact",
null=True, null=True,
blank=True, blank=True,
related_name="submitted_applications", related_name="submitted_domain_requests",
on_delete=models.PROTECT, on_delete=models.PROTECT,
) )
@ -550,7 +550,7 @@ class DomainApplication(TimeStampedModel):
other_contacts = models.ManyToManyField( other_contacts = models.ManyToManyField(
"registrar.Contact", "registrar.Contact",
blank=True, blank=True,
related_name="contact_applications", related_name="contact_domain_requests",
verbose_name="contacts", verbose_name="contacts",
) )
@ -572,7 +572,7 @@ class DomainApplication(TimeStampedModel):
help_text="Acknowledged .gov acceptable use policy", help_text="Acknowledged .gov acceptable use policy",
) )
# submission date records when application is submitted # submission date records when domain_request is submitted
submission_date = models.DateField( submission_date = models.DateField(
null=True, null=True,
blank=True, blank=True,
@ -591,7 +591,7 @@ class DomainApplication(TimeStampedModel):
if self.requested_domain and self.requested_domain.name: if self.requested_domain and self.requested_domain.name:
return self.requested_domain.name return self.requested_domain.name
else: else:
return f"{self.status} application created by {self.creator}" return f"{self.status} domain_request created by {self.creator}"
except Exception: except Exception:
return "" return ""
@ -639,7 +639,7 @@ class DomainApplication(TimeStampedModel):
email_template, email_template,
email_template_subject, email_template_subject,
self.submitter.email, self.submitter.email,
context={"application": self}, context={"domain_request": self},
bcc_address=bcc_address, bcc_address=bcc_address,
) )
logger.info(f"The {new_status} email sent to: {self.submitter.email}") logger.info(f"The {new_status} email sent to: {self.submitter.email}")
@ -657,15 +657,15 @@ class DomainApplication(TimeStampedModel):
@transition( @transition(
field="status", field="status",
source=[ source=[
ApplicationStatus.STARTED, DomainRequestStatus.STARTED,
ApplicationStatus.IN_REVIEW, DomainRequestStatus.IN_REVIEW,
ApplicationStatus.ACTION_NEEDED, DomainRequestStatus.ACTION_NEEDED,
ApplicationStatus.WITHDRAWN, DomainRequestStatus.WITHDRAWN,
], ],
target=ApplicationStatus.SUBMITTED, target=DomainRequestStatus.SUBMITTED,
) )
def submit(self): def submit(self):
"""Submit an application that is started. """Submit an domain_request that is started.
As a side effect, an email notification is sent.""" As a side effect, an email notification is sent."""
@ -685,7 +685,7 @@ class DomainApplication(TimeStampedModel):
self.save() self.save()
# Limit email notifications to transitions from Started and Withdrawn # Limit email notifications to transitions from Started and Withdrawn
limited_statuses = [self.ApplicationStatus.STARTED, self.ApplicationStatus.WITHDRAWN] limited_statuses = [self.DomainRequestStatus.STARTED, self.DomainRequestStatus.WITHDRAWN]
bcc_address = "" bcc_address = ""
if settings.IS_PRODUCTION: if settings.IS_PRODUCTION:
@ -703,17 +703,17 @@ class DomainApplication(TimeStampedModel):
@transition( @transition(
field="status", field="status",
source=[ source=[
ApplicationStatus.SUBMITTED, DomainRequestStatus.SUBMITTED,
ApplicationStatus.ACTION_NEEDED, DomainRequestStatus.ACTION_NEEDED,
ApplicationStatus.APPROVED, DomainRequestStatus.APPROVED,
ApplicationStatus.REJECTED, DomainRequestStatus.REJECTED,
ApplicationStatus.INELIGIBLE, DomainRequestStatus.INELIGIBLE,
], ],
target=ApplicationStatus.IN_REVIEW, target=DomainRequestStatus.IN_REVIEW,
conditions=[domain_is_not_active, investigator_exists_and_is_staff], conditions=[domain_is_not_active, investigator_exists_and_is_staff],
) )
def in_review(self): def in_review(self):
"""Investigate an application that has been submitted. """Investigate an domain_request that has been submitted.
This action is logged. This action is logged.
@ -722,13 +722,13 @@ class DomainApplication(TimeStampedModel):
As side effects this will delete the domain and domain_information As side effects this will delete the domain and domain_information
(will cascade) when they exist.""" (will cascade) when they exist."""
if self.status == self.ApplicationStatus.APPROVED: if self.status == self.DomainRequestStatus.APPROVED:
self.delete_and_clean_up_domain("in_review") self.delete_and_clean_up_domain("in_review")
if self.status == self.ApplicationStatus.REJECTED: if self.status == self.DomainRequestStatus.REJECTED:
self.rejection_reason = None self.rejection_reason = None
literal = DomainApplication.ApplicationStatus.IN_REVIEW literal = DomainRequest.DomainRequestStatus.IN_REVIEW
# Check if the tuple exists, then grab its value # Check if the tuple exists, then grab its value
in_review = literal if literal is not None else "In Review" in_review = literal if literal is not None else "In Review"
logger.info(f"A status change occurred. {self} was changed to '{in_review}'") logger.info(f"A status change occurred. {self} was changed to '{in_review}'")
@ -736,16 +736,16 @@ class DomainApplication(TimeStampedModel):
@transition( @transition(
field="status", field="status",
source=[ source=[
ApplicationStatus.IN_REVIEW, DomainRequestStatus.IN_REVIEW,
ApplicationStatus.APPROVED, DomainRequestStatus.APPROVED,
ApplicationStatus.REJECTED, DomainRequestStatus.REJECTED,
ApplicationStatus.INELIGIBLE, DomainRequestStatus.INELIGIBLE,
], ],
target=ApplicationStatus.ACTION_NEEDED, target=DomainRequestStatus.ACTION_NEEDED,
conditions=[domain_is_not_active, investigator_exists_and_is_staff], conditions=[domain_is_not_active, investigator_exists_and_is_staff],
) )
def action_needed(self): def action_needed(self):
"""Send back an application that is under investigation or rejected. """Send back an domain_request that is under investigation or rejected.
This action is logged. This action is logged.
@ -754,13 +754,13 @@ class DomainApplication(TimeStampedModel):
As side effects this will delete the domain and domain_information As side effects this will delete the domain and domain_information
(will cascade) when they exist.""" (will cascade) when they exist."""
if self.status == self.ApplicationStatus.APPROVED: if self.status == self.DomainRequestStatus.APPROVED:
self.delete_and_clean_up_domain("reject_with_prejudice") self.delete_and_clean_up_domain("reject_with_prejudice")
if self.status == self.ApplicationStatus.REJECTED: if self.status == self.DomainRequestStatus.REJECTED:
self.rejection_reason = None self.rejection_reason = None
literal = DomainApplication.ApplicationStatus.ACTION_NEEDED literal = DomainRequest.DomainRequestStatus.ACTION_NEEDED
# Check if the tuple is setup correctly, then grab its value # Check if the tuple is setup correctly, then grab its value
action_needed = literal if literal is not None else "Action Needed" action_needed = literal if literal is not None else "Action Needed"
logger.info(f"A status change occurred. {self} was changed to '{action_needed}'") logger.info(f"A status change occurred. {self} was changed to '{action_needed}'")
@ -768,38 +768,38 @@ class DomainApplication(TimeStampedModel):
@transition( @transition(
field="status", field="status",
source=[ source=[
ApplicationStatus.SUBMITTED, DomainRequestStatus.SUBMITTED,
ApplicationStatus.IN_REVIEW, DomainRequestStatus.IN_REVIEW,
ApplicationStatus.ACTION_NEEDED, DomainRequestStatus.ACTION_NEEDED,
ApplicationStatus.REJECTED, DomainRequestStatus.REJECTED,
], ],
target=ApplicationStatus.APPROVED, target=DomainRequestStatus.APPROVED,
conditions=[investigator_exists_and_is_staff], conditions=[investigator_exists_and_is_staff],
) )
def approve(self, send_email=True): def approve(self, send_email=True):
"""Approve an application that has been submitted. """Approve an domain_request that has been submitted.
This action cleans up the rejection status if moving away from rejected. This action cleans up the rejection status if moving away from rejected.
This has substantial side-effects because it creates another database This has substantial side-effects because it creates another database
object for the approved Domain and makes the user who created the object for the approved Domain and makes the user who created the
application into an admin on that domain. It also triggers an email domain_request into an admin on that domain. It also triggers an email
notification.""" notification."""
# create the domain # create the domain
Domain = apps.get_model("registrar.Domain") Domain = apps.get_model("registrar.Domain")
# == Check that the application is valid == # # == Check that the domain_request is valid == #
if Domain.objects.filter(name=self.requested_domain.name).exists(): if Domain.objects.filter(name=self.requested_domain.name).exists():
raise FSMApplicationError(code=FSMErrorCodes.APPROVE_DOMAIN_IN_USE) raise FSMdomain_requestError(code=FSMErrorCodes.APPROVE_DOMAIN_IN_USE)
# == Create the domain and related components == # # == Create the domain and related components == #
created_domain = Domain.objects.create(name=self.requested_domain.name) created_domain = Domain.objects.create(name=self.requested_domain.name)
self.approved_domain = created_domain self.approved_domain = created_domain
# copy the information from domainapplication into domaininformation # copy the information from DomainRequest into domaininformation
DomainInformation = apps.get_model("registrar.DomainInformation") DomainInformation = apps.get_model("registrar.DomainInformation")
DomainInformation.create_from_da(domain_application=self, domain=created_domain) DomainInformation.create_from_da(domain_domain_request=self, domain=created_domain)
# create the permission for the user # create the permission for the user
UserDomainRole = apps.get_model("registrar.UserDomainRole") UserDomainRole = apps.get_model("registrar.UserDomainRole")
@ -807,12 +807,12 @@ class DomainApplication(TimeStampedModel):
user=self.creator, domain=created_domain, role=UserDomainRole.Roles.MANAGER user=self.creator, domain=created_domain, role=UserDomainRole.Roles.MANAGER
) )
if self.status == self.ApplicationStatus.REJECTED: if self.status == self.DomainRequestStatus.REJECTED:
self.rejection_reason = None self.rejection_reason = None
# == Send out an email == # # == Send out an email == #
self._send_status_update_email( self._send_status_update_email(
"application approved", "domain_request approved",
"emails/status_change_approved.txt", "emails/status_change_approved.txt",
"emails/status_change_approved_subject.txt", "emails/status_change_approved_subject.txt",
send_email, send_email,
@ -820,11 +820,11 @@ class DomainApplication(TimeStampedModel):
@transition( @transition(
field="status", field="status",
source=[ApplicationStatus.SUBMITTED, ApplicationStatus.IN_REVIEW, ApplicationStatus.ACTION_NEEDED], source=[DomainRequestStatus.SUBMITTED, DomainRequestStatus.IN_REVIEW, DomainRequestStatus.ACTION_NEEDED],
target=ApplicationStatus.WITHDRAWN, target=DomainRequestStatus.WITHDRAWN,
) )
def withdraw(self): def withdraw(self):
"""Withdraw an application that has been submitted.""" """Withdraw an domain_request that has been submitted."""
self._send_status_update_email( self._send_status_update_email(
"withdraw", "withdraw",
@ -834,17 +834,17 @@ class DomainApplication(TimeStampedModel):
@transition( @transition(
field="status", field="status",
source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.ACTION_NEEDED, ApplicationStatus.APPROVED], source=[DomainRequestStatus.IN_REVIEW, DomainRequestStatus.ACTION_NEEDED, DomainRequestStatus.APPROVED],
target=ApplicationStatus.REJECTED, target=DomainRequestStatus.REJECTED,
conditions=[domain_is_not_active, investigator_exists_and_is_staff], conditions=[domain_is_not_active, investigator_exists_and_is_staff],
) )
def reject(self): def reject(self):
"""Reject an application that has been submitted. """Reject an domain_request that has been submitted.
As side effects this will delete the domain and domain_information As side effects this will delete the domain and domain_information
(will cascade), and send an email notification.""" (will cascade), and send an email notification."""
if self.status == self.ApplicationStatus.APPROVED: if self.status == self.DomainRequestStatus.APPROVED:
self.delete_and_clean_up_domain("reject") self.delete_and_clean_up_domain("reject")
self._send_status_update_email( self._send_status_update_email(
@ -856,24 +856,24 @@ class DomainApplication(TimeStampedModel):
@transition( @transition(
field="status", field="status",
source=[ source=[
ApplicationStatus.IN_REVIEW, DomainRequestStatus.IN_REVIEW,
ApplicationStatus.ACTION_NEEDED, DomainRequestStatus.ACTION_NEEDED,
ApplicationStatus.APPROVED, DomainRequestStatus.APPROVED,
ApplicationStatus.REJECTED, DomainRequestStatus.REJECTED,
], ],
target=ApplicationStatus.INELIGIBLE, target=DomainRequestStatus.INELIGIBLE,
conditions=[domain_is_not_active, investigator_exists_and_is_staff], conditions=[domain_is_not_active, investigator_exists_and_is_staff],
) )
def reject_with_prejudice(self): def reject_with_prejudice(self):
"""The applicant is a bad actor, reject with prejudice. """The applicant is a bad actor, reject with prejudice.
No email As a side effect, but we block the applicant from editing No email As a side effect, but we block the applicant from editing
any existing domains/applications and from submitting new aplications. any existing domains/domain_requests and from submitting new aplications.
We do this by setting an ineligible status on the user, which the We do this by setting an ineligible status on the user, which the
permissions classes test against. This will also delete the domain permissions classes test against. This will also delete the domain
and domain_information (will cascade) when they exist.""" and domain_information (will cascade) when they exist."""
if self.status == self.ApplicationStatus.APPROVED: if self.status == self.DomainRequestStatus.APPROVED:
self.delete_and_clean_up_domain("reject_with_prejudice") self.delete_and_clean_up_domain("reject_with_prejudice")
self.creator.restrict_user() self.creator.restrict_user()
@ -881,18 +881,18 @@ class DomainApplication(TimeStampedModel):
# ## Form policies ### # ## Form policies ###
# #
# These methods control what questions need to be answered by applicants # These methods control what questions need to be answered by applicants
# during the application flow. They are policies about the application so # during the domain_request flow. They are policies about the domain_request so
# they appear here. # they appear here.
def show_organization_federal(self) -> bool: def show_organization_federal(self) -> bool:
"""Show this step if the answer to the first question was "federal".""" """Show this step if the answer to the first question was "federal"."""
user_choice = self.organization_type user_choice = self.organization_type
return user_choice == DomainApplication.OrganizationChoices.FEDERAL return user_choice == DomainRequest.OrganizationChoices.FEDERAL
def show_tribal_government(self) -> bool: def show_tribal_government(self) -> bool:
"""Show this step if the answer to the first question was "tribal".""" """Show this step if the answer to the first question was "tribal"."""
user_choice = self.organization_type user_choice = self.organization_type
return user_choice == DomainApplication.OrganizationChoices.TRIBAL return user_choice == DomainRequest.OrganizationChoices.TRIBAL
def show_organization_election(self) -> bool: def show_organization_election(self) -> bool:
"""Show this step if the answer to the first question implies it. """Show this step if the answer to the first question implies it.
@ -902,9 +902,9 @@ class DomainApplication(TimeStampedModel):
""" """
user_choice = self.organization_type user_choice = self.organization_type
excluded = [ excluded = [
DomainApplication.OrganizationChoices.FEDERAL, DomainRequest.OrganizationChoices.FEDERAL,
DomainApplication.OrganizationChoices.INTERSTATE, DomainRequest.OrganizationChoices.INTERSTATE,
DomainApplication.OrganizationChoices.SCHOOL_DISTRICT, DomainRequest.OrganizationChoices.SCHOOL_DISTRICT,
] ]
return bool(user_choice and user_choice not in excluded) return bool(user_choice and user_choice not in excluded)
@ -912,27 +912,27 @@ class DomainApplication(TimeStampedModel):
"""Show this step if this is a special district or interstate.""" """Show this step if this is a special district or interstate."""
user_choice = self.organization_type user_choice = self.organization_type
return user_choice in [ return user_choice in [
DomainApplication.OrganizationChoices.SPECIAL_DISTRICT, DomainRequest.OrganizationChoices.SPECIAL_DISTRICT,
DomainApplication.OrganizationChoices.INTERSTATE, DomainRequest.OrganizationChoices.INTERSTATE,
] ]
def has_rationale(self) -> bool: def has_rationale(self) -> bool:
"""Does this application have no_other_contacts_rationale?""" """Does this domain_request have no_other_contacts_rationale?"""
return bool(self.no_other_contacts_rationale) return bool(self.no_other_contacts_rationale)
def has_other_contacts(self) -> bool: def has_other_contacts(self) -> bool:
"""Does this application have other contacts listed?""" """Does this domain_request have other contacts listed?"""
return self.other_contacts.exists() return self.other_contacts.exists()
def is_federal(self) -> Union[bool, None]: def is_federal(self) -> Union[bool, None]:
"""Is this application for a federal agency? """Is this domain_request for a federal agency?
organization_type can be both null and blank, organization_type can be both null and blank,
""" """
if not self.organization_type: if not self.organization_type:
# organization_type is either blank or None, can't answer # organization_type is either blank or None, can't answer
return None return None
if self.organization_type == DomainApplication.OrganizationChoices.FEDERAL: if self.organization_type == DomainRequest.OrganizationChoices.FEDERAL:
return True return True
return False return False

View file

@ -33,8 +33,8 @@ class UserGroup(Group):
}, },
{ {
"app_label": "registrar", "app_label": "registrar",
"model": "domainapplication", "model": "domainrequest",
"permissions": ["change_domainapplication"], "permissions": ["change_domainrequest"],
}, },
{ {
"app_label": "registrar", "app_label": "registrar",

View file

@ -184,7 +184,7 @@ class DomainHelper:
model_1_fields = set(field.name for field in model_1._meta.get_fields() if field.name != "id") model_1_fields = set(field.name for field in model_1._meta.get_fields() if field.name != "id")
model_2_fields = set(field.name for field in model_2._meta.get_fields() if field.name != "id") model_2_fields = set(field.name for field in model_2._meta.get_fields() if field.name != "id")
# Get the fields that exist on both DomainApplication and DomainInformation # Get the fields that exist on both DomainRequest and DomainInformation
common_fields = model_1_fields & model_2_fields common_fields = model_1_fields & model_2_fields
return common_fields return common_fields

View file

@ -4,7 +4,7 @@ from .utility.time_stamped_model import TimeStampedModel
class Website(TimeStampedModel): class Website(TimeStampedModel):
"""Keep domain names in their own table so that applications can refer to """Keep domain names in their own table so that domain requests can refer to
many of them.""" many of them."""
# domain names have strictly limited lengths, 255 characters is more than # domain names have strictly limited lengths, 255 characters is more than

View file

@ -1,126 +0,0 @@
{% extends 'base.html' %}
{% load custom_filters %}
{% block title %}Domain request status | {{ domainapplication.requested_domain.name }} | {% endblock %}
{% load static url_helpers %}
{% block content %}
<main id="main-content" class="grid-container">
<div class="grid-col desktop:grid-offset-2 desktop:grid-col-8">
<a href="{% url 'home' %}" class="breadcrumb__back">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
<use xlink:href="{% static 'img/sprite.svg' %}#arrow_back"></use>
</svg>
<p class="margin-left-05 margin-top-0 margin-bottom-0 line-height-sans-1">
Back to manage your domains
</p>
</a>
<h1>Domain request for {{ domainapplication.requested_domain.name }}</h1>
<div
class="usa-summary-box dotgov-status-box margin-top-3 padding-left-2"
role="region"
aria-labelledby="summary-box-key-information"
>
<div class="usa-summary-box__body">
<p class="usa-summary-box__heading font-sans-md margin-bottom-0"
id="summary-box-key-information"
>
<span class="text-bold text-primary-darker">
Status:
</span>
{% if domainapplication.status == 'approved' %} Approved
{% elif domainapplication.status == 'in review' %} In review
{% elif domainapplication.status == 'rejected' %} Rejected
{% elif domainapplication.status == 'submitted' %} Submitted
{% elif domainapplication.status == 'ineligible' %} Ineligible
{% else %}ERROR Please contact technical support/dev
{% endif %}
</p>
</div>
</div>
<br>
<p> <b class="review__step__name">Last updated:</b> {{domainapplication.updated_at|date:"F j, Y"}}<br>
<b class="review__step__name">Request #:</b> {{domainapplication.id}}</p>
<p>{% include "includes/domain_application.html" %}</p>
<p><a href="{% url 'application-withdraw-confirmation' pk=domainapplication.id %}" class="usa-button usa-button--outline withdraw_outline">
Withdraw request</a>
</p>
</div>
<div class="grid-col desktop:grid-offset-2 maxw-tablet">
<h2 class="text-primary-darker"> Summary of your domain request </h2>
{% with heading_level='h3' %}
{% with org_type=domainapplication.get_organization_type_display %}
{% include "includes/summary_item.html" with title='Type of organization' value=org_type heading_level=heading_level %}
{% endwith %}
{% if domainapplication.tribe_name %}
{% include "includes/summary_item.html" with title='Tribal government' value=domainapplication.tribe_name heading_level=heading_level %}
{% if domainapplication.federally_recognized_tribe %}
<p>Federally-recognized tribe</p>
{% endif %}
{% if domainapplication.state_recognized_tribe %}
<p>State-recognized tribe</p>
{% endif %}
{% endif %}
{% if domainapplication.get_federal_type_display %}
{% include "includes/summary_item.html" with title='Federal government branch' value=domainapplication.get_federal_type_display heading_level=heading_level %}
{% endif %}
{% if domainapplication.is_election_board %}
{% with value=domainapplication.is_election_board|yesno:"Yes,No,Incomplete" %}
{% include "includes/summary_item.html" with title='Election office' value=value heading_level=heading_level %}
{% endwith %}
{% endif %}
{% if domainapplication.organization_name %}
{% include "includes/summary_item.html" with title='Organization name and mailing address' value=domainapplication address='true' heading_level=heading_level %}
{% endif %}
{% if domainapplication.about_your_organization %}
{% include "includes/summary_item.html" with title='About your organization' value=domainapplication.about_your_organization heading_level=heading_level %}
{% endif %}
{% if domainapplication.authorizing_official %}
{% include "includes/summary_item.html" with title='Authorizing official' value=domainapplication.authorizing_official contact='true' heading_level=heading_level %}
{% endif %}
{% if domainapplication.current_websites.all %}
{% include "includes/summary_item.html" with title='Current websites' value=domainapplication.current_websites.all list='true' heading_level=heading_level %}
{% endif %}
{% if domainapplication.requested_domain %}
{% include "includes/summary_item.html" with title='.gov domain' value=domainapplication.requested_domain heading_level=heading_level %}
{% endif %}
{% if domainapplication.alternative_domains.all %}
{% include "includes/summary_item.html" with title='Alternative domains' value=domainapplication.alternative_domains.all list='true' heading_level=heading_level %}
{% endif %}
{% if domainapplication.purpose %}
{% include "includes/summary_item.html" with title='Purpose of your domain' value=domainapplication.purpose heading_level=heading_level %}
{% endif %}
{% if domainapplication.submitter %}
{% include "includes/summary_item.html" with title='Your contact information' value=domainapplication.submitter contact='true' heading_level=heading_level %}
{% endif %}
{% if domainapplication.other_contacts.all %}
{% include "includes/summary_item.html" with title='Other employees from your organization' value=domainapplication.other_contacts.all contact='true' list='true' heading_level=heading_level %}
{% else %}
{% include "includes/summary_item.html" with title='Other employees from your organization' value=domainapplication.no_other_contacts_rationale heading_level=heading_level %}
{% endif %}
{% include "includes/summary_item.html" with title='Anything else?' value=domainapplication.anything_else|default:"No" heading_level=heading_level %}
{% endwith %}
</div>
</main>
{% endblock %}

View file

@ -1,21 +0,0 @@
{% extends 'base.html' %}
{% block title %}Withdraw request for {{ domainapplication.requested_domain.name }} | {% endblock %}
{% load static url_helpers %}
{% block content %}
<div class="grid-container">
<div class="grid-col desktop:grid-offset-2 desktop:grid-col-8">
<h1>Withdraw request for {{ domainapplication.requested_domain.name }}?</h1>
<p>If you withdraw your request, we won't review it. Once you withdraw your request, you can edit it and submit it again. </p>
<p><a href="{% url 'application-withdrawn' domainapplication.id %}" class="usa-button withdraw">Withdraw request</a>
<a href="{% url 'application-status' domainapplication.id %}">Cancel</a></p>
</div>
</div>
{% endblock %}

View file

@ -1,4 +1,4 @@
{% extends 'application_form.html' %} {% extends 'domain_request_form.html' %}
{% load field_helpers %} {% load field_helpers %}
{% block form_instructions %} {% block form_instructions %}

View file

@ -1,4 +1,4 @@
{% extends 'application_form.html' %} {% extends 'domain_request_form.html' %}
{% load field_helpers %} {% load field_helpers %}
{% block form_instructions %} {% block form_instructions %}

View file

@ -1,4 +1,4 @@
{% extends 'application_form.html' %} {% extends 'domain_request_form.html' %}
{% load field_helpers url_helpers %} {% load field_helpers url_helpers %}
{% block form_instructions %} {% block form_instructions %}

View file

@ -1,4 +1,4 @@
{% extends 'application_form.html' %} {% extends 'domain_request_form.html' %}
{% load static field_helpers %} {% load static field_helpers %}
{% block form_instructions %} {% block form_instructions %}

View file

@ -19,15 +19,16 @@
<h2>Next steps in this process</h2> <h2>Next steps in this process</h2>
<p> Well review your request. This usually takes 20 business days. During <p> Well review your request. This review period can take 30 business days. Due to the volume of requests, the wait time is longer than usual. We appreciate your patience.</p>
this review well verify that:</p>
<p>During our review well verify that:</p>
<ul class="usa-list"> <ul class="usa-list">
<li>Your organization is eligible for a .gov domain.</li> <li>Your organization is eligible for a .gov domain.</li>
<li>You work at the organization and/or can make requests on its behalf.</li> <li>You work at the organization and/or can make requests on its behalf.</li>
<li>Your requested domain meets our naming requirements.</li> <li>Your requested domain meets our naming requirements.</li>
</ul> </ul>
<p> Well email you if we have questions and when we complete our review. You can <a href="{% url 'home' %}">check the status</a> <p> Well email you if we have questions. Well also email you as soon as we complete our review. You can <a href="{% url 'home' %}">check the status</a>
of your request at any time on the registrar homepage.</p> of your request at any time on the registrar homepage.</p>
<p> <a class="usa-link" rel="noopener noreferrer" target="_blank" href="{% public_site_url 'contact' %}">Contact us if you need help during this process</a>.</p> <p> <a class="usa-link" rel="noopener noreferrer" target="_blank" href="{% public_site_url 'contact' %}">Contact us if you need help during this process</a>.</p>

View file

@ -1,4 +1,4 @@
{% extends 'application_form.html' %} {% extends 'domain_request_form.html' %}
{% load static field_helpers url_helpers %} {% load static field_helpers url_helpers %}
{% block form_instructions %} {% block form_instructions %}

View file

@ -6,12 +6,12 @@
<div class="grid-container"> <div class="grid-container">
<div class="grid-row grid-gap"> <div class="grid-row grid-gap">
<div class="tablet:grid-col-3"> <div class="tablet:grid-col-3">
{% include 'application_sidebar.html' %} {% include 'domain_request_sidebar.html' %}
</div> </div>
<div class="tablet:grid-col-9"> <div class="tablet:grid-col-9">
<main id="main-content" class="grid-container register-form-step"> <main id="main-content" class="grid-container register-form-step">
{% if steps.prev %} {% if steps.prev %}
<a href="{% namespaced_url 'application' steps.prev %}" class="breadcrumb__back"> <a href="{% namespaced_url 'domain-request' steps.prev %}" class="breadcrumb__back">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24" height="24"> <svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24" height="24">
<use xlink:href="{%static 'img/sprite.svg'%}#arrow_back"></use> <use xlink:href="{%static 'img/sprite.svg'%}#arrow_back"></use>
</svg><span class="margin-left-05">Previous step</span> </svg><span class="margin-left-05">Previous step</span>

View file

@ -1,4 +1,4 @@
{% extends 'application_form.html' %} {% extends 'domain_request_form.html' %}
{% load field_helpers url_helpers %} {% load field_helpers url_helpers %}
{% block form_instructions %} {% block form_instructions %}

View file

@ -1,4 +1,4 @@
{% extends 'application_form.html' %} {% extends 'domain_request_form.html' %}
{% load field_helpers %} {% load field_helpers %}
{% block form_instructions %} {% block form_instructions %}

View file

@ -1,4 +1,4 @@
{% extends 'application_form.html' %} {% extends 'domain_request_form.html' %}
{% load field_helpers %} {% load field_helpers %}
{% block form_instructions %} {% block form_instructions %}

View file

@ -1,4 +1,4 @@
{% extends 'application_form.html' %} {% extends 'domain_request_form.html' %}
{% load field_helpers %} {% load field_helpers %}
{% block form_instructions %} {% block form_instructions %}

View file

@ -1,4 +1,4 @@
{% extends 'application_form.html' %} {% extends 'domain_request_form.html' %}
{% load static field_helpers %} {% load static field_helpers %}
{% block form_instructions %} {% block form_instructions %}

View file

@ -1,4 +1,4 @@
{% extends 'application_form.html' %} {% extends 'domain_request_form.html' %}
{% load field_helpers url_helpers %} {% load field_helpers url_helpers %}
{% block form_instructions %} {% block form_instructions %}

View file

@ -1,4 +1,4 @@
{% extends 'application_form.html' %} {% extends 'domain_request_form.html' %}
{% load field_helpers %} {% load field_helpers %}
{% block form_instructions %} {% block form_instructions %}

View file

@ -1,4 +1,4 @@
{% extends 'application_form.html' %} {% extends 'domain_request_form.html' %}
{% load static url_helpers %} {% load static url_helpers %}
{% load custom_filters %} {% load custom_filters %}
@ -23,98 +23,98 @@
<section class="summary-item margin-top-3"> <section class="summary-item margin-top-3">
{% if step == Step.ORGANIZATION_TYPE %} {% if step == Step.ORGANIZATION_TYPE %}
{% namespaced_url 'application' step as application_url %} {% namespaced_url 'domain-request' step as domain_request_url %}
{% if application.organization_type is not None %} {% if domain_request.organization_type is not None %}
{% with title=form_titles|get_item:step value=application.get_organization_type_display|default:"Incomplete" %} {% with title=form_titles|get_item:step value=domain_request.get_organization_type_display|default:"Incomplete" %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %}
{% endwith %} {% endwith %}
{% else %} {% else %}
{% with title=form_titles|get_item:step value="Incomplete" %} {% with title=form_titles|get_item:step value="Incomplete" %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %}
{% endwith %} {% endwith %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if step == Step.TRIBAL_GOVERNMENT %} {% if step == Step.TRIBAL_GOVERNMENT %}
{% namespaced_url 'application' step as application_url %} {% namespaced_url 'domain-request' step as domain_request_url %}
{% with title=form_titles|get_item:step value=application.tribe_name|default:"Incomplete" %} {% with title=form_titles|get_item:step value=domain_request.tribe_name|default:"Incomplete" %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %}
{% endwith %} {% endwith %}
{% if application.federally_recognized_tribe %}<p>Federally-recognized tribe</p>{% endif %} {% if domain_request.federally_recognized_tribe %}<p>Federally-recognized tribe</p>{% endif %}
{% if application.state_recognized_tribe %}<p>State-recognized tribe</p>{% endif %} {% if domain_request.state_recognized_tribe %}<p>State-recognized tribe</p>{% endif %}
{% endif %} {% endif %}
{% if step == Step.ORGANIZATION_FEDERAL %} {% if step == Step.ORGANIZATION_FEDERAL %}
{% namespaced_url 'application' step as application_url %} {% namespaced_url 'domain-request' step as domain_request_url %}
{% with title=form_titles|get_item:step value=application.get_federal_type_display|default:"Incomplete" %} {% with title=form_titles|get_item:step value=domain_request.get_federal_type_display|default:"Incomplete" %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %}
{% endwith %} {% endwith %}
{% endif %} {% endif %}
{% if step == Step.ORGANIZATION_ELECTION %} {% if step == Step.ORGANIZATION_ELECTION %}
{% namespaced_url 'application' step as application_url %} {% namespaced_url 'domain-request' step as domain_request_url %}
{% with title=form_titles|get_item:step value=application.is_election_board|yesno:"Yes,No,Incomplete" %} {% with title=form_titles|get_item:step value=domain_request.is_election_board|yesno:"Yes,No,Incomplete" %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %}
{% endwith %} {% endwith %}
{% endif %} {% endif %}
{% if step == Step.ORGANIZATION_CONTACT %} {% if step == Step.ORGANIZATION_CONTACT %}
{% namespaced_url 'application' step as application_url %} {% namespaced_url 'domain-request' step as domain_request_url %}
{% if application.organization_name %} {% if domain_request.organization_name %}
{% with title=form_titles|get_item:step value=application %} {% with title=form_titles|get_item:step value=domain_request %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url address='true' %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url address='true' %}
{% endwith %} {% endwith %}
{% else %} {% else %}
{% with title=form_titles|get_item:step value='Incomplete' %} {% with title=form_titles|get_item:step value='Incomplete' %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %}
{% endwith %} {% endwith %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if step == Step.ABOUT_YOUR_ORGANIZATION %} {% if step == Step.ABOUT_YOUR_ORGANIZATION %}
{% namespaced_url 'application' step as application_url %} {% namespaced_url 'domain-request' step as domain_request_url %}
{% with title=form_titles|get_item:step value=application.about_your_organization|default:"Incomplete" %} {% with title=form_titles|get_item:step value=domain_request.about_your_organization|default:"Incomplete" %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %}
{% endwith %} {% endwith %}
{% endif %} {% endif %}
{% if step == Step.AUTHORIZING_OFFICIAL %} {% if step == Step.AUTHORIZING_OFFICIAL %}
{% namespaced_url 'application' step as application_url %} {% namespaced_url 'domain-request' step as domain_request_url %}
{% if application.authorizing_official is not None %} {% if domain_request.authorizing_official is not None %}
{% with title=form_titles|get_item:step value=application.authorizing_official %} {% with title=form_titles|get_item:step value=domain_request.authorizing_official %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url contact='true' %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url contact='true' %}
{% endwith %} {% endwith %}
{% else %} {% else %}
{% with title=form_titles|get_item:step value="Incomplete" %} {% with title=form_titles|get_item:step value="Incomplete" %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %}
{% endwith %} {% endwith %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if step == Step.CURRENT_SITES %} {% if step == Step.CURRENT_SITES %}
{% namespaced_url 'application' step as application_url %} {% namespaced_url 'domain-request' step as domain_request_url %}
{% if application.current_websites.all %} {% if domain_request.current_websites.all %}
{% with title=form_titles|get_item:step value=application.current_websites.all %} {% with title=form_titles|get_item:step value=domain_request.current_websites.all %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url list='true' %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url list='true' %}
{% endwith %} {% endwith %}
{% else %} {% else %}
{% with title=form_titles|get_item:step value='None' %} {% with title=form_titles|get_item:step value='None' %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %}
{% endwith %} {% endwith %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if step == Step.DOTGOV_DOMAIN %} {% if step == Step.DOTGOV_DOMAIN %}
{% namespaced_url 'application' step as application_url %} {% namespaced_url 'domain-request' step as domain_request_url %}
{% with title=form_titles|get_item:step value=application.requested_domain.name|default:"Incomplete" %} {% with title=form_titles|get_item:step value=domain_request.requested_domain.name|default:"Incomplete" %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %}
{% endwith %} {% endwith %}
{% if application.alternative_domains.all %} {% if domain_request.alternative_domains.all %}
<h3 class="register-form-review-header">Alternative domains</h3> <h3 class="register-form-review-header">Alternative domains</h3>
<ul class="usa-list usa-list--unstyled margin-top-0"> <ul class="usa-list usa-list--unstyled margin-top-0">
{% for site in application.alternative_domains.all %} {% for site in domain_request.alternative_domains.all %}
<li>{{ site.website }}</li> <li>{{ site.website }}</li>
{% endfor %} {% endfor %}
</ul> </ul>
@ -122,51 +122,51 @@
{% endif %} {% endif %}
{% if step == Step.PURPOSE %} {% if step == Step.PURPOSE %}
{% namespaced_url 'application' step as application_url %} {% namespaced_url 'domain-request' step as domain_request_url %}
{% with title=form_titles|get_item:step value=application.purpose|default:"Incomplete" %} {% with title=form_titles|get_item:step value=domain_request.purpose|default:"Incomplete" %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %}
{% endwith %} {% endwith %}
{% endif %} {% endif %}
{% if step == Step.YOUR_CONTACT %} {% if step == Step.YOUR_CONTACT %}
{% namespaced_url 'application' step as application_url %} {% namespaced_url 'domain-request' step as domain_request_url %}
{% if application.submitter is not None %} {% if domain_request.submitter is not None %}
{% with title=form_titles|get_item:step value=application.submitter %} {% with title=form_titles|get_item:step value=domain_request.submitter %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url contact='true' %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url contact='true' %}
{% endwith %} {% endwith %}
{% else %} {% else %}
{% with title=form_titles|get_item:step value="Incomplete" %} {% with title=form_titles|get_item:step value="Incomplete" %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %}
{% endwith %} {% endwith %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if step == Step.OTHER_CONTACTS %} {% if step == Step.OTHER_CONTACTS %}
{% namespaced_url 'application' step as application_url %} {% namespaced_url 'domain-request' step as domain_request_url %}
{% if application.other_contacts.all %} {% if domain_request.other_contacts.all %}
{% with title=form_titles|get_item:step value=application.other_contacts.all %} {% with title=form_titles|get_item:step value=domain_request.other_contacts.all %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url contact='true' list='true' %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url contact='true' list='true' %}
{% endwith %} {% endwith %}
{% else %} {% else %}
{% with title=form_titles|get_item:step value=application.no_other_contacts_rationale|default:"Incomplete" %} {% with title=form_titles|get_item:step value=domain_request.no_other_contacts_rationale|default:"Incomplete" %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %}
{% endwith %} {% endwith %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if step == Step.ANYTHING_ELSE %} {% if step == Step.ANYTHING_ELSE %}
{% namespaced_url 'application' step as application_url %} {% namespaced_url 'domain-request' step as domain_request_url %}
{% with title=form_titles|get_item:step value=application.anything_else|default:"No" %} {% with title=form_titles|get_item:step value=domain_request.anything_else|default:"No" %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %}
{% endwith %} {% endwith %}
{% endif %} {% endif %}
{% if step == Step.REQUIREMENTS %} {% if step == Step.REQUIREMENTS %}
{% namespaced_url 'application' step as application_url %} {% namespaced_url 'domain-request' step as domain_request_url %}
{% with title=form_titles|get_item:step value=application.is_policy_acknowledged|yesno:"I agree.,I do not agree.,I do not agree." %} {% with title=form_titles|get_item:step value=domain_request.is_policy_acknowledged|yesno:"I agree.,I do not agree.,I do not agree." %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %}
{% endwith %} {% endwith %}
{% endif %} {% endif %}

View file

@ -15,7 +15,7 @@
</svg> </svg>
{% endif %} {% endif %}
{% endif %} {% endif %}
<a href="{% namespaced_url 'application' this_step %}" <a href="{% namespaced_url 'domain-request' this_step %}"
{% if this_step == steps.current %} {% if this_step == steps.current %}
class="usa-current" class="usa-current"
{% else %} {% else %}

View file

@ -0,0 +1,126 @@
{% extends 'base.html' %}
{% load custom_filters %}
{% block title %}Domain request status | {{ DomainRequest.requested_domain.name }} | {% endblock %}
{% load static url_helpers %}
{% block content %}
<main id="main-content" class="grid-container">
<div class="grid-col desktop:grid-offset-2 desktop:grid-col-8">
<a href="{% url 'home' %}" class="breadcrumb__back">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
<use xlink:href="{% static 'img/sprite.svg' %}#arrow_back"></use>
</svg>
<p class="margin-left-05 margin-top-0 margin-bottom-0 line-height-sans-1">
Back to manage your domains
</p>
</a>
<h1>Domain request for {{ DomainRequest.requested_domain.name }}</h1>
<div
class="usa-summary-box dotgov-status-box margin-top-3 padding-left-2"
role="region"
aria-labelledby="summary-box-key-information"
>
<div class="usa-summary-box__body">
<p class="usa-summary-box__heading font-sans-md margin-bottom-0"
id="summary-box-key-information"
>
<span class="text-bold text-primary-darker">
Status:
</span>
{% if DomainRequest.status == 'approved' %} Approved
{% elif DomainRequest.status == 'in review' %} In review
{% elif DomainRequest.status == 'rejected' %} Rejected
{% elif DomainRequest.status == 'submitted' %} Submitted
{% elif DomainRequest.status == 'ineligible' %} Ineligible
{% else %}ERROR Please contact technical support/dev
{% endif %}
</p>
</div>
</div>
<br>
<p> <b class="review__step__name">Last updated:</b> {{DomainRequest.updated_at|date:"F j, Y"}}<br>
<b class="review__step__name">Request #:</b> {{DomainRequest.id}}</p>
<p>{% include "includes/domain_request.html" %}</p>
<p><a href="{% url 'domain-request-withdraw-confirmation' pk=DomainRequest.id %}" class="usa-button usa-button--outline withdraw_outline">
Withdraw request</a>
</p>
</div>
<div class="grid-col desktop:grid-offset-2 maxw-tablet">
<h2 class="text-primary-darker"> Summary of your domain request </h2>
{% with heading_level='h3' %}
{% with org_type=DomainRequest.get_organization_type_display %}
{% include "includes/summary_item.html" with title='Type of organization' value=org_type heading_level=heading_level %}
{% endwith %}
{% if DomainRequest.tribe_name %}
{% include "includes/summary_item.html" with title='Tribal government' value=DomainRequest.tribe_name heading_level=heading_level %}
{% if DomainRequest.federally_recognized_tribe %}
<p>Federally-recognized tribe</p>
{% endif %}
{% if DomainRequest.state_recognized_tribe %}
<p>State-recognized tribe</p>
{% endif %}
{% endif %}
{% if DomainRequest.get_federal_type_display %}
{% include "includes/summary_item.html" with title='Federal government branch' value=DomainRequest.get_federal_type_display heading_level=heading_level %}
{% endif %}
{% if DomainRequest.is_election_board %}
{% with value=DomainRequest.is_election_board|yesno:"Yes,No,Incomplete" %}
{% include "includes/summary_item.html" with title='Election office' value=value heading_level=heading_level %}
{% endwith %}
{% endif %}
{% if DomainRequest.organization_name %}
{% include "includes/summary_item.html" with title='Organization name and mailing address' value=DomainRequest address='true' heading_level=heading_level %}
{% endif %}
{% if DomainRequest.about_your_organization %}
{% include "includes/summary_item.html" with title='About your organization' value=DomainRequest.about_your_organization heading_level=heading_level %}
{% endif %}
{% if DomainRequest.authorizing_official %}
{% include "includes/summary_item.html" with title='Authorizing official' value=DomainRequest.authorizing_official contact='true' heading_level=heading_level %}
{% endif %}
{% if DomainRequest.current_websites.all %}
{% include "includes/summary_item.html" with title='Current websites' value=DomainRequest.current_websites.all list='true' heading_level=heading_level %}
{% endif %}
{% if DomainRequest.requested_domain %}
{% include "includes/summary_item.html" with title='.gov domain' value=DomainRequest.requested_domain heading_level=heading_level %}
{% endif %}
{% if DomainRequest.alternative_domains.all %}
{% include "includes/summary_item.html" with title='Alternative domains' value=DomainRequest.alternative_domains.all list='true' heading_level=heading_level %}
{% endif %}
{% if DomainRequest.purpose %}
{% include "includes/summary_item.html" with title='Purpose of your domain' value=DomainRequest.purpose heading_level=heading_level %}
{% endif %}
{% if DomainRequest.submitter %}
{% include "includes/summary_item.html" with title='Your contact information' value=DomainRequest.submitter contact='true' heading_level=heading_level %}
{% endif %}
{% if DomainRequest.other_contacts.all %}
{% include "includes/summary_item.html" with title='Other employees from your organization' value=DomainRequest.other_contacts.all contact='true' list='true' heading_level=heading_level %}
{% else %}
{% include "includes/summary_item.html" with title='Other employees from your organization' value=DomainRequest.no_other_contacts_rationale heading_level=heading_level %}
{% endif %}
{% include "includes/summary_item.html" with title='Anything else?' value=DomainRequest.anything_else|default:"No" heading_level=heading_level %}
{% endwith %}
</div>
</main>
{% endblock %}

View file

@ -1,4 +1,4 @@
{% extends 'application_form.html' %} {% extends 'domain_request_form.html' %}
{% load field_helpers %} {% load field_helpers %}
{% block form_instructions %} {% block form_instructions %}

View file

@ -0,0 +1,21 @@
{% extends 'base.html' %}
{% block title %}Withdraw request for {{ DomainRequest.requested_domain.name }} | {% endblock %}
{% load static url_helpers %}
{% block content %}
<div class="grid-container">
<div class="grid-col desktop:grid-offset-2 desktop:grid-col-8">
<h1>Withdraw request for {{ DomainRequest.requested_domain.name }}?</h1>
<p>If you withdraw your request, we won't review it. Once you withdraw your request, you can edit it and submit it again. </p>
<p><a href="{% url 'domain-request-withdrawn' DomainRequest.id %}" class="usa-button withdraw">Withdraw request</a>
<a href="{% url 'domain-request-status' DomainRequest.id %}">Cancel</a></p>
</div>
</div>
{% endblock %}

View file

@ -1,4 +1,4 @@
{% extends 'application_form.html' %} {% extends 'domain_request_form.html' %}
{% load field_helpers %} {% load field_helpers %}
{% block form_instructions %} {% block form_instructions %}

View file

@ -1,10 +1,10 @@
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #} {% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
Hi, {{ application.submitter.first_name }}. Hi, {{ domain_request.submitter.first_name }}.
Your .gov domain request has been withdrawn and will not be reviewed by our team. Your .gov domain request has been withdrawn and will not be reviewed by our team.
DOMAIN REQUESTED: {{ application.requested_domain.name }} DOMAIN REQUESTED: {{ domain_request.requested_domain.name }}
REQUEST RECEIVED ON: {{ application.submission_date|date }} REQUEST RECEIVED ON: {{ domain_request.submission_date|date }}
STATUS: Withdrawn STATUS: Withdrawn
---------------------------------------------------------------- ----------------------------------------------------------------

View file

@ -1 +1 @@
Update on your .gov request: {{ application.requested_domain.name }} Update on your .gov request: {{ domain_request.requested_domain.name }}

View file

@ -1,55 +0,0 @@
SUMMARY OF YOUR DOMAIN REQUEST
Type of organization:
{{ application.get_organization_type_display }}
{% if application.show_organization_federal %}
Federal government branch:
{{ application.get_federal_type_display }}
{% elif application.show_tribal_government %}
Tribal government:
{{ application.tribe_name|default:"Incomplete" }}{% if application.federally_recognized_tribe %}
Federally-recognized tribe
{% endif %}{% if application.state_recognized_tribe %}
State-recognized tribe
{% endif %}{% endif %}{% if application.show_organization_election %}
Election office:
{{ application.is_election_board|yesno:"Yes,No,Incomplete" }}
{% endif %}
Organization name and mailing address:
{% spaceless %}{{ application.federal_agency }}
{{ application.organization_name }}
{{ application.address_line1 }}{% if application.address_line2 %}
{{ application.address_line2 }}{% endif %}
{{ application.city }}, {{ application.state_territory }}
{{ application.zipcode }}{% if application.urbanization %}
{{ application.urbanization }}{% endif %}{% endspaceless %}
{% if application.about_your_organization %}{# if block makes one newline if it's false #}
About your organization:
{% spaceless %}{{ application.about_your_organization }}{% endspaceless %}
{% endif %}
Authorizing official:
{% spaceless %}{% include "emails/includes/contact.txt" with contact=application.authorizing_official %}{% endspaceless %}
{% if application.current_websites.exists %}{# if block makes a newline #}
Current websites: {% for site in application.current_websites.all %}
{% spaceless %}{{ site.website }}{% endspaceless %}
{% endfor %}{% endif %}
.gov domain:
{{ application.requested_domain.name }}
{% if application.alternative_domains.all %}
Alternative domains:
{% for site in application.alternative_domains.all %}{% spaceless %}{{ site.website }}{% endspaceless %}
{% endfor %}{% endif %}
Purpose of your domain:
{{ application.purpose }}
Your contact information:
{% spaceless %}{% include "emails/includes/contact.txt" with contact=application.submitter %}{% endspaceless %}
Other employees from your organization:{% for other in application.other_contacts.all %}
{% spaceless %}{% include "emails/includes/contact.txt" with contact=other %}{% endspaceless %}
{% empty %}
{{ application.no_other_contacts_rationale }}
{% endfor %}{% if application.anything_else %}
Anything else?
{{ application.anything_else }}
{% endif %}

View file

@ -0,0 +1,55 @@
SUMMARY OF YOUR DOMAIN REQUEST
Type of organization:
{{ domain_request.get_organization_type_display }}
{% if domain_request.show_organization_federal %}
Federal government branch:
{{ domain_request.get_federal_type_display }}
{% elif domain_request.show_tribal_government %}
Tribal government:
{{ domain_request.tribe_name|default:"Incomplete" }}{% if domain_request.federally_recognized_tribe %}
Federally-recognized tribe
{% endif %}{% if domain_request.state_recognized_tribe %}
State-recognized tribe
{% endif %}{% endif %}{% if domain_request.show_organization_election %}
Election office:
{{ domain_request.is_election_board|yesno:"Yes,No,Incomplete" }}
{% endif %}
Organization name and mailing address:
{% spaceless %}{{ domain_request.federal_agency }}
{{ domain_request.organization_name }}
{{ domain_request.address_line1 }}{% if domain_request.address_line2 %}
{{ domain_request.address_line2 }}{% endif %}
{{ domain_request.city }}, {{ domain_request.state_territory }}
{{ domain_request.zipcode }}{% if domain_request.urbanization %}
{{ domain_request.urbanization }}{% endif %}{% endspaceless %}
{% if domain_request.about_your_organization %}{# if block makes one newline if it's false #}
About your organization:
{% spaceless %}{{ domain_request.about_your_organization }}{% endspaceless %}
{% endif %}
Authorizing official:
{% spaceless %}{% include "emails/includes/contact.txt" with contact=domain_request.authorizing_official %}{% endspaceless %}
{% if domain_request.current_websites.exists %}{# if block makes a newline #}
Current websites: {% for site in domain_request.current_websites.all %}
{% spaceless %}{{ site.website }}{% endspaceless %}
{% endfor %}{% endif %}
.gov domain:
{{ domain_request.requested_domain.name }}
{% if domain_request.alternative_domains.all %}
Alternative domains:
{% for site in domain_request.alternative_domains.all %}{% spaceless %}{{ site.website }}{% endspaceless %}
{% endfor %}{% endif %}
Purpose of your domain:
{{ domain_request.purpose }}
Your contact information:
{% spaceless %}{% include "emails/includes/contact.txt" with contact=domain_request.submitter %}{% endspaceless %}
Other employees from your organization:{% for other in domain_request.other_contacts.all %}
{% spaceless %}{% include "emails/includes/contact.txt" with contact=other %}{% endspaceless %}
{% empty %}
{{ domain_request.no_other_contacts_rationale }}
{% endfor %}{% if domain_request.anything_else %}
Anything else?
{{ domain_request.anything_else }}
{% endif %}

View file

@ -1,10 +1,10 @@
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #} {% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
Hi, {{ application.submitter.first_name }}. Hi, {{ domain_request.submitter.first_name }}.
Congratulations! Your .gov domain request has been approved. Congratulations! Your .gov domain request has been approved.
DOMAIN REQUESTED: {{ application.requested_domain.name }} DOMAIN REQUESTED: {{ domain_request.requested_domain.name }}
REQUEST RECEIVED ON: {{ application.submission_date|date }} REQUEST RECEIVED ON: {{ domain_request.submission_date|date }}
STATUS: Approved STATUS: Approved
You can manage your approved domain on the .gov registrar <https://manage.get.gov>. You can manage your approved domain on the .gov registrar <https://manage.get.gov>.

View file

@ -1 +1 @@
Update on your .gov request: {{ application.requested_domain.name }} Update on your .gov request: {{ domain_request.requested_domain.name }}

View file

@ -1,15 +1,15 @@
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #} {% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
Hi, {{ application.submitter.first_name }}. Hi, {{ domain_request.submitter.first_name }}.
Your .gov domain request has been rejected. Your .gov domain request has been rejected.
DOMAIN REQUESTED: {{ application.requested_domain.name }} DOMAIN REQUESTED: {{ domain_request.requested_domain.name }}
REQUEST RECEIVED ON: {{ application.submission_date|date }} REQUEST RECEIVED ON: {{ domain_request.submission_date|date }}
STATUS: Rejected STATUS: Rejected
---------------------------------------------------------------- ----------------------------------------------------------------
{% if application.rejection_reason != 'other' %} {% if domain_request.rejection_reason != 'other' %}
REJECTION REASON{% endif %}{% if application.rejection_reason == 'purpose_not_met' %} REJECTION REASON{% endif %}{% if domain_request.rejection_reason == 'purpose_not_met' %}
Your domain request was rejected because the purpose you provided did not meet our Your domain request was rejected because the purpose you provided did not meet our
requirements. You didnt provide enough information about how you intend to use the requirements. You didnt provide enough information about how you intend to use the
domain. domain.
@ -18,16 +18,16 @@ Learn more about:
- Eligibility for a .gov domain <https://get.gov/domains/eligibility> - Eligibility for a .gov domain <https://get.gov/domains/eligibility>
- What you can and cant do with .gov domains <https://get.gov/domains/requirements/> - What you can and cant do with .gov domains <https://get.gov/domains/requirements/>
If you have questions or comments, reply to this email.{% elif application.rejection_reason == 'requestor_not_eligible' %} If you have questions or comments, reply to this email.{% elif domain_request.rejection_reason == 'requestor_not_eligible' %}
Your domain request was rejected because we dont believe youre eligible to request a Your domain request was rejected because we dont believe youre eligible to request a
.gov domain on behalf of {{ application.organization_name }}. You must be a government employee, or be .gov domain on behalf of {{ domain_request.organization_name }}. You must be a government employee, or be
working on behalf of a government organization, to request a .gov domain. working on behalf of a government organization, to request a .gov domain.
DEMONSTRATE ELIGIBILITY DEMONSTRATE ELIGIBILITY
If you can provide more information that demonstrates your eligibility, or you want to If you can provide more information that demonstrates your eligibility, or you want to
discuss further, reply to this email.{% elif application.rejection_reason == 'org_has_domain' %} discuss further, reply to this email.{% elif domain_request.rejection_reason == 'org_has_domain' %}
Your domain request was rejected because {{ application.organization_name }} has a .gov domain. Our Your domain request was rejected because {{ domain_request.organization_name }} has a .gov domain. Our
practice is to approve one domain per online service per government organization. We practice is to approve one domain per online service per government organization. We
evaluate additional requests on a case-by-case basis. You did not provide sufficient evaluate additional requests on a case-by-case basis. You did not provide sufficient
justification for an additional domain. justification for an additional domain.
@ -35,10 +35,10 @@ justification for an additional domain.
Read more about our practice of approving one domain per online service Read more about our practice of approving one domain per online service
<https://get.gov/domains/before/#one-domain-per-service>. <https://get.gov/domains/before/#one-domain-per-service>.
If you have questions or comments, reply to this email.{% elif application.rejection_reason == 'contacts_not_verified' %} If you have questions or comments, reply to this email.{% elif domain_request.rejection_reason == 'contacts_not_verified' %}
Your domain request was rejected because we could not verify the organizational Your domain request was rejected because we could not verify the organizational
contacts you provided. If you have questions or comments, reply to this email.{% elif application.rejection_reason == 'org_not_eligible' %} contacts you provided. If you have questions or comments, reply to this email.{% elif domain_request.rejection_reason == 'org_not_eligible' %}
Your domain request was rejected because we determined that {{ application.organization_name }} is not Your domain request was rejected because we determined that {{ domain_request.organization_name }} is not
eligible for a .gov domain. .Gov domains are only available to official U.S.-based eligible for a .gov domain. .Gov domains are only available to official U.S.-based
government organizations. government organizations.
@ -48,7 +48,7 @@ If you can provide documentation that demonstrates your eligibility, reply to th
This can include links to (or copies of) your authorizing legislation, your founding This can include links to (or copies of) your authorizing legislation, your founding
charter or bylaws, or other similar documentation. Without this, we cant approve a charter or bylaws, or other similar documentation. Without this, we cant approve a
.gov domain for your organization. Learn more about eligibility for .gov domains .gov domain for your organization. Learn more about eligibility for .gov domains
<https://get.gov/domains/eligibility/>.{% elif application.rejection_reason == 'naming_not_met' %} <https://get.gov/domains/eligibility/>.{% elif domain_request.rejection_reason == 'naming_not_met' %}
Your domain request was rejected because it does not meet our naming requirements. Your domain request was rejected because it does not meet our naming requirements.
Domains should uniquely identify a government organization and be clear to the Domains should uniquely identify a government organization and be clear to the
general public. Learn more about naming requirements for your type of organization general public. Learn more about naming requirements for your type of organization
@ -57,7 +57,7 @@ general public. Learn more about naming requirements for your type of organizati
YOU CAN SUBMIT A NEW REQUEST YOU CAN SUBMIT A NEW REQUEST
We encourage you to request a domain that meets our requirements. If you have We encourage you to request a domain that meets our requirements. If you have
questions or want to discuss potential domain names, reply to this email.{% elif application.rejection_reason == 'other' %} questions or want to discuss potential domain names, reply to this email.{% elif domain_request.rejection_reason == 'other' %}
YOU CAN SUBMIT A NEW REQUEST YOU CAN SUBMIT A NEW REQUEST
If your organization is eligible for a .gov domain and you meet our other requirements, you can submit a new request. If your organization is eligible for a .gov domain and you meet our other requirements, you can submit a new request.

View file

@ -1 +1 @@
Update on your .gov request: {{ application.requested_domain.name }} Update on your .gov request: {{ domain_request.requested_domain.name }}

View file

@ -1,21 +1,23 @@
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #} {% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
Hi, {{ application.submitter.first_name }}. Hi, {{ domain_request.submitter.first_name }}.
We received your .gov domain request. We received your .gov domain request.
DOMAIN REQUESTED: {{ application.requested_domain.name }} DOMAIN REQUESTED: {{ domain_request.requested_domain.name }}
REQUEST RECEIVED ON: {{ application.submission_date|date }} REQUEST RECEIVED ON: {{ domain_request.submission_date|date }}
STATUS: Submitted STATUS: Submitted
---------------------------------------------------------------- ----------------------------------------------------------------
NEXT STEPS NEXT STEPS
Well review your request. This usually takes 20 business days. During this review well verify that: Well review your request. This review period can take 30 business days. Due to the volume of requests, the wait time is longer than usual. We appreciate your patience.
During our review well verify that:
- Your organization is eligible for a .gov domain - Your organization is eligible for a .gov domain
- You work at the organization and/or can make requests on its behalf - You work at the organization and/or can make requests on its behalf
- Your requested domain meets our naming requirements - Your requested domain meets our naming requirements
Well email you if we have questions and when we complete our review. You can check the status of your request at any time on the registrar homepage. <https://manage.get.gov> Well email you if we have questions. Well also email you as soon as we complete our review. You can check the status of your request at any time on the registrar homepage. <https://manage.get.gov>
NEED TO MAKE CHANGES? NEED TO MAKE CHANGES?
@ -29,7 +31,7 @@ THANK YOU
---------------------------------------------------------------- ----------------------------------------------------------------
{% include 'emails/includes/application_summary.txt' %} {% include 'emails/includes/domain_request_summary.txt' %}
---------------------------------------------------------------- ----------------------------------------------------------------
The .gov team The .gov team

View file

@ -1 +1 @@
Update on your .gov request: {{ application.requested_domain.name }} Update on your .gov request: {{ domain_request.requested_domain.name }}

View file

@ -17,7 +17,7 @@
<p class="margin-top-4"> <p class="margin-top-4">
<a href="{% url 'application:' %}" class="usa-button" <a href="{% url 'domain-request:' %}" class="usa-button"
> >
Start a new domain request Start a new domain request
</a> </a>
@ -103,7 +103,7 @@
<section class="section--outlined"> <section class="section--outlined">
<h2>Domain requests</h2> <h2>Domain requests</h2>
{% if domain_applications %} {% if domain_requests %}
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked"> <table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked">
<caption class="sr-only">Your domain requests</caption> <caption class="sr-only">Your domain requests</caption>
<thead> <thead>
@ -112,61 +112,61 @@
<th data-sortable scope="col" role="columnheader">Date submitted</th> <th data-sortable scope="col" role="columnheader">Date submitted</th>
<th data-sortable scope="col" role="columnheader">Status</th> <th data-sortable scope="col" role="columnheader">Status</th>
<th scope="col" role="columnheader"><span class="usa-sr-only">Action</span></th> <th scope="col" role="columnheader"><span class="usa-sr-only">Action</span></th>
{% if has_deletable_applications %} {% if has_deletable_domain_requests %}
<th scope="col" role="columnheader"><span class="usa-sr-only">Delete Action</span></th> <th scope="col" role="columnheader"><span class="usa-sr-only">Delete Action</span></th>
{% endif %} {% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for application in domain_applications %} {% for domain_request in domain_requests %}
<tr> <tr>
<th th scope="row" role="rowheader" data-label="Domain name"> <th th scope="row" role="rowheader" data-label="Domain name">
{% if application.requested_domain is None %} {% if domain_request.requested_domain is None %}
New domain request New domain request
{# Add a breakpoint #} {# Add a breakpoint #}
<div aria-hidden="true"></div> <div aria-hidden="true"></div>
<span class="text-base font-body-xs">({{ application.created_at }} UTC)</span> <span class="text-base font-body-xs">({{ domain_request.created_at }} UTC)</span>
{% else %} {% else %}
{{ application.requested_domain.name }} {{ domain_request.requested_domain.name }}
{% endif %} {% endif %}
</th> </th>
<td data-sort-value="{{ application.submission_date|date:"U" }}" data-label="Date submitted"> <td data-sort-value="{{ domain_request.submission_date|date:"U" }}" data-label="Date submitted">
{% if application.submission_date %} {% if domain_request.submission_date %}
{{ application.submission_date|date }} {{ domain_request.submission_date|date }}
{% else %} {% else %}
<span class="text-base">Not submitted</span> <span class="text-base">Not submitted</span>
{% endif %} {% endif %}
</td> </td>
<td data-label="Status">{{ application.get_status_display }}</td> <td data-label="Status">{{ domain_request.get_status_display }}</td>
<td> <td>
{% with prefix="New domain request ("%} {% with prefix="New domain request ("%}
{% with date=application.created_at|date:"DATETIME_FORMAT"%} {% with date=domain_request.created_at|date:"DATETIME_FORMAT"%}
{% with name_default=prefix|add:date|add:" UTC)"%} {% with name_default=prefix|add:date|add:" UTC)"%}
{% if application.status == application.ApplicationStatus.STARTED or application.status == application.ApplicationStatus.ACTION_NEEDED or application.status == application.ApplicationStatus.WITHDRAWN %} {% if domain_request.status == domain_request.DomainRequestStatus.STARTED or domain_request.status == domain_request.DomainRequestStatus.ACTION_NEEDED or domain_request.status == domain_request.DomainRequestStatus.WITHDRAWN %}
<a href="{% url 'edit-application' application.pk %}"> <a href="{% url 'edit-domain-request' domain_request.pk %}">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24"> <svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="{%static 'img/sprite.svg'%}#edit"></use> <use xlink:href="{%static 'img/sprite.svg'%}#edit"></use>
</svg> </svg>
{% if application.requested_domain is not None%} {% if domain_request.requested_domain is not None%}
Edit <span class="usa-sr-only">{{ application.requested_domain.name }}</span> Edit <span class="usa-sr-only">{{ domain_request.requested_domain.name }}</span>
{% else %} {% else %}
Edit <span class="usa-sr-only">{{ name_default }}</span> Edit <span class="usa-sr-only">{{ name_default }}</span>
{% endif %} {% endif %}
{% else %} {% else %}
<a href="{% url 'application-status' application.pk %}"> <a href="{% url 'domain-request-status' domain_request.pk %}">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24"> <svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="{%static 'img/sprite.svg'%}#settings"></use> <use xlink:href="{%static 'img/sprite.svg'%}#settings"></use>
</svg> </svg>
Manage <span class="usa-sr-only">{{ application.requested_domain.name|default:name_default }}</span> Manage <span class="usa-sr-only">{{ domain_request.requested_domain.name|default:name_default }}</span>
{% endif %} {% endif %}
{% endwith %} {% endwith %}
{% endwith %} {% endwith %}
{% endwith %} {% endwith %}
</a> </a>
</td> </td>
{% if has_deletable_applications %} {% if has_deletable_domain_requests %}
<td> <td>
{% if application.status == "started" or application.status == "withdrawn" %} {% if domain_request.status == "started" or domain_request.status == "withdrawn" %}
<a <a
role="button" role="button"
id="button-toggle-delete-domain-alert-{{ forloop.counter }}" id="button-toggle-delete-domain-alert-{{ forloop.counter }}"
@ -179,10 +179,10 @@
<use xlink:href="{%static 'img/sprite.svg'%}#delete"></use> <use xlink:href="{%static 'img/sprite.svg'%}#delete"></use>
</svg> </svg>
{% with prefix="New domain request ("%} {% with prefix="New domain request ("%}
{% with date=application.created_at|date:"DATETIME_FORMAT"%} {% with date=domain_request.created_at|date:"DATETIME_FORMAT"%}
{% with name_default=prefix|add:date|add:" UTC)"%} {% with name_default=prefix|add:date|add:" UTC)"%}
{% if application.requested_domain is not None %} {% if domain_request.requested_domain is not None %}
Delete <span class="usa-sr-only">{{ application.requested_domain.name }}</span> Delete <span class="usa-sr-only">{{ domain_request.requested_domain.name }}</span>
{% else %} {% else %}
Delete <span class="usa-sr-only">{{ name_default }}</span> Delete <span class="usa-sr-only">{{ name_default }}</span>
{% endif %} {% endif %}
@ -198,11 +198,11 @@
aria-describedby="Domain will be removed" aria-describedby="Domain will be removed"
data-force-action data-force-action
> >
<form method="POST" action="{% url "application-delete" pk=application.id %}"> <form method="POST" action="{% url "domain-request-delete" pk=domain_request.id %}">
{% if application.requested_domain is None %} {% if domain_request.requested_domain is None %}
{% if application.created_at %} {% if domain_request.created_at %}
{% with prefix="(created " %} {% with prefix="(created " %}
{% with formatted_date=application.created_at|date:"DATETIME_FORMAT" %} {% with formatted_date=domain_request.created_at|date:"DATETIME_FORMAT" %}
{% with modal_content=prefix|add:formatted_date|add:" UTC)" %} {% with modal_content=prefix|add:formatted_date|add:" UTC)" %}
{% include 'includes/modal.html' with modal_heading="Are you sure you want to delete this domain request?" modal_description="This will remove the domain request "|add:modal_content|add:" from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %} {% include 'includes/modal.html' with modal_heading="Are you sure you want to delete this domain request?" modal_description="This will remove the domain request "|add:modal_content|add:" from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %}
{% endwith %} {% endwith %}
@ -212,7 +212,7 @@
{% include 'includes/modal.html' with modal_heading="Are you sure you want to delete New domain request?" modal_description="This will remove the domain request from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %} {% include 'includes/modal.html' with modal_heading="Are you sure you want to delete New domain request?" modal_description="This will remove the domain request from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %}
{% endif %} {% endif %}
{% else %} {% else %}
{% with modal_heading_value=application.requested_domain.name|add:"?" %} {% with modal_heading_value=domain_request.requested_domain.name|add:"?" %}
{% include 'includes/modal.html' with modal_heading="Are you sure you want to delete" heading_value=modal_heading_value modal_description="This will remove the domain request from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %} {% include 'includes/modal.html' with modal_heading="Are you sure you want to delete" heading_value=modal_heading_value modal_description="This will remove the domain request from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %}
{% endwith %} {% endwith %}
{% endif %} {% endif %}
@ -231,7 +231,7 @@
></div> ></div>
{% else %} {% else %}
<p>You haven't requested any domains.</p> <p>You haven't requested any domains.</p>
<!-- <p><a href="{% url 'application:' %}" class="usa-button">Start a new domain request</a></p> --> <!-- <p><a href="{% url 'domain-request:' %}" class="usa-button">Start a new domain request</a></p> -->
{% endif %} {% endif %}
</section> </section>

View file

@ -1,7 +1,7 @@
import logging import logging
from django import template from django import template
import re import re
from registrar.models.domain_application import DomainApplication from registrar.models.domain_request import DomainRequest
register = template.Library() register = template.Library()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -55,7 +55,7 @@ def contains_checkbox(html_list):
@register.filter @register.filter
def get_organization_long_name(organization_type): def get_organization_long_name(organization_type):
organization_choices_dict = dict(DomainApplication.OrganizationChoicesVerbose.choices) organization_choices_dict = dict(DomainRequest.OrganizationChoicesVerbose.choices)
long_form_type = organization_choices_dict[organization_type] long_form_type = organization_choices_dict[organization_type]
if long_form_type is None: if long_form_type is None:
logger.error("Organization type error, triggered by a template's custom filter") logger.error("Organization type error, triggered by a template's custom filter")

View file

@ -18,7 +18,7 @@ from registrar.models import (
Contact, Contact,
DraftDomain, DraftDomain,
Website, Website,
DomainApplication, DomainRequest,
DomainInvitation, DomainInvitation,
User, User,
UserGroup, UserGroup,
@ -221,7 +221,7 @@ class AuditedAdminMockData:
# Constants for different domain object types # Constants for different domain object types
INFORMATION = "information" INFORMATION = "information"
APPLICATION = "application" DOMAIN_REQUEST = "domain_request"
INVITATION = "invitation" INVITATION = "invitation"
def dummy_user(self, item_name, short_hand): def dummy_user(self, item_name, short_hand):
@ -365,24 +365,24 @@ class AuditedAdminMockData:
self, self,
domain_type, domain_type,
item_name, item_name,
status=DomainApplication.ApplicationStatus.STARTED, status=DomainRequest.DomainRequestStatus.STARTED,
org_type="federal", org_type="federal",
federal_type="executive", federal_type="executive",
purpose="Purpose of the site", purpose="Purpose of the site",
): ):
""" """
Returns a prebuilt kwarg dictionary for DomainApplication, Returns a prebuilt kwarg dictionary for DomainRequest,
DomainInformation, or DomainInvitation. DomainInformation, or DomainInvitation.
Args: Args:
domain_type (str): is either 'application', 'information', domain_type (str): is either 'domain_request', 'information',
or 'invitation'. or 'invitation'.
item_name (str): A shared str value appended to first_name, last_name, item_name (str): A shared str value appended to first_name, last_name,
organization_name, address_line1, address_line2, organization_name, address_line1, address_line2,
title, email, and username. title, email, and username.
status (str - optional): Defines the status for DomainApplication, status (str - optional): Defines the status for DomainRequest,
e.g. DomainApplication.ApplicationStatus.STARTED e.g. DomainRequest.DomainRequestStatus.STARTED
org_type (str - optional): Sets a domains org_type org_type (str - optional): Sets a domains org_type
@ -391,13 +391,13 @@ class AuditedAdminMockData:
purpose (str - optional): Sets a domains purpose purpose (str - optional): Sets a domains purpose
Returns: Returns:
dict: Returns a dictionary structurally consistent with the expected input dict: Returns a dictionary structurally consistent with the expected input
of either DomainApplication, DomainInvitation, or DomainInformation of either DomainRequest, DomainInvitation, or DomainInformation
based on the 'domain_type' field. based on the 'domain_type' field.
""" # noqa """ # noqa
common_args = self.get_common_domain_arg_dictionary(item_name, org_type, federal_type, purpose) common_args = self.get_common_domain_arg_dictionary(item_name, org_type, federal_type, purpose)
full_arg_dict = None full_arg_dict = None
match domain_type: match domain_type:
case self.APPLICATION: case self.DOMAIN_REQUEST:
full_arg_dict = dict( full_arg_dict = dict(
**common_args, **common_args,
requested_domain=self.dummy_draft_domain(item_name), requested_domain=self.dummy_draft_domain(item_name),
@ -405,11 +405,11 @@ class AuditedAdminMockData:
status=status, status=status,
) )
case self.INFORMATION: case self.INFORMATION:
domain_app = self.create_full_dummy_domain_application(item_name) domain_req = self.create_full_dummy_domain_request(item_name)
full_arg_dict = dict( full_arg_dict = dict(
**common_args, **common_args,
domain=self.dummy_domain(item_name, True), domain=self.dummy_domain(item_name, True),
domain_application=domain_app, domain_request=domain_req,
) )
case self.INVITATION: case self.INVITATION:
full_arg_dict = dict( full_arg_dict = dict(
@ -419,24 +419,24 @@ class AuditedAdminMockData:
) )
return full_arg_dict return full_arg_dict
def create_full_dummy_domain_application(self, item_name, status=DomainApplication.ApplicationStatus.STARTED): def create_full_dummy_domain_request(self, item_name, status=DomainRequest.DomainRequestStatus.STARTED):
"""Creates a dummy domain application object""" """Creates a dummy domain request object"""
domain_application_kwargs = self.dummy_kwarg_boilerplate(self.APPLICATION, item_name, status) domain_request_kwargs = self.dummy_kwarg_boilerplate(self.DOMAIN_REQUEST, item_name, status)
application = DomainApplication.objects.get_or_create(**domain_application_kwargs)[0] domain_request = DomainRequest.objects.get_or_create(**domain_request_kwargs)[0]
return application return domain_request
def create_full_dummy_domain_information(self, item_name, status=DomainApplication.ApplicationStatus.STARTED): def create_full_dummy_domain_information(self, item_name, status=DomainRequest.DomainRequestStatus.STARTED):
"""Creates a dummy domain information object""" """Creates a dummy domain information object"""
domain_application_kwargs = self.dummy_kwarg_boilerplate(self.INFORMATION, item_name, status) domain_request_kwargs = self.dummy_kwarg_boilerplate(self.INFORMATION, item_name, status)
application = DomainInformation.objects.get_or_create(**domain_application_kwargs)[0] domain_request = DomainInformation.objects.get_or_create(**domain_request_kwargs)[0]
return application return domain_request
def create_full_dummy_domain_invitation(self, item_name, status=DomainApplication.ApplicationStatus.STARTED): def create_full_dummy_domain_invitation(self, item_name, status=DomainRequest.DomainRequestStatus.STARTED):
"""Creates a dummy domain invitation object""" """Creates a dummy domain invitation object"""
domain_application_kwargs = self.dummy_kwarg_boilerplate(self.INVITATION, item_name, status) domain_request_kwargs = self.dummy_kwarg_boilerplate(self.INVITATION, item_name, status)
application = DomainInvitation.objects.get_or_create(**domain_application_kwargs)[0] domain_request = DomainInvitation.objects.get_or_create(**domain_request_kwargs)[0]
return application return domain_request
def create_full_dummy_domain_object( def create_full_dummy_domain_object(
self, self,
@ -445,31 +445,31 @@ class AuditedAdminMockData:
has_other_contacts=True, has_other_contacts=True,
has_current_website=True, has_current_website=True,
has_alternative_gov_domain=True, has_alternative_gov_domain=True,
status=DomainApplication.ApplicationStatus.STARTED, status=DomainRequest.DomainRequestStatus.STARTED,
): ):
"""A helper to create a dummy domain application object""" """A helper to create a dummy domain request object"""
application = None domain_request = None
match domain_type: match domain_type:
case self.APPLICATION: case self.DOMAIN_REQUEST:
application = self.create_full_dummy_domain_application(item_name, status) domain_request = self.create_full_dummy_domain_request(item_name, status)
case self.INVITATION: case self.INVITATION:
application = self.create_full_dummy_domain_invitation(item_name, status) domain_request = self.create_full_dummy_domain_invitation(item_name, status)
case self.INFORMATION: case self.INFORMATION:
application = self.create_full_dummy_domain_information(item_name, status) domain_request = self.create_full_dummy_domain_information(item_name, status)
case _: case _:
raise ValueError("Invalid domain_type, must conform to given constants") raise ValueError("Invalid domain_type, must conform to given constants")
if has_other_contacts and domain_type != self.INVITATION: if has_other_contacts and domain_type != self.INVITATION:
other = self.dummy_contact(item_name, "other") other = self.dummy_contact(item_name, "other")
application.other_contacts.add(other) domain_request.other_contacts.add(other)
if has_current_website and domain_type == self.APPLICATION: if has_current_website and domain_type == self.DOMAIN_REQUEST:
current = self.dummy_current(item_name) current = self.dummy_current(item_name)
application.current_websites.add(current) domain_request.current_websites.add(current)
if has_alternative_gov_domain and domain_type == self.APPLICATION: if has_alternative_gov_domain and domain_type == self.DOMAIN_REQUEST:
alt = self.dummy_alt(item_name) alt = self.dummy_alt(item_name)
application.alternative_domains.add(alt) domain_request.alternative_domains.add(alt)
return application return domain_request
def mock_user(): def mock_user():
@ -519,19 +519,19 @@ def create_ready_domain():
return domain return domain
def completed_application( def completed_domain_request(
has_other_contacts=True, has_other_contacts=True,
has_current_website=True, has_current_website=True,
has_alternative_gov_domain=True, has_alternative_gov_domain=True,
has_about_your_organization=True, has_about_your_organization=True,
has_anything_else=True, has_anything_else=True,
status=DomainApplication.ApplicationStatus.STARTED, status=DomainRequest.DomainRequestStatus.STARTED,
user=False, user=False,
submitter=False, submitter=False,
name="city.gov", name="city.gov",
investigator=None, investigator=None,
): ):
"""A completed domain application.""" """A completed domain request."""
if not user: if not user:
user = get_user_model().objects.create(username="username" + str(uuid.uuid4())[:8]) user = get_user_model().objects.create(username="username" + str(uuid.uuid4())[:8])
ao, _ = Contact.objects.get_or_create( ao, _ = Contact.objects.get_or_create(
@ -566,7 +566,7 @@ def completed_application(
last_name="Bob", last_name="Bob",
is_staff=True, is_staff=True,
) )
domain_application_kwargs = dict( domain_request_kwargs = dict(
organization_type="federal", organization_type="federal",
federal_type="executive", federal_type="executive",
purpose="Purpose of the site", purpose="Purpose of the site",
@ -584,50 +584,49 @@ def completed_application(
investigator=investigator, investigator=investigator,
) )
if has_about_your_organization: if has_about_your_organization:
domain_application_kwargs["about_your_organization"] = "e-Government" domain_request_kwargs["about_your_organization"] = "e-Government"
if has_anything_else: if has_anything_else:
domain_application_kwargs["anything_else"] = "There is more" domain_request_kwargs["anything_else"] = "There is more"
application, _ = DomainApplication.objects.get_or_create(**domain_application_kwargs) domain_request, _ = DomainRequest.objects.get_or_create(**domain_request_kwargs)
if has_other_contacts: if has_other_contacts:
application.other_contacts.add(other) domain_request.other_contacts.add(other)
if has_current_website: if has_current_website:
application.current_websites.add(current) domain_request.current_websites.add(current)
if has_alternative_gov_domain: if has_alternative_gov_domain:
application.alternative_domains.add(alt) domain_request.alternative_domains.add(alt)
return application return domain_request
def set_domain_request_investigators(application_list: list[DomainRequest], investigator_user: User):
def set_applications_investigators(application_list: list[DomainApplication], investigator_user: User): """Helper method that sets the investigator field of all provided domain_requests"""
"""Helper method that sets the investigator field of all provided applications"""
for application in application_list: for application in application_list:
application.investigator = investigator_user application.investigator = investigator_user
application.save() application.save()
def multiple_unalphabetical_domain_objects( def multiple_unalphabetical_domain_objects(
domain_type=AuditedAdminMockData.APPLICATION, domain_type=AuditedAdminMockData.DOMAIN_REQUEST,
): ):
"""Returns a list of generic domain objects for testing purposes""" """Returns a list of generic domain objects for testing purposes"""
applications = [] domain_requests = []
list_of_letters = list(ascii_uppercase) list_of_letters = list(ascii_uppercase)
random.shuffle(list_of_letters) random.shuffle(list_of_letters)
mock = AuditedAdminMockData() mock = AuditedAdminMockData()
for object_name in list_of_letters: for object_name in list_of_letters:
application = mock.create_full_dummy_domain_object(domain_type, object_name) domain_request = mock.create_full_dummy_domain_object(domain_type, object_name)
applications.append(application) domain_requests.append(domain_request)
return applications return domain_requests
def generic_domain_object(domain_type, object_name): def generic_domain_object(domain_type, object_name):
"""Returns a generic domain object of """Returns a generic domain object of
domain_type 'application', 'information', or 'invitation'""" domain_type 'domain_request', 'information', or 'invitation'"""
mock = AuditedAdminMockData() mock = AuditedAdminMockData()
application = mock.create_full_dummy_domain_object(domain_type, object_name) domain_request = mock.create_full_dummy_domain_object(domain_type, object_name)
return application return domain_request
class MockEppLib(TestCase): class MockEppLib(TestCase):

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@
from unittest.mock import MagicMock from unittest.mock import MagicMock
from django.test import TestCase from django.test import TestCase
from .common import completed_application, less_console_noise from .common import completed_domain_request, less_console_noise
import boto3_mocking # type: ignore import boto3_mocking # type: ignore
@ -17,11 +17,11 @@ class TestEmails(TestCase):
@boto3_mocking.patching @boto3_mocking.patching
def test_submission_confirmation(self): def test_submission_confirmation(self):
"""Submission confirmation email works.""" """Submission confirmation email works."""
application = completed_application() domain_request = completed_domain_request()
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
with less_console_noise(): with less_console_noise():
application.submit() domain_request.submit()
# check that an email was sent # check that an email was sent
self.assertTrue(self.mock_client.send_email.called) self.assertTrue(self.mock_client.send_email.called)
@ -55,10 +55,10 @@ class TestEmails(TestCase):
@boto3_mocking.patching @boto3_mocking.patching
def test_submission_confirmation_no_current_website_spacing(self): def test_submission_confirmation_no_current_website_spacing(self):
"""Test line spacing without current_website.""" """Test line spacing without current_website."""
application = completed_application(has_current_website=False) domain_request = completed_domain_request(has_current_website=False)
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
with less_console_noise(): with less_console_noise():
application.submit() domain_request.submit()
_, kwargs = self.mock_client.send_email.call_args _, kwargs = self.mock_client.send_email.call_args
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
self.assertNotIn("Current websites:", body) self.assertNotIn("Current websites:", body)
@ -68,10 +68,10 @@ class TestEmails(TestCase):
@boto3_mocking.patching @boto3_mocking.patching
def test_submission_confirmation_current_website_spacing(self): def test_submission_confirmation_current_website_spacing(self):
"""Test line spacing with current_website.""" """Test line spacing with current_website."""
application = completed_application(has_current_website=True) domain_request = completed_domain_request(has_current_website=True)
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
with less_console_noise(): with less_console_noise():
application.submit() domain_request.submit()
_, kwargs = self.mock_client.send_email.call_args _, kwargs = self.mock_client.send_email.call_args
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
self.assertIn("Current websites:", body) self.assertIn("Current websites:", body)
@ -82,10 +82,10 @@ class TestEmails(TestCase):
@boto3_mocking.patching @boto3_mocking.patching
def test_submission_confirmation_other_contacts_spacing(self): def test_submission_confirmation_other_contacts_spacing(self):
"""Test line spacing with other contacts.""" """Test line spacing with other contacts."""
application = completed_application(has_other_contacts=True) domain_request = completed_domain_request(has_other_contacts=True)
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
with less_console_noise(): with less_console_noise():
application.submit() domain_request.submit()
_, kwargs = self.mock_client.send_email.call_args _, kwargs = self.mock_client.send_email.call_args
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
self.assertIn("Other employees from your organization:", body) self.assertIn("Other employees from your organization:", body)
@ -96,10 +96,10 @@ class TestEmails(TestCase):
@boto3_mocking.patching @boto3_mocking.patching
def test_submission_confirmation_no_other_contacts_spacing(self): def test_submission_confirmation_no_other_contacts_spacing(self):
"""Test line spacing without other contacts.""" """Test line spacing without other contacts."""
application = completed_application(has_other_contacts=False) domain_request = completed_domain_request(has_other_contacts=False)
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
with less_console_noise(): with less_console_noise():
application.submit() domain_request.submit()
_, kwargs = self.mock_client.send_email.call_args _, kwargs = self.mock_client.send_email.call_args
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
# spacing should be right between adjacent elements # spacing should be right between adjacent elements
@ -109,10 +109,10 @@ class TestEmails(TestCase):
@boto3_mocking.patching @boto3_mocking.patching
def test_submission_confirmation_alternative_govdomain_spacing(self): def test_submission_confirmation_alternative_govdomain_spacing(self):
"""Test line spacing with alternative .gov domain.""" """Test line spacing with alternative .gov domain."""
application = completed_application(has_alternative_gov_domain=True) domain_request = completed_domain_request(has_alternative_gov_domain=True)
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
with less_console_noise(): with less_console_noise():
application.submit() domain_request.submit()
_, kwargs = self.mock_client.send_email.call_args _, kwargs = self.mock_client.send_email.call_args
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
self.assertIn("city1.gov", body) self.assertIn("city1.gov", body)
@ -122,10 +122,10 @@ class TestEmails(TestCase):
@boto3_mocking.patching @boto3_mocking.patching
def test_submission_confirmation_no_alternative_govdomain_spacing(self): def test_submission_confirmation_no_alternative_govdomain_spacing(self):
"""Test line spacing without alternative .gov domain.""" """Test line spacing without alternative .gov domain."""
application = completed_application(has_alternative_gov_domain=False) domain_request = completed_domain_request(has_alternative_gov_domain=False)
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
with less_console_noise(): with less_console_noise():
application.submit() domain_request.submit()
_, kwargs = self.mock_client.send_email.call_args _, kwargs = self.mock_client.send_email.call_args
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
self.assertNotIn("city1.gov", body) self.assertNotIn("city1.gov", body)
@ -135,10 +135,10 @@ class TestEmails(TestCase):
@boto3_mocking.patching @boto3_mocking.patching
def test_submission_confirmation_about_your_organization_spacing(self): def test_submission_confirmation_about_your_organization_spacing(self):
"""Test line spacing with about your organization.""" """Test line spacing with about your organization."""
application = completed_application(has_about_your_organization=True) domain_request = completed_domain_request(has_about_your_organization=True)
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
with less_console_noise(): with less_console_noise():
application.submit() domain_request.submit()
_, kwargs = self.mock_client.send_email.call_args _, kwargs = self.mock_client.send_email.call_args
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
self.assertIn("About your organization:", body) self.assertIn("About your organization:", body)
@ -148,10 +148,10 @@ class TestEmails(TestCase):
@boto3_mocking.patching @boto3_mocking.patching
def test_submission_confirmation_no_about_your_organization_spacing(self): def test_submission_confirmation_no_about_your_organization_spacing(self):
"""Test line spacing without about your organization.""" """Test line spacing without about your organization."""
application = completed_application(has_about_your_organization=False) domain_request = completed_domain_request(has_about_your_organization=False)
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
with less_console_noise(): with less_console_noise():
application.submit() domain_request.submit()
_, kwargs = self.mock_client.send_email.call_args _, kwargs = self.mock_client.send_email.call_args
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
self.assertNotIn("About your organization:", body) self.assertNotIn("About your organization:", body)
@ -161,10 +161,10 @@ class TestEmails(TestCase):
@boto3_mocking.patching @boto3_mocking.patching
def test_submission_confirmation_anything_else_spacing(self): def test_submission_confirmation_anything_else_spacing(self):
"""Test line spacing with anything else.""" """Test line spacing with anything else."""
application = completed_application(has_anything_else=True) domain_request = completed_domain_request(has_anything_else=True)
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
with less_console_noise(): with less_console_noise():
application.submit() domain_request.submit()
_, kwargs = self.mock_client.send_email.call_args _, kwargs = self.mock_client.send_email.call_args
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
# spacing should be right between adjacent elements # spacing should be right between adjacent elements
@ -173,10 +173,10 @@ class TestEmails(TestCase):
@boto3_mocking.patching @boto3_mocking.patching
def test_submission_confirmation_no_anything_else_spacing(self): def test_submission_confirmation_no_anything_else_spacing(self):
"""Test line spacing without anything else.""" """Test line spacing without anything else."""
application = completed_application(has_anything_else=False) domain_request = completed_domain_request(has_anything_else=False)
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
with less_console_noise(): with less_console_noise():
application.submit() domain_request.submit()
_, kwargs = self.mock_client.send_email.call_args _, kwargs = self.mock_client.send_email.call_args
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
self.assertNotIn("Anything else", body) self.assertNotIn("Anything else", body)

View file

@ -4,7 +4,7 @@ import json
from django.test import TestCase, RequestFactory from django.test import TestCase, RequestFactory
from api.views import available from api.views import available
from registrar.forms.application_wizard import ( from registrar.forms.domain_request_wizard import (
AlternativeDomainForm, AlternativeDomainForm,
CurrentSitesForm, CurrentSitesForm,
DotGovDomainForm, DotGovDomainForm,

View file

@ -34,10 +34,10 @@ class TestGroups(TestCase):
"view_logentry", "view_logentry",
"change_contact", "change_contact",
"view_domain", "view_domain",
"change_domainapplication",
"change_domaininformation", "change_domaininformation",
"add_domaininvitation", "add_domaininvitation",
"view_domaininvitation", "view_domaininvitation",
"change_domainrequest",
"change_draftdomain", "change_draftdomain",
"analyst_access_permission", "analyst_access_permission",
"change_user", "change_user",

View file

@ -4,7 +4,7 @@ from unittest.mock import patch
from registrar.models import ( from registrar.models import (
Contact, Contact,
DomainApplication, DomainRequest,
DomainInformation, DomainInformation,
User, User,
Website, Website,
@ -17,51 +17,51 @@ from registrar.models import (
import boto3_mocking import boto3_mocking
from registrar.models.transition_domain import TransitionDomain from registrar.models.transition_domain import TransitionDomain
from registrar.models.verified_by_staff import VerifiedByStaff # type: ignore from registrar.models.verified_by_staff import VerifiedByStaff # type: ignore
from .common import MockSESClient, less_console_noise, completed_application, set_applications_investigators from .common import MockSESClient, less_console_noise, completed_domain_request, set_domain_request_investigators
from django_fsm import TransitionNotAllowed from django_fsm import TransitionNotAllowed
# Test comment for push -- will remove # Test comment for push -- will remove
# The DomainApplication submit method has a side effect of sending an email # The DomainRequest submit method has a side effect of sending an email
# with AWS SES, so mock that out in all of these test cases # with AWS SES, so mock that out in all of these test cases
@boto3_mocking.patching @boto3_mocking.patching
class TestDomainApplication(TestCase): class TestDomainRequest(TestCase):
def setUp(self): def setUp(self):
self.started_application = completed_application( self.started_domain_request = completed_domain_request(
status=DomainApplication.ApplicationStatus.STARTED, name="started.gov" status=DomainRequest.DomainRequestStatus.STARTED, name="started.gov"
) )
self.submitted_application = completed_application( self.submitted_domain_request = completed_domain_request(
status=DomainApplication.ApplicationStatus.SUBMITTED, name="submitted.gov" status=DomainRequest.DomainRequestStatus.SUBMITTED, name="submitted.gov"
) )
self.in_review_application = completed_application( self.in_review_domain_request = completed_domain_request(
status=DomainApplication.ApplicationStatus.IN_REVIEW, name="in-review.gov" status=DomainRequest.DomainRequestStatus.IN_REVIEW, name="in-review.gov"
) )
self.action_needed_application = completed_application( self.action_needed_domain_request = completed_domain_request(
status=DomainApplication.ApplicationStatus.ACTION_NEEDED, name="action-needed.gov" status=DomainRequest.DomainRequestStatus.ACTION_NEEDED, name="action-needed.gov"
) )
self.approved_application = completed_application( self.approved_domain_request = completed_domain_request(
status=DomainApplication.ApplicationStatus.APPROVED, name="approved.gov" status=DomainRequest.DomainRequestStatus.APPROVED, name="approved.gov"
) )
self.withdrawn_application = completed_application( self.withdrawn_domain_request = completed_domain_request(
status=DomainApplication.ApplicationStatus.WITHDRAWN, name="withdrawn.gov" status=DomainRequest.DomainRequestStatus.WITHDRAWN, name="withdrawn.gov"
) )
self.rejected_application = completed_application( self.rejected_domain_request = completed_domain_request(
status=DomainApplication.ApplicationStatus.REJECTED, name="rejected.gov" status=DomainRequest.DomainRequestStatus.REJECTED, name="rejected.gov"
) )
self.ineligible_application = completed_application( self.ineligible_domain_request = completed_domain_request(
status=DomainApplication.ApplicationStatus.INELIGIBLE, name="ineligible.gov" status=DomainRequest.DomainRequestStatus.INELIGIBLE, name="ineligible.gov"
) )
# Store all application statuses in a variable for ease of use # Store all application statuses in a variable for ease of use
self.all_applications = [ self.all_applications = [
self.started_application, self.started_domain_request,
self.submitted_application, self.submitted_domain_request,
self.in_review_application, self.in_review_domain_request,
self.action_needed_application, self.action_needed_domain_request,
self.approved_application, self.approved_domain_request,
self.withdrawn_application, self.withdrawn_domain_request,
self.rejected_application, self.rejected_domain_request,
self.ineligible_application, self.ineligible_domain_request,
] ]
self.mock_client = MockSESClient() self.mock_client = MockSESClient()
@ -76,19 +76,19 @@ class TestDomainApplication(TestCase):
return self.assertRaises(Exception, None, exception_type) return self.assertRaises(Exception, None, exception_type)
def test_empty_create_fails(self): def test_empty_create_fails(self):
"""Can't create a completely empty domain application. """Can't create a completely empty domain request.
NOTE: something about theexception this test raises messes up with the NOTE: something about theexception this test raises messes up with the
atomic block in a custom tearDown method for the parent test class.""" atomic block in a custom tearDown method for the parent test class."""
with less_console_noise(): with less_console_noise():
with self.assertRaisesRegex(IntegrityError, "creator"): with self.assertRaisesRegex(IntegrityError, "creator"):
DomainApplication.objects.create() DomainRequest.objects.create()
def test_minimal_create(self): def test_minimal_create(self):
"""Can create with just a creator.""" """Can create with just a creator."""
with less_console_noise(): with less_console_noise():
user, _ = User.objects.get_or_create(username="testy") user, _ = User.objects.get_or_create(username="testy")
application = DomainApplication.objects.create(creator=user) domain_request = DomainRequest.objects.create(creator=user)
self.assertEqual(application.status, DomainApplication.ApplicationStatus.STARTED) self.assertEqual(domain_request.status, DomainRequest.DomainRequestStatus.STARTED)
def test_full_create(self): def test_full_create(self):
"""Can create with all fields.""" """Can create with all fields."""
@ -98,11 +98,11 @@ class TestDomainApplication(TestCase):
com_website, _ = Website.objects.get_or_create(website="igorville.com") com_website, _ = Website.objects.get_or_create(website="igorville.com")
gov_website, _ = Website.objects.get_or_create(website="igorville.gov") gov_website, _ = Website.objects.get_or_create(website="igorville.gov")
domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
application = DomainApplication.objects.create( domain_request = DomainRequest.objects.create(
creator=user, creator=user,
investigator=user, investigator=user,
organization_type=DomainApplication.OrganizationChoices.FEDERAL, organization_type=DomainRequest.OrganizationChoices.FEDERAL,
federal_type=DomainApplication.BranchChoices.EXECUTIVE, federal_type=DomainRequest.BranchChoices.EXECUTIVE,
is_election_board=False, is_election_board=False,
organization_name="Test", organization_name="Test",
address_line1="100 Main St.", address_line1="100 Main St.",
@ -116,10 +116,10 @@ class TestDomainApplication(TestCase):
anything_else="All of Igorville loves the dotgov program.", anything_else="All of Igorville loves the dotgov program.",
is_policy_acknowledged=True, is_policy_acknowledged=True,
) )
application.current_websites.add(com_website) domain_request.current_websites.add(com_website)
application.alternative_domains.add(gov_website) domain_request.alternative_domains.add(gov_website)
application.other_contacts.add(contact) domain_request.other_contacts.add(contact)
application.save() domain_request.save()
def test_domain_info(self): def test_domain_info(self):
"""Can create domain info with all fields.""" """Can create domain info with all fields."""
@ -152,35 +152,35 @@ class TestDomainApplication(TestCase):
def test_status_fsm_submit_fail(self): def test_status_fsm_submit_fail(self):
with less_console_noise(): with less_console_noise():
user, _ = User.objects.get_or_create(username="testy") user, _ = User.objects.get_or_create(username="testy")
application = DomainApplication.objects.create(creator=user) domain_request = DomainRequest.objects.create(creator=user)
with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
with less_console_noise(): with less_console_noise():
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
# can't submit an application with a null domain name # can't submit a domain request with a null domain name
application.submit() domain_request.submit()
def test_status_fsm_submit_succeed(self): def test_status_fsm_submit_succeed(self):
with less_console_noise(): with less_console_noise():
user, _ = User.objects.get_or_create(username="testy") user, _ = User.objects.get_or_create(username="testy")
site = DraftDomain.objects.create(name="igorville.gov") site = DraftDomain.objects.create(name="igorville.gov")
application = DomainApplication.objects.create(creator=user, requested_domain=site) domain_request = DomainRequest.objects.create(creator=user, requested_domain=site)
# no submitter email so this emits a log warning # no submitter email so this emits a log warning
with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
with less_console_noise(): with less_console_noise():
application.submit() domain_request.submit()
self.assertEqual(application.status, application.ApplicationStatus.SUBMITTED) self.assertEqual(domain_request.status, domain_request.DomainRequestStatus.SUBMITTED)
def check_email_sent(self, application: DomainApplication, msg, action, expected_count): def check_email_sent(self, domain_request, msg, action, expected_count):
"""Check if an email was sent after performing an action.""" """Check if an email was sent after performing an action."""
with self.subTest(msg=msg, action=action): with self.subTest(msg=msg, action=action):
with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
with less_console_noise(): with less_console_noise():
# Perform the specified action # Perform the specified action
action_method = getattr(application, action) action_method = getattr(domain_request, action)
action_method() action_method()
# Check if an email was sent # Check if an email was sent
@ -192,53 +192,53 @@ class TestDomainApplication(TestCase):
self.assertEqual(len(sent_emails), expected_count) self.assertEqual(len(sent_emails), expected_count)
def test_submit_from_started_sends_email(self): def test_submit_from_started_sends_email(self):
msg = "Create an application and submit it and see if email was sent." msg = "Create a domain request and submit it and see if email was sent."
application = completed_application() domain_request = completed_domain_request()
self.check_email_sent(application, msg, "submit", 1) self.check_email_sent(domain_request, msg, "submit", 1)
def test_submit_from_withdrawn_sends_email(self): def test_submit_from_withdrawn_sends_email(self):
msg = "Create a withdrawn application and submit it and see if email was sent." msg = "Create a withdrawn domain request and submit it and see if email was sent."
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.WITHDRAWN)
self.check_email_sent(application, msg, "submit", 1) self.check_email_sent(domain_request, msg, "submit", 1)
def test_submit_from_action_needed_does_not_send_email(self): def test_submit_from_action_needed_does_not_send_email(self):
msg = "Create an application with ACTION_NEEDED status and submit it, check if email was not sent." msg = "Create a domain request with ACTION_NEEDED status and submit it, check if email was not sent."
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.ACTION_NEEDED)
self.check_email_sent(application, msg, "submit", 0) self.check_email_sent(domain_request, msg, "submit", 0)
def test_submit_from_in_review_does_not_send_email(self): def test_submit_from_in_review_does_not_send_email(self):
msg = "Create a withdrawn application and submit it and see if email was sent." msg = "Create a withdrawn domain request and submit it and see if email was sent."
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW)
self.check_email_sent(application, msg, "submit", 0) self.check_email_sent(domain_request, msg, "submit", 0)
def test_approve_sends_email(self): def test_approve_sends_email(self):
msg = "Create an application and approve it and see if email was sent." msg = "Create a domain request and approve it and see if email was sent."
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW)
self.check_email_sent(application, msg, "approve", 1) self.check_email_sent(domain_request, msg, "approve", 1)
def test_withdraw_sends_email(self): def test_withdraw_sends_email(self):
msg = "Create an application and withdraw it and see if email was sent." msg = "Create a domain request and withdraw it and see if email was sent."
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW)
self.check_email_sent(application, msg, "withdraw", 1) self.check_email_sent(domain_request, msg, "withdraw", 1)
def test_reject_sends_email(self): def test_reject_sends_email(self):
msg = "Create an application and reject it and see if email was sent." msg = "Create a domain request and reject it and see if email was sent."
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.APPROVED)
self.check_email_sent(application, msg, "reject", 1) self.check_email_sent(domain_request, msg, "reject", 1)
def test_reject_with_prejudice_does_not_send_email(self): def test_reject_with_prejudice_does_not_send_email(self):
msg = "Create an application and reject it with prejudice and see if email was sent." msg = "Create a domain request and reject it with prejudice and see if email was sent."
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.APPROVED)
self.check_email_sent(application, msg, "reject_with_prejudice", 0) self.check_email_sent(domain_request, msg, "reject_with_prejudice", 0)
def assert_fsm_transition_raises_error(self, test_cases, method_to_run): def assert_fsm_transition_raises_error(self, test_cases, method_to_run):
"""Given a list of test cases, check if each transition throws the intended error""" """Given a list of test cases, check if each transition throws the intended error"""
with boto3_mocking.clients.handler_for("sesv2", self.mock_client), less_console_noise(): with boto3_mocking.clients.handler_for("sesv2", self.mock_client), less_console_noise():
for application, exception_type in test_cases: for domain_request, exception_type in test_cases:
with self.subTest(application=application, exception_type=exception_type): with self.subTest(domain_request=domain_request, exception_type=exception_type):
with self.assertRaises(exception_type): with self.assertRaises(exception_type):
# Retrieve the method by name from the application object and call it # Retrieve the method by name from the domain_request object and call it
method = getattr(application, method_to_run) method = getattr(domain_request, method_to_run)
# Call the method # Call the method
method() method()
@ -262,12 +262,14 @@ class TestDomainApplication(TestCase):
""" """
test_cases = [ test_cases = [
(self.in_review_application, TransitionNotAllowed), (self.started_domain_request, TransitionNotAllowed),
(self.action_needed_application, TransitionNotAllowed), (self.in_review_domain_request, TransitionNotAllowed),
(self.action_needed_domain_request, TransitionNotAllowed),
(self.withdrawn_domain_request, TransitionNotAllowed),
] ]
# Set all investigators to none # Set all investigators to none
set_applications_investigators(self.all_applications, None) set_domain_request_investigators(self.all_applications, None)
self.assert_fsm_transition_does_not_raise_error(test_cases, "submit") self.assert_fsm_transition_does_not_raise_error(test_cases, "submit")
@ -278,13 +280,13 @@ class TestDomainApplication(TestCase):
""" """
test_cases = [ test_cases = [
(self.in_review_application, TransitionNotAllowed), (self.in_review_domain_request, TransitionNotAllowed),
(self.action_needed_application, TransitionNotAllowed), (self.action_needed_domain_request, TransitionNotAllowed),
] ]
# Set all investigators to a user with no staff privs # Set all investigators to a user with no staff privs
user, _ = User.objects.get_or_create(username="pancakesyrup", is_staff=False) user, _ = User.objects.get_or_create(username="pancakesyrup", is_staff=False)
set_applications_investigators(self.all_applications, user) set_domain_request_investigators(self.all_applications, user)
self.assert_fsm_transition_does_not_raise_error(test_cases, "submit") self.assert_fsm_transition_does_not_raise_error(test_cases, "submit")
@ -293,10 +295,10 @@ class TestDomainApplication(TestCase):
Test that calling submit from allowable statuses does raises TransitionNotAllowed. Test that calling submit from allowable statuses does raises TransitionNotAllowed.
""" """
test_cases = [ test_cases = [
(self.started_application, TransitionNotAllowed), (self.started_domain_request, TransitionNotAllowed),
(self.in_review_application, TransitionNotAllowed), (self.in_review_domain_request, TransitionNotAllowed),
(self.action_needed_application, TransitionNotAllowed), (self.action_needed_domain_request, TransitionNotAllowed),
(self.withdrawn_application, TransitionNotAllowed), (self.withdrawn_domain_request, TransitionNotAllowed),
] ]
self.assert_fsm_transition_does_not_raise_error(test_cases, "submit") self.assert_fsm_transition_does_not_raise_error(test_cases, "submit")
@ -309,27 +311,27 @@ class TestDomainApplication(TestCase):
with less_console_noise(): with less_console_noise():
try: try:
# Make a submission # Make a submission
self.in_review_application.submit() self.in_review_domain_request.submit()
# Rerun the old method to get back to the original state # Rerun the old method to get back to the original state
self.in_review_application.in_review() self.in_review_domain_request.in_review()
# Make another submission # Make another submission
self.in_review_application.submit() self.in_review_domain_request.submit()
except TransitionNotAllowed: except TransitionNotAllowed:
self.fail("TransitionNotAllowed was raised, but it was not expected.") self.fail("TransitionNotAllowed was raised, but it was not expected.")
self.assertEqual(self.in_review_application.status, DomainApplication.ApplicationStatus.SUBMITTED) self.assertEqual(self.in_review_domain_request.status, DomainRequest.DomainRequestStatus.SUBMITTED)
def test_submit_transition_not_allowed(self): def test_submit_transition_not_allowed(self):
""" """
Test that calling submit against transition rules raises TransitionNotAllowed. Test that calling submit against transition rules raises TransitionNotAllowed.
""" """
test_cases = [ test_cases = [
(self.submitted_application, TransitionNotAllowed), (self.submitted_domain_request, TransitionNotAllowed),
(self.approved_application, TransitionNotAllowed), (self.approved_domain_request, TransitionNotAllowed),
(self.rejected_application, TransitionNotAllowed), (self.rejected_domain_request, TransitionNotAllowed),
(self.ineligible_application, TransitionNotAllowed), (self.ineligible_domain_request, TransitionNotAllowed),
] ]
self.assert_fsm_transition_raises_error(test_cases, "submit") self.assert_fsm_transition_raises_error(test_cases, "submit")
@ -339,11 +341,11 @@ class TestDomainApplication(TestCase):
Test that calling in_review from allowable statuses does raises TransitionNotAllowed. Test that calling in_review from allowable statuses does raises TransitionNotAllowed.
""" """
test_cases = [ test_cases = [
(self.submitted_application, TransitionNotAllowed), (self.submitted_domain_request, TransitionNotAllowed),
(self.action_needed_application, TransitionNotAllowed), (self.action_needed_domain_request, TransitionNotAllowed),
(self.approved_application, TransitionNotAllowed), (self.approved_domain_request, TransitionNotAllowed),
(self.rejected_application, TransitionNotAllowed), (self.rejected_domain_request, TransitionNotAllowed),
(self.ineligible_application, TransitionNotAllowed), (self.ineligible_domain_request, TransitionNotAllowed),
] ]
self.assert_fsm_transition_does_not_raise_error(test_cases, "in_review") self.assert_fsm_transition_does_not_raise_error(test_cases, "in_review")
@ -354,14 +356,14 @@ class TestDomainApplication(TestCase):
""" """
test_cases = [ test_cases = [
(self.action_needed_application, TransitionNotAllowed), (self.action_needed_domain_request, TransitionNotAllowed),
(self.approved_application, TransitionNotAllowed), (self.approved_domain_request, TransitionNotAllowed),
(self.rejected_application, TransitionNotAllowed), (self.rejected_domain_request, TransitionNotAllowed),
(self.ineligible_application, TransitionNotAllowed), (self.ineligible_domain_request, TransitionNotAllowed),
] ]
# Set all investigators to none # Set all investigators to none
set_applications_investigators(self.all_applications, None) set_domain_request_investigators(self.all_applications, None)
self.assert_fsm_transition_raises_error(test_cases, "in_review") self.assert_fsm_transition_raises_error(test_cases, "in_review")
@ -389,9 +391,9 @@ class TestDomainApplication(TestCase):
Test that calling in_review against transition rules raises TransitionNotAllowed. Test that calling in_review against transition rules raises TransitionNotAllowed.
""" """
test_cases = [ test_cases = [
(self.started_application, TransitionNotAllowed), (self.started_domain_request, TransitionNotAllowed),
(self.in_review_application, TransitionNotAllowed), (self.in_review_domain_request, TransitionNotAllowed),
(self.withdrawn_application, TransitionNotAllowed), (self.withdrawn_domain_request, TransitionNotAllowed),
] ]
self.assert_fsm_transition_raises_error(test_cases, "in_review") self.assert_fsm_transition_raises_error(test_cases, "in_review")
@ -401,10 +403,10 @@ class TestDomainApplication(TestCase):
Test that calling action_needed from allowable statuses does raises TransitionNotAllowed. Test that calling action_needed from allowable statuses does raises TransitionNotAllowed.
""" """
test_cases = [ test_cases = [
(self.in_review_application, TransitionNotAllowed), (self.in_review_domain_request, TransitionNotAllowed),
(self.approved_application, TransitionNotAllowed), (self.approved_domain_request, TransitionNotAllowed),
(self.rejected_application, TransitionNotAllowed), (self.rejected_domain_request, TransitionNotAllowed),
(self.ineligible_application, TransitionNotAllowed), (self.ineligible_domain_request, TransitionNotAllowed),
] ]
self.assert_fsm_transition_does_not_raise_error(test_cases, "action_needed") self.assert_fsm_transition_does_not_raise_error(test_cases, "action_needed")
@ -415,14 +417,14 @@ class TestDomainApplication(TestCase):
""" """
test_cases = [ test_cases = [
(self.in_review_application, TransitionNotAllowed), (self.in_review_domain_request, TransitionNotAllowed),
(self.approved_application, TransitionNotAllowed), (self.approved_domain_request, TransitionNotAllowed),
(self.rejected_application, TransitionNotAllowed), (self.rejected_domain_request, TransitionNotAllowed),
(self.ineligible_application, TransitionNotAllowed), (self.ineligible_domain_request, TransitionNotAllowed),
] ]
# Set all investigators to none # Set all investigators to none
set_applications_investigators(self.all_applications, None) set_domain_request_investigators(self.all_applications, None)
self.assert_fsm_transition_raises_error(test_cases, "action_needed") self.assert_fsm_transition_raises_error(test_cases, "action_needed")
@ -449,10 +451,10 @@ class TestDomainApplication(TestCase):
Test that calling action_needed against transition rules raises TransitionNotAllowed. Test that calling action_needed against transition rules raises TransitionNotAllowed.
""" """
test_cases = [ test_cases = [
(self.started_application, TransitionNotAllowed), (self.started_domain_request, TransitionNotAllowed),
(self.submitted_application, TransitionNotAllowed), (self.submitted_domain_request, TransitionNotAllowed),
(self.action_needed_application, TransitionNotAllowed), (self.action_needed_domain_request, TransitionNotAllowed),
(self.withdrawn_application, TransitionNotAllowed), (self.withdrawn_domain_request, TransitionNotAllowed),
] ]
self.assert_fsm_transition_raises_error(test_cases, "action_needed") self.assert_fsm_transition_raises_error(test_cases, "action_needed")
@ -462,10 +464,10 @@ class TestDomainApplication(TestCase):
Test that calling action_needed from allowable statuses does raises TransitionNotAllowed. Test that calling action_needed from allowable statuses does raises TransitionNotAllowed.
""" """
test_cases = [ test_cases = [
(self.submitted_application, TransitionNotAllowed), (self.submitted_domain_request, TransitionNotAllowed),
(self.in_review_application, TransitionNotAllowed), (self.in_review_domain_request, TransitionNotAllowed),
(self.action_needed_application, TransitionNotAllowed), (self.action_needed_domain_request, TransitionNotAllowed),
(self.rejected_application, TransitionNotAllowed), (self.rejected_domain_request, TransitionNotAllowed),
] ]
self.assert_fsm_transition_does_not_raise_error(test_cases, "approve") self.assert_fsm_transition_does_not_raise_error(test_cases, "approve")
@ -476,13 +478,13 @@ class TestDomainApplication(TestCase):
""" """
test_cases = [ test_cases = [
(self.in_review_application, TransitionNotAllowed), (self.in_review_domain_request, TransitionNotAllowed),
(self.action_needed_application, TransitionNotAllowed), (self.action_needed_domain_request, TransitionNotAllowed),
(self.rejected_application, TransitionNotAllowed), (self.rejected_domain_request, TransitionNotAllowed),
] ]
# Set all investigators to none # Set all investigators to none
set_applications_investigators(self.all_applications, None) set_domain_request_investigators(self.all_applications, None)
self.assert_fsm_transition_raises_error(test_cases, "approve") self.assert_fsm_transition_raises_error(test_cases, "approve")
@ -511,7 +513,7 @@ class TestDomainApplication(TestCase):
with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
with less_console_noise(): with less_console_noise():
self.submitted_application.approve(send_email=False) self.submitted_domain_request.approve(send_email=False)
# Assert that no emails were sent # Assert that no emails were sent
self.assertEqual(len(self.mock_client.EMAILS_SENT), 0) self.assertEqual(len(self.mock_client.EMAILS_SENT), 0)
@ -521,10 +523,10 @@ class TestDomainApplication(TestCase):
Test that calling action_needed against transition rules raises TransitionNotAllowed. Test that calling action_needed against transition rules raises TransitionNotAllowed.
""" """
test_cases = [ test_cases = [
(self.started_application, TransitionNotAllowed), (self.started_domain_request, TransitionNotAllowed),
(self.approved_application, TransitionNotAllowed), (self.approved_domain_request, TransitionNotAllowed),
(self.withdrawn_application, TransitionNotAllowed), (self.withdrawn_domain_request, TransitionNotAllowed),
(self.ineligible_application, TransitionNotAllowed), (self.ineligible_domain_request, TransitionNotAllowed),
] ]
self.assert_fsm_transition_raises_error(test_cases, "approve") self.assert_fsm_transition_raises_error(test_cases, "approve")
@ -533,9 +535,9 @@ class TestDomainApplication(TestCase):
Test that calling action_needed from allowable statuses does raises TransitionNotAllowed. Test that calling action_needed from allowable statuses does raises TransitionNotAllowed.
""" """
test_cases = [ test_cases = [
(self.submitted_application, TransitionNotAllowed), (self.submitted_domain_request, TransitionNotAllowed),
(self.in_review_application, TransitionNotAllowed), (self.in_review_domain_request, TransitionNotAllowed),
(self.action_needed_application, TransitionNotAllowed), (self.action_needed_domain_request, TransitionNotAllowed),
] ]
self.assert_fsm_transition_does_not_raise_error(test_cases, "withdraw") self.assert_fsm_transition_does_not_raise_error(test_cases, "withdraw")
@ -547,13 +549,13 @@ class TestDomainApplication(TestCase):
""" """
test_cases = [ test_cases = [
(self.submitted_application, TransitionNotAllowed), (self.submitted_domain_request, TransitionNotAllowed),
(self.in_review_application, TransitionNotAllowed), (self.in_review_domain_request, TransitionNotAllowed),
(self.action_needed_application, TransitionNotAllowed), (self.action_needed_domain_request, TransitionNotAllowed),
] ]
# Set all investigators to none # Set all investigators to none
set_applications_investigators(self.all_applications, None) set_domain_request_investigators(self.all_applications, None)
self.assert_fsm_transition_does_not_raise_error(test_cases, "withdraw") self.assert_fsm_transition_does_not_raise_error(test_cases, "withdraw")
@ -580,11 +582,11 @@ class TestDomainApplication(TestCase):
Test that calling action_needed against transition rules raises TransitionNotAllowed. Test that calling action_needed against transition rules raises TransitionNotAllowed.
""" """
test_cases = [ test_cases = [
(self.started_application, TransitionNotAllowed), (self.started_domain_request, TransitionNotAllowed),
(self.approved_application, TransitionNotAllowed), (self.approved_domain_request, TransitionNotAllowed),
(self.withdrawn_application, TransitionNotAllowed), (self.withdrawn_domain_request, TransitionNotAllowed),
(self.rejected_application, TransitionNotAllowed), (self.rejected_domain_request, TransitionNotAllowed),
(self.ineligible_application, TransitionNotAllowed), (self.ineligible_domain_request, TransitionNotAllowed),
] ]
self.assert_fsm_transition_raises_error(test_cases, "withdraw") self.assert_fsm_transition_raises_error(test_cases, "withdraw")
@ -594,9 +596,9 @@ class TestDomainApplication(TestCase):
Test that calling action_needed from allowable statuses does raises TransitionNotAllowed. Test that calling action_needed from allowable statuses does raises TransitionNotAllowed.
""" """
test_cases = [ test_cases = [
(self.in_review_application, TransitionNotAllowed), (self.in_review_domain_request, TransitionNotAllowed),
(self.action_needed_application, TransitionNotAllowed), (self.action_needed_domain_request, TransitionNotAllowed),
(self.approved_application, TransitionNotAllowed), (self.approved_domain_request, TransitionNotAllowed),
] ]
self.assert_fsm_transition_does_not_raise_error(test_cases, "reject") self.assert_fsm_transition_does_not_raise_error(test_cases, "reject")
@ -607,13 +609,13 @@ class TestDomainApplication(TestCase):
""" """
test_cases = [ test_cases = [
(self.in_review_application, TransitionNotAllowed), (self.in_review_domain_request, TransitionNotAllowed),
(self.action_needed_application, TransitionNotAllowed), (self.action_needed_domain_request, TransitionNotAllowed),
(self.approved_application, TransitionNotAllowed), (self.approved_domain_request, TransitionNotAllowed),
] ]
# Set all investigators to none # Set all investigators to none
set_applications_investigators(self.all_applications, None) set_domain_request_investigators(self.all_applications, None)
self.assert_fsm_transition_raises_error(test_cases, "reject") self.assert_fsm_transition_raises_error(test_cases, "reject")
@ -639,11 +641,11 @@ class TestDomainApplication(TestCase):
Test that calling action_needed against transition rules raises TransitionNotAllowed. Test that calling action_needed against transition rules raises TransitionNotAllowed.
""" """
test_cases = [ test_cases = [
(self.started_application, TransitionNotAllowed), (self.started_domain_request, TransitionNotAllowed),
(self.submitted_application, TransitionNotAllowed), (self.submitted_domain_request, TransitionNotAllowed),
(self.withdrawn_application, TransitionNotAllowed), (self.withdrawn_domain_request, TransitionNotAllowed),
(self.rejected_application, TransitionNotAllowed), (self.rejected_domain_request, TransitionNotAllowed),
(self.ineligible_application, TransitionNotAllowed), (self.ineligible_domain_request, TransitionNotAllowed),
] ]
self.assert_fsm_transition_raises_error(test_cases, "reject") self.assert_fsm_transition_raises_error(test_cases, "reject")
@ -653,10 +655,10 @@ class TestDomainApplication(TestCase):
Test that calling action_needed from allowable statuses does raises TransitionNotAllowed. Test that calling action_needed from allowable statuses does raises TransitionNotAllowed.
""" """
test_cases = [ test_cases = [
(self.in_review_application, TransitionNotAllowed), (self.in_review_domain_request, TransitionNotAllowed),
(self.action_needed_application, TransitionNotAllowed), (self.action_needed_domain_request, TransitionNotAllowed),
(self.approved_application, TransitionNotAllowed), (self.approved_domain_request, TransitionNotAllowed),
(self.rejected_application, TransitionNotAllowed), (self.rejected_domain_request, TransitionNotAllowed),
] ]
self.assert_fsm_transition_does_not_raise_error(test_cases, "reject_with_prejudice") self.assert_fsm_transition_does_not_raise_error(test_cases, "reject_with_prejudice")
@ -667,14 +669,14 @@ class TestDomainApplication(TestCase):
""" """
test_cases = [ test_cases = [
(self.in_review_application, TransitionNotAllowed), (self.in_review_domain_request, TransitionNotAllowed),
(self.action_needed_application, TransitionNotAllowed), (self.action_needed_domain_request, TransitionNotAllowed),
(self.approved_application, TransitionNotAllowed), (self.approved_domain_request, TransitionNotAllowed),
(self.rejected_application, TransitionNotAllowed), (self.rejected_domain_request, TransitionNotAllowed),
] ]
# Set all investigators to none # Set all investigators to none
set_applications_investigators(self.all_applications, None) set_domain_request_investigators(self.all_applications, None)
self.assert_fsm_transition_raises_error(test_cases, "reject_with_prejudice") self.assert_fsm_transition_raises_error(test_cases, "reject_with_prejudice")
@ -701,21 +703,21 @@ class TestDomainApplication(TestCase):
Test that calling action_needed against transition rules raises TransitionNotAllowed. Test that calling action_needed against transition rules raises TransitionNotAllowed.
""" """
test_cases = [ test_cases = [
(self.started_application, TransitionNotAllowed), (self.started_domain_request, TransitionNotAllowed),
(self.submitted_application, TransitionNotAllowed), (self.submitted_domain_request, TransitionNotAllowed),
(self.withdrawn_application, TransitionNotAllowed), (self.withdrawn_domain_request, TransitionNotAllowed),
(self.ineligible_application, TransitionNotAllowed), (self.ineligible_domain_request, TransitionNotAllowed),
] ]
self.assert_fsm_transition_raises_error(test_cases, "reject_with_prejudice") self.assert_fsm_transition_raises_error(test_cases, "reject_with_prejudice")
def test_transition_not_allowed_approved_in_review_when_domain_is_active(self): def test_transition_not_allowed_approved_in_review_when_domain_is_active(self):
"""Create an application with status approved, create a matching domain that """Create a domain request with status approved, create a matching domain that
is active, and call in_review against transition rules""" is active, and call in_review against transition rules"""
domain = Domain.objects.create(name=self.approved_application.requested_domain.name) domain = Domain.objects.create(name=self.approved_domain_request.requested_domain.name)
self.approved_application.approved_domain = domain self.approved_domain_request.approved_domain = domain
self.approved_application.save() self.approved_domain_request.save()
# Define a custom implementation for is_active # Define a custom implementation for is_active
def custom_is_active(self): def custom_is_active(self):
@ -727,15 +729,15 @@ class TestDomainApplication(TestCase):
with patch.object(Domain, "is_active", custom_is_active): with patch.object(Domain, "is_active", custom_is_active):
# Now, when you call is_active on Domain, it will return True # Now, when you call is_active on Domain, it will return True
with self.assertRaises(TransitionNotAllowed): with self.assertRaises(TransitionNotAllowed):
self.approved_application.in_review() self.approved_domain_request.in_review()
def test_transition_not_allowed_approved_action_needed_when_domain_is_active(self): def test_transition_not_allowed_approved_action_needed_when_domain_is_active(self):
"""Create an application with status approved, create a matching domain that """Create a domain request with status approved, create a matching domain that
is active, and call action_needed against transition rules""" is active, and call action_needed against transition rules"""
domain = Domain.objects.create(name=self.approved_application.requested_domain.name) domain = Domain.objects.create(name=self.approved_domain_request.requested_domain.name)
self.approved_application.approved_domain = domain self.approved_domain_request.approved_domain = domain
self.approved_application.save() self.approved_domain_request.save()
# Define a custom implementation for is_active # Define a custom implementation for is_active
def custom_is_active(self): def custom_is_active(self):
@ -747,15 +749,15 @@ class TestDomainApplication(TestCase):
with patch.object(Domain, "is_active", custom_is_active): with patch.object(Domain, "is_active", custom_is_active):
# Now, when you call is_active on Domain, it will return True # Now, when you call is_active on Domain, it will return True
with self.assertRaises(TransitionNotAllowed): with self.assertRaises(TransitionNotAllowed):
self.approved_application.action_needed() self.approved_domain_request.action_needed()
def test_transition_not_allowed_approved_rejected_when_domain_is_active(self): def test_transition_not_allowed_approved_rejected_when_domain_is_active(self):
"""Create an application with status approved, create a matching domain that """Create a domain request with status approved, create a matching domain that
is active, and call reject against transition rules""" is active, and call reject against transition rules"""
domain = Domain.objects.create(name=self.approved_application.requested_domain.name) domain = Domain.objects.create(name=self.approved_domain_request.requested_domain.name)
self.approved_application.approved_domain = domain self.approved_domain_request.approved_domain = domain
self.approved_application.save() self.approved_domain_request.save()
# Define a custom implementation for is_active # Define a custom implementation for is_active
def custom_is_active(self): def custom_is_active(self):
@ -767,15 +769,15 @@ class TestDomainApplication(TestCase):
with patch.object(Domain, "is_active", custom_is_active): with patch.object(Domain, "is_active", custom_is_active):
# Now, when you call is_active on Domain, it will return True # Now, when you call is_active on Domain, it will return True
with self.assertRaises(TransitionNotAllowed): with self.assertRaises(TransitionNotAllowed):
self.approved_application.reject() self.approved_domain_request.reject()
def test_transition_not_allowed_approved_ineligible_when_domain_is_active(self): def test_transition_not_allowed_approved_ineligible_when_domain_is_active(self):
"""Create an application with status approved, create a matching domain that """Create a domain request with status approved, create a matching domain that
is active, and call reject_with_prejudice against transition rules""" is active, and call reject_with_prejudice against transition rules"""
domain = Domain.objects.create(name=self.approved_application.requested_domain.name) domain = Domain.objects.create(name=self.approved_domain_request.requested_domain.name)
self.approved_application.approved_domain = domain self.approved_domain_request.approved_domain = domain
self.approved_application.save() self.approved_domain_request.save()
# Define a custom implementation for is_active # Define a custom implementation for is_active
def custom_is_active(self): def custom_is_active(self):
@ -787,83 +789,83 @@ class TestDomainApplication(TestCase):
with patch.object(Domain, "is_active", custom_is_active): with patch.object(Domain, "is_active", custom_is_active):
# Now, when you call is_active on Domain, it will return True # Now, when you call is_active on Domain, it will return True
with self.assertRaises(TransitionNotAllowed): with self.assertRaises(TransitionNotAllowed):
self.approved_application.reject_with_prejudice() self.approved_domain_request.reject_with_prejudice()
def test_approve_from_rejected_clears_rejection_reason(self): def test_approve_from_rejected_clears_rejection_reason(self):
"""When transitioning from rejected to approved on a domain request, """When transitioning from rejected to approved on a domain request,
the rejection_reason is cleared.""" the rejection_reason is cleared."""
with less_console_noise(): with less_console_noise():
# Create a sample application # Create a sample domain request
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.REJECTED)
application.rejection_reason = DomainApplication.RejectionReasons.DOMAIN_PURPOSE domain_request.rejection_reason = DomainRequest.RejectionReasons.DOMAIN_PURPOSE
# Approve # Approve
with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
application.approve() domain_request.approve()
self.assertEqual(application.status, DomainApplication.ApplicationStatus.APPROVED) self.assertEqual(domain_request.status, DomainRequest.DomainRequestStatus.APPROVED)
self.assertEqual(application.rejection_reason, None) self.assertEqual(domain_request.rejection_reason, None)
def test_in_review_from_rejected_clears_rejection_reason(self): def test_in_review_from_rejected_clears_rejection_reason(self):
"""When transitioning from rejected to in_review on a domain request, """When transitioning from rejected to in_review on a domain request,
the rejection_reason is cleared.""" the rejection_reason is cleared."""
with less_console_noise(): with less_console_noise():
# Create a sample application # Create a sample domain request
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.REJECTED)
application.domain_is_not_active = True domain_request.domain_is_not_active = True
application.rejection_reason = DomainApplication.RejectionReasons.DOMAIN_PURPOSE domain_request.rejection_reason = DomainRequest.RejectionReasons.DOMAIN_PURPOSE
# Approve # Approve
with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
application.in_review() domain_request.in_review()
self.assertEqual(application.status, DomainApplication.ApplicationStatus.IN_REVIEW) self.assertEqual(domain_request.status, DomainRequest.DomainRequestStatus.IN_REVIEW)
self.assertEqual(application.rejection_reason, None) self.assertEqual(domain_request.rejection_reason, None)
def test_action_needed_from_rejected_clears_rejection_reason(self): def test_action_needed_from_rejected_clears_rejection_reason(self):
"""When transitioning from rejected to action_needed on a domain request, """When transitioning from rejected to action_needed on a domain request,
the rejection_reason is cleared.""" the rejection_reason is cleared."""
with less_console_noise(): with less_console_noise():
# Create a sample application # Create a sample domain request
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.REJECTED)
application.domain_is_not_active = True domain_request.domain_is_not_active = True
application.rejection_reason = DomainApplication.RejectionReasons.DOMAIN_PURPOSE domain_request.rejection_reason = DomainRequest.RejectionReasons.DOMAIN_PURPOSE
# Approve # Approve
with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
application.action_needed() domain_request.action_needed()
self.assertEqual(application.status, DomainApplication.ApplicationStatus.ACTION_NEEDED) self.assertEqual(domain_request.status, DomainRequest.DomainRequestStatus.ACTION_NEEDED)
self.assertEqual(application.rejection_reason, None) self.assertEqual(domain_request.rejection_reason, None)
def test_has_rationale_returns_true(self): def test_has_rationale_returns_true(self):
"""has_rationale() returns true when an application has no_other_contacts_rationale""" """has_rationale() returns true when a domain request has no_other_contacts_rationale"""
with less_console_noise(): with less_console_noise():
self.started_application.no_other_contacts_rationale = "You talkin' to me?" self.started_domain_request.no_other_contacts_rationale = "You talkin' to me?"
self.started_application.save() self.started_domain_request.save()
self.assertEquals(self.started_application.has_rationale(), True) self.assertEquals(self.started_domain_request.has_rationale(), True)
def test_has_rationale_returns_false(self): def test_has_rationale_returns_false(self):
"""has_rationale() returns false when an application has no no_other_contacts_rationale""" """has_rationale() returns false when a domain request has no no_other_contacts_rationale"""
with less_console_noise(): with less_console_noise():
self.assertEquals(self.started_application.has_rationale(), False) self.assertEquals(self.started_domain_request.has_rationale(), False)
def test_has_other_contacts_returns_true(self): def test_has_other_contacts_returns_true(self):
"""has_other_contacts() returns true when an application has other_contacts""" """has_other_contacts() returns true when a domain request has other_contacts"""
with less_console_noise(): with less_console_noise():
# completed_application has other contacts by default # completed_domain_request has other contacts by default
self.assertEquals(self.started_application.has_other_contacts(), True) self.assertEquals(self.started_domain_request.has_other_contacts(), True)
def test_has_other_contacts_returns_false(self): def test_has_other_contacts_returns_false(self):
"""has_other_contacts() returns false when an application has no other_contacts""" """has_other_contacts() returns false when a domain request has no other_contacts"""
with less_console_noise(): with less_console_noise():
application = completed_application( domain_request = completed_domain_request(
status=DomainApplication.ApplicationStatus.STARTED, name="no-others.gov", has_other_contacts=False status=DomainRequest.DomainRequestStatus.STARTED, name="no-others.gov", has_other_contacts=False
) )
self.assertEquals(application.has_other_contacts(), False) self.assertEquals(domain_request.has_other_contacts(), False)
class TestPermissions(TestCase): class TestPermissions(TestCase):
@ -882,15 +884,15 @@ class TestPermissions(TestCase):
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
user, _ = User.objects.get_or_create() user, _ = User.objects.get_or_create()
investigator, _ = User.objects.get_or_create(username="frenchtoast", is_staff=True) investigator, _ = User.objects.get_or_create(username="frenchtoast", is_staff=True)
application = DomainApplication.objects.create( domain_request = DomainRequest.objects.create(
creator=user, requested_domain=draft_domain, investigator=investigator creator=user, requested_domain=draft_domain, investigator=investigator
) )
with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
with less_console_noise(): with less_console_noise():
# skip using the submit method # skip using the submit method
application.status = DomainApplication.ApplicationStatus.SUBMITTED domain_request.status = DomainRequest.DomainRequestStatus.SUBMITTED
application.approve() domain_request.approve()
# should be a role for this user # should be a role for this user
domain = Domain.objects.get(name="igorville.gov") domain = Domain.objects.get(name="igorville.gov")
@ -909,7 +911,7 @@ class TestDomainInformation(TestCase):
self.mock_client.EMAILS_SENT.clear() self.mock_client.EMAILS_SENT.clear()
Domain.objects.all().delete() Domain.objects.all().delete()
DomainInformation.objects.all().delete() DomainInformation.objects.all().delete()
DomainApplication.objects.all().delete() DomainRequest.objects.all().delete()
User.objects.all().delete() User.objects.all().delete()
DraftDomain.objects.all().delete() DraftDomain.objects.all().delete()
@ -918,16 +920,13 @@ class TestDomainInformation(TestCase):
self.maxDiff = None self.maxDiff = None
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
user, _ = User.objects.get_or_create() user, _ = User.objects.get_or_create()
investigator, _ = User.objects.get_or_create(username="frenchtoast", is_staff=True) domain_request = DomainApplication.objects.create(creator=user, requested_domain=draft_domain, notes="test notes")
application = DomainApplication.objects.create(
creator=user, requested_domain=draft_domain, notes="test notes", investigator=investigator
)
with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
with less_console_noise(): with less_console_noise():
# skip using the submit method # skip using the submit method
application.status = DomainApplication.ApplicationStatus.SUBMITTED domain_request.status = DomainRequest.DomainRequestStatus.SUBMITTED
application.approve() domain_request.approve()
# should be an information present for this domain # should be an information present for this domain
domain = Domain.objects.get(name="igorville.gov") domain = Domain.objects.get(name="igorville.gov")
@ -940,7 +939,7 @@ class TestDomainInformation(TestCase):
creator=user, creator=user,
domain=domain, domain=domain,
notes="test notes", notes="test notes",
domain_application=application, domain_request=domain_request,
).__dict__ ).__dict__
# Test the two records for consistency # Test the two records for consistency
@ -1073,11 +1072,11 @@ class TestContact(TestCase):
self.contact, _ = Contact.objects.get_or_create(user=self.user) self.contact, _ = Contact.objects.get_or_create(user=self.user)
self.contact_as_ao, _ = Contact.objects.get_or_create(email="newguy@igorville.gov") self.contact_as_ao, _ = Contact.objects.get_or_create(email="newguy@igorville.gov")
self.application = DomainApplication.objects.create(creator=self.user, authorizing_official=self.contact_as_ao) self.domain_request = DomainRequest.objects.create(creator=self.user, authorizing_official=self.contact_as_ao)
def tearDown(self): def tearDown(self):
super().tearDown() super().tearDown()
DomainApplication.objects.all().delete() DomainRequest.objects.all().delete()
Contact.objects.all().delete() Contact.objects.all().delete()
User.objects.all().delete() User.objects.all().delete()
@ -1157,6 +1156,6 @@ class TestContact(TestCase):
# test for a contact which has one user defined # test for a contact which has one user defined
self.assertFalse(self.contact.has_more_than_one_join("user")) self.assertFalse(self.contact.has_more_than_one_join("user"))
self.assertTrue(self.contact.has_more_than_one_join("authorizing_official")) self.assertTrue(self.contact.has_more_than_one_join("authorizing_official"))
# test for a contact which is assigned as an authorizing official on an application # test for a contact which is assigned as an authorizing official on a domain request
self.assertFalse(self.contact_as_ao.has_more_than_one_join("authorizing_official")) self.assertFalse(self.contact_as_ao.has_more_than_one_join("authorizing_official"))
self.assertTrue(self.contact_as_ao.has_more_than_one_join("submitted_applications")) self.assertTrue(self.contact_as_ao.has_more_than_one_join("submitted_domain_requests"))

View file

@ -12,7 +12,7 @@ from django.utils.timezone import make_aware
from registrar.models import Domain, Host, HostIP from registrar.models import Domain, Host, HostIP
from unittest import skip from unittest import skip
from registrar.models.domain_application import DomainApplication from registrar.models.domain_request import DomainRequest
from registrar.models.domain_information import DomainInformation from registrar.models.domain_information import DomainInformation
from registrar.models.draft_domain import DraftDomain from registrar.models.draft_domain import DraftDomain
from registrar.models.public_contact import PublicContact from registrar.models.public_contact import PublicContact
@ -311,13 +311,13 @@ class TestDomainCache(MockEppLib):
class TestDomainCreation(MockEppLib): class TestDomainCreation(MockEppLib):
"""Rule: An approved domain application must result in a domain""" """Rule: An approved domain request must result in a domain"""
@boto3_mocking.patching @boto3_mocking.patching
def test_approved_application_creates_domain_locally(self): def test_approved_domain_request_creates_domain_locally(self):
""" """
Scenario: Analyst approves a domain application Scenario: Analyst approves a domain request
When the DomainApplication transitions to approved When the DomainRequest transitions to approved
Then a Domain exists in the database with the same `name` Then a Domain exists in the database with the same `name`
But a domain object does not exist in the registry But a domain object does not exist in the registry
""" """
@ -325,16 +325,16 @@ class TestDomainCreation(MockEppLib):
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
user, _ = User.objects.get_or_create() user, _ = User.objects.get_or_create()
investigator, _ = User.objects.get_or_create(username="frenchtoast", is_staff=True) investigator, _ = User.objects.get_or_create(username="frenchtoast", is_staff=True)
application = DomainApplication.objects.create( domain_request = DomainRequest.objects.create(
creator=user, requested_domain=draft_domain, investigator=investigator creator=user, requested_domain=draft_domain, investigator=investigator
) )
mock_client = MockSESClient() mock_client = MockSESClient()
with boto3_mocking.clients.handler_for("sesv2", mock_client): with boto3_mocking.clients.handler_for("sesv2", mock_client):
# skip using the submit method # skip using the submit method
application.status = DomainApplication.ApplicationStatus.SUBMITTED domain_request.status = DomainRequest.DomainRequestStatus.SUBMITTED
# transition to approve state # transition to approve state
application.approve() domain_request.approve()
# should have information present for this domain # should have information present for this domain
domain = Domain.objects.get(name="igorville.gov") domain = Domain.objects.get(name="igorville.gov")
self.assertTrue(domain) self.assertTrue(domain)
@ -398,7 +398,7 @@ class TestDomainCreation(MockEppLib):
def tearDown(self) -> None: def tearDown(self) -> None:
DomainInformation.objects.all().delete() DomainInformation.objects.all().delete()
DomainApplication.objects.all().delete() DomainRequest.objects.all().delete()
PublicContact.objects.all().delete() PublicContact.objects.all().delete()
Domain.objects.all().delete() Domain.objects.all().delete()
User.objects.all().delete() User.objects.all().delete()

View file

@ -27,7 +27,7 @@ logger = logging.getLogger(__name__)
class TestProcessedMigrations(TestCase): class TestProcessedMigrations(TestCase):
"""This test case class is designed to verify the idempotency of migrations """This test case class is designed to verify the idempotency of migrations
related to domain transitions in the application.""" related to domain transitions in the domain_request."""
def setUp(self): def setUp(self):
"""Defines the file name of migration_json and the folder its contained in""" """Defines the file name of migration_json and the folder its contained in"""

View file

@ -5,7 +5,7 @@ from .common import MockEppLib # type: ignore
from registrar.models import ( from registrar.models import (
DomainApplication, DomainRequest,
DomainInformation, DomainInformation,
) )
import logging import logging
@ -26,8 +26,8 @@ class TestViews(TestCase):
response = self.client.get("/") response = self.client.get("/")
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
def test_application_form_not_logged_in(self): def test_domain_request_form_not_logged_in(self):
"""Application form not accessible without a logged-in user.""" """Domain request form not accessible without a logged-in user."""
response = self.client.get("/request/") response = self.client.get("/request/")
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertIn("/login?next=/request/", response.headers["Location"]) self.assertIn("/login?next=/request/", response.headers["Location"])
@ -45,9 +45,9 @@ class TestWithUser(MockEppLib):
) )
def tearDown(self): def tearDown(self):
# delete any applications too # delete any domain requests too
super().tearDown() super().tearDown()
DomainApplication.objects.all().delete() DomainRequest.objects.all().delete()
DomainInformation.objects.all().delete() DomainInformation.objects.all().delete()
self.user.delete() self.user.delete()

File diff suppressed because it is too large Load diff

View file

@ -21,7 +21,7 @@ from registrar.utility.errors import (
) )
from registrar.models import ( from registrar.models import (
DomainApplication, DomainRequest,
Domain, Domain,
DomainInformation, DomainInformation,
DomainInvitation, DomainInvitation,
@ -120,7 +120,7 @@ class TestWithDomainPermissions(TestWithUser):
UserDomainRole.objects.all().delete() UserDomainRole.objects.all().delete()
if hasattr(self.domain, "contacts"): if hasattr(self.domain, "contacts"):
self.domain.contacts.all().delete() self.domain.contacts.all().delete()
DomainApplication.objects.all().delete() DomainRequest.objects.all().delete()
DomainInformation.objects.all().delete() DomainInformation.objects.all().delete()
PublicContact.objects.all().delete() PublicContact.objects.all().delete()
HostIP.objects.all().delete() HostIP.objects.all().delete()
@ -309,9 +309,9 @@ class TestDomainDetail(TestDomainOverview):
self.assertContains(detail_page, "(1.2.3.4,") self.assertContains(detail_page, "(1.2.3.4,")
self.assertContains(detail_page, "2.3.4.5)") self.assertContains(detail_page, "2.3.4.5)")
def test_domain_detail_with_no_information_or_application(self): def test_domain_detail_with_no_information_or_domain_request(self):
"""Test that domain management page returns 200 and displays error """Test that domain management page returns 200 and displays error
when no domain information or domain application exist""" when no domain information or domain request exist"""
with less_console_noise(): with less_console_noise():
# have to use staff user for this test # have to use staff user for this test
staff_user = create_user() staff_user = create_user()

View file

@ -1,4 +1,4 @@
from .application import * from .domain_request import *
from .domain import ( from .domain import (
DomainView, DomainView,
DomainAuthorizingOfficialView, DomainAuthorizingOfficialView,

View file

@ -8,18 +8,18 @@ from django.utils.translation import gettext_lazy as _
from django.views.generic import TemplateView from django.views.generic import TemplateView
from django.contrib import messages from django.contrib import messages
from registrar.forms import application_wizard as forms from registrar.forms import domain_request_wizard as forms
from registrar.models import DomainApplication from registrar.models import DomainRequest
from registrar.models.contact import Contact 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 DomainApplicationPermissionDeleteView from registrar.views.utility.permission_views import DomainRequestPermissionDeleteView
from .utility import ( from .utility import (
DomainApplicationPermissionView, DomainRequestPermissionView,
DomainApplicationPermissionWithdrawView, DomainRequestPermissionWithdrawView,
ApplicationWizardPermissionView, DomainRequestWizardPermissionView,
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -27,7 +27,7 @@ logger = logging.getLogger(__name__)
class Step(StrEnum): class Step(StrEnum):
""" """
Names for each page of the application wizard. Names for each page of the domain request wizard.
As with Django's own `TextChoices` class, steps will As with Django's own `TextChoices` class, steps will
appear in the order they are defined. (Order matters.) appear in the order they are defined. (Order matters.)
@ -50,15 +50,15 @@ class Step(StrEnum):
REVIEW = "review" REVIEW = "review"
class ApplicationWizard(ApplicationWizardPermissionView, TemplateView): class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
""" """
A common set of methods and configuration. A common set of methods and configuration.
The registrar's domain application is several pages of "steps". The registrar's domain request is several pages of "steps".
Together, these steps constitute a "wizard". Together, these steps constitute a "wizard".
This base class sets up a shared state (stored in the user's session) This base class sets up a shared state (stored in the user's session)
between pages of the application and provides common methods for between pages of the domain request and provides common methods for
processing form data. processing form data.
Views for each step should inherit from this base class. Views for each step should inherit from this base class.
@ -73,9 +73,9 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
# (this is not seen _in_ urls, only for Django's internal naming) # (this is not seen _in_ urls, only for Django's internal naming)
# NB: this is included here for reference. Do not change it without # NB: this is included here for reference. Do not change it without
# also changing the many places it is hardcoded in the HTML templates # also changing the many places it is hardcoded in the HTML templates
URL_NAMESPACE = "application" URL_NAMESPACE = "domain-request"
# name for accessing /application/<id>/edit # name for accessing /domain-request/<id>/edit
EDIT_URL_NAME = "edit-application" EDIT_URL_NAME = "edit-domain-request"
NEW_URL_NAME = "/request/" NEW_URL_NAME = "/request/"
# We need to pass our human-readable step titles as context to the templates. # We need to pass our human-readable step titles as context to the templates.
TITLES = { TITLES = {
@ -108,28 +108,28 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.steps = StepsHelper(self) self.steps = StepsHelper(self)
self._application = None # for caching self._domain_request = None # for caching
def has_pk(self): def has_pk(self):
"""Does this wizard know about a DomainApplication database record?""" """Does this wizard know about a DomainRequest database record?"""
return "application_id" in self.storage return "domain_request_id" in self.storage
@property @property
def prefix(self): def prefix(self):
"""Namespace the wizard to avoid clashes in session variable names.""" """Namespace the wizard to avoid clashes in session variable names."""
# this is a string literal but can be made dynamic if we'd like # this is a string literal but can be made dynamic if we'd like
# users to have multiple applications open for editing simultaneously # users to have multiple domain requests open for editing simultaneously
return "wizard_application" return "wizard_domain_request"
@property @property
def application(self) -> DomainApplication: def domain_request(self) -> DomainRequest:
""" """
Attempt to match the current wizard with a DomainApplication. Attempt to match the current wizard with a DomainRequest.
Will create an application if none exists. Will create a domain request if none exists.
""" """
if self._application: if self._domain_request:
return self._application return self._domain_request
# For linter. The else block should never be hit, but if it does, # For linter. The else block should never be hit, but if it does,
# there may be a UI consideration. That will need to be handled in another ticket. # there may be a UI consideration. That will need to be handled in another ticket.
@ -140,20 +140,20 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
raise ValueError("Invalid value for User") raise ValueError("Invalid value for User")
if self.has_pk(): if self.has_pk():
id = self.storage["application_id"] id = self.storage["domain_request_id"]
try: try:
self._application = DomainApplication.objects.get( self._domain_request = DomainRequest.objects.get(
creator=creator, creator=creator,
pk=id, pk=id,
) )
return self._application return self._domain_request
except DomainApplication.DoesNotExist: except DomainRequest.DoesNotExist:
logger.debug("Application id %s did not have a DomainApplication" % id) logger.debug("DomainRequest id %s did not have a DomainRequest" % id)
self._application = DomainApplication.objects.create(creator=self.request.user) self._domain_request = DomainRequest.objects.create(creator=self.request.user)
self.storage["application_id"] = self._application.id self.storage["domain_request_id"] = self._domain_request.id
return self._application return self._domain_request
@property @property
def storage(self): def storage(self):
@ -179,9 +179,9 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
def done(self): def done(self):
"""Called when the user clicks the submit button, if all forms are valid.""" """Called when the user clicks the submit button, if all forms are valid."""
self.application.submit() # change the status to submitted self.domain_request.submit() # change the status to submitted
self.application.save() self.domain_request.save()
logger.debug("Application object saved: %s", self.application.id) logger.debug("Domain Request object saved: %s", self.domain_request.id)
return redirect(reverse(f"{self.URL_NAMESPACE}:finished")) return redirect(reverse(f"{self.URL_NAMESPACE}:finished"))
def from_model(self, attribute: str, default, *args, **kwargs): def from_model(self, attribute: str, default, *args, **kwargs):
@ -194,8 +194,8 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
in the database before the wizard has been saved. in the database before the wizard has been saved.
""" """
if self.has_pk(): if self.has_pk():
if hasattr(self.application, attribute): if hasattr(self.domain_request, attribute):
attr = getattr(self.application, attribute) attr = getattr(self.domain_request, attribute)
if callable(attr): if callable(attr):
return attr(*args, **kwargs) return attr(*args, **kwargs)
else: else:
@ -210,24 +210,24 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
current_url = resolve(request.path_info).url_name current_url = resolve(request.path_info).url_name
# if user visited via an "edit" url, associate the id of the # if user visited via an "edit" url, associate the id of the
# application they are trying to edit to this wizard instance # domain request they are trying to edit to this wizard instance
# and remove any prior wizard data from their session # and remove any prior wizard data from their session
if current_url == self.EDIT_URL_NAME and "id" in kwargs: if current_url == self.EDIT_URL_NAME and "id" in kwargs:
del self.storage del self.storage
self.storage["application_id"] = kwargs["id"] self.storage["domain_request_id"] = kwargs["id"]
self.storage["step_history"] = self.db_check_for_unlocking_steps() self.storage["step_history"] = self.db_check_for_unlocking_steps()
# if accessing this class directly, redirect to the first step # if accessing this class directly, redirect to the first step
# in other words, if `ApplicationWizard` is called as view # in other words, if `DomainRequestWizard` is called as view
# directly by some redirect or url handler, we'll send users # directly by some redirect or url handler, we'll send users
# either to an acknowledgement page or to the first step in # either to an acknowledgement page or to the first step in
# the processes (if an edit rather than a new request); subclasses # the processes (if an edit rather than a new request); subclasses
# will NOT be redirected. The purpose of this is to allow code to # will NOT be redirected. The purpose of this is to allow code to
# send users "to the application wizard" without needing to # send users "to the domain request wizard" without needing to
# know which view is first in the list of steps. # know which view is first in the list of steps.
if self.__class__ == ApplicationWizard: if self.__class__ == DomainRequestWizard:
if request.path_info == self.NEW_URL_NAME: if request.path_info == self.NEW_URL_NAME:
return render(request, "application_intro.html") return render(request, "domain_request_intro.html")
else: else:
return self.goto(self.steps.first) return self.goto(self.steps.first)
@ -236,7 +236,7 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
context["forms"] = self.get_forms() context["forms"] = self.get_forms()
# if pending requests exist and user does not have approved domains, # if pending requests exist and user does not have approved domains,
# present message that domain application cannot be submitted # present message that domain request cannot be submitted
pending_requests = self.pending_requests() pending_requests = self.pending_requests()
if len(pending_requests) > 0: if len(pending_requests) > 0:
message_header = "You cannot submit this request yet" message_header = "You cannot submit this request yet"
@ -278,7 +278,7 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
kwargs = { kwargs = {
"files": files, "files": files,
"prefix": self.steps.current, "prefix": self.steps.current,
"application": self.application, # this is a property, not an object "domain_request": self.domain_request, # this is a property, not an object
} }
if step is None: if step is None:
@ -290,7 +290,7 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
instantiated = [] instantiated = []
for form in forms: for form in forms:
data = form.from_database(self.application) if self.has_pk() else None data = form.from_database(self.domain_request) if self.has_pk() else None
if use_post: if use_post:
instantiated.append(form(self.request.POST, **kwargs)) instantiated.append(form(self.request.POST, **kwargs))
elif use_db: elif use_db:
@ -303,72 +303,73 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
def pending_requests(self): def pending_requests(self):
"""return an array of pending requests if user has pending requests """return an array of pending requests if user has pending requests
and no approved requests""" and no approved requests"""
if self.approved_applications_exist() or self.approved_domains_exist(): if self.approved_domain_requests_exist() or self.approved_domains_exist():
return [] return []
else: else:
return self.pending_applications() return self.pending_domain_requests()
def approved_applications_exist(self): def approved_domain_requests_exist(self):
"""Checks if user is creator of applications with ApplicationStatus.APPROVED status""" """Checks if user is creator of domain requests with DomainRequestStatus.APPROVED status"""
approved_application_count = DomainApplication.objects.filter( approved_domain_request_count = DomainRequest.objects.filter(
creator=self.request.user, status=DomainApplication.ApplicationStatus.APPROVED creator=self.request.user, status=DomainRequest.DomainRequestStatus.APPROVED
).count() ).count()
return approved_application_count > 0 return approved_domain_request_count > 0
def approved_domains_exist(self): def approved_domains_exist(self):
"""Checks if user has permissions on approved domains """Checks if user has permissions on approved domains
This additional check is necessary to account for domains which were migrated This additional check is necessary to account for domains which were migrated
and do not have an application""" and do not have a domain request"""
return self.request.user.permissions.count() > 0 return self.request.user.permissions.count() > 0
def pending_applications(self): def pending_domain_requests(self):
"""Returns a List of user's applications with one of the following states: """Returns a List of user's domain requests with one of the following states:
ApplicationStatus.SUBMITTED, ApplicationStatus.IN_REVIEW, ApplicationStatus.ACTION_NEEDED""" DomainRequestStatus.SUBMITTED, DomainRequestStatus.IN_REVIEW, DomainRequestStatus.ACTION_NEEDED"""
# if the current application has ApplicationStatus.ACTION_NEEDED status, this check should not be performed # if the current domain request has DomainRequestStatus.ACTION_NEEDED status, this check should not be performed
if self.application.status == DomainApplication.ApplicationStatus.ACTION_NEEDED: if self.domain_request.status == DomainRequest.DomainRequestStatus.ACTION_NEEDED:
return [] return []
check_statuses = [ check_statuses = [
DomainApplication.ApplicationStatus.SUBMITTED, DomainRequest.DomainRequestStatus.SUBMITTED,
DomainApplication.ApplicationStatus.IN_REVIEW, DomainRequest.DomainRequestStatus.IN_REVIEW,
DomainApplication.ApplicationStatus.ACTION_NEEDED, DomainRequest.DomainRequestStatus.ACTION_NEEDED,
] ]
return DomainApplication.objects.filter(creator=self.request.user, status__in=check_statuses) return DomainRequest.objects.filter(creator=self.request.user, status__in=check_statuses)
def db_check_for_unlocking_steps(self): def db_check_for_unlocking_steps(self):
"""Helper for get_context_data """Helper for get_context_data
Queries the DB for an application and returns a list of unlocked steps.""" Queries the DB for a domain request and returns a list of unlocked steps."""
history_dict = { history_dict = {
"organization_type": self.application.organization_type is not None, "organization_type": self.domain_request.organization_type is not None,
"tribal_government": self.application.tribe_name is not None, "tribal_government": self.domain_request.tribe_name is not None,
"organization_federal": self.application.federal_type is not None, "organization_federal": self.domain_request.federal_type is not None,
"organization_election": self.application.is_election_board is not None, "organization_election": self.domain_request.is_election_board is not None,
"organization_contact": ( "organization_contact": (
self.application.federal_agency is not None self.domain_request.federal_agency is not None
or self.application.organization_name is not None or self.domain_request.organization_name is not None
or self.application.address_line1 is not None or self.domain_request.address_line1 is not None
or self.application.city is not None or self.domain_request.city is not None
or self.application.state_territory is not None or self.domain_request.state_territory is not None
or self.application.zipcode is not None or self.domain_request.zipcode is not None
or self.application.urbanization is not None or self.domain_request.urbanization is not None
), ),
"about_your_organization": self.application.about_your_organization is not None, "about_your_organization": self.domain_request.about_your_organization is not None,
"authorizing_official": self.application.authorizing_official is not None, "authorizing_official": self.domain_request.authorizing_official is not None,
"current_sites": ( "current_sites": (
self.application.current_websites.exists() or self.application.requested_domain is not None self.domain_request.current_websites.exists() or self.domain_request.requested_domain is not None
), ),
"dotgov_domain": self.application.requested_domain is not None, "dotgov_domain": self.domain_request.requested_domain is not None,
"purpose": self.application.purpose is not None, "purpose": self.domain_request.purpose is not None,
"your_contact": self.application.submitter is not None, "your_contact": self.domain_request.submitter is not None,
"other_contacts": ( "other_contacts": (
self.application.other_contacts.exists() or self.application.no_other_contacts_rationale is not None self.domain_request.other_contacts.exists()
or self.domain_request.no_other_contacts_rationale is not None
), ),
"anything_else": ( "anything_else": (
self.application.anything_else is not None or self.application.is_policy_acknowledged is not None self.domain_request.anything_else is not None or self.domain_request.is_policy_acknowledged is not None
), ),
"requirements": self.application.is_policy_acknowledged is not None, "requirements": self.domain_request.is_policy_acknowledged is not None,
"review": self.application.is_policy_acknowledged is not None, "review": self.domain_request.is_policy_acknowledged is not None,
} }
return [key for key, value in history_dict.items() if value] return [key for key, value in history_dict.items() if value]
@ -377,8 +378,8 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
# Build the submit button that we'll pass to the modal. # Build the submit button that we'll pass to the modal.
modal_button = '<button type="submit" ' 'class="usa-button" ' ">Submit request</button>" modal_button = '<button type="submit" ' 'class="usa-button" ' ">Submit request</button>"
# Concatenate the modal header that we'll pass to the modal. # Concatenate the modal header that we'll pass to the modal.
if self.application.requested_domain: if self.domain_request.requested_domain:
modal_heading = "You are about to submit a domain request for " + str(self.application.requested_domain) modal_heading = "You are about to submit a domain request for " + str(self.domain_request.requested_domain)
else: else:
modal_heading = "You are about to submit an incomplete request" modal_heading = "You are about to submit an incomplete request"
@ -387,7 +388,7 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
"steps": self.steps, "steps": self.steps,
# Add information about which steps should be unlocked # Add information about which steps should be unlocked
"visited": self.storage.get("step_history", []), "visited": self.storage.get("step_history", []),
"is_federal": self.application.is_federal(), "is_federal": self.domain_request.is_federal(),
"modal_button": modal_button, "modal_button": modal_button,
"modal_heading": modal_heading, "modal_heading": modal_heading,
} }
@ -434,7 +435,7 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
return self.goto(self.steps.first) return self.goto(self.steps.first)
# if accessing this class directly, redirect to the first step # if accessing this class directly, redirect to the first step
if self.__class__ == ApplicationWizard: if self.__class__ == DomainRequestWizard:
return self.goto(self.steps.first) return self.goto(self.steps.first)
forms = self.get_forms(use_post=True) forms = self.get_forms(use_post=True)
@ -462,86 +463,86 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
""" """
Unpack the form responses onto the model object properties. Unpack the form responses onto the model object properties.
Saves the application to the database. Saves the domain request to the database.
""" """
for form in forms: for form in forms:
if form is not None and hasattr(form, "to_database"): if form is not None and hasattr(form, "to_database"):
form.to_database(self.application) form.to_database(self.domain_request)
class OrganizationType(ApplicationWizard): class OrganizationType(DomainRequestWizard):
template_name = "application_org_type.html" template_name = "domain_request_org_type.html"
forms = [forms.OrganizationTypeForm] forms = [forms.OrganizationTypeForm]
class TribalGovernment(ApplicationWizard): class TribalGovernment(DomainRequestWizard):
template_name = "application_tribal_government.html" template_name = "domain_request_tribal_government.html"
forms = [forms.TribalGovernmentForm] forms = [forms.TribalGovernmentForm]
class OrganizationFederal(ApplicationWizard): class OrganizationFederal(DomainRequestWizard):
template_name = "application_org_federal.html" template_name = "domain_request_org_federal.html"
forms = [forms.OrganizationFederalForm] forms = [forms.OrganizationFederalForm]
class OrganizationElection(ApplicationWizard): class OrganizationElection(DomainRequestWizard):
template_name = "application_org_election.html" template_name = "domain_request_org_election.html"
forms = [forms.OrganizationElectionForm] forms = [forms.OrganizationElectionForm]
class OrganizationContact(ApplicationWizard): class OrganizationContact(DomainRequestWizard):
template_name = "application_org_contact.html" template_name = "domain_request_org_contact.html"
forms = [forms.OrganizationContactForm] forms = [forms.OrganizationContactForm]
class AboutYourOrganization(ApplicationWizard): class AboutYourOrganization(DomainRequestWizard):
template_name = "application_about_your_organization.html" template_name = "domain_request_about_your_organization.html"
forms = [forms.AboutYourOrganizationForm] forms = [forms.AboutYourOrganizationForm]
class AuthorizingOfficial(ApplicationWizard): class AuthorizingOfficial(DomainRequestWizard):
template_name = "application_authorizing_official.html" template_name = "domain_request_authorizing_official.html"
forms = [forms.AuthorizingOfficialForm] forms = [forms.AuthorizingOfficialForm]
def get_context_data(self): def get_context_data(self):
context = super().get_context_data() context = super().get_context_data()
context["organization_type"] = self.application.organization_type context["organization_type"] = self.domain_request.organization_type
context["federal_type"] = self.application.federal_type context["federal_type"] = self.domain_request.federal_type
return context return context
class CurrentSites(ApplicationWizard): class CurrentSites(DomainRequestWizard):
template_name = "application_current_sites.html" template_name = "domain_request_current_sites.html"
forms = [forms.CurrentSitesFormSet] forms = [forms.CurrentSitesFormSet]
class DotgovDomain(ApplicationWizard): class DotgovDomain(DomainRequestWizard):
template_name = "application_dotgov_domain.html" template_name = "domain_request_dotgov_domain.html"
forms = [forms.DotGovDomainForm, forms.AlternativeDomainFormSet] forms = [forms.DotGovDomainForm, forms.AlternativeDomainFormSet]
def get_context_data(self): def get_context_data(self):
context = super().get_context_data() context = super().get_context_data()
context["organization_type"] = self.application.organization_type context["organization_type"] = self.domain_request.organization_type
context["federal_type"] = self.application.federal_type context["federal_type"] = self.domain_request.federal_type
return context return context
class Purpose(ApplicationWizard): class Purpose(DomainRequestWizard):
template_name = "application_purpose.html" template_name = "domain_request_purpose.html"
forms = [forms.PurposeForm] forms = [forms.PurposeForm]
class YourContact(ApplicationWizard): class YourContact(DomainRequestWizard):
template_name = "application_your_contact.html" template_name = "domain_request_your_contact.html"
forms = [forms.YourContactForm] forms = [forms.YourContactForm]
class OtherContacts(ApplicationWizard): class OtherContacts(DomainRequestWizard):
template_name = "application_other_contacts.html" template_name = "domain_request_other_contacts.html"
forms = [forms.OtherContactsYesNoForm, forms.OtherContactsFormSet, forms.NoOtherContactsForm] forms = [forms.OtherContactsYesNoForm, forms.OtherContactsFormSet, forms.NoOtherContactsForm]
def is_valid(self, forms: list) -> bool: def is_valid(self, forms: list) -> bool:
"""Overrides default behavior defined in ApplicationWizard. """Overrides default behavior defined in DomainRequestWizard.
Depending on value in other_contacts_yes_no_form, marks forms in Depending on value in other_contacts_yes_no_form, marks forms in
other_contacts or no_other_contacts for deletion. Then validates other_contacts or no_other_contacts for deletion. Then validates
all forms. all forms.
@ -580,24 +581,24 @@ class OtherContacts(ApplicationWizard):
return all_forms_valid return all_forms_valid
class AnythingElse(ApplicationWizard): class AnythingElse(DomainRequestWizard):
template_name = "application_anything_else.html" template_name = "domain_request_anything_else.html"
forms = [forms.AnythingElseForm] forms = [forms.AnythingElseForm]
class Requirements(ApplicationWizard): class Requirements(DomainRequestWizard):
template_name = "application_requirements.html" template_name = "domain_request_requirements.html"
forms = [forms.RequirementsForm] forms = [forms.RequirementsForm]
class Review(ApplicationWizard): class Review(DomainRequestWizard):
template_name = "application_review.html" template_name = "domain_request_review.html"
forms = [] # type: ignore forms = [] # type: ignore
def get_context_data(self): def get_context_data(self):
context = super().get_context_data() context = super().get_context_data()
context["Step"] = Step.__members__ context["Step"] = Step.__members__
context["application"] = self.application context["domain_request"] = self.domain_request
return context return context
def goto_next_step(self): def goto_next_step(self):
@ -623,33 +624,33 @@ class Review(ApplicationWizard):
# return self.goto(self.steps.current) # return self.goto(self.steps.current)
class Finished(ApplicationWizard): class Finished(DomainRequestWizard):
template_name = "application_done.html" template_name = "domain_request_done.html"
forms = [] # type: ignore forms = [] # type: ignore
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
context = self.get_context_data() context = self.get_context_data()
context["application_id"] = self.application.id context["domain_request_id"] = self.domain_request.id
# clean up this wizard session, because we are done with it # clean up this wizard session, because we are done with it
del self.storage del self.storage
return render(self.request, self.template_name, context) return render(self.request, self.template_name, context)
class ApplicationStatus(DomainApplicationPermissionView): class DomainRequestStatus(DomainRequestPermissionView):
template_name = "application_status.html" template_name = "domain_request_status.html"
class ApplicationWithdrawConfirmation(DomainApplicationPermissionWithdrawView): class DomainRequestWithdrawConfirmation(DomainRequestPermissionWithdrawView):
"""This page will ask user to confirm if they want to withdraw """This page will ask user to confirm if they want to withdraw
The DomainApplicationPermissionView restricts access so that only the The DomainRequestPermissionView restricts access so that only the
`creator` of the application may withdraw it. `creator` of the domain request may withdraw it.
""" """
template_name = "application_withdraw_confirmation.html" template_name = "domain_request_withdraw_confirmation.html"
class ApplicationWithdrawn(DomainApplicationPermissionWithdrawView): class DomainRequestWithdrawn(DomainRequestPermissionWithdrawView):
# this view renders no template # this view renders no template
template_name = "" template_name = ""
@ -659,16 +660,16 @@ class ApplicationWithdrawn(DomainApplicationPermissionWithdrawView):
If user click on withdraw confirm button, this view updates the status If user click on withdraw confirm button, this view updates the status
to withdraw and send back to homepage. to withdraw and send back to homepage.
""" """
application = DomainApplication.objects.get(id=self.kwargs["pk"]) domain_request = DomainRequest.objects.get(id=self.kwargs["pk"])
application.withdraw() domain_request.withdraw()
application.save() domain_request.save()
return HttpResponseRedirect(reverse("home")) return HttpResponseRedirect(reverse("home"))
class DomainApplicationDeleteView(DomainApplicationPermissionDeleteView): class DomainRequestDeleteView(DomainRequestPermissionDeleteView):
"""Delete view for home that allows the end user to delete DomainApplications""" """Delete view for home that allows the end user to delete DomainRequests"""
object: DomainApplication # workaround for type mismatch in DeleteView object: DomainRequest # workaround for type mismatch in DeleteView
def has_permission(self): def has_permission(self):
"""Custom override for has_permission to exclude all statuses, except WITHDRAWN and STARTED""" """Custom override for has_permission to exclude all statuses, except WITHDRAWN and STARTED"""
@ -677,7 +678,7 @@ class DomainApplicationDeleteView(DomainApplicationPermissionDeleteView):
return False return False
status = self.get_object().status status = self.get_object().status
valid_statuses = [DomainApplication.ApplicationStatus.WITHDRAWN, DomainApplication.ApplicationStatus.STARTED] valid_statuses = [DomainRequest.DomainRequestStatus.WITHDRAWN, DomainRequest.DomainRequestStatus.STARTED]
if status not in valid_statuses: if status not in valid_statuses:
return False return False
@ -689,10 +690,10 @@ class DomainApplicationDeleteView(DomainApplicationPermissionDeleteView):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
# Grab all orphaned contacts # Grab all orphaned contacts
application: DomainApplication = self.get_object() domain_request: DomainRequest = self.get_object()
contacts_to_delete, duplicates = self._get_orphaned_contacts(application) contacts_to_delete, duplicates = self._get_orphaned_contacts(domain_request)
# Delete the DomainApplication # Delete the DomainRequest
response = super().post(request, *args, **kwargs) response = super().post(request, *args, **kwargs)
# Delete orphaned contacts - but only for if they are not associated with a user # Delete orphaned contacts - but only for if they are not associated with a user
@ -702,21 +703,21 @@ class DomainApplicationDeleteView(DomainApplicationPermissionDeleteView):
# This determines if any of these three fields share a contact, which is used for # This determines if any of these three fields share a contact, which is used for
# the edge case where the same user may be an AO, and a submitter, for example. # the edge case where the same user may be an AO, and a submitter, for example.
if len(duplicates) > 0: if len(duplicates) > 0:
duplicates_to_delete, _ = self._get_orphaned_contacts(application, check_db=True) duplicates_to_delete, _ = self._get_orphaned_contacts(domain_request, check_db=True)
Contact.objects.filter(id__in=duplicates_to_delete, user=None).delete() Contact.objects.filter(id__in=duplicates_to_delete, user=None).delete()
return response return response
def _get_orphaned_contacts(self, application: DomainApplication, check_db=False): def _get_orphaned_contacts(self, domain_request: DomainRequest, check_db=False):
""" """
Collects all orphaned contacts associated with a given DomainApplication object. Collects all orphaned contacts associated with a given DomainRequest object.
An orphaned contact is defined as a contact that is associated with the application, An orphaned contact is defined as a contact that is associated with the domain request,
but not with any other application. This includes the authorizing official, the submitter, but not with any other domain_request. This includes the authorizing official, the submitter,
and any other contacts linked to the application. and any other contacts linked to the domain_request.
Parameters: Parameters:
application (DomainApplication): The DomainApplication object for which to find orphaned contacts. domain_request (DomainRequest): The DomainRequest object for which to find orphaned contacts.
check_db (bool, optional): A flag indicating whether to check the database for the existence of the contacts. check_db (bool, optional): A flag indicating whether to check the database for the existence of the contacts.
Defaults to False. Defaults to False.
@ -726,11 +727,11 @@ class DomainApplicationDeleteView(DomainApplicationPermissionDeleteView):
""" """
contacts_to_delete = [] contacts_to_delete = []
# Get each contact object on the DomainApplication object # Get each contact object on the DomainRequest object
ao = application.authorizing_official ao = domain_request.authorizing_official
submitter = application.submitter submitter = domain_request.submitter
other_contacts = list(application.other_contacts.all()) other_contacts = list(domain_request.other_contacts.all())
other_contact_ids = application.other_contacts.all().values_list("id", flat=True) other_contact_ids = domain_request.other_contacts.all().values_list("id", flat=True)
# Check if the desired item still exists in the DB # Check if the desired item still exists in the DB
if check_db: if check_db:
@ -739,8 +740,8 @@ class DomainApplicationDeleteView(DomainApplicationPermissionDeleteView):
other_contacts = self._get_contacts_by_id(other_contact_ids) other_contacts = self._get_contacts_by_id(other_contact_ids)
# Pair each contact with its db related name for use in checking if it has joins # Pair each contact with its db related name for use in checking if it has joins
checked_contacts = [(ao, "authorizing_official"), (submitter, "submitted_applications")] checked_contacts = [(ao, "authorizing_official"), (submitter, "submitted_domain_requests")]
checked_contacts.extend((contact, "contact_applications") for contact in other_contacts) checked_contacts.extend((contact, "contact_domain_requests") for contact in other_contacts)
for contact, related_name in checked_contacts: for contact, related_name in checked_contacts:
if contact is not None and not contact.has_more_than_one_join(related_name): if contact is not None and not contact.has_more_than_one_join(related_name):

View file

@ -1,56 +1,56 @@
from django.shortcuts import render from django.shortcuts import render
from registrar.models import DomainApplication, Domain, UserDomainRole from registrar.models import DomainRequest, Domain, UserDomainRole
def index(request): def index(request):
"""This page is available to anyone without logging in.""" """This page is available to anyone without logging in."""
context = {} context = {}
if request.user.is_authenticated: if request.user.is_authenticated:
# Get all domain applications the user has access to # Get all domain requests the user has access to
applications, deletable_applications = _get_applications(request) domain_requests, deletable_domain_requests = _get_domain_requests(request)
context["domain_applications"] = applications context["domain_requests"] = domain_requests
# Get all domains the user has access to # Get all domains the user has access to
domains = _get_domains(request) domains = _get_domains(request)
context["domains"] = domains context["domains"] = domains
# Determine if the user will see applications that they can delete # Determine if the user will see domain requests that they can delete
has_deletable_applications = deletable_applications.exists() has_deletable_domain_requests = deletable_domain_requests.exists()
context["has_deletable_applications"] = has_deletable_applications context["has_deletable_domain_requests"] = has_deletable_domain_requests
# If they can delete applications, add the delete button to the context # If they can delete domain requests, add the delete button to the context
if has_deletable_applications: if has_deletable_domain_requests:
# Add the delete modal button to the context # Add the delete modal button to the context
modal_button = ( modal_button = (
'<button type="submit" ' '<button type="submit" '
'class="usa-button usa-button--secondary" ' 'class="usa-button usa-button--secondary" '
'name="delete-application">Yes, delete request</button>' 'name="delete-domain-request">Yes, delete request</button>'
) )
context["modal_button"] = modal_button context["modal_button"] = modal_button
return render(request, "home.html", context) return render(request, "home.html", context)
def _get_applications(request): def _get_domain_requests(request):
"""Given the current request, """Given the current request,
get all DomainApplications that are associated with the UserDomainRole object. get all DomainRequests that are associated with the UserDomainRole object.
Returns a tuple of all applications, and those that are deletable by the user. Returns a tuple of all domain requests, and those that are deletable by the user.
""" """
# Let's exclude the approved applications since our # Let's exclude the approved domain requests since our
# domain_applications context will be used to populate # domain_requests context will be used to populate
# the active applications table # the active domain requests table
applications = DomainApplication.objects.filter(creator=request.user).exclude( domain_requests = DomainRequest.objects.filter(creator=request.user).exclude(
status=DomainApplication.ApplicationStatus.APPROVED status=DomainRequest.DomainRequestStatus.APPROVED
) )
# Create a placeholder DraftDomain for each incomplete draft # Create a placeholder DraftDomain for each incomplete draft
valid_statuses = [DomainApplication.ApplicationStatus.STARTED, DomainApplication.ApplicationStatus.WITHDRAWN] valid_statuses = [DomainRequest.DomainRequestStatus.STARTED, DomainRequest.DomainRequestStatus.WITHDRAWN]
deletable_applications = applications.filter(status__in=valid_statuses) deletable_domain_requests = domain_requests.filter(status__in=valid_statuses)
return (applications, deletable_applications) return (domain_requests, deletable_domain_requests)
def _get_domains(request): def _get_domains(request):

View file

@ -3,8 +3,8 @@ from .always_404 import always_404
from .permission_views import ( from .permission_views import (
DomainPermissionView, DomainPermissionView,
DomainApplicationPermissionView, DomainRequestPermissionView,
DomainApplicationPermissionWithdrawView, DomainRequestPermissionWithdrawView,
DomainInvitationPermissionDeleteView, DomainInvitationPermissionDeleteView,
ApplicationWizardPermissionView, DomainRequestWizardPermissionView,
) )

View file

@ -4,7 +4,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin
from registrar.models import ( from registrar.models import (
Domain, Domain,
DomainApplication, DomainRequest,
DomainInvitation, DomainInvitation,
DomainInformation, DomainInformation,
UserDomainRole, UserDomainRole,
@ -230,10 +230,10 @@ class DomainPermission(PermissionsLoginMixin):
# Analysts may manage domains, when they are in these statuses: # Analysts may manage domains, when they are in these statuses:
valid_domain_statuses = [ valid_domain_statuses = [
DomainApplication.ApplicationStatus.APPROVED, DomainRequest.DomainRequestStatus.APPROVED,
DomainApplication.ApplicationStatus.IN_REVIEW, DomainRequest.DomainRequestStatus.IN_REVIEW,
DomainApplication.ApplicationStatus.REJECTED, DomainRequest.DomainRequestStatus.REJECTED,
DomainApplication.ApplicationStatus.ACTION_NEEDED, DomainRequest.DomainRequestStatus.ACTION_NEEDED,
# Edge case - some domains do not have # Edge case - some domains do not have
# a status or DomainInformation... aka a status of 'None'. # a status or DomainInformation... aka a status of 'None'.
# It is necessary to access those to correct errors. # It is necessary to access those to correct errors.
@ -244,14 +244,14 @@ class DomainPermission(PermissionsLoginMixin):
if DomainInformation.objects.filter(id=pk).exists(): if DomainInformation.objects.filter(id=pk).exists():
requested_domain = DomainInformation.objects.get(id=pk) requested_domain = DomainInformation.objects.get(id=pk)
# if no domain information or application exist, the user # if no domain information or domain request exist, the user
# should be able to manage the domain; however, if domain information # should be able to manage the domain; however, if domain information
# and domain application exist, and application is not in valid status, # and domain request exist, and domain request is not in valid status,
# user should not be able to manage domain # user should not be able to manage domain
if ( if (
requested_domain requested_domain
and requested_domain.domain_application and requested_domain.domain_request
and requested_domain.domain_application.status not in valid_domain_statuses and requested_domain.domain_request.status not in valid_domain_statuses
): ):
return False return False
@ -261,12 +261,12 @@ class DomainPermission(PermissionsLoginMixin):
return True return True
class DomainApplicationPermission(PermissionsLoginMixin): class DomainRequestPermission(PermissionsLoginMixin):
"""Permission mixin that redirects to domain application if user """Permission mixin that redirects to domain request if user
has access, otherwise 403""" has access, otherwise 403"""
def has_permission(self): def has_permission(self):
"""Check if this user has access to this domain application. """Check if this user has access to this domain request.
The user is in self.request.user and the domain needs to be looked The user is in self.request.user and the domain needs to be looked
up from the domain's primary key in self.kwargs["pk"] up from the domain's primary key in self.kwargs["pk"]
@ -274,10 +274,10 @@ class DomainApplicationPermission(PermissionsLoginMixin):
if not self.request.user.is_authenticated: if not self.request.user.is_authenticated:
return False return False
# user needs to be the creator of the application # user needs to be the creator of the domain request
# this query is empty if there isn't a domain application with this # this query is empty if there isn't a domain request with this
# id and this user as creator # id and this user as creator
if not DomainApplication.objects.filter(creator=self.request.user, id=self.kwargs["pk"]).exists(): if not DomainRequest.objects.filter(creator=self.request.user, id=self.kwargs["pk"]).exists():
return False return False
return True return True
@ -288,7 +288,7 @@ class UserDeleteDomainRolePermission(PermissionsLoginMixin):
has access, otherwise 403""" has access, otherwise 403"""
def has_permission(self): def has_permission(self):
"""Check if this user has access to this domain application. """Check if this user has access to this domain request.
The user is in self.request.user and the domain needs to be looked The user is in self.request.user and the domain needs to be looked
up from the domain's primary key in self.kwargs["pk"] up from the domain's primary key in self.kwargs["pk"]
@ -319,19 +319,19 @@ class UserDeleteDomainRolePermission(PermissionsLoginMixin):
return True return True
class DomainApplicationPermissionWithdraw(PermissionsLoginMixin): class DomainRequestPermissionWithdraw(PermissionsLoginMixin):
"""Permission mixin that redirects to withdraw action on domain application """Permission mixin that redirects to withdraw action on domain request
if user has access, otherwise 403""" if user has access, otherwise 403"""
def has_permission(self): def has_permission(self):
"""Check if this user has access to withdraw this domain application.""" """Check if this user has access to withdraw this domain request."""
if not self.request.user.is_authenticated: if not self.request.user.is_authenticated:
return False return False
# user needs to be the creator of the application # user needs to be the creator of the domain request
# this query is empty if there isn't a domain application with this # this query is empty if there isn't a domain request with this
# id and this user as creator # id and this user as creator
if not DomainApplication.objects.filter(creator=self.request.user, id=self.kwargs["pk"]).exists(): if not DomainRequest.objects.filter(creator=self.request.user, id=self.kwargs["pk"]).exists():
return False return False
# Restricted users should not be able to withdraw domain requests # Restricted users should not be able to withdraw domain requests
@ -341,12 +341,12 @@ class DomainApplicationPermissionWithdraw(PermissionsLoginMixin):
return True return True
class ApplicationWizardPermission(PermissionsLoginMixin): class DomainRequestWizardPermission(PermissionsLoginMixin):
"""Permission mixin that redirects to start or edit domain application if """Permission mixin that redirects to start or edit domain request if
user has access, otherwise 403""" user has access, otherwise 403"""
def has_permission(self): def has_permission(self):
"""Check if this user has permission to start or edit an application. """Check if this user has permission to start or edit a domain request.
The user is in self.request.user The user is in self.request.user
""" """

View file

@ -3,15 +3,15 @@
import abc # abstract base class import abc # abstract base class
from django.views.generic import DetailView, DeleteView, TemplateView from django.views.generic import DetailView, DeleteView, TemplateView
from registrar.models import Domain, DomainApplication, DomainInvitation from registrar.models import Domain, DomainRequest, DomainInvitation
from registrar.models.user_domain_role import UserDomainRole from registrar.models.user_domain_role import UserDomainRole
from .mixins import ( from .mixins import (
DomainPermission, DomainPermission,
DomainApplicationPermission, DomainRequestPermission,
DomainApplicationPermissionWithdraw, DomainRequestPermissionWithdraw,
DomainInvitationPermission, DomainInvitationPermission,
ApplicationWizardPermission, DomainRequestWizardPermission,
UserDeleteDomainRolePermission, UserDeleteDomainRolePermission,
) )
import logging import logging
@ -56,17 +56,17 @@ class DomainPermissionView(DomainPermission, DetailView, abc.ABC):
raise NotImplementedError raise NotImplementedError
class DomainApplicationPermissionView(DomainApplicationPermission, DetailView, abc.ABC): class DomainRequestPermissionView(DomainRequestPermission, DetailView, abc.ABC):
"""Abstract base view for domain applications that enforces permissions """Abstract base view for domain requests that enforces permissions
This abstract view cannot be instantiated. Actual views must specify This abstract view cannot be instantiated. Actual views must specify
`template_name`. `template_name`.
""" """
# DetailView property for what model this is viewing # DetailView property for what model this is viewing
model = DomainApplication model = DomainRequest
# variable name in template context for the model object # variable name in template context for the model object
context_object_name = "domainapplication" context_object_name = "DomainRequest"
# Abstract property enforces NotImplementedError on an attribute. # Abstract property enforces NotImplementedError on an attribute.
@property @property
@ -75,17 +75,17 @@ class DomainApplicationPermissionView(DomainApplicationPermission, DetailView, a
raise NotImplementedError raise NotImplementedError
class DomainApplicationPermissionWithdrawView(DomainApplicationPermissionWithdraw, DetailView, abc.ABC): class DomainRequestPermissionWithdrawView(DomainRequestPermissionWithdraw, DetailView, abc.ABC):
"""Abstract base view for domain application withdraw function """Abstract base view for domain request withdraw function
This abstract view cannot be instantiated. Actual views must specify This abstract view cannot be instantiated. Actual views must specify
`template_name`. `template_name`.
""" """
# DetailView property for what model this is viewing # DetailView property for what model this is viewing
model = DomainApplication model = DomainRequest
# variable name in template context for the model object # variable name in template context for the model object
context_object_name = "domainapplication" context_object_name = "DomainRequest"
# Abstract property enforces NotImplementedError on an attribute. # Abstract property enforces NotImplementedError on an attribute.
@property @property
@ -94,8 +94,8 @@ class DomainApplicationPermissionWithdrawView(DomainApplicationPermissionWithdra
raise NotImplementedError raise NotImplementedError
class ApplicationWizardPermissionView(ApplicationWizardPermission, TemplateView, abc.ABC): class DomainRequestWizardPermissionView(DomainRequestWizardPermission, TemplateView, abc.ABC):
"""Abstract base view for the application form that enforces permissions """Abstract base view for the domain request form that enforces permissions
This abstract view cannot be instantiated. Actual views must specify This abstract view cannot be instantiated. Actual views must specify
`template_name`. `template_name`.
@ -121,11 +121,11 @@ class DomainInvitationPermissionDeleteView(DomainInvitationPermission, DeleteVie
object: DomainInvitation # workaround for type mismatch in DeleteView object: DomainInvitation # workaround for type mismatch in DeleteView
class DomainApplicationPermissionDeleteView(DomainApplicationPermission, DeleteView, abc.ABC): class DomainRequestPermissionDeleteView(DomainRequestPermission, DeleteView, abc.ABC):
"""Abstract view for deleting a DomainApplication.""" """Abstract view for deleting a DomainRequest."""
model = DomainApplication model = DomainRequest
object: DomainApplication object: DomainRequest
class UserDomainRolePermissionDeleteView(UserDeleteDomainRolePermission, DeleteView, abc.ABC): class UserDomainRolePermissionDeleteView(UserDeleteDomainRolePermission, DeleteView, abc.ABC):