From a0f50465f8604822b7df10a411bcca49242b32d5 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 16 Aug 2023 15:30:59 -0600 Subject: [PATCH 001/110] Update home.html --- src/registrar/templates/home.html | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/registrar/templates/home.html b/src/registrar/templates/home.html index 792beec43..a1a54376a 100644 --- a/src/registrar/templates/home.html +++ b/src/registrar/templates/home.html @@ -43,13 +43,13 @@ {{ domain.application_status|title }} - Manage {{ domain.name }} @@ -115,14 +115,16 @@ aria-live="polite" > {% else %} -

You don't have any active domain requests right now

+

You don't have any active domain requests right now

{% endif %}

Start a new domain request

+ {# Note: Reimplement this later after MVP #} + {% else %} {# not user.is_authenticated #} From f4b4c2e0e31df0bd025b460c223a5e019cfbe936 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 16 Aug 2023 15:34:56 -0600 Subject: [PATCH 002/110] Merge was a bit funky, fixing --- src/registrar/templates/home.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/registrar/templates/home.html b/src/registrar/templates/home.html index a24ae97d6..6daaf35ba 100644 --- a/src/registrar/templates/home.html +++ b/src/registrar/templates/home.html @@ -126,6 +126,7 @@

Archived domains

You don't have any archived domains

+ --> - + + --> {% else %} {# not user.is_authenticated #} From e384b03d3610c1e6fc9b3bf9daf6235fd2138a23 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 17 Aug 2023 07:34:12 -0600 Subject: [PATCH 003/110] Domain Request Button --- src/registrar/templates/home.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/templates/home.html b/src/registrar/templates/home.html index 6daaf35ba..b2f784ebd 100644 --- a/src/registrar/templates/home.html +++ b/src/registrar/templates/home.html @@ -116,8 +116,8 @@ > {% else %}

You don't have any active domain requests right now

- {% endif %}

Start a new domain request

+ {% endif %} {# Note: Reimplement this later after MVP #} From 16c70d555c04d2cc3636193b69cdfda528028f1b Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 17 Aug 2023 08:38:09 -0600 Subject: [PATCH 004/110] Verbiage change --- src/registrar/templates/home.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/templates/home.html b/src/registrar/templates/home.html index b2f784ebd..86fefef4a 100644 --- a/src/registrar/templates/home.html +++ b/src/registrar/templates/home.html @@ -120,7 +120,7 @@ {% endif %} - {# Note: Reimplement this later after MVP #} + {# Note: Reimplement this after MVP #} {% endif %} - {# this is the input field, itself #} {% include widget.template_name %} From a7a07c81902bac8f6c182368d2015f94988149de Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 28 Aug 2023 12:51:19 -0600 Subject: [PATCH 062/110] Split www_gov into two vars --- .../templates/application_dotgov_domain.html | 4 ++-- .../templates/includes/input_with_errors.html | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/registrar/templates/application_dotgov_domain.html b/src/registrar/templates/application_dotgov_domain.html index b2ee6cab6..fd6ce9604 100644 --- a/src/registrar/templates/application_dotgov_domain.html +++ b/src/registrar/templates/application_dotgov_domain.html @@ -51,7 +51,7 @@ {% with attr_aria_describedby="domain_instructions domain_instructions2" %} {# attr_validate / validate="domain" invokes code in get-gov.js #} - {% with www_gov=True attr_validate="domain" add_label_class="usa-sr-only" %} + {% with append_gov=True attr_validate="domain" add_label_class="usa-sr-only" %} {% input_with_errors forms.0.requested_domain %} {% endwith %} {% endwith %} @@ -75,7 +75,7 @@ {% with attr_aria_describedby="alt_domain_instructions" %} {# attr_validate / validate="domain" invokes code in get-gov.js #} {# attr_auto_validate likewise triggers behavior in get-gov.js #} - {% with www_gov=True attr_validate="domain" attr_auto_validate=True %} + {% with append_gov=True attr_validate="domain" attr_auto_validate=True %} {% for form in forms.1 %} {% input_with_errors form.alternative_domain %} {% endfor %} diff --git a/src/registrar/templates/includes/input_with_errors.html b/src/registrar/templates/includes/input_with_errors.html index 7540766af..2a4420327 100644 --- a/src/registrar/templates/includes/input_with_errors.html +++ b/src/registrar/templates/includes/input_with_errors.html @@ -55,18 +55,20 @@ error messages, if necessary. {% endif %} - {% if www_gov %} + {% if prepend_www or append_gov %}
- {# Commented out for ticket #720: https://github.com/cisagov/getgov/issues/720 #} - + {# ticket #720: https://github.com/cisagov/getgov/issues/720 #} + {% if prepend_www %} + www. + {% endif %} {% endif %} {# this is the input field, itself #} {% include widget.template_name %} - {% if www_gov %} + {% if prepend_www or append_gov %} + {% if append_gov %} .gov + {% endif %}
{% endif %} From 36e00d4020e541d0be58e9c15b3d9430946a6eb2 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 28 Aug 2023 13:57:59 -0600 Subject: [PATCH 063/110] Removed comment and prepend_www --- .../templates/includes/input_with_errors.html | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/registrar/templates/includes/input_with_errors.html b/src/registrar/templates/includes/input_with_errors.html index 2a4420327..2adc08984 100644 --- a/src/registrar/templates/includes/input_with_errors.html +++ b/src/registrar/templates/includes/input_with_errors.html @@ -55,20 +55,14 @@ error messages, if necessary. {% endif %} - {% if prepend_www or append_gov %} + {% if append_gov %}
- {# ticket #720: https://github.com/cisagov/getgov/issues/720 #} - {% if prepend_www %} - www. - {% endif %} {% endif %} {# this is the input field, itself #} {% include widget.template_name %} - {% if prepend_www or append_gov %} - {% if append_gov %} + {% if append_gov %} .gov - {% endif %}
{% endif %} From 7d404d54945b6d6c753ab93c97d4b458c2e147d2 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 28 Aug 2023 14:43:40 -0600 Subject: [PATCH 064/110] Logging test Temporarily removed assertEqual to log weird values --- src/registrar/tests/test_admin.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index ea4eaeb62..fadff2393 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -649,11 +649,6 @@ class DomainSessionVariableTest(TestCase): self.admin = DomainAdmin(Domain, None) self.client = Client(HTTP_HOST="localhost:8080") - # Test data seems to linger in the sandbox for tests. - # We delete this here for these tests. - DomainInformation.objects.all().delete() - Domain.objects.all().delete() - def test_session_vars_set_correctly(self): """Checks if session variables are being set correctly""" @@ -737,8 +732,8 @@ class DomainSessionVariableTest(TestCase): logger.info( f"After populate - Domain Pk: {item.domain.pk} obj pk: {item.pk}" ) - self.assertEqual(request.session["analyst_action"], "edit") - self.assertEqual(request.session["analyst_action_location"], item.domain.pk) + #self.assertEqual(request.session["analyst_action"], "edit") + #self.assertEqual(request.session["analyst_action_location"], item.domain.pk) def test_session_variables_concurrent_requests(self): """Simulates two requests at once""" From 4ea8ea4c08bda01da3e758767341f76ad1785403 Mon Sep 17 00:00:00 2001 From: Cameron Dixon Date: Mon, 28 Aug 2023 16:51:06 -0400 Subject: [PATCH 065/110] Update default mail-sending address Short and sweet: help@get.gov --- 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 8b53fa82a..f0468999e 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -251,8 +251,7 @@ AWS_MAX_ATTEMPTS = 3 BOTO_CONFIG = Config(retries={"mode": AWS_RETRY_MODE, "max_attempts": AWS_MAX_ATTEMPTS}) # email address to use for various automated correspondence -# TODO: pick something sensible here -DEFAULT_FROM_EMAIL = "registrar@get.gov" +DEFAULT_FROM_EMAIL = "help@get.gov" # connect to an (external) SMTP server for sending email EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" From 666e17e4ba09e876fcb0672f05dc4eb2df96fd1a Mon Sep 17 00:00:00 2001 From: Cameron Dixon Date: Mon, 28 Aug 2023 17:22:10 -0400 Subject: [PATCH 066/110] wrap email in < > Co-authored-by: rachidatecs <107004823+rachidatecs@users.noreply.github.com> --- src/registrar/config/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index f0468999e..e272e6622 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -251,7 +251,7 @@ AWS_MAX_ATTEMPTS = 3 BOTO_CONFIG = Config(retries={"mode": AWS_RETRY_MODE, "max_attempts": AWS_MAX_ATTEMPTS}) # email address to use for various automated correspondence -DEFAULT_FROM_EMAIL = "help@get.gov" +DEFAULT_FROM_EMAIL = "help@get.gov " # connect to an (external) SMTP server for sending email EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" From 31d4bfb5a7f04a9858d8181eda086bc39fa83180 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 28 Aug 2023 15:23:27 -0600 Subject: [PATCH 067/110] Test case fix / remove redundant --- src/registrar/tests/test_admin.py | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index fadff2393..5cad9c646 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -648,7 +648,7 @@ class DomainSessionVariableTest(TestCase): self.factory = RequestFactory() self.admin = DomainAdmin(Domain, None) self.client = Client(HTTP_HOST="localhost:8080") - + def test_session_vars_set_correctly(self): """Checks if session variables are being set correctly""" @@ -698,22 +698,6 @@ class DomainSessionVariableTest(TestCase): dummy_domain_information.domain.pk, ) - def test_unauthorized_user(self): - """Checks for when a user has invalid perms""" - - p = "userpass" - self.client.login(username="staffuser", password=p) - - dummy_domain_information = generic_domain_object("information", "session") - request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk) - self.populate_session_values(request, dummy_domain_information.domain) - - self.assertEqual(request.session["analyst_action"], "edit") - self.assertEqual( - request.session["analyst_action_location"], - dummy_domain_information.domain.pk, - ) - def test_session_variables_retain_information(self): """Checks to see if session variables retain old information""" @@ -725,15 +709,10 @@ class DomainSessionVariableTest(TestCase): ) for item in dummy_domain_information_list: request = self.get_factory_post_edit_domain(item.domain.pk) - logger.info( - f"Before populate - Domain Pk: {item.domain.pk} obj pk: {item.pk}" - ) - self.populate_session_values(request, item) - logger.info( - f"After populate - Domain Pk: {item.domain.pk} obj pk: {item.pk}" - ) - #self.assertEqual(request.session["analyst_action"], "edit") - #self.assertEqual(request.session["analyst_action_location"], item.domain.pk) + self.populate_session_values(request, item.domain) + + self.assertEqual(request.session["analyst_action"], "edit") + self.assertEqual(request.session["analyst_action_location"], item.domain.pk) def test_session_variables_concurrent_requests(self): """Simulates two requests at once""" From 6ac58525465671955ac57df2161536ca3edf8286 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 28 Aug 2023 15:31:14 -0600 Subject: [PATCH 068/110] Linter --- src/registrar/tests/test_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 5cad9c646..df967414b 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -648,7 +648,7 @@ class DomainSessionVariableTest(TestCase): self.factory = RequestFactory() self.admin = DomainAdmin(Domain, None) self.client = Client(HTTP_HOST="localhost:8080") - + def test_session_vars_set_correctly(self): """Checks if session variables are being set correctly""" From 5f5289faa3fa233f452ff9fac0813870e6249e4f Mon Sep 17 00:00:00 2001 From: Cameron Dixon Date: Mon, 28 Aug 2023 17:48:51 -0400 Subject: [PATCH 069/110] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2685a8ec3..f457e7e69 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Get (your very own) .gov +# Infrastructure as a (public) service -Welcome to the repo for a WIP brand new registrar for .gov domains. Get.gov intends to serve all government entities in the United States looking for a .gov domain to use publicly (for a website, for an email address, etc.). Here you can find the code for the registrar and other artifacts about our product strategy and research. +The .gov domain helps U.S.-based government organizations gain public trust by being easily recognized online. This repo contains the code for the new .gov registrar – where governments request and manage domains – and other artifacts about our product strategy and research. ## Onboarding From df090b06f4058436a9cc9eb148616c2d7f5e90e4 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 29 Aug 2023 10:49:40 -0600 Subject: [PATCH 070/110] Added get-gov-admin.js --- src/registrar/assets/js/get-gov-admin.js | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/registrar/assets/js/get-gov-admin.js diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js new file mode 100644 index 000000000..d7d589214 --- /dev/null +++ b/src/registrar/assets/js/get-gov-admin.js @@ -0,0 +1,37 @@ +/** + * @file get-gov-admin.js includes custom code for the .gov registrar admin portal. + * + * Constants and helper functions are at the top. + * Event handlers are in the middle. + * Initialization (run-on-load) stuff goes at the bottom. + */ + +// <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>> +// Helper functions. +/** Either sets attribute target="_blank" to a given element, or removes it */ +function openInNewTab(el, removeAttribute = false){ + if(removeAttribute){ + el.setAttribute("target", "_blank"); + }else{ + el.removeAttribute("target", "_blank"); + } +}; + +// <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>> +// Event handlers. + +// <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>> +// Initialization code. + +/** An IIFE for pages in DjangoAdmin which may need custom JS implementation. + * Currently only appends target="_blank" to the domain_form object, + * but this can be expanded. +*/ +(function prepareDjangoAdmin(){ + let domainFormElement = document.getElementById("domain_form"); + let domainSubmitButton = document.getElementById("manageDomainSubmitButton"); + if(domainSubmitButton && domainFormElement){ + domainSubmitButton.addEventListener("mouseover", () => openInNewTab(domainFormElement, true)); + domainSubmitButton.addEventListener("mouseout", () => openInNewTab(domainFormElement, false)); + } +})(); \ No newline at end of file From 501cddebde7172cdf91ae05c458cf571df733f9e Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Tue, 29 Aug 2023 14:16:39 -0400 Subject: [PATCH 071/110] Change ineligible to restricted on User, fix status tempplate for DAs --- src/registrar/admin.py | 19 +++++++------- .../migrations/0030_alter_user_status.py | 23 +++++++++++++++++ src/registrar/models/domain_application.py | 2 +- src/registrar/models/user.py | 16 ++++++------ .../templates/application_status.html | 1 + src/registrar/tests/test_admin.py | 25 +++++++++---------- src/registrar/tests/test_views.py | 4 +-- src/registrar/views/utility/mixins.py | 4 +-- 8 files changed, 57 insertions(+), 37 deletions(-) create mode 100644 src/registrar/migrations/0030_alter_user_status.py diff --git a/src/registrar/admin.py b/src/registrar/admin.py index a832d34bd..4696a15bf 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -116,7 +116,7 @@ class MyUserAdmin(BaseUserAdmin): ( None, {"fields": ("username", "password", "status")}, - ), # Add the 'status' field here + ), ("Personal Info", {"fields": ("first_name", "last_name", "email")}), ( "Permissions", @@ -301,7 +301,7 @@ class DomainApplicationAdmin(ListHeaderAdmin): # Trigger action when a fieldset is changed def save_model(self, request, obj, form, change): - if obj and obj.creator.status != "ineligible": + if obj and obj.creator.status != models.User.RESTRICTED: if change: # Check if the application is being edited # Get the original application from the database original_obj = models.DomainApplication.objects.get(pk=obj.pk) @@ -336,7 +336,7 @@ class DomainApplicationAdmin(ListHeaderAdmin): messages.error( request, "This action is not permitted for applications " - + "with an ineligible creator.", + + "with a restricted creator.", ) def get_readonly_fields(self, request, obj=None): @@ -348,8 +348,8 @@ class DomainApplicationAdmin(ListHeaderAdmin): readonly_fields = list(self.readonly_fields) - # Check if the creator is ineligible - if obj and obj.creator.status == "ineligible": + # Check if the creator is restricted + if obj and obj.creator.status == models.User.RESTRICTED: # For fields like CharField, IntegerField, etc., the widget used is # straightforward and the readonly_fields list can control their behavior readonly_fields.extend([field.name for field in self.model._meta.fields]) @@ -365,17 +365,16 @@ class DomainApplicationAdmin(ListHeaderAdmin): readonly_fields.extend([field for field in self.analyst_readonly_fields]) return readonly_fields - def display_ineligible_warning(self, request, obj): - if obj and obj.creator.status == "ineligible": + def display_restricted_warning(self, request, obj): + if obj and obj.creator.status == models.User.RESTRICTED: messages.warning( request, - "Cannot edit an application when its creator " - + "has a status of ineligible.", + "Cannot edit an application with a restricted creator.", ) def change_view(self, request, object_id, form_url="", extra_context=None): obj = self.get_object(request, object_id) - self.display_ineligible_warning(request, obj) + self.display_restricted_warning(request, obj) return super().change_view(request, object_id, form_url, extra_context) diff --git a/src/registrar/migrations/0030_alter_user_status.py b/src/registrar/migrations/0030_alter_user_status.py new file mode 100644 index 000000000..7dd27bfa4 --- /dev/null +++ b/src/registrar/migrations/0030_alter_user_status.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.1 on 2023-08-29 17:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0029_user_status_alter_domainapplication_status"), + ] + + operations = [ + migrations.AlterField( + model_name="user", + name="status", + field=models.CharField( + blank=True, + choices=[("restricted", "restricted")], + default=None, + max_length=10, + null=True, + ), + ), + ] diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index c06985079..b1230b703 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -621,7 +621,7 @@ class DomainApplication(TimeStampedModel): We do this by setting an ineligible status on the user, which the permissions classes test against""" - self.creator.block_user() + self.creator.restrict_user() # ## Form policies ### # diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index 9b3ba9162..5cf1dd71f 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -18,8 +18,8 @@ class User(AbstractUser): """ # #### Constants for choice fields #### - INELIGIBLE = "ineligible" - STATUS_CHOICES = ((INELIGIBLE, INELIGIBLE),) + RESTRICTED = "restricted" + STATUS_CHOICES = ((RESTRICTED, RESTRICTED),) status = models.CharField( max_length=10, @@ -51,18 +51,16 @@ class User(AbstractUser): else: return self.username - def block_user(self): - self.status = "ineligible" + def restrict_user(self): + self.status = self.RESTRICTED self.save() - def unblock_user(self): + def unrestrict_user(self): self.status = None self.save() - def is_blocked(self): - if self.status == "ineligible": - return True - return False + def is_restricted(self): + return self.status == self.RESTRICTED def first_login(self): """Callback when the user is authenticated for the very first time. diff --git a/src/registrar/templates/application_status.html b/src/registrar/templates/application_status.html index 99f6a1d4c..c95f6a98d 100644 --- a/src/registrar/templates/application_status.html +++ b/src/registrar/templates/application_status.html @@ -23,6 +23,7 @@ {% elif domainapplication.status == 'in review' %} In Review {% elif domainapplication.status == 'rejected' %} Rejected {% elif domainapplication.status == 'submitted' %} Submitted + {% elif domainapplication.status == 'ineligible' %} Ineligible {% else %}ERROR Please contact technical support/dev {% endif %}

diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 4a38d3576..fc5478dd9 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -284,7 +284,7 @@ class TestDomainApplicationAdmin(TestCase): # Perform assertions on the mock call itself mock_client_instance.send_email.assert_called_once() - def test_save_model_sets_ineligible_status_on_user(self): + def test_save_model_sets_restricted_status_on_user(self): # make sure there is no user with this email EMAIL = "mayor@igorville.gov" User.objects.filter(email=EMAIL).delete() @@ -304,11 +304,11 @@ class TestDomainApplicationAdmin(TestCase): self.admin.save_model(request, application, form=None, change=True) # Test that approved domain exists and equals requested domain - self.assertEqual(application.creator.status, "ineligible") + self.assertEqual(application.creator.status, "restricted") - def test_readonly_when_ineligible_creator(self): + def test_readonly_when_restricted_creator(self): application = completed_application(status=DomainApplication.IN_REVIEW) - application.creator.status = "ineligible" + application.creator.status = User.RESTRICTED application.creator.save() request = self.factory.get("/") @@ -388,10 +388,10 @@ class TestDomainApplicationAdmin(TestCase): self.assertEqual(readonly_fields, expected_fields) - def test_saving_when_ineligible_creator(self): + def test_saving_when_restricted_creator(self): # Create an instance of the model application = completed_application(status=DomainApplication.IN_REVIEW) - application.creator.status = "ineligible" + application.creator.status = User.RESTRICTED application.creator.save() # Create a request object with a superuser @@ -405,17 +405,17 @@ class TestDomainApplicationAdmin(TestCase): # Assert that the error message was called with the correct argument mock_error.assert_called_once_with( request, - "This action is not permitted for applications with " - + "an ineligible creator.", + "This action is not permitted for applications " + + "with a restricted creator.", ) # Assert that the status has not changed self.assertEqual(application.status, DomainApplication.IN_REVIEW) - def test_change_view_with_ineligible_creator(self): + def test_change_view_with_restricted_creator(self): # Create an instance of the model application = completed_application(status=DomainApplication.IN_REVIEW) - application.creator.status = "ineligible" + application.creator.status = User.RESTRICTED application.creator.save() with patch("django.contrib.messages.warning") as mock_warning: @@ -425,13 +425,12 @@ class TestDomainApplicationAdmin(TestCase): ) request.user = self.superuser - self.admin.display_ineligible_warning(request, application) + self.admin.display_restricted_warning(request, application) # Assert that the error message was called with the correct argument mock_warning.assert_called_once_with( request, - "Cannot edit an application when its creator " - + "has a status of ineligible.", + "Cannot edit an application with a restricted creator.", ) def tearDown(self): diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index 0d1239cf3..017cb4d8d 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -105,7 +105,7 @@ class LoggedInTests(TestWithUser): """Application form not accessible for an ineligible user. This test should be solid enough since all application wizard views share the same permissions class""" - self.user.status = "ineligible" + self.user.status = User.RESTRICTED self.user.save() with less_console_noise(): @@ -1439,7 +1439,7 @@ class TestDomainDetail(TestWithDomainPermissions, WebTest): """We could easily duplicate this test for all domain management views, but a single url test should be solid enough since all domain management pages share the same permissions class""" - self.user.status = "ineligible" + self.user.status = User.RESTRICTED self.user.save() home_page = self.app.get("/") self.assertContains(home_page, "igorville.gov") diff --git a/src/registrar/views/utility/mixins.py b/src/registrar/views/utility/mixins.py index e85348d6c..363709a21 100644 --- a/src/registrar/views/utility/mixins.py +++ b/src/registrar/views/utility/mixins.py @@ -40,7 +40,7 @@ class DomainPermission(PermissionsLoginMixin): return False # The user has an ineligible flag - if self.request.user.is_blocked(): + if self.request.user.is_restricted(): return False # if we need to check more about the nature of role, do it here. @@ -82,7 +82,7 @@ class ApplicationWizardPermission(PermissionsLoginMixin): """ # The user has an ineligible flag - if self.request.user.is_blocked(): + if self.request.user.is_restricted(): return False return True From f5300053dc3b47e8ba0882ce5a272bed8259d9e1 Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Tue, 29 Aug 2023 15:29:02 -0400 Subject: [PATCH 072/110] show requested domain and alternate domains on manage screen; formatting conditional based on existence of alternate domains; add check for alternate domains in test_views --- src/registrar/templates/application_status.html | 4 ++++ .../templates/includes/summary_item.html | 15 +++++++++++++++ src/registrar/tests/test_views.py | 2 ++ 3 files changed, 21 insertions(+) diff --git a/src/registrar/templates/application_status.html b/src/registrar/templates/application_status.html index 99f6a1d4c..7e23cfe32 100644 --- a/src/registrar/templates/application_status.html +++ b/src/registrar/templates/application_status.html @@ -83,6 +83,10 @@ {% include "includes/summary_item.html" with title='Current website for your organization' value=domainapplication.current_websites.all list='true' heading_level=heading_level %} {% endif %} + {% if domainapplication.requested_domain %} + {% include "includes/summary_item.html" with title='.gov domain' value=domainapplication requested_domain='true' heading_level=heading_level %} + {% endif %} + {% if domainapplication.purpose %} {% include "includes/summary_item.html" with title='Purpose of your domain' value=domainapplication.purpose heading_level=heading_level %} {% endif %} diff --git a/src/registrar/templates/includes/summary_item.html b/src/registrar/templates/includes/summary_item.html index a2035b227..3295963ed 100644 --- a/src/registrar/templates/includes/summary_item.html +++ b/src/registrar/templates/includes/summary_item.html @@ -43,6 +43,21 @@ {% else %} {% include "includes/contact.html" with contact=value %} {% endif %} + {% elif requested_domain %} + {% if value.alternative_domains.all %} +
    +
  • {{ value.requested_domain.name|default:"Incomplete" }}
  • +
+
    + {% for domain in value.alternative_domains.all %} +
  • {{ domain.website }}
  • + {% endfor %} +
+ {% else %} +

+ {{ value.requested_domain }} +

+ {% endif %} {% elif list %} {% if value|length == 1 %} {% if users %} diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index b4795de72..1a4d2197f 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -1442,6 +1442,7 @@ class TestApplicationStatus(TestWithUser, WebTest): # click the "Manage" link detail_page = home_page.click("Manage") self.assertContains(detail_page, "city.gov") + self.assertContains(detail_page, "city1.gov") self.assertContains(detail_page, "Chief Tester") self.assertContains(detail_page, "testy@town.com") self.assertContains(detail_page, "Admin Tester") @@ -1459,6 +1460,7 @@ class TestApplicationStatus(TestWithUser, WebTest): # click the "Manage" link detail_page = home_page.click("Manage") self.assertContains(detail_page, "city.gov") + self.assertContains(detail_page, "city1.gov") self.assertContains(detail_page, "Chief Tester") self.assertContains(detail_page, "testy@town.com") self.assertContains(detail_page, "Admin Tester") From 1a81d75170963eca872ddf3c330e1b062959d23b Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Wed, 30 Aug 2023 07:02:18 -0400 Subject: [PATCH 073/110] show requested domain and alternate domains on manage screen; formatting conditional based on existence of alternative domains; check added for alternative domains in test_views --- src/registrar/templates/application_status.html | 6 +++++- .../templates/includes/summary_item.html | 15 --------------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/registrar/templates/application_status.html b/src/registrar/templates/application_status.html index 7e23cfe32..dcae617a8 100644 --- a/src/registrar/templates/application_status.html +++ b/src/registrar/templates/application_status.html @@ -84,7 +84,11 @@ {% endif %} {% if domainapplication.requested_domain %} - {% include "includes/summary_item.html" with title='.gov domain' value=domainapplication requested_domain='true' heading_level=heading_level %} + {% include "includes/summary_item.html" with title='.gov domain' value=domainapplication.requested_domain heading_level=heading_level %} + {% endif %} + + {% if domainapplication.alternative_domains.all %} + {% include "includes/summary_item.html" with title='Alternative domains' value=domainapplication.alternative_domains.all list='true' heading_level=heading_level %} {% endif %} {% if domainapplication.purpose %} diff --git a/src/registrar/templates/includes/summary_item.html b/src/registrar/templates/includes/summary_item.html index 3295963ed..a2035b227 100644 --- a/src/registrar/templates/includes/summary_item.html +++ b/src/registrar/templates/includes/summary_item.html @@ -43,21 +43,6 @@ {% else %} {% include "includes/contact.html" with contact=value %} {% endif %} - {% elif requested_domain %} - {% if value.alternative_domains.all %} -
    -
  • {{ value.requested_domain.name|default:"Incomplete" }}
  • -
-
    - {% for domain in value.alternative_domains.all %} -
  • {{ domain.website }}
  • - {% endfor %} -
- {% else %} -

- {{ value.requested_domain }} -

- {% endif %} {% elif list %} {% if value|length == 1 %} {% if users %} From 4dd175f2a06f6ce6b2825669aab50ec65dfc1882 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 30 Aug 2023 07:43:26 -0600 Subject: [PATCH 074/110] Rebuild sandbox --- src/registrar/tests/test_admin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index df967414b..7d9b1d9ae 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -674,6 +674,7 @@ class DomainSessionVariableTest(TestCase): "information", "session" ) dummy_domain_information.domain.pk = 1 + request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk) self.populate_session_values(request, dummy_domain_information.domain) self.assertEqual(request.session["analyst_action"], "edit") From 19d173efd4496dd34ccbe65c066b2df4b3bd0a39 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 30 Aug 2023 09:32:08 -0600 Subject: [PATCH 075/110] Fixed merge issues --- src/registrar/views/utility/mixins.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/registrar/views/utility/mixins.py b/src/registrar/views/utility/mixins.py index 8617f8acb..1fd21b18e 100644 --- a/src/registrar/views/utility/mixins.py +++ b/src/registrar/views/utility/mixins.py @@ -41,11 +41,15 @@ class DomainPermission(PermissionsLoginMixin): if pk is None: raise ValueError("Primary key is None") - # user needs to have a role on the domain + # user needs to have a role on the domain, + # and user cannot be restricted if UserDomainRole.objects.filter( user=self.request.user, domain__id=pk - ).exists(): + ).exists() and not self.request.user.is_restricted(): return True + elif self.request.user.is_restricted(): + return False + # ticket 806 requested_domain = None @@ -87,10 +91,6 @@ class DomainPermission(PermissionsLoginMixin): if can_do_action and user_is_analyst_or_superuser: return True - # The user has an ineligible flag - if self.request.user.is_restricted(): - return False - # if we need to check more about the nature of role, do it here. return False From 560d2c3ac56153c37b07e573ace55abfcb22dea6 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 30 Aug 2023 12:34:11 -0600 Subject: [PATCH 076/110] Update oidc.py --- src/djangooidc/oidc.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/djangooidc/oidc.py b/src/djangooidc/oidc.py index 26eb54d3d..2f08a0177 100644 --- a/src/djangooidc/oidc.py +++ b/src/djangooidc/oidc.py @@ -238,6 +238,10 @@ class Client(oic.Client): "client_secret": self.client_secret, }, authn_method=self.registration_response["token_endpoint_auth_method"], + # There is a time desync issue between login.gov and cloud, + # this addresses that by adding a clock skew. + # See here: https://github.com/GSA-TTS/FAC/pull/1968#pullrequestreview-1601224051 + skew=10, ) except Exception as err: logger.error(err) From a5ee50627d9e509a2a53479c680e4db0a4b44063 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Wed, 30 Aug 2023 14:37:55 -0400 Subject: [PATCH 077/110] Fix test against checkbox in django admin by using a custom filter --- .../templates/admin/change_list_results.html | 2 +- src/registrar/templatetags/custom_filters.py | 8 ++++++++ src/registrar/tests/test_templatetags.py | 19 +++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/registrar/templates/admin/change_list_results.html b/src/registrar/templates/admin/change_list_results.html index 9ee3f9f59..831350888 100644 --- a/src/registrar/templates/admin/change_list_results.html +++ b/src/registrar/templates/admin/change_list_results.html @@ -17,7 +17,7 @@ Load our custom filters to extract info from the django generated markup. -{% if results.0.form %} +{% if results.0|contains_checkbox %} {# .gov - hardcode the select all checkbox #}
diff --git a/src/registrar/templatetags/custom_filters.py b/src/registrar/templatetags/custom_filters.py index f16408bf8..3614db18e 100644 --- a/src/registrar/templatetags/custom_filters.py +++ b/src/registrar/templatetags/custom_filters.py @@ -40,3 +40,11 @@ def slice_after(value, substring): result = value[index + len(substring) :] return result return value + + +@register.filter +def contains_checkbox(html_list): + for html_string in html_list: + if re.search(r']*type="checkbox"', html_string): + return True + return False diff --git a/src/registrar/tests/test_templatetags.py b/src/registrar/tests/test_templatetags.py index 36325ab5d..d5f8523c8 100644 --- a/src/registrar/tests/test_templatetags.py +++ b/src/registrar/tests/test_templatetags.py @@ -8,6 +8,7 @@ from registrar.templatetags.custom_filters import ( extract_a_text, find_index, slice_after, + contains_checkbox, ) @@ -83,3 +84,21 @@ class CustomFiltersTestCase(TestCase): self.assertEqual( result, value ) # Should return the original value if substring not found + + def test_contains_checkbox_with_checkbox(self): + # Test the filter when HTML list contains a checkbox + html_list = [ + '', + "
Some other HTML content
", + ] + result = contains_checkbox(html_list) + self.assertTrue(result) # Expecting True + + def test_contains_checkbox_without_checkbox(self): + # Test the filter when HTML list does not contain a checkbox + html_list = [ + "
Some HTML content without checkbox
", + "

More HTML content

", + ] + result = contains_checkbox(html_list) + self.assertFalse(result) # Expecting False From 107cdc3b0e06234778443358c2dbb72dcc88fdf0 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 30 Aug 2023 12:54:12 -0600 Subject: [PATCH 078/110] Linter change --- src/djangooidc/oidc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/djangooidc/oidc.py b/src/djangooidc/oidc.py index 2f08a0177..9907e4ee4 100644 --- a/src/djangooidc/oidc.py +++ b/src/djangooidc/oidc.py @@ -238,7 +238,7 @@ class Client(oic.Client): "client_secret": self.client_secret, }, authn_method=self.registration_response["token_endpoint_auth_method"], - # There is a time desync issue between login.gov and cloud, + # There is a time desync issue between login.gov and cloud, # this addresses that by adding a clock skew. # See here: https://github.com/GSA-TTS/FAC/pull/1968#pullrequestreview-1601224051 skew=10, From 6be3725612c16d16e3f4b2542e1d63add95a4f72 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 30 Aug 2023 13:08:37 -0600 Subject: [PATCH 079/110] Fix linter issue --- src/djangooidc/oidc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/djangooidc/oidc.py b/src/djangooidc/oidc.py index 9907e4ee4..03745e056 100644 --- a/src/djangooidc/oidc.py +++ b/src/djangooidc/oidc.py @@ -238,7 +238,7 @@ class Client(oic.Client): "client_secret": self.client_secret, }, authn_method=self.registration_response["token_endpoint_auth_method"], - # There is a time desync issue between login.gov and cloud, + # There is a time desync issue between login.gov and cloud # this addresses that by adding a clock skew. # See here: https://github.com/GSA-TTS/FAC/pull/1968#pullrequestreview-1601224051 skew=10, From 1a347b5ed7d08bd82ed5523542a074e8664581ff Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 30 Aug 2023 13:11:04 -0600 Subject: [PATCH 080/110] Fix lint real --- src/djangooidc/oidc.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/djangooidc/oidc.py b/src/djangooidc/oidc.py index 03745e056..286e519a4 100644 --- a/src/djangooidc/oidc.py +++ b/src/djangooidc/oidc.py @@ -240,7 +240,6 @@ class Client(oic.Client): authn_method=self.registration_response["token_endpoint_auth_method"], # There is a time desync issue between login.gov and cloud # this addresses that by adding a clock skew. - # See here: https://github.com/GSA-TTS/FAC/pull/1968#pullrequestreview-1601224051 skew=10, ) except Exception as err: From 98ab0180a4862a30458821f83a09bc82204954c1 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 30 Aug 2023 13:14:53 -0600 Subject: [PATCH 081/110] Updated Pauls userid --- src/registrar/fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/fixtures.py b/src/registrar/fixtures.py index f7168dbf3..58a03045b 100644 --- a/src/registrar/fixtures.py +++ b/src/registrar/fixtures.py @@ -63,7 +63,7 @@ class UserFixture: "last_name": "Adkinson", }, { - "username": "bb21f687-c773-4df3-9243-111cfd4c0be4", + "username": "2bf518c2-485a-4c42-ab1a-f5a8b0a08484", "first_name": "Paul", "last_name": "Kuykendall", }, From a564fe017c1b3c51be87a078be844ac0e042984e Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Wed, 30 Aug 2023 15:15:43 -0400 Subject: [PATCH 082/110] skew fix --- src/djangooidc/oidc.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/djangooidc/oidc.py b/src/djangooidc/oidc.py index 26eb54d3d..9907e4ee4 100644 --- a/src/djangooidc/oidc.py +++ b/src/djangooidc/oidc.py @@ -238,6 +238,10 @@ class Client(oic.Client): "client_secret": self.client_secret, }, authn_method=self.registration_response["token_endpoint_auth_method"], + # There is a time desync issue between login.gov and cloud, + # this addresses that by adding a clock skew. + # See here: https://github.com/GSA-TTS/FAC/pull/1968#pullrequestreview-1601224051 + skew=10, ) except Exception as err: logger.error(err) From aa41df0ca465703587a7a17b3be7739faf84d484 Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Wed, 30 Aug 2023 15:21:12 -0400 Subject: [PATCH 083/110] fixed lint feedback --- src/djangooidc/oidc.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/djangooidc/oidc.py b/src/djangooidc/oidc.py index 9907e4ee4..0d196194d 100644 --- a/src/djangooidc/oidc.py +++ b/src/djangooidc/oidc.py @@ -240,7 +240,6 @@ class Client(oic.Client): authn_method=self.registration_response["token_endpoint_auth_method"], # There is a time desync issue between login.gov and cloud, # this addresses that by adding a clock skew. - # See here: https://github.com/GSA-TTS/FAC/pull/1968#pullrequestreview-1601224051 skew=10, ) except Exception as err: From 56eda2922563a01109182454455d560861f4f4a8 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 30 Aug 2023 13:25:21 -0600 Subject: [PATCH 084/110] Linting for recent changes --- src/registrar/views/utility/mixins.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/registrar/views/utility/mixins.py b/src/registrar/views/utility/mixins.py index 1fd21b18e..aff82699a 100644 --- a/src/registrar/views/utility/mixins.py +++ b/src/registrar/views/utility/mixins.py @@ -43,14 +43,16 @@ class DomainPermission(PermissionsLoginMixin): # user needs to have a role on the domain, # and user cannot be restricted - if UserDomainRole.objects.filter( - user=self.request.user, domain__id=pk - ).exists() and not self.request.user.is_restricted(): + if ( + UserDomainRole.objects.filter( + user=self.request.user, domain__id=pk + ).exists() + and not self.request.user.is_restricted() + ): return True elif self.request.user.is_restricted(): return False - # ticket 806 requested_domain = None if DomainInformation.objects.filter(id=pk).exists(): From 4df52e5a45b968a39dafcadc01de4c7769ed0e65 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Wed, 30 Aug 2023 18:41:43 -0400 Subject: [PATCH 085/110] Change ul and li to p for django admin object-tools links --- src/registrar/templates/admin/change_form.html | 11 +++++++++++ .../templates/admin/change_form_object_tools.html | 7 +++++++ src/registrar/templates/admin/change_list.html | 8 ++++++++ .../templates/admin/change_list_object_tools.html | 10 ++++++++++ 4 files changed, 36 insertions(+) create mode 100644 src/registrar/templates/admin/change_form.html create mode 100644 src/registrar/templates/admin/change_form_object_tools.html create mode 100644 src/registrar/templates/admin/change_list_object_tools.html diff --git a/src/registrar/templates/admin/change_form.html b/src/registrar/templates/admin/change_form.html new file mode 100644 index 000000000..dbb4c6a4a --- /dev/null +++ b/src/registrar/templates/admin/change_form.html @@ -0,0 +1,11 @@ +{% extends "admin/change_form.html" %} + +{% block object-tools %} +{% if change and not is_popup %} +

+ {% block object-tools-items %} + {{ block.super }} + {% endblock %} +

+{% endif %} +{% endblock %} \ No newline at end of file diff --git a/src/registrar/templates/admin/change_form_object_tools.html b/src/registrar/templates/admin/change_form_object_tools.html new file mode 100644 index 000000000..8e462c0c3 --- /dev/null +++ b/src/registrar/templates/admin/change_form_object_tools.html @@ -0,0 +1,7 @@ +{% load i18n admin_urls %} + +{% block object-tools-items %} +{% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %} +{% translate "History" %} +{% if has_absolute_url %}{% translate "View on site" %}{% endif %} +{% endblock %} \ No newline at end of file diff --git a/src/registrar/templates/admin/change_list.html b/src/registrar/templates/admin/change_list.html index 1026e7d60..535493815 100644 --- a/src/registrar/templates/admin/change_list.html +++ b/src/registrar/templates/admin/change_list.html @@ -24,3 +24,11 @@ {% endif %} {% endblock %} + +{% block object-tools %} +

+ {% block object-tools-items %} + {{ block.super }} + {% endblock %} +

+{% endblock %} \ No newline at end of file diff --git a/src/registrar/templates/admin/change_list_object_tools.html b/src/registrar/templates/admin/change_list_object_tools.html new file mode 100644 index 000000000..e0f67acea --- /dev/null +++ b/src/registrar/templates/admin/change_list_object_tools.html @@ -0,0 +1,10 @@ +{% load i18n admin_urls %} + +{% block object-tools-items %} + {% if has_add_permission %} + {% url cl.opts|admin_urlname:'add' as add_url %} + + {% blocktranslate with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktranslate %} + + {% endif %} +{% endblock %} \ No newline at end of file From c9269a56e000baf6df5f9e4c844e4de7dad862ec Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Wed, 30 Aug 2023 19:02:59 -0400 Subject: [PATCH 086/110] Comment out rejected fixture --- src/registrar/fixtures.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/registrar/fixtures.py b/src/registrar/fixtures.py index 2a1df7d0d..e288a21d4 100644 --- a/src/registrar/fixtures.py +++ b/src/registrar/fixtures.py @@ -268,10 +268,11 @@ class DomainApplicationFixture: "status": "action needed", "organization_name": "Example - Action Needed", }, - { - "status": "rejected", - "organization_name": "Example - Rejected", - }, + # Pa11y does not like this fixture + # { + # "status": "rejected", + # "organization_name": "Example - Rejected", + # }, ] @classmethod From 2f77ddf6de44961899feb58bc50df86fc202953d Mon Sep 17 00:00:00 2001 From: Gaby Disarli Date: Wed, 30 Aug 2023 17:12:23 -0700 Subject: [PATCH 087/110] Add myslef to fixtures.py --- src/registrar/fixtures.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/registrar/fixtures.py b/src/registrar/fixtures.py index 2a1df7d0d..5fbf3454a 100644 --- a/src/registrar/fixtures.py +++ b/src/registrar/fixtures.py @@ -116,6 +116,12 @@ class UserFixture: "username": "5dc6c9a6-61d9-42b4-ba54-4beff28bac3c", "first_name": "David-Analyst", "last_name": "Kennedy-Analyst", + } + { + "username": "0eb6f326-a3d4-410f-a521-aa4c1fad4e47", + "first_name": "Gaby-Analyst", + "last_name": "DiSarli-Analyst", + "email": "gaby@truss.works" }, ] From c94458dcd7c4cf0214a21609aac166efc57c40c7 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 31 Aug 2023 08:23:16 -0600 Subject: [PATCH 088/110] Added missing comma --- src/registrar/templates/domain_base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/templates/domain_base.html b/src/registrar/templates/domain_base.html index 78c493a9c..d2870a82c 100644 --- a/src/registrar/templates/domain_base.html +++ b/src/registrar/templates/domain_base.html @@ -27,7 +27,7 @@

Attention!

- You are making changes to a registrant’s domain. When finished making changes, close this tab and inform the registrant of your updates + You are making changes to a registrant’s domain. When finished making changes, close this tab and inform the registrant of your updates.

From f4df0a2bc498a453273b62ffa6655a1c51b2f177 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Thu, 31 Aug 2023 11:26:47 -0400 Subject: [PATCH 089/110] Implement isActive method on Domain --- src/registrar/models/domain.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index a7e46f888..5421c13cc 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -337,6 +337,9 @@ class Domain(TimeStampedModel, DomainHelper): protected=True, # cannot change state directly, particularly in Django admin help_text="Very basic info about the lifecycle of this domain object", ) + + def isActive(self): + return self.state == Domain.State.CREATED # ForeignKey on UserDomainRole creates a "permissions" member for # all of the user-roles that are in place for this domain From 0a5d4e5f77c04d9bdb88539166e4682888824bac Mon Sep 17 00:00:00 2001 From: Gabriela DiSarli <107440934+gabydisarli@users.noreply.github.com> Date: Thu, 31 Aug 2023 09:38:23 -0700 Subject: [PATCH 090/110] Update src/registrar/fixtures.py fixing comma, thanks Rebecca! Co-authored-by: Rebecca H. --- src/registrar/fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/fixtures.py b/src/registrar/fixtures.py index 5fbf3454a..4351d9b93 100644 --- a/src/registrar/fixtures.py +++ b/src/registrar/fixtures.py @@ -121,7 +121,7 @@ class UserFixture: "username": "0eb6f326-a3d4-410f-a521-aa4c1fad4e47", "first_name": "Gaby-Analyst", "last_name": "DiSarli-Analyst", - "email": "gaby@truss.works" + "email": "gaby@truss.works", }, ] From 5dbbf59c1340a8ba3cf78815f9107d00bb92304f Mon Sep 17 00:00:00 2001 From: Gabriela DiSarli <107440934+gabydisarli@users.noreply.github.com> Date: Thu, 31 Aug 2023 09:38:44 -0700 Subject: [PATCH 091/110] Update src/registrar/fixtures.py Co-authored-by: rachidatecs <107004823+rachidatecs@users.noreply.github.com> --- src/registrar/fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/fixtures.py b/src/registrar/fixtures.py index 4351d9b93..2685fc72f 100644 --- a/src/registrar/fixtures.py +++ b/src/registrar/fixtures.py @@ -116,7 +116,7 @@ class UserFixture: "username": "5dc6c9a6-61d9-42b4-ba54-4beff28bac3c", "first_name": "David-Analyst", "last_name": "Kennedy-Analyst", - } + }, { "username": "0eb6f326-a3d4-410f-a521-aa4c1fad4e47", "first_name": "Gaby-Analyst", From 2708b3baea5f34216e9cdec09e825ad06ee30d92 Mon Sep 17 00:00:00 2001 From: Gaby Disarli Date: Thu, 31 Aug 2023 10:29:08 -0700 Subject: [PATCH 092/110] fixed formatting error --- src/registrar/fixtures.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/registrar/fixtures.py b/src/registrar/fixtures.py index 2685fc72f..3fea25033 100644 --- a/src/registrar/fixtures.py +++ b/src/registrar/fixtures.py @@ -118,10 +118,10 @@ class UserFixture: "last_name": "Kennedy-Analyst", }, { - "username": "0eb6f326-a3d4-410f-a521-aa4c1fad4e47", - "first_name": "Gaby-Analyst", - "last_name": "DiSarli-Analyst", - "email": "gaby@truss.works", + "username": "0eb6f326-a3d4-410f-a521-aa4c1fad4e47", + "first_name": "Gaby-Analyst", + "last_name": "DiSarli-Analyst", + "email": "gaby@truss.works", }, ] From 3cfa7f8b034854ed529f1da8d9809138328ebd0d Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 31 Aug 2023 13:15:40 -0600 Subject: [PATCH 093/110] Changed permission flow for mixins / Made logging a bit more detailed --- src/registrar/views/domain.py | 21 +++-- src/registrar/views/utility/mixins.py | 92 +++++++++++-------- .../views/utility/permission_views.py | 24 +++-- 3 files changed, 82 insertions(+), 55 deletions(-) diff --git a/src/registrar/views/domain.py b/src/registrar/views/domain.py index 559ea4517..dd4b9d791 100644 --- a/src/registrar/views/domain.py +++ b/src/registrar/views/domain.py @@ -83,10 +83,10 @@ class DomainOrgNameAddressView(DomainPermissionView, FormMixin): # Q: Is there a more efficent way to do this? # It would be ideal if we didn't have to repeat this. if self.request.user.is_staff or self.request.user.is_superuser: - changes = {field: form.cleaned_data[field] for field in form.changed_data} # if they are editing from an '/admin' redirect, log their actions + changes = {field: form.cleaned_data[field] for field in form.changed_data} self.log_analyst_form_actions( - self.form_class.__name__, self.get_object().domain_info, changes + self.form_class.__name__, self.get_object().domain_info, changes, obj=self.get_object() ) # superclass has the redirect @@ -134,8 +134,9 @@ class DomainAuthorizingOfficialView(DomainPermissionView, FormMixin): if self.request.user.is_staff or self.request.user.is_superuser: # if they are editing from an '/admin' redirect, log their actions + changes = {field: form.cleaned_data[field] for field in form.changed_data} self.log_analyst_form_actions( - self.form_class.__name__, self.get_object().domain_info + self.form_class.__name__, self.get_object().domain_info, changes, obj=self.get_object() ) # superclass has the redirect @@ -207,8 +208,9 @@ class DomainNameserversView(DomainPermissionView, FormMixin): if self.request.user.is_staff or self.request.user.is_superuser: # if they are editing from an '/admin' redirect, log their actions + changes = {field: formset.cleaned_data[field] for field in formset.changed_data} self.log_analyst_form_actions( - self.form_class.__name__, self.get_object().domain_info + self.form_class.__name__, self.get_object().domain_info, changes, obj=self.get_object() ) # superclass has the redirect @@ -254,8 +256,9 @@ class DomainYourContactInformationView(DomainPermissionView, FormMixin): if self.request.user.is_staff or self.request.user.is_superuser: # if they are editing from an '/admin' redirect, log their actions + changes = {field: form.cleaned_data[field] for field in form.changed_data} self.log_analyst_form_actions( - self.form_class.__name__, self.get_object().domain_info + self.form_class.__name__, self.get_object().domain_info, changes, obj=self.get_object() ) # superclass has the redirect @@ -306,8 +309,9 @@ class DomainSecurityEmailView(DomainPermissionView, FormMixin): if self.request.user.is_staff or self.request.user.is_superuser: # if they are editing from an '/admin' redirect, log their actions + changes = {field: form.cleaned_data[field] for field in form.changed_data} self.log_analyst_form_actions( - self.form_class.__name__, self.get_object().domain_info + self.form_class.__name__, self.get_object().domain_info, changes, obj=self.get_object() ) # superclass has the redirect @@ -388,7 +392,7 @@ class DomainAddUserView(DomainPermissionView, FormMixin): if self.request.user.is_staff or self.request.user.is_superuser: # if they are editing from an '/admin' redirect, log their actions self.log_analyst_form_actions( - self.form_class.__name__, self.get_object().domain_info + self.form_class.__name__, self.get_object().domain_info, obj=self.get_object() ) return redirect(self.get_success_url()) @@ -414,8 +418,9 @@ class DomainAddUserView(DomainPermissionView, FormMixin): if self.request.user.is_staff or self.request.user.is_superuser: # if they are editing from an '/admin' redirect, log their actions + changes = {field: form.cleaned_data[field] for field in form.changed_data} self.log_analyst_form_actions( - self.form_class.__name__, self.get_object().domain_info + self.form_class.__name__, self.get_object().domain_info, changes, obj=self.get_object() ) return redirect(self.get_success_url()) diff --git a/src/registrar/views/utility/mixins.py b/src/registrar/views/utility/mixins.py index aff82699a..9a19d78d1 100644 --- a/src/registrar/views/utility/mixins.py +++ b/src/registrar/views/utility/mixins.py @@ -36,27 +36,55 @@ class DomainPermission(PermissionsLoginMixin): if not self.request.user.is_authenticated: return False + if self.request.user.is_restricted(): + return False + pk = self.kwargs["pk"] # If pk is none then something went very wrong... if pk is None: raise ValueError("Primary key is None") + # ticket 806 + if self.can_access_other_user_domains(pk): + return True + # user needs to have a role on the domain, # and user cannot be restricted - if ( - UserDomainRole.objects.filter( - user=self.request.user, domain__id=pk - ).exists() - and not self.request.user.is_restricted() - ): - return True - elif self.request.user.is_restricted(): + if not UserDomainRole.objects.filter( + user=self.request.user, domain__id=pk + ).exists(): return False - # ticket 806 - requested_domain = None - if DomainInformation.objects.filter(id=pk).exists(): - requested_domain = DomainInformation.objects.get(id=pk) + # if we need to check more about the nature of role, do it here. + return True + + def can_access_other_user_domains(self, pk): + """Checks to see if an authorized user (staff or superuser) + can access a domain that they did not create or was invited to. + """ + + # Check if the user is permissioned... + user_is_analyst_or_superuser = ( + self.request.user.is_staff or self.request.user.is_superuser + ) + logger.debug(f"is auth {user_is_analyst_or_superuser}") + + if not user_is_analyst_or_superuser: + return False + + # Check if the user is attempting a valid edit action. + # In other words, if the analyst/admin did not click + # the 'Manage Domain' button in /admin, + # then they cannot access this page. + session = self.request.session + can_do_action = ( + "analyst_action" in session + and "analyst_action_location" in session + and session["analyst_action_location"] == pk + ) + logger.debug(f"can do {can_do_action}") + if not can_do_action: + return False # Analysts may manage domains, when they are in these statuses: valid_domain_statuses = [ @@ -64,37 +92,23 @@ class DomainPermission(PermissionsLoginMixin): DomainApplication.IN_REVIEW, DomainApplication.REJECTED, DomainApplication.ACTION_NEEDED, + # Edge case - some domains do not have + # a status or DomainInformation... aka a status of 'None'. + # It is necessary to access those to correct errors. + None, ] - # Check if the user is permissioned... - user_is_analyst_or_superuser = ( - self.request.user.is_staff or self.request.user.is_superuser - ) + requested_domain = None + if DomainInformation.objects.filter(id=pk).exists(): + requested_domain = DomainInformation.objects.get(id=pk) - session = self.request.session - # Check if the user is attempting a valid edit action. - can_do_action = ( - "analyst_action" in session - and "analyst_action_location" in session - and session["analyst_action_location"] == pk - ) + if not requested_domain.domain_application.status in valid_domain_statuses: + return False - # Edge case - some domains do not have - # a status or DomainInformation... aka a status of 'None' - # This checks that it has a status, before checking if it does - # Otherwise, analysts can edit these domains - if requested_domain is not None: - can_do_action = ( - can_do_action - and requested_domain.domain_application.status in valid_domain_statuses - ) - # If the valid session keys exist, if the user is permissioned, - # and if its in a valid status - if can_do_action and user_is_analyst_or_superuser: - return True - - # if we need to check more about the nature of role, do it here. - return False + # Valid session keys exist, + # the user is permissioned, + # and it is in a valid status + return True class DomainApplicationPermission(PermissionsLoginMixin): diff --git a/src/registrar/views/utility/permission_views.py b/src/registrar/views/utility/permission_views.py index a42238150..920ec6212 100644 --- a/src/registrar/views/utility/permission_views.py +++ b/src/registrar/views/utility/permission_views.py @@ -3,9 +3,9 @@ import abc # abstract base class from django.views.generic import DetailView, DeleteView, TemplateView - +from django.contrib.contenttypes.models import ContentType from registrar.models import Domain, DomainApplication, DomainInvitation - +from django.contrib.admin.models import LogEntry, CHANGE from .mixins import ( DomainPermission, @@ -48,7 +48,7 @@ class DomainPermissionView(DomainPermission, DetailView, abc.ABC): return context def log_analyst_form_actions( - self, form_class_name, printable_object_info, changes=None + self, form_class_name, printable_object_info, changes=None, obj=None ): """Generates a log for when key 'analyst_action' exists on the session. Follows this format: @@ -72,12 +72,20 @@ class DomainPermissionView(DomainPermission, DetailView, abc.ABC): # or 'copy', for instance. match action: case "edit": - # Q: do we want to be logging on every changed field? - # I could see that becoming spammy log-wise, - # but it may also be important. + if obj is not None: + content_type = ContentType.objects.get_for_model(obj) + LogEntry.objects.log_action( + user_id=self.request.user.id, + content_type_id=content_type.pk, + object_id=obj.id, + object_repr=str(obj), + action_flag=CHANGE, + ) + if changes is not None: - # Logs every change made to the domain field - # noqa for readability/format + # Logs every change made to the domain field. + # noqa for readability/format. + # Used to manually capture changes, if need be. for field, new_value in changes.items(): logger.info( f""" From 2c2d6dfaeb557e0b1cae67c186047f780cf73217 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Thu, 31 Aug 2023 17:09:13 -0400 Subject: [PATCH 094/110] lint --- src/registrar/models/domain.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 5421c13cc..59563d3d8 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -337,9 +337,9 @@ class Domain(TimeStampedModel, DomainHelper): protected=True, # cannot change state directly, particularly in Django admin help_text="Very basic info about the lifecycle of this domain object", ) - + def isActive(self): - return self.state == Domain.State.CREATED + return self.state == Domain.State.CREATED # ForeignKey on UserDomainRole creates a "permissions" member for # all of the user-roles that are in place for this domain From 9ca424d30c5a5937132d4e86ef43412750240ccf Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Fri, 1 Sep 2023 14:20:06 -0400 Subject: [PATCH 095/110] Remove load fixtures signal on migrate, add load command to docker compose, add DEBUG env check to load.py --- src/docker-compose.yml | 1 + src/registrar/management/commands/load.py | 14 +++++++++----- src/registrar/signals.py | 10 ---------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/docker-compose.yml b/src/docker-compose.yml index 82642bc93..786f4c24b 100644 --- a/src/docker-compose.yml +++ b/src/docker-compose.yml @@ -54,6 +54,7 @@ services: # command: "python" command: > bash -c " python manage.py migrate && + python manage.py load && python manage.py runserver 0.0.0.0:8080" db: diff --git a/src/registrar/management/commands/load.py b/src/registrar/management/commands/load.py index e48d3f211..69e7e9ec8 100644 --- a/src/registrar/management/commands/load.py +++ b/src/registrar/management/commands/load.py @@ -2,6 +2,7 @@ import logging from django.core.management.base import BaseCommand from auditlog.context import disable_auditlog # type: ignore +from django.conf import settings from registrar.fixtures import UserFixture, DomainApplicationFixture, DomainFixture @@ -12,8 +13,11 @@ class Command(BaseCommand): def handle(self, *args, **options): # django-auditlog has some bugs with fixtures # https://github.com/jazzband/django-auditlog/issues/17 - with disable_auditlog(): - UserFixture.load() - DomainApplicationFixture.load() - DomainFixture.load() - logger.info("All fixtures loaded.") + if settings.DEBUG: + with disable_auditlog(): + UserFixture.load() + DomainApplicationFixture.load() + DomainFixture.load() + logger.info("All fixtures loaded.") + else: + logger.warn("Refusing to load fixture data in a non DEBUG env") diff --git a/src/registrar/signals.py b/src/registrar/signals.py index 62c5873f0..45c4c4f97 100644 --- a/src/registrar/signals.py +++ b/src/registrar/signals.py @@ -1,6 +1,5 @@ import logging -from django.conf import settings from django.core.management import call_command from django.db.models.signals import post_save, post_migrate from django.dispatch import receiver @@ -56,12 +55,3 @@ def handle_profile(sender, instance, **kwargs): f" Picking #{contacts[0].id} for User #{instance.id}." ) - -@receiver(post_migrate) -def handle_loaddata(**kwargs): - """Attempt to load test fixtures when in DEBUG mode.""" - if settings.DEBUG: - try: - call_command("load") - except Exception as e: - logger.warning(e) From cc9745ba68fed94f980d6adea5a911dca5f33e00 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Fri, 1 Sep 2023 14:40:59 -0400 Subject: [PATCH 096/110] lint --- src/registrar/signals.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/registrar/signals.py b/src/registrar/signals.py index 45c4c4f97..9a1787227 100644 --- a/src/registrar/signals.py +++ b/src/registrar/signals.py @@ -54,4 +54,3 @@ def handle_profile(sender, instance, **kwargs): "There are multiple Contacts with the same email address." f" Picking #{contacts[0].id} for User #{instance.id}." ) - From 72a2e989844ff6ba81e13870085007dc29bc2ff2 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Fri, 1 Sep 2023 14:51:58 -0400 Subject: [PATCH 097/110] lint --- src/registrar/signals.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/registrar/signals.py b/src/registrar/signals.py index 9a1787227..4e7768ef4 100644 --- a/src/registrar/signals.py +++ b/src/registrar/signals.py @@ -1,7 +1,6 @@ import logging -from django.core.management import call_command -from django.db.models.signals import post_save, post_migrate +from django.db.models.signals import post_save from django.dispatch import receiver from .models import User, Contact From 410cbd1130d787ba8d4f9acc3995d6e4435477d1 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Fri, 1 Sep 2023 15:11:17 -0400 Subject: [PATCH 098/110] Refactor to make more resilient in case of more than one item in change_form_object_tools, add comments --- src/registrar/fixtures.py | 9 ++++----- .../templates/admin/change_form.html | 5 +++-- .../admin/change_form_object_tools.html | 19 ++++++++++++++++--- .../templates/admin/change_list.html | 5 +++-- .../admin/change_list_object_tools.html | 11 +++++++---- 5 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/registrar/fixtures.py b/src/registrar/fixtures.py index e288a21d4..2a1df7d0d 100644 --- a/src/registrar/fixtures.py +++ b/src/registrar/fixtures.py @@ -268,11 +268,10 @@ class DomainApplicationFixture: "status": "action needed", "organization_name": "Example - Action Needed", }, - # Pa11y does not like this fixture - # { - # "status": "rejected", - # "organization_name": "Example - Rejected", - # }, + { + "status": "rejected", + "organization_name": "Example - Rejected", + }, ] @classmethod diff --git a/src/registrar/templates/admin/change_form.html b/src/registrar/templates/admin/change_form.html index dbb4c6a4a..2df780aaa 100644 --- a/src/registrar/templates/admin/change_form.html +++ b/src/registrar/templates/admin/change_form.html @@ -1,11 +1,12 @@ {% extends "admin/change_form.html" %} +{% comment %} Replace the Django ul markup with a div. We'll edit the child markup accordingly in form_list_object_tools {% endcomment %} {% block object-tools %} {% if change and not is_popup %} -

+

{% block object-tools-items %} {{ block.super }} {% endblock %} -

+
{% endif %} {% endblock %} \ No newline at end of file diff --git a/src/registrar/templates/admin/change_form_object_tools.html b/src/registrar/templates/admin/change_form_object_tools.html index 8e462c0c3..28c655bbc 100644 --- a/src/registrar/templates/admin/change_form_object_tools.html +++ b/src/registrar/templates/admin/change_form_object_tools.html @@ -1,7 +1,20 @@ {% load i18n admin_urls %} +{% comment %} Replace li with p for more semantic HTML if we have a single child {% endcomment %} {% block object-tools-items %} -{% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %} -{% translate "History" %} -{% if has_absolute_url %}{% translate "View on site" %}{% endif %} + {% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %} + {% if has_absolute_url %} + + {% else %} +

+ {% translate "History" %} +

+ {% endif %} {% endblock %} \ No newline at end of file diff --git a/src/registrar/templates/admin/change_list.html b/src/registrar/templates/admin/change_list.html index 535493815..4a58a4b7e 100644 --- a/src/registrar/templates/admin/change_list.html +++ b/src/registrar/templates/admin/change_list.html @@ -25,10 +25,11 @@ {% endblock %} +{% comment %} Replace the Django ul markup with a div. We'll replace the li with a p in change_list_object_tools {% endcomment %} {% block object-tools %} -

+

{% block object-tools-items %} {{ block.super }} {% endblock %} -

+
{% endblock %} \ No newline at end of file diff --git a/src/registrar/templates/admin/change_list_object_tools.html b/src/registrar/templates/admin/change_list_object_tools.html index e0f67acea..9a046b4bb 100644 --- a/src/registrar/templates/admin/change_list_object_tools.html +++ b/src/registrar/templates/admin/change_list_object_tools.html @@ -1,10 +1,13 @@ {% load i18n admin_urls %} +{% comment %} Replace li with p for more semantic HTML {% endcomment %} {% block object-tools-items %} {% if has_add_permission %} - {% url cl.opts|admin_urlname:'add' as add_url %} - - {% blocktranslate with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktranslate %} - +

+ {% url cl.opts|admin_urlname:'add' as add_url %} + + {% blocktranslate with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktranslate %} + +

{% endif %} {% endblock %} \ No newline at end of file From d2d5e291dc53e6658a23fa6af49791189e92444d Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Fri, 1 Sep 2023 15:37:13 -0400 Subject: [PATCH 099/110] Fix typo in comment --- src/registrar/templates/admin/change_form.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/templates/admin/change_form.html b/src/registrar/templates/admin/change_form.html index 2df780aaa..e0f9ae1a4 100644 --- a/src/registrar/templates/admin/change_form.html +++ b/src/registrar/templates/admin/change_form.html @@ -1,6 +1,6 @@ {% extends "admin/change_form.html" %} -{% comment %} Replace the Django ul markup with a div. We'll edit the child markup accordingly in form_list_object_tools {% endcomment %} +{% comment %} Replace the Django ul markup with a div. We'll edit the child markup accordingly in change_form_object_tools {% endcomment %} {% block object-tools %} {% if change and not is_popup %}
From 11bed39dd8a0e2d2d209027d755dc005f75f0ca5 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Sep 2023 14:16:44 -0600 Subject: [PATCH 100/110] Remove custom logging / cleanup --- src/registrar/assets/js/get-gov-admin.js | 17 ++++-- src/registrar/views/domain.py | 49 +---------------- src/registrar/views/utility/mixins.py | 8 +-- .../views/utility/permission_views.py | 55 ------------------- 4 files changed, 16 insertions(+), 113 deletions(-) diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js index d7d589214..d193b2fb6 100644 --- a/src/registrar/assets/js/get-gov-admin.js +++ b/src/registrar/assets/js/get-gov-admin.js @@ -27,11 +27,16 @@ function openInNewTab(el, removeAttribute = false){ * Currently only appends target="_blank" to the domain_form object, * but this can be expanded. */ -(function prepareDjangoAdmin(){ - let domainFormElement = document.getElementById("domain_form"); - let domainSubmitButton = document.getElementById("manageDomainSubmitButton"); - if(domainSubmitButton && domainFormElement){ - domainSubmitButton.addEventListener("mouseover", () => openInNewTab(domainFormElement, true)); - domainSubmitButton.addEventListener("mouseout", () => openInNewTab(domainFormElement, false)); +(function (){ + // Keep scope tight and allow for scalability + function prepareDjangoAdmin() { + let domainFormElement = document.getElementById("domain_form"); + let domainSubmitButton = document.getElementById("manageDomainSubmitButton"); + if(domainSubmitButton && domainFormElement){ + domainSubmitButton.addEventListener("mouseover", () => openInNewTab(domainFormElement, true)); + domainSubmitButton.addEventListener("mouseout", () => openInNewTab(domainFormElement, false)); + } } + + prepareDjangoAdmin(); })(); \ No newline at end of file diff --git a/src/registrar/views/domain.py b/src/registrar/views/domain.py index dd4b9d791..29d06da39 100644 --- a/src/registrar/views/domain.py +++ b/src/registrar/views/domain.py @@ -80,15 +80,6 @@ class DomainOrgNameAddressView(DomainPermissionView, FormMixin): self.request, "The organization name and mailing address has been updated." ) - # Q: Is there a more efficent way to do this? - # It would be ideal if we didn't have to repeat this. - if self.request.user.is_staff or self.request.user.is_superuser: - # if they are editing from an '/admin' redirect, log their actions - changes = {field: form.cleaned_data[field] for field in form.changed_data} - self.log_analyst_form_actions( - self.form_class.__name__, self.get_object().domain_info, changes, obj=self.get_object() - ) - # superclass has the redirect return super().form_valid(form) @@ -132,12 +123,7 @@ class DomainAuthorizingOfficialView(DomainPermissionView, FormMixin): self.request, "The authorizing official for this domain has been updated." ) - if self.request.user.is_staff or self.request.user.is_superuser: - # if they are editing from an '/admin' redirect, log their actions - changes = {field: form.cleaned_data[field] for field in form.changed_data} - self.log_analyst_form_actions( - self.form_class.__name__, self.get_object().domain_info, changes, obj=self.get_object() - ) + # superclass has the redirect return super().form_valid(form) @@ -206,13 +192,6 @@ class DomainNameserversView(DomainPermissionView, FormMixin): self.request, "The name servers for this domain have been updated." ) - if self.request.user.is_staff or self.request.user.is_superuser: - # if they are editing from an '/admin' redirect, log their actions - changes = {field: formset.cleaned_data[field] for field in formset.changed_data} - self.log_analyst_form_actions( - self.form_class.__name__, self.get_object().domain_info, changes, obj=self.get_object() - ) - # superclass has the redirect return super().form_valid(formset) @@ -254,13 +233,6 @@ class DomainYourContactInformationView(DomainPermissionView, FormMixin): self.request, "Your contact information for this domain has been updated." ) - if self.request.user.is_staff or self.request.user.is_superuser: - # if they are editing from an '/admin' redirect, log their actions - changes = {field: form.cleaned_data[field] for field in form.changed_data} - self.log_analyst_form_actions( - self.form_class.__name__, self.get_object().domain_info, changes, obj=self.get_object() - ) - # superclass has the redirect return super().form_valid(form) @@ -307,13 +279,6 @@ class DomainSecurityEmailView(DomainPermissionView, FormMixin): self.request, "The security email for this domain have been updated." ) - if self.request.user.is_staff or self.request.user.is_superuser: - # if they are editing from an '/admin' redirect, log their actions - changes = {field: form.cleaned_data[field] for field in form.changed_data} - self.log_analyst_form_actions( - self.form_class.__name__, self.get_object().domain_info, changes, obj=self.get_object() - ) - # superclass has the redirect return redirect(self.get_success_url()) @@ -389,11 +354,7 @@ class DomainAddUserView(DomainPermissionView, FormMixin): messages.success( self.request, f"Invited {email_address} to this domain." ) - if self.request.user.is_staff or self.request.user.is_superuser: - # if they are editing from an '/admin' redirect, log their actions - self.log_analyst_form_actions( - self.form_class.__name__, self.get_object().domain_info, obj=self.get_object() - ) + return redirect(self.get_success_url()) def form_valid(self, form): @@ -416,12 +377,6 @@ class DomainAddUserView(DomainPermissionView, FormMixin): messages.success(self.request, f"Added user {requested_email}.") - if self.request.user.is_staff or self.request.user.is_superuser: - # if they are editing from an '/admin' redirect, log their actions - changes = {field: form.cleaned_data[field] for field in form.changed_data} - self.log_analyst_form_actions( - self.form_class.__name__, self.get_object().domain_info, changes, obj=self.get_object() - ) return redirect(self.get_success_url()) diff --git a/src/registrar/views/utility/mixins.py b/src/registrar/views/utility/mixins.py index 9a19d78d1..57bb97be6 100644 --- a/src/registrar/views/utility/mixins.py +++ b/src/registrar/views/utility/mixins.py @@ -10,6 +10,7 @@ from registrar.models import ( ) import logging + logger = logging.getLogger(__name__) @@ -44,12 +45,10 @@ class DomainPermission(PermissionsLoginMixin): if pk is None: raise ValueError("Primary key is None") - # ticket 806 if self.can_access_other_user_domains(pk): return True - # user needs to have a role on the domain, - # and user cannot be restricted + # user needs to have a role on the domain if not UserDomainRole.objects.filter( user=self.request.user, domain__id=pk ).exists(): @@ -67,7 +66,6 @@ class DomainPermission(PermissionsLoginMixin): user_is_analyst_or_superuser = ( self.request.user.is_staff or self.request.user.is_superuser ) - logger.debug(f"is auth {user_is_analyst_or_superuser}") if not user_is_analyst_or_superuser: return False @@ -82,7 +80,7 @@ class DomainPermission(PermissionsLoginMixin): and "analyst_action_location" in session and session["analyst_action_location"] == pk ) - logger.debug(f"can do {can_do_action}") + if not can_do_action: return False diff --git a/src/registrar/views/utility/permission_views.py b/src/registrar/views/utility/permission_views.py index 920ec6212..aa4c79690 100644 --- a/src/registrar/views/utility/permission_views.py +++ b/src/registrar/views/utility/permission_views.py @@ -47,61 +47,6 @@ class DomainPermissionView(DomainPermission, DetailView, abc.ABC): return context - def log_analyst_form_actions( - self, form_class_name, printable_object_info, changes=None, obj=None - ): - """Generates a log for when key 'analyst_action' exists on the session. - Follows this format: - - for field, new_value in changes.items(): - "{user_type} '{self.request.user}' - set field '{field}': '{new_value}' - under class '{form_class_name}' - in domain '{printable_object_info}'" - """ - if "analyst_action" in self.request.session: - action = self.request.session["analyst_action"] - - user_type = "Analyst" - if self.request.user.is_superuser: - user_type = "Superuser" - - # Template for potential future expansion, - # in the event we want more logging granularity. - # Could include things such as 'view' - # or 'copy', for instance. - match action: - case "edit": - if obj is not None: - content_type = ContentType.objects.get_for_model(obj) - LogEntry.objects.log_action( - user_id=self.request.user.id, - content_type_id=content_type.pk, - object_id=obj.id, - object_repr=str(obj), - action_flag=CHANGE, - ) - - if changes is not None: - # Logs every change made to the domain field. - # noqa for readability/format. - # Used to manually capture changes, if need be. - for field, new_value in changes.items(): - logger.info( - f""" - An analyst or superuser made alterations to a domain: - {user_type} '{self.request.user}' - set field '{field}': '{new_value}' - under class '{form_class_name}' - in domain '{printable_object_info}' - """ # noqa - ) - else: - # noqa here as breaking this up further leaves it hard to read - logger.info( - f"{user_type} {self.request.user} edited {form_class_name} in {printable_object_info}" # noqa - ) - # Abstract property enforces NotImplementedError on an attribute. @property @abc.abstractmethod From 6617ecb8a0cb4901458d67ee6583d0e7d5a0a985 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Fri, 1 Sep 2023 17:39:58 -0400 Subject: [PATCH 101/110] concatenate the value + extracted text for both label for attribute and checkbox id attribute --- src/registrar/templates/admin/change_list_results.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/registrar/templates/admin/change_list_results.html b/src/registrar/templates/admin/change_list_results.html index 831350888..1d0c70c30 100644 --- a/src/registrar/templates/admin/change_list_results.html +++ b/src/registrar/templates/admin/change_list_results.html @@ -60,12 +60,11 @@ Load our custom filters to extract info from the django generated markup. {{ result.form.non_field_errors }} {% endif %} - {% with result_value=result.0|extract_value %} {% with result_label=result.1|extract_a_text %} - - + + {% endwith %} {% endwith %} From 7a5438e3d000834952241fec2d32e019e7a7b6ce Mon Sep 17 00:00:00 2001 From: rachidatecs <107004823+rachidatecs@users.noreply.github.com> Date: Tue, 5 Sep 2023 11:59:33 -0400 Subject: [PATCH 102/110] Update src/registrar/templates/admin/change_list_results.html Co-authored-by: Neil MartinsenBurrell --- src/registrar/templates/admin/change_list_results.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/templates/admin/change_list_results.html b/src/registrar/templates/admin/change_list_results.html index 1d0c70c30..4ced73eb3 100644 --- a/src/registrar/templates/admin/change_list_results.html +++ b/src/registrar/templates/admin/change_list_results.html @@ -63,8 +63,8 @@ Load our custom filters to extract info from the django generated markup. {% with result_value=result.0|extract_value %} {% with result_label=result.1|extract_a_text %} - - + + {% endwith %} {% endwith %} From 6d2b397a1bf71be3695c80003a07909e7b3428b5 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Sep 2023 14:07:35 -0600 Subject: [PATCH 103/110] Linter fixes --- src/registrar/views/domain.py | 3 --- src/registrar/views/utility/mixins.py | 2 +- src/registrar/views/utility/permission_views.py | 2 -- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/registrar/views/domain.py b/src/registrar/views/domain.py index 29d06da39..f945bc443 100644 --- a/src/registrar/views/domain.py +++ b/src/registrar/views/domain.py @@ -123,8 +123,6 @@ class DomainAuthorizingOfficialView(DomainPermissionView, FormMixin): self.request, "The authorizing official for this domain has been updated." ) - - # superclass has the redirect return super().form_valid(form) @@ -377,7 +375,6 @@ class DomainAddUserView(DomainPermissionView, FormMixin): messages.success(self.request, f"Added user {requested_email}.") - return redirect(self.get_success_url()) diff --git a/src/registrar/views/utility/mixins.py b/src/registrar/views/utility/mixins.py index 57bb97be6..fd58b3475 100644 --- a/src/registrar/views/utility/mixins.py +++ b/src/registrar/views/utility/mixins.py @@ -100,7 +100,7 @@ class DomainPermission(PermissionsLoginMixin): if DomainInformation.objects.filter(id=pk).exists(): requested_domain = DomainInformation.objects.get(id=pk) - if not requested_domain.domain_application.status in valid_domain_statuses: + if requested_domain.domain_application.status not in valid_domain_statuses: return False # Valid session keys exist, diff --git a/src/registrar/views/utility/permission_views.py b/src/registrar/views/utility/permission_views.py index aa4c79690..417ee8417 100644 --- a/src/registrar/views/utility/permission_views.py +++ b/src/registrar/views/utility/permission_views.py @@ -3,9 +3,7 @@ import abc # abstract base class from django.views.generic import DetailView, DeleteView, TemplateView -from django.contrib.contenttypes.models import ContentType from registrar.models import Domain, DomainApplication, DomainInvitation -from django.contrib.admin.models import LogEntry, CHANGE from .mixins import ( DomainPermission, From 48e56724acbe5ccb544d2b776c4142818b9b0aae Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 6 Sep 2023 10:00:37 -0600 Subject: [PATCH 104/110] Added comments on get-gov-admin.js --- src/registrar/assets/js/get-gov-admin.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js index d193b2fb6..3b9f19a49 100644 --- a/src/registrar/assets/js/get-gov-admin.js +++ b/src/registrar/assets/js/get-gov-admin.js @@ -28,7 +28,15 @@ function openInNewTab(el, removeAttribute = false){ * but this can be expanded. */ (function (){ - // Keep scope tight and allow for scalability + /* + On mouseover, appends target="_blank" on domain_form under the Domain page. + The reason for this is that the template has a form that contains multiple buttons. + The structure of that template complicates seperating those buttons + out of the form (while maintaining the same position on the page). + However, if we want to open one of those submit actions to a new tab - + such as the manage domain button - we need to dynamically append target. + As there is no built-in django method which handles this, we do it here. + */ function prepareDjangoAdmin() { let domainFormElement = document.getElementById("domain_form"); let domainSubmitButton = document.getElementById("manageDomainSubmitButton"); From d663e46fbbcd9678e5f531cab3418546474e4b17 Mon Sep 17 00:00:00 2001 From: Gaby Disarli Date: Wed, 6 Sep 2023 17:12:28 -0700 Subject: [PATCH 105/110] changes to domain contact information --- src/registrar/templates/domain_your_contact_information.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/registrar/templates/domain_your_contact_information.html b/src/registrar/templates/domain_your_contact_information.html index e18deeb50..1976a6def 100644 --- a/src/registrar/templates/domain_your_contact_information.html +++ b/src/registrar/templates/domain_your_contact_information.html @@ -7,7 +7,8 @@

Domain contact information

-

If you’d like us to use a different name, email, or phone number you can make those changes below. Changing your contact information here won’t affect your Login.gov account information.

+

If you’d like us to use a different name, email, or phone number you can make those changes below. Please note that updating your contact information here will update the contact information for all domains in your account. However, it won’t affect your Login.gov account information. +

{% include "includes/required_fields.html" %} From b77bffd4835cf263dfc384dc571120dd987a5ed8 Mon Sep 17 00:00:00 2001 From: Gaby Disarli Date: Thu, 7 Sep 2023 08:56:02 -0700 Subject: [PATCH 106/110] remove 'please note that' --- src/registrar/templates/domain_your_contact_information.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/templates/domain_your_contact_information.html b/src/registrar/templates/domain_your_contact_information.html index 1976a6def..81c62584c 100644 --- a/src/registrar/templates/domain_your_contact_information.html +++ b/src/registrar/templates/domain_your_contact_information.html @@ -7,7 +7,7 @@

Domain contact information

-

If you’d like us to use a different name, email, or phone number you can make those changes below. Please note that updating your contact information here will update the contact information for all domains in your account. However, it won’t affect your Login.gov account information. +

If you’d like us to use a different name, email, or phone number you can make those changes below. Updating your contact information here will update the contact information for all domains in your account. However, it won’t affect your Login.gov account information.

{% include "includes/required_fields.html" %} From 3d254c0f90f0c32395a1b7ead4c9ab68d97d9d71 Mon Sep 17 00:00:00 2001 From: Katherine-Osos <119689946+Katherine-Osos@users.noreply.github.com> Date: Thu, 7 Sep 2023 11:06:44 -0500 Subject: [PATCH 107/110] Update AO description to match updates in request form --- src/registrar/templates/domain_authorizing_official.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/registrar/templates/domain_authorizing_official.html b/src/registrar/templates/domain_authorizing_official.html index c08dbb237..c12f1f290 100644 --- a/src/registrar/templates/domain_authorizing_official.html +++ b/src/registrar/templates/domain_authorizing_official.html @@ -10,8 +10,7 @@

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.

+ authorize domain requests. This person must be in a role of significant, executive responsibility within the organization. Read more about who can serve as an authorizing official.

{% include "includes/required_fields.html" %} From f5506c76cb043080f75eeaac9e990b65b4f40da1 Mon Sep 17 00:00:00 2001 From: CuriousX Date: Fri, 8 Sep 2023 14:11:28 -0600 Subject: [PATCH 108/110] add Nicolle LeClair to admin and staff --- src/registrar/fixtures.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/registrar/fixtures.py b/src/registrar/fixtures.py index 76b01abf7..ccf60e591 100644 --- a/src/registrar/fixtures.py +++ b/src/registrar/fixtures.py @@ -77,6 +77,11 @@ class UserFixture: "first_name": "David", "last_name": "Kennedy", }, + { + "username": "f14433d8-f0e9-41bf-9c72-b99b110e665d", + "first_name": "Nicolle", + "last_name": "LeClair", + }, ] STAFF = [ @@ -123,6 +128,12 @@ class UserFixture: "last_name": "DiSarli-Analyst", "email": "gaby@truss.works", }, + { + "username": "cfe7c2fc-e24a-480e-8b78-28645a1459b3", + "first_name": "Nicolle", + "last_name": "LeClair", + "email": "nicolle.leclair@ecstech.com", + }, ] STAFF_PERMISSIONS = [ From 8be7608c6cd74bd290d0dde1ded3ca020ff114ad Mon Sep 17 00:00:00 2001 From: CuriousX Date: Fri, 8 Sep 2023 15:31:30 -0600 Subject: [PATCH 109/110] linted fixtures file --- src/registrar/fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/fixtures.py b/src/registrar/fixtures.py index ccf60e591..531cc5b2a 100644 --- a/src/registrar/fixtures.py +++ b/src/registrar/fixtures.py @@ -132,7 +132,7 @@ class UserFixture: "username": "cfe7c2fc-e24a-480e-8b78-28645a1459b3", "first_name": "Nicolle", "last_name": "LeClair", - "email": "nicolle.leclair@ecstech.com", + "email": "nicolle.leclair@ecstech.com", }, ] From bfd466db03b897f150b15e9cc4ec7b7554ea37ff Mon Sep 17 00:00:00 2001 From: CuriousX Date: Fri, 8 Sep 2023 15:35:23 -0600 Subject: [PATCH 110/110] updated analyst name --- src/registrar/fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/fixtures.py b/src/registrar/fixtures.py index 531cc5b2a..f37474e71 100644 --- a/src/registrar/fixtures.py +++ b/src/registrar/fixtures.py @@ -130,7 +130,7 @@ class UserFixture: }, { "username": "cfe7c2fc-e24a-480e-8b78-28645a1459b3", - "first_name": "Nicolle", + "first_name": "Nicolle-Analyst", "last_name": "LeClair", "email": "nicolle.leclair@ecstech.com", },