mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-14 21:44:08 +02:00
Merge pull request #2491 from cisagov/gd/2354-handle-portfolio-edit-mode
Ticket #2354: Read-only suborganization view
This commit is contained in:
commit
25d3a540af
5 changed files with 127 additions and 60 deletions
|
@ -64,11 +64,19 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% url 'domain-org-name-address' pk=domain.id as url %}
|
{% if portfolio %}
|
||||||
{% include "includes/summary_item.html" with title='Organization name and mailing address' value=domain.domain_info address='true' edit_link=url editable=is_editable %}
|
{% comment %} TODO - uncomment in #2352 and add to edit_link
|
||||||
|
{% url 'domain-suborganization' pk=domain.id as url %}
|
||||||
|
{% endcomment %}
|
||||||
|
{% include "includes/summary_item.html" with title='Suborganization' value=domain.domain_info.sub_organization edit_link="#" editable=is_editable %}
|
||||||
|
{% else %}
|
||||||
|
{% url 'domain-org-name-address' pk=domain.id as url %}
|
||||||
|
{% include "includes/summary_item.html" with title='Organization name and mailing address' value=domain.domain_info address='true' edit_link=url editable=is_editable %}
|
||||||
|
|
||||||
|
{% url 'domain-senior-official' pk=domain.id as url %}
|
||||||
|
{% include "includes/summary_item.html" with title='Senior official' value=domain.domain_info.senior_official contact='true' edit_link=url editable=is_editable %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% url 'domain-senior-official' pk=domain.id as url %}
|
|
||||||
{% include "includes/summary_item.html" with title='Senior official' value=domain.domain_info.senior_official contact='true' edit_link=url editable=is_editable %}
|
|
||||||
|
|
||||||
{# Conditionally display profile #}
|
{# Conditionally display profile #}
|
||||||
{% if not has_profile_feature_flag %}
|
{% if not has_profile_feature_flag %}
|
||||||
|
|
|
@ -3,16 +3,19 @@
|
||||||
<div class="margin-bottom-4 tablet:margin-bottom-0">
|
<div class="margin-bottom-4 tablet:margin-bottom-0">
|
||||||
<nav aria-label="Domain sections">
|
<nav aria-label="Domain sections">
|
||||||
<ul class="usa-sidenav">
|
<ul class="usa-sidenav">
|
||||||
<li class="usa-sidenav__item">
|
|
||||||
{% url 'domain' pk=domain.id as url %}
|
{% with url_name="domain" %}
|
||||||
<a href="{{ url }}"
|
{% include "includes/domain_sidenav_item.html" with item_text="Domain overview" %}
|
||||||
{% if request.path == url %}class="usa-current"{% endif %}
|
{% endwith %}
|
||||||
>
|
|
||||||
Domain overview
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
{% if is_editable %}
|
{% if is_editable %}
|
||||||
|
|
||||||
|
{% if not portfolio %}
|
||||||
|
{% with url_name="domain-org-name-address" %}
|
||||||
|
{% include "includes/domain_sidenav_item.html" with item_text="Organization name and mailing address" %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<li class="usa-sidenav__item">
|
<li class="usa-sidenav__item">
|
||||||
{% url 'domain-dns' pk=domain.id as url %}
|
{% url 'domain-dns' pk=domain.id as url %}
|
||||||
<a href="{{ url }}" {% if request.path|startswith:url %}class="usa-current"{% endif %}>
|
<a href="{{ url }}" {% if request.path|startswith:url %}class="usa-current"{% endif %}>
|
||||||
|
@ -55,53 +58,32 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="usa-sidenav__item">
|
|
||||||
{% url 'domain-org-name-address' pk=domain.id as url %}
|
|
||||||
<a href="{{ url }}"
|
|
||||||
{% if request.path == url %}class="usa-current"{% endif %}
|
|
||||||
>
|
|
||||||
Organization name and mailing address
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="usa-sidenav__item">
|
{% if portfolio %}
|
||||||
{% url 'domain-senior-official' pk=domain.id as url %}
|
{% with url="#" %}
|
||||||
<a href="{{ url }}"
|
{% include "includes/domain_sidenav_item.html" with item_text="Suborganization" %}
|
||||||
{% if request.path == url %}class="usa-current"{% endif %}
|
{% endwith %}
|
||||||
>
|
{% else %}
|
||||||
Senior official
|
{% with url_name="domain-senior-official" %}
|
||||||
</a>
|
{% include "includes/domain_sidenav_item.html" with item_text="Senior official" %}
|
||||||
</li>
|
{% endwith %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if not has_profile_feature_flag %}
|
{% if not has_profile_feature_flag %}
|
||||||
{# Conditionally display profile link in main nav #}
|
{# Conditionally display profile link in main nav #}
|
||||||
<li class="usa-sidenav__item">
|
{% with url_name="domain-your-contact-information" %}
|
||||||
{% url 'domain-your-contact-information' pk=domain.id as url %}
|
{% include "includes/domain_sidenav_item.html" with item_text="Your contact information" %}
|
||||||
<a href="{{ url }}"
|
{% endwith %}
|
||||||
{% if request.path == url %}class="usa-current"{% endif %}
|
|
||||||
>
|
|
||||||
Your contact information
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<li class="usa-sidenav__item">
|
{% with url_name="domain-security-email" %}
|
||||||
{% url 'domain-security-email' pk=domain.id as url %}
|
{% include "includes/domain_sidenav_item.html" with item_text="Security email" %}
|
||||||
<a href="{{ url }}"
|
{% endwith %}
|
||||||
{% if request.path == url %}class="usa-current"{% endif %}
|
|
||||||
>
|
{% with url_name="domain-users" %}
|
||||||
Security email
|
{% include "includes/domain_sidenav_item.html" with item_text="Domain managers" %}
|
||||||
</a>
|
{% endwith %}
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="usa-sidenav__item">
|
|
||||||
{% url 'domain-users' pk=domain.id as url %}
|
|
||||||
<a href="{{ url }}"
|
|
||||||
{% if request.path|startswith:url %}class="usa-current"{% endif %}
|
|
||||||
>
|
|
||||||
Domain managers
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
10
src/registrar/templates/includes/domain_sidenav_item.html
Normal file
10
src/registrar/templates/includes/domain_sidenav_item.html
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<li class="usa-sidenav__item">
|
||||||
|
{% if url_name %}
|
||||||
|
{% url url_name pk=domain.id as url %}
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ url }}"
|
||||||
|
{% if request.path == url %}class="usa-current"{% endif %}
|
||||||
|
>
|
||||||
|
{{ item_text }}
|
||||||
|
</a>
|
||||||
|
</li>
|
|
@ -4,10 +4,9 @@ from unittest.mock import MagicMock, ANY, patch
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
from waffle.testutils import override_flag
|
||||||
from api.tests.common import less_console_noise_decorator
|
from api.tests.common import less_console_noise_decorator
|
||||||
from registrar.models.portfolio import Portfolio
|
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
|
||||||
from registrar.models.utility.portfolio_helper import UserPortfolioRoleChoices
|
|
||||||
from .common import MockEppLib, MockSESClient, create_user # type: ignore
|
from .common import MockEppLib, MockSESClient, create_user # type: ignore
|
||||||
from django_webtest import WebTest # type: ignore
|
from django_webtest import WebTest # type: ignore
|
||||||
import boto3_mocking # type: ignore
|
import boto3_mocking # type: ignore
|
||||||
|
@ -35,6 +34,8 @@ from registrar.models import (
|
||||||
UserDomainRole,
|
UserDomainRole,
|
||||||
User,
|
User,
|
||||||
FederalAgency,
|
FederalAgency,
|
||||||
|
Portfolio,
|
||||||
|
Suborganization,
|
||||||
)
|
)
|
||||||
from datetime import date, datetime, timedelta
|
from datetime import date, datetime, timedelta
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -140,6 +141,7 @@ class TestWithDomainPermissions(TestWithUser):
|
||||||
Host.objects.all().delete()
|
Host.objects.all().delete()
|
||||||
Domain.objects.all().delete()
|
Domain.objects.all().delete()
|
||||||
UserDomainRole.objects.all().delete()
|
UserDomainRole.objects.all().delete()
|
||||||
|
Suborganization.objects.all().delete()
|
||||||
Portfolio.objects.all().delete()
|
Portfolio.objects.all().delete()
|
||||||
except ValueError: # pass if already deleted
|
except ValueError: # pass if already deleted
|
||||||
pass
|
pass
|
||||||
|
@ -1126,7 +1128,7 @@ class TestDomainSeniorOfficial(TestDomainOverview):
|
||||||
def test_domain_senior_official(self):
|
def test_domain_senior_official(self):
|
||||||
"""Can load domain's senior official page."""
|
"""Can load domain's senior official page."""
|
||||||
page = self.client.get(reverse("domain-senior-official", kwargs={"pk": self.domain.id}))
|
page = self.client.get(reverse("domain-senior-official", kwargs={"pk": self.domain.id}))
|
||||||
self.assertContains(page, "Senior official", count=13)
|
self.assertContains(page, "Senior official", count=14)
|
||||||
|
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
def test_domain_senior_official_content(self):
|
def test_domain_senior_official_content(self):
|
||||||
|
@ -1346,7 +1348,7 @@ class TestDomainOrganization(TestDomainOverview):
|
||||||
"""Can load domain's org name and mailing address page."""
|
"""Can load domain's org name and mailing address page."""
|
||||||
page = self.client.get(reverse("domain-org-name-address", kwargs={"pk": self.domain.id}))
|
page = self.client.get(reverse("domain-org-name-address", kwargs={"pk": self.domain.id}))
|
||||||
# once on the sidebar, once in the page title, once as H1
|
# once on the sidebar, once in the page title, once as H1
|
||||||
self.assertContains(page, "Organization name and mailing address", count=3)
|
self.assertContains(page, "Organization name and mailing address", count=4)
|
||||||
|
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
def test_domain_org_name_address_content(self):
|
def test_domain_org_name_address_content(self):
|
||||||
|
@ -1521,6 +1523,52 @@ class TestDomainOrganization(TestDomainOverview):
|
||||||
self.assertNotEqual(self.domain_information.federal_agency, new_value)
|
self.assertNotEqual(self.domain_information.federal_agency, new_value)
|
||||||
|
|
||||||
|
|
||||||
|
class TestDomainSuborganization(TestDomainOverview):
|
||||||
|
"""Tests the Suborganization page for portfolio users"""
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
def test_has_suborganization_field_on_overview_with_flag(self):
|
||||||
|
"""Ensures that the suborganization field is visible
|
||||||
|
and displays correctly on the domain overview page"""
|
||||||
|
|
||||||
|
# Create a portfolio
|
||||||
|
portfolio = Portfolio.objects.create(creator=self.user, organization_name="Ice Cream")
|
||||||
|
suborg = Suborganization.objects.create(portfolio=portfolio, name="Vanilla")
|
||||||
|
|
||||||
|
# Add the portfolio to the domain_information object
|
||||||
|
self.domain_information.portfolio = portfolio
|
||||||
|
|
||||||
|
# Add a organization_name to test if the old value still displays
|
||||||
|
self.domain_information.organization_name = "Broccoli"
|
||||||
|
self.domain_information.save()
|
||||||
|
self.domain_information.refresh_from_db()
|
||||||
|
|
||||||
|
# Add portfolio perms to the user object
|
||||||
|
self.user.portfolio = portfolio
|
||||||
|
self.user.portfolio_additional_permissions = [UserPortfolioPermissionChoices.VIEW_PORTFOLIO]
|
||||||
|
self.user.save()
|
||||||
|
self.user.refresh_from_db()
|
||||||
|
|
||||||
|
# Navigate to the domain overview page
|
||||||
|
page = self.app.get(reverse("domain", kwargs={"pk": self.domain.id}))
|
||||||
|
|
||||||
|
# Test for the title change
|
||||||
|
self.assertContains(page, "Suborganization")
|
||||||
|
self.assertNotContains(page, "Organization name")
|
||||||
|
|
||||||
|
# Test for the good value
|
||||||
|
self.assertContains(page, "Ice Cream")
|
||||||
|
|
||||||
|
# Test for the bad value
|
||||||
|
self.assertNotContains(page, "Broccoli")
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
self.domain_information.delete()
|
||||||
|
suborg.delete()
|
||||||
|
portfolio.delete()
|
||||||
|
|
||||||
|
|
||||||
class TestDomainContactInformation(TestDomainOverview):
|
class TestDomainContactInformation(TestDomainOverview):
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
def test_domain_your_contact_information(self):
|
def test_domain_your_contact_information(self):
|
||||||
|
|
|
@ -23,8 +23,8 @@ from registrar.models import (
|
||||||
DomainInvitation,
|
DomainInvitation,
|
||||||
User,
|
User,
|
||||||
UserDomainRole,
|
UserDomainRole,
|
||||||
|
PublicContact,
|
||||||
)
|
)
|
||||||
from registrar.models.public_contact import PublicContact
|
|
||||||
from registrar.utility.enums import DefaultEmail
|
from registrar.utility.enums import DefaultEmail
|
||||||
from registrar.utility.errors import (
|
from registrar.utility.errors import (
|
||||||
GenericError,
|
GenericError,
|
||||||
|
@ -231,6 +231,16 @@ class DomainOrgNameAddressView(DomainFormBaseView):
|
||||||
# superclass has the redirect
|
# superclass has the redirect
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def has_permission(self):
|
||||||
|
"""Override for the has_permission class to exclude portfolio users"""
|
||||||
|
|
||||||
|
# Org users shouldn't have access to this page
|
||||||
|
is_org_user = self.request.user.is_org_user(self.request)
|
||||||
|
if self.request.user.portfolio and is_org_user:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return super().has_permission()
|
||||||
|
|
||||||
|
|
||||||
class DomainSeniorOfficialView(DomainFormBaseView):
|
class DomainSeniorOfficialView(DomainFormBaseView):
|
||||||
"""Domain senior official editing view."""
|
"""Domain senior official editing view."""
|
||||||
|
@ -248,7 +258,6 @@ class DomainSeniorOfficialView(DomainFormBaseView):
|
||||||
domain_info = self.get_domain_info_from_domain()
|
domain_info = self.get_domain_info_from_domain()
|
||||||
invalid_fields = [DomainRequest.OrganizationChoices.FEDERAL, DomainRequest.OrganizationChoices.TRIBAL]
|
invalid_fields = [DomainRequest.OrganizationChoices.FEDERAL, DomainRequest.OrganizationChoices.TRIBAL]
|
||||||
is_federal_or_tribal = domain_info and (domain_info.generic_org_type in invalid_fields)
|
is_federal_or_tribal = domain_info and (domain_info.generic_org_type in invalid_fields)
|
||||||
|
|
||||||
form_kwargs["disable_fields"] = is_federal_or_tribal
|
form_kwargs["disable_fields"] = is_federal_or_tribal
|
||||||
return form_kwargs
|
return form_kwargs
|
||||||
|
|
||||||
|
@ -276,6 +285,16 @@ class DomainSeniorOfficialView(DomainFormBaseView):
|
||||||
# superclass has the redirect
|
# superclass has the redirect
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def has_permission(self):
|
||||||
|
"""Override for the has_permission class to exclude portfolio users"""
|
||||||
|
|
||||||
|
# Org users shouldn't have access to this page
|
||||||
|
is_org_user = self.request.user.is_org_user(self.request)
|
||||||
|
if self.request.user.portfolio and is_org_user:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return super().has_permission()
|
||||||
|
|
||||||
|
|
||||||
class DomainDNSView(DomainBaseView):
|
class DomainDNSView(DomainBaseView):
|
||||||
"""DNS Information View."""
|
"""DNS Information View."""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue