mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-11 20:19:38 +02:00
Merge branch 'main' of https://github.com/cisagov/manage.get.gov into rh/3576-phantom-domain-request
This commit is contained in:
commit
e8b57bf01b
25 changed files with 824 additions and 330 deletions
|
@ -2758,17 +2758,16 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
||||||
"investigator",
|
"investigator",
|
||||||
"portfolio",
|
"portfolio",
|
||||||
"sub_organization",
|
"sub_organization",
|
||||||
|
"senior_official",
|
||||||
]
|
]
|
||||||
|
|
||||||
filter_horizontal = ("current_websites", "alternative_domains", "other_contacts")
|
filter_horizontal = ("current_websites", "alternative_domains", "other_contacts")
|
||||||
|
|
||||||
# Table ordering
|
# Table ordering
|
||||||
# NOTE: This impacts the select2 dropdowns (combobox)
|
# NOTE: This impacts the select2 dropdowns (combobox)
|
||||||
# Currentl, there's only one for requests on DomainInfo
|
# Currently, there's only one for requests on DomainInfo
|
||||||
ordering = ["-last_submitted_date", "requested_domain__name"]
|
ordering = ["-last_submitted_date", "requested_domain__name"]
|
||||||
|
|
||||||
change_form_template = "django/admin/domain_request_change_form.html"
|
|
||||||
|
|
||||||
def get_fieldsets(self, request, obj=None):
|
def get_fieldsets(self, request, obj=None):
|
||||||
fieldsets = super().get_fieldsets(request, obj)
|
fieldsets = super().get_fieldsets(request, obj)
|
||||||
|
|
||||||
|
|
60
src/registrar/assets/src/js/getgov-admin/andi.js
Normal file
60
src/registrar/assets/src/js/getgov-admin/andi.js
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
This function intercepts all select2 dropdowns and adds aria content.
|
||||||
|
It relies on an override in detail_table_fieldset.html that provides
|
||||||
|
a span with a corresponding id for aria-describedby content.
|
||||||
|
|
||||||
|
This allows us to avoid overriding aria-label, which is used by select2
|
||||||
|
to send the current dropdown selection to ANDI.
|
||||||
|
*/
|
||||||
|
export function initAriaInjectionsForSelect2Dropdowns() {
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
// Find all spans with "--aria-description" in their id
|
||||||
|
const descriptionSpans = document.querySelectorAll('span[id*="--aria-description"]');
|
||||||
|
|
||||||
|
descriptionSpans.forEach(function (span) {
|
||||||
|
// Extract the base ID from the span's id (remove "--aria-description")
|
||||||
|
const fieldId = span.id.replace('--aria-description', '');
|
||||||
|
const field = document.getElementById(fieldId);
|
||||||
|
|
||||||
|
if (field) {
|
||||||
|
// If Select2 is already initialized, apply aria-describedby immediately
|
||||||
|
if (field.classList.contains('select2-hidden-accessible')) {
|
||||||
|
applyAriaDescribedBy(field, span.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use MutationObserver to detect Select2 initialization
|
||||||
|
const observer = new MutationObserver(function (mutations) {
|
||||||
|
if (document.getElementById(fieldId)?.classList.contains("select2-hidden-accessible")) {
|
||||||
|
applyAriaDescribedBy(field, span.id);
|
||||||
|
observer.disconnect(); // Stop observing after applying attributes
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Function to apply aria-describedby to Select2 UI
|
||||||
|
function applyAriaDescribedBy(field, descriptionId) {
|
||||||
|
let select2ElementDetected = false;
|
||||||
|
const select2Id = "select2-" + field.id + "-container";
|
||||||
|
|
||||||
|
// Find the Select2 selection box
|
||||||
|
const select2SpanThatTriggersAria = document.querySelector(`span[aria-labelledby='${select2Id}']`);
|
||||||
|
|
||||||
|
if (select2SpanThatTriggersAria) {
|
||||||
|
select2SpanThatTriggersAria.setAttribute('aria-describedby', descriptionId);
|
||||||
|
select2ElementDetected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no Select2 component was detected, apply aria-describedby directly to the field
|
||||||
|
if (!select2ElementDetected) {
|
||||||
|
field.setAttribute('aria-describedby', descriptionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ import { initDynamicDomainInformationFields } from './domain-information-form.js
|
||||||
import { initDynamicDomainFields } from './domain-form.js';
|
import { initDynamicDomainFields } from './domain-form.js';
|
||||||
import { initAnalyticsDashboard } from './analytics.js';
|
import { initAnalyticsDashboard } from './analytics.js';
|
||||||
import { initButtonLinks } from './button-utils.js';
|
import { initButtonLinks } from './button-utils.js';
|
||||||
|
import { initAriaInjectionsForSelect2Dropdowns } from './andi.js'
|
||||||
|
|
||||||
// General
|
// General
|
||||||
initModals();
|
initModals();
|
||||||
|
@ -26,6 +27,7 @@ initCopyToClipboard();
|
||||||
initFilterHorizontalWidget();
|
initFilterHorizontalWidget();
|
||||||
initDescriptions();
|
initDescriptions();
|
||||||
initSubmitBar();
|
initSubmitBar();
|
||||||
|
initAriaInjectionsForSelect2Dropdowns();
|
||||||
initButtonLinks();
|
initButtonLinks();
|
||||||
|
|
||||||
// Domain request
|
// Domain request
|
||||||
|
|
|
@ -99,9 +99,7 @@ body {
|
||||||
}
|
}
|
||||||
.section-outlined__search {
|
.section-outlined__search {
|
||||||
flex-grow: 4;
|
flex-grow: 4;
|
||||||
// Align right
|
|
||||||
max-width: 383px;
|
max-width: 383px;
|
||||||
margin-left: auto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,52 +89,52 @@ urlpatterns = [
|
||||||
name="members",
|
name="members",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"member/<int:pk>",
|
"member/<int:member_pk>",
|
||||||
views.PortfolioMemberView.as_view(),
|
views.PortfolioMemberView.as_view(),
|
||||||
name="member",
|
name="member",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"member/<int:pk>/delete",
|
"member/<int:member_pk>/delete",
|
||||||
views.PortfolioMemberDeleteView.as_view(),
|
views.PortfolioMemberDeleteView.as_view(),
|
||||||
name="member-delete",
|
name="member-delete",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"member/<int:pk>/permissions",
|
"member/<int:member_pk>/permissions",
|
||||||
views.PortfolioMemberEditView.as_view(),
|
views.PortfolioMemberEditView.as_view(),
|
||||||
name="member-permissions",
|
name="member-permissions",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"member/<int:pk>/domains",
|
"member/<int:member_pk>/domains",
|
||||||
views.PortfolioMemberDomainsView.as_view(),
|
views.PortfolioMemberDomainsView.as_view(),
|
||||||
name="member-domains",
|
name="member-domains",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"member/<int:pk>/domains/edit",
|
"member/<int:member_pk>/domains/edit",
|
||||||
views.PortfolioMemberDomainsEditView.as_view(),
|
views.PortfolioMemberDomainsEditView.as_view(),
|
||||||
name="member-domains-edit",
|
name="member-domains-edit",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"invitedmember/<int:pk>",
|
"invitedmember/<int:invitedmember_pk>",
|
||||||
views.PortfolioInvitedMemberView.as_view(),
|
views.PortfolioInvitedMemberView.as_view(),
|
||||||
name="invitedmember",
|
name="invitedmember",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"invitedmember/<int:pk>/delete",
|
"invitedmember/<int:invitedmember_pk>/delete",
|
||||||
views.PortfolioInvitedMemberDeleteView.as_view(),
|
views.PortfolioInvitedMemberDeleteView.as_view(),
|
||||||
name="invitedmember-delete",
|
name="invitedmember-delete",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"invitedmember/<int:pk>/permissions",
|
"invitedmember/<int:invitedmember_pk>/permissions",
|
||||||
views.PortfolioInvitedMemberEditView.as_view(),
|
views.PortfolioInvitedMemberEditView.as_view(),
|
||||||
name="invitedmember-permissions",
|
name="invitedmember-permissions",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"invitedmember/<int:pk>/domains",
|
"invitedmember/<int:invitedmember_pk>/domains",
|
||||||
views.PortfolioInvitedMemberDomainsView.as_view(),
|
views.PortfolioInvitedMemberDomainsView.as_view(),
|
||||||
name="invitedmember-domains",
|
name="invitedmember-domains",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"invitedmember/<int:pk>/domains/edit",
|
"invitedmember/<int:invitedmember_pk>/domains/edit",
|
||||||
views.PortfolioInvitedMemberDomainsEditView.as_view(),
|
views.PortfolioInvitedMemberDomainsEditView.as_view(),
|
||||||
name="invitedmember-domains-edit",
|
name="invitedmember-domains-edit",
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
|
import logging
|
||||||
import functools
|
import functools
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from registrar.models import Domain, DomainInformation, DomainInvitation, DomainRequest, UserDomainRole
|
from registrar.models import Domain, DomainInformation, DomainInvitation, DomainRequest, UserDomainRole
|
||||||
|
from registrar.models.portfolio_invitation import PortfolioInvitation
|
||||||
|
from registrar.models.user_portfolio_permission import UserPortfolioPermission
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Constants for clarity
|
# Constants for clarity
|
||||||
ALL = "all"
|
ALL = "all"
|
||||||
|
@ -98,24 +104,38 @@ def _user_has_permission(user, request, rules, **kwargs):
|
||||||
if not user.is_authenticated or user.is_restricted():
|
if not user.is_authenticated or user.is_restricted():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
portfolio = request.session.get("portfolio")
|
||||||
# Define permission checks
|
# Define permission checks
|
||||||
permission_checks = [
|
permission_checks = [
|
||||||
(IS_STAFF, lambda: user.is_staff),
|
(IS_STAFF, lambda: user.is_staff),
|
||||||
(IS_DOMAIN_MANAGER, lambda: _is_domain_manager(user, **kwargs)),
|
(
|
||||||
|
IS_DOMAIN_MANAGER,
|
||||||
|
lambda: (not user.is_org_user(request) and _is_domain_manager(user, **kwargs))
|
||||||
|
or (
|
||||||
|
user.is_org_user(request)
|
||||||
|
and _is_domain_manager(user, **kwargs)
|
||||||
|
and _domain_exists_under_portfolio(portfolio, kwargs.get("domain_pk"))
|
||||||
|
),
|
||||||
|
),
|
||||||
(IS_STAFF_MANAGING_DOMAIN, lambda: _is_staff_managing_domain(request, **kwargs)),
|
(IS_STAFF_MANAGING_DOMAIN, lambda: _is_staff_managing_domain(request, **kwargs)),
|
||||||
(IS_PORTFOLIO_MEMBER, lambda: user.is_org_user(request)),
|
(IS_PORTFOLIO_MEMBER, lambda: user.is_org_user(request)),
|
||||||
(
|
(
|
||||||
HAS_PORTFOLIO_DOMAINS_VIEW_ALL,
|
HAS_PORTFOLIO_DOMAINS_VIEW_ALL,
|
||||||
lambda: _has_portfolio_view_all_domains(request, kwargs.get("domain_pk")),
|
lambda: user.is_org_user(request)
|
||||||
|
and user.has_view_all_domains_portfolio_permission(portfolio)
|
||||||
|
and _domain_exists_under_portfolio(portfolio, kwargs.get("domain_pk")),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
HAS_PORTFOLIO_DOMAINS_ANY_PERM,
|
HAS_PORTFOLIO_DOMAINS_ANY_PERM,
|
||||||
lambda: user.is_org_user(request)
|
lambda: user.is_org_user(request)
|
||||||
and user.has_any_domains_portfolio_permission(request.session.get("portfolio")),
|
and user.has_any_domains_portfolio_permission(portfolio)
|
||||||
|
and _domain_exists_under_portfolio(portfolio, kwargs.get("domain_pk")),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
IS_PORTFOLIO_MEMBER_AND_DOMAIN_MANAGER,
|
IS_PORTFOLIO_MEMBER_AND_DOMAIN_MANAGER,
|
||||||
lambda: _is_domain_manager(user, **kwargs) and _is_portfolio_member(request),
|
lambda: _is_domain_manager(user, **kwargs)
|
||||||
|
and _is_portfolio_member(request)
|
||||||
|
and _domain_exists_under_portfolio(portfolio, kwargs.get("domain_pk")),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
IS_DOMAIN_MANAGER_AND_NOT_PORTFOLIO_MEMBER,
|
IS_DOMAIN_MANAGER_AND_NOT_PORTFOLIO_MEMBER,
|
||||||
|
@ -129,34 +149,55 @@ def _user_has_permission(user, request, rules, **kwargs):
|
||||||
(
|
(
|
||||||
HAS_PORTFOLIO_DOMAIN_REQUESTS_ANY_PERM,
|
HAS_PORTFOLIO_DOMAIN_REQUESTS_ANY_PERM,
|
||||||
lambda: user.is_org_user(request)
|
lambda: user.is_org_user(request)
|
||||||
and user.has_any_requests_portfolio_permission(request.session.get("portfolio")),
|
and user.has_any_requests_portfolio_permission(portfolio)
|
||||||
|
and _domain_request_exists_under_portfolio(portfolio, kwargs.get("domain_request_pk")),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
HAS_PORTFOLIO_DOMAIN_REQUESTS_VIEW_ALL,
|
HAS_PORTFOLIO_DOMAIN_REQUESTS_VIEW_ALL,
|
||||||
lambda: user.is_org_user(request)
|
lambda: user.is_org_user(request)
|
||||||
and user.has_view_all_domain_requests_portfolio_permission(request.session.get("portfolio")),
|
and user.has_view_all_domain_requests_portfolio_permission(portfolio)
|
||||||
|
and _domain_request_exists_under_portfolio(portfolio, kwargs.get("domain_request_pk")),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
HAS_PORTFOLIO_DOMAIN_REQUESTS_EDIT,
|
HAS_PORTFOLIO_DOMAIN_REQUESTS_EDIT,
|
||||||
lambda: _has_portfolio_domain_requests_edit(user, request, kwargs.get("domain_request_pk")),
|
lambda: _has_portfolio_domain_requests_edit(user, request, kwargs.get("domain_request_pk"))
|
||||||
|
and _domain_request_exists_under_portfolio(portfolio, kwargs.get("domain_request_pk")),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
HAS_PORTFOLIO_MEMBERS_ANY_PERM,
|
HAS_PORTFOLIO_MEMBERS_ANY_PERM,
|
||||||
lambda: user.is_org_user(request)
|
lambda: user.is_org_user(request)
|
||||||
and (
|
and (
|
||||||
user.has_view_members_portfolio_permission(request.session.get("portfolio"))
|
user.has_view_members_portfolio_permission(portfolio)
|
||||||
or user.has_edit_members_portfolio_permission(request.session.get("portfolio"))
|
or user.has_edit_members_portfolio_permission(portfolio)
|
||||||
|
)
|
||||||
|
and (
|
||||||
|
# AND rather than OR because these functions return true if the PK is not found.
|
||||||
|
# This adds support for if the view simply doesn't have said PK.
|
||||||
|
_member_exists_under_portfolio(portfolio, kwargs.get("member_pk"))
|
||||||
|
and _member_invitation_exists_under_portfolio(portfolio, kwargs.get("invitedmember_pk"))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
HAS_PORTFOLIO_MEMBERS_EDIT,
|
HAS_PORTFOLIO_MEMBERS_EDIT,
|
||||||
lambda: user.is_org_user(request)
|
lambda: user.is_org_user(request)
|
||||||
and user.has_edit_members_portfolio_permission(request.session.get("portfolio")),
|
and user.has_edit_members_portfolio_permission(portfolio)
|
||||||
|
and (
|
||||||
|
# AND rather than OR because these functions return true if the PK is not found.
|
||||||
|
# This adds support for if the view simply doesn't have said PK.
|
||||||
|
_member_exists_under_portfolio(portfolio, kwargs.get("member_pk"))
|
||||||
|
and _member_invitation_exists_under_portfolio(portfolio, kwargs.get("invitedmember_pk"))
|
||||||
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
HAS_PORTFOLIO_MEMBERS_VIEW,
|
HAS_PORTFOLIO_MEMBERS_VIEW,
|
||||||
lambda: user.is_org_user(request)
|
lambda: user.is_org_user(request)
|
||||||
and user.has_view_members_portfolio_permission(request.session.get("portfolio")),
|
and user.has_view_members_portfolio_permission(portfolio)
|
||||||
|
and (
|
||||||
|
# AND rather than OR because these functions return true if the PK is not found.
|
||||||
|
# This adds support for if the view simply doesn't have said PK.
|
||||||
|
_member_exists_under_portfolio(portfolio, kwargs.get("member_pk"))
|
||||||
|
and _member_invitation_exists_under_portfolio(portfolio, kwargs.get("invitedmember_pk"))
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -191,6 +232,70 @@ def _is_domain_manager(user, **kwargs):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _domain_exists_under_portfolio(portfolio, domain_pk):
|
||||||
|
"""Checks to see if the given domain exists under the provided portfolio.
|
||||||
|
HELPFUL REMINDER: Watch for typos! Verify that the kwarg key exists before using this function.
|
||||||
|
Returns True if the pk is falsy. Otherwise, returns a bool if said object exists.
|
||||||
|
"""
|
||||||
|
# The view expects this, and the page will throw an error without this if it needs it.
|
||||||
|
# Thus, if it is none, we are not checking on a specific record and therefore there is nothing to check.
|
||||||
|
if not domain_pk:
|
||||||
|
logger.warning(
|
||||||
|
"_domain_exists_under_portfolio => Could not find domain_pk. "
|
||||||
|
"This is a non-issue if called from the right context."
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
return Domain.objects.filter(domain_info__portfolio=portfolio, id=domain_pk).exists()
|
||||||
|
|
||||||
|
|
||||||
|
def _domain_request_exists_under_portfolio(portfolio, domain_request_pk):
|
||||||
|
"""Checks to see if the given domain request exists under the provided portfolio.
|
||||||
|
HELPFUL REMINDER: Watch for typos! Verify that the kwarg key exists before using this function.
|
||||||
|
Returns True if the pk is falsy. Otherwise, returns a bool if said object exists.
|
||||||
|
"""
|
||||||
|
# The view expects this, and the page will throw an error without this if it needs it.
|
||||||
|
# Thus, if it is none, we are not checking on a specific record and therefore there is nothing to check.
|
||||||
|
if not domain_request_pk:
|
||||||
|
logger.warning(
|
||||||
|
"_domain_request_exists_under_portfolio => Could not find domain_request_pk. "
|
||||||
|
"This is a non-issue if called from the right context."
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
return DomainRequest.objects.filter(portfolio=portfolio, id=domain_request_pk).exists()
|
||||||
|
|
||||||
|
|
||||||
|
def _member_exists_under_portfolio(portfolio, member_pk):
|
||||||
|
"""Checks to see if the given UserPortfolioPermission exists under the provided portfolio.
|
||||||
|
HELPFUL REMINDER: Watch for typos! Verify that the kwarg key exists before using this function.
|
||||||
|
Returns True if the pk is falsy. Otherwise, returns a bool if said object exists.
|
||||||
|
"""
|
||||||
|
# The view expects this, and the page will throw an error without this if it needs it.
|
||||||
|
# Thus, if it is none, we are not checking on a specific record and therefore there is nothing to check.
|
||||||
|
if not member_pk:
|
||||||
|
logger.warning(
|
||||||
|
"_member_exists_under_portfolio => Could not find member_pk. "
|
||||||
|
"This is a non-issue if called from the right context."
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
return UserPortfolioPermission.objects.filter(portfolio=portfolio, id=member_pk).exists()
|
||||||
|
|
||||||
|
|
||||||
|
def _member_invitation_exists_under_portfolio(portfolio, invitedmember_pk):
|
||||||
|
"""Checks to see if the given PortfolioInvitation exists under the provided portfolio.
|
||||||
|
HELPFUL REMINDER: Watch for typos! Verify that the kwarg key exists before using this function.
|
||||||
|
Returns True if the pk is falsy. Otherwise, returns a bool if said object exists.
|
||||||
|
"""
|
||||||
|
# The view expects this, and the page will throw an error without this if it needs it.
|
||||||
|
# Thus, if it is none, we are not checking on a specific record and therefore there is nothing to check.
|
||||||
|
if not invitedmember_pk:
|
||||||
|
logger.warning(
|
||||||
|
"_member_invitation_exists_under_portfolio => Could not find invitedmember_pk. "
|
||||||
|
"This is a non-issue if called from the right context."
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
return PortfolioInvitation.objects.filter(portfolio=portfolio, id=invitedmember_pk).exists()
|
||||||
|
|
||||||
|
|
||||||
def _is_domain_request_creator(user, domain_request_pk):
|
def _is_domain_request_creator(user, domain_request_pk):
|
||||||
"""Checks to see if the user is the creator of a domain request
|
"""Checks to see if the user is the creator of a domain request
|
||||||
with domain_request_pk."""
|
with domain_request_pk."""
|
||||||
|
@ -286,15 +391,3 @@ def _is_staff_managing_domain(request, **kwargs):
|
||||||
# the user is permissioned,
|
# the user is permissioned,
|
||||||
# and it is in a valid status
|
# and it is in a valid status
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _has_portfolio_view_all_domains(request, domain_pk):
|
|
||||||
"""Returns whether the user in the request can access the domain
|
|
||||||
via portfolio view all domains permission."""
|
|
||||||
portfolio = request.session.get("portfolio")
|
|
||||||
if request.user.has_view_all_domains_portfolio_permission(portfolio):
|
|
||||||
if Domain.objects.filter(id=domain_pk).exists():
|
|
||||||
domain = Domain.objects.get(id=domain_pk)
|
|
||||||
if domain.domain_info.portfolio == portfolio:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
|
@ -347,7 +347,7 @@ class OrganizationContactForm(RegistrarForm):
|
||||||
error_messages={
|
error_messages={
|
||||||
"required": ("Select the state, territory, or military post where your organization is located.")
|
"required": ("Select the state, territory, or military post where your organization is located.")
|
||||||
},
|
},
|
||||||
widget=ComboboxWidget,
|
widget=ComboboxWidget(attrs={"required": True}),
|
||||||
)
|
)
|
||||||
zipcode = forms.CharField(
|
zipcode = forms.CharField(
|
||||||
label="Zip code",
|
label="Zip code",
|
||||||
|
|
|
@ -160,6 +160,16 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
|
||||||
{% endblock field_readonly %}
|
{% endblock field_readonly %}
|
||||||
|
|
||||||
{% block field_other %}
|
{% block field_other %}
|
||||||
|
{% comment %}
|
||||||
|
.gov override - add Aria messages for select2 dropdowns. These messages are hooked-up to their respective DOM
|
||||||
|
elements via javascript (see andi.js)
|
||||||
|
{% endcomment %}
|
||||||
|
{% if "related_widget_wrapper" in field.field.field.widget.template_name %}
|
||||||
|
<span id="{{ field.field.id_for_label }}--aria-description" class="visually-hidden admin-select--aria-description">
|
||||||
|
{{ field.field.label }}, edit, has autocomplete. To set the value, use the arrow keys or type the text.
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if field.field.name == "action_needed_reason_email" %}
|
{% if field.field.name == "action_needed_reason_email" %}
|
||||||
{{ field.field }}
|
{{ field.field }}
|
||||||
|
|
||||||
|
@ -251,7 +261,6 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
|
||||||
|
|
||||||
{% elif field.field.name == "rejection_reason_email" %}
|
{% elif field.field.name == "rejection_reason_email" %}
|
||||||
{{ field.field }}
|
{{ field.field }}
|
||||||
|
|
||||||
<div class="margin-top-05 text-faded custom-email-placeholder">
|
<div class="margin-top-05 text-faded custom-email-placeholder">
|
||||||
–
|
–
|
||||||
</div>
|
</div>
|
||||||
|
@ -331,7 +340,6 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if original_object.rejection_reason_email %}
|
{% if original_object.rejection_reason_email %}
|
||||||
<input id="last-sent-rejection-email-content" class="display-none" value="{{original_object.rejection_reason_email}}">
|
<input id="last-sent-rejection-email-content" class="display-none" value="{{original_object.rejection_reason_email}}">
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
|
@ -15,14 +15,14 @@
|
||||||
{% include "includes/form_messages.html" %}
|
{% include "includes/form_messages.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
<h1>Manage your domains</h1>
|
<div class="grid-row margin-bottom-3">
|
||||||
|
<h1 class="flex-fill">Manage your domains</h1>
|
||||||
<p class="margin-top-4">
|
<div>
|
||||||
<button data-href="{% url 'domain-request:start' %}" class="usa-button use-button-as-link"
|
<button data-href="{% url 'domain-request:start' %}" class="usa-button use-button-as-link">
|
||||||
>
|
Start a new domain request
|
||||||
Start a new domain request
|
</button>
|
||||||
</button>
|
</div>
|
||||||
</p>
|
</div>
|
||||||
|
|
||||||
{% include "includes/domains_table.html" with user_domain_count=user_domain_count %}
|
{% include "includes/domains_table.html" with user_domain_count=user_domain_count %}
|
||||||
{% include "includes/domain_requests_table.html" %}
|
{% include "includes/domain_requests_table.html" %}
|
||||||
|
|
|
@ -4,49 +4,20 @@
|
||||||
{% url 'get_domain_requests_json' as url %}
|
{% url 'get_domain_requests_json' as url %}
|
||||||
<span id="get_domain_requests_json_url" class="display-none">{{url}}</span>
|
<span id="get_domain_requests_json_url" class="display-none">{{url}}</span>
|
||||||
|
|
||||||
<section class="section-outlined domain-requests {% if portfolio %}section-outlined--border-base-light{% endif %}" id="domain-requests">
|
<section class="section-outlined domain-requests {% if portfolio %}margin-top-0 section-outlined--border-base-light{% endif %}" id="domain-requests">
|
||||||
<div class="section-outlined__header margin-bottom-3 {% if not portfolio %}section-outlined__header--no-portfolio justify-content-space-between{% else %} grid-row{% endif %}">
|
<div class="section-outlined__header margin-bottom-3 grid-row">
|
||||||
{% if not portfolio %}
|
{% if not portfolio %}
|
||||||
<h2 id="domain-requests-header" class="display-inline-block">Domain requests</h2>
|
<div class="grid-row grid-col-12">
|
||||||
|
<h2 id="domain-requests-header" class="display-inline-block flex-fill">Domain requests</h2>
|
||||||
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<!-- Embedding the portfolio value in a data attribute -->
|
<!-- Embedding the portfolio value in a data attribute -->
|
||||||
<span id="portfolio-js-value" data-portfolio="{{ portfolio.id }}"></span>
|
<span id="portfolio-js-value" data-portfolio="{{ portfolio.id }}"></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<!-- ---------- SEARCH ---------- -->
|
||||||
<div class="section-outlined__search section-outlined__search--widescreen {% if portfolio %}mobile:grid-col-12 desktop:grid-col-6{% endif %}">
|
{% with label_text=portfolio|yesno:"Search by domain name or creator,Search by domain name" item_name="domain-requests" aria_label_text="Domain requests search component"%}
|
||||||
<section aria-label="Domain requests search component" id="domain-requests-search-component" class="margin-top-2">
|
{% include "includes/search.html" %}
|
||||||
<form class="usa-search usa-search--small" method="POST" role="search">
|
{% endwith %}
|
||||||
{% csrf_token %}
|
|
||||||
<button class="usa-button usa-button--unstyled margin-right-3 display-none" id="domain-requests__reset-search" type="button" aria-labelledby="domain-requests-search-component">
|
|
||||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
|
||||||
<use xlink:href="{%static 'img/sprite.svg'%}#close"></use>
|
|
||||||
</svg>
|
|
||||||
Reset
|
|
||||||
</button>
|
|
||||||
<input
|
|
||||||
class="usa-input"
|
|
||||||
id="domain-requests__search-field"
|
|
||||||
type="search"
|
|
||||||
name="domain-requests-search"
|
|
||||||
{% if portfolio %}
|
|
||||||
placeholder="Search by domain name or creator"
|
|
||||||
{% else %}
|
|
||||||
placeholder="Search by domain name"
|
|
||||||
{% endif %}
|
|
||||||
aria-labelledby="domain-requests-search-component"
|
|
||||||
/>
|
|
||||||
<div class="usa-sr-only" id="domain-requests-search-button__description">Click to search</div>
|
|
||||||
<button class="usa-button" type="submit" id="domain-requests__search-field-submit" aria-labelledby="domain-requests-search-component" aria-describedby="domain-requests-search-button__description">
|
|
||||||
<img
|
|
||||||
src="{% static 'img/usa-icons-bg/search--white.svg' %}"
|
|
||||||
class="usa-search__submit-icon"
|
|
||||||
alt="Search"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if portfolio %}
|
{% if portfolio %}
|
||||||
|
|
|
@ -26,56 +26,32 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<section class="section-outlined domains margin-top-0{% if portfolio %} section-outlined--border-base-light{% endif %}" id="domains">
|
<section class="section-outlined domains margin-top-0{% if portfolio %} section-outlined--border-base-light{% endif %}" id="domains">
|
||||||
<div class="section-outlined__header margin-bottom-3 {% if not portfolio %} section-outlined__header--no-portfolio justify-content-space-between{% else %} grid-row{% endif %}">
|
<div class="section-outlined__header margin-bottom-3 grid-row">
|
||||||
{% if not portfolio %}
|
{% if not portfolio %}
|
||||||
<h2 id="domains-header" class="display-inline-block">Domains</h2>
|
<div class="grid-row grid-col-12">
|
||||||
|
<h2 id="domains-header" class="display-inline-block flex-fill">Domains</h2>
|
||||||
|
<!-- ---------- EXPORT (non-org placement) ---------- -->
|
||||||
|
{% if user_domain_count and user_domain_count > 0 %}
|
||||||
|
{% with export_aria="Domains report component" export_url='export_data_type_user' %}
|
||||||
|
{% include "includes/export.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<!-- Embedding the portfolio value in a data attribute -->
|
<!-- Embedding the portfolio value in a data attribute -->
|
||||||
<span id="portfolio-js-value" data-portfolio="{{ portfolio.id }}"></span>
|
<span id="portfolio-js-value" data-portfolio="{{ portfolio.id }}"></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="section-outlined__search section-outlined__search--widescreen {% if portfolio %}mobile:grid-col-12 desktop:grid-col-6{% endif %}">
|
<!-- ---------- SEARCH ---------- -->
|
||||||
<section aria-label="Domains search component" class="margin-top-2" id="domains-search-component">
|
{% with label_text="Search by domain name" item_name="domains" aria_label_text="Domains search component"%}
|
||||||
<form class="usa-search usa-search--small" method="POST" role="search">
|
{% include "includes/search.html" %}
|
||||||
{% csrf_token %}
|
{% endwith %}
|
||||||
<button class="usa-button usa-button--unstyled margin-right-3 display-none" id="domains__reset-search" type="button" aria-labelledby="domains-search-component">
|
<!-- ---------- EXPORT (org placement) ---------- -->
|
||||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
{% if user_domain_count and user_domain_count > 0 and portfolio%}
|
||||||
<use xlink:href="{%static 'img/sprite.svg'%}#close"></use>
|
{% with export_aria="Domains report component" export_url='export_data_type_user' %}
|
||||||
</svg>
|
{% include "includes/export.html" %}
|
||||||
Reset
|
{% endwith %}
|
||||||
</button>
|
|
||||||
<input
|
|
||||||
class="usa-input"
|
|
||||||
id="domains__search-field"
|
|
||||||
type="search"
|
|
||||||
name="domains-search"
|
|
||||||
placeholder="Search by domain name"
|
|
||||||
aria-labelledby="domains-search-component"
|
|
||||||
/>
|
|
||||||
<div class="usa-sr-only" id="domains-search-button__description">Click to search</div>
|
|
||||||
<button class="usa-button" type="submit" id="domains__search-field-submit" aria-labelledby="domains-search-component" aria-describedby="domains-search-button__description">
|
|
||||||
<img
|
|
||||||
src="{% static 'img/usa-icons-bg/search--white.svg' %}"
|
|
||||||
class="usa-search__submit-icon"
|
|
||||||
alt="Search"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
{% if user_domain_count and user_domain_count > 0 %}
|
|
||||||
<div class="section-outlined__utility-button mobile-lg:padding-right-105 {% if portfolio %} mobile:grid-col-12 desktop:grid-col-6 desktop:padding-left-3{% endif %}">
|
|
||||||
<section aria-label="Domains report component" class="margin-top-205" id="domains-report-component">
|
|
||||||
<div class="usa-sr-only" id="domains-export-button__description">Click to export as csv</div>
|
|
||||||
<button data-href="{% url 'export_data_type_user' %}" class="use-button-as-link usa-button usa-button--unstyled usa-button--with-icon usa-button--justify-right" aria-labelledby="domains-report-component" aria-describedby="domains-export-button__description">
|
|
||||||
<svg class="usa-icon usa-icon--large" aria-hidden="true" focusable="false" role="img" width="24" height="24">
|
|
||||||
<use xlink:href="{%static 'img/sprite.svg'%}#file_download"></use>
|
|
||||||
</svg>Export as CSV
|
|
||||||
</button>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Non org model banner -->
|
<!-- Non org model banner -->
|
||||||
{% if num_expiring_domains > 0 and not portfolio %}
|
{% if num_expiring_domains > 0 and not portfolio %}
|
||||||
<section class="usa-site-alert--slim usa-site-alert--info margin-bottom-2 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
<section class="usa-site-alert--slim usa-site-alert--info margin-bottom-2 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
||||||
|
|
12
src/registrar/templates/includes/export.html
Normal file
12
src/registrar/templates/includes/export.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{% load static %}
|
||||||
|
<div class="section-outlined__utility-button mobile-lg:padding-right-105 {% if portfolio %} flex-auto desktop:padding-left-3{% endif %} margin-top-0">
|
||||||
|
<section aria-label="{{export_aria}}" class="margin-top-205">
|
||||||
|
<button data-href="{% url export_url %}" class="usa-button usa-button--unstyled usa-button--with-icon usa-button--justify-right use-button-as-link">
|
||||||
|
<svg class="usa-icon usa-icon--large" aria-hidden="true" focusable="false" role="img" width="24" height="24">
|
||||||
|
<use xlink:href="{%static 'img/sprite.svg'%}#file_download"></use>
|
||||||
|
</svg>Export as CSV
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -91,9 +91,9 @@
|
||||||
aria-describedby="You have unsaved changes that will be lost."
|
aria-describedby="You have unsaved changes that will be lost."
|
||||||
>
|
>
|
||||||
{% if portfolio_permission %}
|
{% if portfolio_permission %}
|
||||||
{% url 'member-domains' pk=portfolio_permission.id as url %}
|
{% url 'member-domains' member_pk=portfolio_permission.id as url %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% url 'invitedmember-domains' pk=portfolio_invitation.id as url %}
|
{% url 'invitedmember-domains' invitedmember_pk=portfolio_invitation.id as url %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% include 'includes/modal.html' with modal_heading="Are you sure you want to continue?" modal_description="You have unsaved changes that will be lost." modal_button_url=url modal_button_text="Continue without saving" %}
|
{% include 'includes/modal.html' with modal_heading="Are you sure you want to continue?" modal_description="You have unsaved changes that will be lost." modal_button_url=url modal_button_text="Continue without saving" %}
|
||||||
|
|
|
@ -7,47 +7,14 @@
|
||||||
<span id="get_members_json_url" class="display-none">{{url}}</span>
|
<span id="get_members_json_url" class="display-none">{{url}}</span>
|
||||||
<section class="section-outlined members margin-top-0 section-outlined--border-base-light" id="members">
|
<section class="section-outlined members margin-top-0 section-outlined--border-base-light" id="members">
|
||||||
<div class="section-outlined__header margin-bottom-3 grid-row">
|
<div class="section-outlined__header margin-bottom-3 grid-row">
|
||||||
<!-- ---------- SEARCH ---------- -->
|
<!-- ---------- SEARCH ---------- -->
|
||||||
<div class="section-outlined__search mobile:grid-col-12 desktop:grid-col-6 section-outlined__search--widescreen">
|
{% with label_text="Search by member name" item_name="members" aria_label_text="Members search component"%}
|
||||||
<section aria-label="Members search component" class="margin-top-2" id="members-search-component">
|
{% include "includes/search.html" %}
|
||||||
<form class="usa-search usa-search--small" method="POST" role="search">
|
{% endwith %}
|
||||||
{% csrf_token %}
|
{% with export_aria="Members report component" export_url='export_members_portfolio' %}
|
||||||
<button class="usa-button usa-button--unstyled margin-right-3 display-none" id="members__reset-search" type="button" aria-labelledby="members-search-component">
|
{% include "includes/export.html" %}
|
||||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
{% endwith %}
|
||||||
<use xlink:href="{%static 'img/sprite.svg'%}#close"></use>
|
</div>
|
||||||
</svg>
|
|
||||||
Reset
|
|
||||||
</button>
|
|
||||||
<input
|
|
||||||
class="usa-input"
|
|
||||||
id="members__search-field"
|
|
||||||
type="search"
|
|
||||||
name="members-search"
|
|
||||||
placeholder="Search by member name"
|
|
||||||
aria-labelledby="members-search-component"
|
|
||||||
/>
|
|
||||||
<div class="usa-sr-only" id="members-search-button__description">Click to search</div>
|
|
||||||
<button class="usa-button" type="submit" id="members__search-field-submit" aria-labelledby="members-search-component" aria-describedby="members-search-button__description">
|
|
||||||
<img
|
|
||||||
src="{% static 'img/usa-icons-bg/search--white.svg' %}"
|
|
||||||
class="usa-search__submit-icon"
|
|
||||||
alt="Search"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<div class="section-outlined__utility-button mobile-lg:padding-right-105 {% if portfolio %} mobile:grid-col-12 desktop:grid-col-6 desktop:padding-left-3{% endif %}">
|
|
||||||
<section aria-label="Members report component" class="margin-top-205" id="members-report-component">
|
|
||||||
<div class="usa-sr-only" id="members-export-button__description">Click to export as csv</div>
|
|
||||||
<button href="{% url 'export_members_portfolio' %}" class="use-button-as-link usa-button usa-button--unstyled usa-button--with-icon usa-button--justify-right" aria-labelledby="members-report-component" aria-describedby="members-export-button__description">
|
|
||||||
<svg class="usa-icon usa-icon--large" aria-hidden="true" focusable="false" role="img" width="24" height="24">
|
|
||||||
<use xlink:href="{%static 'img/sprite.svg'%}#file_download"></use>
|
|
||||||
</svg>Export as CSV
|
|
||||||
</button>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ---------- MAIN TABLE ---------- -->
|
<!-- ---------- MAIN TABLE ---------- -->
|
||||||
<div class="display-none margin-top-0" id="members__table-wrapper">
|
<div class="display-none margin-top-0" id="members__table-wrapper">
|
||||||
|
|
|
@ -1,26 +1,28 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
<div class="section-outlined__search mobile:grid-col-12 desktop:grid-col-9">
|
<div class="section-outlined__search tablet:grid-col">
|
||||||
<section aria-label="{{aria_label_text}}">
|
<section aria-label="{{aria_label_text}}">
|
||||||
<form class="usa-search usa-search--show-label" method="POST" role="search">
|
<form class="usa-search {% if use_search_icon %} usa-search--small {% else %} usa-search--default {% endif %}usa-search--show-label" method="POST" role="search">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<label class="usa-label display-block margin-bottom-05 maxw-none" for="{{item_name}}__search-field">
|
<label id="{{item_name}}__search-label" class="usa-label display-block maxw-none margin-top-2 margin-bottom-1" for="{{item_name}}__search-field">
|
||||||
{{ label_text }}
|
{{ label_text }}
|
||||||
</label>
|
</label>
|
||||||
<div class="usa-search--show-label__input-wrapper flex-align-self-end">
|
<div class="usa-search--show-label__input-wrapper">
|
||||||
<input
|
<input
|
||||||
class="usa-input minw-15"
|
class="usa-input minw-15"
|
||||||
id="{{item_name}}__search-field"
|
id="{{item_name}}__search-field"
|
||||||
type="search"
|
type="search"
|
||||||
name="{{item_name}}-search"
|
name="{{item_name}}-search"
|
||||||
/>
|
/>
|
||||||
<button class="usa-button" type="submit" id="{{item_name}}__search-field-submit">
|
<button class="usa-button" type="submit" id="{{item_name}}__search-field-submit" aria-labelledby="{{item_name}}__search-label">
|
||||||
<span class="usa-search__submit-text">Search </span>
|
|
||||||
<img
|
<img
|
||||||
src="{% static 'img/usa-icons-bg/search--white.svg' %}"
|
src="{% static 'img/usa-icons-bg/search--white.svg' %}"
|
||||||
class="usa-search__submit-icon"
|
class="usa-search__submit-icon"
|
||||||
alt="Search"
|
alt="Search"
|
||||||
/>
|
/>
|
||||||
|
{% if not use_search_icon %}
|
||||||
|
<span class="usa-search__submit-text">Search </span>
|
||||||
|
{% endif %}
|
||||||
</button>
|
</button>
|
||||||
<button class="usa-button usa-button--unstyled margin-left-3 display-none flex-1" id="{{item_name}}__reset-search" type="button">
|
<button class="usa-button usa-button--unstyled margin-left-3 display-none flex-1" id="{{item_name}}__reset-search" type="button">
|
||||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
||||||
|
|
|
@ -15,11 +15,11 @@
|
||||||
|
|
||||||
{% url 'members' as url %}
|
{% url 'members' as url %}
|
||||||
{% if portfolio_permission %}
|
{% if portfolio_permission %}
|
||||||
{% url 'member' pk=portfolio_permission.id as url2 %}
|
{% url 'member' member_pk=portfolio_permission.id as url2 %}
|
||||||
{% url 'member-domains-edit' pk=portfolio_permission.id as url3 %}
|
{% url 'member-domains-edit' member_pk=portfolio_permission.id as url3 %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% url 'invitedmember' pk=portfolio_invitation.id as url2 %}
|
{% url 'invitedmember' invitedmember_pk=portfolio_invitation.id as url2 %}
|
||||||
{% url 'invitedmember-domains-edit' pk=portfolio_invitation.id as url3 %}
|
{% url 'invitedmember-domains-edit' invitedmember_pk=portfolio_invitation.id as url3 %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<nav class="usa-breadcrumb padding-top-0" aria-label="Portfolio member breadcrumb">
|
<nav class="usa-breadcrumb padding-top-0" aria-label="Portfolio member breadcrumb">
|
||||||
<ol class="usa-breadcrumb__list">
|
<ol class="usa-breadcrumb__list">
|
||||||
|
|
|
@ -15,11 +15,11 @@
|
||||||
|
|
||||||
{% url 'members' as url %}
|
{% url 'members' as url %}
|
||||||
{% if portfolio_permission %}
|
{% if portfolio_permission %}
|
||||||
{% url 'member' pk=portfolio_permission.id as url2 %}
|
{% url 'member' member_pk=portfolio_permission.id as url2 %}
|
||||||
{% url 'member-domains' pk=portfolio_permission.id as url3 %}
|
{% url 'member-domains' member_pk=portfolio_permission.id as url3 %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% url 'invitedmember' pk=portfolio_invitation.id as url2 %}
|
{% url 'invitedmember' invitedmember_pk=portfolio_invitation.id as url2 %}
|
||||||
{% url 'invitedmember-domains' pk=portfolio_invitation.id as url3 %}
|
{% url 'invitedmember-domains' invitedmember_pk=portfolio_invitation.id as url3 %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<nav class="usa-breadcrumb padding-top-0" aria-label="Portfolio member breadcrumb">
|
<nav class="usa-breadcrumb padding-top-0" aria-label="Portfolio member breadcrumb">
|
||||||
<ol class="usa-breadcrumb__list">
|
<ol class="usa-breadcrumb__list">
|
||||||
|
|
|
@ -20,9 +20,9 @@
|
||||||
<!-- Navigation breadcrumbs -->
|
<!-- Navigation breadcrumbs -->
|
||||||
{% url 'members' as url %}
|
{% url 'members' as url %}
|
||||||
{% if portfolio_permission %}
|
{% if portfolio_permission %}
|
||||||
{% url 'member' pk=portfolio_permission.id as url2 %}
|
{% url 'member' member_pk=portfolio_permission.id as url2 %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% url 'invitedmember' pk=invitation.id as url2 %}
|
{% url 'invitedmember' invitedmember_pk=invitation.id as url2 %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<nav class="usa-breadcrumb padding-top-0 bg-gray-1" aria-label="Portfolio member breadcrumb">
|
<nav class="usa-breadcrumb padding-top-0 bg-gray-1" aria-label="Portfolio member breadcrumb">
|
||||||
<ol class="usa-breadcrumb__list">
|
<ol class="usa-breadcrumb__list">
|
||||||
|
|
|
@ -16,15 +16,14 @@
|
||||||
{% endblock messages%}
|
{% endblock messages%}
|
||||||
|
|
||||||
<div id="main-content">
|
<div id="main-content">
|
||||||
<h1 id="domain-requests-header" class="margin-bottom-1">Domain requests</h1>
|
<div class="grid-row grid-gap margin-bottom-3">
|
||||||
<div class="grid-row grid-gap">
|
<div class="mobile:grid-col-12 tablet:grid-col-6">
|
||||||
|
<h1 id="domain-requests-header" class="margin-bottom-1">Domain requests</h1>
|
||||||
|
<p class="margin-y-0">Domain requests can only be modified by the person who created the request.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% if has_edit_request_portfolio_permission %}
|
{% if has_edit_request_portfolio_permission %}
|
||||||
<div class="mobile:grid-col-12 tablet:grid-col-6">
|
<div class="mobile:grid-col-12 tablet:grid-col-6">
|
||||||
<p class="margin-y-0">Domain requests can only be modified by the person who created the request.</p>
|
|
||||||
</div>
|
|
||||||
<div class="mobile:grid-col-12 tablet:grid-col-6">
|
|
||||||
|
|
||||||
<p class="float-right-tablet tablet:margin-y-0">
|
<p class="float-right-tablet tablet:margin-y-0">
|
||||||
<button data-href="{% url 'domain-request:start' %}" class="usa-button use-button-as-link"
|
<button data-href="{% url 'domain-request:start' %}" class="usa-button use-button-as-link"
|
||||||
>
|
>
|
||||||
|
@ -32,12 +31,11 @@
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
|
||||||
<p class="margin-y-0">Domain requests can only be modified by the person who created the request.</p>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% include "includes/domain_requests_table.html" with portfolio=portfolio %}
|
{% include "includes/domain_requests_table.html" with portfolio=portfolio %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
355
src/registrar/tests/test_resource_access.py
Normal file
355
src/registrar/tests/test_resource_access.py
Normal file
|
@ -0,0 +1,355 @@
|
||||||
|
from django.test import Client
|
||||||
|
from django.urls import reverse
|
||||||
|
from waffle.testutils import override_flag
|
||||||
|
|
||||||
|
from registrar.tests.common import (
|
||||||
|
MockDbForIndividualTests,
|
||||||
|
less_console_noise_decorator,
|
||||||
|
completed_domain_request,
|
||||||
|
)
|
||||||
|
from registrar.models import (
|
||||||
|
DomainRequest,
|
||||||
|
Portfolio,
|
||||||
|
UserPortfolioPermission,
|
||||||
|
PortfolioInvitation,
|
||||||
|
)
|
||||||
|
from registrar.models.utility.portfolio_helper import (
|
||||||
|
UserPortfolioRoleChoices,
|
||||||
|
UserPortfolioPermissionChoices,
|
||||||
|
)
|
||||||
|
from registrar.decorators import (
|
||||||
|
_domain_exists_under_portfolio,
|
||||||
|
_domain_request_exists_under_portfolio,
|
||||||
|
_member_exists_under_portfolio,
|
||||||
|
_member_invitation_exists_under_portfolio,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestPortfolioResourceAccess(MockDbForIndividualTests):
|
||||||
|
"""Test functions that verify resources belong to a portfolio.
|
||||||
|
More specifically, this function tests our helper utilities in decorators.py"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
# Create portfolios
|
||||||
|
self.portfolio = Portfolio.objects.create(creator=self.user, organization_name="Test Portfolio")
|
||||||
|
self.other_portfolio = Portfolio.objects.create(
|
||||||
|
creator=self.custom_staffuser, organization_name="Other Portfolio"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create domain requests
|
||||||
|
self.domain_request = completed_domain_request(name="eggnog.gov", user=self.user, portfolio=self.portfolio)
|
||||||
|
|
||||||
|
self.other_domain_request = completed_domain_request(
|
||||||
|
name="christmas.gov", user=self.tired_user, portfolio=self.other_portfolio
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create domains
|
||||||
|
self.approved_domain_request_1 = completed_domain_request(
|
||||||
|
name="done_1.gov",
|
||||||
|
user=self.tired_user,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW,
|
||||||
|
)
|
||||||
|
self.approved_domain_request_2 = completed_domain_request(
|
||||||
|
name="done_2.gov",
|
||||||
|
user=self.tired_user,
|
||||||
|
portfolio=self.other_portfolio,
|
||||||
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW,
|
||||||
|
)
|
||||||
|
self.approved_domain_request_1.approve()
|
||||||
|
self.approved_domain_request_2.approve()
|
||||||
|
self.domain = self.approved_domain_request_1.approved_domain
|
||||||
|
self.other_domain = self.approved_domain_request_2.approved_domain
|
||||||
|
|
||||||
|
# Create portfolio permissions
|
||||||
|
self.user_permission = UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.other_user_permission = UserPortfolioPermission.objects.create(
|
||||||
|
user=self.tired_user, portfolio=self.other_portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create portfolio invitations
|
||||||
|
self.portfolio_invitation = PortfolioInvitation.objects.create(
|
||||||
|
email="invited@example.com",
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
|
status=PortfolioInvitation.PortfolioInvitationStatus.INVITED,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.other_portfolio_invitation = PortfolioInvitation.objects.create(
|
||||||
|
email="other-invited@example.com",
|
||||||
|
portfolio=self.other_portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
|
status=PortfolioInvitation.PortfolioInvitationStatus.INVITED,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Domain request tests
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_domain_request_exists_under_portfolio_when_pk_is_none(self):
|
||||||
|
"""Check behavior when the PK is None."""
|
||||||
|
self.assertTrue(_domain_request_exists_under_portfolio(self.portfolio, None))
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_domain_request_exists_under_portfolio_when_exists(self):
|
||||||
|
"""Verify returns True when the domain request exists under the portfolio."""
|
||||||
|
self.assertTrue(_domain_request_exists_under_portfolio(self.portfolio, self.domain_request.id))
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_domain_request_exists_under_portfolio_when_not_exists(self):
|
||||||
|
"""Verify returns False when the domain request does not exist under the portfolio."""
|
||||||
|
self.assertFalse(_domain_request_exists_under_portfolio(self.portfolio, self.other_domain_request.id))
|
||||||
|
|
||||||
|
# Domain tests
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_domain_exists_under_portfolio_when_pk_is_none(self):
|
||||||
|
"""Check behavior when the PK is None."""
|
||||||
|
self.assertTrue(_domain_exists_under_portfolio(self.portfolio, None))
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_domain_exists_under_portfolio_when_exists(self):
|
||||||
|
"""Verify returns True when the domain exists under the portfolio."""
|
||||||
|
self.assertTrue(_domain_exists_under_portfolio(self.portfolio, self.domain.id))
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_domain_exists_under_portfolio_when_not_exists(self):
|
||||||
|
"""Verify returns False when the domain does not exist under the portfolio."""
|
||||||
|
self.assertFalse(_domain_exists_under_portfolio(self.portfolio, self.other_domain.id))
|
||||||
|
|
||||||
|
# Member tests
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_member_exists_under_portfolio_when_pk_is_none(self):
|
||||||
|
"""Check behavior when the PK is None."""
|
||||||
|
self.assertTrue(_member_exists_under_portfolio(self.portfolio, None))
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_member_exists_under_portfolio_when_exists(self):
|
||||||
|
"""Verify returns True when the member exists under the portfolio."""
|
||||||
|
self.assertTrue(_member_exists_under_portfolio(self.portfolio, self.user_permission.id))
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_member_exists_under_portfolio_when_not_exists(self):
|
||||||
|
"""Verify returns False when the member does not exist under the portfolio."""
|
||||||
|
self.assertFalse(_member_exists_under_portfolio(self.portfolio, self.other_user_permission.id))
|
||||||
|
|
||||||
|
# Member invitation tests
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_member_invitation_exists_under_portfolio_when_pk_is_none(self):
|
||||||
|
"""Check behavior when the PK is None."""
|
||||||
|
self.assertTrue(_member_invitation_exists_under_portfolio(self.portfolio, None))
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_member_invitation_exists_under_portfolio_when_exists(self):
|
||||||
|
"""Verify returns True when the member invitation exists under the portfolio."""
|
||||||
|
self.assertTrue(_member_invitation_exists_under_portfolio(self.portfolio, self.portfolio_invitation.id))
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_member_invitation_exists_under_portfolio_when_not_exists(self):
|
||||||
|
"""Verify returns False when the member invitation does not exist under the portfolio."""
|
||||||
|
self.assertFalse(_member_invitation_exists_under_portfolio(self.portfolio, self.other_portfolio_invitation.id))
|
||||||
|
|
||||||
|
|
||||||
|
class TestPortfolioDomainRequestViewAccess(MockDbForIndividualTests):
|
||||||
|
"""Tests for domain request views to ensure users can only access domain requests in their portfolio."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.client = Client()
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
|
# Create portfolios
|
||||||
|
self.portfolio = Portfolio.objects.create(creator=self.user, organization_name="Test Portfolio")
|
||||||
|
self.other_portfolio = Portfolio.objects.create(creator=self.tired_user, organization_name="Other Portfolio")
|
||||||
|
|
||||||
|
# Create domain requests
|
||||||
|
self.domain_request = completed_domain_request(
|
||||||
|
name="test-domain.gov",
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
status=DomainRequest.DomainRequestStatus.STARTED,
|
||||||
|
user=self.user,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.other_domain_request = completed_domain_request(
|
||||||
|
name="other-domain.gov",
|
||||||
|
portfolio=self.other_portfolio,
|
||||||
|
status=DomainRequest.DomainRequestStatus.STARTED,
|
||||||
|
user=self.tired_user,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Give user permission to view all requests
|
||||||
|
self.user_permission = UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
||||||
|
additional_permissions=[UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Setup session for portfolio views
|
||||||
|
session = self.client.session
|
||||||
|
session["portfolio"] = self.portfolio
|
||||||
|
session.save()
|
||||||
|
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
@override_flag("organization_requests", active=True)
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_domain_request_view_same_portfolio(self):
|
||||||
|
"""Test that user can access domain requests in their portfolio."""
|
||||||
|
# With just the view all permission, access should be denied
|
||||||
|
response = self.client.get(reverse("edit-domain-request", kwargs={"domain_request_pk": self.domain_request.pk}))
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
|
# But with the edit permission, the user should be able to access this domain request
|
||||||
|
self.user_permission.additional_permissions = [
|
||||||
|
UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS,
|
||||||
|
UserPortfolioPermissionChoices.EDIT_REQUESTS,
|
||||||
|
]
|
||||||
|
self.user_permission.save()
|
||||||
|
self.user_permission.refresh_from_db()
|
||||||
|
response = self.client.get(
|
||||||
|
reverse("edit-domain-request", kwargs={"domain_request_pk": self.domain_request.pk}), follow=True
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
@override_flag("organization_requests", active=True)
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_domain_request_view_different_portfolio(self):
|
||||||
|
"""Test that user cannot access domain request not in their portfolio."""
|
||||||
|
response = self.client.get(
|
||||||
|
reverse("edit-domain-request", kwargs={"domain_request_pk": self.other_domain_request.pk})
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
@override_flag("organization_requests", active=True)
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_domain_request_viewonly_same_portfolio(self):
|
||||||
|
"""Test that user can access view-only domain request in their portfolio."""
|
||||||
|
response = self.client.get(
|
||||||
|
reverse("domain-request-status-viewonly", kwargs={"domain_request_pk": self.domain_request.pk})
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
@override_flag("organization_requests", active=True)
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_domain_request_viewonly_different_portfolio(self):
|
||||||
|
"""Test that user cannot access view-only domain request not in their portfolio."""
|
||||||
|
response = self.client.get(
|
||||||
|
reverse("domain-request-status-viewonly", kwargs={"domain_request_pk": self.other_domain_request.pk})
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
|
|
||||||
|
class TestPortfolioDomainViewAccess(MockDbForIndividualTests):
|
||||||
|
"""Tests for domain views to ensure users can only access domains in their portfolio."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.client = Client()
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
|
# Create portfolios
|
||||||
|
self.portfolio = Portfolio.objects.create(creator=self.user, organization_name="Test Portfolio")
|
||||||
|
self.other_portfolio = Portfolio.objects.create(creator=self.tired_user, organization_name="Other Portfolio")
|
||||||
|
|
||||||
|
# Create domains through domain requests
|
||||||
|
self.domain_request = completed_domain_request(
|
||||||
|
name="test-domain.gov",
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW,
|
||||||
|
user=self.user,
|
||||||
|
)
|
||||||
|
self.domain_request.approve()
|
||||||
|
self.domain = self.domain_request.approved_domain
|
||||||
|
|
||||||
|
self.other_domain_request = completed_domain_request(
|
||||||
|
name="other-domain.gov",
|
||||||
|
portfolio=self.other_portfolio,
|
||||||
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW,
|
||||||
|
user=self.user,
|
||||||
|
)
|
||||||
|
self.other_domain_request.approve()
|
||||||
|
self.other_domain = self.other_domain_request.approved_domain
|
||||||
|
|
||||||
|
# Give user permission to view all domains
|
||||||
|
self.user_permission = UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
||||||
|
additional_permissions=[UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Setup session for portfolio views
|
||||||
|
session = self.client.session
|
||||||
|
session["portfolio"] = self.portfolio
|
||||||
|
session.save()
|
||||||
|
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_domain_view_same_portfolio(self):
|
||||||
|
"""Test that user can access domain in their portfolio."""
|
||||||
|
response = self.client.get(reverse("domain", kwargs={"domain_pk": self.domain.pk}))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_domain_view_different_portfolio(self):
|
||||||
|
"""Test that user cannot access domain not in their portfolio."""
|
||||||
|
response = self.client.get(reverse("domain", kwargs={"domain_pk": self.other_domain.pk}))
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
|
|
||||||
|
class TestPortfolioMemberViewAccess(MockDbForIndividualTests):
|
||||||
|
"""Tests for member views to ensure users can only access members in their portfolio."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.client = Client()
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
|
# Create portfolios
|
||||||
|
self.portfolio = Portfolio.objects.create(creator=self.user, organization_name="Test Portfolio")
|
||||||
|
self.other_portfolio = Portfolio.objects.create(creator=self.tired_user, organization_name="Other Portfolio")
|
||||||
|
|
||||||
|
# Create portfolio permissions
|
||||||
|
self.member_permission = UserPortfolioPermission.objects.create(
|
||||||
|
user=self.meoward_user, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.other_member_permission = UserPortfolioPermission.objects.create(
|
||||||
|
user=self.lebowski_user,
|
||||||
|
portfolio=self.other_portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Give user permission to view/edit members
|
||||||
|
self.user_permission = UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Setup session for portfolio views
|
||||||
|
session = self.client.session
|
||||||
|
session["portfolio"] = self.portfolio
|
||||||
|
session.save()
|
||||||
|
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
@override_flag("organization_members", active=True)
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_member_view_same_portfolio(self):
|
||||||
|
"""Test that user can access member in their portfolio."""
|
||||||
|
response = self.client.get(reverse("member", kwargs={"member_pk": self.member_permission.pk}))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
@override_flag("organization_members", active=True)
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_member_view_different_portfolio(self):
|
||||||
|
"""Test that user cannot access member not in their portfolio."""
|
||||||
|
response = self.client.get(reverse("member", kwargs={"member_pk": self.other_member_permission.pk}))
|
||||||
|
self.assertEqual(response.status_code, 403)
|
|
@ -29,6 +29,8 @@ SAMPLE_KWARGS = {
|
||||||
"user_pk": "1",
|
"user_pk": "1",
|
||||||
"portfolio_id": "1",
|
"portfolio_id": "1",
|
||||||
"user_id": "1",
|
"user_id": "1",
|
||||||
|
"member_pk": "1",
|
||||||
|
"invitedmember_pk": "1",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Our test suite will ignore some namespaces.
|
# Our test suite will ignore some namespaces.
|
||||||
|
|
|
@ -867,7 +867,7 @@ class TestPortfolio(WebTest):
|
||||||
|
|
||||||
# Verify that the user cannot access the member page
|
# Verify that the user cannot access the member page
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.get(reverse("member", kwargs={"pk": 1}), follow=True)
|
response = self.client.get(reverse("member", kwargs={"member_pk": 1}), follow=True)
|
||||||
# Make sure the page is denied
|
# Make sure the page is denied
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
|
@ -886,7 +886,7 @@ class TestPortfolio(WebTest):
|
||||||
|
|
||||||
# Verify that the user cannot access the member page
|
# Verify that the user cannot access the member page
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.get(reverse("member", kwargs={"pk": 1}), follow=True)
|
response = self.client.get(reverse("member", kwargs={"member_pk": 1}), follow=True)
|
||||||
# Make sure the page is denied
|
# Make sure the page is denied
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
|
@ -909,7 +909,7 @@ class TestPortfolio(WebTest):
|
||||||
|
|
||||||
# Verify the page can be accessed
|
# Verify the page can be accessed
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.get(reverse("member", kwargs={"pk": permission_obj.pk}), follow=True)
|
response = self.client.get(reverse("member", kwargs={"member_pk": permission_obj.pk}), follow=True)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
# Assert text within the page is correct
|
# Assert text within the page is correct
|
||||||
|
@ -942,7 +942,7 @@ class TestPortfolio(WebTest):
|
||||||
|
|
||||||
# Verify the page can be accessed
|
# Verify the page can be accessed
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.get(reverse("member", kwargs={"pk": permission_obj.pk}), follow=True)
|
response = self.client.get(reverse("member", kwargs={"member_pk": permission_obj.pk}), follow=True)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
# Assert text within the page is correct
|
# Assert text within the page is correct
|
||||||
|
@ -966,7 +966,7 @@ class TestPortfolio(WebTest):
|
||||||
|
|
||||||
# Verify that the user cannot access the member page
|
# Verify that the user cannot access the member page
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.get(reverse("invitedmember", kwargs={"pk": 1}), follow=True)
|
response = self.client.get(reverse("invitedmember", kwargs={"invitedmember_pk": 1}), follow=True)
|
||||||
# Make sure the page is denied
|
# Make sure the page is denied
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
|
@ -985,7 +985,7 @@ class TestPortfolio(WebTest):
|
||||||
|
|
||||||
# Verify that the user cannot access the member page
|
# Verify that the user cannot access the member page
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.get(reverse("invitedmember", kwargs={"pk": 1}), follow=True)
|
response = self.client.get(reverse("invitedmember", kwargs={"invitedmember_pk": 1}), follow=True)
|
||||||
# Make sure the page is denied
|
# Make sure the page is denied
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
|
@ -1016,7 +1016,9 @@ class TestPortfolio(WebTest):
|
||||||
|
|
||||||
# Verify the page can be accessed
|
# Verify the page can be accessed
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.get(reverse("invitedmember", kwargs={"pk": portfolio_invitation.pk}), follow=True)
|
response = self.client.get(
|
||||||
|
reverse("invitedmember", kwargs={"invitedmember_pk": portfolio_invitation.pk}), follow=True
|
||||||
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
# Assert text within the page is correct
|
# Assert text within the page is correct
|
||||||
|
@ -1054,7 +1056,9 @@ class TestPortfolio(WebTest):
|
||||||
|
|
||||||
# Verify the page can be accessed
|
# Verify the page can be accessed
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.get(reverse("invitedmember", kwargs={"pk": portfolio_invitation.pk}), follow=True)
|
response = self.client.get(
|
||||||
|
reverse("invitedmember", kwargs={"invitedmember_pk": portfolio_invitation.pk}), follow=True
|
||||||
|
)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
# Assert text within the page is correct
|
# Assert text within the page is correct
|
||||||
|
@ -1697,7 +1701,7 @@ class TestPortfolioMemberDeleteView(WebTest):
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
# We check X_REQUESTED_WITH bc those return JSON responses
|
# We check X_REQUESTED_WITH bc those return JSON responses
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("member-delete", kwargs={"pk": upp.pk}), HTTP_X_REQUESTED_WITH="XMLHttpRequest"
|
reverse("member-delete", kwargs={"member_pk": upp.pk}), HTTP_X_REQUESTED_WITH="XMLHttpRequest"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 400) # Bad request due to active requests
|
self.assertEqual(response.status_code, 400) # Bad request due to active requests
|
||||||
|
@ -1738,7 +1742,8 @@ class TestPortfolioMemberDeleteView(WebTest):
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
# We check X_REQUESTED_WITH bc those return JSON responses
|
# We check X_REQUESTED_WITH bc those return JSON responses
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("member-delete", kwargs={"pk": admin_perm_user.pk}), HTTP_X_REQUESTED_WITH="XMLHttpRequest"
|
reverse("member-delete", kwargs={"member_pk": admin_perm_user.pk}),
|
||||||
|
HTTP_X_REQUESTED_WITH="XMLHttpRequest",
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 400)
|
self.assertEqual(response.status_code, 400)
|
||||||
|
@ -1795,7 +1800,7 @@ class TestPortfolioMemberDeleteView(WebTest):
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
# We check X_REQUESTED_WITH bc those return JSON responses
|
# We check X_REQUESTED_WITH bc those return JSON responses
|
||||||
reverse("member-delete", kwargs={"pk": upp.pk}),
|
reverse("member-delete", kwargs={"member_pk": upp.pk}),
|
||||||
HTTP_X_REQUESTED_WITH="XMLHttpRequest",
|
HTTP_X_REQUESTED_WITH="XMLHttpRequest",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1862,7 +1867,7 @@ class TestPortfolioMemberDeleteView(WebTest):
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
# We check X_REQUESTED_WITH bc those return JSON responses
|
# We check X_REQUESTED_WITH bc those return JSON responses
|
||||||
reverse("member-delete", kwargs={"pk": upp.pk}),
|
reverse("member-delete", kwargs={"member_pk": upp.pk}),
|
||||||
HTTP_X_REQUESTED_WITH="XMLHttpRequest",
|
HTTP_X_REQUESTED_WITH="XMLHttpRequest",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1939,7 +1944,7 @@ class TestPortfolioMemberDeleteView(WebTest):
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
# We check X_REQUESTED_WITH bc those return JSON responses
|
# We check X_REQUESTED_WITH bc those return JSON responses
|
||||||
reverse("member-delete", kwargs={"pk": upp.pk}),
|
reverse("member-delete", kwargs={"member_pk": upp.pk}),
|
||||||
HTTP_X_REQUESTED_WITH="XMLHttpRequest",
|
HTTP_X_REQUESTED_WITH="XMLHttpRequest",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2000,7 +2005,7 @@ class TestPortfolioMemberDeleteView(WebTest):
|
||||||
with patch("django.contrib.messages.error") as mock_error:
|
with patch("django.contrib.messages.error") as mock_error:
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("member-delete", kwargs={"pk": upp.pk}),
|
reverse("member-delete", kwargs={"member_pk": upp.pk}),
|
||||||
)
|
)
|
||||||
# We don't want to do follow=True in response bc that does automatic redirection
|
# We don't want to do follow=True in response bc that does automatic redirection
|
||||||
|
|
||||||
|
@ -2023,7 +2028,7 @@ class TestPortfolioMemberDeleteView(WebTest):
|
||||||
|
|
||||||
# Location is used for a 3xx HTTP status code to indicate that the URL was redirected
|
# Location is used for a 3xx HTTP status code to indicate that the URL was redirected
|
||||||
# and then confirm that we're still on the Manage Members page
|
# and then confirm that we're still on the Manage Members page
|
||||||
self.assertEqual(response.headers["Location"], reverse("member", kwargs={"pk": upp.pk}))
|
self.assertEqual(response.headers["Location"], reverse("member", kwargs={"member_pk": upp.pk}))
|
||||||
|
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
@override_flag("organization_feature", active=True)
|
@override_flag("organization_feature", active=True)
|
||||||
|
@ -2047,7 +2052,7 @@ class TestPortfolioMemberDeleteView(WebTest):
|
||||||
with patch("django.contrib.messages.error") as mock_error:
|
with patch("django.contrib.messages.error") as mock_error:
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("member-delete", kwargs={"pk": admin_perm_user.pk}),
|
reverse("member-delete", kwargs={"member_pk": admin_perm_user.pk}),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
|
@ -2066,7 +2071,9 @@ class TestPortfolioMemberDeleteView(WebTest):
|
||||||
|
|
||||||
# Location is used for a 3xx HTTP status code to indicate that the URL was redirected
|
# Location is used for a 3xx HTTP status code to indicate that the URL was redirected
|
||||||
# and then confirm that we're still on the Manage Members page
|
# and then confirm that we're still on the Manage Members page
|
||||||
self.assertEqual(response.headers["Location"], reverse("member", kwargs={"pk": admin_perm_user.pk}))
|
self.assertEqual(
|
||||||
|
response.headers["Location"], reverse("member", kwargs={"member_pk": admin_perm_user.pk})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestPortfolioInvitedMemberDeleteView(WebTest):
|
class TestPortfolioInvitedMemberDeleteView(WebTest):
|
||||||
|
@ -2125,7 +2132,7 @@ class TestPortfolioInvitedMemberDeleteView(WebTest):
|
||||||
with patch("django.contrib.messages.success") as mock_success:
|
with patch("django.contrib.messages.success") as mock_success:
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("invitedmember-delete", kwargs={"pk": invitation.pk}),
|
reverse("invitedmember-delete", kwargs={"invitedmember_pk": invitation.pk}),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
|
@ -2190,7 +2197,7 @@ class TestPortfolioInvitedMemberDeleteView(WebTest):
|
||||||
with patch("django.contrib.messages.success") as mock_success:
|
with patch("django.contrib.messages.success") as mock_success:
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("invitedmember-delete", kwargs={"pk": invitation.pk}),
|
reverse("invitedmember-delete", kwargs={"invitedmember_pk": invitation.pk}),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
|
@ -2263,7 +2270,7 @@ class TestPortfolioInvitedMemberDeleteView(WebTest):
|
||||||
with patch("django.contrib.messages.success") as mock_success:
|
with patch("django.contrib.messages.success") as mock_success:
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("invitedmember-delete", kwargs={"pk": invitation.pk}),
|
reverse("invitedmember-delete", kwargs={"invitedmember_pk": invitation.pk}),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
|
@ -2365,7 +2372,7 @@ class TestPortfolioMemberDomainsView(TestWithUser, WebTest):
|
||||||
"""Tests that the portfolio member domains view is accessible."""
|
"""Tests that the portfolio member domains view is accessible."""
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
response = self.client.get(reverse("member-domains", kwargs={"pk": self.permission.id}))
|
response = self.client.get(reverse("member-domains", kwargs={"member_pk": self.permission.id}))
|
||||||
|
|
||||||
# Make sure the page loaded, and that we're on the right page
|
# Make sure the page loaded, and that we're on the right page
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
@ -2378,7 +2385,7 @@ class TestPortfolioMemberDomainsView(TestWithUser, WebTest):
|
||||||
"""Tests that the portfolio member domains view is not accessible to user with no perms."""
|
"""Tests that the portfolio member domains view is not accessible to user with no perms."""
|
||||||
self.client.force_login(self.user_no_perms)
|
self.client.force_login(self.user_no_perms)
|
||||||
|
|
||||||
response = self.client.get(reverse("member-domains", kwargs={"pk": self.permission.id}))
|
response = self.client.get(reverse("member-domains", kwargs={"member_pk": self.permission.id}))
|
||||||
|
|
||||||
# Make sure the request returns forbidden
|
# Make sure the request returns forbidden
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
@ -2390,7 +2397,7 @@ class TestPortfolioMemberDomainsView(TestWithUser, WebTest):
|
||||||
"""Tests that the portfolio member domains view is not accessible when no authenticated user."""
|
"""Tests that the portfolio member domains view is not accessible when no authenticated user."""
|
||||||
self.client.logout()
|
self.client.logout()
|
||||||
|
|
||||||
response = self.client.get(reverse("member-domains", kwargs={"pk": self.permission.id}))
|
response = self.client.get(reverse("member-domains", kwargs={"member_pk": self.permission.id}))
|
||||||
|
|
||||||
# Make sure the request returns redirect to openid login
|
# Make sure the request returns redirect to openid login
|
||||||
self.assertEqual(response.status_code, 302) # Redirect to openid login
|
self.assertEqual(response.status_code, 302) # Redirect to openid login
|
||||||
|
@ -2403,7 +2410,7 @@ class TestPortfolioMemberDomainsView(TestWithUser, WebTest):
|
||||||
"""Tests that the portfolio member domains view returns not found if user portfolio permission not found."""
|
"""Tests that the portfolio member domains view returns not found if user portfolio permission not found."""
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
response = self.client.get(reverse("member-domains", kwargs={"pk": "0"}))
|
response = self.client.get(reverse("member-domains", kwargs={"member_pk": "0"}))
|
||||||
|
|
||||||
# Make sure the response is not found
|
# Make sure the response is not found
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
@ -2463,7 +2470,7 @@ class TestPortfolioInvitedMemberDomainsView(TestWithUser, WebTest):
|
||||||
"""Tests that the portfolio invited member domains view is accessible."""
|
"""Tests that the portfolio invited member domains view is accessible."""
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
response = self.client.get(reverse("invitedmember-domains", kwargs={"pk": self.invitation.id}))
|
response = self.client.get(reverse("invitedmember-domains", kwargs={"invitedmember_pk": self.invitation.id}))
|
||||||
|
|
||||||
# Make sure the page loaded, and that we're on the right page
|
# Make sure the page loaded, and that we're on the right page
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
@ -2476,7 +2483,7 @@ class TestPortfolioInvitedMemberDomainsView(TestWithUser, WebTest):
|
||||||
"""Tests that the portfolio invited member domains view is not accessible to user with no perms."""
|
"""Tests that the portfolio invited member domains view is not accessible to user with no perms."""
|
||||||
self.client.force_login(self.user_no_perms)
|
self.client.force_login(self.user_no_perms)
|
||||||
|
|
||||||
response = self.client.get(reverse("invitedmember-domains", kwargs={"pk": self.invitation.id}))
|
response = self.client.get(reverse("invitedmember-domains", kwargs={"invitedmember_pk": self.invitation.id}))
|
||||||
|
|
||||||
# Make sure the request returns forbidden
|
# Make sure the request returns forbidden
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
@ -2488,7 +2495,7 @@ class TestPortfolioInvitedMemberDomainsView(TestWithUser, WebTest):
|
||||||
"""Tests that the portfolio invited member domains view is not accessible when no authenticated user."""
|
"""Tests that the portfolio invited member domains view is not accessible when no authenticated user."""
|
||||||
self.client.logout()
|
self.client.logout()
|
||||||
|
|
||||||
response = self.client.get(reverse("invitedmember-domains", kwargs={"pk": self.invitation.id}))
|
response = self.client.get(reverse("invitedmember-domains", kwargs={"invitedmember_pk": self.invitation.id}))
|
||||||
|
|
||||||
# Make sure the request returns redirect to openid login
|
# Make sure the request returns redirect to openid login
|
||||||
self.assertEqual(response.status_code, 302) # Redirect to openid login
|
self.assertEqual(response.status_code, 302) # Redirect to openid login
|
||||||
|
@ -2501,7 +2508,7 @@ class TestPortfolioInvitedMemberDomainsView(TestWithUser, WebTest):
|
||||||
"""Tests that the portfolio invited member domains view returns not found if user is not a member."""
|
"""Tests that the portfolio invited member domains view returns not found if user is not a member."""
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
response = self.client.get(reverse("invitedmember-domains", kwargs={"pk": "0"}))
|
response = self.client.get(reverse("invitedmember-domains", kwargs={"invitedmember_pk": "0"}))
|
||||||
|
|
||||||
# Make sure the response is not found
|
# Make sure the response is not found
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
@ -2566,7 +2573,7 @@ class TestPortfolioMemberDomainsEditView(TestWithUser, WebTest):
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
# Create url to be used in all tests
|
# Create url to be used in all tests
|
||||||
self.url = reverse("member-domains-edit", kwargs={"pk": self.portfolio_permission.pk})
|
self.url = reverse("member-domains-edit", kwargs={"member_pk": self.portfolio_permission.pk})
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
|
@ -2584,7 +2591,7 @@ class TestPortfolioMemberDomainsEditView(TestWithUser, WebTest):
|
||||||
"""Tests that the portfolio member domains edit view is accessible."""
|
"""Tests that the portfolio member domains edit view is accessible."""
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
response = self.client.get(reverse("member-domains-edit", kwargs={"pk": self.permission.id}))
|
response = self.client.get(reverse("member-domains-edit", kwargs={"member_pk": self.permission.id}))
|
||||||
|
|
||||||
# Make sure the page loaded, and that we're on the right page
|
# Make sure the page loaded, and that we're on the right page
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
@ -2597,7 +2604,7 @@ class TestPortfolioMemberDomainsEditView(TestWithUser, WebTest):
|
||||||
"""Tests that the portfolio member domains edit view is not accessible to user with no perms."""
|
"""Tests that the portfolio member domains edit view is not accessible to user with no perms."""
|
||||||
self.client.force_login(self.user_no_perms)
|
self.client.force_login(self.user_no_perms)
|
||||||
|
|
||||||
response = self.client.get(reverse("member-domains-edit", kwargs={"pk": self.permission.id}))
|
response = self.client.get(reverse("member-domains-edit", kwargs={"member_pk": self.permission.id}))
|
||||||
|
|
||||||
# Make sure the request returns forbidden
|
# Make sure the request returns forbidden
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
@ -2609,7 +2616,7 @@ class TestPortfolioMemberDomainsEditView(TestWithUser, WebTest):
|
||||||
"""Tests that the portfolio member domains edit view is not accessible when no authenticated user."""
|
"""Tests that the portfolio member domains edit view is not accessible when no authenticated user."""
|
||||||
self.client.logout()
|
self.client.logout()
|
||||||
|
|
||||||
response = self.client.get(reverse("member-domains-edit", kwargs={"pk": self.permission.id}))
|
response = self.client.get(reverse("member-domains-edit", kwargs={"member_pk": self.permission.id}))
|
||||||
|
|
||||||
# Make sure the request returns redirect to openid login
|
# Make sure the request returns redirect to openid login
|
||||||
self.assertEqual(response.status_code, 302) # Redirect to openid login
|
self.assertEqual(response.status_code, 302) # Redirect to openid login
|
||||||
|
@ -2623,7 +2630,7 @@ class TestPortfolioMemberDomainsEditView(TestWithUser, WebTest):
|
||||||
portfolio permission not found."""
|
portfolio permission not found."""
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
response = self.client.get(reverse("member-domains-edit", kwargs={"pk": "0"}))
|
response = self.client.get(reverse("member-domains-edit", kwargs={"member_pk": "0"}))
|
||||||
|
|
||||||
# Make sure the response is not found
|
# Make sure the response is not found
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
@ -2645,7 +2652,7 @@ class TestPortfolioMemberDomainsEditView(TestWithUser, WebTest):
|
||||||
self.assertEqual(UserDomainRole.objects.filter(user=self.user, role=UserDomainRole.Roles.MANAGER).count(), 3)
|
self.assertEqual(UserDomainRole.objects.filter(user=self.user, role=UserDomainRole.Roles.MANAGER).count(), 3)
|
||||||
|
|
||||||
# Check for a success message and a redirect
|
# Check for a success message and a redirect
|
||||||
self.assertRedirects(response, reverse("member-domains", kwargs={"pk": self.portfolio_permission.pk}))
|
self.assertRedirects(response, reverse("member-domains", kwargs={"member_pk": self.portfolio_permission.pk}))
|
||||||
messages = list(response.wsgi_request._messages)
|
messages = list(response.wsgi_request._messages)
|
||||||
self.assertEqual(len(messages), 1)
|
self.assertEqual(len(messages), 1)
|
||||||
self.assertEqual(str(messages[0]), "The domain assignment changes have been saved.")
|
self.assertEqual(str(messages[0]), "The domain assignment changes have been saved.")
|
||||||
|
@ -2681,7 +2688,7 @@ class TestPortfolioMemberDomainsEditView(TestWithUser, WebTest):
|
||||||
self.assertEqual(UserDomainRole.objects.filter(domain=self.domain3, user=self.user).count(), 1)
|
self.assertEqual(UserDomainRole.objects.filter(domain=self.domain3, user=self.user).count(), 1)
|
||||||
|
|
||||||
# Check for a success message and a redirect
|
# Check for a success message and a redirect
|
||||||
self.assertRedirects(response, reverse("member-domains", kwargs={"pk": self.portfolio_permission.pk}))
|
self.assertRedirects(response, reverse("member-domains", kwargs={"member_pk": self.portfolio_permission.pk}))
|
||||||
messages = list(response.wsgi_request._messages)
|
messages = list(response.wsgi_request._messages)
|
||||||
self.assertEqual(len(messages), 1)
|
self.assertEqual(len(messages), 1)
|
||||||
self.assertEqual(str(messages[0]), "The domain assignment changes have been saved.")
|
self.assertEqual(str(messages[0]), "The domain assignment changes have been saved.")
|
||||||
|
@ -2706,7 +2713,7 @@ class TestPortfolioMemberDomainsEditView(TestWithUser, WebTest):
|
||||||
self.assertEqual(UserDomainRole.objects.filter(user=self.user).count(), 0)
|
self.assertEqual(UserDomainRole.objects.filter(user=self.user).count(), 0)
|
||||||
|
|
||||||
# Check for an error message and a redirect
|
# Check for an error message and a redirect
|
||||||
self.assertRedirects(response, reverse("member-domains", kwargs={"pk": self.portfolio_permission.pk}))
|
self.assertRedirects(response, reverse("member-domains", kwargs={"member_pk": self.portfolio_permission.pk}))
|
||||||
messages = list(response.wsgi_request._messages)
|
messages = list(response.wsgi_request._messages)
|
||||||
self.assertEqual(len(messages), 1)
|
self.assertEqual(len(messages), 1)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -2729,7 +2736,7 @@ class TestPortfolioMemberDomainsEditView(TestWithUser, WebTest):
|
||||||
self.assertEqual(UserDomainRole.objects.filter(user=self.user).count(), 0)
|
self.assertEqual(UserDomainRole.objects.filter(user=self.user).count(), 0)
|
||||||
|
|
||||||
# Check for an error message and a redirect
|
# Check for an error message and a redirect
|
||||||
self.assertRedirects(response, reverse("member-domains", kwargs={"pk": self.portfolio_permission.pk}))
|
self.assertRedirects(response, reverse("member-domains", kwargs={"member_pk": self.portfolio_permission.pk}))
|
||||||
messages = list(response.wsgi_request._messages)
|
messages = list(response.wsgi_request._messages)
|
||||||
self.assertEqual(len(messages), 1)
|
self.assertEqual(len(messages), 1)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -2749,7 +2756,7 @@ class TestPortfolioMemberDomainsEditView(TestWithUser, WebTest):
|
||||||
self.assertEqual(UserDomainRole.objects.filter(user=self.user).count(), 0)
|
self.assertEqual(UserDomainRole.objects.filter(user=self.user).count(), 0)
|
||||||
|
|
||||||
# Check for an info message and a redirect
|
# Check for an info message and a redirect
|
||||||
self.assertRedirects(response, reverse("member-domains", kwargs={"pk": self.portfolio_permission.pk}))
|
self.assertRedirects(response, reverse("member-domains", kwargs={"member_pk": self.portfolio_permission.pk}))
|
||||||
messages = list(response.wsgi_request._messages)
|
messages = list(response.wsgi_request._messages)
|
||||||
self.assertEqual(len(messages), 1)
|
self.assertEqual(len(messages), 1)
|
||||||
self.assertEqual(str(messages[0]), "The domain assignment changes have been saved.")
|
self.assertEqual(str(messages[0]), "The domain assignment changes have been saved.")
|
||||||
|
@ -2772,7 +2779,9 @@ class TestPortfolioMemberDomainsEditView(TestWithUser, WebTest):
|
||||||
self.assertEqual(UserDomainRole.objects.filter(user=self.user, role=UserDomainRole.Roles.MANAGER).count(), 0)
|
self.assertEqual(UserDomainRole.objects.filter(user=self.user, role=UserDomainRole.Roles.MANAGER).count(), 0)
|
||||||
|
|
||||||
# Check for an error message and a redirect to edit form
|
# Check for an error message and a redirect to edit form
|
||||||
self.assertRedirects(response, reverse("member-domains-edit", kwargs={"pk": self.portfolio_permission.pk}))
|
self.assertRedirects(
|
||||||
|
response, reverse("member-domains-edit", kwargs={"member_pk": self.portfolio_permission.pk})
|
||||||
|
)
|
||||||
messages = list(response.wsgi_request._messages)
|
messages = list(response.wsgi_request._messages)
|
||||||
self.assertEqual(len(messages), 1)
|
self.assertEqual(len(messages), 1)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -2831,7 +2840,7 @@ class TestPortfolioInvitedMemberEditDomainsView(TestWithUser, WebTest):
|
||||||
UserPortfolioPermissionChoices.EDIT_MEMBERS,
|
UserPortfolioPermissionChoices.EDIT_MEMBERS,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
self.url = reverse("invitedmember-domains-edit", kwargs={"pk": self.invitation.pk})
|
self.url = reverse("invitedmember-domains-edit", kwargs={"invitedmember_pk": self.invitation.pk})
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
|
@ -2849,7 +2858,9 @@ class TestPortfolioInvitedMemberEditDomainsView(TestWithUser, WebTest):
|
||||||
"""Tests that the portfolio invited member domains edit view is accessible."""
|
"""Tests that the portfolio invited member domains edit view is accessible."""
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
response = self.client.get(reverse("invitedmember-domains-edit", kwargs={"pk": self.invitation.id}))
|
response = self.client.get(
|
||||||
|
reverse("invitedmember-domains-edit", kwargs={"invitedmember_pk": self.invitation.id})
|
||||||
|
)
|
||||||
|
|
||||||
# Make sure the page loaded, and that we're on the right page
|
# Make sure the page loaded, and that we're on the right page
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
@ -2862,7 +2873,9 @@ class TestPortfolioInvitedMemberEditDomainsView(TestWithUser, WebTest):
|
||||||
"""Tests that the portfolio invited member domains edit view is not accessible to user with no perms."""
|
"""Tests that the portfolio invited member domains edit view is not accessible to user with no perms."""
|
||||||
self.client.force_login(self.user_no_perms)
|
self.client.force_login(self.user_no_perms)
|
||||||
|
|
||||||
response = self.client.get(reverse("invitedmember-domains-edit", kwargs={"pk": self.invitation.id}))
|
response = self.client.get(
|
||||||
|
reverse("invitedmember-domains-edit", kwargs={"invitedmember_pk": self.invitation.id})
|
||||||
|
)
|
||||||
|
|
||||||
# Make sure the request returns forbidden
|
# Make sure the request returns forbidden
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
@ -2874,7 +2887,9 @@ class TestPortfolioInvitedMemberEditDomainsView(TestWithUser, WebTest):
|
||||||
"""Tests that the portfolio invited member domains edit view is not accessible when no authenticated user."""
|
"""Tests that the portfolio invited member domains edit view is not accessible when no authenticated user."""
|
||||||
self.client.logout()
|
self.client.logout()
|
||||||
|
|
||||||
response = self.client.get(reverse("invitedmember-domains-edit", kwargs={"pk": self.invitation.id}))
|
response = self.client.get(
|
||||||
|
reverse("invitedmember-domains-edit", kwargs={"invitedmember_pk": self.invitation.id})
|
||||||
|
)
|
||||||
|
|
||||||
# Make sure the request returns redirect to openid login
|
# Make sure the request returns redirect to openid login
|
||||||
self.assertEqual(response.status_code, 302) # Redirect to openid login
|
self.assertEqual(response.status_code, 302) # Redirect to openid login
|
||||||
|
@ -2887,7 +2902,7 @@ class TestPortfolioInvitedMemberEditDomainsView(TestWithUser, WebTest):
|
||||||
"""Tests that the portfolio invited member domains edit view returns not found if user is not a member."""
|
"""Tests that the portfolio invited member domains edit view returns not found if user is not a member."""
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
response = self.client.get(reverse("invitedmember-domains-edit", kwargs={"pk": "0"}))
|
response = self.client.get(reverse("invitedmember-domains-edit", kwargs={"invitedmember_pk": "0"}))
|
||||||
|
|
||||||
# Make sure the response is not found
|
# Make sure the response is not found
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
@ -2914,7 +2929,9 @@ class TestPortfolioInvitedMemberEditDomainsView(TestWithUser, WebTest):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check for a success message and a redirect
|
# Check for a success message and a redirect
|
||||||
self.assertRedirects(response, reverse("invitedmember-domains", kwargs={"pk": self.invitation.pk}))
|
self.assertRedirects(
|
||||||
|
response, reverse("invitedmember-domains", kwargs={"invitedmember_pk": self.invitation.pk})
|
||||||
|
)
|
||||||
messages = list(response.wsgi_request._messages)
|
messages = list(response.wsgi_request._messages)
|
||||||
self.assertEqual(len(messages), 1)
|
self.assertEqual(len(messages), 1)
|
||||||
self.assertEqual(str(messages[0]), "The domain assignment changes have been saved.")
|
self.assertEqual(str(messages[0]), "The domain assignment changes have been saved.")
|
||||||
|
@ -2971,7 +2988,9 @@ class TestPortfolioInvitedMemberEditDomainsView(TestWithUser, WebTest):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check for a success message and a redirect
|
# Check for a success message and a redirect
|
||||||
self.assertRedirects(response, reverse("invitedmember-domains", kwargs={"pk": self.invitation.pk}))
|
self.assertRedirects(
|
||||||
|
response, reverse("invitedmember-domains", kwargs={"invitedmember_pk": self.invitation.pk})
|
||||||
|
)
|
||||||
|
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
@override_flag("organization_feature", active=True)
|
@override_flag("organization_feature", active=True)
|
||||||
|
@ -3015,7 +3034,9 @@ class TestPortfolioInvitedMemberEditDomainsView(TestWithUser, WebTest):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check for a success message and a redirect
|
# Check for a success message and a redirect
|
||||||
self.assertRedirects(response, reverse("invitedmember-domains", kwargs={"pk": self.invitation.pk}))
|
self.assertRedirects(
|
||||||
|
response, reverse("invitedmember-domains", kwargs={"invitedmember_pk": self.invitation.pk})
|
||||||
|
)
|
||||||
# assert that send_domain_invitation_email is not called
|
# assert that send_domain_invitation_email is not called
|
||||||
mock_send_domain_email.assert_not_called()
|
mock_send_domain_email.assert_not_called()
|
||||||
|
|
||||||
|
@ -3035,7 +3056,9 @@ class TestPortfolioInvitedMemberEditDomainsView(TestWithUser, WebTest):
|
||||||
self.assertEqual(DomainInvitation.objects.count(), 0)
|
self.assertEqual(DomainInvitation.objects.count(), 0)
|
||||||
|
|
||||||
# Check for an error message and a redirect
|
# Check for an error message and a redirect
|
||||||
self.assertRedirects(response, reverse("invitedmember-domains", kwargs={"pk": self.invitation.pk}))
|
self.assertRedirects(
|
||||||
|
response, reverse("invitedmember-domains", kwargs={"invitedmember_pk": self.invitation.pk})
|
||||||
|
)
|
||||||
messages = list(response.wsgi_request._messages)
|
messages = list(response.wsgi_request._messages)
|
||||||
self.assertEqual(len(messages), 1)
|
self.assertEqual(len(messages), 1)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -3058,7 +3081,9 @@ class TestPortfolioInvitedMemberEditDomainsView(TestWithUser, WebTest):
|
||||||
self.assertEqual(DomainInvitation.objects.count(), 0)
|
self.assertEqual(DomainInvitation.objects.count(), 0)
|
||||||
|
|
||||||
# Check for an error message and a redirect
|
# Check for an error message and a redirect
|
||||||
self.assertRedirects(response, reverse("invitedmember-domains", kwargs={"pk": self.invitation.pk}))
|
self.assertRedirects(
|
||||||
|
response, reverse("invitedmember-domains", kwargs={"invitedmember_pk": self.invitation.pk})
|
||||||
|
)
|
||||||
messages = list(response.wsgi_request._messages)
|
messages = list(response.wsgi_request._messages)
|
||||||
self.assertEqual(len(messages), 1)
|
self.assertEqual(len(messages), 1)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -3078,7 +3103,9 @@ class TestPortfolioInvitedMemberEditDomainsView(TestWithUser, WebTest):
|
||||||
self.assertEqual(DomainInvitation.objects.count(), 0)
|
self.assertEqual(DomainInvitation.objects.count(), 0)
|
||||||
|
|
||||||
# Check for an info message and a redirect
|
# Check for an info message and a redirect
|
||||||
self.assertRedirects(response, reverse("invitedmember-domains", kwargs={"pk": self.invitation.pk}))
|
self.assertRedirects(
|
||||||
|
response, reverse("invitedmember-domains", kwargs={"invitedmember_pk": self.invitation.pk})
|
||||||
|
)
|
||||||
messages = list(response.wsgi_request._messages)
|
messages = list(response.wsgi_request._messages)
|
||||||
self.assertEqual(len(messages), 1)
|
self.assertEqual(len(messages), 1)
|
||||||
self.assertEqual(str(messages[0]), "The domain assignment changes have been saved.")
|
self.assertEqual(str(messages[0]), "The domain assignment changes have been saved.")
|
||||||
|
@ -3106,7 +3133,9 @@ class TestPortfolioInvitedMemberEditDomainsView(TestWithUser, WebTest):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check for an error message and a redirect to edit form
|
# Check for an error message and a redirect to edit form
|
||||||
self.assertRedirects(response, reverse("invitedmember-domains-edit", kwargs={"pk": self.invitation.pk}))
|
self.assertRedirects(
|
||||||
|
response, reverse("invitedmember-domains-edit", kwargs={"invitedmember_pk": self.invitation.pk})
|
||||||
|
)
|
||||||
messages = list(response.wsgi_request._messages)
|
messages = list(response.wsgi_request._messages)
|
||||||
self.assertEqual(len(messages), 1)
|
self.assertEqual(len(messages), 1)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -4081,7 +4110,7 @@ class TestPortfolioMemberEditView(WebTest):
|
||||||
mock_send_update_email.return_value = True
|
mock_send_update_email.return_value = True
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("member-permissions", kwargs={"pk": basic_permission.id}),
|
reverse("member-permissions", kwargs={"member_pk": basic_permission.id}),
|
||||||
{
|
{
|
||||||
"role": UserPortfolioRoleChoices.ORGANIZATION_ADMIN,
|
"role": UserPortfolioRoleChoices.ORGANIZATION_ADMIN,
|
||||||
},
|
},
|
||||||
|
@ -4144,7 +4173,7 @@ class TestPortfolioMemberEditView(WebTest):
|
||||||
mock_send_update_email.return_value = False
|
mock_send_update_email.return_value = False
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("member-permissions", kwargs={"pk": basic_permission.id}),
|
reverse("member-permissions", kwargs={"member_pk": basic_permission.id}),
|
||||||
{
|
{
|
||||||
"role": UserPortfolioRoleChoices.ORGANIZATION_ADMIN,
|
"role": UserPortfolioRoleChoices.ORGANIZATION_ADMIN,
|
||||||
},
|
},
|
||||||
|
@ -4211,7 +4240,7 @@ class TestPortfolioMemberEditView(WebTest):
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("member-permissions", kwargs={"pk": admin_permission.id}),
|
reverse("member-permissions", kwargs={"member_pk": admin_permission.id}),
|
||||||
{
|
{
|
||||||
"role": UserPortfolioRoleChoices.ORGANIZATION_ADMIN,
|
"role": UserPortfolioRoleChoices.ORGANIZATION_ADMIN,
|
||||||
},
|
},
|
||||||
|
@ -4249,7 +4278,7 @@ class TestPortfolioMemberEditView(WebTest):
|
||||||
mock_send_update_email.return_value = True
|
mock_send_update_email.return_value = True
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("member-permissions", kwargs={"pk": basic_permission.id}),
|
reverse("member-permissions", kwargs={"member_pk": basic_permission.id}),
|
||||||
{
|
{
|
||||||
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
||||||
"domain_permissions": UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS,
|
"domain_permissions": UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS,
|
||||||
|
@ -4298,7 +4327,7 @@ class TestPortfolioMemberEditView(WebTest):
|
||||||
mock_send_update_email.return_value = True
|
mock_send_update_email.return_value = True
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("member-permissions", kwargs={"pk": admin_permission.id}),
|
reverse("member-permissions", kwargs={"member_pk": admin_permission.id}),
|
||||||
{
|
{
|
||||||
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
||||||
"domain_permissions": UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS,
|
"domain_permissions": UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS,
|
||||||
|
@ -4361,7 +4390,7 @@ class TestPortfolioMemberEditView(WebTest):
|
||||||
mock_send_update_email.return_value = False
|
mock_send_update_email.return_value = False
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("member-permissions", kwargs={"pk": admin_permission.id}),
|
reverse("member-permissions", kwargs={"member_pk": admin_permission.id}),
|
||||||
{
|
{
|
||||||
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
||||||
"domain_permissions": UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS,
|
"domain_permissions": UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS,
|
||||||
|
@ -4421,7 +4450,7 @@ class TestPortfolioMemberEditView(WebTest):
|
||||||
|
|
||||||
# Test missing required admin permissions
|
# Test missing required admin permissions
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("member-permissions", kwargs={"pk": permission.id}),
|
reverse("member-permissions", kwargs={"member_pk": permission.id}),
|
||||||
{
|
{
|
||||||
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
||||||
# Missing required admin fields
|
# Missing required admin fields
|
||||||
|
@ -4453,7 +4482,7 @@ class TestPortfolioMemberEditView(WebTest):
|
||||||
admin_permission = UserPortfolioPermission.objects.get(user=self.user, portfolio=self.portfolio)
|
admin_permission = UserPortfolioPermission.objects.get(user=self.user, portfolio=self.portfolio)
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("member-permissions", kwargs={"pk": admin_permission.id}),
|
reverse("member-permissions", kwargs={"member_pk": admin_permission.id}),
|
||||||
{
|
{
|
||||||
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
||||||
"domain_permissions": UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS,
|
"domain_permissions": UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS,
|
||||||
|
@ -4526,7 +4555,7 @@ class TestPortfolioInvitedMemberEditView(WebTest):
|
||||||
|
|
||||||
# Test updating invitation permissions
|
# Test updating invitation permissions
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("invitedmember-permissions", kwargs={"pk": self.invitation.id}),
|
reverse("invitedmember-permissions", kwargs={"invitedmember_pk": self.invitation.id}),
|
||||||
{
|
{
|
||||||
"role": UserPortfolioRoleChoices.ORGANIZATION_ADMIN,
|
"role": UserPortfolioRoleChoices.ORGANIZATION_ADMIN,
|
||||||
},
|
},
|
||||||
|
@ -4569,7 +4598,7 @@ class TestPortfolioInvitedMemberEditView(WebTest):
|
||||||
|
|
||||||
# Test updating invitation permissions
|
# Test updating invitation permissions
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("invitedmember-permissions", kwargs={"pk": self.invitation.id}),
|
reverse("invitedmember-permissions", kwargs={"invitedmember_pk": self.invitation.id}),
|
||||||
{
|
{
|
||||||
"role": UserPortfolioRoleChoices.ORGANIZATION_ADMIN,
|
"role": UserPortfolioRoleChoices.ORGANIZATION_ADMIN,
|
||||||
},
|
},
|
||||||
|
@ -4615,7 +4644,7 @@ class TestPortfolioInvitedMemberEditView(WebTest):
|
||||||
|
|
||||||
# Test updating invitation permissions
|
# Test updating invitation permissions
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("invitedmember-permissions", kwargs={"pk": self.admin_invitation.id}),
|
reverse("invitedmember-permissions", kwargs={"invitedmember_pk": self.admin_invitation.id}),
|
||||||
{
|
{
|
||||||
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
||||||
"domain_permissions": UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS,
|
"domain_permissions": UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS,
|
||||||
|
@ -4661,7 +4690,7 @@ class TestPortfolioInvitedMemberEditView(WebTest):
|
||||||
|
|
||||||
# Test updating invitation permissions
|
# Test updating invitation permissions
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("invitedmember-permissions", kwargs={"pk": self.admin_invitation.id}),
|
reverse("invitedmember-permissions", kwargs={"invitedmember_pk": self.admin_invitation.id}),
|
||||||
{
|
{
|
||||||
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
||||||
"domain_permissions": UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS,
|
"domain_permissions": UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS,
|
||||||
|
@ -4707,7 +4736,7 @@ class TestPortfolioInvitedMemberEditView(WebTest):
|
||||||
|
|
||||||
# Test updating invitation permissions
|
# Test updating invitation permissions
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("invitedmember-permissions", kwargs={"pk": self.invitation.id}),
|
reverse("invitedmember-permissions", kwargs={"invitedmember_pk": self.invitation.id}),
|
||||||
{
|
{
|
||||||
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
||||||
"domain_permissions": UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS,
|
"domain_permissions": UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS,
|
||||||
|
@ -4734,7 +4763,7 @@ class TestPortfolioInvitedMemberEditView(WebTest):
|
||||||
|
|
||||||
# Test updating invitation permissions
|
# Test updating invitation permissions
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("invitedmember-permissions", kwargs={"pk": self.admin_invitation.id}),
|
reverse("invitedmember-permissions", kwargs={"invitedmember_pk": self.admin_invitation.id}),
|
||||||
{
|
{
|
||||||
"role": UserPortfolioRoleChoices.ORGANIZATION_ADMIN,
|
"role": UserPortfolioRoleChoices.ORGANIZATION_ADMIN,
|
||||||
},
|
},
|
||||||
|
|
|
@ -2796,7 +2796,7 @@ class DomainRequestTests(TestWithUser, WebTest):
|
||||||
self.assertEqual(intro_page.status_code, 200)
|
self.assertEqual(intro_page.status_code, 200)
|
||||||
|
|
||||||
# This user should also be allowed to edit existing ones
|
# This user should also be allowed to edit existing ones
|
||||||
domain_request = completed_domain_request(user=self.user)
|
domain_request = completed_domain_request(user=self.user, portfolio=portfolio)
|
||||||
edit_page = self.app.get(
|
edit_page = self.app.get(
|
||||||
reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk})
|
reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk})
|
||||||
).follow()
|
).follow()
|
||||||
|
@ -2904,7 +2904,9 @@ class DomainRequestTestDifferentStatuses(TestWithUser, WebTest):
|
||||||
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
additional_permissions=[UserPortfolioPermissionChoices.EDIT_REQUESTS],
|
additional_permissions=[UserPortfolioPermissionChoices.EDIT_REQUESTS],
|
||||||
)
|
)
|
||||||
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user)
|
domain_request = completed_domain_request(
|
||||||
|
status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user, portfolio=portfolio
|
||||||
|
)
|
||||||
domain_request.save()
|
domain_request.save()
|
||||||
|
|
||||||
detail_page = self.app.get(f"/domain-request/{domain_request.id}")
|
detail_page = self.app.get(f"/domain-request/{domain_request.id}")
|
||||||
|
@ -3044,13 +3046,17 @@ class TestDomainRequestWizard(TestWithUser, WebTest):
|
||||||
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
additional_permissions=[UserPortfolioPermissionChoices.EDIT_REQUESTS],
|
additional_permissions=[UserPortfolioPermissionChoices.EDIT_REQUESTS],
|
||||||
)
|
)
|
||||||
|
domain_request.portfolio = portfolio
|
||||||
|
domain_request.save()
|
||||||
|
domain_request.refresh_from_db()
|
||||||
|
|
||||||
# Check portfolio-specific breadcrumb
|
# Check portfolio-specific breadcrumb
|
||||||
portfolio_page = self.app.get(f"/domain-request/{domain_request.id}/edit/").follow()
|
portfolio_page = self.app.get(f"/domain-request/{domain_request.id}/edit/").follow()
|
||||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
|
|
||||||
self.assertContains(portfolio_page, "Domain requests")
|
self.assertContains(portfolio_page, "Domain requests")
|
||||||
|
domain_request.portfolio = None
|
||||||
|
domain_request.save()
|
||||||
# Clean up portfolio
|
# Clean up portfolio
|
||||||
permission.delete()
|
permission.delete()
|
||||||
portfolio.delete()
|
portfolio.delete()
|
||||||
|
@ -3177,15 +3183,6 @@ class TestDomainRequestWizard(TestWithUser, WebTest):
|
||||||
- The user does not see the Domain and Domain requests buttons
|
- The user does not see the Domain and Domain requests buttons
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# This should unlock 4 steps by default.
|
|
||||||
# Purpose, .gov domain, current websites, and requirements for operating
|
|
||||||
domain_request = completed_domain_request(
|
|
||||||
status=DomainRequest.DomainRequestStatus.STARTED,
|
|
||||||
user=self.user,
|
|
||||||
)
|
|
||||||
domain_request.anything_else = None
|
|
||||||
domain_request.save()
|
|
||||||
|
|
||||||
federal_agency = FederalAgency.objects.get(agency="Non-Federal Agency")
|
federal_agency = FederalAgency.objects.get(agency="Non-Federal Agency")
|
||||||
# Add a portfolio
|
# Add a portfolio
|
||||||
portfolio = Portfolio.objects.create(
|
portfolio = Portfolio.objects.create(
|
||||||
|
@ -3203,6 +3200,14 @@ class TestDomainRequestWizard(TestWithUser, WebTest):
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# This should unlock 4 steps by default.
|
||||||
|
# Purpose, .gov domain, current websites, and requirements for operating
|
||||||
|
domain_request = completed_domain_request(
|
||||||
|
status=DomainRequest.DomainRequestStatus.STARTED, user=self.user, portfolio=portfolio
|
||||||
|
)
|
||||||
|
domain_request.anything_else = None
|
||||||
|
domain_request.save()
|
||||||
|
|
||||||
response = self.app.get(f"/domain-request/{domain_request.id}/edit/")
|
response = self.app.get(f"/domain-request/{domain_request.id}/edit/")
|
||||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||||
# resetting the session key on each new request, thus destroying the concept
|
# resetting the session key on each new request, thus destroying the concept
|
||||||
|
@ -3247,6 +3252,8 @@ class TestDomainRequestWizard(TestWithUser, WebTest):
|
||||||
self.fail(f"Expected a redirect, but got a different response: {response}")
|
self.fail(f"Expected a redirect, but got a different response: {response}")
|
||||||
|
|
||||||
# Data cleanup
|
# Data cleanup
|
||||||
|
domain_request.portfolio = None
|
||||||
|
domain_request.save()
|
||||||
user_portfolio_permission.delete()
|
user_portfolio_permission.delete()
|
||||||
portfolio.delete()
|
portfolio.delete()
|
||||||
federal_agency.delete()
|
federal_agency.delete()
|
||||||
|
@ -3311,7 +3318,9 @@ class TestPortfolioDomainRequestViewonly(TestWithUser, WebTest):
|
||||||
user=self.user, portfolio=portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
user=self.user, portfolio=portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
||||||
)
|
)
|
||||||
dummy_user, _ = User.objects.get_or_create(username="testusername123456")
|
dummy_user, _ = User.objects.get_or_create(username="testusername123456")
|
||||||
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=dummy_user)
|
domain_request = completed_domain_request(
|
||||||
|
status=DomainRequest.DomainRequestStatus.SUBMITTED, user=dummy_user, portfolio=portfolio
|
||||||
|
)
|
||||||
domain_request.save()
|
domain_request.save()
|
||||||
|
|
||||||
detail_page = self.app.get(f"/domain-request/viewonly/{domain_request.id}")
|
detail_page = self.app.get(f"/domain-request/viewonly/{domain_request.id}")
|
||||||
|
|
|
@ -213,9 +213,12 @@ class PortfolioMembersJson(View):
|
||||||
view_only = not user.has_edit_members_portfolio_permission(portfolio) or not user_can_edit_other_users
|
view_only = not user.has_edit_members_portfolio_permission(portfolio) or not user_can_edit_other_users
|
||||||
|
|
||||||
is_admin = UserPortfolioRoleChoices.ORGANIZATION_ADMIN in (item.get("roles") or [])
|
is_admin = UserPortfolioRoleChoices.ORGANIZATION_ADMIN in (item.get("roles") or [])
|
||||||
action_url = reverse(item["type"], kwargs={"pk": item["id"]})
|
|
||||||
|
|
||||||
item_type = item.get("type", "")
|
item_type = item.get("type", "")
|
||||||
|
if item_type == "invitedmember":
|
||||||
|
action_url = reverse(item["type"], kwargs={"invitedmember_pk": item["id"]})
|
||||||
|
else:
|
||||||
|
action_url = reverse(item["type"], kwargs={"member_pk": item["id"]})
|
||||||
|
|
||||||
# Ensure domain_info is properly processed for invites -
|
# Ensure domain_info is properly processed for invites -
|
||||||
# we need to un-concatenate the subquery
|
# we need to un-concatenate the subquery
|
||||||
|
|
|
@ -76,9 +76,10 @@ class PortfolioMemberView(DetailView, View):
|
||||||
model = Portfolio
|
model = Portfolio
|
||||||
context_object_name = "portfolio"
|
context_object_name = "portfolio"
|
||||||
template_name = "portfolio_member.html"
|
template_name = "portfolio_member.html"
|
||||||
|
pk_url_kwarg = "member_pk"
|
||||||
|
|
||||||
def get(self, request, pk):
|
def get(self, request, member_pk):
|
||||||
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=pk)
|
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=member_pk)
|
||||||
member = portfolio_permission.user
|
member = portfolio_permission.user
|
||||||
|
|
||||||
# We have to explicitely name these with member_ otherwise we'll have conflicts with context preprocessors
|
# We have to explicitely name these with member_ otherwise we'll have conflicts with context preprocessors
|
||||||
|
@ -102,8 +103,8 @@ class PortfolioMemberView(DetailView, View):
|
||||||
request,
|
request,
|
||||||
self.template_name,
|
self.template_name,
|
||||||
{
|
{
|
||||||
"edit_url": reverse("member-permissions", args=[pk]),
|
"edit_url": reverse("member-permissions", args=[member_pk]),
|
||||||
"domains_url": reverse("member-domains", args=[pk]),
|
"domains_url": reverse("member-domains", args=[member_pk]),
|
||||||
"portfolio_permission": portfolio_permission,
|
"portfolio_permission": portfolio_permission,
|
||||||
"member": member,
|
"member": member,
|
||||||
"member_has_view_all_requests_portfolio_permission": member_has_view_all_requests_portfolio_permission,
|
"member_has_view_all_requests_portfolio_permission": member_has_view_all_requests_portfolio_permission,
|
||||||
|
@ -115,22 +116,23 @@ class PortfolioMemberView(DetailView, View):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@grant_access(HAS_PORTFOLIO_MEMBERS_ANY_PERM)
|
@grant_access(HAS_PORTFOLIO_MEMBERS_EDIT)
|
||||||
class PortfolioMemberDeleteView(View):
|
class PortfolioMemberDeleteView(View):
|
||||||
|
pk_url_kwarg = "member_pk"
|
||||||
|
|
||||||
def post(self, request, pk):
|
def post(self, request, member_pk):
|
||||||
"""
|
"""
|
||||||
Find and delete the portfolio member using the provided primary key (pk).
|
Find and delete the portfolio member using the provided primary key (pk).
|
||||||
Redirect to a success page after deletion (or any other appropriate page).
|
Redirect to a success page after deletion (or any other appropriate page).
|
||||||
"""
|
"""
|
||||||
portfolio_member_permission = get_object_or_404(UserPortfolioPermission, pk=pk)
|
portfolio_member_permission = get_object_or_404(UserPortfolioPermission, pk=member_pk)
|
||||||
member = portfolio_member_permission.user
|
member = portfolio_member_permission.user
|
||||||
portfolio = portfolio_member_permission.portfolio
|
portfolio = portfolio_member_permission.portfolio
|
||||||
|
|
||||||
# Validate if the member can be removed
|
# Validate if the member can be removed
|
||||||
error_message = self._validate_member_removal(request, member, portfolio)
|
error_message = self._validate_member_removal(request, member, portfolio)
|
||||||
if error_message:
|
if error_message:
|
||||||
return self._handle_error_response(request, error_message, pk)
|
return self._handle_error_response(request, error_message, member_pk)
|
||||||
|
|
||||||
# Attempt to send notification emails
|
# Attempt to send notification emails
|
||||||
self._send_removal_notifications(request, portfolio_member_permission)
|
self._send_removal_notifications(request, portfolio_member_permission)
|
||||||
|
@ -161,14 +163,14 @@ class PortfolioMemberDeleteView(View):
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _handle_error_response(self, request, error_message, pk):
|
def _handle_error_response(self, request, error_message, member_pk):
|
||||||
"""
|
"""
|
||||||
Return an error response (JSON or redirect with messages).
|
Return an error response (JSON or redirect with messages).
|
||||||
"""
|
"""
|
||||||
if request.headers.get("X-Requested-With") == "XMLHttpRequest":
|
if request.headers.get("X-Requested-With") == "XMLHttpRequest":
|
||||||
return JsonResponse({"error": error_message}, status=400)
|
return JsonResponse({"error": error_message}, status=400)
|
||||||
messages.error(request, error_message)
|
messages.error(request, error_message)
|
||||||
return redirect(reverse("member", kwargs={"pk": pk}))
|
return redirect(reverse("member", kwargs={"member_pk": member_pk}))
|
||||||
|
|
||||||
def _send_removal_notifications(self, request, portfolio_member_permission):
|
def _send_removal_notifications(self, request, portfolio_member_permission):
|
||||||
"""
|
"""
|
||||||
|
@ -223,9 +225,10 @@ class PortfolioMemberEditView(DetailView, View):
|
||||||
context_object_name = "portfolio"
|
context_object_name = "portfolio"
|
||||||
template_name = "portfolio_member_permissions.html"
|
template_name = "portfolio_member_permissions.html"
|
||||||
form_class = portfolioForms.PortfolioMemberForm
|
form_class = portfolioForms.PortfolioMemberForm
|
||||||
|
pk_url_kwarg = "member_pk"
|
||||||
|
|
||||||
def get(self, request, pk):
|
def get(self, request, member_pk):
|
||||||
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=pk)
|
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=member_pk)
|
||||||
user = portfolio_permission.user
|
user = portfolio_permission.user
|
||||||
|
|
||||||
form = self.form_class(instance=portfolio_permission)
|
form = self.form_class(instance=portfolio_permission)
|
||||||
|
@ -240,8 +243,8 @@ class PortfolioMemberEditView(DetailView, View):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
def post(self, request, pk):
|
def post(self, request, member_pk):
|
||||||
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=pk)
|
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=member_pk)
|
||||||
user = portfolio_permission.user
|
user = portfolio_permission.user
|
||||||
form = self.form_class(request.POST, instance=portfolio_permission)
|
form = self.form_class(request.POST, instance=portfolio_permission)
|
||||||
removing_admin_role_on_self = False
|
removing_admin_role_on_self = False
|
||||||
|
@ -276,7 +279,7 @@ class PortfolioMemberEditView(DetailView, View):
|
||||||
self._handle_exceptions(e)
|
self._handle_exceptions(e)
|
||||||
form.save()
|
form.save()
|
||||||
messages.success(self.request, "The member access and permission changes have been saved.")
|
messages.success(self.request, "The member access and permission changes have been saved.")
|
||||||
return redirect("member", pk=pk) if not removing_admin_role_on_self else redirect("home")
|
return redirect("member", member_pk=member_pk) if not removing_admin_role_on_self else redirect("home")
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
|
@ -304,9 +307,10 @@ class PortfolioMemberEditView(DetailView, View):
|
||||||
class PortfolioMemberDomainsView(View):
|
class PortfolioMemberDomainsView(View):
|
||||||
|
|
||||||
template_name = "portfolio_member_domains.html"
|
template_name = "portfolio_member_domains.html"
|
||||||
|
pk_url_kwarg = "member_pk"
|
||||||
|
|
||||||
def get(self, request, pk):
|
def get(self, request, member_pk):
|
||||||
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=pk)
|
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=member_pk)
|
||||||
member = portfolio_permission.user
|
member = portfolio_permission.user
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
|
@ -324,9 +328,10 @@ class PortfolioMemberDomainsEditView(DetailView, View):
|
||||||
model = Portfolio
|
model = Portfolio
|
||||||
context_object_name = "portfolio"
|
context_object_name = "portfolio"
|
||||||
template_name = "portfolio_member_domains_edit.html"
|
template_name = "portfolio_member_domains_edit.html"
|
||||||
|
pk_url_kwarg = "member_pk"
|
||||||
|
|
||||||
def get(self, request, pk):
|
def get(self, request, member_pk):
|
||||||
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=pk)
|
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=member_pk)
|
||||||
member = portfolio_permission.user
|
member = portfolio_permission.user
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
|
@ -338,33 +343,33 @@ class PortfolioMemberDomainsEditView(DetailView, View):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
def post(self, request, pk):
|
def post(self, request, member_pk):
|
||||||
"""
|
"""
|
||||||
Handles adding and removing domains for a portfolio member.
|
Handles adding and removing domains for a portfolio member.
|
||||||
"""
|
"""
|
||||||
added_domains = request.POST.get("added_domains")
|
added_domains = request.POST.get("added_domains")
|
||||||
removed_domains = request.POST.get("removed_domains")
|
removed_domains = request.POST.get("removed_domains")
|
||||||
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=pk)
|
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=member_pk)
|
||||||
member = portfolio_permission.user
|
member = portfolio_permission.user
|
||||||
portfolio = portfolio_permission.portfolio
|
portfolio = portfolio_permission.portfolio
|
||||||
|
|
||||||
added_domain_ids = self._parse_domain_ids(added_domains, "added domains")
|
added_domain_ids = self._parse_domain_ids(added_domains, "added domains")
|
||||||
if added_domain_ids is None:
|
if added_domain_ids is None:
|
||||||
return redirect(reverse("member-domains", kwargs={"pk": pk}))
|
return redirect(reverse("member-domains", kwargs={"member_pk": member_pk}))
|
||||||
|
|
||||||
removed_domain_ids = self._parse_domain_ids(removed_domains, "removed domains")
|
removed_domain_ids = self._parse_domain_ids(removed_domains, "removed domains")
|
||||||
if removed_domain_ids is None:
|
if removed_domain_ids is None:
|
||||||
return redirect(reverse("member-domains", kwargs={"pk": pk}))
|
return redirect(reverse("member-domains", kwargs={"member_pk": member_pk}))
|
||||||
|
|
||||||
if not (added_domain_ids or removed_domain_ids):
|
if not (added_domain_ids or removed_domain_ids):
|
||||||
messages.success(request, "The domain assignment changes have been saved.")
|
messages.success(request, "The domain assignment changes have been saved.")
|
||||||
return redirect(reverse("member-domains", kwargs={"pk": pk}))
|
return redirect(reverse("member-domains", kwargs={"member_pk": member_pk}))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._process_added_domains(added_domain_ids, member, request.user, portfolio)
|
self._process_added_domains(added_domain_ids, member, request.user, portfolio)
|
||||||
self._process_removed_domains(removed_domain_ids, member)
|
self._process_removed_domains(removed_domain_ids, member)
|
||||||
messages.success(request, "The domain assignment changes have been saved.")
|
messages.success(request, "The domain assignment changes have been saved.")
|
||||||
return redirect(reverse("member-domains", kwargs={"pk": pk}))
|
return redirect(reverse("member-domains", kwargs={"member_pk": member_pk}))
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
messages.error(
|
messages.error(
|
||||||
request,
|
request,
|
||||||
|
@ -372,7 +377,7 @@ class PortfolioMemberDomainsEditView(DetailView, View):
|
||||||
f"please contact {DefaultUserValues.HELP_EMAIL}.",
|
f"please contact {DefaultUserValues.HELP_EMAIL}.",
|
||||||
)
|
)
|
||||||
logger.error("A database error occurred while saving changes.", exc_info=True)
|
logger.error("A database error occurred while saving changes.", exc_info=True)
|
||||||
return redirect(reverse("member-domains-edit", kwargs={"pk": pk}))
|
return redirect(reverse("member-domains-edit", kwargs={"member_pk": member_pk}))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
messages.error(
|
messages.error(
|
||||||
request,
|
request,
|
||||||
|
@ -380,7 +385,7 @@ class PortfolioMemberDomainsEditView(DetailView, View):
|
||||||
f"please contact {DefaultUserValues.HELP_EMAIL}.",
|
f"please contact {DefaultUserValues.HELP_EMAIL}.",
|
||||||
)
|
)
|
||||||
logger.error(f"An unexpected error occurred: {str(e)}", exc_info=True)
|
logger.error(f"An unexpected error occurred: {str(e)}", exc_info=True)
|
||||||
return redirect(reverse("member-domains-edit", kwargs={"pk": pk}))
|
return redirect(reverse("member-domains-edit", kwargs={"member_pk": member_pk}))
|
||||||
|
|
||||||
def _parse_domain_ids(self, domain_data, domain_type):
|
def _parse_domain_ids(self, domain_data, domain_type):
|
||||||
"""
|
"""
|
||||||
|
@ -437,9 +442,10 @@ class PortfolioInvitedMemberView(DetailView, View):
|
||||||
context_object_name = "portfolio"
|
context_object_name = "portfolio"
|
||||||
template_name = "portfolio_member.html"
|
template_name = "portfolio_member.html"
|
||||||
# form_class = PortfolioInvitedMemberForm
|
# form_class = PortfolioInvitedMemberForm
|
||||||
|
pk_url_kwarg = "invitedmember_pk"
|
||||||
|
|
||||||
def get(self, request, pk):
|
def get(self, request, invitedmember_pk):
|
||||||
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=pk)
|
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=invitedmember_pk)
|
||||||
# form = self.form_class(instance=portfolio_invitation)
|
# form = self.form_class(instance=portfolio_invitation)
|
||||||
|
|
||||||
# We have to explicitely name these with member_ otherwise we'll have conflicts with context preprocessors
|
# We have to explicitely name these with member_ otherwise we'll have conflicts with context preprocessors
|
||||||
|
@ -463,8 +469,8 @@ class PortfolioInvitedMemberView(DetailView, View):
|
||||||
request,
|
request,
|
||||||
self.template_name,
|
self.template_name,
|
||||||
{
|
{
|
||||||
"edit_url": reverse("invitedmember-permissions", args=[pk]),
|
"edit_url": reverse("invitedmember-permissions", args=[invitedmember_pk]),
|
||||||
"domains_url": reverse("invitedmember-domains", args=[pk]),
|
"domains_url": reverse("invitedmember-domains", args=[invitedmember_pk]),
|
||||||
"portfolio_invitation": portfolio_invitation,
|
"portfolio_invitation": portfolio_invitation,
|
||||||
"member_has_view_all_requests_portfolio_permission": member_has_view_all_requests_portfolio_permission,
|
"member_has_view_all_requests_portfolio_permission": member_has_view_all_requests_portfolio_permission,
|
||||||
"member_has_edit_request_portfolio_permission": member_has_edit_request_portfolio_permission,
|
"member_has_edit_request_portfolio_permission": member_has_edit_request_portfolio_permission,
|
||||||
|
@ -475,15 +481,16 @@ class PortfolioInvitedMemberView(DetailView, View):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@grant_access(HAS_PORTFOLIO_MEMBERS_ANY_PERM)
|
@grant_access(HAS_PORTFOLIO_MEMBERS_EDIT)
|
||||||
class PortfolioInvitedMemberDeleteView(View):
|
class PortfolioInvitedMemberDeleteView(View):
|
||||||
|
pk_url_kwarg = "invitedmember_pk"
|
||||||
|
|
||||||
def post(self, request, pk):
|
def post(self, request, invitedmember_pk):
|
||||||
"""
|
"""
|
||||||
Find and delete the portfolio invited member using the provided primary key (pk).
|
Find and delete the portfolio invited member using the provided primary key (pk).
|
||||||
Redirect to a success page after deletion (or any other appropriate page).
|
Redirect to a success page after deletion (or any other appropriate page).
|
||||||
"""
|
"""
|
||||||
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=pk)
|
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=invitedmember_pk)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# if invitation being removed is an admin
|
# if invitation being removed is an admin
|
||||||
|
@ -527,9 +534,10 @@ class PortfolioInvitedMemberEditView(DetailView, View):
|
||||||
context_object_name = "portfolio"
|
context_object_name = "portfolio"
|
||||||
template_name = "portfolio_member_permissions.html"
|
template_name = "portfolio_member_permissions.html"
|
||||||
form_class = portfolioForms.PortfolioInvitedMemberForm
|
form_class = portfolioForms.PortfolioInvitedMemberForm
|
||||||
|
pk_url_kwarg = "invitedmember_pk"
|
||||||
|
|
||||||
def get(self, request, pk):
|
def get(self, request, invitedmember_pk):
|
||||||
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=pk)
|
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=invitedmember_pk)
|
||||||
form = self.form_class(instance=portfolio_invitation)
|
form = self.form_class(instance=portfolio_invitation)
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
|
@ -541,8 +549,8 @@ class PortfolioInvitedMemberEditView(DetailView, View):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
def post(self, request, pk):
|
def post(self, request, invitedmember_pk):
|
||||||
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=pk)
|
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=invitedmember_pk)
|
||||||
form = self.form_class(request.POST, instance=portfolio_invitation)
|
form = self.form_class(request.POST, instance=portfolio_invitation)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
try:
|
try:
|
||||||
|
@ -568,7 +576,7 @@ class PortfolioInvitedMemberEditView(DetailView, View):
|
||||||
self._handle_exceptions(e)
|
self._handle_exceptions(e)
|
||||||
form.save()
|
form.save()
|
||||||
messages.success(self.request, "The member access and permission changes have been saved.")
|
messages.success(self.request, "The member access and permission changes have been saved.")
|
||||||
return redirect("invitedmember", pk=pk)
|
return redirect("invitedmember", invitedmember_pk=invitedmember_pk)
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
|
@ -596,9 +604,10 @@ class PortfolioInvitedMemberEditView(DetailView, View):
|
||||||
class PortfolioInvitedMemberDomainsView(View):
|
class PortfolioInvitedMemberDomainsView(View):
|
||||||
|
|
||||||
template_name = "portfolio_member_domains.html"
|
template_name = "portfolio_member_domains.html"
|
||||||
|
pk_url_kwarg = "invitedmember_pk"
|
||||||
|
|
||||||
def get(self, request, pk):
|
def get(self, request, invitedmember_pk):
|
||||||
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=pk)
|
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=invitedmember_pk)
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
|
@ -615,9 +624,10 @@ class PortfolioInvitedMemberDomainsEditView(DetailView, View):
|
||||||
model = Portfolio
|
model = Portfolio
|
||||||
context_object_name = "portfolio"
|
context_object_name = "portfolio"
|
||||||
template_name = "portfolio_member_domains_edit.html"
|
template_name = "portfolio_member_domains_edit.html"
|
||||||
|
pk_url_kwarg = "invitedmember_pk"
|
||||||
|
|
||||||
def get(self, request, pk):
|
def get(self, request, invitedmember_pk):
|
||||||
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=pk)
|
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=invitedmember_pk)
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
|
@ -627,33 +637,33 @@ class PortfolioInvitedMemberDomainsEditView(DetailView, View):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
def post(self, request, pk):
|
def post(self, request, invitedmember_pk):
|
||||||
"""
|
"""
|
||||||
Handles adding and removing domains for a portfolio invitee.
|
Handles adding and removing domains for a portfolio invitee.
|
||||||
"""
|
"""
|
||||||
added_domains = request.POST.get("added_domains")
|
added_domains = request.POST.get("added_domains")
|
||||||
removed_domains = request.POST.get("removed_domains")
|
removed_domains = request.POST.get("removed_domains")
|
||||||
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=pk)
|
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=invitedmember_pk)
|
||||||
email = portfolio_invitation.email
|
email = portfolio_invitation.email
|
||||||
portfolio = portfolio_invitation.portfolio
|
portfolio = portfolio_invitation.portfolio
|
||||||
|
|
||||||
added_domain_ids = self._parse_domain_ids(added_domains, "added domains")
|
added_domain_ids = self._parse_domain_ids(added_domains, "added domains")
|
||||||
if added_domain_ids is None:
|
if added_domain_ids is None:
|
||||||
return redirect(reverse("invitedmember-domains", kwargs={"pk": pk}))
|
return redirect(reverse("invitedmember-domains", kwargs={"invitedmember_pk": invitedmember_pk}))
|
||||||
|
|
||||||
removed_domain_ids = self._parse_domain_ids(removed_domains, "removed domains")
|
removed_domain_ids = self._parse_domain_ids(removed_domains, "removed domains")
|
||||||
if removed_domain_ids is None:
|
if removed_domain_ids is None:
|
||||||
return redirect(reverse("invitedmember-domains", kwargs={"pk": pk}))
|
return redirect(reverse("invitedmember-domains", kwargs={"invitedmember_pk": invitedmember_pk}))
|
||||||
|
|
||||||
if not (added_domain_ids or removed_domain_ids):
|
if not (added_domain_ids or removed_domain_ids):
|
||||||
messages.success(request, "The domain assignment changes have been saved.")
|
messages.success(request, "The domain assignment changes have been saved.")
|
||||||
return redirect(reverse("invitedmember-domains", kwargs={"pk": pk}))
|
return redirect(reverse("invitedmember-domains", kwargs={"invitedmember_pk": invitedmember_pk}))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._process_added_domains(added_domain_ids, email, request.user, portfolio)
|
self._process_added_domains(added_domain_ids, email, request.user, portfolio)
|
||||||
self._process_removed_domains(removed_domain_ids, email)
|
self._process_removed_domains(removed_domain_ids, email)
|
||||||
messages.success(request, "The domain assignment changes have been saved.")
|
messages.success(request, "The domain assignment changes have been saved.")
|
||||||
return redirect(reverse("invitedmember-domains", kwargs={"pk": pk}))
|
return redirect(reverse("invitedmember-domains", kwargs={"invitedmember_pk": invitedmember_pk}))
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
messages.error(
|
messages.error(
|
||||||
request,
|
request,
|
||||||
|
@ -661,7 +671,7 @@ class PortfolioInvitedMemberDomainsEditView(DetailView, View):
|
||||||
f"please contact {DefaultUserValues.HELP_EMAIL}.",
|
f"please contact {DefaultUserValues.HELP_EMAIL}.",
|
||||||
)
|
)
|
||||||
logger.error("A database error occurred while saving changes.", exc_info=True)
|
logger.error("A database error occurred while saving changes.", exc_info=True)
|
||||||
return redirect(reverse("invitedmember-domains-edit", kwargs={"pk": pk}))
|
return redirect(reverse("invitedmember-domains-edit", kwargs={"invitedmember_pk": invitedmember_pk}))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
messages.error(
|
messages.error(
|
||||||
request,
|
request,
|
||||||
|
@ -669,7 +679,7 @@ class PortfolioInvitedMemberDomainsEditView(DetailView, View):
|
||||||
f"please contact {DefaultUserValues.HELP_EMAIL}.",
|
f"please contact {DefaultUserValues.HELP_EMAIL}.",
|
||||||
)
|
)
|
||||||
logger.error(f"An unexpected error occurred: {str(e)}.", exc_info=True)
|
logger.error(f"An unexpected error occurred: {str(e)}.", exc_info=True)
|
||||||
return redirect(reverse("invitedmember-domains-edit", kwargs={"pk": pk}))
|
return redirect(reverse("invitedmember-domains-edit", kwargs={"invitedmember_pk": invitedmember_pk}))
|
||||||
|
|
||||||
def _parse_domain_ids(self, domain_data, domain_type):
|
def _parse_domain_ids(self, domain_data, domain_type):
|
||||||
"""
|
"""
|
||||||
|
@ -903,7 +913,7 @@ class PortfolioMembersView(View):
|
||||||
return render(request, "portfolio_members.html")
|
return render(request, "portfolio_members.html")
|
||||||
|
|
||||||
|
|
||||||
@grant_access(HAS_PORTFOLIO_MEMBERS_ANY_PERM)
|
@grant_access(HAS_PORTFOLIO_MEMBERS_EDIT)
|
||||||
class PortfolioAddMemberView(DetailView, FormMixin):
|
class PortfolioAddMemberView(DetailView, FormMixin):
|
||||||
|
|
||||||
template_name = "portfolio_members_add_new.html"
|
template_name = "portfolio_members_add_new.html"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue