Skip to content

Commit

Permalink
Merge pull request #5556 from nyaruka/move_service_view
Browse files Browse the repository at this point in the history
Move org service view to staff app
  • Loading branch information
rowanseymour authored Oct 17, 2024
2 parents 79f0bf7 + 40ac05b commit 01b7f99
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 119 deletions.
79 changes: 1 addition & 78 deletions temba/orgs/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,7 @@
from temba.channels.models import Channel, ChannelLog, SyncEvent
from temba.classifiers.models import Classifier
from temba.classifiers.types.wit import WitType
from temba.contacts.models import (
URN,
Contact,
ContactExport,
ContactField,
ContactGroup,
ContactImport,
ContactImportBatch,
)
from temba.contacts.models import URN, ContactExport, ContactField, ContactGroup, ContactImport, ContactImportBatch
from temba.flows.models import Flow, FlowLabel, FlowRun, FlowSession, FlowStart, FlowStartCount, ResultsExport
from temba.globals.models import Global
from temba.locations.models import AdminBoundary
Expand Down Expand Up @@ -2753,75 +2745,6 @@ def test_login_case_not_sensitive(self):
self.assertIn("form", response.context)
self.assertTrue(response.context["form"].errors)

@mock_mailroom
def test_service(self, mr_mocks):
service_url = reverse("orgs.org_service")
inbox_url = reverse("msgs.msg_inbox")

# without logging in, try to service our main org
response = self.client.get(service_url, {"other_org": self.org.id, "next": inbox_url})
self.assertLoginRedirect(response)

response = self.client.post(service_url, {"other_org": self.org.id})
self.assertLoginRedirect(response)

# try logging in with a normal user
self.login(self.admin)

# same thing, no permission
response = self.client.get(service_url, {"other_org": self.org.id, "next": inbox_url})
self.assertLoginRedirect(response)

response = self.client.post(service_url, {"other_org": self.org.id})
self.assertLoginRedirect(response)

# ok, log in as our cs rep
self.login(self.customer_support)

# getting invalid org, has no service form
response = self.client.get(service_url, {"other_org": 325253256, "next": inbox_url})
self.assertContains(response, "Invalid org")

# posting invalid org just redirects back to manage page
response = self.client.post(service_url, {"other_org": 325253256})
self.assertRedirect(response, "/staff/org/")

# then service our org
response = self.client.get(service_url, {"other_org": self.org.id})
self.assertContains(response, "You are about to service the workspace, <b>Nyaruka</b>.")

# requesting a next page has a slightly different message
response = self.client.get(service_url, {"other_org": self.org.id, "next": inbox_url})
self.assertContains(response, "The page you are requesting belongs to a different workspace, <b>Nyaruka</b>.")

response = self.client.post(service_url, {"other_org": self.org.id})
self.assertRedirect(response, "/msg/")
self.assertEqual(self.org.id, self.client.session["org_id"])
self.assertTrue(self.client.session["servicing"])

# specify redirect_url
response = self.client.post(service_url, {"other_org": self.org.id, "next": "/flow/"})
self.assertRedirect(response, "/flow/")

# create a new contact
response = self.client.post(
reverse("contacts.contact_create"), data=dict(name="Ben Haggerty", phone="0788123123")
)
self.assertNoFormErrors(response)

# make sure that contact's created on is our cs rep
contact = Contact.objects.get(urns__path="+250788123123", org=self.org)
self.assertEqual(self.customer_support, contact.created_by)

self.assertEqual(self.org.id, self.client.session["org_id"])
self.assertTrue(self.client.session["servicing"])

# stop servicing
response = self.client.post(service_url, {})
self.assertRedirect(response, "/staff/org/")
self.assertIsNone(self.client.session["org_id"])
self.assertFalse(self.client.session["servicing"])

def test_languages(self):
settings_url = reverse("orgs.org_workspace")
langs_url = reverse("orgs.org_languages")
Expand Down
2 changes: 1 addition & 1 deletion temba/orgs/views/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def pre_process(self, request, *args, **kwargs):
org = self.get_object_org()
if request.user.is_staff and self.request.org != org:
return HttpResponseRedirect(
f"{reverse('orgs.org_service')}?next={quote_plus(request.path)}&other_org={org.id}"
f"{reverse('staff.org_service')}?next={quote_plus(request.path)}&other_org={org.id}"
)


Expand Down
34 changes: 0 additions & 34 deletions temba/orgs/views/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
from django.contrib.auth.views import LoginView as AuthLoginView
from django.core.exceptions import ValidationError
from django.db.models.functions import Lower
from django.forms import ModelChoiceField
from django.http import Http404, HttpResponseRedirect, JsonResponse
from django.shortcuts import get_object_or_404, resolve_url
from django.urls import reverse, reverse_lazy
Expand Down Expand Up @@ -63,7 +62,6 @@
PostOnlyMixin,
RequireRecentAuthMixin,
SpaMixin,
StaffOnlyMixin,
)

from ..models import (
Expand Down Expand Up @@ -962,7 +960,6 @@ class OrgCRUDL(SmartCRUDL):
"export",
"prometheus",
"resthooks",
"service",
"flow_smtp",
"workspace",
)
Expand Down Expand Up @@ -1450,37 +1447,6 @@ def post(self, request, *args, **kwargs):
self.object.release(request.user)
return self.render_modal_response()

class Service(StaffOnlyMixin, SmartFormView):
class ServiceForm(forms.Form):
other_org = ModelChoiceField(queryset=Org.objects.all(), widget=forms.HiddenInput())
next = forms.CharField(widget=forms.HiddenInput(), required=False)

form_class = ServiceForm
fields = ("other_org", "next")

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["other_org"] = Org.objects.filter(id=self.request.GET.get("other_org")).first()
context["next"] = self.request.GET.get("next", "")
return context

def derive_initial(self):
initial = super().derive_initial()
initial["other_org"] = self.request.GET.get("other_org", "")
initial["next"] = self.request.GET.get("next", "")
return initial

# valid form means we set our org and redirect to their inbox
def form_valid(self, form):
switch_to_org(self.request, form.cleaned_data["other_org"], servicing=True)
success_url = form.cleaned_data["next"] or reverse("msgs.msg_inbox")
return HttpResponseRedirect(success_url)

# invalid form login 'logs out' the user from the org and takes them to the org manage page
def form_invalid(self, form):
switch_to_org(self.request, None)
return HttpResponseRedirect(reverse("staff.org_list"))

class SubOrgs(SpaMixin, ContextMenuMixin, OrgPermsMixin, InferOrgMixin, SmartListView):
title = _("Workspaces")
menu_path = "/settings/workspaces"
Expand Down
70 changes: 70 additions & 0 deletions temba/staff/tests.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.contrib.auth.models import Group
from django.urls import reverse

from temba.contacts.models import Contact
from temba.orgs.models import Org, OrgMembership, OrgRole
from temba.tests import CRUDLTestMixin, TembaTest, mock_mailroom
from temba.utils.views.mixins import TEMBA_MENU_SELECTION
Expand Down Expand Up @@ -136,6 +137,75 @@ def assertOrgFilter(query: str, expected_orgs: list):
self.org.refresh_from_db()
self.assertTrue(self.org.is_verified)

@mock_mailroom
def test_service(self, mr_mocks):
service_url = reverse("staff.org_service")
inbox_url = reverse("msgs.msg_inbox")

# without logging in, try to service our main org
response = self.client.get(service_url, {"other_org": self.org.id, "next": inbox_url})
self.assertLoginRedirect(response)

response = self.client.post(service_url, {"other_org": self.org.id})
self.assertLoginRedirect(response)

# try logging in with a normal user
self.login(self.admin)

# same thing, no permission
response = self.client.get(service_url, {"other_org": self.org.id, "next": inbox_url})
self.assertLoginRedirect(response)

response = self.client.post(service_url, {"other_org": self.org.id})
self.assertLoginRedirect(response)

# ok, log in as our cs rep
self.login(self.customer_support)

# getting invalid org, has no service form
response = self.client.get(service_url, {"other_org": 325253256, "next": inbox_url})
self.assertContains(response, "Invalid org")

# posting invalid org just redirects back to manage page
response = self.client.post(service_url, {"other_org": 325253256})
self.assertRedirect(response, "/staff/org/")

# then service our org
response = self.client.get(service_url, {"other_org": self.org.id})
self.assertContains(response, "You are about to service the workspace, <b>Nyaruka</b>.")

# requesting a next page has a slightly different message
response = self.client.get(service_url, {"other_org": self.org.id, "next": inbox_url})
self.assertContains(response, "The page you are requesting belongs to a different workspace, <b>Nyaruka</b>.")

response = self.client.post(service_url, {"other_org": self.org.id})
self.assertRedirect(response, "/msg/")
self.assertEqual(self.org.id, self.client.session["org_id"])
self.assertTrue(self.client.session["servicing"])

# specify redirect_url
response = self.client.post(service_url, {"other_org": self.org.id, "next": "/flow/"})
self.assertRedirect(response, "/flow/")

# create a new contact
response = self.client.post(
reverse("contacts.contact_create"), data=dict(name="Ben Haggerty", phone="0788123123")
)
self.assertNoFormErrors(response)

# make sure that contact's created on is our cs rep
contact = Contact.objects.get(urns__path="+250788123123", org=self.org)
self.assertEqual(self.customer_support, contact.created_by)

