mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-01 23:42:17 +02:00
Did it the endpoint method way which was sadly incorrect
This commit is contained in:
parent
4b523a75ce
commit
95d912ecbc
8 changed files with 114 additions and 60 deletions
|
@ -11,6 +11,7 @@ from django.db.models import (
|
|||
Value,
|
||||
When,
|
||||
)
|
||||
|
||||
from django.db.models.functions import Concat, Coalesce
|
||||
from django.http import HttpResponseRedirect
|
||||
from registrar.models.federal_agency import FederalAgency
|
||||
|
@ -24,7 +25,7 @@ from registrar.utility.admin_helpers import (
|
|||
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
|
||||
|
@ -1478,26 +1479,6 @@ class BaseInvitationAdmin(ListHeaderAdmin):
|
|||
return response
|
||||
|
||||
|
||||
# class DomainInvitationAdminForm(forms.ModelForm):
|
||||
# """Custom form for DomainInvitation in admin to only allow cancellations."""
|
||||
|
||||
# STATUS_CHOICES = [
|
||||
# ("", "------"), # no action
|
||||
# ("canceled", "Canceled"),
|
||||
# ]
|
||||
|
||||
# status = forms.ChoiceField(choices=STATUS_CHOICES, required=False, label="Status")
|
||||
|
||||
# class Meta:
|
||||
# model = models.DomainInvitation
|
||||
# fields = "__all__"
|
||||
|
||||
# def clean_status(self):
|
||||
# # Clean status - we purposely dont edit anything so we dont mess with the state
|
||||
# status = self.cleaned_data.get("status")
|
||||
# return status
|
||||
|
||||
|
||||
class DomainInvitationAdmin(BaseInvitationAdmin):
|
||||
"""Custom domain invitation admin class."""
|
||||
|
||||
|
@ -1527,18 +1508,29 @@ class DomainInvitationAdmin(BaseInvitationAdmin):
|
|||
|
||||
search_help_text = "Search by email or domain."
|
||||
|
||||
# # Mark the FSM field 'status' as readonly
|
||||
# # to allow admin users to create Domain Invitations
|
||||
# # without triggering the FSM Transition Not Allowed
|
||||
# # error.
|
||||
# readonly_fields = ["status"]
|
||||
|
||||
readonly_fields = []
|
||||
# Mark the FSM field 'status' as readonly
|
||||
# to allow admin users to create Domain Invitations
|
||||
# without triggering the FSM Transition Not Allowed
|
||||
# error.
|
||||
readonly_fields = ["status"]
|
||||
|
||||
autocomplete_fields = ["domain"]
|
||||
|
||||
change_form_template = "django/admin/domain_invitation_change_form.html"
|
||||
|
||||
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 = {}
|
||||
|
||||
# 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):
|
||||
"""
|
||||
Override the save_model method.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -46,7 +46,6 @@ body {
|
|||
background-color: color('gray-1');
|
||||
}
|
||||
|
||||
|
||||
.section-outlined {
|
||||
background-color: color('white');
|
||||
border: 1px solid color('base-lighter');
|
||||
|
|
|
@ -78,10 +78,6 @@ class DomainInvitation(TimeStampedModel):
|
|||
@transition(field="status", source=DomainInvitationStatus.INVITED, target=DomainInvitationStatus.CANCELED)
|
||||
def cancel_invitation(self):
|
||||
"""When an invitation is canceled, change the status to canceled"""
|
||||
# print("***** IN CANCEL_INVITATION SECTION")
|
||||
# logger.info(f"Invitation for {self.email} to {self.domain} has been canceled.")
|
||||
# print("WHEN INVITATION IS CANCELED > CHANGE STATUS TO CANCELED")
|
||||
# Send email here maybe?
|
||||
pass
|
||||
|
||||
@transition(field="status", source=DomainInvitationStatus.CANCELED, target=DomainInvitationStatus.INVITED)
|
||||
|
|
|
@ -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 -->
|
||||
|
@ -31,5 +46,4 @@
|
|||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% endblock %}
|
|
@ -1,22 +0,0 @@
|
|||
{% load static %}
|
||||
|
||||
<div class="admin-icon-group">
|
||||
<span id="status-field">{{ field.value | capfirst }}</span>
|
||||
|
||||
<input aria-hidden="true" class="display-none" value="">
|
||||
<button
|
||||
class="usa-button--dja usa-button usa-button__small-text usa-button--unstyled padding-left-1 usa-button--icon copy-to-clipboard"
|
||||
type="button"
|
||||
>
|
||||
<div class="no-outline-on-click">
|
||||
<svg
|
||||
class="usa-icon"
|
||||
>
|
||||
<use aria-hidden="true" xlink:href="{%static 'img/sprite.svg'%}#highlight_off"></use>
|
||||
</svg>
|
||||
<!-- the span is targeted in JS, do not remove -->
|
||||
<span>Cancel invitation</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -7,8 +7,6 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
|
|||
{% block field_other %}
|
||||
{% if field.field.name == "email" %}
|
||||
{% include "admin/input_with_clipboard.html" with field=field.field %}
|
||||
{% elif field.field.name == "status" %}
|
||||
{% include "admin/status_with_clipboard.html" with field=field.field %}
|
||||
{% else %}
|
||||
{{ block.super }}
|
||||
{% endif %}
|
||||
|
|
|
@ -12,6 +12,7 @@ from registrar.models import (
|
|||
Domain,
|
||||
DomainRequest,
|
||||
DomainInformation,
|
||||
DomainInvitation,
|
||||
User,
|
||||
Host,
|
||||
Portfolio,
|
||||
|
@ -30,6 +31,9 @@ from .common import (
|
|||
)
|
||||
from unittest.mock import ANY, call, patch
|
||||
|
||||
from django.contrib.messages import get_messages
|
||||
|
||||
|
||||
import boto3_mocking # type: ignore
|
||||
import logging
|
||||
|
||||
|
@ -495,6 +499,63 @@ class TestDomainInformationInline(MockEppLib):
|
|||
self.assertIn("poopy@gov.gov", domain_managers)
|
||||
|
||||
|
||||
class DomainInvitationAdminTest(TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.staffuser = create_user(email="staffdomainmanager@meoward.com", 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="inviteddomainmanager@meoward.com",
|
||||
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.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue