mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-16 17:47:02 +02:00
Merge branch 'main' into za/813-sort-dropdowns-alphabetically
This commit is contained in:
commit
251b8291f9
19 changed files with 375 additions and 155 deletions
|
@ -24,13 +24,13 @@ There are several tools we use locally that you will need to have.
|
||||||
### Steps for the onboardee
|
### Steps for the onboardee
|
||||||
- [ ] Setup [commit signing in Github](#setting-up-commit-signing) and with git locally.
|
- [ ] Setup [commit signing in Github](#setting-up-commit-signing) and with git locally.
|
||||||
- [ ] [Create a cloud.gov account](https://cloud.gov/docs/getting-started/accounts/)
|
- [ ] [Create a cloud.gov account](https://cloud.gov/docs/getting-started/accounts/)
|
||||||
- [ ] Have an admin add you to the CISA Github organization and Dotgov Team.
|
- [ ] Email github@cisa.dhs.gov (cc: Cameron) to add you to the [CISA Github organization](https://github.com/getgov) and [.gov Team](https://github.com/orgs/cisagov/teams/gov).
|
||||||
- [ ] Ensure you can login to your cloud.gov account via the CLI
|
- [ ] Ensure you can login to your cloud.gov account via the CLI
|
||||||
```bash
|
```bash
|
||||||
cf login -a api.fr.cloud.gov --sso
|
cf login -a api.fr.cloud.gov --sso
|
||||||
```
|
```
|
||||||
- [ ] Have an admin add you to cloud.gov org and set up your [sandbox developer space](#setting-up-developer-sandbox). Ensure you can deploy to your sandbox space.
|
- [ ] Have an admin add you to cloud.gov org and set up your [sandbox developer space](#setting-up-developer-sandbox). Ensure you can deploy to your sandbox space.
|
||||||
- [ ] Have an admin add you to our login.gov sandbox team (`.gov registrar poc`) via the [dashboard](https://dashboard.int.identitysandbox.gov/).
|
- [ ] Have an admin add you to our login.gov sandbox team (`.gov Registrar`) via the [dashboard](https://dashboard.int.identitysandbox.gov/).
|
||||||
|
|
||||||
**Note:** As mentioned in the [Login documentation](https://developers.login.gov/testing/), the sandbox Login account is different account from your regular, production Login account. If you have not created a Login account for the sandbox before, you will need to create a new account first.
|
**Note:** As mentioned in the [Login documentation](https://developers.login.gov/testing/), the sandbox Login account is different account from your regular, production Login account. If you have not created a Login account for the sandbox before, you will need to create a new account first.
|
||||||
|
|
||||||
|
@ -39,12 +39,12 @@ cf login -a api.fr.cloud.gov --sso
|
||||||
### Steps for the onboarder
|
### Steps for the onboarder
|
||||||
- [ ] Add the onboardee to cloud.gov org (cisa-getgov-prototyping)
|
- [ ] Add the onboardee to cloud.gov org (cisa-getgov-prototyping)
|
||||||
- [ ] Setup a [developer specific space for the new developer](#setting-up-developer-sandbox)
|
- [ ] Setup a [developer specific space for the new developer](#setting-up-developer-sandbox)
|
||||||
- [ ] Add the onboardee to our login.gov sandbox team (`.gov registrar poc`) via the [dashboard](https://dashboard.int.identitysandbox.gov/)
|
- [ ] Add the onboardee to our login.gov sandbox team (`.gov Registrar`) via the [dashboard](https://dashboard.int.identitysandbox.gov/)
|
||||||
|
|
||||||
|
|
||||||
## Documents to Review
|
## Documents to Review
|
||||||
|
|
||||||
- [ ] [Team Charter](https://docs.google.com/document/d/1xhMKlW8bMcxyF7ipsOYxw1SQYVi-lWPkcDHSUS6miNg/edit), in particular our Github Policy
|
- [ ] [Team Onboarding](https://docs.google.com/document/d/1ukbpW4LSqkb_CCt8LWfpehP03qqfyYfvK3Fl21NaEq8/edit?usp=sharing)
|
||||||
- [ ] [Architecture Decision Records](https://github.com/cisagov/dotgov/tree/main/docs/architecture/decisions)
|
- [ ] [Architecture Decision Records](https://github.com/cisagov/dotgov/tree/main/docs/architecture/decisions)
|
||||||
- [ ] [Contributing Policy](https://github.com/cisagov/dotgov/tree/main/CONTRIBUTING.md)
|
- [ ] [Contributing Policy](https://github.com/cisagov/dotgov/tree/main/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
|
139
.github/pull_request_template.md
vendored
139
.github/pull_request_template.md
vendored
|
@ -1,13 +1,134 @@
|
||||||
# <!-- Use the title to describe PR changes in the imperative mood --> #
|
## Ticket
|
||||||
|
|
||||||
## 🗣 Description ##
|
Resolves #00
|
||||||
|
<!--Reminder, when a code change is made that is user facing, beyond content updates, then the following are required:
|
||||||
|
- a developer approves the PR
|
||||||
|
- a designer approves the PR or checks off all relevant items in this checklist.
|
||||||
|
|
||||||
<!-- Describe the "what" of your changes in detail. -->
|
All other changes require just a single approving review.-->
|
||||||
<!-- Please link to any relevant issues. -->
|
|
||||||
|
|
||||||
## 💭 Motivation and context ##
|
## Changes
|
||||||
|
|
||||||
<!-- Why is this change required? -->
|
<!-- What was added, updated, or removed in this PR. -->
|
||||||
<!-- What problem does this change solve? How did you solve it? -->
|
- Change 1
|
||||||
<!-- Mention any related issue(s) here using appropriate keywords such -->
|
- Change 2
|
||||||
<!-- as "closes" or "resolves" to auto-close them on merge. -->
|
|
||||||
|
<!--
|
||||||
|
Please add/remove/edit any of the template below to fit the needs
|
||||||
|
of this specific PR.
|
||||||
|
--->
|
||||||
|
|
||||||
|
## Context for reviewers
|
||||||
|
|
||||||
|
<!--Background context, more in-depth details of the implementation, and anything else you'd like to call out or ask reviewers. -->
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
<!-- Add any steps or code to run in this section to help others run your code.
|
||||||
|
|
||||||
|
Example 1:
|
||||||
|
```sh
|
||||||
|
echo "Code goes here"
|
||||||
|
```
|
||||||
|
|
||||||
|
Example 2: If the PR was to add a new link with a redirect, this section could simply be:
|
||||||
|
-go to /path/to/start/page
|
||||||
|
-click the blue link in the <insert location>
|
||||||
|
-notice user is redirected to <proper end location>
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Code Review Verification Steps
|
||||||
|
|
||||||
|
### As the original developer, I have
|
||||||
|
|
||||||
|
#### Satisfied acceptance criteria and met development standards
|
||||||
|
|
||||||
|
- [ ] Met the acceptance criteria, or will meet them in a subsequent PR
|
||||||
|
- [ ] Created/modified automated tests
|
||||||
|
- [ ] Added at least 2 developers as PR reviewers (only 1 will need to approve)
|
||||||
|
- [ ] Messaged on Slack or in standup to notify the team that a PR is ready for review
|
||||||
|
- [ ] Changes to “how we do things” are documented in READMEs and or onboarding guide
|
||||||
|
- [ ] If any model was updated to modify/add/delete columns, makemigrations was ran and the assoicated migrations file has been commited.
|
||||||
|
|
||||||
|
#### Ensured code standards are met (Original Developer)
|
||||||
|
|
||||||
|
- [ ] All new functions and methods are commented using plain language
|
||||||
|
- [ ] Did dependency updates in Pipfile also get changed in requirements.txt?
|
||||||
|
- [ ] Interactions with external systems are wrapped in try/except
|
||||||
|
- [ ] Error handling exists for unusual or missing values
|
||||||
|
|
||||||
|
#### Validated user-facing changes (if applicable)
|
||||||
|
|
||||||
|
- [ ] New pages have been added to .pa11yci file so that they will be tested with our automated accessibility testing
|
||||||
|
- [ ] Checked keyboard navigability
|
||||||
|
- [ ] Tested general usability, landmarks, page header structure, and links with a screen reader (such as Voiceover or ANDI)
|
||||||
|
- [ ] Add at least 1 designer as PR reviewer
|
||||||
|
|
||||||
|
### As a code reviewer, I have
|
||||||
|
|
||||||
|
#### Reviewed, tested, and left feedback about the changes
|
||||||
|
|
||||||
|
- [ ] Pulled this branch locally and tested it
|
||||||
|
- [ ] Reviewed this code and left comments
|
||||||
|
- [ ] Checked that all code is adequately covered by tests
|
||||||
|
- [ ] Made it clear which comments need to be addressed before this work is merged
|
||||||
|
- [ ] If any model was updated to modify/add/delete columns, makemigrations was ran and the assoicated migrations file has been commited.
|
||||||
|
|
||||||
|
#### Ensured code standards are met (Code reviewer)
|
||||||
|
|
||||||
|
- [ ] All new functions and methods are commented using plain language
|
||||||
|
- [ ] Interactions with external systems are wrapped in try/except
|
||||||
|
- [ ] Error handling exists for unusual or missing values
|
||||||
|
- [ ] (Rarely needed) Did dependency updates in Pipfile also get changed in requirements.txt?
|
||||||
|
|
||||||
|
#### Validated user-facing changes as a developer
|
||||||
|
|
||||||
|
- [ ] New pages have been added to .pa11yci file so that they will be tested with our automated accessibility testing
|
||||||
|
- [ ] Checked keyboard navigability
|
||||||
|
- [ ] Meets all designs and user flows provided by design/product
|
||||||
|
- [ ] Tested general usability, landmarks, page header structure, and links with a screen reader (such as Voiceover or ANDI)
|
||||||
|
|
||||||
|
- [ ] Tested with multiple browsers, the suggestion is to use ones that the developer didn't (check off which ones were used)
|
||||||
|
- [ ] Chrome
|
||||||
|
- [ ] Microsoft Edge
|
||||||
|
- [ ] FireFox
|
||||||
|
- [ ] Safari
|
||||||
|
|
||||||
|
- [ ] (Rarely needed) Tested as both an analyst and applicant user
|
||||||
|
|
||||||
|
**Note:** Multiple code reviewers can share the checklists above, a second reviewers should not make a duplicate checklist
|
||||||
|
|
||||||
|
### As a designer reviewer, I have
|
||||||
|
|
||||||
|
#### Verified that the changes match the design intention
|
||||||
|
|
||||||
|
- [ ] Checked that the design translated visually
|
||||||
|
- [ ] Checked behavior
|
||||||
|
- [ ] Checked different states (empty, one, some, error)
|
||||||
|
- [ ] Checked for landmarks, page heading structure, and links
|
||||||
|
- [ ] Tried to break the intended flow
|
||||||
|
|
||||||
|
#### Validated user-facing changes as a designer
|
||||||
|
|
||||||
|
- [ ] Checked keyboard navigability
|
||||||
|
- [ ] Tested general usability, landmarks, page header structure, and links with a screen reader (such as Voiceover or ANDI)
|
||||||
|
|
||||||
|
- [ ] Tested with multiple browsers (check off which ones were used)
|
||||||
|
- [ ] Chrome
|
||||||
|
- [ ] Microsoft Edge
|
||||||
|
- [ ] FireFox
|
||||||
|
- [ ] Safari
|
||||||
|
|
||||||
|
- [ ] (Rarely needed) Tested as both an analyst and applicant user
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|
<!-- If this PR makes visible interface changes, an image of the finished interface can help reviewers
|
||||||
|
and casual observers understand the context of the changes.
|
||||||
|
A before image is optional and can be included at the submitter's discretion.
|
||||||
|
|
||||||
|
Consider using an animated image to show an entire workflow.
|
||||||
|
You may want to use [GIPHY Capture](https://giphy.com/apps/giphycapture) for this! 📸
|
||||||
|
|
||||||
|
_Please frame images to show useful context but also highlight the affected regions._
|
||||||
|
--->
|
||||||
|
|
|
@ -9,6 +9,22 @@ There are a handful of things we do not commit to the repository:
|
||||||
- Compliance documentation that includes IP addresses
|
- Compliance documentation that includes IP addresses
|
||||||
- Secrets of any kind
|
- Secrets of any kind
|
||||||
|
|
||||||
|
## Branch naming convention
|
||||||
|
|
||||||
|
For developers, you can auto-deploy your code to your sandbox (if applicable) by naming your branch thusly: jsd/123-feature-description
|
||||||
|
Where 'jsd' stands for your initials and sandbox environment name (if you were called John Smith Doe), and 123 matches the ticket number if applicable.
|
||||||
|
|
||||||
|
## Approvals
|
||||||
|
|
||||||
|
When a code change is made that is not user facing, then the following is required:
|
||||||
|
- a developer approves the PR
|
||||||
|
|
||||||
|
When a code change is made that is user facing, beyond content updates, then the following are required:
|
||||||
|
- a developer approves the PR
|
||||||
|
- a designer approves the PR or checks off all relevant items in this checklist
|
||||||
|
|
||||||
|
Content or document updates require a single person to approve.
|
||||||
|
|
||||||
## Project Management
|
## Project Management
|
||||||
|
|
||||||
We use [Github Projects](https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects) for project management and tracking.
|
We use [Github Projects](https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects) for project management and tracking.
|
||||||
|
|
|
@ -34,6 +34,13 @@ In contrast to building an admin interface from scratch where development activi
|
||||||
involve _building up_, leveraging Django Admin will require carefully _pairing back_ the functionalities available to
|
involve _building up_, leveraging Django Admin will require carefully _pairing back_ the functionalities available to
|
||||||
users such as analysts.
|
users such as analysts.
|
||||||
|
|
||||||
|
On accessibility: Django admin is almost fully accessible out-of-the-box, the exceptions being tables, checkboxes, and
|
||||||
|
color contrast. We have remedied the first 2 with template overrides and the 3rd with theming (see below).
|
||||||
|
|
||||||
|
On USWDS and theming: Django admin brings its own high level design framework. We have determined that theming on top of Django (scss)
|
||||||
|
is easy and worthwhile, while overwriting Django's templates with USWDS is hard and provides little return on investment
|
||||||
|
([research PR](https://github.com/cisagov/getgov/pull/831)).
|
||||||
|
|
||||||
While we anticipate that Django Admin will meet (or even exceed) the user needs that we are aware of today, it is still
|
While we anticipate that Django Admin will meet (or even exceed) the user needs that we are aware of today, it is still
|
||||||
an open question whether Django Admin will be the long-term administrator tool of choice. A pivot away from Django Admin
|
an open question whether Django Admin will be the long-term administrator tool of choice. A pivot away from Django Admin
|
||||||
in the future would of course mean starting from scratch at a later date, and potentially juggling two separate admin
|
in the future would of course mean starting from scratch at a later date, and potentially juggling two separate admin
|
||||||
|
|
|
@ -18,9 +18,19 @@ If you're new to Django, see [Getting Started with Django](https://www.djangopro
|
||||||
|
|
||||||
Visit the running application at [http://localhost:8080](http://localhost:8080).
|
Visit the running application at [http://localhost:8080](http://localhost:8080).
|
||||||
|
|
||||||
Note: If you are using Windows, you may need to change your [line endings](https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings). If not, you may not be able to run manage.py.
|
|
||||||
|
### Troubleshooting
|
||||||
|
|
||||||
|
* If you are using Windows, you may need to change your [line endings](https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings). If not, you may not be able to run manage.py.
|
||||||
* Unix based operating systems (like macOS or Linux) handle line separators [differently than Windows does](https://superuser.com/questions/374028/how-are-n-and-r-handled-differently-on-linux-and-windows). This can break bash scripts in particular. In the case of manage.py, it uses *#!/usr/bin/env python* to access the Python executable. Since the script is still thinking in terms of unix line seperators, it may look for the executable *python\r* rather than *python* (since Windows cannot read the carriage return on its own) - thus leading to the error `usr/bin/env: 'python\r' no such file or directory`
|
* Unix based operating systems (like macOS or Linux) handle line separators [differently than Windows does](https://superuser.com/questions/374028/how-are-n-and-r-handled-differently-on-linux-and-windows). This can break bash scripts in particular. In the case of manage.py, it uses *#!/usr/bin/env python* to access the Python executable. Since the script is still thinking in terms of unix line seperators, it may look for the executable *python\r* rather than *python* (since Windows cannot read the carriage return on its own) - thus leading to the error `usr/bin/env: 'python\r' no such file or directory`
|
||||||
* If you'd rather not change this globally, add a `.gitattributes` file in the project root with `* text eol=lf` as the text content, and [refresh the repo](https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings#refreshing-a-repository-after-changing-line-endings)
|
* If you'd rather not change this globally, add a `.gitattributes` file in the project root with `* text eol=lf` as the text content, and [refresh the repo](https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings#refreshing-a-repository-after-changing-line-endings)
|
||||||
|
* If you are using a Mac with a M1 chip, and see this error `The chromium binary is not available for arm64.` or an error involving `puppeteer`, try adding this line below into your `.bashrc` or `.zshrc`.
|
||||||
|
|
||||||
|
```
|
||||||
|
export DOCKER_DEFAULT_PLATFORM=linux/amd64
|
||||||
|
```
|
||||||
|
|
||||||
|
When completed, don't forget to rerun `docker-compose up`!
|
||||||
|
|
||||||
## Branch Conventions
|
## Branch Conventions
|
||||||
|
|
||||||
|
@ -84,6 +94,7 @@ 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 applications" you should see fake domains with various fake statuses.
|
||||||
|
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:
|
||||||
|
@ -105,6 +116,7 @@ Analysts are a variant of the admin role with limited permissions. The process f
|
||||||
```
|
```
|
||||||
|
|
||||||
5. In the browser, navigate to /admin. To verify that all is working correctly, verify that you can only see a sub-section of the modules and some are set to view-only.
|
5. In the browser, navigate to /admin. To verify that all is working correctly, verify that you can only see a sub-section of the modules and some are set to view-only.
|
||||||
|
6. Add an optional email key/value pair
|
||||||
|
|
||||||
Do note that if you wish to have both an analyst and admin account, append `-Analyst` to your first and last name, or use a completely different first/last name to avoid confusion. Example: `Bob-Analyst`
|
Do note that if you wish to have both an analyst and admin account, append `-Analyst` to your first and last name, or use a completely different first/last name to avoid confusion. Example: `Bob-Analyst`
|
||||||
## Adding to CODEOWNERS (optional)
|
## Adding to CODEOWNERS (optional)
|
||||||
|
|
|
@ -277,22 +277,27 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
||||||
pass
|
pass
|
||||||
elif obj.status == models.DomainApplication.SUBMITTED:
|
elif obj.status == models.DomainApplication.SUBMITTED:
|
||||||
# This is an fsm in model which will throw an error if the
|
# This is an fsm in model which will throw an error if the
|
||||||
# transition condition is violated, so we call it on the
|
# transition condition is violated, so we roll back the
|
||||||
# original object which has the right status value, and pass
|
# status to what it was before the admin user changed it and
|
||||||
# the updated object which contains the up-to-date data
|
# let the fsm method set it. Same comment applies to
|
||||||
# for the side effects (like an email send). Same
|
# transition method calls below.
|
||||||
# comment applies to original_obj method calls below.
|
obj.status = original_obj.status
|
||||||
original_obj.submit(updated_domain_application=obj)
|
obj.submit()
|
||||||
elif obj.status == models.DomainApplication.IN_REVIEW:
|
elif obj.status == models.DomainApplication.IN_REVIEW:
|
||||||
original_obj.in_review(updated_domain_application=obj)
|
obj.status = original_obj.status
|
||||||
|
obj.in_review()
|
||||||
elif obj.status == models.DomainApplication.ACTION_NEEDED:
|
elif obj.status == models.DomainApplication.ACTION_NEEDED:
|
||||||
original_obj.action_needed(updated_domain_application=obj)
|
obj.status = original_obj.status
|
||||||
|
obj.action_needed()
|
||||||
elif obj.status == models.DomainApplication.APPROVED:
|
elif obj.status == models.DomainApplication.APPROVED:
|
||||||
original_obj.approve(updated_domain_application=obj)
|
obj.status = original_obj.status
|
||||||
|
obj.approve()
|
||||||
elif obj.status == models.DomainApplication.WITHDRAWN:
|
elif obj.status == models.DomainApplication.WITHDRAWN:
|
||||||
original_obj.withdraw()
|
obj.status = original_obj.status
|
||||||
|
obj.withdraw()
|
||||||
elif obj.status == models.DomainApplication.REJECTED:
|
elif obj.status == models.DomainApplication.REJECTED:
|
||||||
original_obj.reject(updated_domain_application=obj)
|
obj.status = original_obj.status
|
||||||
|
obj.reject()
|
||||||
else:
|
else:
|
||||||
logger.warning("Unknown status selected in django admin")
|
logger.warning("Unknown status selected in django admin")
|
||||||
|
|
||||||
|
|
|
@ -105,19 +105,29 @@ html[data-theme="light"] {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dark mode django (bug due to scss cascade) and USWDS tables
|
// Dark mode django (bug due to scss cascade) and USWDS tables
|
||||||
body,
|
|
||||||
.change-list .usa-table,
|
.change-list .usa-table,
|
||||||
.change-list .usa-table--striped tbody tr:nth-child(odd) td {
|
.change-list .usa-table--striped tbody tr:nth-child(odd) td,
|
||||||
color: var(--body-fg)!important;
|
.change-list .usa-table--borderless thead th,
|
||||||
|
.change-list .usa-table thead td,
|
||||||
|
.change-list .usa-table thead th,
|
||||||
|
body.dashboard,
|
||||||
|
body.change-list,
|
||||||
|
body.change-form {
|
||||||
|
color: var(--body-fg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Firefox needs this to be specifically set
|
// Firefox needs this to be specifically set
|
||||||
html[data-theme="dark"] {
|
html[data-theme="dark"] {
|
||||||
body,
|
|
||||||
.change-list .usa-table,
|
.change-list .usa-table,
|
||||||
.change-list .usa-table--striped tbody tr:nth-child(odd) td {
|
.change-list .usa-table--striped tbody tr:nth-child(odd) td,
|
||||||
color: var(--body-fg)!important;
|
.change-list .usa-table--borderless thead th,
|
||||||
|
.change-list .usa-table thead td,
|
||||||
|
.change-list .usa-table thead th,
|
||||||
|
body.dashboard,
|
||||||
|
body.change-list,
|
||||||
|
body.change-form {
|
||||||
|
color: var(--body-fg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ For more information see:
|
||||||
https://docs.djangoproject.com/en/4.0/topics/http/urls/
|
https://docs.djangoproject.com/en/4.0/topics/http/urls/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
from django.views.generic import RedirectView
|
from django.views.generic import RedirectView
|
||||||
|
@ -45,6 +44,10 @@ for step, view in [
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", views.index, name="home"),
|
path("", views.index, name="home"),
|
||||||
|
path(
|
||||||
|
"admin/logout/",
|
||||||
|
RedirectView.as_view(pattern_name="logout", permanent=False),
|
||||||
|
),
|
||||||
path("admin/", admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
path(
|
path(
|
||||||
"application/<id>/edit/",
|
"application/<id>/edit/",
|
||||||
|
@ -114,20 +117,6 @@ urlpatterns = [
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
if not settings.DEBUG:
|
|
||||||
urlpatterns += [
|
|
||||||
# redirect to login.gov
|
|
||||||
path(
|
|
||||||
"admin/login/", RedirectView.as_view(pattern_name="login", permanent=False)
|
|
||||||
),
|
|
||||||
# redirect to login.gov
|
|
||||||
path(
|
|
||||||
"admin/logout/",
|
|
||||||
RedirectView.as_view(pattern_name="logout", permanent=False),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
# we normally would guard these with `if settings.DEBUG` but tests run with
|
# we normally would guard these with `if settings.DEBUG` but tests run with
|
||||||
# DEBUG = False even when these apps have been loaded because settings.DEBUG
|
# DEBUG = False even when these apps have been loaded because settings.DEBUG
|
||||||
# was actually True. Instead, let's add these URLs any time we are able to
|
# was actually True. Instead, let's add these URLs any time we are able to
|
||||||
|
|
|
@ -67,6 +67,11 @@ class UserFixture:
|
||||||
"first_name": "Paul",
|
"first_name": "Paul",
|
||||||
"last_name": "Kuykendall",
|
"last_name": "Kuykendall",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"username": "2a88a97b-be96-4aad-b99e-0b605b492c78",
|
||||||
|
"first_name": "Rebecca",
|
||||||
|
"last_name": "Hsieh",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
STAFF = [
|
STAFF = [
|
||||||
|
@ -74,6 +79,7 @@ class UserFixture:
|
||||||
"username": "319c490d-453b-43d9-bc4d-7d6cd8ff6844",
|
"username": "319c490d-453b-43d9-bc4d-7d6cd8ff6844",
|
||||||
"first_name": "Rachid-Analyst",
|
"first_name": "Rachid-Analyst",
|
||||||
"last_name": "Mrad-Analyst",
|
"last_name": "Mrad-Analyst",
|
||||||
|
"email": "rachid.mrad@gmail.com",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"username": "b6a15987-5c88-4e26-8de2-ca71a0bdb2cd",
|
"username": "b6a15987-5c88-4e26-8de2-ca71a0bdb2cd",
|
||||||
|
@ -90,6 +96,11 @@ class UserFixture:
|
||||||
"first_name": "Paul-Analyst",
|
"first_name": "Paul-Analyst",
|
||||||
"last_name": "Kuykendall-Analyst",
|
"last_name": "Kuykendall-Analyst",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"username": "e474e7a9-71ca-449d-833c-8a6e094dd117",
|
||||||
|
"first_name": "Rebecca-Analyst",
|
||||||
|
"last_name": "Hsieh-Analyst",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
STAFF_PERMISSIONS = [
|
STAFF_PERMISSIONS = [
|
||||||
|
@ -119,6 +130,8 @@ class UserFixture:
|
||||||
user.is_superuser = True
|
user.is_superuser = True
|
||||||
user.first_name = admin["first_name"]
|
user.first_name = admin["first_name"]
|
||||||
user.last_name = admin["last_name"]
|
user.last_name = admin["last_name"]
|
||||||
|
if "email" in admin.keys():
|
||||||
|
user.email = admin["email"]
|
||||||
user.is_staff = True
|
user.is_staff = True
|
||||||
user.is_active = True
|
user.is_active = True
|
||||||
user.save()
|
user.save()
|
||||||
|
@ -136,6 +149,8 @@ class UserFixture:
|
||||||
user.is_superuser = False
|
user.is_superuser = False
|
||||||
user.first_name = staff["first_name"]
|
user.first_name = staff["first_name"]
|
||||||
user.last_name = staff["last_name"]
|
user.last_name = staff["last_name"]
|
||||||
|
if "email" in admin.keys():
|
||||||
|
user.email = admin["email"]
|
||||||
user.is_staff = True
|
user.is_staff = True
|
||||||
user.is_active = True
|
user.is_active = True
|
||||||
|
|
||||||
|
|
|
@ -504,15 +504,10 @@ class DomainApplication(TimeStampedModel):
|
||||||
@transition(
|
@transition(
|
||||||
field="status", source=[STARTED, ACTION_NEEDED, WITHDRAWN], target=SUBMITTED
|
field="status", source=[STARTED, ACTION_NEEDED, WITHDRAWN], target=SUBMITTED
|
||||||
)
|
)
|
||||||
def submit(self, updated_domain_application=None):
|
def submit(self):
|
||||||
"""Submit an application that is started.
|
"""Submit an application that is started.
|
||||||
|
|
||||||
As a side effect, an email notification is sent.
|
As a side effect, an email notification is sent."""
|
||||||
|
|
||||||
This method is called in admin.py on the original application
|
|
||||||
which has the correct status value, but is passed the changed
|
|
||||||
application which has the up-to-date data that we'll use
|
|
||||||
in the email."""
|
|
||||||
|
|
||||||
# check our conditions here inside the `submit` method so that we
|
# check our conditions here inside the `submit` method so that we
|
||||||
# can raise more informative exceptions
|
# can raise more informative exceptions
|
||||||
|
@ -528,16 +523,6 @@ class DomainApplication(TimeStampedModel):
|
||||||
if not DraftDomain.string_could_be_domain(self.requested_domain.name):
|
if not DraftDomain.string_could_be_domain(self.requested_domain.name):
|
||||||
raise ValueError("Requested domain is not a valid domain name.")
|
raise ValueError("Requested domain is not a valid domain name.")
|
||||||
|
|
||||||
if updated_domain_application is not None:
|
|
||||||
# A DomainApplication is being passed to this method (ie from admin)
|
|
||||||
updated_domain_application._send_status_update_email(
|
|
||||||
"submission confirmation",
|
|
||||||
"emails/submission_confirmation.txt",
|
|
||||||
"emails/submission_confirmation_subject.txt",
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# Or this method is called with the right application
|
|
||||||
# for context, ie from views/application.py
|
|
||||||
self._send_status_update_email(
|
self._send_status_update_email(
|
||||||
"submission confirmation",
|
"submission confirmation",
|
||||||
"emails/submission_confirmation.txt",
|
"emails/submission_confirmation.txt",
|
||||||
|
@ -545,29 +530,24 @@ class DomainApplication(TimeStampedModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
@transition(field="status", source=SUBMITTED, target=IN_REVIEW)
|
@transition(field="status", source=SUBMITTED, target=IN_REVIEW)
|
||||||
def in_review(self, updated_domain_application):
|
def in_review(self):
|
||||||
"""Investigate an application that has been submitted.
|
"""Investigate an application that has been submitted.
|
||||||
|
|
||||||
As a side effect, an email notification is sent.
|
As a side effect, an email notification is sent."""
|
||||||
|
|
||||||
This method is called in admin.py on the original application
|
self._send_status_update_email(
|
||||||
which has the correct status value, but is passed the changed
|
|
||||||
application which has the up-to-date data that we'll use
|
|
||||||
in the email."""
|
|
||||||
|
|
||||||
updated_domain_application._send_status_update_email(
|
|
||||||
"application in review",
|
"application in review",
|
||||||
"emails/status_change_in_review.txt",
|
"emails/status_change_in_review.txt",
|
||||||
"emails/status_change_in_review_subject.txt",
|
"emails/status_change_in_review_subject.txt",
|
||||||
)
|
)
|
||||||
|
|
||||||
@transition(field="status", source=[IN_REVIEW, REJECTED], target=ACTION_NEEDED)
|
@transition(field="status", source=[IN_REVIEW, REJECTED], target=ACTION_NEEDED)
|
||||||
def action_needed(self, updated_domain_application):
|
def action_needed(self):
|
||||||
"""Send back an application that is under investigation or rejected.
|
"""Send back an application that is under investigation or rejected.
|
||||||
|
|
||||||
As a side effect, an email notification is sent, similar to in_review"""
|
As a side effect, an email notification is sent."""
|
||||||
|
|
||||||
updated_domain_application._send_status_update_email(
|
self._send_status_update_email(
|
||||||
"action needed",
|
"action needed",
|
||||||
"emails/status_change_action_needed.txt",
|
"emails/status_change_action_needed.txt",
|
||||||
"emails/status_change_action_needed_subject.txt",
|
"emails/status_change_action_needed_subject.txt",
|
||||||
|
@ -576,19 +556,13 @@ class DomainApplication(TimeStampedModel):
|
||||||
@transition(
|
@transition(
|
||||||
field="status", source=[SUBMITTED, IN_REVIEW, REJECTED], target=APPROVED
|
field="status", source=[SUBMITTED, IN_REVIEW, REJECTED], target=APPROVED
|
||||||
)
|
)
|
||||||
def approve(self, updated_domain_application=None):
|
def approve(self):
|
||||||
"""Approve an application that has been submitted.
|
"""Approve an application that has been submitted.
|
||||||
|
|
||||||
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
|
application into an admin on that domain. It also triggers an email
|
||||||
notification.
|
notification."""
|
||||||
|
|
||||||
This method is called in admin.py on the original application
|
|
||||||
which has the correct status value, but is passed the changed
|
|
||||||
application which has the up-to-date data that we'll use
|
|
||||||
in the email.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# create the domain
|
# create the domain
|
||||||
Domain = apps.get_model("registrar.Domain")
|
Domain = apps.get_model("registrar.Domain")
|
||||||
|
@ -607,14 +581,6 @@ class DomainApplication(TimeStampedModel):
|
||||||
user=self.creator, domain=created_domain, role=UserDomainRole.Roles.ADMIN
|
user=self.creator, domain=created_domain, role=UserDomainRole.Roles.ADMIN
|
||||||
)
|
)
|
||||||
|
|
||||||
if updated_domain_application is not None:
|
|
||||||
# A DomainApplication is being passed to this method (ie from admin)
|
|
||||||
updated_domain_application._send_status_update_email(
|
|
||||||
"application approved",
|
|
||||||
"emails/status_change_approved.txt",
|
|
||||||
"emails/status_change_approved_subject.txt",
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self._send_status_update_email(
|
self._send_status_update_email(
|
||||||
"application approved",
|
"application approved",
|
||||||
"emails/status_change_approved.txt",
|
"emails/status_change_approved.txt",
|
||||||
|
@ -626,12 +592,12 @@ class DomainApplication(TimeStampedModel):
|
||||||
"""Withdraw an application that has been submitted."""
|
"""Withdraw an application that has been submitted."""
|
||||||
|
|
||||||
@transition(field="status", source=[IN_REVIEW, APPROVED], target=REJECTED)
|
@transition(field="status", source=[IN_REVIEW, APPROVED], target=REJECTED)
|
||||||
def reject(self, updated_domain_application):
|
def reject(self):
|
||||||
"""Reject an application that has been submitted.
|
"""Reject an application that has been submitted.
|
||||||
|
|
||||||
As a side effect, an email notification is sent, similar to in_review"""
|
As a side effect, an email notification is sent, similar to in_review"""
|
||||||
|
|
||||||
updated_domain_application._send_status_update_email(
|
self._send_status_update_email(
|
||||||
"action needed",
|
"action needed",
|
||||||
"emails/status_change_rejected.txt",
|
"emails/status_change_rejected.txt",
|
||||||
"emails/status_change_rejected_subject.txt",
|
"emails/status_change_rejected_subject.txt",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{% extends "admin/base.html" %}
|
{% extends "admin/base.html" %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
|
{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
|
||||||
|
|
||||||
|
@ -13,5 +14,25 @@
|
||||||
{% include "admin/color_theme_toggle.html" %}
|
{% include "admin/color_theme_toggle.html" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
{% comment %}
|
||||||
|
This was copied from the 'userlinks' template, with a few minor changes.
|
||||||
|
You can find that here:
|
||||||
|
https://github.com/django/django/blob/d25f3892114466d689fd6936f79f3bd9a9acc30e/django/contrib/admin/templates/admin/base.html#L59
|
||||||
|
{% endcomment %}
|
||||||
|
{% block userlinks %}
|
||||||
|
{% if site_url %}
|
||||||
|
<a href="{{ site_url }}">{% translate 'View site' %}</a> /
|
||||||
|
{% endif %}
|
||||||
|
{% if user.is_active and user.is_staff %}
|
||||||
|
{% url 'django-admindocs-docroot' as docsroot %}
|
||||||
|
{% if docsroot %}
|
||||||
|
<a href="{{ docsroot }}">{% translate 'Documentation' %}</a> /
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% if user.has_usable_password %}
|
||||||
|
<a href="{% url 'admin:password_change' %}">{% translate 'Change password' %}</a> /
|
||||||
|
{% endif %}
|
||||||
|
<a href="{% url 'admin:logout' %}" id="admin-logout-button">{% translate 'Log out' %}</a>
|
||||||
|
{% include "admin/color_theme_toggle.html" %}
|
||||||
|
{% endblock %}
|
||||||
{% block nav-global %}{% endblock %}
|
{% block nav-global %}{% endblock %}
|
|
@ -17,6 +17,7 @@ Load our custom filters to extract info from the django generated markup.
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
||||||
|
{% if results.0.form %}
|
||||||
{# .gov - hardcode the select all checkbox #}
|
{# .gov - hardcode the select all checkbox #}
|
||||||
<th scope="col" class="action-checkbox-column" title="Toggle all">
|
<th scope="col" class="action-checkbox-column" title="Toggle all">
|
||||||
<div class="text">
|
<div class="text">
|
||||||
|
@ -29,7 +30,6 @@ Load our custom filters to extract info from the django generated markup.
|
||||||
</th>
|
</th>
|
||||||
{# .gov - don't let django generate the select all checkbox #}
|
{# .gov - don't let django generate the select all checkbox #}
|
||||||
{% for header in result_headers|slice:"1:" %}
|
{% for header in result_headers|slice:"1:" %}
|
||||||
|
|
||||||
<th scope="col"{{ header.class_attrib }}>
|
<th scope="col"{{ header.class_attrib }}>
|
||||||
{% if header.sortable %}
|
{% if header.sortable %}
|
||||||
{% if header.sort_priority > 0 %}
|
{% if header.sort_priority > 0 %}
|
||||||
|
@ -48,21 +48,13 @@ Load our custom filters to extract info from the django generated markup.
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
||||||
{% comment %}
|
|
||||||
{% for result in results %}
|
|
||||||
{% if result.form.non_field_errors %}
|
|
||||||
<tr><td colspan="{{ result|length }}">{{ result.form.non_field_errors }}</td></tr>
|
|
||||||
{% endif %}
|
|
||||||
<tr>{% for item in result %}{{ item }}{% endfor %}</tr>
|
|
||||||
{% endfor %}
|
|
||||||
{% endcomment %}
|
|
||||||
|
|
||||||
{% comment %}
|
{% comment %}
|
||||||
.gov - hardcode the row checkboxes using the custom filters to extract
|
.gov - hardcode the row checkboxes using the custom filters to extract
|
||||||
the value attribute's value, and a label based on the anchor elements's
|
the value attribute's value, and a label based on the anchor elements's
|
||||||
text. Then edit the for loop to keep django from generating the row select
|
text. Then edit the for loop to keep django from generating the row select
|
||||||
checkboxes.
|
checkboxes.
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% for result in results %}
|
{% for result in results %}
|
||||||
{% if result.form.non_field_errors %}
|
{% if result.form.non_field_errors %}
|
||||||
<tr><td colspan="{{ result|length }}">{{ result.form.non_field_errors }}</td></tr>
|
<tr><td colspan="{{ result|length }}">{{ result.form.non_field_errors }}</td></tr>
|
||||||
|
@ -84,6 +76,36 @@ checkboxes.
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
{% else %} {# results doesn't have a form as its first element #}
|
||||||
|
|
||||||
|
{% for header in result_headers %}
|
||||||
|
<th scope="col"{{ header.class_attrib }}>
|
||||||
|
{% if header.sortable %}
|
||||||
|
{% if header.sort_priority > 0 %}
|
||||||
|
<div class="sortoptions">
|
||||||
|
<a class="sortremove" href="{{ header.url_remove }}" title="{% translate "Remove from sorting" %}"></a>
|
||||||
|
{% if num_sorted_fields > 1 %}<span class="sortpriority" title="{% blocktranslate with priority_number=header.sort_priority %}Sorting priority: {{ priority_number }}{% endblocktranslate %}">{{ header.sort_priority }}</span>{% endif %}
|
||||||
|
<a href="{{ header.url_toggle }}" class="toggle {% if header.ascending %}ascending{% else %}descending{% endif %}" title="{% translate "Toggle sorting" %}"></a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
<div class="text">{% if header.sortable %}<a href="{{ header.url_primary }}">{{ header.text|capfirst }}</a>{% else %}<span>{{ header.text|capfirst }}</span>{% endif %}</div>
|
||||||
|
<div class="clear"></div>
|
||||||
|
</th>{% endfor %}
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
{% for result in results %}
|
||||||
|
{% if result.form.non_field_errors %}
|
||||||
|
<tr><td colspan="{{ result|length }}">{{ result.form.non_field_errors }}</td></tr>
|
||||||
|
{% endif %}
|
||||||
|
<tr>{% for item in result %}{{ item }}{% endfor %}</tr>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,13 +6,13 @@
|
||||||
Who is the authorizing official for your organization?
|
Who is the authorizing official for your organization?
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<p>Your authorizing official is the person within your organization who can authorize your domain request. This is generally the highest-ranking or highest-elected official in your organization.</p>
|
<p>Your authorizing official is the person within your organization who can authorize your domain request. This person must be in a role of significant, executive responsibility within the organization.</p>
|
||||||
|
|
||||||
<div class="ao_example">
|
<div class="ao_example">
|
||||||
{% include "includes/ao_example.html" %}
|
{% include "includes/ao_example.html" %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>We might contact your authorizing official, or their office, to double check that they approve this request. Read more about <a href="{% public_site_url 'domains/eligibility/#you-must-have-approval-from-an-authorizing-official-within-your-organization' %}">who can serve as an authorizing official</a>.</p>
|
<p>We typically don’t reach out to the authorizing official, but if contact is necessary, our practice is to coordinate first with you, the requestor. Read more about <a href="{% public_site_url 'domains/eligibility/#you-must-have-approval-from-an-authorizing-official-within-your-organization' %}">who can serve as an authorizing official</a>.</p>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,13 @@
|
||||||
{% load static field_helpers %}
|
{% load static field_helpers %}
|
||||||
|
|
||||||
{% block form_instructions %}
|
{% block form_instructions %}
|
||||||
<p>We’d like to contact other employees in your organization about your domain request. For example, they could be involved in managing your organization or its technical infrastructure. <strong>This information will help us assess your eligibility for a .gov domain.</strong> These contacts should be in addition to you and your authorizing official. They should be employees of your organization.</p>
|
<p>To help us assess your eligibility for a .gov domain, please provide contact information for other employees from your organization.
|
||||||
|
<ul class="usa-list">
|
||||||
|
<li>They should be clearly and publicly affiliated with your organization and familiar with your domain request. </li>
|
||||||
|
<li>They don't need to be involved with the technical management of your domain (although they can be). </li>
|
||||||
|
<li>We typically don’t reach out to these employees, but if contact is necessary, our practice is to coordinate first with you, the requestor. </li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
{% if domainapplication.status == 'approved' %} Approved
|
{% if domainapplication.status == 'approved' %} Approved
|
||||||
{% elif domainapplication.status == 'in review' %} In Review
|
{% elif domainapplication.status == 'in review' %} In Review
|
||||||
{% elif domainapplication.status == 'rejected' %} Rejected
|
{% elif domainapplication.status == 'rejected' %} Rejected
|
||||||
{% elif domainapplication.status == 'submitted' %} Received
|
{% elif domainapplication.status == 'submitted' %} Submitted
|
||||||
{% else %}ERROR Please contact technical support/dev
|
{% else %}ERROR Please contact technical support/dev
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -125,13 +125,14 @@
|
||||||
<p>You don't have any archived domains</p>
|
<p>You don't have any archived domains</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="tablet:grid-col-11 desktop:grid-col-10">
|
<!-- Note: Uncomment below when this is being implemented post-MVP -->
|
||||||
|
<!-- <section class="tablet:grid-col-11 desktop:grid-col-10">
|
||||||
<h2 class="padding-top-1 mobile-lg:padding-top-3"> Export domains</h2>
|
<h2 class="padding-top-1 mobile-lg:padding-top-3"> Export domains</h2>
|
||||||
<p>Download a list of your domains and their statuses as a csv file.</p>
|
<p>Download a list of your domains and their statuses as a csv file.</p>
|
||||||
<a href="{% url 'todo' %}" class="usa-button usa-button--outline">
|
<a href="{% url 'todo' %}" class="usa-button usa-button--outline">
|
||||||
Export domains as csv
|
Export domains as csv
|
||||||
</a>
|
</a>
|
||||||
</section>
|
</section> -->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -174,6 +174,33 @@ class TestDomainApplicationAdmin(TestCase):
|
||||||
# Perform assertions on the mock call itself
|
# Perform assertions on the mock call itself
|
||||||
mock_client_instance.send_email.assert_called_once()
|
mock_client_instance.send_email.assert_called_once()
|
||||||
|
|
||||||
|
def test_save_model_sets_approved_domain(self):
|
||||||
|
# make sure there is no user with this email
|
||||||
|
EMAIL = "mayor@igorville.gov"
|
||||||
|
User.objects.filter(email=EMAIL).delete()
|
||||||
|
|
||||||
|
# Create a sample application
|
||||||
|
application = completed_application(status=DomainApplication.IN_REVIEW)
|
||||||
|
|
||||||
|
# Create a mock request
|
||||||
|
request = self.factory.post(
|
||||||
|
"/admin/registrar/domainapplication/{}/change/".format(application.pk)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create an instance of the model admin
|
||||||
|
model_admin = DomainApplicationAdmin(DomainApplication, self.site)
|
||||||
|
|
||||||
|
# Modify the application's property
|
||||||
|
application.status = DomainApplication.APPROVED
|
||||||
|
|
||||||
|
# Use the model admin's save_model method
|
||||||
|
model_admin.save_model(request, application, form=None, change=True)
|
||||||
|
|
||||||
|
# Test that approved domain exists and equals requested domain
|
||||||
|
self.assertEqual(
|
||||||
|
application.requested_domain.name, application.approved_domain.name
|
||||||
|
)
|
||||||
|
|
||||||
@boto3_mocking.patching
|
@boto3_mocking.patching
|
||||||
def test_save_model_sends_action_needed_email(self):
|
def test_save_model_sends_action_needed_email(self):
|
||||||
# make sure there is no user with this email
|
# make sure there is no user with this email
|
||||||
|
|
|
@ -7,7 +7,7 @@ certifi==2023.7.22 ; python_version >= '3.6'
|
||||||
cfenv==0.5.3
|
cfenv==0.5.3
|
||||||
cffi==1.15.1
|
cffi==1.15.1
|
||||||
charset-normalizer==3.1.0 ; python_full_version >= '3.7.0'
|
charset-normalizer==3.1.0 ; python_full_version >= '3.7.0'
|
||||||
cryptography==41.0.2 ; python_version >= '3.7'
|
cryptography==41.0.3 ; python_version >= '3.7'
|
||||||
defusedxml==0.7.1 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
defusedxml==0.7.1 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
||||||
dj-database-url==2.0.0
|
dj-database-url==2.0.0
|
||||||
dj-email-url==1.0.6
|
dj-email-url==1.0.6
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
10027 OUTOFSCOPE http://app:8080/public/js/uswds-init.min.js
|
10027 OUTOFSCOPE http://app:8080/public/js/uswds-init.min.js
|
||||||
# get-gov.js contains suspicious word "from" as in `Array.from()`
|
# get-gov.js contains suspicious word "from" as in `Array.from()`
|
||||||
10027 OUTOFSCOPE http://app:8080/public/js/get-gov.js
|
10027 OUTOFSCOPE http://app:8080/public/js/get-gov.js
|
||||||
|
# Ignore wording of "TODO"
|
||||||
|
10027 OUTOFSCOPE http://app:8080.*$
|
||||||
10028 FAIL (Open Redirect - Passive/beta)
|
10028 FAIL (Open Redirect - Passive/beta)
|
||||||
10029 FAIL (Cookie Poisoning - Passive/beta)
|
10029 FAIL (Cookie Poisoning - Passive/beta)
|
||||||
10030 FAIL (User Controllable Charset - Passive/beta)
|
10030 FAIL (User Controllable Charset - Passive/beta)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue