mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-25 03:58:39 +02:00
merge
This commit is contained in:
commit
ff37b3bc9b
19 changed files with 316 additions and 92 deletions
|
@ -1233,7 +1233,7 @@ class DomainInformationAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
|||
search_help_text = "Search by domain."
|
||||
|
||||
fieldsets = [
|
||||
(None, {"fields": ["creator", "submitter", "domain_request", "notes"]}),
|
||||
(None, {"fields": ["portfolio", "creator", "submitter", "domain_request", "notes"]}),
|
||||
(".gov domain", {"fields": ["domain"]}),
|
||||
("Contacts", {"fields": ["authorizing_official", "other_contacts", "no_other_contacts_rationale"]}),
|
||||
("Background info", {"fields": ["anything_else"]}),
|
||||
|
@ -1319,6 +1319,32 @@ class DomainInformationAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
|||
|
||||
change_form_template = "django/admin/domain_information_change_form.html"
|
||||
|
||||
superuser_only_fields = [
|
||||
"portfolio",
|
||||
]
|
||||
|
||||
# DEVELOPER's NOTE:
|
||||
# Normally, to exclude a field from an Admin form, we could simply utilize
|
||||
# Django's "exclude" feature. However, it causes a "missing key" error if we
|
||||
# go that route for this particular form. The error gets thrown by our
|
||||
# custom fieldset.html code and is due to the fact that "exclude" removes
|
||||
# fields from base_fields but not fieldsets. Rather than reworking our
|
||||
# custom frontend, it seems more straightforward (and easier to read) to simply
|
||||
# modify the fieldsets list so that it excludes any fields we want to remove
|
||||
# based on permissions (eg. superuser_only_fields) or other conditions.
|
||||
def get_fieldsets(self, request, obj=None):
|
||||
fieldsets = self.fieldsets
|
||||
|
||||
# Create a modified version of fieldsets to exclude certain fields
|
||||
if not request.user.has_perm("registrar.full_access_permission"):
|
||||
modified_fieldsets = []
|
||||
for name, data in fieldsets:
|
||||
fields = data.get("fields", [])
|
||||
fields = tuple(field for field in fields if field not in DomainInformationAdmin.superuser_only_fields)
|
||||
modified_fieldsets.append((name, {"fields": fields}))
|
||||
return modified_fieldsets
|
||||
return fieldsets
|
||||
|
||||
def get_readonly_fields(self, request, obj=None):
|
||||
"""Set the read-only state on form elements.
|
||||
We have 1 conditions that determine which fields are read-only:
|
||||
|
@ -1482,6 +1508,7 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
|||
None,
|
||||
{
|
||||
"fields": [
|
||||
"portfolio",
|
||||
"status",
|
||||
"rejection_reason",
|
||||
"action_needed_reason",
|
||||
|
@ -1592,6 +1619,32 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
|||
]
|
||||
filter_horizontal = ("current_websites", "alternative_domains", "other_contacts")
|
||||
|
||||
superuser_only_fields = [
|
||||
"portfolio",
|
||||
]
|
||||
|
||||
# DEVELOPER's NOTE:
|
||||
# Normally, to exclude a field from an Admin form, we could simply utilize
|
||||
# Django's "exclude" feature. However, it causes a "missing key" error if we
|
||||
# go that route for this particular form. The error gets thrown by our
|
||||
# custom fieldset.html code and is due to the fact that "exclude" removes
|
||||
# fields from base_fields but not fieldsets. Rather than reworking our
|
||||
# custom frontend, it seems more straightforward (and easier to read) to simply
|
||||
# modify the fieldsets list so that it excludes any fields we want to remove
|
||||
# based on permissions (eg. superuser_only_fields) or other conditions.
|
||||
def get_fieldsets(self, request, obj=None):
|
||||
fieldsets = super().get_fieldsets(request, obj)
|
||||
|
||||
# Create a modified version of fieldsets to exclude certain fields
|
||||
if not request.user.has_perm("registrar.full_access_permission"):
|
||||
modified_fieldsets = []
|
||||
for name, data in fieldsets:
|
||||
fields = data.get("fields", [])
|
||||
fields = tuple(field for field in fields if field not in self.superuser_only_fields)
|
||||
modified_fieldsets.append((name, {"fields": fields}))
|
||||
return modified_fieldsets
|
||||
return fieldsets
|
||||
|
||||
# Table ordering
|
||||
# NOTE: This impacts the select2 dropdowns (combobox)
|
||||
# Currentl, there's only one for requests on DomainInfo
|
||||
|
@ -1937,13 +1990,7 @@ class DomainInformationInline(admin.StackedInline):
|
|||
template = "django/admin/includes/domain_info_inline_stacked.html"
|
||||
model = models.DomainInformation
|
||||
|
||||
fieldsets = copy.deepcopy(DomainInformationAdmin.fieldsets)
|
||||
# remove .gov domain from fieldset
|
||||
for index, (title, f) in enumerate(fieldsets):
|
||||
if title == ".gov domain":
|
||||
del fieldsets[index]
|
||||
break
|
||||
|
||||
fieldsets = DomainInformationAdmin.fieldsets
|
||||
readonly_fields = DomainInformationAdmin.readonly_fields
|
||||
analyst_readonly_fields = DomainInformationAdmin.analyst_readonly_fields
|
||||
|
||||
|
@ -1990,6 +2037,23 @@ class DomainInformationInline(admin.StackedInline):
|
|||
def get_readonly_fields(self, request, obj=None):
|
||||
return DomainInformationAdmin.get_readonly_fields(self, request, obj=None)
|
||||
|
||||
# Re-route the get_fieldsets method to utilize DomainInformationAdmin.get_fieldsets
|
||||
# since that has all the logic for excluding certain fields according to user permissions.
|
||||
# Then modify the remaining fields to further trim out any we don't want for this inline
|
||||
# form
|
||||
def get_fieldsets(self, request, obj=None):
|
||||
# Grab fieldsets from DomainInformationAdmin so that it handles all logic
|
||||
# for permission-based field visibility.
|
||||
modified_fieldsets = DomainInformationAdmin.get_fieldsets(self, request, obj=None)
|
||||
|
||||
# remove .gov domain from fieldset
|
||||
for index, (title, f) in enumerate(modified_fieldsets):
|
||||
if title == ".gov domain":
|
||||
del modified_fieldsets[index]
|
||||
break
|
||||
|
||||
return modified_fieldsets
|
||||
|
||||
|
||||
class DomainResource(FsmModelResource):
|
||||
"""defines how each field in the referenced model should be mapped to the corresponding fields in the
|
||||
|
@ -2656,6 +2720,14 @@ class WaffleFlagAdmin(FlagAdmin):
|
|||
fields = "__all__"
|
||||
|
||||
|
||||
class DomainGroupAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
||||
list_display = ["name", "portfolio"]
|
||||
|
||||
|
||||
class SuborganizationAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
||||
list_display = ["name", "portfolio"]
|
||||
|
||||
|
||||
admin.site.unregister(LogEntry) # Unregister the default registration
|
||||
|
||||
admin.site.register(LogEntry, CustomLogEntryAdmin)
|
||||
|
@ -2679,6 +2751,8 @@ admin.site.register(models.DomainRequest, DomainRequestAdmin)
|
|||
admin.site.register(models.TransitionDomain, TransitionDomainAdmin)
|
||||
admin.site.register(models.VerifiedByStaff, VerifiedByStaffAdmin)
|
||||
admin.site.register(models.Portfolio, PortfolioAdmin)
|
||||
admin.site.register(models.DomainGroup, DomainGroupAdmin)
|
||||
admin.site.register(models.Suborganization, SuborganizationAdmin)
|
||||
|
||||
# Register our custom waffle implementations
|
||||
admin.site.register(models.WaffleFlag, WaffleFlagAdmin)
|
||||
|
|
|
@ -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";
|
||||
|
@ -1407,6 +1434,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
|
||||
|
@ -1461,6 +1492,14 @@ 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 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 => {
|
||||
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();
|
||||
|
@ -1582,7 +1621,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
`
|
||||
`
|
||||
|
||||
domainRequestsSectionWrapper.appendChild(modal);
|
||||
}
|
||||
|
|
|
@ -189,7 +189,7 @@ MIDDLEWARE = [
|
|||
# Used for waffle feature flags
|
||||
"waffle.middleware.WaffleMiddleware",
|
||||
"registrar.registrar_middleware.CheckUserProfileMiddleware",
|
||||
"registrar.registrar_middleware.CheckOrganizationMiddleware",
|
||||
"registrar.registrar_middleware.CheckPortfolioMiddleware",
|
||||
]
|
||||
|
||||
# application object used by Django’s built-in servers (e.g. `runserver`)
|
||||
|
|
|
@ -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 organization_domains, organization_domain_requests
|
||||
from registrar.views.portfolios import portfolio_domains, portfolio_domain_requests
|
||||
from api.views import available, get_current_federal, get_current_full
|
||||
|
||||
|
||||
|
@ -60,14 +60,14 @@ for step, view in [
|
|||
urlpatterns = [
|
||||
path("", views.index, name="home"),
|
||||
path(
|
||||
"organization/<int:portfolio_id>/domains/",
|
||||
organization_domains,
|
||||
name="organization-domains",
|
||||
"portfolio/<int:portfolio_id>/domains/",
|
||||
portfolio_domains,
|
||||
name="portfolio-domains",
|
||||
),
|
||||
path(
|
||||
"organization/<int:portfolio_id>/domain_requests/",
|
||||
organization_domain_requests,
|
||||
name="organization-domain-requests",
|
||||
"portfolio/<int:portfolio_id>/domain_requests/",
|
||||
portfolio_domain_requests,
|
||||
name="portfolio-domain-requests",
|
||||
),
|
||||
path(
|
||||
"admin/logout/",
|
||||
|
|
41
src/registrar/migrations/0105_suborganization_domaingroup.py
Normal file
41
src/registrar/migrations/0105_suborganization_domaingroup.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Generated by Django 4.2.10 on 2024-06-21 18:15
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("registrar", "0104_create_groups_v13"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Suborganization",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("name", models.CharField(help_text="Suborganization", max_length=1000, unique=True)),
|
||||
("portfolio", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="registrar.portfolio")),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="DomainGroup",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("name", models.CharField(help_text="Domain group", unique=True)),
|
||||
("domains", models.ManyToManyField(blank=True, to="registrar.domaininformation")),
|
||||
("portfolio", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="registrar.portfolio")),
|
||||
],
|
||||
options={
|
||||
"unique_together": {("name", "portfolio")},
|
||||
},
|
||||
),
|
||||
]
|
37
src/registrar/migrations/0106_create_groups_v14.py
Normal file
37
src/registrar/migrations/0106_create_groups_v14.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
# This migration creates the create_full_access_group and create_cisa_analyst_group groups
|
||||
# It is dependent on 0079 (which populates federal agencies)
|
||||
# If permissions on the groups need changing, edit CISA_ANALYST_GROUP_PERMISSIONS
|
||||
# in the user_group model then:
|
||||
# [NOT RECOMMENDED]
|
||||
# step 1: docker-compose exec app ./manage.py migrate --fake registrar 0035_contenttypes_permissions
|
||||
# step 2: docker-compose exec app ./manage.py migrate registrar 0036_create_groups
|
||||
# step 3: fake run the latest migration in the migrations list
|
||||
# [RECOMMENDED]
|
||||
# Alternatively:
|
||||
# step 1: duplicate the migration that loads data
|
||||
# step 2: docker-compose exec app ./manage.py migrate
|
||||
|
||||
from django.db import migrations
|
||||
from registrar.models import UserGroup
|
||||
from typing import Any
|
||||
|
||||
|
||||
# For linting: RunPython expects a function reference,
|
||||
# so let's give it one
|
||||
def create_groups(apps, schema_editor) -> Any:
|
||||
UserGroup.create_cisa_analyst_group(apps, schema_editor)
|
||||
UserGroup.create_full_access_group(apps, schema_editor)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("registrar", "0105_suborganization_domaingroup"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
create_groups,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
atomic=True,
|
||||
),
|
||||
]
|
|
@ -17,6 +17,8 @@ from .transition_domain import TransitionDomain
|
|||
from .verified_by_staff import VerifiedByStaff
|
||||
from .waffle_flag import WaffleFlag
|
||||
from .portfolio import Portfolio
|
||||
from .domain_group import DomainGroup
|
||||
from .suborganization import Suborganization
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
@ -38,6 +40,8 @@ __all__ = [
|
|||
"VerifiedByStaff",
|
||||
"WaffleFlag",
|
||||
"Portfolio",
|
||||
"DomainGroup",
|
||||
"Suborganization",
|
||||
]
|
||||
|
||||
auditlog.register(Contact)
|
||||
|
@ -58,3 +62,5 @@ auditlog.register(TransitionDomain)
|
|||
auditlog.register(VerifiedByStaff)
|
||||
auditlog.register(WaffleFlag)
|
||||
auditlog.register(Portfolio)
|
||||
auditlog.register(DomainGroup)
|
||||
auditlog.register(Suborganization)
|
||||
|
|
23
src/registrar/models/domain_group.py
Normal file
23
src/registrar/models/domain_group.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
from django.db import models
|
||||
from .utility.time_stamped_model import TimeStampedModel
|
||||
|
||||
|
||||
class DomainGroup(TimeStampedModel):
|
||||
"""
|
||||
Organized group of domains.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
unique_together = [("name", "portfolio")]
|
||||
|
||||
name = models.CharField(
|
||||
unique=True,
|
||||
help_text="Domain group",
|
||||
)
|
||||
|
||||
portfolio = models.ForeignKey("registrar.Portfolio", on_delete=models.PROTECT)
|
||||
|
||||
domains = models.ManyToManyField("registrar.DomainInformation", blank=True)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.name}"
|
|
@ -97,3 +97,6 @@ class Portfolio(TimeStampedModel):
|
|||
verbose_name="security contact e-mail",
|
||||
max_length=320,
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.organization_name}"
|
||||
|
|
22
src/registrar/models/suborganization.py
Normal file
22
src/registrar/models/suborganization.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
from django.db import models
|
||||
from .utility.time_stamped_model import TimeStampedModel
|
||||
|
||||
|
||||
class Suborganization(TimeStampedModel):
|
||||
"""
|
||||
Suborganization under an organization (portfolio)
|
||||
"""
|
||||
|
||||
name = models.CharField(
|
||||
unique=True,
|
||||
max_length=1000,
|
||||
help_text="Suborganization",
|
||||
)
|
||||
|
||||
portfolio = models.ForeignKey(
|
||||
"registrar.Portfolio",
|
||||
on_delete=models.PROTECT,
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.name}"
|
|
@ -125,10 +125,10 @@ class CheckUserProfileMiddleware:
|
|||
return None
|
||||
|
||||
|
||||
class CheckOrganizationMiddleware:
|
||||
class CheckPortfolioMiddleware:
|
||||
"""
|
||||
Checks if the current user has a portfolio
|
||||
If they do, redirect them to the org homepage when they navigate to home.
|
||||
If they do, redirect them to the portfolio homepage when they navigate to home.
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
|
@ -150,8 +150,6 @@ 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}
|
||||
)
|
||||
return HttpResponseRedirect(home_organization_with_portfolio)
|
||||
home_with_portfolio = reverse("portfolio-domains", kwargs={"portfolio_id": first_portfolio.id})
|
||||
return HttpResponseRedirect(home_with_portfolio)
|
||||
return None
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
</legend>
|
||||
|
||||
<!-- Toggle -->
|
||||
<em>Select one (<abbr class="usa-hint usa-hint--required" title="required">*</abbr>).</em>
|
||||
<em>Select one. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.0.has_cisa_representative %}
|
||||
{% endwith %}
|
||||
|
@ -36,7 +36,7 @@
|
|||
</legend>
|
||||
|
||||
<!-- Toggle -->
|
||||
<em>Select one (<abbr class="usa-hint usa-hint--required" title="required">*</abbr>).</em>
|
||||
<em>Select one. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.2.has_anything_else_text %}
|
||||
{% endwith %}
|
||||
|
@ -44,7 +44,7 @@
|
|||
</fieldset>
|
||||
|
||||
<div class="margin-top-3" id="anything-else">
|
||||
<p>Provide details below (<abbr class="usa-hint usa-hint--required" title="required">*</abbr>).</p>
|
||||
<p>Provide details below. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></p>
|
||||
{% with attr_maxlength=2000 add_label_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.3.anything_else %}
|
||||
{% endwith %}
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
{% extends 'home.html' %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% block homepage_content %}
|
||||
|
||||
<div class="tablet:grid-col-12">
|
||||
<div class="grid-row grid-gap">
|
||||
<div class="tablet:grid-col-3">
|
||||
{% include "organization_sidebar.html" with portfolio=portfolio %}
|
||||
</div>
|
||||
<div class="tablet:grid-col-9">
|
||||
{% block messages %}
|
||||
{% include "includes/form_messages.html" %}
|
||||
{% endblock %}
|
||||
{# Note: Reimplement commented out functionality #}
|
||||
|
||||
{% block organization_content %}
|
||||
{% endblock %}
|
||||
|
||||
{# Note: Reimplement this after MVP #}
|
||||
<!--
|
||||
<section class="section--outlined tablet:grid-col-11 desktop:grid-col-10">
|
||||
<h2>Archived domains</h2>
|
||||
<p>You don't have any archived domains</p>
|
||||
</section>
|
||||
-->
|
||||
|
||||
<!-- Note: Uncomment below when this is being implemented post-MVP -->
|
||||
<!-- <section class="tablet:grid-col-11 desktop:grid-col-10">
|
||||
<h2 class="padding-top-1 mobile-lg:padding-top-3"> Export domains</h2>
|
||||
<p>Download a list of your domains and their statuses as a csv file.</p>
|
||||
<a href="{% url 'todo' %}" class="usa-button usa-button--outline">
|
||||
Export domains as csv
|
||||
</a>
|
||||
</section>
|
||||
-->
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
24
src/registrar/templates/portfolio.html
Normal file
24
src/registrar/templates/portfolio.html
Normal file
|
@ -0,0 +1,24 @@
|
|||
{% extends 'home.html' %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% block homepage_content %}
|
||||
|
||||
<div class="tablet:grid-col-12">
|
||||
<div class="grid-row grid-gap">
|
||||
<div class="tablet:grid-col-3">
|
||||
{% include "portfolio_sidebar.html" with portfolio=portfolio %}
|
||||
</div>
|
||||
<div class="tablet:grid-col-9">
|
||||
{% block messages %}
|
||||
{% include "includes/form_messages.html" %}
|
||||
{% endblock %}
|
||||
{# Note: Reimplement commented out functionality #}
|
||||
|
||||
{% block portfolio_content %}
|
||||
{% endblock %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -1,8 +1,8 @@
|
|||
{% extends 'organization.html' %}
|
||||
{% extends 'portfolio.html' %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% block organization_content %}
|
||||
{% block portfolio_content %}
|
||||
<h1>Domains</h1>
|
||||
{% include "includes/domains_table.html" with portfolio=portfolio %}
|
||||
{% endblock %}
|
|
@ -1,8 +1,8 @@
|
|||
{% extends 'organization.html' %}
|
||||
{% extends 'portfolio.html' %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% block organization_content %}
|
||||
{% block portfolio_content %}
|
||||
<h1>Domain requests</h1>
|
||||
|
||||
{% comment %}
|
|
@ -5,14 +5,14 @@
|
|||
<h2 class="margin-top-0 text-semibold">{{ portfolio.organization_name }}</h2>
|
||||
<ul class="usa-sidenav usa-sidenav--portfolio">
|
||||
<li class="usa-sidenav__item">
|
||||
{% url 'organization-domains' portfolio.id as url %}
|
||||
{% url 'portfolio-domains' portfolio.id as url %}
|
||||
<a href="{{ url }}" {% if request.path == url %}class="usa-current"{% endif %}>
|
||||
Domains
|
||||
</a>
|
||||
|
||||
</li>
|
||||
<li class="usa-sidenav__item">
|
||||
{% url 'organization-domain-requests' portfolio.id as url %}
|
||||
{% url 'portfolio-domain-requests' portfolio.id as url %}
|
||||
<a href="{{ url }}" {% if request.path == url %}class="usa-current"{% endif %}>
|
||||
Domain requests
|
||||
</a>
|
|
@ -908,7 +908,7 @@ class UserProfileTests(TestWithUser, WebTest):
|
|||
self.assertContains(profile_page, "Your profile has been updated")
|
||||
|
||||
|
||||
class OrganizationsTests(TestWithUser, WebTest):
|
||||
class PortfoliosTests(TestWithUser, WebTest):
|
||||
"""A series of tests that target the organizations"""
|
||||
|
||||
# csrf checks do not work well with WebTest.
|
||||
|
@ -939,33 +939,33 @@ class OrganizationsTests(TestWithUser, WebTest):
|
|||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
||||
@less_console_noise_decorator
|
||||
def test_middleware_redirects_to_organization_homepage(self):
|
||||
"""Tests that a user is redirected to the org homepage when organization_feature is on and
|
||||
def test_middleware_redirects_to_portfolio_homepage(self):
|
||||
"""Tests that a user is redirected to the portfolio homepage when organization_feature is on and
|
||||
a portfolio belongs to the user, test for the special h1s which only exist in that version
|
||||
of the homepage"""
|
||||
self.app.set_user(self.user.username)
|
||||
with override_flag("organization_feature", active=True):
|
||||
# This will redirect the user to the org page.
|
||||
# This will redirect the user to the portfolio page.
|
||||
# Follow implicity checks if our redirect is working.
|
||||
org_page = self.app.get(reverse("home")).follow()
|
||||
portfolio_page = self.app.get(reverse("home")).follow()
|
||||
self._set_session_cookie()
|
||||
|
||||
# Assert that we're on the right page
|
||||
self.assertContains(org_page, self.portfolio.organization_name)
|
||||
self.assertContains(portfolio_page, self.portfolio.organization_name)
|
||||
|
||||
self.assertContains(org_page, "<h1>Domains</h1>")
|
||||
self.assertContains(portfolio_page, "<h1>Domains</h1>")
|
||||
|
||||
@less_console_noise_decorator
|
||||
def test_no_redirect_when_org_flag_false(self):
|
||||
"""No redirect so no follow,
|
||||
implicitely test for the presense of the h2 by looking up its id"""
|
||||
self.app.set_user(self.user.username)
|
||||
org_page = self.app.get(reverse("home"))
|
||||
home_page = self.app.get(reverse("home"))
|
||||
self._set_session_cookie()
|
||||
|
||||
self.assertNotContains(org_page, self.portfolio.organization_name)
|
||||
self.assertNotContains(home_page, self.portfolio.organization_name)
|
||||
|
||||
self.assertContains(org_page, 'id="domain-requests-header"')
|
||||
self.assertContains(home_page, 'id="domain-requests-header"')
|
||||
|
||||
@less_console_noise_decorator
|
||||
def test_no_redirect_when_user_has_no_portfolios(self):
|
||||
|
@ -974,9 +974,9 @@ class OrganizationsTests(TestWithUser, WebTest):
|
|||
self.portfolio.delete()
|
||||
self.app.set_user(self.user.username)
|
||||
with override_flag("organization_feature", active=True):
|
||||
org_page = self.app.get(reverse("home"))
|
||||
home_page = self.app.get(reverse("home"))
|
||||
self._set_session_cookie()
|
||||
|
||||
self.assertNotContains(org_page, self.portfolio.organization_name)
|
||||
self.assertNotContains(home_page, self.portfolio.organization_name)
|
||||
|
||||
self.assertContains(org_page, 'id="domain-requests-header"')
|
||||
self.assertContains(home_page, 'id="domain-requests-header"')
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.contrib.auth.decorators import login_required
|
|||
|
||||
|
||||
@login_required
|
||||
def organization_domains(request, portfolio_id):
|
||||
def portfolio_domains(request, portfolio_id):
|
||||
context = {}
|
||||
|
||||
if request.user.is_authenticated:
|
||||
|
@ -17,11 +17,11 @@ def organization_domains(request, portfolio_id):
|
|||
portfolio = get_object_or_404(Portfolio, id=portfolio_id)
|
||||
context["portfolio"] = portfolio
|
||||
|
||||
return render(request, "organization_domains.html", context)
|
||||
return render(request, "portfolio_domains.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
def organization_domain_requests(request, portfolio_id):
|
||||
def portfolio_domain_requests(request, portfolio_id):
|
||||
context = {}
|
||||
|
||||
if request.user.is_authenticated:
|
||||
|
@ -36,4 +36,4 @@ def organization_domain_requests(request, portfolio_id):
|
|||
# This controls the creation of a new domain request in the wizard
|
||||
request.session["new_request"] = True
|
||||
|
||||
return render(request, "organization_requests.html", context)
|
||||
return render(request, "portfolio_requests.html", context)
|
Loading…
Add table
Add a link
Reference in a new issue