Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT] #3348: Cancel invitation within Django Admin - [RH] #3477

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions src/registrar/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
Value,
When,
)

from django.db.models.functions import Concat, Coalesce
from django.http import HttpResponseRedirect
from registrar.models.federal_agency import FederalAgency
Expand All @@ -24,7 +25,7 @@
from django.conf import settings
from django.contrib.messages import get_messages
from django.contrib.admin.helpers import AdminForm
from django.shortcuts import redirect
from django.shortcuts import redirect, get_object_or_404
from django_fsm import get_available_FIELD_transitions, FSMField
from registrar.models import DomainInformation, Portfolio, UserPortfolioPermission, DomainInvitation
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
Expand Down Expand Up @@ -1481,6 +1482,8 @@ def response_add(self, request, obj, post_url_continue=None):
class DomainInvitationAdmin(BaseInvitationAdmin):
"""Custom domain invitation admin class."""

# form = DomainInvitationAdminForm

class Meta:
model = models.DomainInvitation
fields = "__all__"
Expand Down Expand Up @@ -1515,13 +1518,18 @@ class Meta:

change_form_template = "django/admin/domain_invitation_change_form.html"

# Select domain invitations to change -> Domain invitations
def changelist_view(self, request, extra_context=None):
def change_view(self, request, object_id, form_url="", extra_context=None):
"""Override the change_view to add the invitation obj for the
change_form_object_tools template"""

if extra_context is None:
extra_context = {}
extra_context["tabtitle"] = "Domain invitations"
# Get the filtered values
return super().changelist_view(request, extra_context=extra_context)

# Get the domain invitation object
invitation = get_object_or_404(DomainInvitation, id=object_id)
extra_context["invitation"] = invitation

return super().change_view(request, object_id, form_url, extra_context)

def save_model(self, request, obj, form, change):
"""
Expand All @@ -1531,6 +1539,7 @@ def save_model(self, request, obj, form, change):
which will be successful if a single User exists for that email; otherwise, will
just continue to create the invitation.
"""

if not change:
domain = obj.domain
domain_org = getattr(domain.domain_info, "portfolio", None)
Expand Down
16 changes: 16 additions & 0 deletions src/registrar/assets/src/sass/_theme/_admin.scss
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,22 @@ input[type=submit].button--dja-toolbar:focus, input[type=submit].button--dja-too
font-size: 13px;
}

.object-tools li button {
font-family: Source Sans Pro Web, Helvetica Neue, Helvetica, Roboto, Arial, sans-serif;
text-transform: none !important;
font-size: 14px !important;
display: block;
float: left;
padding: 3px 12px;
background: var(--object-tools-bg) !important;
color: var(--object-tools-fg);
font-weight: 400;
font-size: 0.6875rem;
text-transform: uppercase;
letter-spacing: 0.5px;
border-radius: 15px;
}

.module--custom {
a {
font-size: 13px;
Expand Down
1 change: 0 additions & 1 deletion src/registrar/assets/src/sass/_theme/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ body {
background-color: color('gray-1');
}


.section-outlined {
background-color: color('white');
border: 1px solid color('base-lighter');
Expand Down
20 changes: 17 additions & 3 deletions src/registrar/templates/admin/change_form_object_tools.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,28 @@
</ul>
{% else %}
<ul>
{% if opts.model_name == 'domaininvitation' %}
<li>
<form method="post" action="{% url 'invitation-cancel' pk=invitation.id %}">
{% csrf_token %}
<button type="submit" class="usa-button--dja">
<svg class="usa-icon">
<use xlink:href="{%static 'img/sprite.svg'%}#cancel"></use>
</svg>
<span>{% translate "Cancel invitation" %}</span>
</button>
</form>
</li>
{% endif %}

<li>
<a href="{% add_preserved_filters history_url %}">{% translate "History" %}</a>
</li>

{% if opts.model_name == 'domainrequest' %}
<li>
<a id="id-copy-to-clipboard-summary" class="usa-button--dja" type="button" href="#">
<svg class="usa-icon" >
<svg class="usa-icon">
<use xlink:href="{%static 'img/sprite.svg'%}#content_copy"></use>
</svg>
<!-- the span is targeted in JS, do not remove -->
Expand All @@ -31,5 +46,4 @@
{% endif %}
</ul>
{% endif %}
{% endblock %}

{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@
</div>
</div>
{{ block.super }}
{% endblock %}
{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
{{ block.super }}
{% endif %}
{% endblock field_other %}

61 changes: 61 additions & 0 deletions src/registrar/tests/test_admin_domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Domain,
DomainRequest,
DomainInformation,
DomainInvitation,
User,
Host,
Portfolio,
Expand All @@ -30,6 +31,9 @@
)
from unittest.mock import ANY, call, patch

from django.contrib.messages import get_messages


import boto3_mocking # type: ignore
import logging

Expand Down Expand Up @@ -495,6 +499,63 @@ def test_invited_domain_managers_display(self):
self.assertIn("[email protected]", domain_managers)


class DomainInvitationAdminTest(TestCase):

@classmethod
def setUpClass(cls):
super().setUpClass()
cls.staffuser = create_user(email="[email protected]", is_staff=True)
cls.site = AdminSite()
cls.admin = DomainAdmin(model=Domain, admin_site=cls.site)
cls.factory = RequestFactory()

def setUp(self):
self.client = Client(HTTP_HOST="localhost:8080")
self.client.force_login(self.staffuser)
super().setUp()

def test_cancel_invitation_flow_in_admin(self):
"""Testing canceling a domain invitation in Django Admin."""

# 1. Create a domain and assign staff user role + domain manager
domain = Domain.objects.create(name="cancelinvitationflowviaadmin.gov")
UserDomainRole.objects.create(user=self.staffuser, domain=domain, role="manager")

# 2. Invite a domain manager to the above domain
invitation = DomainInvitation.objects.create(
email="[email protected]",
domain=domain,
status=DomainInvitation.DomainInvitationStatus.INVITED,
)

# 3. Go to the Domain Invitations list in /admin
domain_invitation_list_url = reverse("admin:registrar_domaininvitation_changelist")
response = self.client.get(domain_invitation_list_url)
self.assertEqual(response.status_code, 200)

# 4. Go to the change view of that invitation and make sure you can see the button
domain_invitation_change_url = reverse("admin:registrar_domaininvitation_change", args=[invitation.id])
response = self.client.get(domain_invitation_change_url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Cancel invitation")

# 5. Click the "Cancel invitation" button (a POST)
cancel_invitation_url = reverse("invitation-cancel", args=[invitation.id])
response = self.client.post(cancel_invitation_url, follow=True)

# 6.Confirm we're redirected to the domain managers page for the domain
expected_redirect_url = reverse("domain-users", args=[domain.id])
self.assertRedirects(response, expected_redirect_url)

# 7. Get the messages
messages = list(get_messages(response.wsgi_request))
message_texts = [str(message) for message in messages]

# 8. Check that the success banner text is in the messages
expected_message = f"Canceled invitation to {invitation.email}."
self.assertIn(expected_message, message_texts)


class TestDomainAdminWithClient(TestCase):
"""Test DomainAdmin class as super user.

Expand Down
Loading