analytics/support: Add ability to edit realm org type.

This commit is contained in:
Eeshan Garg 2021-10-07 15:30:54 -04:00 committed by Tim Abbott
parent bac64070c1
commit f25230c7d4
6 changed files with 88 additions and 4 deletions

View File

@ -71,12 +71,10 @@ class TestSupportEndpoint(ZulipTestCase):
def check_zulip_realm_query_result(result: HttpResponse) -> None: def check_zulip_realm_query_result(result: HttpResponse) -> None:
zulip_realm = get_realm("zulip") zulip_realm = get_realm("zulip")
org_type_display_name = get_org_type_display_name(zulip_realm.org_type)
first_human_user = zulip_realm.get_first_human_user() first_human_user = zulip_realm.get_first_human_user()
assert first_human_user is not None assert first_human_user is not None
self.assert_in_success_response( self.assert_in_success_response(
[ [
f"<b>Organization type</b>: {org_type_display_name}",
f"<b>First human user</b>: {first_human_user.delivery_email}\n", f"<b>First human user</b>: {first_human_user.delivery_email}\n",
f'<input type="hidden" name="realm_id" value="{zulip_realm.id}"', f'<input type="hidden" name="realm_id" value="{zulip_realm.id}"',
"Zulip Dev</h3>", "Zulip Dev</h3>",
@ -85,6 +83,7 @@ class TestSupportEndpoint(ZulipTestCase):
'input type="number" name="discount" value="None"', 'input type="number" name="discount" value="None"',
'<option value="active" selected>Active</option>', '<option value="active" selected>Active</option>',
'<option value="deactivated" >Deactivated</option>', '<option value="deactivated" >Deactivated</option>',
f'<option value="{zulip_realm.org_type}" selected>',
'scrub-realm-button">', 'scrub-realm-button">',
'data-string-id="zulip"', 'data-string-id="zulip"',
], ],
@ -357,6 +356,28 @@ class TestSupportEndpoint(ZulipTestCase):
["Plan type of zulip changed from self hosted to limited"], result ["Plan type of zulip changed from self hosted to limited"], result
) )
def test_change_org_type(self) -> None:
cordelia = self.example_user("cordelia")
self.login_user(cordelia)
result = self.client_post(
"/activity/support", {"realm_id": f"{cordelia.realm_id}", "org_type": "70"}
)
self.assertEqual(result.status_code, 302)
self.assertEqual(result["Location"], "/login/")
iago = self.example_user("iago")
self.login_user(iago)
with mock.patch("analytics.views.support.do_change_realm_org_type") as m:
result = self.client_post(
"/activity/support", {"realm_id": f"{iago.realm_id}", "org_type": "70"}
)
m.assert_called_once_with(get_realm("zulip"), 70, acting_user=iago)
self.assert_in_success_response(
["Org type of zulip changed from Business to Government"], result
)
def test_attach_discount(self) -> None: def test_attach_discount(self) -> None:
cordelia = self.example_user("cordelia") cordelia = self.example_user("cordelia")
lear_realm = get_realm("lear") lear_realm = get_realm("lear")

View File

