From f71e2c824786ace3cf1d492ce99b0abb6913f102 Mon Sep 17 00:00:00 2001 From: Mateusz Mandera Date: Wed, 4 Oct 2023 13:38:12 +0200 Subject: [PATCH] support: Add basic support endpoint for remote servers. --- analytics/tests/test_support_views.py | 48 +++++++++++++++++++ analytics/urls.py | 3 +- analytics/views/support.py | 45 +++++++++++++++++ .../analytics/remote_server_support.html | 33 +++++++++++++ tools/linter_lib/custom_check.py | 1 + 5 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 templates/analytics/remote_server_support.html diff --git a/analytics/tests/test_support_views.py b/analytics/tests/test_support_views.py index 5557fde51e..ea2f62bf74 100644 --- a/analytics/tests/test_support_views.py +++ b/analytics/tests/test_support_views.py @@ -25,6 +25,54 @@ from zerver.models import ( if TYPE_CHECKING: from django.test.client import _MonkeyPatchedWSGIResponse as TestHttpResponse +import uuid + +from zilencer.models import RemoteZulipServer + + +class TestRemoteServerSupportEndpoint(ZulipTestCase): + def setUp(self) -> None: + super().setUp() + + # Set up some initial example data. + for i in range(20): + hostname = f"zulip-{i}.example.com" + RemoteZulipServer.objects.create( + hostname=hostname, contact_email=f"admin@{hostname}", plan_type=1, uuid=uuid.uuid4() + ) + + def test_search(self) -> None: + self.login("cordelia") + + result = self.client_get("/activity/remote/support") + self.assertEqual(result.status_code, 302) + self.assertEqual(result["Location"], "/login/") + + # Iago is the user with the appropriate permissions to access this page. + self.login("iago") + assert self.example_user("iago").is_staff + + result = self.client_get("/activity/remote/support") + self.assert_in_success_response( + [ + 'input type="text" name="q" class="input-xxlarge search-query" placeholder="hostname or contact email"' + ], + result, + ) + + result = self.client_get("/activity/remote/support", {"q": "zulip-1.example.com"}) + self.assert_in_success_response(["

zulip-1.example.com

"], result) + self.assert_not_in_success_response(["

zulip-2.example.com

"], result) + + result = self.client_get("/activity/remote/support", {"q": "example.com"}) + for i in range(20): + self.assert_in_success_response([f"

zulip-{i}.example.com

"], result) + + result = self.client_get("/activity/remote/support", {"q": "admin@zulip-2.example.com"}) + self.assert_in_success_response(["

zulip-2.example.com

"], result) + self.assert_in_success_response(["Contact email: admin@zulip-2.example.com"], result) + self.assert_not_in_success_response(["

zulip-1.example.com

"], result) + class TestSupportEndpoint(ZulipTestCase): def test_search(self) -> None: diff --git a/analytics/urls.py b/analytics/urls.py index 8d7380a906..7d7556c2db 100644 --- a/analytics/urls.py +++ b/analytics/urls.py @@ -21,7 +21,7 @@ from analytics.views.stats import ( stats_for_remote_installation, stats_for_remote_realm, ) -from analytics.views.support import support +from analytics.views.support import remote_servers_support, support from analytics.views.user_activity import get_user_activity from zerver.lib.rest import rest_path @@ -30,6 +30,7 @@ i18n_urlpatterns: List[Union[URLPattern, URLResolver]] = [ path("activity", get_installation_activity), path("activity/remote", get_remote_server_activity), path("activity/support", support, name="support"), + path("activity/remote/support", remote_servers_support, name="remote_servers_support"), path("realm_activity//", get_realm_activity), path("user_activity//", get_user_activity), path("stats/realm//", stats_for_realm), diff --git a/analytics/views/support.py b/analytics/views/support.py index c487e498f9..a8a6089750 100644 --- a/analytics/views/support.py +++ b/analytics/views/support.py @@ -17,6 +17,7 @@ from django.utils.timesince import timesince from django.utils.timezone import now as timezone_now from django.utils.translation import gettext as _ +from analytics.views.activity_common import remote_installation_stats_link from confirmation.models import Confirmation, confirmation_url from confirmation.settings import STATUS_USED from zerver.actions.create_realm import do_change_realm_subdomain @@ -48,6 +49,9 @@ from zerver.models import ( ) from zerver.views.invite import get_invitee_emails_set +if settings.ZILENCER_ENABLED: + from zilencer.models import RemoteZulipServer + if settings.BILLING_ENABLED: from corporate.lib.stripe import approve_sponsorship as do_approve_sponsorship from corporate.lib.stripe import ( @@ -406,3 +410,44 @@ def support( ) return render(request, "analytics/support.html", context=context) + + +def get_remote_servers_for_support( + email_to_search: Optional[str], hostname_to_search: Optional[str] +) -> List["RemoteZulipServer"]: + if not email_to_search and not hostname_to_search: + return [] + + remote_servers_query = RemoteZulipServer.objects.order_by("id") + if email_to_search: + remote_servers_query = remote_servers_query.filter(contact_email__iexact=email_to_search) + elif hostname_to_search: + remote_servers_query = remote_servers_query.filter(hostname__icontains=hostname_to_search) + + return list(remote_servers_query) + + +@require_server_admin +@has_request_variables +def remote_servers_support( + request: HttpRequest, query: Optional[str] = REQ("q", default=None) +) -> HttpResponse: + email_to_search = None + hostname_to_search = None + if query: + if "@" in query: + email_to_search = query + else: + hostname_to_search = query + + remote_servers = get_remote_servers_for_support( + email_to_search=email_to_search, hostname_to_search=hostname_to_search + ) + return render( + request, + "analytics/remote_server_support.html", + context=dict( + remote_servers=remote_servers, + remote_installation_stats_link=remote_installation_stats_link, + ), + ) diff --git a/templates/analytics/remote_server_support.html b/templates/analytics/remote_server_support.html new file mode 100644 index 0000000000..f70d393f6c --- /dev/null +++ b/templates/analytics/remote_server_support.html @@ -0,0 +1,33 @@ +{% extends "zerver/base.html" %} +{% set entrypoint = "support" %} + +{# Remote servers. #} + +{% block title %} +Remote servers +{% endblock %} + + +{% block content %} +
+
+
+
+ + +
+
+ +
+ {% for remote_server in remote_servers %} +
+ remote server +

{{ remote_server.hostname }}

+ Contact email: {{ remote_server.contact_email }}
+ Last updated: {{ remote_server.last_updated|timesince }} ago
+
+ {% endfor %} +
+
+ +{% endblock %} diff --git a/tools/linter_lib/custom_check.py b/tools/linter_lib/custom_check.py index 466ae52592..094192835c 100644 --- a/tools/linter_lib/custom_check.py +++ b/tools/linter_lib/custom_check.py @@ -548,6 +548,7 @@ html_rules: List["Rule"] = [ }, "exclude": { "templates/analytics/support.html", + "templates/analytics/remote_server_support.html", # We have URL template and Pygments language name as placeholders # in the below template which we don't want to be translatable. "web/templates/settings/playground_settings_admin.hbs",