mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-04 00:42:16 +02:00
merged main and resolved conflict
This commit is contained in:
commit
1c1ae51023
14 changed files with 461 additions and 110 deletions
3
.github/workflows/deploy-sandbox.yaml
vendored
3
.github/workflows/deploy-sandbox.yaml
vendored
|
@ -25,6 +25,9 @@ jobs:
|
|||
|| startsWith(github.head_ref, 'meoward/')
|
||||
|| startsWith(github.head_ref, 'bob/')
|
||||
|| startsWith(github.head_ref, 'cb/')
|
||||
|| startsWith(github.head_ref, 'hotgov/')
|
||||
|| startsWith(github.head_ref, 'litterbox/')
|
||||
|| startsWith(github.head_ref, 'ag/')
|
||||
outputs:
|
||||
environment: ${{ steps.var.outputs.environment}}
|
||||
runs-on: "ubuntu-latest"
|
||||
|
|
2
.github/workflows/migrate.yaml
vendored
2
.github/workflows/migrate.yaml
vendored
|
@ -17,6 +17,8 @@ on:
|
|||
- staging
|
||||
- development
|
||||
- ag
|
||||
- litterbox
|
||||
- hotgov
|
||||
- cb
|
||||
- bob
|
||||
- meoward
|
||||
|
|
2
.github/workflows/reset-db.yaml
vendored
2
.github/workflows/reset-db.yaml
vendored
|
@ -17,6 +17,8 @@ on:
|
|||
- staging
|
||||
- development
|
||||
- ag
|
||||
- litterbox
|
||||
- hotgov
|
||||
- cb
|
||||
- bob
|
||||
- meoward
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
# Complete model documentation
|
||||
|
||||
This is an auto-generated diagram of our data models generated with the
|
||||
[django-model2puml](https://github.com/sen-den/django-model2puml) library
|
||||
using the command
|
||||
[django-model2puml](https://github.com/sen-den/django-model2puml) library.
|
||||
|
||||
## How to generate the puml
|
||||
|
||||
1. Uncomment `puml_generator` from `INSTALLED_APPS` in settings.py and docker-compose down and up
|
||||
2. Run the following command to generate a puml file
|
||||
|
||||
```bash
|
||||
$ docker compose app ./manage.py generate_puml --include registrar
|
||||
docker compose exec app ./manage.py generate_puml --include registrar
|
||||
```
|
||||
|
||||

|
||||
|
@ -13,12 +17,19 @@ $ docker compose app ./manage.py generate_puml --include registrar
|
|||
<details>
|
||||
<summary>PlantUML source code</summary>
|
||||
|
||||
To regenerate this image using Docker, run
|
||||
## How To regenerate the database svg image
|
||||
|
||||
1. Copy your puml file contents into the bottom of this file and replace the current code marked by `plantuml`
|
||||
2. Run the following command
|
||||
|
||||
```bash
|
||||
$ docker run -v $(pwd):$(pwd) -w $(pwd) -it plantuml/plantuml -tsvg models_diagram.md
|
||||
docker run -v $(pwd):$(pwd) -w $(pwd) -it plantuml/plantuml -tsvg models_diagram.md
|
||||
```
|
||||
|
||||
3. Remove the puml file from earlier (if you still have it)
|
||||
4. Commit the new image and the md file
|
||||
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
class "registrar.Contact <Registrar>" as registrar.Contact #d6f4e9 {
|
||||
|
@ -28,17 +39,97 @@ class "registrar.Contact <Registrar>" as registrar.Contact #d6f4e9 {
|
|||
+ created_at (DateTimeField)
|
||||
+ updated_at (DateTimeField)
|
||||
~ user (OneToOneField)
|
||||
+ first_name (TextField)
|
||||
+ middle_name (TextField)
|
||||
+ last_name (TextField)
|
||||
+ title (TextField)
|
||||
+ email (TextField)
|
||||
+ first_name (CharField)
|
||||
+ middle_name (CharField)
|
||||
+ last_name (CharField)
|
||||
+ title (CharField)
|
||||
+ email (EmailField)
|
||||
+ phone (PhoneNumberField)
|
||||
--
|
||||
}
|
||||
registrar.Contact -- registrar.User
|
||||
|
||||
|
||||
class "registrar.Host <Registrar>" as registrar.Host #d6f4e9 {
|
||||
host
|
||||
--
|
||||
+ id (BigAutoField)
|
||||
+ created_at (DateTimeField)
|
||||
+ updated_at (DateTimeField)
|
||||
+ name (CharField)
|
||||
~ domain (ForeignKey)
|
||||
--
|
||||
}
|
||||
registrar.Host -- registrar.Domain
|
||||
|
||||
|
||||
class "registrar.HostIP <Registrar>" as registrar.HostIP #d6f4e9 {
|
||||
host ip
|
||||
--
|
||||
+ id (BigAutoField)
|
||||
+ created_at (DateTimeField)
|
||||
+ updated_at (DateTimeField)
|
||||
+ address (CharField)
|
||||
~ host (ForeignKey)
|
||||
--
|
||||
}
|
||||
registrar.HostIP -- registrar.Host
|
||||
|
||||
|
||||
class "registrar.PublicContact <Registrar>" as registrar.PublicContact #d6f4e9 {
|
||||
public contact
|
||||
--
|
||||
+ id (BigAutoField)
|
||||
+ created_at (DateTimeField)
|
||||
+ updated_at (DateTimeField)
|
||||
+ contact_type (CharField)
|
||||
+ registry_id (CharField)
|
||||
~ domain (ForeignKey)
|
||||
+ name (CharField)
|
||||
+ org (CharField)
|
||||
+ street1 (CharField)
|
||||
+ street2 (CharField)
|
||||
+ street3 (CharField)
|
||||
+ city (CharField)
|
||||
+ sp (CharField)
|
||||
+ pc (CharField)
|
||||
+ cc (CharField)
|
||||
+ email (EmailField)
|
||||
+ voice (CharField)
|
||||
+ fax (CharField)
|
||||
+ pw (CharField)
|
||||
--
|
||||
}
|
||||
registrar.PublicContact -- registrar.Domain
|
||||
|
||||
|
||||
class "registrar.Domain <Registrar>" as registrar.Domain #d6f4e9 {
|
||||
domain
|
||||
--
|
||||
+ id (BigAutoField)
|
||||
+ created_at (DateTimeField)
|
||||
+ updated_at (DateTimeField)
|
||||
+ name (DomainField)
|
||||
+ state (FSMField)
|
||||
+ expiration_date (DateField)
|
||||
+ security_contact_registry_id (TextField)
|
||||
+ deleted (DateField)
|
||||
+ first_ready (DateField)
|
||||
--
|
||||
}
|
||||
|
||||
|
||||
class "registrar.FederalAgency <Registrar>" as registrar.FederalAgency #d6f4e9 {
|
||||
Federal agency
|
||||
--
|
||||
+ id (BigAutoField)
|
||||
+ created_at (DateTimeField)
|
||||
+ updated_at (DateTimeField)
|
||||
+ agency (CharField)
|
||||
--
|
||||
}
|
||||
|
||||
|
||||
class "registrar.DomainRequest <Registrar>" as registrar.DomainRequest #d6f4e9 {
|
||||
domain request
|
||||
--
|
||||
|
@ -46,24 +137,25 @@ class "registrar.DomainRequest <Registrar>" as registrar.DomainRequest #d6f4e9 {
|
|||
+ created_at (DateTimeField)
|
||||
+ updated_at (DateTimeField)
|
||||
+ status (FSMField)
|
||||
+ rejection_reason (TextField)
|
||||
~ federal_agency (ForeignKey)
|
||||
~ creator (ForeignKey)
|
||||
~ investigator (ForeignKey)
|
||||
+ generic_org_type (CharField)
|
||||
+ is_election_board (BooleanField)
|
||||
+ organization_type (CharField)
|
||||
+ federally_recognized_tribe (BooleanField)
|
||||
+ state_recognized_tribe (BooleanField)
|
||||
+ tribe_name (TextField)
|
||||
+ federal_agency (TextField)
|
||||
+ tribe_name (CharField)
|
||||
+ federal_type (CharField)
|
||||
+ is_election_board (BooleanField)
|
||||
+ organization_name (TextField)
|
||||
+ address_line1 (TextField)
|
||||
+ organization_name (CharField)
|
||||
+ address_line1 (CharField)
|
||||
+ address_line2 (CharField)
|
||||
+ city (TextField)
|
||||
+ city (CharField)
|
||||
+ state_territory (CharField)
|
||||
+ zipcode (CharField)
|
||||
+ urbanization (TextField)
|
||||
+ type_of_work (TextField)
|
||||
+ more_organization_information (TextField)
|
||||
+ urbanization (CharField)
|
||||
+ about_your_organization (TextField)
|
||||
~ authorizing_official (ForeignKey)
|
||||
~ approved_domain (OneToOneField)
|
||||
~ requested_domain (OneToOneField)
|
||||
|
@ -71,17 +163,23 @@ class "registrar.DomainRequest <Registrar>" as registrar.DomainRequest #d6f4e9 {
|
|||
+ purpose (TextField)
|
||||
+ no_other_contacts_rationale (TextField)
|
||||
+ anything_else (TextField)
|
||||
+ has_anything_else_text (BooleanField)
|
||||
+ cisa_representative_email (EmailField)
|
||||
+ has_cisa_representative (BooleanField)
|
||||
+ is_policy_acknowledged (BooleanField)
|
||||
+ submission_date (DateField)
|
||||
+ notes (TextField)
|
||||
# current_websites (ManyToManyField)
|
||||
# alternative_domains (ManyToManyField)
|
||||
# other_contacts (ManyToManyField)
|
||||
--
|
||||
}
|
||||
registrar.DomainRequest -- registrar.FederalAgency
|
||||
registrar.DomainRequest -- registrar.User
|
||||
registrar.DomainRequest -- registrar.User
|
||||
registrar.DomainRequest -- registrar.Contact
|
||||
registrar.DomainRequest -- registrar.DraftDomain
|
||||
registrar.DomainRequest -- registrar.Domain
|
||||
registrar.DomainRequest -- registrar.DraftDomain
|
||||
registrar.DomainRequest -- registrar.Contact
|
||||
registrar.DomainRequest *--* registrar.Website
|
||||
registrar.DomainRequest *--* registrar.Website
|
||||
|
@ -94,35 +192,37 @@ class "registrar.DomainInformation <Registrar>" as registrar.DomainInformation #
|
|||
+ id (BigAutoField)
|
||||
+ created_at (DateTimeField)
|
||||
+ updated_at (DateTimeField)
|
||||
~ federal_agency (ForeignKey)
|
||||
~ creator (ForeignKey)
|
||||
~ domain_request (OneToOneField)
|
||||
+ generic_org_type (CharField)
|
||||
+ organization_type (CharField)
|
||||
+ federally_recognized_tribe (BooleanField)
|
||||
+ state_recognized_tribe (BooleanField)
|
||||
+ tribe_name (TextField)
|
||||
+ federal_agency (TextField)
|
||||
+ tribe_name (CharField)
|
||||
+ federal_type (CharField)
|
||||
+ is_election_board (BooleanField)
|
||||
+ organization_name (TextField)
|
||||
+ address_line1 (TextField)
|
||||
+ organization_name (CharField)
|
||||
+ address_line1 (CharField)
|
||||
+ address_line2 (CharField)
|
||||
+ city (TextField)
|
||||
+ city (CharField)
|
||||
+ state_territory (CharField)
|
||||
+ zipcode (CharField)
|
||||
+ urbanization (TextField)
|
||||
+ type_of_work (TextField)
|
||||
+ more_organization_information (TextField)
|
||||
+ urbanization (CharField)
|
||||
+ about_your_organization (TextField)
|
||||
~ authorizing_official (ForeignKey)
|
||||
~ domain (OneToOneField)
|
||||
~ submitter (ForeignKey)
|
||||
+ purpose (TextField)
|
||||
+ no_other_contacts_rationale (TextField)
|
||||
+ anything_else (TextField)
|
||||
+ cisa_representative_email (EmailField)
|
||||
+ is_policy_acknowledged (BooleanField)
|
||||
+ security_email (EmailField)
|
||||
+ notes (TextField)
|
||||
# other_contacts (ManyToManyField)
|
||||
--
|
||||
}
|
||||
registrar.DomainInformation -- registrar.FederalAgency
|
||||
registrar.DomainInformation -- registrar.User
|
||||
registrar.DomainInformation -- registrar.DomainRequest
|
||||
registrar.DomainInformation -- registrar.Contact
|
||||
|
@ -142,43 +242,6 @@ class "registrar.DraftDomain <Registrar>" as registrar.DraftDomain #d6f4e9 {
|
|||
}
|
||||
|
||||
|
||||
class "registrar.Domain <Registrar>" as registrar.Domain #d6f4e9 {
|
||||
domain
|
||||
--
|
||||
+ id (BigAutoField)
|
||||
+ created_at (DateTimeField)
|
||||
+ updated_at (DateTimeField)
|
||||
+ name (CharField)
|
||||
--
|
||||
}
|
||||
|
||||
|
||||
class "registrar.HostIP <Registrar>" as registrar.HostIP #d6f4e9 {
|
||||
host ip
|
||||
--
|
||||
+ id (BigAutoField)
|
||||
+ created_at (DateTimeField)
|
||||
+ updated_at (DateTimeField)
|
||||
+ address (CharField)
|
||||
~ host (ForeignKey)
|
||||
--
|
||||
}
|
||||
registrar.HostIP -- registrar.Host
|
||||
|
||||
|
||||
class "registrar.Host <Registrar>" as registrar.Host #d6f4e9 {
|
||||
host
|
||||
--
|
||||
+ id (BigAutoField)
|
||||
+ created_at (DateTimeField)
|
||||
+ updated_at (DateTimeField)
|
||||
+ name (CharField)
|
||||
~ domain (ForeignKey)
|
||||
--
|
||||
}
|
||||
registrar.Host -- registrar.Domain
|
||||
|
||||
|
||||
class "registrar.UserDomainRole <Registrar>" as registrar.UserDomainRole #d6f4e9 {
|
||||
user domain role
|
||||
--
|
||||
|
@ -208,47 +271,49 @@ class "registrar.DomainInvitation <Registrar>" as registrar.DomainInvitation #d6
|
|||
registrar.DomainInvitation -- registrar.Domain
|
||||
|
||||
|
||||
class "registrar.Nameserver <Registrar>" as registrar.Nameserver #d6f4e9 {
|
||||
nameserver
|
||||
class "registrar.TransitionDomain <Registrar>" as registrar.TransitionDomain #d6f4e9 {
|
||||
transition domain
|
||||
--
|
||||
+ id (BigAutoField)
|
||||
+ created_at (DateTimeField)
|
||||
+ updated_at (DateTimeField)
|
||||
+ name (CharField)
|
||||
~ domain (ForeignKey)
|
||||
~ host_ptr (OneToOneField)
|
||||
+ username (CharField)
|
||||
+ domain_name (CharField)
|
||||
+ status (CharField)
|
||||
+ email_sent (BooleanField)
|
||||
+ processed (BooleanField)
|
||||
+ generic_org_type (CharField)
|
||||
+ organization_name (CharField)
|
||||
+ federal_type (CharField)
|
||||
+ federal_agency (CharField)
|
||||
+ epp_creation_date (DateField)
|
||||
+ epp_expiration_date (DateField)
|
||||
+ first_name (CharField)
|
||||
+ middle_name (CharField)
|
||||
+ last_name (CharField)
|
||||
+ title (CharField)
|
||||
+ email (EmailField)
|
||||
+ phone (CharField)
|
||||
+ address_line (CharField)
|
||||
+ city (CharField)
|
||||
+ state_territory (CharField)
|
||||
+ zipcode (CharField)
|
||||
--
|
||||
}
|
||||
registrar.Nameserver -- registrar.Domain
|
||||
registrar.Nameserver -- registrar.Host
|
||||
|
||||
|
||||
class "registrar.PublicContact <Registrar>" as registrar.PublicContact #d6f4e9 {
|
||||
public contact
|
||||
class "registrar.VerifiedByStaff <Registrar>" as registrar.VerifiedByStaff #d6f4e9 {
|
||||
verified by staff
|
||||
--
|
||||
+ id (BigAutoField)
|
||||
+ created_at (DateTimeField)
|
||||
+ updated_at (DateTimeField)
|
||||
+ contact_type (CharField)
|
||||
+ registry_id (CharField)
|
||||
~ domain (ForeignKey)
|
||||
+ name (TextField)
|
||||
+ org (TextField)
|
||||
+ street1 (TextField)
|
||||
+ street2 (TextField)
|
||||
+ street3 (TextField)
|
||||
+ city (TextField)
|
||||
+ sp (TextField)
|
||||
+ pc (TextField)
|
||||
+ cc (TextField)
|
||||
+ email (TextField)
|
||||
+ voice (TextField)
|
||||
+ fax (TextField)
|
||||
+ pw (TextField)
|
||||
+ email (EmailField)
|
||||
~ requestor (ForeignKey)
|
||||
+ notes (TextField)
|
||||
--
|
||||
}
|
||||
|
||||
registrar.PublicContact -- registrar.Domain
|
||||
registrar.VerifiedByStaff -- registrar.User
|
||||
|
||||
|
||||
class "registrar.User <Registrar>" as registrar.User #d6f4e9 {
|
||||
|
@ -265,7 +330,11 @@ class "registrar.User <Registrar>" as registrar.User #d6f4e9 {
|
|||
+ is_staff (BooleanField)
|
||||
+ is_active (BooleanField)
|
||||
+ date_joined (DateTimeField)
|
||||
+ status (CharField)
|
||||
+ phone (PhoneNumberField)
|
||||
+ middle_name (CharField)
|
||||
+ title (CharField)
|
||||
+ verification_type (CharField)
|
||||
# groups (ManyToManyField)
|
||||
# user_permissions (ManyToManyField)
|
||||
# domains (ManyToManyField)
|
||||
|
@ -274,6 +343,17 @@ class "registrar.User <Registrar>" as registrar.User #d6f4e9 {
|
|||
registrar.User *--* registrar.Domain
|
||||
|
||||
|
||||
class "registrar.UserGroup <Registrar>" as registrar.UserGroup #d6f4e9 {
|
||||
User group
|
||||
--
|
||||
- id (AutoField)
|
||||
+ name (CharField)
|
||||
~ group_ptr (OneToOneField)
|
||||
# permissions (ManyToManyField)
|
||||
--
|
||||
}
|
||||
|
||||
|
||||
class "registrar.Website <Registrar>" as registrar.Website #d6f4e9 {
|
||||
website
|
||||
--
|
||||
|
@ -285,6 +365,29 @@ class "registrar.Website <Registrar>" as registrar.Website #d6f4e9 {
|
|||
}
|
||||
|
||||
|
||||
class "registrar.WaffleFlag <Registrar>" as registrar.WaffleFlag #d6f4e9 {
|
||||
waffle flag
|
||||
--
|
||||
+ id (BigAutoField)
|
||||
+ name (CharField)
|
||||
+ everyone (BooleanField)
|
||||
+ percent (DecimalField)
|
||||
+ testing (BooleanField)
|
||||
+ superusers (BooleanField)
|
||||
+ staff (BooleanField)
|
||||
+ authenticated (BooleanField)
|
||||
+ languages (TextField)
|
||||
+ rollout (BooleanField)
|
||||
+ note (TextField)
|
||||
+ created (DateTimeField)
|
||||
+ modified (DateTimeField)
|
||||
# groups (ManyToManyField)
|
||||
# users (ManyToManyField)
|
||||
--
|
||||
}
|
||||
registrar.WaffleFlag *--* registrar.User
|
||||
|
||||
|
||||
@enduml
|
||||
```
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 97 KiB |
32
ops/manifests/manifest-hotgov.yaml
Normal file
32
ops/manifests/manifest-hotgov.yaml
Normal file
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
applications:
|
||||
- name: getgov-hotgov
|
||||
buildpacks:
|
||||
- python_buildpack
|
||||
path: ../../src
|
||||
instances: 1
|
||||
memory: 512M
|
||||
stack: cflinuxfs4
|
||||
timeout: 180
|
||||
command: ./run.sh
|
||||
health-check-type: http
|
||||
health-check-http-endpoint: /health
|
||||
health-check-invocation-timeout: 40
|
||||
env:
|
||||
# Send stdout and stderr straight to the terminal without buffering
|
||||
PYTHONUNBUFFERED: yup
|
||||
# Tell Django where to find its configuration
|
||||
DJANGO_SETTINGS_MODULE: registrar.config.settings
|
||||
# Tell Django where it is being hosted
|
||||
DJANGO_BASE_URL: https://getgov-hotgov.app.cloud.gov
|
||||
# Tell Django how much stuff to log
|
||||
DJANGO_LOG_LEVEL: INFO
|
||||
# default public site location
|
||||
GETGOV_PUBLIC_SITE_URL: https://get.gov
|
||||
# Flag to disable/enable features in prod environments
|
||||
IS_PRODUCTION: False
|
||||
routes:
|
||||
- route: getgov-hotgov.app.cloud.gov
|
||||
services:
|
||||
- getgov-credentials
|
||||
- getgov-hotgov-database
|
32
ops/manifests/manifest-litterbox.yaml
Normal file
32
ops/manifests/manifest-litterbox.yaml
Normal file
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
applications:
|
||||
- name: getgov-litterbox
|
||||
buildpacks:
|
||||
- python_buildpack
|
||||
path: ../../src
|
||||
instances: 1
|
||||
memory: 512M
|
||||
stack: cflinuxfs4
|
||||
timeout: 180
|
||||
command: ./run.sh
|
||||
health-check-type: http
|
||||
health-check-http-endpoint: /health
|
||||
health-check-invocation-timeout: 40
|
||||
env:
|
||||
# Send stdout and stderr straight to the terminal without buffering
|
||||
PYTHONUNBUFFERED: yup
|
||||
# Tell Django where to find its configuration
|
||||
DJANGO_SETTINGS_MODULE: registrar.config.settings
|
||||
# Tell Django where it is being hosted
|
||||
DJANGO_BASE_URL: https://getgov-litterbox.app.cloud.gov
|
||||
# Tell Django how much stuff to log
|
||||
DJANGO_LOG_LEVEL: INFO
|
||||
# default public site location
|
||||
GETGOV_PUBLIC_SITE_URL: https://get.gov
|
||||
# Flag to disable/enable features in prod environments
|
||||
IS_PRODUCTION: False
|
||||
routes:
|
||||
- route: getgov-litterbox.app.cloud.gov
|
||||
services:
|
||||
- getgov-credentials
|
||||
- getgov-litterbox-database
|
|
@ -15,6 +15,7 @@ from django.contrib.contenttypes.models import ContentType
|
|||
from django.urls import reverse
|
||||
from dateutil.relativedelta import relativedelta # type: ignore
|
||||
from epplibwrapper.errors import ErrorCode, RegistryError
|
||||
from registrar.models.user_domain_role import UserDomainRole
|
||||
from waffle.admin import FlagAdmin
|
||||
from waffle.models import Sample, Switch
|
||||
from registrar.models import Contact, Domain, DomainRequest, DraftDomain, User, Website
|
||||
|
@ -588,6 +589,7 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
|
|||
resource_classes = [UserResource]
|
||||
|
||||
form = MyUserAdminForm
|
||||
change_form_template = "django/admin/user_change_form.html"
|
||||
|
||||
class Meta:
|
||||
"""Contains meta information about this class"""
|
||||
|
@ -627,7 +629,7 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
|
|||
None,
|
||||
{"fields": ("username", "password", "status", "verification_type")},
|
||||
),
|
||||
("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "title", "email", "phone")}),
|
||||
("Personal info", {"fields": ("first_name", "middle_name", "last_name", "title", "email", "phone")}),
|
||||
(
|
||||
"Permissions",
|
||||
{
|
||||
|
@ -706,8 +708,6 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
|
|||
ordering = ["first_name", "last_name", "email"]
|
||||
search_help_text = "Search by first name, last name, or email."
|
||||
|
||||
change_form_template = "django/admin/email_clipboard_change_form.html"
|
||||
|
||||
def get_search_results(self, request, queryset, search_term):
|
||||
"""
|
||||
Override for get_search_results. This affects any upstream model using autocomplete_fields,
|
||||
|
@ -787,6 +787,23 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
|
|||
# users who might not belong to groups
|
||||
return self.analyst_readonly_fields
|
||||
|
||||
def change_view(self, request, object_id, form_url="", extra_context=None):
|
||||
"""Add user's related domains and requests to context"""
|
||||
obj = self.get_object(request, object_id)
|
||||
|
||||
domain_requests = DomainRequest.objects.filter(creator=obj).exclude(
|
||||
Q(status=DomainRequest.DomainRequestStatus.STARTED) | Q(status=DomainRequest.DomainRequestStatus.WITHDRAWN)
|
||||
)
|
||||
sort_by = request.GET.get("sort_by", "requested_domain__name")
|
||||
domain_requests = domain_requests.order_by(sort_by)
|
||||
|
||||
user_domain_roles = UserDomainRole.objects.filter(user=obj)
|
||||
domain_ids = user_domain_roles.values_list("domain_id", flat=True)
|
||||
domains = Domain.objects.filter(id__in=domain_ids).exclude(state=Domain.State.DELETED)
|
||||
|
||||
extra_context = {"domain_requests": domain_requests, "domains": domains}
|
||||
return super().change_view(request, object_id, form_url, extra_context)
|
||||
|
||||
|
||||
class HostIPInline(admin.StackedInline):
|
||||
"""Edit an ip address on the host page."""
|
||||
|
|
|
@ -130,7 +130,7 @@ html[data-theme="light"] {
|
|||
// Sets darker color on delete page links.
|
||||
// Remove when dark mode successfully applies to Django delete page.
|
||||
.delete-confirmation .content a:not(.button) {
|
||||
color: #005288;
|
||||
color: color('primary');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,7 @@ html[data-theme="dark"] {
|
|||
// Sets darker color on delete page links.
|
||||
// Remove when dark mode successfully applies to Django delete page.
|
||||
.delete-confirmation .content a:not(.button) {
|
||||
color: #005288;
|
||||
color: color('primary');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -186,6 +186,14 @@ div#content > h2 {
|
|||
margin: units(2) 0 units(1) 0;
|
||||
}
|
||||
|
||||
.module ul.padding-0 {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.module ul.margin-0 {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.change-list {
|
||||
.usa-table--striped tbody tr:nth-child(odd) td,
|
||||
.usa-table--striped tbody tr:nth-child(odd) th,
|
||||
|
@ -732,7 +740,7 @@ div.dja__model-description{
|
|||
|
||||
a, a:link, a:visited {
|
||||
font-size: medium;
|
||||
color: #005288 !important;
|
||||
color: color('primary') !important;
|
||||
}
|
||||
|
||||
&.dja__model-description--no-overflow {
|
||||
|
@ -761,3 +769,7 @@ div.dja__model-description{
|
|||
.usa-summary-box h3 {
|
||||
color: #{$dhs-blue-60};
|
||||
}
|
||||
|
||||
.module caption, .inline-group h2 {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
|
|
@ -660,6 +660,8 @@ ALLOWED_HOSTS = [
|
|||
"getgov-staging.app.cloud.gov",
|
||||
"getgov-development.app.cloud.gov",
|
||||
"getgov-ag.app.cloud.gov",
|
||||
"getgov-litterbox.app.cloud.gov",
|
||||
"getgov-hotgov.app.cloud.gov",
|
||||
"getgov-cb.app.cloud.gov",
|
||||
"getgov-bob.app.cloud.gov",
|
||||
"getgov-meoward.app.cloud.gov",
|
||||
|
|
|
@ -106,6 +106,12 @@ class UserFixture:
|
|||
"last_name": "Orr",
|
||||
"email": "riley+320@truss.works",
|
||||
},
|
||||
{
|
||||
"username": "76612d84-66b0-4ae9-9870-81e98b9858b6",
|
||||
"first_name": "Anna",
|
||||
"last_name": "Gingle",
|
||||
"email": "annagingle@truss.works",
|
||||
},
|
||||
]
|
||||
|
||||
STAFF = [
|
||||
|
@ -194,6 +200,12 @@ class UserFixture:
|
|||
"last_name": "Orr-Analyst",
|
||||
"email": "riley+321@truss.works",
|
||||
},
|
||||
{
|
||||
"username": "e1e350b1-cfc1-4753-a6cb-3ae6d912f99c",
|
||||
"first_name": "Anna-Analyst",
|
||||
"last_name": "Gingle-Analyst",
|
||||
"email": "annagingle+analyst@truss.works",
|
||||
},
|
||||
]
|
||||
|
||||
def load_users(cls, users, group_name, are_superusers=False):
|
||||
|
|
36
src/registrar/templates/django/admin/user_change_form.html
Normal file
36
src/registrar/templates/django/admin/user_change_form.html
Normal file
|
@ -0,0 +1,36 @@
|
|||
{% extends 'django/admin/email_clipboard_change_form.html' %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block after_related_objects %}
|
||||
<div class="module aligned padding-3">
|
||||
<h2>Associated requests and domains</h2>
|
||||
<div class="grid-row grid-gap mobile:padding-x-1 desktop:padding-x-4">
|
||||
<div class="mobile:grid-col-12 tablet:grid-col-6 desktop:grid-col-4">
|
||||
<h3>Domain requests</h3>
|
||||
<ul class="margin-0 padding-0">
|
||||
{% for domain_request in domain_requests %}
|
||||
<li>
|
||||
<a href="{% url 'admin:registrar_domainrequest_change' domain_request.pk %}">
|
||||
{{ domain_request.requested_domain }}
|
||||
</a>
|
||||
({{ domain_request.status }})
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="mobile:grid-col-12 tablet:grid-col-6 desktop:grid-col-4">
|
||||
<h3>Domains</h3>
|
||||
<ul class="margin-0 padding-0">
|
||||
{% for domain in domains %}
|
||||
<li>
|
||||
<a href="{% url 'admin:registrar_domain_change' domain.pk %}">
|
||||
{{ domain.name }}
|
||||
</a>
|
||||
({{ domain.state }})
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -667,7 +667,7 @@ class MockDb(TestCase):
|
|||
is_election_board=False,
|
||||
)
|
||||
|
||||
meoward_user = get_user_model().objects.create(
|
||||
self.meoward_user = get_user_model().objects.create(
|
||||
username="meoward_username", first_name="first_meoward", last_name="last_meoward", email="meoward@rocks.com"
|
||||
)
|
||||
|
||||
|
@ -676,7 +676,7 @@ class MockDb(TestCase):
|
|||
)
|
||||
|
||||
_, created = UserDomainRole.objects.get_or_create(
|
||||
user=meoward_user, domain=self.domain_1, role=UserDomainRole.Roles.MANAGER
|
||||
user=self.meoward_user, domain=self.domain_1, role=UserDomainRole.Roles.MANAGER
|
||||
)
|
||||
|
||||
_, created = UserDomainRole.objects.get_or_create(
|
||||
|
@ -688,19 +688,21 @@ class MockDb(TestCase):
|
|||
)
|
||||
|
||||
_, created = UserDomainRole.objects.get_or_create(
|
||||
user=meoward_user, domain=self.domain_2, role=UserDomainRole.Roles.MANAGER
|
||||
user=self.meoward_user, domain=self.domain_2, role=UserDomainRole.Roles.MANAGER
|
||||
)
|
||||
|
||||
_, created = UserDomainRole.objects.get_or_create(
|
||||
user=meoward_user, domain=self.domain_11, role=UserDomainRole.Roles.MANAGER
|
||||
user=self.meoward_user, domain=self.domain_11, role=UserDomainRole.Roles.MANAGER
|
||||
)
|
||||
|
||||
_, created = UserDomainRole.objects.get_or_create(
|
||||
user=meoward_user, domain=self.domain_12, role=UserDomainRole.Roles.MANAGER
|
||||
user=self.meoward_user, domain=self.domain_12, role=UserDomainRole.Roles.MANAGER
|
||||
)
|
||||
|
||||
_, created = DomainInvitation.objects.get_or_create(
|
||||
email=meoward_user.email, domain=self.domain_1, status=DomainInvitation.DomainInvitationStatus.RETRIEVED
|
||||
email=self.meoward_user.email,
|
||||
domain=self.domain_1,
|
||||
status=DomainInvitation.DomainInvitationStatus.RETRIEVED,
|
||||
)
|
||||
|
||||
_, created = DomainInvitation.objects.get_or_create(
|
||||
|
|
|
@ -47,6 +47,7 @@ from registrar.models import (
|
|||
from registrar.models.user_domain_role import UserDomainRole
|
||||
from registrar.models.verified_by_staff import VerifiedByStaff
|
||||
from .common import (
|
||||
MockDb,
|
||||
MockSESClient,
|
||||
AuditedAdminMockData,
|
||||
completed_domain_request,
|
||||
|
@ -3438,16 +3439,19 @@ class TestListHeaderAdmin(TestCase):
|
|||
User.objects.all().delete()
|
||||
|
||||
|
||||
class TestMyUserAdmin(TestCase):
|
||||
class TestMyUserAdmin(MockDb):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
admin_site = AdminSite()
|
||||
self.admin = MyUserAdmin(model=get_user_model(), admin_site=admin_site)
|
||||
self.client = Client(HTTP_HOST="localhost:8080")
|
||||
self.superuser = create_superuser()
|
||||
self.staffuser = create_user()
|
||||
self.test_helper = GenericTestHelper(admin=self.admin)
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
DomainRequest.objects.all().delete()
|
||||
User.objects.all().delete()
|
||||
|
||||
@less_console_noise_decorator
|
||||
|
@ -3472,7 +3476,7 @@ class TestMyUserAdmin(TestCase):
|
|||
"""
|
||||
Tests for the correct helper text on this page
|
||||
"""
|
||||
user = create_user()
|
||||
user = self.staffuser
|
||||
|
||||
p = "adminpass"
|
||||
self.client.login(username="superuser", password=p)
|
||||
|
@ -3493,10 +3497,11 @@ class TestMyUserAdmin(TestCase):
|
|||
]
|
||||
self.test_helper.assert_response_contains_distinct_values(response, expected_values)
|
||||
|
||||
@less_console_noise_decorator
|
||||
def test_list_display_without_username(self):
|
||||
with less_console_noise():
|
||||
request = self.client.request().wsgi_request
|
||||
request.user = create_user()
|
||||
request.user = self.staffuser
|
||||
|
||||
list_display = self.admin.get_list_display(request)
|
||||
expected_list_display = [
|
||||
|
@ -3522,7 +3527,7 @@ class TestMyUserAdmin(TestCase):
|
|||
def test_get_fieldsets_cisa_analyst(self):
|
||||
with less_console_noise():
|
||||
request = self.client.request().wsgi_request
|
||||
request.user = create_user()
|
||||
request.user = self.staffuser
|
||||
fieldsets = self.admin.get_fieldsets(request)
|
||||
expected_fieldsets = (
|
||||
(
|
||||
|
@ -3540,6 +3545,97 @@ class TestMyUserAdmin(TestCase):
|
|||
)
|
||||
self.assertEqual(fieldsets, expected_fieldsets)
|
||||
|
||||
def test_analyst_can_see_related_domains_and_requests_in_user_form(self):
|
||||
"""Tests if an analyst can see the related domains and domain requests for a user in that user's form"""
|
||||
|
||||
# From MockDb, we have self.meoward_user which we'll use as creator
|
||||
# Create fake domain requests
|
||||
domain_request_started = completed_domain_request(
|
||||
status=DomainRequest.DomainRequestStatus.STARTED, user=self.meoward_user, name="started.gov"
|
||||
)
|
||||
domain_request_submitted = completed_domain_request(
|
||||
status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.meoward_user, name="submitted.gov"
|
||||
)
|
||||
domain_request_in_review = completed_domain_request(
|
||||
status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=self.meoward_user, name="in-review.gov"
|
||||
)
|
||||
domain_request_withdrawn = completed_domain_request(
|
||||
status=DomainRequest.DomainRequestStatus.WITHDRAWN, user=self.meoward_user, name="withdrawn.gov"
|
||||
)
|
||||
domain_request_approved = completed_domain_request(
|
||||
status=DomainRequest.DomainRequestStatus.APPROVED, user=self.meoward_user, name="approved.gov"
|
||||
)
|
||||
domain_request_rejected = completed_domain_request(
|
||||
status=DomainRequest.DomainRequestStatus.REJECTED, user=self.meoward_user, name="rejected.gov"
|
||||
)
|
||||
domain_request_ineligible = completed_domain_request(
|
||||
status=DomainRequest.DomainRequestStatus.INELIGIBLE, user=self.meoward_user, name="ineligible.gov"
|
||||
)
|
||||
|
||||
# From MockDb, we have sel.meoward_user who's admin on
|
||||
# self.domain_1 - READY
|
||||
# self.domain_2 - DNS_NEEDED
|
||||
# self.domain_11 - READY
|
||||
# self.domain_12 - READY
|
||||
# DELETED:
|
||||
domain_deleted, _ = Domain.objects.get_or_create(
|
||||
name="domain_deleted.gov", state=Domain.State.DELETED, deleted=timezone.make_aware(datetime(2024, 4, 2))
|
||||
)
|
||||
_, created = UserDomainRole.objects.get_or_create(
|
||||
user=self.meoward_user, domain=domain_deleted, role=UserDomainRole.Roles.MANAGER
|
||||
)
|
||||
|
||||
p = "userpass"
|
||||
self.client.login(username="staffuser", password=p)
|
||||
response = self.client.get(
|
||||
"/admin/registrar/user/{}/change/".format(self.meoward_user.id),
|
||||
follow=True,
|
||||
)
|
||||
|
||||
# Make sure the page loaded and contains the expected domain request names and links to the domain requests
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
self.assertContains(response, domain_request_submitted.requested_domain.name)
|
||||
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_submitted.pk])
|
||||
self.assertContains(response, expected_href)
|
||||
|
||||
self.assertContains(response, domain_request_in_review.requested_domain.name)
|
||||
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_in_review.pk])
|
||||
self.assertContains(response, expected_href)
|
||||
|
||||
self.assertContains(response, domain_request_approved.requested_domain.name)
|
||||
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_approved.pk])
|
||||
self.assertContains(response, expected_href)
|
||||
|
||||
self.assertContains(response, domain_request_rejected.requested_domain.name)
|
||||
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_rejected.pk])
|
||||
self.assertContains(response, expected_href)
|
||||
|
||||
self.assertContains(response, domain_request_ineligible.requested_domain.name)
|
||||
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_ineligible.pk])
|
||||
self.assertContains(response, expected_href)
|
||||
|
||||
# We filter out those requests
|
||||
# STARTED
|
||||
self.assertNotContains(response, domain_request_started.requested_domain.name)
|
||||
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_started.pk])
|
||||
self.assertNotContains(response, expected_href)
|
||||
|
||||
# WITHDRAWN
|
||||
self.assertNotContains(response, domain_request_withdrawn.requested_domain.name)
|
||||
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_withdrawn.pk])
|
||||
self.assertNotContains(response, expected_href)
|
||||
|
||||
# Make sure the page contains the expected domain names and links to the domains
|
||||
self.assertContains(response, self.domain_1.name)
|
||||
expected_href = reverse("admin:registrar_domain_change", args=[self.domain_1.pk])
|
||||
self.assertContains(response, expected_href)
|
||||
|
||||
# We filter out DELETED
|
||||
self.assertNotContains(response, domain_deleted.name)
|
||||
expected_href = reverse("admin:registrar_domain_change", args=[domain_deleted.pk])
|
||||
self.assertNotContains(response, expected_href)
|
||||
|
||||
|
||||
class AuditedAdminTest(TestCase):
|
||||
def setUp(self):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue