diff --git a/analytics/tests/test_views.py b/analytics/tests/test_views.py
index 03568bf133..1a061999b5 100644
--- a/analytics/tests/test_views.py
+++ b/analytics/tests/test_views.py
@@ -4,6 +4,7 @@ from typing import List, Optional
import mock
from django.utils.timezone import utc
from django.http import HttpResponse
+import ujson
from analytics.lib.counts import COUNT_STATS, CountStat
from analytics.lib.time_utils import time_range
@@ -14,7 +15,9 @@ from analytics.views import rewrite_client_arrays, \
from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.timestamp import ceiling_to_day, \
ceiling_to_hour, datetime_to_timestamp
-from zerver.models import Client, get_realm
+from zerver.lib.actions import do_create_multiuse_invite_link, \
+ do_send_realm_reactivation_email
+from zerver.models import Client, get_realm, MultiuseInvite
class TestStatsEndpoint(ZulipTestCase):
def test_stats(self) -> None:
@@ -335,7 +338,7 @@ class TestSupportEndpoint(ZulipTestCase):
self.assert_in_success_response(['user\n', '
King Hamlet
',
'Email: hamlet@zulip.com', 'Is active: True
',
'Admins: iago@zulip.com\n',
- 'class="copy-button" data-admin-emails="iago@zulip.com"'
+ 'class="copy-button" data-copytext="iago@zulip.com"'
], result)
def check_zulip_realm_query_result(result: HttpResponse) -> None:
@@ -362,6 +365,39 @@ class TestSupportEndpoint(ZulipTestCase):
'scrub-realm-button">',
'data-string-id="lear"'], result)
+ def check_preregistration_user_query_result(result: HttpResponse, email: str, invite: Optional[bool]=False) -> None:
+ self.assert_in_success_response(['preregistration user\n',
+ 'Email: {}'.format(email),
+ ], result)
+ if invite:
+ self.assert_in_success_response(['invite'], result)
+ self.assert_in_success_response(['Expires in: 1\xa0week, 3',
+ 'Status: Link has never been clicked'], result)
+ self.assert_in_success_response([], result)
+ else:
+ self.assert_not_in_success_response(['invite'], result)
+ self.assert_in_success_response(['Expires in: 1\xa0day',
+ 'Status: Link has never been clicked'], result)
+
+ def check_realm_creation_query_result(result: HttpResponse, email: str) -> None:
+ self.assert_in_success_response(['preregistration user\n',
+ 'realm creation\n',
+ 'Link: http://zulip.testserver/accounts/do_confirm/',
+ 'Expires in: 1\xa0day
\n'
+ ], result)
+
+ def check_multiuse_invite_link_query_result(result: HttpResponse) -> None:
+ self.assert_in_success_response(['multiuse invite\n',
+ 'Link: http://zulip.testserver/join/',
+ 'Expires in: 1\xa0week, 3'
+ ], result)
+
+ def check_realm_reactivation_link_query_result(result: HttpResponse) -> None:
+ self.assert_in_success_response(['realm reactivation\n',
+ 'Link: http://zulip.testserver/reactivate/',
+ 'Expires in: 1\xa0day'
+ ], result)
+
cordelia_email = self.example_email("cordelia")
self.login(cordelia_email)
@@ -399,6 +435,36 @@ class TestSupportEndpoint(ZulipTestCase):
check_zulip_realm_query_result(result)
check_lear_realm_query_result(result)
+ self.client_post('/accounts/home/', {'email': self.nonreg_email("test")})
+ self.login(iago_email)
+ result = self.client_get("/activity/support", {"q": self.nonreg_email("test")})
+ check_preregistration_user_query_result(result, self.nonreg_email("test"))
+ check_zulip_realm_query_result(result)
+
+ stream_ids = [self.get_stream_id("Denmark")]
+ invitee_emails = [self.nonreg_email("test1")]
+ self.client_post("/json/invites", {"invitee_emails": invitee_emails,
+ "stream_ids": ujson.dumps(stream_ids), "invite_as": 1})
+ result = self.client_get("/activity/support", {"q": self.nonreg_email("test1")})
+ check_preregistration_user_query_result(result, self.nonreg_email("test1"), invite=True)
+ check_zulip_realm_query_result(result)
+
+ email = self.nonreg_email('alice')
+ self.client_post('/new/', {'email': email})
+ result = self.client_get("/activity/support", {"q": email})
+ check_realm_creation_query_result(result, email)
+
+ do_create_multiuse_invite_link(self.example_user("hamlet"), invited_as=1)
+ result = self.client_get("/activity/support", {"q": "zulip"})
+ check_multiuse_invite_link_query_result(result)
+ check_zulip_realm_query_result(result)
+ MultiuseInvite.objects.all().delete()
+
+ do_send_realm_reactivation_email(get_realm("zulip"))
+ result = self.client_get("/activity/support", {"q": "zulip"})
+ check_realm_reactivation_link_query_result(result)
+ check_zulip_realm_query_result(result)
+
def test_change_plan_type(self) -> None:
cordelia = self.example_user("cordelia")
self.login(cordelia.email)
diff --git a/analytics/views.py b/analytics/views.py
index 202e449898..4e530dfb98 100644
--- a/analytics/views.py
+++ b/analytics/views.py
@@ -20,6 +20,7 @@ from django.shortcuts import render
from django.template import loader
from django.utils.timezone import now as timezone_now, utc as timezone_utc
from django.utils.translation import ugettext as _
+from django.utils.timesince import timesince
from django.core.validators import URLValidator
from django.core.exceptions import ValidationError
from jinja2 import Markup as mark_safe
@@ -28,6 +29,7 @@ from analytics.lib.counts import COUNT_STATS, CountStat
from analytics.lib.time_utils import time_range
from analytics.models import BaseCount, InstallationCount, \
RealmCount, StreamCount, UserCount, last_successful_fill, installation_epoch
+from confirmation.models import Confirmation, confirmation_url, _properties
from zerver.decorator import require_server_admin, require_server_admin_api, \
to_non_negative_int, to_utc_datetime, zulip_login_required, require_non_guest_user
from zerver.lib.exceptions import JsonableError
@@ -39,12 +41,13 @@ from zerver.views.invite import get_invitee_emails_set
from zerver.lib.subdomains import get_subdomain_from_hostname
from zerver.lib.actions import do_change_plan_type, do_deactivate_realm, \
do_reactivate_realm, do_scrub_realm
+from confirmation.settings import STATUS_ACTIVE
if settings.BILLING_ENABLED:
from corporate.lib.stripe import attach_discount_to_realm, get_discount_for_realm
-from zerver.models import Client, get_realm, Realm, \
- UserActivity, UserActivityInterval, UserProfile
+from zerver.models import Client, get_realm, Realm, UserActivity, UserActivityInterval, \
+ UserProfile, PreregistrationUser, MultiuseInvite
if settings.ZILENCER_ENABLED:
from zilencer.models import RemoteInstallationCount, RemoteRealmCount, \
@@ -1027,6 +1030,46 @@ def get_activity(request: HttpRequest) -> HttpResponse:
context=dict(data=data, title=title, is_home=True),
)
+def get_confirmations(types: List[int], object_ids: List[int],
+ hostname: Optional[str]=None) -> List[Dict[str, Any]]:
+ lowest_datetime = timezone_now() - timedelta(days=30)
+ confirmations = Confirmation.objects.filter(type__in=types, object_id__in=object_ids,
+ date_sent__gte=lowest_datetime)
+ confirmation_dicts = []
+ for confirmation in confirmations:
+ realm = confirmation.realm
+ content_object = confirmation.content_object
+
+ if realm is not None:
+ realm_host = realm.host
+ elif isinstance(content_object, Realm):
+ realm_host = content_object.host
+ else:
+ realm_host = hostname
+
+ type = confirmation.type
+ days_to_activate = _properties[type].validity_in_days
+ expiry_date = confirmation.date_sent + timedelta(days=days_to_activate)
+
+ if hasattr(content_object, "status"):
+ if content_object.status == STATUS_ACTIVE:
+ link_status = "Link has been clicked"
+ else:
+ link_status = "Link has never been clicked"
+ else:
+ link_status = ""
+
+ if timezone_now() < expiry_date:
+ expires_in = timesince(confirmation.date_sent, expiry_date)
+ else:
+ expires_in = "Expired"
+
+ url = confirmation_url(confirmation.confirmation_key, realm_host, type)
+ confirmation_dicts.append({"object": confirmation.content_object,
+ "url": url, "type": type, "link_status": link_status,
+ "expires_in": expires_in})
+ return confirmation_dicts
+
@require_server_admin
def support(request: HttpRequest) -> HttpResponse:
context = {} # type: Dict[str, Any]
@@ -1091,12 +1134,27 @@ def support(request: HttpRequest) -> HttpResponse:
context["realms"] = realms
+ confirmations = [] # type: List[Dict[str, Any]]
+
+ preregistration_users = PreregistrationUser.objects.filter(email__in=key_words)
+ confirmations += get_confirmations([Confirmation.USER_REGISTRATION, Confirmation.INVITATION,
+ Confirmation.REALM_CREATION], preregistration_users,
+ hostname=request.get_host())
+
+ multiuse_invites = MultiuseInvite.objects.filter(realm__in=realms)
+ confirmations += get_confirmations([Confirmation.MULTIUSE_INVITE], multiuse_invites)
+
+ confirmations += get_confirmations([Confirmation.REALM_REACTIVATION], [realm.id for realm in realms])
+
+ context["confirmations"] = confirmations
+
def realm_admin_emails(realm: Realm) -> str:
return ", ".join(realm.get_human_admin_users().values_list("delivery_email", flat=True))
context["realm_admin_emails"] = realm_admin_emails
context["get_discount_for_realm"] = get_discount_for_realm
context["realm_icon_url"] = realm_icon_url
+ context["Confirmation"] = Confirmation
return render(request, 'analytics/support.html', context=context)
def get_user_activity_records_for_realm(realm: str, is_bot: bool) -> QuerySet:
diff --git a/static/js/analytics/support.js b/static/js/analytics/support.js
index c127d4ac41..c8d9b172ea 100644
--- a/static/js/analytics/support.js
+++ b/static/js/analytics/support.js
@@ -9,6 +9,6 @@ $(function () {
});
$('a.copy-button').click(function () {
- common.copy_data_attribute_value($(this), "admin-emails");
+ common.copy_data_attribute_value($(this), "copytext");
});
});
diff --git a/templates/analytics/realm_details.html b/templates/analytics/realm_details.html
index 216ab7ebea..ab6aec4878 100644
--- a/templates/analytics/realm_details.html
+++ b/templates/analytics/realm_details.html
@@ -5,7 +5,7 @@
activity
Date created: {{ realm.date_created|timesince }} ago
Admins: {{ realm_admin_emails(realm) }}
-
+
-
diff --git a/templates/analytics/support.html b/templates/analytics/support.html
index eef59c7b1e..e39faa35b5 100644
--- a/templates/analytics/support.html
+++ b/templates/analytics/support.html
@@ -41,7 +41,7 @@
Date joined: {{ user.date_joined|timesince }} ago
Is active: {{ user.is_active }}
Is admin: {{ user.is_realm_admin }}
-
+
{% include "analytics/realm_details.html" %}
@@ -55,6 +55,61 @@
{% endfor %}
+ {% for confirmation in confirmations %}
+ {% set object = confirmation.object %}
+
+ {% if confirmation.type == Confirmation.USER_REGISTRATION %}
+
preregistration user
+ {% set email = object.email %}
+ {% set realm = object.realm %}
+ {% set show_realm_details = True %}
+ {% elif confirmation.type == Confirmation.REALM_CREATION %}
+
preregistration user
+
realm creation
+ {% set email = object.email %}
+ {% set show_realm_details = False %}
+ {% elif confirmation.type == Confirmation.INVITATION %}
+
preregistration user
+
invite
+ {% set email = object.email %}
+ {% set realm = object.realm %}
+ {% set show_realm_details = True %}
+ {% elif confirmation.type == Confirmation.MULTIUSE_INVITE %}
+
multiuse invite
+ {% set realm = object.realm %}
+ {% set show_realm_details = False %}
+ {% elif confirmation.type == Confirmation.REALM_REACTIVATION %}
+
realm reactivation
+ {% set realm = object %}
+ {% set show_realm_details = False %}
+ {% endif %}
+
+
+ {% if email %}
+
Email: {{ email }}
+ {% endif %}
+
Link: {{ confirmation.url }}
+
+
+
+
Expires in: {{ confirmation.expires_in }}
+ {% if confirmation.link_status %}
+
Status: {{ confirmation.link_status }}
+ {% endif %}
+
+ {% if show_realm_details %}
+
+
+ {% include "analytics/realm_details.html" %}
+
+ {% elif realm %}
+
Realm: {{ realm.string_id }}
+
+ {% endif %}
+
+
+
+ {% endfor %}
{% endblock %}
diff --git a/tools/linter_lib/custom_check.py b/tools/linter_lib/custom_check.py
index b35378854c..a9fc0e74ef 100644
--- a/tools/linter_lib/custom_check.py
+++ b/tools/linter_lib/custom_check.py
@@ -631,7 +631,7 @@ html_rules = whitespace_rules + prose_style_rules + [
('templates/zerver/app/markdown_help.html',
' | ')
]),
- 'exclude': set(["templates/zerver/emails", "templates/analytics/realm_details.html"]),
+ 'exclude': set(["templates/zerver/emails", "templates/analytics/realm_details.html", "templates/analytics/support.html"]),
'description': "`title` value should be translatable."},
{'pattern': r'''\Walt=["'][^{"']''',
'description': "alt argument should be enclosed by _() or it should be an empty string.",