From fee659d1c400d645bacf95c4038a823d63690642 Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Fri, 12 May 2023 14:34:43 -0500 Subject: [PATCH 01/71] Add model documentation diagrams --- docs/architecture/diagrams/model_timeline.md | 133 ++++++++++ docs/architecture/diagrams/models_diagram.md | 250 +++++++++++++++++++ src/Pipfile | 1 + src/Pipfile.lock | 171 +++++++------ src/registrar/config/settings.py | 2 + 5 files changed, 475 insertions(+), 82 deletions(-) create mode 100644 docs/architecture/diagrams/model_timeline.md create mode 100644 docs/architecture/diagrams/models_diagram.md diff --git a/docs/architecture/diagrams/model_timeline.md b/docs/architecture/diagrams/model_timeline.md new file mode 100644 index 000000000..30363e4d4 --- /dev/null +++ b/docs/architecture/diagrams/model_timeline.md @@ -0,0 +1,133 @@ +# Data Model Timeline + +This diagram connects the data models along with various workflow stages. + +1. The applicant starts the process at `/register` interacting with the + `DomainApplication` object. + +2. The analyst approves the application using the `DomainApplication`'s + `approve()` method which creates many related objects: `UserDomainRole`, + `Domain`, and `DomainInformation`. + +3. After the domain is approved, users interact with various + `/domain//...` views which make changes to the `Domain`, + `DomainInformation`, and `UserDomainRole` models. For inviting new users, + there is a `DomainInvitation` model that allows people to be added to + domains who are not already users. + +A more complete diagram of the data models, their fields, and their +relationships are in [models_diagram.md](./models_diagram.md), created with +the `django-model2puml` plugin. + +@startuml + +allowmixing +left to right direction + +class DomainApplication { + Application for a domain + -- + creator (User) + investigator (User) + authorizing_official (Contact) + submitter (Contact) + other_contacts (Contacts) + requested_domain (Domain) + current_websites (Websites) + alternative_domains (Websites) + -- + Request information... +} + +class User { + Django's user class + -- + ... + -- +} +note left of User + Created by DjangoOIDC + when users arrive back + from Login.gov + + username is the Login UUID +end note + +DomainApplication -l- User : creator, investigator + +class Contact { + Contact info for a person + -- + first_name + middle_name + last_name + title + email + phone + -- +} + +DomainApplication *-r-* Contact : authorizing_official, submitter, other_contacts + +class Domain { + Approved domain + -- + name + is_active + -- + EPP methods +} + +DomainApplication .right[#blue].> Domain : approve() + +class DomainInformation { + Registrar information on a domain + -- + domain (Domain) + domain_application (DomainApplication) + security_email + -- + Request information... +} + +DomainInformation -- Domain +DomainInformation -- DomainApplication +DomainApplication .[#blue].> DomainInformation : approve() + +class UserDomainRole { + Permissions + -- + domain (Domain) + user (User) + role="ADMIN" + -- +} +UserDomainRole -- User +UserDomainRole -- Domain +DomainApplication .[#blue].> UserDomainRole : approve() + +class DomainInvitation { + Email invitations sent + -- + email + domain (Domain) + status + -- +} +DomainInvitation -- Domain +DomainInvitation .[#green].> UserDomainRole : User.first_login() + +actor applicant #Red +applicant -d-> DomainApplication : **/register** + +actor analyst #Blue +analyst -[#blue]-> DomainApplication : **approve()** + +actor user1 #Green +user1 -[#green]-> Domain : **/domain//nameservers** +actor user2 #Green +user2 -[#green]-> DomainInformation : **/domain//?????** +actor user3 #Green +user3 -right[#green]-> UserDomainRole : **/domain//users/add** +user3 -right[#green]-> DomainInvitation : **/domain//users/add** +@enduml diff --git a/docs/architecture/diagrams/models_diagram.md b/docs/architecture/diagrams/models_diagram.md new file mode 100644 index 000000000..f10c6ad87 --- /dev/null +++ b/docs/architecture/diagrams/models_diagram.md @@ -0,0 +1,250 @@ +@startuml +class "registrar.Contact " as registrar.Contact #d6f4e9 { + contact + -- + + id (BigAutoField) + + created_at (DateTimeField) + + updated_at (DateTimeField) + ~ user (OneToOneField) + + first_name (TextField) + + middle_name (TextField) + + last_name (TextField) + + title (TextField) + + email (TextField) + + phone (PhoneNumberField) + -- +} +registrar.Contact -- registrar.User + + +class "registrar.DomainApplication " as registrar.DomainApplication #d6f4e9 { + domain application + -- + + id (BigAutoField) + + created_at (DateTimeField) + + updated_at (DateTimeField) + + status (FSMField) + ~ creator (ForeignKey) + ~ investigator (ForeignKey) + + organization_type (CharField) + + federally_recognized_tribe (BooleanField) + + state_recognized_tribe (BooleanField) + + tribe_name (TextField) + + federal_agency (TextField) + + federal_type (CharField) + + is_election_board (BooleanField) + + organization_name (TextField) + + address_line1 (TextField) + + address_line2 (CharField) + + city (TextField) + + state_territory (CharField) + + zipcode (CharField) + + urbanization (TextField) + + type_of_work (TextField) + + more_organization_information (TextField) + ~ authorizing_official (ForeignKey) + ~ requested_domain (OneToOneField) + ~ submitter (ForeignKey) + + purpose (TextField) + + no_other_contacts_rationale (TextField) + + anything_else (TextField) + + is_policy_acknowledged (BooleanField) + # current_websites (ManyToManyField) + # alternative_domains (ManyToManyField) + # other_contacts (ManyToManyField) + -- +} +registrar.DomainApplication -- registrar.User +registrar.DomainApplication -- registrar.User +registrar.DomainApplication -- registrar.Contact +registrar.DomainApplication -- registrar.Domain +registrar.DomainApplication -- registrar.Contact +registrar.DomainApplication *--* registrar.Website +registrar.DomainApplication *--* registrar.Website +registrar.DomainApplication *--* registrar.Contact + + +class "registrar.DomainInformation " as registrar.DomainInformation #d6f4e9 { + domain information + -- + + id (BigAutoField) + + created_at (DateTimeField) + + updated_at (DateTimeField) + ~ creator (ForeignKey) + ~ domain_application (OneToOneField) + + organization_type (CharField) + + federally_recognized_tribe (BooleanField) + + state_recognized_tribe (BooleanField) + + tribe_name (TextField) + + federal_agency (TextField) + + federal_type (CharField) + + is_election_board (BooleanField) + + organization_name (TextField) + + address_line1 (TextField) + + address_line2 (CharField) + + city (TextField) + + state_territory (CharField) + + zipcode (CharField) + + urbanization (TextField) + + type_of_work (TextField) + + more_organization_information (TextField) + ~ authorizing_official (ForeignKey) + ~ domain (OneToOneField) + ~ submitter (ForeignKey) + + purpose (TextField) + + no_other_contacts_rationale (TextField) + + anything_else (TextField) + + is_policy_acknowledged (BooleanField) + + security_email (EmailField) + # other_contacts (ManyToManyField) + -- +} +registrar.DomainInformation -- registrar.User +registrar.DomainInformation -- registrar.DomainApplication +registrar.DomainInformation -- registrar.Contact +registrar.DomainInformation -- registrar.Domain +registrar.DomainInformation -- registrar.Contact +registrar.DomainInformation *--* registrar.Contact + + +class "registrar.Domain " as registrar.Domain #d6f4e9 { + domain + -- + + id (BigAutoField) + + created_at (DateTimeField) + + updated_at (DateTimeField) + + name (CharField) + + is_active (FSMField) + -- +} + + +class "registrar.HostIP " 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 " as registrar.Host #d6f4e9 { + host + -- + + id (BigAutoField) + + created_at (DateTimeField) + + updated_at (DateTimeField) + + name (CharField) + ~ domain (ForeignKey) + -- +} +registrar.Host -- registrar.Domain + + +class "registrar.UserDomainRole " as registrar.UserDomainRole #d6f4e9 { + user domain role + -- + + id (BigAutoField) + + created_at (DateTimeField) + + updated_at (DateTimeField) + ~ user (ForeignKey) + ~ domain (ForeignKey) + + role (TextField) + -- +} +registrar.UserDomainRole -- registrar.User +registrar.UserDomainRole -- registrar.Domain + + +class "registrar.DomainInvitation " as registrar.DomainInvitation #d6f4e9 { + domain invitation + -- + + id (BigAutoField) + + created_at (DateTimeField) + + updated_at (DateTimeField) + + email (EmailField) + ~ domain (ForeignKey) + + status (FSMField) + -- +} +registrar.DomainInvitation -- registrar.Domain + + +class "registrar.Nameserver " as registrar.Nameserver #d6f4e9 { + nameserver + -- + + id (BigAutoField) + + created_at (DateTimeField) + + updated_at (DateTimeField) + + name (CharField) + ~ domain (ForeignKey) + ~ host_ptr (OneToOneField) + -- +} +registrar.Nameserver -- registrar.Domain +registrar.Nameserver -- registrar.Host + + +class "registrar.PublicContact " as registrar.PublicContact #d6f4e9 { + public contact + -- + + id (BigAutoField) + + created_at (DateTimeField) + + updated_at (DateTimeField) + + contact_type (CharField) + + 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) + -- +} + + +class "registrar.User " as registrar.User #d6f4e9 { + user + -- + + id (BigAutoField) + + password (CharField) + + last_login (DateTimeField) + + is_superuser (BooleanField) + + username (CharField) + + first_name (CharField) + + last_name (CharField) + + email (EmailField) + + is_staff (BooleanField) + + is_active (BooleanField) + + date_joined (DateTimeField) + + phone (PhoneNumberField) + # groups (ManyToManyField) + # user_permissions (ManyToManyField) + # domains (ManyToManyField) + -- +} +registrar.User *--* registrar.Domain + + +class "registrar.Website " as registrar.Website #d6f4e9 { + website + -- + + id (BigAutoField) + + created_at (DateTimeField) + + updated_at (DateTimeField) + + website (CharField) + -- +} + + +@enduml diff --git a/src/Pipfile b/src/Pipfile index d7551f63b..a3a20051c 100644 --- a/src/Pipfile +++ b/src/Pipfile @@ -37,3 +37,4 @@ django-webtest = "*" types-cachetools = "*" boto3-mocking = "*" boto3-stubs = "*" +django-model2puml = "*" diff --git a/src/Pipfile.lock b/src/Pipfile.lock index e77116ef5..04722b876 100644 --- a/src/Pipfile.lock +++ b/src/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "ebec8b958bcfde525ad74aa1e777b55855e86b2d63264612bc2855bf167070b1" + "sha256": "b6c1a957da6c715c734906059a81da21cb0eb4c4ab04f204eb58a48ddb8f7234" }, "pipfile-spec": 6, "requires": {}, @@ -24,19 +24,19 @@ }, "boto3": { "hashes": [ - "sha256:38ca632be379963f2a2749b5f63a81fe1679913b954914f470ad282c77674bbc", - "sha256:4d575c180312bec6108852bae12e6396b9d1bb404154d652c57ee849c62fbb83" + "sha256:62285ecee7629a4388d55ae369536f759622d68d5b9a0ced7c58a0c1a409c0f7", + "sha256:8ff0af0b25266a01616396abc19eb34dc3d44bd867fa4158985924128b9034fb" ], "index": "pypi", - "version": "==1.26.122" + "version": "==1.26.133" }, "botocore": { "hashes": [ - "sha256:9e4984a9e9777c6b949aa1e98323fa35480d9f99d447af7e179ae611f7ed5af9", - "sha256:c3b41078d235761b9c5dc22f534a76952622ef96787b96bbd10242ec4d73f2a5" + "sha256:7b38e540f73c921d8cb0ac72794072000af9e10758c04ba7f53d5629cc52fa87", + "sha256:b266185d7414a559952569005009a400de50af91fd3da44f05cf05b00946c4a7" ], "markers": "python_version >= '3.7'", - "version": "==1.29.122" + "version": "==1.29.133" }, "cachetools": { "hashes": [ @@ -48,11 +48,11 @@ }, "certifi": { "hashes": [ - "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3", - "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18" + "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", + "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" ], "markers": "python_version >= '3.6'", - "version": "==2022.12.7" + "version": "==2023.5.7" }, "cfenv": { "hashes": [ @@ -261,11 +261,11 @@ }, "django": { "hashes": [ - "sha256:ad33ed68db9398f5dfb33282704925bce044bef4261cd4fb59e4e7f9ae505a78", - "sha256:c36e2ab12824e2ac36afa8b2515a70c53c7742f0d6eaefa7311ec379558db997" + "sha256:066b6debb5ac335458d2a713ed995570536c8b59a580005acb0732378d5eb1ee", + "sha256:7efa6b1f781a6119a10ac94b4794ded90db8accbe7802281cd26f8664ffed59c" ], "index": "pypi", - "version": "==4.2" + "version": "==4.2.1" }, "django-allow-cidr": { "hashes": [ @@ -338,11 +338,11 @@ }, "faker": { "hashes": [ - "sha256:49060d40e6659e116f53353c5771ad2f2cbcd12b15771f49e3000a3a451f13ec", - "sha256:ac903ba8cb5adbce2cdd15e5536118d484bbe01126f3c774dd9f6df77b61232d" + "sha256:38dbc3b80e655d7301e190426ab30f04b6b7f6ca4764c5dd02772ffde0fa6dcd", + "sha256:f02c6d3fdb5bc781f80b440cf2bdec336ed47ecfb8d620b20c3d4188ed051831" ], "index": "pypi", - "version": "==18.6.0" + "version": "==18.7.0" }, "furl": { "hashes": [ @@ -623,19 +623,19 @@ }, "requests": { "hashes": [ - "sha256:e8f3c9be120d3333921d213eef078af392fba3933ab7ed2d1cba3b56f2568c3b", - "sha256:f2e34a75f4749019bb0e3effb66683630e4ffeaf75819fb51bebef1bf5aef059" + "sha256:10e94cc4f3121ee6da529d358cdaeaff2f1c409cd377dbc72b825852f2f7e294", + "sha256:239d7d4458afcb28a692cdd298d87542235f4ca8d36d03a15bfc128a6559a2f4" ], "index": "pypi", - "version": "==2.29.0" + "version": "==2.30.0" }, "s3transfer": { "hashes": [ - "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd", - "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947" + "sha256:3c0da2d074bf35d6870ef157158641178a4204a6e689e82546083e31e0311346", + "sha256:640bb492711f4c0c0905e1f62b6aaeb771881935ad27884852411f8e9cacbca9" ], "markers": "python_version >= '3.7'", - "version": "==0.6.0" + "version": "==0.6.1" }, "setuptools": { "hashes": [ @@ -752,11 +752,11 @@ }, "boto3": { "hashes": [ - "sha256:38ca632be379963f2a2749b5f63a81fe1679913b954914f470ad282c77674bbc", - "sha256:4d575c180312bec6108852bae12e6396b9d1bb404154d652c57ee849c62fbb83" + "sha256:62285ecee7629a4388d55ae369536f759622d68d5b9a0ced7c58a0c1a409c0f7", + "sha256:8ff0af0b25266a01616396abc19eb34dc3d44bd867fa4158985924128b9034fb" ], "index": "pypi", - "version": "==1.26.122" + "version": "==1.26.133" }, "boto3-mocking": { "hashes": [ @@ -768,27 +768,27 @@ }, "boto3-stubs": { "hashes": [ - "sha256:401e7fe51d88a51b527d883d195ed20c7f57aeb2c0aea24bbb3e911b6d2ad3aa", - "sha256:743a37bfd7d1eed4d67cdf825283abc1d93b7900b81d7426aab7e691e075c897" + "sha256:a921814574761842073822dc5e9fc7ca4f1c5fdeaa53d83cd8831e060dae09c8", + "sha256:cc6a662700e755c1e3dec2383c146b89cd8c70b5921033504bfb8367d03a538f" ], "index": "pypi", - "version": "==1.26.122" + "version": "==1.26.133" }, "botocore": { "hashes": [ - "sha256:9e4984a9e9777c6b949aa1e98323fa35480d9f99d447af7e179ae611f7ed5af9", - "sha256:c3b41078d235761b9c5dc22f534a76952622ef96787b96bbd10242ec4d73f2a5" + "sha256:7b38e540f73c921d8cb0ac72794072000af9e10758c04ba7f53d5629cc52fa87", + "sha256:b266185d7414a559952569005009a400de50af91fd3da44f05cf05b00946c4a7" ], "markers": "python_version >= '3.7'", - "version": "==1.29.122" + "version": "==1.29.133" }, "botocore-stubs": { "hashes": [ - "sha256:59873a3b535ec3ff0b6bf5f41c9f8a0f8c48032a871bea4d6e4faebbbfc68e8b", - "sha256:e6e6c527a6cac0ec69dd1b755d530c9b2dab01d423ce47bdc636dd01ebb01b1b" + "sha256:5f6f1967d23c45834858a055cbf65b66863f9f28d05f32f57bf52864a13512d9", + "sha256:622c4a5cd740498439008d81c5ded612146f4f0d575341c12591f978edbbe733" ], "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==1.29.122" + "version": "==1.29.130" }, "click": { "hashes": [ @@ -800,11 +800,11 @@ }, "django": { "hashes": [ - "sha256:ad33ed68db9398f5dfb33282704925bce044bef4261cd4fb59e4e7f9ae505a78", - "sha256:c36e2ab12824e2ac36afa8b2515a70c53c7742f0d6eaefa7311ec379558db997" + "sha256:066b6debb5ac335458d2a713ed995570536c8b59a580005acb0732378d5eb1ee", + "sha256:7efa6b1f781a6119a10ac94b4794ded90db8accbe7802281cd26f8664ffed59c" ], "index": "pypi", - "version": "==4.2" + "version": "==4.2.1" }, "django-debug-toolbar": { "hashes": [ @@ -814,6 +814,13 @@ "index": "pypi", "version": "==4.0.0" }, + "django-model2puml": { + "hashes": [ + "sha256:6e773d742e556020a04d3216ce5dee5d3551da162e2d42a997f85b4ed1854771" + ], + "index": "pypi", + "version": "==0.4.1" + }, "django-stubs": { "hashes": [ "sha256:93baff824f0a056e71036b423b942a74f07b909e45e3fa38185b910f597c5c08", @@ -896,35 +903,35 @@ }, "mypy": { "hashes": [ - "sha256:023fe9e618182ca6317ae89833ba422c411469156b690fde6a315ad10695a521", - "sha256:031fc69c9a7e12bcc5660b74122ed84b3f1c505e762cc4296884096c6d8ee140", - "sha256:2de7babe398cb7a85ac7f1fd5c42f396c215ab3eff731b4d761d68d0f6a80f48", - "sha256:2e93a8a553e0394b26c4ca683923b85a69f7ccdc0139e6acd1354cc884fe0128", - "sha256:390bc685ec209ada4e9d35068ac6988c60160b2b703072d2850457b62499e336", - "sha256:3a2d219775a120581a0ae8ca392b31f238d452729adbcb6892fa89688cb8306a", - "sha256:3efde4af6f2d3ccf58ae825495dbb8d74abd6d176ee686ce2ab19bd025273f41", - "sha256:4a99fe1768925e4a139aace8f3fb66db3576ee1c30b9c0f70f744ead7e329c9f", - "sha256:4b41412df69ec06ab141808d12e0bf2823717b1c363bd77b4c0820feaa37249e", - "sha256:4c8d8c6b80aa4a1689f2a179d31d86ae1367ea4a12855cc13aa3ba24bb36b2d8", - "sha256:4d19f1a239d59f10fdc31263d48b7937c585810288376671eaf75380b074f238", - "sha256:4e4a682b3f2489d218751981639cffc4e281d548f9d517addfd5a2917ac78119", - "sha256:695c45cea7e8abb6f088a34a6034b1d273122e5530aeebb9c09626cea6dca4cb", - "sha256:701189408b460a2ff42b984e6bd45c3f41f0ac9f5f58b8873bbedc511900086d", - "sha256:70894c5345bea98321a2fe84df35f43ee7bb0feec117a71420c60459fc3e1eed", - "sha256:8293a216e902ac12779eb7a08f2bc39ec6c878d7c6025aa59464e0c4c16f7eb9", - "sha256:8d26b513225ffd3eacece727f4387bdce6469192ef029ca9dd469940158bc89e", - "sha256:a197ad3a774f8e74f21e428f0de7f60ad26a8d23437b69638aac2764d1e06a6a", - "sha256:bea55fc25b96c53affab852ad94bf111a3083bc1d8b0c76a61dd101d8a388cf5", - "sha256:c9a084bce1061e55cdc0493a2ad890375af359c766b8ac311ac8120d3a472950", - "sha256:d0e9464a0af6715852267bf29c9553e4555b61f5904a4fc538547a4d67617937", - "sha256:d8e9187bfcd5ffedbe87403195e1fc340189a68463903c39e2b63307c9fa0394", - "sha256:eaeaa0888b7f3ccb7bcd40b50497ca30923dba14f385bde4af78fac713d6d6f6", - "sha256:f46af8d162f3d470d8ffc997aaf7a269996d205f9d746124a179d3abe05ac602", - "sha256:f70a40410d774ae23fcb4afbbeca652905a04de7948eaf0b1789c8d1426b72d1", - "sha256:fe91be1c51c90e2afe6827601ca14353bbf3953f343c2129fa1e247d55fd95ba" + "sha256:1c4c42c60a8103ead4c1c060ac3cdd3ff01e18fddce6f1016e08939647a0e703", + "sha256:44797d031a41516fcf5cbfa652265bb994e53e51994c1bd649ffcd0c3a7eccbf", + "sha256:473117e310febe632ddf10e745a355714e771ffe534f06db40702775056614c4", + "sha256:4c99c3ecf223cf2952638da9cd82793d8f3c0c5fa8b6ae2b2d9ed1e1ff51ba85", + "sha256:550a8b3a19bb6589679a7c3c31f64312e7ff482a816c96e0cecec9ad3a7564dd", + "sha256:658fe7b674769a0770d4b26cb4d6f005e88a442fe82446f020be8e5f5efb2fae", + "sha256:6e33bb8b2613614a33dff70565f4c803f889ebd2f859466e42b46e1df76018dd", + "sha256:6e42d29e324cdda61daaec2336c42512e59c7c375340bd202efa1fe0f7b8f8ca", + "sha256:74bc9b6e0e79808bf8678d7678b2ae3736ea72d56eede3820bd3849823e7f305", + "sha256:76ec771e2342f1b558c36d49900dfe81d140361dd0d2df6cd71b3db1be155409", + "sha256:7d23370d2a6b7a71dc65d1266f9a34e4cde9e8e21511322415db4b26f46f6b8c", + "sha256:87df44954c31d86df96c8bd6e80dfcd773473e877ac6176a8e29898bfb3501cb", + "sha256:8c5979d0deb27e0f4479bee18ea0f83732a893e81b78e62e2dda3e7e518c92ee", + "sha256:95d8d31a7713510685b05fbb18d6ac287a56c8f6554d88c19e73f724a445448a", + "sha256:a22435632710a4fcf8acf86cbd0d69f68ac389a3892cb23fbad176d1cddaf228", + "sha256:a8763e72d5d9574d45ce5881962bc8e9046bf7b375b0abf031f3e6811732a897", + "sha256:c1eb485cea53f4f5284e5baf92902cd0088b24984f4209e25981cc359d64448d", + "sha256:c5d2cc54175bab47011b09688b418db71403aefad07cbcd62d44010543fc143f", + "sha256:cbc07246253b9e3d7d74c9ff948cd0fd7a71afcc2b77c7f0a59c26e9395cb152", + "sha256:d0b6c62206e04061e27009481cb0ec966f7d6172b5b936f3ead3d74f29fe3dcf", + "sha256:ddae0f39ca146972ff6bb4399f3b2943884a774b8771ea0a8f50e971f5ea5ba8", + "sha256:e1f4d16e296f5135624b34e8fb741eb0eadedca90862405b1f1fde2040b9bd11", + "sha256:e86c2c6852f62f8f2b24cb7a613ebe8e0c7dc1402c61d36a609174f63e0ff017", + "sha256:ebc95f8386314272bbc817026f8ce8f4f0d2ef7ae44f947c4664efac9adec929", + "sha256:f9dca1e257d4cc129517779226753dbefb4f2266c4eaad610fc15c6a7e14283e", + "sha256:faff86aa10c1aa4a10e1a301de160f3d8fc8703b88c7e98de46b531ff1276a9a" ], "index": "pypi", - "version": "==1.2.0" + "version": "==1.3.0" }, "mypy-extensions": { "hashes": [ @@ -968,11 +975,11 @@ }, "platformdirs": { "hashes": [ - "sha256:47692bc24c1958e8b0f13dd727307cff1db103fca36399f457da8e05f222fdc4", - "sha256:7954a68d0ba23558d753f73437c55f89027cf8f5108c19844d4b82e5af396335" + "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f", + "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5" ], "markers": "python_version >= '3.7'", - "version": "==3.5.0" + "version": "==3.5.1" }, "pycodestyle": { "hashes": [ @@ -1062,11 +1069,11 @@ }, "s3transfer": { "hashes": [ - "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd", - "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947" + "sha256:3c0da2d074bf35d6870ef157158641178a4204a6e689e82546083e31e0311346", + "sha256:640bb492711f4c0c0905e1f62b6aaeb771881935ad27884852411f8e9cacbca9" ], "markers": "python_version >= '3.7'", - "version": "==0.6.0" + "version": "==0.6.1" }, "six": { "hashes": [ @@ -1118,11 +1125,11 @@ }, "types-awscrt": { "hashes": [ - "sha256:40854d9d7ce055620d5d41e5adc84df11b879aedbd2cf20de84e73f084aa5797", - "sha256:fe38c6fd71199a9f739b69a7c2f3a574585457c4f63730a62830628a7bffc5b0" + "sha256:9e447df3ad46767887d14fa9c856df94f80e8a0a7f0169577ab23b52ee37bcdf", + "sha256:e28fb3f20568ce9e96e33e01e0b87b891822f36b8f368adb582553b016d4aa08" ], "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==0.16.16" + "version": "==0.16.17" }, "types-cachetools": { "hashes": [ @@ -1148,26 +1155,26 @@ }, "types-requests": { "hashes": [ - "sha256:0d580652ce903f643f8c3b494dd01d29367ea57cea0c7ad7f65cf3169092edb0", - "sha256:cc1aba862575019306b2ed134eb1ea994cab1c887a22e18d3383e6dd42e9789b" + "sha256:c6cf08e120ca9f0dc4fa4e32c3f953c3fba222bcc1db6b97695bce8da1ba9864", + "sha256:dec781054324a70ba64430ae9e62e7e9c8e4618c185a5cb3f87a6738251b5a31" ], "index": "pypi", - "version": "==2.28.11.17" + "version": "==2.30.0.0" }, "types-s3transfer": { "hashes": [ - "sha256:40e665643f0647832d51c4a26d8a8275cda9134b02bf22caf28198b79bcad382", - "sha256:d9c669b30fdd61347720434aacb8ecc4645d900712a70b10f495104f9039c07b" + "sha256:6d1ac1dedac750d570428362acdf60fdd4f277b0788855c3894d3226756b2bfb", + "sha256:75ac1d7143d58c1e6af467cfd4a96c67ee058a3adf7c249d9309999e1f5f41e4" ], "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==0.6.0.post7" + "version": "==0.6.1" }, "types-urllib3": { "hashes": [ - "sha256:04235e792139cf3624b25d38faab593456738fbdb7439634046172e3b1339400", - "sha256:697102ddf4f781eed6f692353f40cee1098643526f5a8b99f49d2ede90fd3754" + "sha256:3300538c9dc11dad32eae4827ac313f5d986b8b21494801f1bf97a1ac6c03ae5", + "sha256:5dbd1d2bef14efee43f5318b5d36d805a489f6600252bb53626d4bfafd95e27c" ], - "version": "==1.26.25.11" + "version": "==1.26.25.13" }, "typing-extensions": { "hashes": [ diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index 9491b354a..ce6307e3d 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -109,6 +109,8 @@ INSTALLED_APPS = [ "registrar", # Our internal API application "api", + # Only for generating documentation, uncomment to run manage.py generate_puml + # "puml_generator", ] # Middleware are routines for processing web requests. From 4b7a8953b7f191b5666c48d2f1caa81591143411 Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Fri, 12 May 2023 14:44:45 -0500 Subject: [PATCH 02/71] Adjust markdown files with PlantUML inside for Github --- docs/architecture/diagrams/model_timeline.md | 2 ++ docs/architecture/diagrams/models_diagram.md | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/docs/architecture/diagrams/model_timeline.md b/docs/architecture/diagrams/model_timeline.md index 30363e4d4..4a39807a1 100644 --- a/docs/architecture/diagrams/model_timeline.md +++ b/docs/architecture/diagrams/model_timeline.md @@ -19,6 +19,7 @@ A more complete diagram of the data models, their fields, and their relationships are in [models_diagram.md](./models_diagram.md), created with the `django-model2puml` plugin. +```plantuml @startuml allowmixing @@ -131,3 +132,4 @@ actor user3 #Green user3 -right[#green]-> UserDomainRole : **/domain//users/add** user3 -right[#green]-> DomainInvitation : **/domain//users/add** @enduml +``` diff --git a/docs/architecture/diagrams/models_diagram.md b/docs/architecture/diagrams/models_diagram.md index f10c6ad87..5e47514d0 100644 --- a/docs/architecture/diagrams/models_diagram.md +++ b/docs/architecture/diagrams/models_diagram.md @@ -1,3 +1,14 @@ +# 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 + +```bash +$ docker compose app ./manage.py generate_puml --include registrar +``` + +```plantuml @startuml class "registrar.Contact " as registrar.Contact #d6f4e9 { contact @@ -248,3 +259,4 @@ class "registrar.Website " as registrar.Website #d6f4e9 { @enduml +``` From ec9cf668816cc23f3e9f1d91e26687e177d16696 Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Fri, 12 May 2023 14:52:11 -0500 Subject: [PATCH 03/71] Try with mermaid instead of plantuml --- docs/architecture/diagrams/model_timeline.md | 2 +- docs/architecture/diagrams/models_diagram.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/architecture/diagrams/model_timeline.md b/docs/architecture/diagrams/model_timeline.md index 4a39807a1..8591e660b 100644 --- a/docs/architecture/diagrams/model_timeline.md +++ b/docs/architecture/diagrams/model_timeline.md @@ -19,7 +19,7 @@ A more complete diagram of the data models, their fields, and their relationships are in [models_diagram.md](./models_diagram.md), created with the `django-model2puml` plugin. -```plantuml +```mermaid @startuml allowmixing diff --git a/docs/architecture/diagrams/models_diagram.md b/docs/architecture/diagrams/models_diagram.md index 5e47514d0..b06c8807a 100644 --- a/docs/architecture/diagrams/models_diagram.md +++ b/docs/architecture/diagrams/models_diagram.md @@ -8,7 +8,7 @@ using the command $ docker compose app ./manage.py generate_puml --include registrar ``` -```plantuml +```mermaid @startuml class "registrar.Contact " as registrar.Contact #d6f4e9 { contact From de9a82725afb9b3704163dccde363d9e8b86faad Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Fri, 12 May 2023 14:54:00 -0500 Subject: [PATCH 04/71] Try without @startuml tag? --- docs/architecture/diagrams/model_timeline.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/architecture/diagrams/model_timeline.md b/docs/architecture/diagrams/model_timeline.md index 8591e660b..2f8991ed6 100644 --- a/docs/architecture/diagrams/model_timeline.md +++ b/docs/architecture/diagrams/model_timeline.md @@ -20,7 +20,6 @@ relationships are in [models_diagram.md](./models_diagram.md), created with the `django-model2puml` plugin. ```mermaid -@startuml allowmixing left to right direction @@ -131,5 +130,4 @@ user2 -[#green]-> DomainInformation : **/domain//?????** actor user3 #Green user3 -right[#green]-> UserDomainRole : **/domain//users/add** user3 -right[#green]-> DomainInvitation : **/domain//users/add** -@enduml ``` From 738e0dabd6da433e4bad562e3ed977aa13a390c8 Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Fri, 12 May 2023 15:07:02 -0500 Subject: [PATCH 05/71] Try with rendered SVGs in Markdown and
--- docs/architecture/diagrams/model_timeline.md | 10 +++++++++- docs/architecture/diagrams/model_timeline.svg | 1 + docs/architecture/diagrams/models_diagram.md | 7 ++++++- docs/architecture/diagrams/models_diagram.svg | 1 + 4 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 docs/architecture/diagrams/model_timeline.svg create mode 100644 docs/architecture/diagrams/models_diagram.svg diff --git a/docs/architecture/diagrams/model_timeline.md b/docs/architecture/diagrams/model_timeline.md index 2f8991ed6..6a0adda2a 100644 --- a/docs/architecture/diagrams/model_timeline.md +++ b/docs/architecture/diagrams/model_timeline.md @@ -19,7 +19,12 @@ A more complete diagram of the data models, their fields, and their relationships are in [models_diagram.md](./models_diagram.md), created with the `django-model2puml` plugin. -```mermaid +![./model_timeline.svg] + +
+PlantUML source code +```plantuml +@startuml allowmixing left to right direction @@ -130,4 +135,7 @@ user2 -[#green]-> DomainInformation : **/domain//?????** actor user3 #Green user3 -right[#green]-> UserDomainRole : **/domain//users/add** user3 -right[#green]-> DomainInvitation : **/domain//users/add** + +@enduml ``` +
diff --git a/docs/architecture/diagrams/model_timeline.svg b/docs/architecture/diagrams/model_timeline.svg new file mode 100644 index 000000000..fb466ec21 --- /dev/null +++ b/docs/architecture/diagrams/model_timeline.svg @@ -0,0 +1 @@ +DomainApplicationApplication for a domaincreator (User)investigator (User)authorizing_official (Contact)submitter (Contact)other_contacts (Contacts)requested_domain (Domain)current_websites (Websites)alternative_domains (Websites)Request information...UserDjango's user class...Created by DjangoOIDCwhen users arrive backfrom Login.gov usernameis the Login UUIDContactContact info for a personfirst_namemiddle_namelast_nametitleemailphoneDomainApproved domainnameis_activeEPP methodsDomainInformationRegistrar information on a domaindomain (Domain)domain_application (DomainApplication)security_emailRequest information...UserDomainRolePermissionsdomain (Domain)user (User)role="ADMIN"DomainInvitationEmail invitations sentemaildomain (Domain)statusapplicantanalystuser1user2user3creator, investigatorauthorizing_official, submitter, other_contactsapprove()approve()approve()User.first_login()/registerapprove()/domain/<id>/nameservers/domain/<id>/?????/domain/<id>/users/add/domain/<id>/users/add \ No newline at end of file diff --git a/docs/architecture/diagrams/models_diagram.md b/docs/architecture/diagrams/models_diagram.md index b06c8807a..95d8c0a58 100644 --- a/docs/architecture/diagrams/models_diagram.md +++ b/docs/architecture/diagrams/models_diagram.md @@ -8,7 +8,11 @@ using the command $ docker compose app ./manage.py generate_puml --include registrar ``` -```mermaid +![./models_diagram.svg] + +
+PlantUML source code +```plantuml @startuml class "registrar.Contact " as registrar.Contact #d6f4e9 { contact @@ -260,3 +264,4 @@ class "registrar.Website " as registrar.Website #d6f4e9 { @enduml ``` +
diff --git a/docs/architecture/diagrams/models_diagram.svg b/docs/architecture/diagrams/models_diagram.svg new file mode 100644 index 000000000..40d0b57b9 --- /dev/null +++ b/docs/architecture/diagrams/models_diagram.svg @@ -0,0 +1 @@ +registrarregistrar.ContactRegistrarcontactid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)user (OneToOneField)first_name (TextField)middle_name (TextField)last_name (TextField)title (TextField)email (TextField)phone (PhoneNumberField)registrar.UserRegistraruserid (BigAutoField)password (CharField)last_login (DateTimeField)is_superuser (BooleanField)username (CharField)first_name (CharField)last_name (CharField)email (EmailField)is_staff (BooleanField)is_active (BooleanField)date_joined (DateTimeField)phone (PhoneNumberField)groups (ManyToManyField)user_permissions (ManyToManyField)domains (ManyToManyField)registrar.DomainApplicationRegistrardomain applicationid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)status (FSMField)creator (ForeignKey)investigator (ForeignKey)organization_type (CharField)federally_recognized_tribe (BooleanField)state_recognized_tribe (BooleanField)tribe_name (TextField)federal_agency (TextField)federal_type (CharField)is_election_board (BooleanField)organization_name (TextField)address_line1 (TextField)address_line2 (CharField)city (TextField)state_territory (CharField)zipcode (CharField)urbanization (TextField)type_of_work (TextField)more_organization_information (TextField)authorizing_official (ForeignKey)requested_domain (OneToOneField)submitter (ForeignKey)purpose (TextField)no_other_contacts_rationale (TextField)anything_else (TextField)is_policy_acknowledged (BooleanField)current_websites (ManyToManyField)alternative_domains (ManyToManyField)other_contacts (ManyToManyField)registrar.DomainRegistrardomainid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)name (CharField)is_active (FSMField)registrar.WebsiteRegistrarwebsiteid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)website (CharField)registrar.DomainInformationRegistrardomain informationid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)creator (ForeignKey)domain_application (OneToOneField)organization_type (CharField)federally_recognized_tribe (BooleanField)state_recognized_tribe (BooleanField)tribe_name (TextField)federal_agency (TextField)federal_type (CharField)is_election_board (BooleanField)organization_name (TextField)address_line1 (TextField)address_line2 (CharField)city (TextField)state_territory (CharField)zipcode (CharField)urbanization (TextField)type_of_work (TextField)more_organization_information (TextField)authorizing_official (ForeignKey)domain (OneToOneField)submitter (ForeignKey)purpose (TextField)no_other_contacts_rationale (TextField)anything_else (TextField)is_policy_acknowledged (BooleanField)security_email (EmailField)other_contacts (ManyToManyField)registrar.HostIPRegistrarhost ipid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)address (CharField)host (ForeignKey)registrar.HostRegistrarhostid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)name (CharField)domain (ForeignKey)registrar.UserDomainRoleRegistraruser domain roleid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)user (ForeignKey)domain (ForeignKey)role (TextField)registrar.DomainInvitationRegistrardomain invitationid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)email (EmailField)domain (ForeignKey)status (FSMField)registrar.NameserverRegistrarnameserverid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)name (CharField)domain (ForeignKey)host_ptr (OneToOneField)registrar.PublicContactRegistrarpublic contactid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)contact_type (CharField)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) \ No newline at end of file From e25353a5610d38ff03aae49b3302bedd8e840bd2 Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Fri, 12 May 2023 15:09:07 -0500 Subject: [PATCH 06/71] Fix markdown image syntax --- docs/architecture/diagrams/model_timeline.md | 2 +- docs/architecture/diagrams/models_diagram.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/architecture/diagrams/model_timeline.md b/docs/architecture/diagrams/model_timeline.md index 6a0adda2a..bb6c11bb2 100644 --- a/docs/architecture/diagrams/model_timeline.md +++ b/docs/architecture/diagrams/model_timeline.md @@ -19,7 +19,7 @@ A more complete diagram of the data models, their fields, and their relationships are in [models_diagram.md](./models_diagram.md), created with the `django-model2puml` plugin. -![./model_timeline.svg] +![Data model timeline diagram](./model_timeline.svg)
PlantUML source code diff --git a/docs/architecture/diagrams/models_diagram.md b/docs/architecture/diagrams/models_diagram.md index 95d8c0a58..23e4091e8 100644 --- a/docs/architecture/diagrams/models_diagram.md +++ b/docs/architecture/diagrams/models_diagram.md @@ -8,7 +8,7 @@ using the command $ docker compose app ./manage.py generate_puml --include registrar ``` -![./models_diagram.svg] +![Complete data models diagram](./models_diagram.svg)
PlantUML source code From 0407cf63dab2d97f6dfa4e6bb7c3213071b8c89f Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Fri, 12 May 2023 15:11:08 -0500 Subject: [PATCH 07/71] Try to get a to work in
--- docs/architecture/diagrams/model_timeline.md | 2 ++ docs/architecture/diagrams/models_diagram.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docs/architecture/diagrams/model_timeline.md b/docs/architecture/diagrams/model_timeline.md index bb6c11bb2..aa4167163 100644 --- a/docs/architecture/diagrams/model_timeline.md +++ b/docs/architecture/diagrams/model_timeline.md @@ -23,6 +23,7 @@ the `django-model2puml` plugin.
PlantUML source code + ```plantuml @startuml @@ -138,4 +139,5 @@ user3 -right[#green]-> DomainInvitation : **/domain//users/add** @enduml ``` +
diff --git a/docs/architecture/diagrams/models_diagram.md b/docs/architecture/diagrams/models_diagram.md index 23e4091e8..04d95ee09 100644 --- a/docs/architecture/diagrams/models_diagram.md +++ b/docs/architecture/diagrams/models_diagram.md @@ -12,6 +12,7 @@ $ docker compose app ./manage.py generate_puml --include registrar
PlantUML source code + ```plantuml @startuml class "registrar.Contact " as registrar.Contact #d6f4e9 { @@ -264,4 +265,5 @@ class "registrar.Website " as registrar.Website #d6f4e9 { @enduml ``` +
From a356e8e9475d4ff3c68d834d39d63ae7de7e630f Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Fri, 12 May 2023 15:24:24 -0500 Subject: [PATCH 08/71] Document how to generate the SVG images --- docs/architecture/diagrams/model_timeline.md | 6 ++++++ docs/architecture/diagrams/model_timeline.svg | 2 +- docs/architecture/diagrams/models_diagram.md | 6 ++++++ docs/architecture/diagrams/models_diagram.svg | 2 +- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/architecture/diagrams/model_timeline.md b/docs/architecture/diagrams/model_timeline.md index aa4167163..f2089ce55 100644 --- a/docs/architecture/diagrams/model_timeline.md +++ b/docs/architecture/diagrams/model_timeline.md @@ -23,6 +23,12 @@ the `django-model2puml` plugin.
PlantUML source code +To regenerate this image using Docker, run + +```bash +$ docker run -v $(pwd):$(pwd) -w $(pwd) -it plantuml/plantuml -tsvg model_timeline.md +``` + ```plantuml @startuml diff --git a/docs/architecture/diagrams/model_timeline.svg b/docs/architecture/diagrams/model_timeline.svg index fb466ec21..cf2eea238 100644 --- a/docs/architecture/diagrams/model_timeline.svg +++ b/docs/architecture/diagrams/model_timeline.svg @@ -1 +1 @@ -DomainApplicationApplication for a domaincreator (User)investigator (User)authorizing_official (Contact)submitter (Contact)other_contacts (Contacts)requested_domain (Domain)current_websites (Websites)alternative_domains (Websites)Request information...UserDjango's user class...Created by DjangoOIDCwhen users arrive backfrom Login.gov usernameis the Login UUIDContactContact info for a personfirst_namemiddle_namelast_nametitleemailphoneDomainApproved domainnameis_activeEPP methodsDomainInformationRegistrar information on a domaindomain (Domain)domain_application (DomainApplication)security_emailRequest information...UserDomainRolePermissionsdomain (Domain)user (User)role="ADMIN"DomainInvitationEmail invitations sentemaildomain (Domain)statusapplicantanalystuser1user2user3creator, investigatorauthorizing_official, submitter, other_contactsapprove()approve()approve()User.first_login()/registerapprove()/domain/<id>/nameservers/domain/<id>/?????/domain/<id>/users/add/domain/<id>/users/add \ No newline at end of file +DomainApplicationApplication for a domaincreator (User)investigator (User)authorizing_official (Contact)submitter (Contact)other_contacts (Contacts)requested_domain (Domain)current_websites (Websites)alternative_domains (Websites)Request information...UserDjango's user class...Created by DjangoOIDCwhen users arrive backfrom Login.gov usernameis the Login UUIDContactContact info for a personfirst_namemiddle_namelast_nametitleemailphoneDomainApproved domainnameis_activeEPP methodsDomainInformationRegistrar information on a domaindomain (Domain)domain_application (DomainApplication)security_emailRequest information...UserDomainRolePermissionsdomain (Domain)user (User)role="ADMIN"DomainInvitationEmail invitations sentemaildomain (Domain)statusapplicantanalystuser1user2user3creator, investigatorauthorizing_official, submitter, other_contactsapprove()approve()approve()User.first_login()/registerapprove()/domain/<id>/nameservers/domain/<id>/?????/domain/<id>/users/add/domain/<id>/users/add \ No newline at end of file diff --git a/docs/architecture/diagrams/models_diagram.md b/docs/architecture/diagrams/models_diagram.md index 04d95ee09..77fa36707 100644 --- a/docs/architecture/diagrams/models_diagram.md +++ b/docs/architecture/diagrams/models_diagram.md @@ -13,6 +13,12 @@ $ docker compose app ./manage.py generate_puml --include registrar
PlantUML source code +To regenerate this image using Docker, run + +```bash +$ docker run -v $(pwd):$(pwd) -w $(pwd) -it plantuml/plantuml -tsvg models_diagram.md +``` + ```plantuml @startuml class "registrar.Contact " as registrar.Contact #d6f4e9 { diff --git a/docs/architecture/diagrams/models_diagram.svg b/docs/architecture/diagrams/models_diagram.svg index 40d0b57b9..e0cdd355f 100644 --- a/docs/architecture/diagrams/models_diagram.svg +++ b/docs/architecture/diagrams/models_diagram.svg @@ -1 +1 @@ -registrarregistrar.ContactRegistrarcontactid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)user (OneToOneField)first_name (TextField)middle_name (TextField)last_name (TextField)title (TextField)email (TextField)phone (PhoneNumberField)registrar.UserRegistraruserid (BigAutoField)password (CharField)last_login (DateTimeField)is_superuser (BooleanField)username (CharField)first_name (CharField)last_name (CharField)email (EmailField)is_staff (BooleanField)is_active (BooleanField)date_joined (DateTimeField)phone (PhoneNumberField)groups (ManyToManyField)user_permissions (ManyToManyField)domains (ManyToManyField)registrar.DomainApplicationRegistrardomain applicationid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)status (FSMField)creator (ForeignKey)investigator (ForeignKey)organization_type (CharField)federally_recognized_tribe (BooleanField)state_recognized_tribe (BooleanField)tribe_name (TextField)federal_agency (TextField)federal_type (CharField)is_election_board (BooleanField)organization_name (TextField)address_line1 (TextField)address_line2 (CharField)city (TextField)state_territory (CharField)zipcode (CharField)urbanization (TextField)type_of_work (TextField)more_organization_information (TextField)authorizing_official (ForeignKey)requested_domain (OneToOneField)submitter (ForeignKey)purpose (TextField)no_other_contacts_rationale (TextField)anything_else (TextField)is_policy_acknowledged (BooleanField)current_websites (ManyToManyField)alternative_domains (ManyToManyField)other_contacts (ManyToManyField)registrar.DomainRegistrardomainid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)name (CharField)is_active (FSMField)registrar.WebsiteRegistrarwebsiteid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)website (CharField)registrar.DomainInformationRegistrardomain informationid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)creator (ForeignKey)domain_application (OneToOneField)organization_type (CharField)federally_recognized_tribe (BooleanField)state_recognized_tribe (BooleanField)tribe_name (TextField)federal_agency (TextField)federal_type (CharField)is_election_board (BooleanField)organization_name (TextField)address_line1 (TextField)address_line2 (CharField)city (TextField)state_territory (CharField)zipcode (CharField)urbanization (TextField)type_of_work (TextField)more_organization_information (TextField)authorizing_official (ForeignKey)domain (OneToOneField)submitter (ForeignKey)purpose (TextField)no_other_contacts_rationale (TextField)anything_else (TextField)is_policy_acknowledged (BooleanField)security_email (EmailField)other_contacts (ManyToManyField)registrar.HostIPRegistrarhost ipid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)address (CharField)host (ForeignKey)registrar.HostRegistrarhostid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)name (CharField)domain (ForeignKey)registrar.UserDomainRoleRegistraruser domain roleid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)user (ForeignKey)domain (ForeignKey)role (TextField)registrar.DomainInvitationRegistrardomain invitationid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)email (EmailField)domain (ForeignKey)status (FSMField)registrar.NameserverRegistrarnameserverid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)name (CharField)domain (ForeignKey)host_ptr (OneToOneField)registrar.PublicContactRegistrarpublic contactid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)contact_type (CharField)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) \ No newline at end of file +registrarregistrar.ContactRegistrarcontactid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)user (OneToOneField)first_name (TextField)middle_name (TextField)last_name (TextField)title (TextField)email (TextField)phone (PhoneNumberField)registrar.UserRegistraruserid (BigAutoField)password (CharField)last_login (DateTimeField)is_superuser (BooleanField)username (CharField)first_name (CharField)last_name (CharField)email (EmailField)is_staff (BooleanField)is_active (BooleanField)date_joined (DateTimeField)phone (PhoneNumberField)groups (ManyToManyField)user_permissions (ManyToManyField)domains (ManyToManyField)registrar.DomainApplicationRegistrardomain applicationid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)status (FSMField)creator (ForeignKey)investigator (ForeignKey)organization_type (CharField)federally_recognized_tribe (BooleanField)state_recognized_tribe (BooleanField)tribe_name (TextField)federal_agency (TextField)federal_type (CharField)is_election_board (BooleanField)organization_name (TextField)address_line1 (TextField)address_line2 (CharField)city (TextField)state_territory (CharField)zipcode (CharField)urbanization (TextField)type_of_work (TextField)more_organization_information (TextField)authorizing_official (ForeignKey)requested_domain (OneToOneField)submitter (ForeignKey)purpose (TextField)no_other_contacts_rationale (TextField)anything_else (TextField)is_policy_acknowledged (BooleanField)current_websites (ManyToManyField)alternative_domains (ManyToManyField)other_contacts (ManyToManyField)registrar.DomainRegistrardomainid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)name (CharField)is_active (FSMField)registrar.WebsiteRegistrarwebsiteid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)website (CharField)registrar.DomainInformationRegistrardomain informationid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)creator (ForeignKey)domain_application (OneToOneField)organization_type (CharField)federally_recognized_tribe (BooleanField)state_recognized_tribe (BooleanField)tribe_name (TextField)federal_agency (TextField)federal_type (CharField)is_election_board (BooleanField)organization_name (TextField)address_line1 (TextField)address_line2 (CharField)city (TextField)state_territory (CharField)zipcode (CharField)urbanization (TextField)type_of_work (TextField)more_organization_information (TextField)authorizing_official (ForeignKey)domain (OneToOneField)submitter (ForeignKey)purpose (TextField)no_other_contacts_rationale (TextField)anything_else (TextField)is_policy_acknowledged (BooleanField)security_email (EmailField)other_contacts (ManyToManyField)registrar.HostIPRegistrarhost ipid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)address (CharField)host (ForeignKey)registrar.HostRegistrarhostid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)name (CharField)domain (ForeignKey)registrar.UserDomainRoleRegistraruser domain roleid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)user (ForeignKey)domain (ForeignKey)role (TextField)registrar.DomainInvitationRegistrardomain invitationid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)email (EmailField)domain (ForeignKey)status (FSMField)registrar.NameserverRegistrarnameserverid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)name (CharField)domain (ForeignKey)host_ptr (OneToOneField)registrar.PublicContactRegistrarpublic contactid (BigAutoField)created_at (DateTimeField)updated_at (DateTimeField)contact_type (CharField)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) \ No newline at end of file From 400232a10f51a72528ac8d44b2ec428ab1e9a4b4 Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Wed, 17 May 2023 13:59:34 -0500 Subject: [PATCH 09/71] Add public_site_url template tag --- ops/manifests/manifest-stable.yaml | 2 ++ ops/scripts/manifest-sandbox-template.yaml | 2 ++ src/.env-example | 1 + src/docker-compose.yml | 2 ++ src/registrar/config/settings.py | 7 +++++++ src/registrar/templates/application_purpose.html | 4 ++-- src/registrar/templatetags/url_helpers.py | 16 ++++++++++++++++ 7 files changed, 32 insertions(+), 2 deletions(-) diff --git a/ops/manifests/manifest-stable.yaml b/ops/manifests/manifest-stable.yaml index 619e5fc7a..72726cd08 100644 --- a/ops/manifests/manifest-stable.yaml +++ b/ops/manifests/manifest-stable.yaml @@ -20,6 +20,8 @@ applications: DJANGO_BASE_URL: https://getgov-stable.app.cloud.gov # Tell Django how much stuff to log DJANGO_LOG_LEVEL: INFO + # Public site base URL + GETGOV_PUBLIC_SITE_URL: https://federalist-877ab29f-16f6-4f12-961c-96cf064cf070.sites.pages.cloud.gov/site/cisagov/getgov-home/ routes: - route: getgov-stable.app.cloud.gov services: diff --git a/ops/scripts/manifest-sandbox-template.yaml b/ops/scripts/manifest-sandbox-template.yaml index ba814b3d5..1bf979c9f 100644 --- a/ops/scripts/manifest-sandbox-template.yaml +++ b/ops/scripts/manifest-sandbox-template.yaml @@ -20,6 +20,8 @@ applications: DJANGO_BASE_URL: https://getgov-ENVIRONMENT.app.cloud.gov # Tell Django how much stuff to log DJANGO_LOG_LEVEL: INFO + # default public site location + GETGOV_PUBLIC_SITE_URL: https://beta.get.gov routes: - route: getgov-ENVIRONMENT.app.cloud.gov services: diff --git a/src/.env-example b/src/.env-example index 7100fc628..9b3f56409 100644 --- a/src/.env-example +++ b/src/.env-example @@ -1,2 +1,3 @@ DJANGO_SECRET_KEY="" DJANGO_SECRET_LOGIN_KEY="" +GETGOV_PUBLIC_SITE_URL="https://federalist-877ab29f-16f6-4f12-961c-96cf064cf070.sites.pages.cloud.gov/site/cisagov/getgov-home/" diff --git a/src/docker-compose.yml b/src/docker-compose.yml index 4399c5b70..8b1111868 100644 --- a/src/docker-compose.yml +++ b/src/docker-compose.yml @@ -27,6 +27,8 @@ services: - DJANGO_DEBUG=True # Tell Django where it is being hosted - DJANGO_BASE_URL=http://localhost:8080 + # Public site URL link + - GETGOV_PUBLIC_SITE_URL="https://beta.get.gov" # Set a username for accessing the registry - REGISTRY_CL_ID=nothing # Set a password for accessing the registry diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index 9491b354a..41559ceba 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -62,6 +62,9 @@ secret_registry_key = b64decode(secret("REGISTRY_KEY", "")) secret_registry_key_passphrase = secret("REGISTRY_KEY_PASSPHRASE", "") secret_registry_hostname = secret("REGISTRY_HOSTNAME") +# this needs to exist or a warning will be generated +secret_getgov_public_site_url = secret("GETGOV_PUBLIC_SITE_URL") + # region: Basic Django Config-----------------------------------------------### # Build paths inside the project like this: BASE_DIR / "subdir". @@ -503,6 +506,10 @@ ROOT_URLCONF = "registrar.config.urls" # Must be relative and end with "/" STATIC_URL = "public/" +# Base URL of our separate static public website. Used by the +# {% public_site_url subdir/path %} template tag +GETGOV_PUBLIC_SITE_URL = secret_getgov_public_site_url + # endregion # region: Registry----------------------------------------------------------### diff --git a/src/registrar/templates/application_purpose.html b/src/registrar/templates/application_purpose.html index a28dc27b3..ca2ff7287 100644 --- a/src/registrar/templates/application_purpose.html +++ b/src/registrar/templates/application_purpose.html @@ -1,5 +1,5 @@ {% extends 'application_form.html' %} -{% load field_helpers %} +{% load field_helpers url_helpers %} {% block form_instructions %}

.Gov domain names are for use on the internet. Don’t register a .gov to simply reserve a @@ -8,7 +8,7 @@ domain name or for mainly internal use.

Describe the reason for your domain request. Explain how you plan to use this domain. Who is your intended audience? Will you use it for a website and/or email? Are you moving your website from another top-level domain (like .com or .org)? -Read about activities that are prohibited on .gov domains.

+Read about activities that are prohibited on .gov domains.

{% endblock %} diff --git a/src/registrar/templatetags/url_helpers.py b/src/registrar/templatetags/url_helpers.py index 6201e61eb..7662a42d8 100644 --- a/src/registrar/templatetags/url_helpers.py +++ b/src/registrar/templatetags/url_helpers.py @@ -1,6 +1,10 @@ +from urllib.parse import urljoin + from django import template from django.urls import reverse +from django.conf import settings + register = template.Library() @@ -15,3 +19,15 @@ def startswith(text, starts): if isinstance(text, str): return text.startswith(starts) return False + + +@register.simple_tag +def public_site_url(url_path): + """Make a full URL for this path at our public site. + + The public site base url is set by a GETGOV_PUBLIC_SITE_URL environment + variable. + """ + base_url = settings.GETGOV_PUBLIC_SITE_URL + public_url = urljoin(base_url, url_path) + return public_url From 135192fd9f44fdd313c3583d5f986ce8df965e9e Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Wed, 17 May 2023 15:02:59 -0500 Subject: [PATCH 10/71] Add a unit test for new template tag --- src/docker-compose.yml | 2 +- src/registrar/templatetags/url_helpers.py | 6 ++++- src/registrar/tests/test_templatetags.py | 31 +++++++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 src/registrar/tests/test_templatetags.py diff --git a/src/docker-compose.yml b/src/docker-compose.yml index 8b1111868..82642bc93 100644 --- a/src/docker-compose.yml +++ b/src/docker-compose.yml @@ -28,7 +28,7 @@ services: # Tell Django where it is being hosted - DJANGO_BASE_URL=http://localhost:8080 # Public site URL link - - GETGOV_PUBLIC_SITE_URL="https://beta.get.gov" + - GETGOV_PUBLIC_SITE_URL=https://beta.get.gov # Set a username for accessing the registry - REGISTRY_CL_ID=nothing # Set a password for accessing the registry diff --git a/src/registrar/templatetags/url_helpers.py b/src/registrar/templatetags/url_helpers.py index 7662a42d8..096d6f2f6 100644 --- a/src/registrar/templatetags/url_helpers.py +++ b/src/registrar/templatetags/url_helpers.py @@ -29,5 +29,9 @@ def public_site_url(url_path): variable. """ base_url = settings.GETGOV_PUBLIC_SITE_URL - public_url = urljoin(base_url, url_path) + # join the two halves with a single slash + public_url ="/".join([ + base_url.rstrip("/"), + url_path.lstrip("/") + ]) return public_url diff --git a/src/registrar/tests/test_templatetags.py b/src/registrar/tests/test_templatetags.py new file mode 100644 index 000000000..10a174be0 --- /dev/null +++ b/src/registrar/tests/test_templatetags.py @@ -0,0 +1,31 @@ +"""Test template tags.""" + +from django.conf import settings +from django.test import TestCase +from django.template import Context, Template + +class TestTemplateTags(TestCase): + + def _render_template(self, string, context=None): + """Helper method to render a template given as a string. + + Originally from https://stackoverflow.com/a/1690879 + """ + context = context or {} + context = Context(context) + return Template(string).render(context) + + def test_public_site_url(self): + result = self._render_template( + "{% load url_helpers %}{% public_site_url 'directory/page' %}" + ) + self.assertTrue(result.startswith(settings.GETGOV_PUBLIC_SITE_URL)) + self.assertTrue(result.endswith("/directory/page")) + + def test_public_site_url_leading_slash(self): + result = self._render_template( + "{% load url_helpers %}{% public_site_url '/directory/page' %}" + ) + self.assertTrue(result.startswith(settings.GETGOV_PUBLIC_SITE_URL)) + # slash-slash host slash directory slash page + self.assertEqual(result.count("/"), 4) From 125877c4af0e9bee51018292a9b33bd54cdc005b Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Wed, 17 May 2023 15:13:48 -0500 Subject: [PATCH 11/71] Fix linting errors --- src/registrar/templatetags/url_helpers.py | 7 +------ src/registrar/tests/test_templatetags.py | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/registrar/templatetags/url_helpers.py b/src/registrar/templatetags/url_helpers.py index 096d6f2f6..5b76c116f 100644 --- a/src/registrar/templatetags/url_helpers.py +++ b/src/registrar/templatetags/url_helpers.py @@ -1,5 +1,3 @@ -from urllib.parse import urljoin - from django import template from django.urls import reverse @@ -30,8 +28,5 @@ def public_site_url(url_path): """ base_url = settings.GETGOV_PUBLIC_SITE_URL # join the two halves with a single slash - public_url ="/".join([ - base_url.rstrip("/"), - url_path.lstrip("/") - ]) + public_url = "/".join([base_url.rstrip("/"), url_path.lstrip("/")]) return public_url diff --git a/src/registrar/tests/test_templatetags.py b/src/registrar/tests/test_templatetags.py index 10a174be0..681d823b7 100644 --- a/src/registrar/tests/test_templatetags.py +++ b/src/registrar/tests/test_templatetags.py @@ -4,8 +4,8 @@ from django.conf import settings from django.test import TestCase from django.template import Context, Template -class TestTemplateTags(TestCase): +class TestTemplateTags(TestCase): def _render_template(self, string, context=None): """Helper method to render a template given as a string. From efe418bf2ec986614ece7320fda57142736c3a66 Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Wed, 17 May 2023 15:25:21 -0500 Subject: [PATCH 12/71] Fix broken test with env default --- src/registrar/config/settings.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index 41559ceba..879948785 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -62,8 +62,7 @@ secret_registry_key = b64decode(secret("REGISTRY_KEY", "")) secret_registry_key_passphrase = secret("REGISTRY_KEY_PASSPHRASE", "") secret_registry_hostname = secret("REGISTRY_HOSTNAME") -# this needs to exist or a warning will be generated -secret_getgov_public_site_url = secret("GETGOV_PUBLIC_SITE_URL") +secret_getgov_public_site_url = secret("GETGOV_PUBLIC_SITE_URL", "") # region: Basic Django Config-----------------------------------------------### From 1511b5deca1abca41a39b822ccc2eeabb0837c47 Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Thu, 18 May 2023 19:14:37 -0400 Subject: [PATCH 13/71] Upate summary item for user lists and edit button option --- .../templates/includes/summary_item.html | 120 +++++++++++------- 1 file changed, 74 insertions(+), 46 deletions(-) diff --git a/src/registrar/templates/includes/summary_item.html b/src/registrar/templates/includes/summary_item.html index c902f1980..170ec67cb 100644 --- a/src/registrar/templates/includes/summary_item.html +++ b/src/registrar/templates/includes/summary_item.html @@ -1,49 +1,77 @@ +{% load static url_helpers %} +

-

- {{ title }} -

- {% if address %} - {% include "includes/organization_address.html" with organization=value %} - {% elif contact %} - {% if list %} - {% if value|length == 1 %} - {% include "includes/contact.html" with contact=value|first %} - {% else %} -
    - {% for item in value %} -
  • -

    - Conatct {{forloop.counter}} -

    - {% include "includes/contact.html" with contact=item %}
  • - {% empty %} -
  • None
  • - {% endfor %}

- - {% endif %} - {% else %} - {% include "includes/contact.html" with contact=value %} - {% endif %} - {% elif list %} - {% if value|length == 1 %} -

{{ value | first }}

- {% else %} -
    - {% for item in value %} -
  • {{ item }}
  • - {% empty %} -
  • None
  • - {% endfor %}

- - {% endif %} - {% else %} -

- {{ value }} -

- {% endif %} - +
+
+

+ {{ title }} +

+ {% if address %} + {% include "includes/organization_address.html" with organization=value %} + {% elif contact %} + {% if list %} + {% if value|length == 1 %} + {% include "includes/contact.html" with contact=value|first %} + {% else %} +
    + {% for item in value %} +
  • +

    + Conatct {{forloop.counter}} +

    + {% include "includes/contact.html" with contact=item %}
  • + {% empty %} +
  • None
  • + {% endfor %}

+ + {% endif %} + {% else %} + {% include "includes/contact.html" with contact=value %} + {% endif %} + + {% elif list %} + {% if value|length == 1 %} +

{{ value | first }}

+ {% else %} +
    + {% for item in value %} +
  • {{ item }}
  • + {% empty %} +
  • None
  • + {% endfor %}

+ + {% endif %} + + {% elif users %} + {% if value|length == 1 %} +

{{ user.email }} ({{ value.first.role }})

+ {% else %} +
    + {% for item in value %} +
  • {{ user.email }} ({{ item.role }})
  • + {% endfor %} + {% endif %} + + {% else %} +

    + {{ value }} +

    + {% endif %} +
+ + {% if edit_link %} + + Edit {{ title }} + + {% endif %} + +
+
From 649be0f49c42f0b81e8b842d201d4ac378de47ff Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Thu, 18 May 2023 19:15:13 -0400 Subject: [PATCH 14/71] Add detail fields to domain overview page --- src/registrar/templates/domain_detail.html | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/registrar/templates/domain_detail.html b/src/registrar/templates/domain_detail.html index dd6faa1be..14ac10639 100644 --- a/src/registrar/templates/domain_detail.html +++ b/src/registrar/templates/domain_detail.html @@ -1,6 +1,33 @@ {% extends "domain_base.html" %} +{% load static url_helpers %} {% block domain_content %} {{ block.super }}

Active: {% if domain.is_active %}Yes{% else %}No{% endif %}

+ org type: {{domain.domain_info.organization_type}} + + {% url 'domain-nameservers' pk=domain.id as url %} + {% include "includes/summary_item.html" with title='DNS name servers' value=domain.nameservers list='true' edit_link=url %} + + {% url 'todo' as url %} + {% if domain.domain_info.organization_name %} + {% include "includes/summary_item.html" with title='Organization name and mailing address' value=domain.domain_info address='true' edit_link=url %} + {% endif %} + + {% if domain.domain_info.authorizing_official %} + {% url 'todo' as url %} + {% include "includes/summary_item.html" with title='Authorizing official' value=domain.domain_info.authorizing_official contact='true' edit_link=url %} + {% endif %} + + {% if domain.domain_info.submitter %} + {% url 'todo' as url %} + {% include "includes/summary_item.html" with title='Your contact information' value='---TODO---' edit_link=url %} + {% endif %} + + {% url 'todo' as url %} + {% include "includes/summary_item.html" with title='Security email' value=domain.domain_info.security_email edit_link=url %} + + {% url 'domain-users' pk=domain.id as url %} + {% include "includes/summary_item.html" with title='User management' users='true' value=domain.permissions.all edit_link=url %} + {% endblock %} {# domain_content #} From 23672f426510ef4d44acfa388ad6159f25a72599 Mon Sep 17 00:00:00 2001 From: rachidatecs Date: Fri, 19 May 2023 10:57:24 -0400 Subject: [PATCH 15/71] url confid for contact information --- src/registrar/config/urls.py | 7 ++++++- src/zap.conf | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index bd5b22da7..2cecd6492 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -84,7 +84,12 @@ urlpatterns = [ name="domain-nameservers", ), path( - "domain//securityemail", + "domain//contact-information", + views.DomainSecurityEmailView.as_view(), + name="domain-contact-information", + ), + path( + "domain//security-email", views.DomainSecurityEmailView.as_view(), name="domain-security-email", ), diff --git a/src/zap.conf b/src/zap.conf index bb86d60b7..ee92e8a1c 100644 --- a/src/zap.conf +++ b/src/zap.conf @@ -52,7 +52,7 @@ 10038 OUTOFSCOPE http://app:8080/users 10038 OUTOFSCOPE http://app:8080/users/add 10038 OUTOFSCOPE http://app:8080/nameservers -10038 OUTOFSCOPE http://app:8080/securityemail +10038 OUTOFSCOPE http://app:8080/security-email 10038 OUTOFSCOPE http://app:8080/delete 10038 OUTOFSCOPE http://app:8080/withdraw 10038 OUTOFSCOPE http://app:8080/withdrawconfirmed From e277c3a64c346f4a7239639488b1c7decccb6927 Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Fri, 19 May 2023 10:52:16 -0500 Subject: [PATCH 16/71] Form to edit a domain's authorizing official --- src/registrar/config/urls.py | 5 +++ src/registrar/forms/__init__.py | 2 +- src/registrar/forms/domain.py | 40 +++++++++++++++++ src/registrar/models/contact.py | 3 ++ .../domain_authorizing_official.html | 43 ++++++++++++++++++ src/registrar/templates/domain_sidebar.html | 2 +- src/registrar/views/__init__.py | 1 + src/registrar/views/domain.py | 45 ++++++++++++++++++- 8 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 src/registrar/templates/domain_authorizing_official.html diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index 204e5ca92..1087a2f19 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -83,6 +83,11 @@ urlpatterns = [ views.DomainNameserversView.as_view(), name="domain-nameservers", ), + path( + "domain//authorizing-official", + views.DomainAuthorizingOfficialView.as_view(), + name="domain-authorizing-official", + ), path( "domain//users/add", views.DomainAddUserView.as_view(), diff --git a/src/registrar/forms/__init__.py b/src/registrar/forms/__init__.py index 6c1b5d8cf..8e35825aa 100644 --- a/src/registrar/forms/__init__.py +++ b/src/registrar/forms/__init__.py @@ -1,2 +1,2 @@ from .application_wizard import * -from .domain import DomainAddUserForm, NameserverFormset +from .domain import DomainAddUserForm, NameserverFormset, ContactForm diff --git a/src/registrar/forms/domain.py b/src/registrar/forms/domain.py index cca0bf5c9..1c8033a2b 100644 --- a/src/registrar/forms/domain.py +++ b/src/registrar/forms/domain.py @@ -3,6 +3,9 @@ from django import forms from django.forms import formset_factory +from phonenumber_field.widgets import RegionalPhoneNumberWidget + +from ..models import Contact class DomainAddUserForm(forms.Form): @@ -22,3 +25,40 @@ NameserverFormset = formset_factory( DomainNameserverForm, extra=1, ) + + +class ContactForm(forms.ModelForm): + + """Form for updating contacts.""" + + class Meta: + model = Contact + fields = ["first_name", "middle_name", "last_name", "title", "email", "phone"] + widgets = { + "first_name": forms.TextInput, + "middle_name": forms.TextInput, + "last_name": forms.TextInput, + "title": forms.TextInput, + "email": forms.EmailInput, + "phone": RegionalPhoneNumberWidget, + } + + # the database fields have blank=True so ModelForm doesn't create + # required fields by default. Use this list in __init__ to mark each + # of these fields as required + required = [ + "first_name", + "last_name", + "title", + "email", + "phone" + ] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # take off maxlength attribute for the phone number field + # which interferes with out input_with_errors template tag + self.fields['phone'].widget.attrs.pop('maxlength', None) + + for field_name in self.required: + self.fields[field_name].required = True diff --git a/src/registrar/models/contact.py b/src/registrar/models/contact.py index d5d32a7ae..cbfde7a23 100644 --- a/src/registrar/models/contact.py +++ b/src/registrar/models/contact.py @@ -20,6 +20,7 @@ class Contact(TimeStampedModel): null=True, blank=True, help_text="First name", + verbose_name="first name / given name", db_index=True, ) middle_name = models.TextField( @@ -31,12 +32,14 @@ class Contact(TimeStampedModel): null=True, blank=True, help_text="Last name", + verbose_name="last name / family name", db_index=True, ) title = models.TextField( null=True, blank=True, help_text="Title", + verbose_name="title or role in your organization", ) email = models.TextField( null=True, diff --git a/src/registrar/templates/domain_authorizing_official.html b/src/registrar/templates/domain_authorizing_official.html new file mode 100644 index 000000000..445db5707 --- /dev/null +++ b/src/registrar/templates/domain_authorizing_official.html @@ -0,0 +1,43 @@ +{% extends "domain_base.html" %} +{% load static field_helpers%} + +{% block title %}Domain authorizing official | {{ domain.name }} | {% endblock %} + +{% block domain_content %} + {# this is right after the messages block in the parent template #} + {% include "includes/form_errors.html" with form=form %} + +

Authorizing official

+ +

Your authorizing official is the person within your organization who can + authorize domain requests. This is generally the highest-ranking or + highest-elected official in your organization. Read more about who can serve + as an authorizing official.

+ + {% include "includes/required_fields.html" %} + +
+ {% csrf_token %} + + {% input_with_errors form.first_name %} + + {% input_with_errors form.middle_name %} + + {% input_with_errors form.last_name %} + + {% input_with_errors form.title %} + + {% input_with_errors form.email %} + + {% input_with_errors form.phone %} + + + + +
+ +{% endblock %} {# domain_content #} diff --git a/src/registrar/templates/domain_sidebar.html b/src/registrar/templates/domain_sidebar.html index c50cc59bd..ab259a4a7 100644 --- a/src/registrar/templates/domain_sidebar.html +++ b/src/registrar/templates/domain_sidebar.html @@ -31,7 +31,7 @@
  • - {% url 'todo' as url %} + {% url 'domain-authorizing-official' pk=domain.id as url %} diff --git a/src/registrar/views/__init__.py b/src/registrar/views/__init__.py index 9f7fe139e..2eae93370 100644 --- a/src/registrar/views/__init__.py +++ b/src/registrar/views/__init__.py @@ -1,6 +1,7 @@ from .application import * from .domain import ( DomainView, + DomainAuthorizingOfficialView, DomainNameserversView, DomainUsersView, DomainAddUserView, diff --git a/src/registrar/views/domain.py b/src/registrar/views/domain.py index 8a9095de3..8b9186958 100644 --- a/src/registrar/views/domain.py +++ b/src/registrar/views/domain.py @@ -12,7 +12,7 @@ from django.views.generic.edit import DeleteView, FormMixin from registrar.models import Domain, DomainInvitation, User, UserDomainRole -from ..forms import DomainAddUserForm, NameserverFormset +from ..forms import DomainAddUserForm, NameserverFormset, ContactForm from ..utility.email import send_templated_email, EmailSendingError from .utility import DomainPermission @@ -29,6 +29,49 @@ class DomainView(DomainPermission, DetailView): context_object_name = "domain" +class DomainAuthorizingOfficialView(DomainPermission, FormMixin, DetailView): + + """Domain authorizing official editing view.""" + + model = Domain + template_name = "domain_authorizing_official.html" + context_object_name = "domain" + form_class = ContactForm + + def get_form_kwargs(self, *args, **kwargs): + """Add domain_info.authorizing_official instance to make a bound form.""" + form_kwargs = super().get_form_kwargs(*args, **kwargs) + form_kwargs["instance"] = self.get_object().domain_info.authorizing_official + return form_kwargs + + def get_success_url(self): + """Redirect to the overview page for the domain.""" + return reverse("domain-authorizing-official", kwargs={"pk": self.object.pk}) + + def post(self, request, *args, **kwargs): + """Form submission posts to this view. + + This post method harmonizes using DetailView and FormMixin together. + """ + self.object = self.get_object() + form = self.get_form() + if form.is_valid(): + return self.form_valid(form) + else: + return self.form_invalid(form) + + def form_valid(self, form): + """The form is valid, save the authorizing official.""" + domain = self.get_object() + form.save() + + messages.success( + self.request, "The authorizing official for this domain has been updated." + ) + # superclass has the redirect + return super().form_valid(form) + + class DomainNameserversView(DomainPermission, FormMixin, DetailView): """Domain nameserver editing view.""" From d99a6da82f695c6c7115af1cf0286f250c981482 Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Fri, 19 May 2023 12:30:59 -0400 Subject: [PATCH 17/71] Fix formatting and remove aria tag --- src/registrar/templates/includes/summary_item.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/registrar/templates/includes/summary_item.html b/src/registrar/templates/includes/summary_item.html index 170ec67cb..bcf8b7ea1 100644 --- a/src/registrar/templates/includes/summary_item.html +++ b/src/registrar/templates/includes/summary_item.html @@ -43,7 +43,7 @@
  • {{ item }}
  • {% empty %}
  • None
  • - {% endfor %}

    + {% endfor %} {% endif %} @@ -58,7 +58,7 @@ {% endif %} {% else %} -

    +

    {{ value }}

    {% endif %} @@ -66,7 +66,6 @@ {% if edit_link %}
    Edit {{ title }} From e17bca1a99d6e4af58ebc7678d5e1f1192122fa3 Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Fri, 19 May 2023 12:33:44 -0400 Subject: [PATCH 18/71] Add conditional text for dns section, remove if gates --- src/registrar/templates/domain_detail.html | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/registrar/templates/domain_detail.html b/src/registrar/templates/domain_detail.html index 14ac10639..8dcef8a70 100644 --- a/src/registrar/templates/domain_detail.html +++ b/src/registrar/templates/domain_detail.html @@ -3,31 +3,31 @@ {% block domain_content %} {{ block.super }} -

    Active: {% if domain.is_active %}Yes{% else %}No{% endif %}

    - org type: {{domain.domain_info.organization_type}} +
    - {% url 'domain-nameservers' pk=domain.id as url %} - {% include "includes/summary_item.html" with title='DNS name servers' value=domain.nameservers list='true' edit_link=url %} + {% url 'domain-nameservers' pk=domain.id as url %} + {% if domain.nameservers %} + {% include "includes/summary_item.html" with title='DNS name servers' value=domain.nameservers list='true' edit_link=url %} + {% else %} +

    DNS name servers

    +

    No DNS name servers have been added yet. Before your domain can be used we’ll need information about your domain name servers.

    +
    Add DNS name servers + {% endif %} - {% url 'todo' as url %} - {% if domain.domain_info.organization_name %} + {% url 'todo' as url %} {% include "includes/summary_item.html" with title='Organization name and mailing address' value=domain.domain_info address='true' edit_link=url %} - {% endif %} - {% if domain.domain_info.authorizing_official %} {% url 'todo' as url %} {% include "includes/summary_item.html" with title='Authorizing official' value=domain.domain_info.authorizing_official contact='true' edit_link=url %} - {% endif %} - {% if domain.domain_info.submitter %} {% url 'todo' as url %} {% include "includes/summary_item.html" with title='Your contact information' value='---TODO---' edit_link=url %} - {% endif %} - {% url 'todo' as url %} - {% include "includes/summary_item.html" with title='Security email' value=domain.domain_info.security_email edit_link=url %} + {% url 'todo' as url %} + {% include "includes/summary_item.html" with title='Security email' value=domain.domain_info.security_email edit_link=url %} - {% url 'domain-users' pk=domain.id as url %} - {% include "includes/summary_item.html" with title='User management' users='true' value=domain.permissions.all edit_link=url %} + {% url 'domain-users' pk=domain.id as url %} + {% include "includes/summary_item.html" with title='User management' users='true' value=domain.permissions.all edit_link=url %} +
    {% endblock %} {# domain_content #} From aa58fe0f265f9b9a55f0ecbc00f0817d9c9ce11a Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Fri, 19 May 2023 12:34:49 -0400 Subject: [PATCH 19/71] Make h2 color default primary-dark --- .../assets/sass/_theme/_uswds-theme-custom-styles.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/registrar/assets/sass/_theme/_uswds-theme-custom-styles.scss b/src/registrar/assets/sass/_theme/_uswds-theme-custom-styles.scss index ff2614fb8..8c06730bf 100644 --- a/src/registrar/assets/sass/_theme/_uswds-theme-custom-styles.scss +++ b/src/registrar/assets/sass/_theme/_uswds-theme-custom-styles.scss @@ -77,6 +77,7 @@ h2 { font-weight: font-weight('semibold'); line-height: line-height('heading', 3); margin: units(4) 0 units(1); + color: color('primary-darker'); } .register-form-step > h1 { @@ -431,4 +432,4 @@ abbr[title] { @include at-media('tablet') { height: units('mobile'); } -} \ No newline at end of file +} From eb51173ab472fb1a026d413610054453c9ef443e Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Fri, 19 May 2023 12:44:36 -0400 Subject: [PATCH 20/71] Add security email section --- src/registrar/templates/domain_detail.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/templates/domain_detail.html b/src/registrar/templates/domain_detail.html index 8dcef8a70..97b15207a 100644 --- a/src/registrar/templates/domain_detail.html +++ b/src/registrar/templates/domain_detail.html @@ -23,8 +23,8 @@ {% url 'todo' as url %} {% include "includes/summary_item.html" with title='Your contact information' value='---TODO---' edit_link=url %} - {% url 'todo' as url %} - {% include "includes/summary_item.html" with title='Security email' value=domain.domain_info.security_email edit_link=url %} + {% url 'domain-security-email' pk=domain.id as url %} + {% include "includes/summary_item.html" with title='Security email' value=domain.security_email edit_link=url %} {% url 'domain-users' pk=domain.id as url %} {% include "includes/summary_item.html" with title='User management' users='true' value=domain.permissions.all edit_link=url %} From 0d5ca5bdcbda92ac4e97b48c4674076faeb63d1a Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Fri, 19 May 2023 12:47:21 -0400 Subject: [PATCH 21/71] Make domain overview sentance case in sidebar --- src/registrar/templates/domain_sidebar.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/templates/domain_sidebar.html b/src/registrar/templates/domain_sidebar.html index 46a87ba76..76877198b 100644 --- a/src/registrar/templates/domain_sidebar.html +++ b/src/registrar/templates/domain_sidebar.html @@ -8,7 +8,7 @@ - Domain Overview + Domain overview From 53de82a406afc0bcde97e9491c64c6b98fdb300e Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Fri, 19 May 2023 14:55:37 -0400 Subject: [PATCH 22/71] Remove extra spacing on single item lists --- src/registrar/templates/includes/summary_item.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/templates/includes/summary_item.html b/src/registrar/templates/includes/summary_item.html index bcf8b7ea1..3b9d555cd 100644 --- a/src/registrar/templates/includes/summary_item.html +++ b/src/registrar/templates/includes/summary_item.html @@ -36,7 +36,7 @@ {% elif list %} {% if value|length == 1 %} -

    {{ value | first }}

    +

    {{ value | first }}

    {% else %}