diff --git a/analytics/tests/test_support_views.py b/analytics/tests/test_support_views.py index 17d26b1390..4b53445cee 100644 --- a/analytics/tests/test_support_views.py +++ b/analytics/tests/test_support_views.py @@ -702,3 +702,26 @@ class TestSupportEndpoint(ZulipTestCase): result = self.client_post("/activity/support", {"realm_id": f"{lear_realm.id}"}) self.assert_json_error(result, "Invalid parameters") m.assert_not_called() + + def test_delete_user(self) -> None: + cordelia = self.example_user("cordelia") + hamlet = self.example_user("hamlet") + hamlet_email = hamlet.delivery_email + realm = get_realm("zulip") + self.login_user(cordelia) + + result = self.client_post( + "/activity/support", {"realm_id": f"{realm.id}", "delete_user_by_id": hamlet.id} + ) + self.assertEqual(result.status_code, 302) + self.assertEqual(result["Location"], "/login/") + + self.login("iago") + + with mock.patch("analytics.views.support.do_delete_user_preserving_messages") as m: + result = self.client_post( + "/activity/support", + {"realm_id": f"{realm.id}", "delete_user_by_id": hamlet.id}, + ) + m.assert_called_once_with(hamlet) + self.assert_in_success_response([f"{hamlet_email} in zulip deleted"], result) diff --git a/analytics/views/support.py b/analytics/views/support.py index 4b88eefb37..c95d310c39 100644 --- a/analytics/views/support.py +++ b/analytics/views/support.py @@ -26,6 +26,7 @@ from zerver.actions.realm_settings import ( do_scrub_realm, do_send_realm_reactivation_email, ) +from zerver.actions.users import do_delete_user_preserving_messages from zerver.decorator import require_server_admin from zerver.forms import check_subdomain_available from zerver.lib.exceptions import JsonableError @@ -42,6 +43,7 @@ from zerver.models import ( UserProfile, get_org_type_display_name, get_realm, + get_user_profile_by_id, ) from zerver.views.invite import get_invitee_emails_set @@ -166,6 +168,7 @@ def support( default=None, str_validator=check_string_in(VALID_MODIFY_PLAN_METHODS) ), scrub_realm: bool = REQ(default=False, json_validator=check_bool), + delete_user_by_id: Optional[int] = REQ(default=None, converter=to_non_negative_int), query: Optional[str] = REQ("q", default=None), org_type: Optional[int] = REQ(default=None, converter=to_non_negative_int), ) -> HttpResponse: @@ -276,6 +279,12 @@ def support( elif scrub_realm: do_scrub_realm(realm, acting_user=acting_user) context["success_message"] = f"{realm.string_id} scrubbed." + elif delete_user_by_id: + user_profile_for_deletion = get_user_profile_by_id(delete_user_by_id) + user_email = user_profile_for_deletion.delivery_email + assert user_profile_for_deletion.realm == realm + do_delete_user_preserving_messages(user_profile_for_deletion) + context["success_message"] = f"{user_email} in {realm.subdomain} deleted." if query: key_words = get_invitee_emails_set(query) diff --git a/templates/analytics/support.html b/templates/analytics/support.html index a30cf9e10d..01c5249c2c 100644 --- a/templates/analytics/support.html +++ b/templates/analytics/support.html @@ -41,6 +41,12 @@ Date joined: {{ user.date_joined|timesince }} ago
Is active: {{ user.is_active }}
Role: {{ user.get_role_name() }}
+
+ {{ csrf_input }} + + + +

{% include "analytics/realm_details.html" %} diff --git a/web/src/analytics/support.ts b/web/src/analytics/support.ts index f7d56eb64d..439a3a542d 100644 --- a/web/src/analytics/support.ts +++ b/web/src/analytics/support.ts @@ -18,6 +18,31 @@ $(() => { } }); + $("body").on("click", ".delete-user-button", function (e) { + e.preventDefault(); + const message = + "Confirm the email of the user you want to delete.\n\n WARNING! This action is irreversible!"; + const actual_email = $(this).attr("data-email"); + // eslint-disable-next-line no-alert + const confirmed_email = window.prompt(message); + if (confirmed_email === actual_email) { + const actual_string_id = $(this).attr("data-string-id"); + // eslint-disable-next-line no-alert + const confirmed_string_id = window.prompt( + "Now provide string_id of the realm to confirm.", + ); + if (confirmed_string_id === actual_string_id) { + this.form.submit(); + } else { + // eslint-disable-next-line no-alert + window.alert("The string_id you entered is not correct. Aborted."); + } + } else { + // eslint-disable-next-line no-alert + window.alert("The email you entered is not correct. Aborted."); + } + }); + $("a.copy-button").on("click", function () { common.copy_data_attribute_value($(this), "copytext"); });