self.assertEqual(self.org.id, self.client.session["org_id"])
self.assertTrue(self.client.session["servicing"])

# stop servicing
response = self.client.post(service_url, {})
self.assertRedirect(response, "/staff/org/")
self.assertIsNone(self.client.session["org_id"])
self.assertFalse(self.client.session["servicing"])


class UserCRUDLTest(TembaTest, CRUDLTestMixin):
def test_list(self):
Expand Down
38 changes: 35 additions & 3 deletions temba/staff/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from smartmin.users.models import FailedLogin, PasswordHistory
from smartmin.users.views import UserUpdateForm
from smartmin.views import SmartCRUDL, SmartDeleteView, SmartListView, SmartReadView, SmartUpdateView
from smartmin.views import SmartCRUDL, SmartDeleteView, SmartFormView, SmartListView, SmartReadView, SmartUpdateView

from django import forms
from django.conf import settings
Expand All @@ -14,14 +14,15 @@
from django.views.decorators.csrf import csrf_exempt

from temba.orgs.models import Org, OrgRole, User
from temba.orgs.views import switch_to_org
from temba.utils import get_anonymous_user
from temba.utils.fields import SelectMultipleWidget
from temba.utils.views.mixins import ComponentFormMixin, ContextMenuMixin, ModalFormMixin, SpaMixin, StaffOnlyMixin


class OrgCRUDL(SmartCRUDL):
model = Org
actions = ("read", "update", "list")
actions = ("read", "update", "list", "service")

class Read(StaffOnlyMixin, SpaMixin, ContextMenuMixin, SmartReadView):
def build_context_menu(self, menu):
Expand Down Expand Up @@ -55,7 +56,7 @@ def build_context_menu(self, menu):
menu.new_group()
menu.add_url_post(
_("Service"),
f'{reverse("orgs.org_service")}?other_org={obj.id}&next={reverse("msgs.msg_inbox", args=[])}',
f'{reverse("staff.org_service")}?other_org={obj.id}&next={reverse("msgs.msg_inbox", args=[])}',
)

def get_context_data(self, **kwargs):
Expand Down Expand Up @@ -226,6 +227,37 @@ def pre_save(self, obj):
obj.limits = cleaned_data["limits"]
return obj

class Service(StaffOnlyMixin, SmartFormView):
class ServiceForm(forms.Form):
other_org = forms.ModelChoiceField(queryset=Org.objects.all(), widget=forms.HiddenInput())
next = forms.CharField(widget=forms.HiddenInput(), required=False)

form_class = ServiceForm
fields = ("other_org", "next")

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["other_org"] = Org.objects.filter(id=self.request.GET.get("other_org")).first()
context["next"] = self.request.GET.get("next", "")
return context

def derive_initial(self):
initial = super().derive_initial()
initial["other_org"] = self.request.GET.get("other_org", "")
initial["next"] = self.request.GET.get("next", "")
return initial

# valid form means we set our org and redirect to their inbox
def form_valid(self, form):
switch_to_org(self.request, form.cleaned_data["other_org"], servicing=True)
success_url = form.cleaned_data["next"] or reverse("msgs.msg_inbox")
return HttpResponseRedirect(success_url)

# invalid form login 'logs out' the user from the org and takes them to the org manage page
def form_invalid(self, form):
switch_to_org(self.request, None)
return HttpResponseRedirect(reverse("staff.org_list"))


class UserCRUDL(SmartCRUDL):
model = User
Expand Down
2 changes: 1 addition & 1 deletion temba/tests/crudl.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ def check(self, test_cls, response, msg_prefix):

class StaffRedirect(BaseCheck):
def check(self, test_cls, response, msg_prefix):
test_cls.assertRedirect(response, reverse("orgs.org_service"), msg=f"{msg_prefix}: expected staff redirect")
test_cls.assertRedirect(response, reverse("staff.org_service"), msg=f"{msg_prefix}: expected staff redirect")


class LoginRedirectOr404(BaseCheck):
Expand Down
2 changes: 1 addition & 1 deletion templates/frame.html
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@
right:0;
bottom:0"
class="servicing absolute bg-secondary my-2 mr-20 rounded shadow-xl">
<a href="{% url 'orgs.org_service' %}"
<a href="{% url 'staff.org_service' %}"
style="text-decoration:none"
onclick="handlePosterizeClick(event)"
class="relative">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
You are about to service the workspace, <b>{{ other_org.name }}</b>.
{% endif %}
</div>
<form method="post" action="{% url 'orgs.org_service' %}">
<form method="post" action="{% url 'staff.org_service' %}">
{% for field in form.fields %}
{% render_field field %}
{% endfor %}
Expand Down

0 comments on commit 01b7f99

Please sign in to comment.