@ -20,6 +20,7 @@ from zerver.decorator import require_server_admin
from zerver.forms import check_subdomain_available from zerver.forms import check_subdomain_available
from zerver.lib.actions import ( from zerver.lib.actions import (
do_change_plan_type, do_change_plan_type,
do_change_realm_org_type,
do_change_realm_subdomain, do_change_realm_subdomain,
do_deactivate_realm, do_deactivate_realm,
do_scrub_realm, do_scrub_realm,
@ -139,6 +140,7 @@ def support(
), ),
scrub_realm: Optional[bool] = REQ(default=None, json_validator=check_bool), scrub_realm: Optional[bool] = REQ(default=None, json_validator=check_bool),
query: Optional[str] = REQ("q", default=None), query: Optional[str] = REQ("q", default=None),
org_type: Optional[int] = REQ(default=None, converter=to_non_negative_int),
) -> HttpResponse: ) -> HttpResponse:
context: Dict[str, Any] = {} context: Dict[str, Any] = {}
@ -164,6 +166,11 @@ def support(
do_change_plan_type(realm, plan_type, acting_user=acting_user) do_change_plan_type(realm, plan_type, acting_user=acting_user)
msg = f"Plan type of {realm.string_id} changed from {get_plan_name(current_plan_type)} to {get_plan_name(plan_type)} " msg = f"Plan type of {realm.string_id} changed from {get_plan_name(current_plan_type)} to {get_plan_name(plan_type)} "
context["success_message"] = msg context["success_message"] = msg
elif org_type is not None:
current_realm_type = realm.org_type
do_change_realm_org_type(realm, org_type, acting_user=acting_user)
msg = f"Org type of {realm.string_id} changed from {get_org_type_display_name(current_realm_type)} to {get_org_type_display_name(org_type)} "
context["success_message"] = msg
elif discount is not None: elif discount is not None:
current_discount = get_discount_for_realm(realm) or 0 current_discount = get_discount_for_realm(realm) or 0
attach_discount_to_realm(realm, discount, acting_user=acting_user) attach_discount_to_realm(realm, discount, acting_user=acting_user)
@ -321,4 +328,8 @@ def support(
context["get_org_type_display_name"] = get_org_type_display_name context["get_org_type_display_name"] = get_org_type_display_name
context["realm_icon_url"] = realm_icon_url context["realm_icon_url"] = realm_icon_url
context["Confirmation"] = Confirmation context["Confirmation"] = Confirmation
context["sorted_realm_types"] = sorted(
Realm.ORG_TYPES.values(), key=lambda d: d["display_order"]
)
return render(request, "analytics/support.html", context=context) return render(request, "analytics/support.html", context=context)

View File

@ -4,8 +4,6 @@
<a target="_blank" rel="noopener noreferrer" href="/stats/realm/{{ realm.string_id }}/">stats</a> | <a target="_blank" rel="noopener noreferrer" href="/stats/realm/{{ realm.string_id }}/">stats</a> |
<a target="_blank" rel="noopener noreferrer" href="/realm_activity/{{ realm.string_id }}/">activity</a><br /> <a target="_blank" rel="noopener noreferrer" href="/realm_activity/{{ realm.string_id }}/">activity</a><br />
<b>Date created</b>: {{ realm.date_created|timesince }} ago<br /> <b>Date created</b>: {{ realm.date_created|timesince }} ago<br />
{% set org_type_display_name = get_org_type_display_name(realm.org_type) %}
<b>Organization type</b>: {{ org_type_display_name }}<br />
{% set owner_emails_string = get_realm_owner_emails_as_string(realm) %} {% set owner_emails_string = get_realm_owner_emails_as_string(realm) %}
<b>Owners</b>: {{ owner_emails_string }} <b>Owners</b>: {{ owner_emails_string }}
{% if owner_emails_string %} {% if owner_emails_string %}
@ -49,6 +47,21 @@
<input type="text" name="new_subdomain" required /> <input type="text" name="new_subdomain" required />
<button type="submit" class="btn btn-default support-submit-button">Update</button> <button type="submit" class="btn btn-default support-submit-button">Update</button>
</form> </form>
<form method="POST">
<b>Org type</b>:<br />
{{ csrf_input }}
<input type="hidden" name="realm_id" value="{{ realm.id }}" />
<select name="org_type" id="org_type">
{% for realm_type in sorted_realm_types %}
{% if not realm_type.hidden %}
<option value="{{ realm_type.id }}" {% if realm.org_type == realm_type.id %}selected{% endif %}>
{{ _(realm_type.name) }}
</option>
{% endif %}
{% endfor %}
</select>
<button type="submit" class="btn btn-default support-submit-button">Update</button>
</form>
<form method="POST" class="support-plan-type-form"> <form method="POST" class="support-plan-type-form">
<b>Plan type</b>:<br /> <b>Plan type</b>:<br />
{{ csrf_input }} {{ csrf_input }}

View File

@ -4582,6 +4582,24 @@ def do_change_logo_source(
send_event(realm, event, active_user_ids(realm.id)) send_event(realm, event, active_user_ids(realm.id))
def do_change_realm_org_type(
realm: Realm,
org_type: int,
acting_user: Optional[UserProfile],
) -> None:
old_value = realm.org_type
realm.org_type = org_type
realm.save(update_fields=["org_type"])
RealmAuditLog.objects.create(
event_type=RealmAuditLog.REALM_ORG_TYPE_CHANGED,
realm=realm,
event_time=timezone_now(),
acting_user=acting_user,
extra_data={"old_value": old_value, "new_value": org_type},
)
def do_change_plan_type( def do_change_plan_type(
realm: Realm, plan_type: int, *, acting_user: Optional[UserProfile] realm: Realm, plan_type: int, *, acting_user: Optional[UserProfile]
) -> None: ) -> None:

View File

@ -3769,6 +3769,7 @@ class AbstractRealmAuditLog(models.Model):
REALM_SUBDOMAIN_CHANGED = 214 REALM_SUBDOMAIN_CHANGED = 214
REALM_CREATED = 215 REALM_CREATED = 215
REALM_DEFAULT_USER_SETTINGS_CHANGED = 216 REALM_DEFAULT_USER_SETTINGS_CHANGED = 216
REALM_ORG_TYPE_CHANGED = 217
SUBSCRIPTION_CREATED = 301 SUBSCRIPTION_CREATED = 301
SUBSCRIPTION_ACTIVATED = 302 SUBSCRIPTION_ACTIVATED = 302

View File

@ -11,6 +11,7 @@ from confirmation.models import Confirmation, create_confirmation_link
from zerver.lib.actions import ( from zerver.lib.actions import (
do_add_deactivated_redirect, do_add_deactivated_redirect,
do_change_plan_type, do_change_plan_type,
do_change_realm_org_type,
do_change_realm_subdomain, do_change_realm_subdomain,
do_create_realm, do_create_realm,
do_deactivate_realm, do_deactivate_realm,
@ -611,6 +612,25 @@ class RealmTest(ZulipTestCase):
self.assertEqual(get_realm("onpremise").message_visibility_limit, None) self.assertEqual(get_realm("onpremise").message_visibility_limit, None)
self.assertEqual(get_realm("onpremise").upload_quota_gb, None) self.assertEqual(get_realm("onpremise").upload_quota_gb, None)
def test_change_org_type(self) -> None:
realm = get_realm("zulip")
iago = self.example_user("iago")
self.assertEqual(realm.org_type, Realm.ORG_TYPES["business"]["id"])
do_change_realm_org_type(realm, Realm.ORG_TYPES["government"]["id"], acting_user=iago)
realm = get_realm("zulip")
realm_audit_log = RealmAuditLog.objects.filter(
event_type=RealmAuditLog.REALM_ORG_TYPE_CHANGED
).last()
assert realm_audit_log is not None
expected_extra_data = {
"old_value": Realm.ORG_TYPES["business"]["id"],
"new_value": Realm.ORG_TYPES["government"]["id"],
}
self.assertEqual(realm_audit_log.extra_data, str(expected_extra_data))
self.assertEqual(realm_audit_log.acting_user, iago)
self.assertEqual(realm.org_type, Realm.ORG_TYPES["government"]["id"])
def test_change_plan_type(self) -> None: def test_change_plan_type(self) -> None:
realm = get_realm("zulip") realm = get_realm("zulip")
iago = self.example_user("iago") iago = self.example_user("iago")