From 0b4cdf7a69b5fe10c82c696a08ca5dd8ef10db34 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Mon, 17 Jun 2024 17:50:10 -0400 Subject: [PATCH 01/30] Fix delete request bug to ensure event listeners are not duplicated --- src/registrar/assets/js/get-gov.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js index 86bb1fe6e..371271c8b 100644 --- a/src/registrar/assets/js/get-gov.js +++ b/src/registrar/assets/js/get-gov.js @@ -1507,8 +1507,13 @@ document.addEventListener('DOMContentLoaded', function() { modals.forEach(modal => { const submitButton = modal.querySelector('.usa-modal__submit'); const closeButton = modal.querySelector('.usa-modal__close'); - submitButton.addEventListener('click', function() { - pk = submitButton.getAttribute('data-pk'); + + // Clone the submit button to remove all existing event listeners + const newSubmitButton = submitButton.cloneNode(true); + submitButton.parentNode.replaceChild(newSubmitButton, submitButton); + + newSubmitButton.addEventListener('click', function() { + pk = newSubmitButton.getAttribute('data-pk'); // Close the modal to remove the USWDS UI local classes closeButton.click(); // If we're deleting the last item on a page that is not page 1, we'll need to refresh the display to the previous page From ff3d18eeaefc97a4dadd7e5d636b6775f3c6bfeb Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Tue, 18 Jun 2024 14:57:48 -0400 Subject: [PATCH 02/30] trace down the modal unload error and fix it in uswds --- src/registrar/assets/js/get-gov.js | 65 ++++++++++++------------- src/registrar/assets/js/uswds-edited.js | 19 +++++--- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js index 371271c8b..4c4f9a9c6 100644 --- a/src/registrar/assets/js/get-gov.js +++ b/src/registrar/assets/js/get-gov.js @@ -33,6 +33,33 @@ const showElement = (element) => { element.classList.remove('display-none'); }; +/** + * Helper function that scrolls to an element + * @param {string} attributeName - The string "class" or "id" + * @param {string} attributeValue - The class or id name + */ +function ScrollToElement(attributeName, attributeValue) { + let targetEl = null; + + if (attributeName === 'class') { + targetEl = document.getElementsByClassName(attributeValue)[0]; + } else if (attributeName === 'id') { + targetEl = document.getElementById(attributeValue); + } else { + console.log('Error: unknown attribute name provided.'); + return; // Exit the function if an invalid attributeName is provided + } + + if (targetEl) { + const rect = targetEl.getBoundingClientRect(); + const scrollTop = window.scrollY || document.documentElement.scrollTop; + window.scrollTo({ + top: rect.top + scrollTop, + behavior: 'smooth' // Optional: for smooth scrolling + }); + } +} + /** Makes an element invisible. */ function makeHidden(el) { el.style.position = "absolute"; @@ -895,33 +922,6 @@ function unloadModals() { window.modal.off(); } -/** - * Helper function that scrolls to an element - * @param {string} attributeName - The string "class" or "id" - * @param {string} attributeValue - The class or id name - */ -function ScrollToElement(attributeName, attributeValue) { - let targetEl = null; - - if (attributeName === 'class') { - targetEl = document.getElementsByClassName(attributeValue)[0]; - } else if (attributeName === 'id') { - targetEl = document.getElementById(attributeValue); - } else { - console.log('Error: unknown attribute name provided.'); - return; // Exit the function if an invalid attributeName is provided - } - - if (targetEl) { - const rect = targetEl.getBoundingClientRect(); - const scrollTop = window.scrollY || document.documentElement.scrollTop; - window.scrollTo({ - top: rect.top + scrollTop, - behavior: 'smooth' // Optional: for smooth scrolling - }); - } -} - /** * Generalized function to update pagination for a list. * @param {string} itemName - The name displayed in the counter @@ -1469,7 +1469,7 @@ document.addEventListener('DOMContentLoaded', function() { - ` + ` domainRequestsSectionWrapper.appendChild(modal); } @@ -1507,13 +1507,10 @@ document.addEventListener('DOMContentLoaded', function() { modals.forEach(modal => { const submitButton = modal.querySelector('.usa-modal__submit'); const closeButton = modal.querySelector('.usa-modal__close'); - - // Clone the submit button to remove all existing event listeners - const newSubmitButton = submitButton.cloneNode(true); - submitButton.parentNode.replaceChild(newSubmitButton, submitButton); - newSubmitButton.addEventListener('click', function() { - pk = newSubmitButton.getAttribute('data-pk'); + + submitButton.addEventListener('click', function() { + pk = submitButton.getAttribute('data-pk'); // Close the modal to remove the USWDS UI local classes closeButton.click(); // If we're deleting the last item on a page that is not page 1, we'll need to refresh the display to the previous page diff --git a/src/registrar/assets/js/uswds-edited.js b/src/registrar/assets/js/uswds-edited.js index e73f3b6c0..f849e944e 100644 --- a/src/registrar/assets/js/uswds-edited.js +++ b/src/registrar/assets/js/uswds-edited.js @@ -5311,14 +5311,17 @@ const cleanUpModal = baseComponent => { const modalID = modalWrapper.getAttribute("id"); const originalLocationPlaceHolder = document.querySelector(`[data-placeholder-for="${modalID}"]`); if (originalLocationPlaceHolder) { - for (let attributeIndex = 0; attributeIndex < originalLocationPlaceHolder.attributes.length; attributeIndex += 1) { - const attribute = originalLocationPlaceHolder.attributes[attributeIndex]; - if (attribute.name.startsWith('data-original-')) { - // data-original- is 14 long - modalContent.setAttribute(attribute.name.substr(14), attribute.value); - } - } - originalLocationPlaceHolder.after(modalContent); + // DOTGOV + // Why is this line here? It seems to be recreating the original placeholder and then adding a + // copy of it after that first placeholder, which is netralizing our call of off() + // for (let attributeIndex = 0; attributeIndex < originalLocationPlaceHolder.attributes.length; attributeIndex += 1) { + // const attribute = originalLocationPlaceHolder.attributes[attributeIndex]; + // if (attribute.name.startsWith('data-original-')) { + // // data-original- is 14 long + // modalContent.setAttribute(attribute.name.substr(14), attribute.value); + // } + // } + //originalLocationPlaceHolder.after(modalContent); originalLocationPlaceHolder.parentElement.removeChild(originalLocationPlaceHolder); } modalWrapper.parentElement.removeChild(modalWrapper); From f608e0b99c3f1279abece406cb6517426902ccab Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Tue, 18 Jun 2024 16:56:08 -0400 Subject: [PATCH 03/30] revise solution to override uswds code instead of deleting it --- src/registrar/assets/js/get-gov.js | 13 +++++++++++++ src/registrar/assets/js/uswds-edited.js | 19 ++++++++----------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js index 4c4f9a9c6..4388395d4 100644 --- a/src/registrar/assets/js/get-gov.js +++ b/src/registrar/assets/js/get-gov.js @@ -1294,6 +1294,10 @@ document.addEventListener('DOMContentLoaded', function() { * @param {*} pageToDisplay - If we're deleting the last item on a page that is not page 1, we'll need to display the previous page */ function deleteDomainRequest(domainRequestPk,pageToDisplay) { + + // Use to debug uswds modal issues + //console.log('deleteDomainRequest') + // Get csrf token const csrfToken = getCsrfToken(); // Create FormData object and append the CSRF token @@ -1348,6 +1352,15 @@ document.addEventListener('DOMContentLoaded', function() { const tbody = document.querySelector('.domain-requests__table tbody'); tbody.innerHTML = ''; + // Unload modals will re-inject the DOM with the initial placeholders to allow for .on() in regular use cases + // We do NOT want that as it will cause multiple placeholders and therfore multiple inits on delete, + // which will cause bad delete requests to be sent. + const preExistingModalPlaceholders = document.querySelectorAll('[data-placeholder-for^="toggle-delete-domain-alert"]'); + preExistingModalPlaceholders.forEach(element => { + console.log('found one'); + element.remove(); + }); + // remove any existing modal elements from the DOM so they can be properly re-initialized // after the DOM content changes and there are new delete modal buttons added unloadModals(); diff --git a/src/registrar/assets/js/uswds-edited.js b/src/registrar/assets/js/uswds-edited.js index f849e944e..e73f3b6c0 100644 --- a/src/registrar/assets/js/uswds-edited.js +++ b/src/registrar/assets/js/uswds-edited.js @@ -5311,17 +5311,14 @@ const cleanUpModal = baseComponent => { const modalID = modalWrapper.getAttribute("id"); const originalLocationPlaceHolder = document.querySelector(`[data-placeholder-for="${modalID}"]`); if (originalLocationPlaceHolder) { - // DOTGOV - // Why is this line here? It seems to be recreating the original placeholder and then adding a - // copy of it after that first placeholder, which is netralizing our call of off() - // for (let attributeIndex = 0; attributeIndex < originalLocationPlaceHolder.attributes.length; attributeIndex += 1) { - // const attribute = originalLocationPlaceHolder.attributes[attributeIndex]; - // if (attribute.name.startsWith('data-original-')) { - // // data-original- is 14 long - // modalContent.setAttribute(attribute.name.substr(14), attribute.value); - // } - // } - //originalLocationPlaceHolder.after(modalContent); + for (let attributeIndex = 0; attributeIndex < originalLocationPlaceHolder.attributes.length; attributeIndex += 1) { + const attribute = originalLocationPlaceHolder.attributes[attributeIndex]; + if (attribute.name.startsWith('data-original-')) { + // data-original- is 14 long + modalContent.setAttribute(attribute.name.substr(14), attribute.value); + } + } + originalLocationPlaceHolder.after(modalContent); originalLocationPlaceHolder.parentElement.removeChild(originalLocationPlaceHolder); } modalWrapper.parentElement.removeChild(modalWrapper); From 0d3e38ce70c031ec87be12064087df9fc0b9dc35 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Tue, 18 Jun 2024 16:57:27 -0400 Subject: [PATCH 04/30] cleanup --- src/registrar/assets/js/get-gov.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js index 4388395d4..aa0230043 100644 --- a/src/registrar/assets/js/get-gov.js +++ b/src/registrar/assets/js/get-gov.js @@ -1520,8 +1520,6 @@ document.addEventListener('DOMContentLoaded', function() { modals.forEach(modal => { const submitButton = modal.querySelector('.usa-modal__submit'); const closeButton = modal.querySelector('.usa-modal__close'); - - submitButton.addEventListener('click', function() { pk = submitButton.getAttribute('data-pk'); // Close the modal to remove the USWDS UI local classes From 4b1ca63016a0f6bc156122f7a03720c7878f5dc4 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Tue, 18 Jun 2024 17:01:06 -0400 Subject: [PATCH 05/30] cleanup --- src/registrar/assets/js/get-gov.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js index aa0230043..45f37a082 100644 --- a/src/registrar/assets/js/get-gov.js +++ b/src/registrar/assets/js/get-gov.js @@ -1353,7 +1353,7 @@ document.addEventListener('DOMContentLoaded', function() { tbody.innerHTML = ''; // Unload modals will re-inject the DOM with the initial placeholders to allow for .on() in regular use cases - // We do NOT want that as it will cause multiple placeholders and therfore multiple inits on delete, + // We do NOT want that as it will cause multiple placeholders and therefore multiple inits on delete, // which will cause bad delete requests to be sent. const preExistingModalPlaceholders = document.querySelectorAll('[data-placeholder-for^="toggle-delete-domain-alert"]'); preExistingModalPlaceholders.forEach(element => { From dde2b6830ce57c71a4c455cad1a9f5b4b5a3d29e Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Tue, 18 Jun 2024 19:14:32 -0400 Subject: [PATCH 06/30] infra --- src/registrar/config/settings.py | 1 + src/registrar/config/urls.py | 6 + src/registrar/registrar_middleware.py | 35 +++ src/registrar/templates/home.html | 215 +++--------------- .../templates/home_organizations.html | 55 +++++ .../includes/domain_requests_table.html | 69 ++++++ .../templates/includes/domains_table.html | 81 +++++++ .../templates/organization_sidebar.html | 18 ++ src/registrar/views/index.py | 3 + src/registrar/views/index_organizations.py | 19 ++ 10 files changed, 324 insertions(+), 178 deletions(-) create mode 100644 src/registrar/templates/home_organizations.html create mode 100644 src/registrar/templates/includes/domain_requests_table.html create mode 100644 src/registrar/templates/includes/domains_table.html create mode 100644 src/registrar/templates/organization_sidebar.html create mode 100644 src/registrar/views/index_organizations.py diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index 8438812c4..aa7d73c2f 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -189,6 +189,7 @@ MIDDLEWARE = [ # Used for waffle feature flags "waffle.middleware.WaffleMiddleware", "registrar.registrar_middleware.CheckUserProfileMiddleware", + "registrar.registrar_middleware.CheckOrganizationMiddleware", ] # application object used by Django’s built-in servers (e.g. `runserver`) diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index dc6c8acb5..72115f66a 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -25,6 +25,7 @@ from registrar.views.domain_request import Step from registrar.views.domain_requests_json import get_domain_requests_json from registrar.views.domains_json import get_domains_json from registrar.views.utility import always_404 +from registrar.views.index_organizations import index_organizations from api.views import available, get_current_federal, get_current_full @@ -58,6 +59,11 @@ for step, view in [ urlpatterns = [ path("", views.index, name="home"), + path( + "organization", + index_organizations, + name="home-organization", + ), path( "admin/logout/", RedirectView.as_view(pattern_name="logout", permanent=False), diff --git a/src/registrar/registrar_middleware.py b/src/registrar/registrar_middleware.py index 79e3b7a11..04ec9fa13 100644 --- a/src/registrar/registrar_middleware.py +++ b/src/registrar/registrar_middleware.py @@ -2,6 +2,7 @@ Contains middleware used in settings.py """ +import logging from urllib.parse import parse_qs from django.urls import reverse from django.http import HttpResponseRedirect @@ -10,6 +11,7 @@ from waffle.decorators import flag_is_active from registrar.models.utility.generic_helper import replace_url_queryparams +logger = logging.getLogger(__name__) class NoCacheMiddleware: """ @@ -119,3 +121,36 @@ class CheckUserProfileMiddleware: else: # Process the view as normal return None + +class CheckOrganizationMiddleware: + """ + """ + + def __init__(self, get_response): + self.get_response = get_response + self.home_organization = reverse("home-organization") + self.home = reverse("home") + self.json1 = reverse("get_domains_json") + self.json2 = reverse("get_domain_requests_json") + + def __call__(self, request): + response = self.get_response(request) + return response + + def process_view(self, request, view_func, view_args, view_kwargs): + current_path = request.path + logger.debug(f"Current path: {current_path}") + + # Avoid infinite loop by skipping the redirect check on the home-organization URL + if current_path == self.home_organization or current_path == self.json1 or current_path == self.json2: + logger.debug("Skipping middleware check for home-organization URL") + return None + + has_organization_feature_flag = flag_is_active(request, "organization_feature") + logger.debug(f"Flag is active: {has_organization_feature_flag}") + + if has_organization_feature_flag: + logger.debug(f"Redirecting to {self.home_organization}") + return HttpResponseRedirect(self.home_organization) + + return None diff --git a/src/registrar/templates/home.html b/src/registrar/templates/home.html index f93159f01..a5ed4c86c 100644 --- a/src/registrar/templates/home.html +++ b/src/registrar/templates/home.html @@ -9,189 +9,48 @@ {% if user.is_authenticated %} {# the entire logged in page goes here #} -
- {% block messages %} - {% include "includes/form_messages.html" %} - {% endblock %} -

Manage your domains

+{% block homepage_content %} - {% comment %} - IMPORTANT: - If this button is added on any other page, make sure to update the - relevant view to reset request.session["new_request"] = True - {% endcomment %} -

- - Start a new domain request - -

+
+ {% block messages %} + {% include "includes/form_messages.html" %} + {% endblock %} +

Manage your domains

-
-
-
-

Domains

-
-
-
- -
-
-
- - - -
- + {% comment %} + IMPORTANT: + If this button is added on any other page, make sure to update the + relevant view to reset request.session["new_request"] = True + {% endcomment %} +

+ + Start a new domain request + +

-
-
-
-

Domain requests

-
-
-
- -
-
-
- - - -
- + {% include "includes/domains_table.html" %} + {% include "includes/domain_requests_table.html" %} - {# Note: Reimplement this after MVP #} - + {# Note: Reimplement this after MVP #} + - - + + + +{% endblock %}
{% else %} {# not user.is_authenticated #} diff --git a/src/registrar/templates/home_organizations.html b/src/registrar/templates/home_organizations.html new file mode 100644 index 000000000..5edd3860a --- /dev/null +++ b/src/registrar/templates/home_organizations.html @@ -0,0 +1,55 @@ +{% extends 'home.html' %} + +{% load static %} + +{% block homepage_content %} + +
+
+
+ {% include "organization_sidebar.html" %} +
+
+ {% block messages %} + {% include "includes/form_messages.html" %} + {% endblock %} +

Manage your domains

+ + {% comment %} + IMPORTANT: + If this button is added on any other page, make sure to update the + relevant view to reset request.session["new_request"] = True + {% endcomment %} +

+ + Start a new domain request + +

+ + {% include "includes/domains_table.html" %} + {% include "includes/domain_requests_table.html" %} + + {# Note: Reimplement this after MVP #} + + + + + + +
+
+ +{% endblock %} diff --git a/src/registrar/templates/includes/domain_requests_table.html b/src/registrar/templates/includes/domain_requests_table.html new file mode 100644 index 000000000..377f49d02 --- /dev/null +++ b/src/registrar/templates/includes/domain_requests_table.html @@ -0,0 +1,69 @@ +{% load static %} + +
+
+
+

Domain requests

+
+
+
+ +
+
+
+ + + +
+ diff --git a/src/registrar/templates/includes/domains_table.html b/src/registrar/templates/includes/domains_table.html new file mode 100644 index 000000000..334dba3da --- /dev/null +++ b/src/registrar/templates/includes/domains_table.html @@ -0,0 +1,81 @@ +{% load static %} + +
+
+
+

Domains

+
+
+
+ +
+
+
+ + + +
+ diff --git a/src/registrar/templates/organization_sidebar.html b/src/registrar/templates/organization_sidebar.html new file mode 100644 index 000000000..ed664affd --- /dev/null +++ b/src/registrar/templates/organization_sidebar.html @@ -0,0 +1,18 @@ +{% load static url_helpers %} + +
+ +
diff --git a/src/registrar/views/index.py b/src/registrar/views/index.py index c05bde21d..5e546e8e7 100644 --- a/src/registrar/views/index.py +++ b/src/registrar/views/index.py @@ -9,8 +9,11 @@ def index(request): if request.user.is_authenticated: # This is a django waffle flag which toggles features based off of the "flag" table context["has_profile_feature_flag"] = flag_is_active(request, "profile_feature") + context["has_organization_feature_flag"] = flag_is_active(request, "organization_feature") # This controls the creation of a new domain request in the wizard request.session["new_request"] = True + print('homepage view') + return render(request, "home.html", context) diff --git a/src/registrar/views/index_organizations.py b/src/registrar/views/index_organizations.py new file mode 100644 index 000000000..4ec15ad59 --- /dev/null +++ b/src/registrar/views/index_organizations.py @@ -0,0 +1,19 @@ +from django.shortcuts import render +from waffle.decorators import flag_is_active + + +def index_organizations(request): + """This page is available to anyone without logging in.""" + context = {} + + if request.user.is_authenticated: + # This is a django waffle flag which toggles features based off of the "flag" table + context["has_profile_feature_flag"] = flag_is_active(request, "profile_feature") + context["has_organization_feature_flag"] = flag_is_active(request, "organization_feature") + + # This controls the creation of a new domain request in the wizard + request.session["new_request"] = True + + print('homepage organizations view') + + return render(request, "home_organizations.html", context) From f2c3c320e9b751fa9f89c8b542a36d4dbe5aa8bb Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Wed, 19 Jun 2024 21:38:20 -0400 Subject: [PATCH 07/30] added portfolio(s) to the middleware --- src/registrar/config/urls.py | 2 +- src/registrar/registrar_middleware.py | 20 +++++++++++++------- src/registrar/views/index_organizations.py | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index 72115f66a..ad53a8b36 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -60,7 +60,7 @@ for step, view in [ urlpatterns = [ path("", views.index, name="home"), path( - "organization", + "organization//", index_organizations, name="home-organization", ), diff --git a/src/registrar/registrar_middleware.py b/src/registrar/registrar_middleware.py index 04ec9fa13..bb0d678c2 100644 --- a/src/registrar/registrar_middleware.py +++ b/src/registrar/registrar_middleware.py @@ -6,6 +6,7 @@ import logging from urllib.parse import parse_qs from django.urls import reverse from django.http import HttpResponseRedirect +from registrar.models.portfolio import Portfolio from registrar.models.user import User from waffle.decorators import flag_is_active @@ -128,7 +129,6 @@ class CheckOrganizationMiddleware: def __init__(self, get_response): self.get_response = get_response - self.home_organization = reverse("home-organization") self.home = reverse("home") self.json1 = reverse("get_domains_json") self.json2 = reverse("get_domain_requests_json") @@ -141,16 +141,22 @@ class CheckOrganizationMiddleware: current_path = request.path logger.debug(f"Current path: {current_path}") - # Avoid infinite loop by skipping the redirect check on the home-organization URL - if current_path == self.home_organization or current_path == self.json1 or current_path == self.json2: - logger.debug("Skipping middleware check for home-organization URL") + # Avoid infinite loop by skipping the redirect check on the home-organization URL and other JSON URLs + if current_path in [self.json1, self.json2] or current_path.startswith('/admin'): + logger.debug("Skipping middleware check for home-organization and JSON URLs") return None has_organization_feature_flag = flag_is_active(request, "organization_feature") logger.debug(f"Flag is active: {has_organization_feature_flag}") if has_organization_feature_flag: - logger.debug(f"Redirecting to {self.home_organization}") - return HttpResponseRedirect(self.home_organization) - + if request.user.is_authenticated: + user_portfolios = Portfolio.objects.filter(creator=request.user) + if user_portfolios.exists(): + first_portfolio = user_portfolios.first() + home_organization_with_portfolio = reverse("home-organization", kwargs={'portfolio_id': first_portfolio.id}) + + if current_path != home_organization_with_portfolio: + logger.debug(f"User has portfolios, redirecting to {home_organization_with_portfolio}") + return HttpResponseRedirect(home_organization_with_portfolio) return None diff --git a/src/registrar/views/index_organizations.py b/src/registrar/views/index_organizations.py index 4ec15ad59..3f881c3f3 100644 --- a/src/registrar/views/index_organizations.py +++ b/src/registrar/views/index_organizations.py @@ -2,7 +2,7 @@ from django.shortcuts import render from waffle.decorators import flag_is_active -def index_organizations(request): +def index_organizations(request, portfolio_id): """This page is available to anyone without logging in.""" context = {} From bd2ad797cfea0c5ad37c3f4c7cd419afe04ca572 Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Thu, 20 Jun 2024 08:24:57 -0400 Subject: [PATCH 08/30] additional views added --- src/registrar/config/urls.py | 12 +++- src/registrar/registrar_middleware.py | 6 +- .../templates/home_organizations.html | 10 +-- .../templates/organization_sidebar.html | 11 ++-- src/registrar/views/index_organizations.py | 19 ------ src/registrar/views/organizations.py | 64 +++++++++++++++++++ 6 files changed, 91 insertions(+), 31 deletions(-) delete mode 100644 src/registrar/views/index_organizations.py create mode 100644 src/registrar/views/organizations.py diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index ad53a8b36..b693a9b65 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -25,7 +25,7 @@ from registrar.views.domain_request import Step from registrar.views.domain_requests_json import get_domain_requests_json from registrar.views.domains_json import get_domains_json from registrar.views.utility import always_404 -from registrar.views.index_organizations import index_organizations +from registrar.views.organizations import index_organizations, organization_domains, organization_domain_requests from api.views import available, get_current_federal, get_current_full @@ -64,6 +64,16 @@ urlpatterns = [ index_organizations, name="home-organization", ), + path( + "organization//domains/", + organization_domains, + name="organization-domains", + ), + path( + "organization//domain_requests/", + organization_domain_requests, + name="organization-domain-requests", + ), path( "admin/logout/", RedirectView.as_view(pattern_name="logout", permanent=False), diff --git a/src/registrar/registrar_middleware.py b/src/registrar/registrar_middleware.py index bb0d678c2..86817f63d 100644 --- a/src/registrar/registrar_middleware.py +++ b/src/registrar/registrar_middleware.py @@ -142,8 +142,8 @@ class CheckOrganizationMiddleware: logger.debug(f"Current path: {current_path}") # Avoid infinite loop by skipping the redirect check on the home-organization URL and other JSON URLs - if current_path in [self.json1, self.json2] or current_path.startswith('/admin'): - logger.debug("Skipping middleware check for home-organization and JSON URLs") + if current_path in [self.json1, self.json2] or current_path.startswith('/admin') or current_path.startswith('/organization'): + logger.debug("Skipping middleware check for admin, organization and JSON URLs") return None has_organization_feature_flag = flag_is_active(request, "organization_feature") @@ -154,7 +154,7 @@ class CheckOrganizationMiddleware: user_portfolios = Portfolio.objects.filter(creator=request.user) if user_portfolios.exists(): first_portfolio = user_portfolios.first() - home_organization_with_portfolio = reverse("home-organization", kwargs={'portfolio_id': first_portfolio.id}) + home_organization_with_portfolio = reverse("organization-domains", kwargs={'portfolio_id': first_portfolio.id}) if current_path != home_organization_with_portfolio: logger.debug(f"User has portfolios, redirecting to {home_organization_with_portfolio}") diff --git a/src/registrar/templates/home_organizations.html b/src/registrar/templates/home_organizations.html index 5edd3860a..8ce67d146 100644 --- a/src/registrar/templates/home_organizations.html +++ b/src/registrar/templates/home_organizations.html @@ -7,7 +7,7 @@
- {% include "organization_sidebar.html" %} + {% include "organization_sidebar.html" with portfolio=portfolio current_path=content %}
{% block messages %} @@ -26,9 +26,11 @@ Start a new domain request

- - {% include "includes/domains_table.html" %} - {% include "includes/domain_requests_table.html" %} + {% if content == 'domains' %} + {% include "includes/domains_table.html" with portfolio=portfolio %} + {% elif content == 'domain-requests' %} + {% include "includes/domain_requests_table.html" with portfolio=portfolio %} + {% endif %} {# Note: Reimplement this after MVP #} {% comment %} IMPORTANT: If this button is added on any other page, make sure to update the relevant view to reset request.session["new_request"] = True {% endcomment %} + {% if content == 'domains' %} {% include "includes/domains_table.html" with portfolio=portfolio %} {% elif content == 'domain-requests' %} diff --git a/src/registrar/templates/organization_sidebar.html b/src/registrar/templates/organization_sidebar.html index 7d1b606f2..35d9d5e95 100644 --- a/src/registrar/templates/organization_sidebar.html +++ b/src/registrar/templates/organization_sidebar.html @@ -16,6 +16,21 @@ Domain requests +
  • + + Members + +
  • +
  • + + Organization + +
  • +
  • + + Senior official + +
  • From 300e305c005152ead50c38f04072852d2fce11cf Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Thu, 20 Jun 2024 11:27:20 -0400 Subject: [PATCH 10/30] cleanup --- src/registrar/config/urls.py | 7 +---- src/registrar/registrar_middleware.py | 28 ++++++++----------- .../templates/organization_sidebar.html | 7 +++-- ..._organizations.html => organizations.html} | 2 +- src/registrar/views/organizations.py | 24 ++-------------- 5 files changed, 20 insertions(+), 48 deletions(-) rename src/registrar/templates/{home_organizations.html => organizations.html} (99%) diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index b693a9b65..3e0d44c4c 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -25,7 +25,7 @@ from registrar.views.domain_request import Step from registrar.views.domain_requests_json import get_domain_requests_json from registrar.views.domains_json import get_domains_json from registrar.views.utility import always_404 -from registrar.views.organizations import index_organizations, organization_domains, organization_domain_requests +from registrar.views.organizations import organization_domains, organization_domain_requests from api.views import available, get_current_federal, get_current_full @@ -59,11 +59,6 @@ for step, view in [ urlpatterns = [ path("", views.index, name="home"), - path( - "organization//", - index_organizations, - name="home-organization", - ), path( "organization//domains/", organization_domains, diff --git a/src/registrar/registrar_middleware.py b/src/registrar/registrar_middleware.py index 86817f63d..30137ee63 100644 --- a/src/registrar/registrar_middleware.py +++ b/src/registrar/registrar_middleware.py @@ -130,8 +130,6 @@ class CheckOrganizationMiddleware: def __init__(self, get_response): self.get_response = get_response self.home = reverse("home") - self.json1 = reverse("get_domains_json") - self.json2 = reverse("get_domain_requests_json") def __call__(self, request): response = self.get_response(request) @@ -141,22 +139,18 @@ class CheckOrganizationMiddleware: current_path = request.path logger.debug(f"Current path: {current_path}") - # Avoid infinite loop by skipping the redirect check on the home-organization URL and other JSON URLs - if current_path in [self.json1, self.json2] or current_path.startswith('/admin') or current_path.startswith('/organization'): - logger.debug("Skipping middleware check for admin, organization and JSON URLs") - return None - has_organization_feature_flag = flag_is_active(request, "organization_feature") logger.debug(f"Flag is active: {has_organization_feature_flag}") - if has_organization_feature_flag: - if request.user.is_authenticated: - user_portfolios = Portfolio.objects.filter(creator=request.user) - if user_portfolios.exists(): - first_portfolio = user_portfolios.first() - home_organization_with_portfolio = reverse("organization-domains", kwargs={'portfolio_id': first_portfolio.id}) - - if current_path != home_organization_with_portfolio: - logger.debug(f"User has portfolios, redirecting to {home_organization_with_portfolio}") - return HttpResponseRedirect(home_organization_with_portfolio) + if current_path == self.home: + if has_organization_feature_flag: + if request.user.is_authenticated: + user_portfolios = Portfolio.objects.filter(creator=request.user) + if user_portfolios.exists(): + first_portfolio = user_portfolios.first() + home_organization_with_portfolio = reverse("organization-domains", kwargs={'portfolio_id': first_portfolio.id}) + + if current_path != home_organization_with_portfolio: + logger.debug(f"User has portfolios, redirecting to {home_organization_with_portfolio}") + return HttpResponseRedirect(home_organization_with_portfolio) return None diff --git a/src/registrar/templates/organization_sidebar.html b/src/registrar/templates/organization_sidebar.html index 35d9d5e95..4f2779816 100644 --- a/src/registrar/templates/organization_sidebar.html +++ b/src/registrar/templates/organization_sidebar.html @@ -7,12 +7,15 @@ {{ portfolio.organization_name }}
  • - + {% url 'organization-domains' portfolio.id as url %} + Domains +
  • - + {% url 'organization-domain-requests' portfolio.id as url %} + Domain requests
  • diff --git a/src/registrar/templates/home_organizations.html b/src/registrar/templates/organizations.html similarity index 99% rename from src/registrar/templates/home_organizations.html rename to src/registrar/templates/organizations.html index ba97ca292..b354fb1bf 100644 --- a/src/registrar/templates/home_organizations.html +++ b/src/registrar/templates/organizations.html @@ -30,7 +30,7 @@ Start a new domain request

    - --> + --> {% if content == 'domains' %} {% include "includes/domains_table.html" with portfolio=portfolio %} {% elif content == 'domain-requests' %} diff --git a/src/registrar/views/organizations.py b/src/registrar/views/organizations.py index 4a736e1f1..8f0578859 100644 --- a/src/registrar/views/organizations.py +++ b/src/registrar/views/organizations.py @@ -3,26 +3,6 @@ from registrar.models.portfolio import Portfolio from waffle.decorators import flag_is_active -def index_organizations(request, portfolio_id): - """This page is available to anyone without logging in.""" - context = {} - - if request.user.is_authenticated: - # This is a django waffle flag which toggles features based off of the "flag" table - context["has_profile_feature_flag"] = flag_is_active(request, "profile_feature") - context["has_organization_feature_flag"] = flag_is_active(request, "organization_feature") - - # Retrieve the portfolio object based on the provided portfolio_id - portfolio = get_object_or_404(Portfolio, id=portfolio_id) - context["portfolio"] = portfolio - - # This controls the creation of a new domain request in the wizard - request.session["new_request"] = True - - print('homepage organizations view') - - return render(request, "home_organizations.html", context) - def organization_domains(request, portfolio_id): context = {} @@ -41,7 +21,7 @@ def organization_domains(request, portfolio_id): print('organization domains view') - return render(request, "home_organizations.html", context) + return render(request, "organizations.html", context) def organization_domain_requests(request, portfolio_id): context = {} @@ -61,4 +41,4 @@ def organization_domain_requests(request, portfolio_id): print('organization domain requests view') - return render(request, "home_organizations.html", context) \ No newline at end of file + return render(request, "organizations.html", context) \ No newline at end of file From c7544dabe2f1edddfc7651ca3e96bf82fd96b430 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Thu, 20 Jun 2024 12:55:42 -0400 Subject: [PATCH 11/30] Unit tests and cleanup --- src/registrar/registrar_middleware.py | 15 ++-- .../includes/domain_requests_table.html | 8 +- .../templates/includes/domains_table.html | 8 +- .../{organizations.html => organization.html} | 27 +------ .../templates/organization_domains.html | 8 ++ .../templates/organization_requests.html | 21 +++++ .../templates/organization_sidebar.html | 4 +- src/registrar/tests/test_views.py | 76 ++++++++++++++++++- src/registrar/views/index.py | 2 +- src/registrar/views/organizations.py | 12 +-- 10 files changed, 131 insertions(+), 50 deletions(-) rename src/registrar/templates/{organizations.html => organization.html} (60%) create mode 100644 src/registrar/templates/organization_domains.html create mode 100644 src/registrar/templates/organization_requests.html diff --git a/src/registrar/registrar_middleware.py b/src/registrar/registrar_middleware.py index 30137ee63..79a728310 100644 --- a/src/registrar/registrar_middleware.py +++ b/src/registrar/registrar_middleware.py @@ -14,6 +14,7 @@ from registrar.models.utility.generic_helper import replace_url_queryparams logger = logging.getLogger(__name__) + class NoCacheMiddleware: """ Middleware to add Cache-control: no-cache to every response. @@ -122,9 +123,12 @@ class CheckUserProfileMiddleware: else: # Process the view as normal return None - + + class CheckOrganizationMiddleware: """ + Checks if the current user has a portfolio + If they do, redirect them to the org homepage when they navigate to home. """ def __init__(self, get_response): @@ -137,10 +141,8 @@ class CheckOrganizationMiddleware: def process_view(self, request, view_func, view_args, view_kwargs): current_path = request.path - logger.debug(f"Current path: {current_path}") has_organization_feature_flag = flag_is_active(request, "organization_feature") - logger.debug(f"Flag is active: {has_organization_feature_flag}") if current_path == self.home: if has_organization_feature_flag: @@ -148,9 +150,10 @@ class CheckOrganizationMiddleware: user_portfolios = Portfolio.objects.filter(creator=request.user) if user_portfolios.exists(): first_portfolio = user_portfolios.first() - home_organization_with_portfolio = reverse("organization-domains", kwargs={'portfolio_id': first_portfolio.id}) - + home_organization_with_portfolio = reverse( + "organization-domains", kwargs={"portfolio_id": first_portfolio.id} + ) + if current_path != home_organization_with_portfolio: - logger.debug(f"User has portfolios, redirecting to {home_organization_with_portfolio}") return HttpResponseRedirect(home_organization_with_portfolio) return None diff --git a/src/registrar/templates/includes/domain_requests_table.html b/src/registrar/templates/includes/domain_requests_table.html index 377f49d02..e760687b6 100644 --- a/src/registrar/templates/includes/domain_requests_table.html +++ b/src/registrar/templates/includes/domain_requests_table.html @@ -2,9 +2,11 @@
    -
    -

    Domain requests

    -
    + {% if portfolio is None %} +
    +

    Domain requests

    +
    + {% endif %}