onboarding-emails: Add new onboarding email for organization creator.

Adds a new onboarding email `onboarding_team_to_zulip` for the user
who created the new Zulip organization.

Co-authored by: Alya Abbott <alya@zulip.com>
This commit is contained in:
Lauryn Menard
2023-04-24 17:46:49 +02:00
committed by Tim Abbott
parent ba895b64ef
commit 02d6b3e16d
9 changed files with 148 additions and 23 deletions

View File

@@ -0,0 +1,40 @@
{% extends "zerver/emails/email_base_default.html" %}
{% block illustration %}
<img src="{{ email_images_base_url }}/email_logo.png" alt=""/>
{% endblock %}
{% block content %}
<p>
{% trans %}If you've already decided to use Zulip for your organization, welcome! You can use our <a href="{{ get_organization_started }}">guide for setting up your organization</a> to get started.{% endtrans %}
</p>
<p>
{% trans %}Otherwise, here is some advice we often hear from customers for evaluating <i>any</i> team chat product:{% endtrans %}
<ol>
<li>{% trans %}<a href="{{ invite_users }}"><b>Invite your teammates</b></a> to explore with you and share their unique perspectives.{% endtrans %}
{% trans %}Use the app itself to chat about your impressions.{% endtrans %}
</li>
<li>{% trans %}<a href="{{ trying_out_zulip}}"><b>Run a week-long trial</b></a> with your team, without using any other chat tools. This is the only way to truly experience how a new chat app will help your team communicate.{% endtrans %}
</li>
</ol>
</p>
<p>
{% trans %}Zulip is designed to <a href="{{ why_zulip }}">enable efficient communication</a>, and we hope these tips help your team experience it in action.{% endtrans %}
</p>
<p>
{% if corporate_enabled %}
{{macros.contact_us_zulip_cloud(support_email)}}
{% else %}
{{macros.contact_us_self_hosted(support_email)}}
{% endif %}
</p>
{% endblock %}
{% block manage_preferences %}
<p><a href="{{ unsubscribe_link }}">{% trans %}Unsubscribe from welcome emails for {{ realm_name }}{% endtrans %}</a></p>
{% endblock %}

View File

@@ -0,0 +1 @@
{{ _("Choosing the chat app for your team") }}

View File

@@ -0,0 +1,19 @@
{% trans %}If you've already decided to use Zulip for your organization, welcome! You can use our guide for setting up your organization to get started.{% endtrans %} [ {{ get_organization_started }} ]
{% trans %}Otherwise, here is some advice we often hear from customers for evaluating any team chat product:{% endtrans %}
1. {% trans %}Invite your teammates to explore with you and share their unique perspectives.{% endtrans %} [ {{ invite_users }} ] {% trans %}Use the app itself to chat about your impressions.{% endtrans %}
2. {% trans %}Run a week-long trial with your team, without using any other chat tools. This is the only way to truly experience how a new chat app will help your team communicate.{% endtrans %} [ {{ trying_out_zulip }} ]
{% trans %}Zulip is designed to enable efficient communication, and we hope these tips help your team experience it in action.{% endtrans %} [ {{ why_zulip }} ]
{% if corporate_enabled %}
{% trans %}Do you have questions or feedback to share? Contact us at {{ support_email }} — we'd love to help!{% endtrans %}
{% else %}
{% trans %}If you have any questions, please contact this Zulip server's administrators at {{ support_email }}.{% endtrans %}
{% endif %}
----
{% trans %}Unsubscribe from welcome emails for {{ realm_name }}{% endtrans %}: {{ unsubscribe_link }}

View File

@@ -682,6 +682,7 @@ def get_onboarding_email_schedule(user: UserProfile) -> Dict[str, timedelta]:
# or comes in while they are dealing with their inbox.
"onboarding_zulip_topics": timedelta(days=2, hours=-1),
"onboarding_zulip_guide": timedelta(days=4, hours=-1),
"onboarding_team_to_zulip": timedelta(days=6, hours=-1),
}
user_tz = user.timezone
@@ -693,17 +694,24 @@ def get_onboarding_email_schedule(user: UserProfile) -> Dict[str, timedelta]:
# -Do not send emails on Saturday or Sunday
# -Have at least one weekday between each (potential) email
# User signed up on Monday
if signup_day == 1:
# Send onboarding_team_to_zulip on Tuesday
onboarding_emails["onboarding_team_to_zulip"] = timedelta(days=8, hours=-1)
# User signed up on Tuesday
if signup_day == 2:
# Send onboarding_zulip_topics on Thursday
# Send onboarding_zulip_guide on Monday
onboarding_emails["onboarding_zulip_guide"] = timedelta(days=6, hours=-1)
# Send onboarding_team_to_zulip on Wednesday
onboarding_emails["onboarding_team_to_zulip"] = timedelta(days=8, hours=-1)
# User signed up on Wednesday
if signup_day == 3:
# Send onboarding_zulip_topics on Friday
# Send onboarding_zulip_guide on Tuesday
onboarding_emails["onboarding_zulip_guide"] = timedelta(days=6, hours=-1)
# Send onboarding_team_to_zulip on Thursday
onboarding_emails["onboarding_team_to_zulip"] = timedelta(days=8, hours=-1)
# User signed up on Thursday
if signup_day == 4:
@@ -711,6 +719,8 @@ def get_onboarding_email_schedule(user: UserProfile) -> Dict[str, timedelta]:
onboarding_emails["onboarding_zulip_topics"] = timedelta(days=4, hours=-1)
# Send onboarding_zulip_guide on Wednesday
onboarding_emails["onboarding_zulip_guide"] = timedelta(days=6, hours=-1)
# Send onboarding_team_to_zulip on Friday
onboarding_emails["onboarding_team_to_zulip"] = timedelta(days=8, hours=-1)
# User signed up on Friday
if signup_day == 5:
@@ -718,6 +728,15 @@ def get_onboarding_email_schedule(user: UserProfile) -> Dict[str, timedelta]:
onboarding_emails["onboarding_zulip_topics"] = timedelta(days=4, hours=-1)
# Send onboarding_zulip_guide on Thursday
onboarding_emails["onboarding_zulip_guide"] = timedelta(days=6, hours=-1)
# Send onboarding_team_to_zulip on Monday
onboarding_emails["onboarding_team_to_zulip"] = timedelta(days=10, hours=-1)
# User signed up on Saturday; no adjustments needed
# User signed up on Sunday
if signup_day == 7:
# Send onboarding_team_to_zulip on Monday
onboarding_emails["onboarding_team_to_zulip"] = timedelta(days=8, hours=-1)
return onboarding_emails
@@ -881,6 +900,28 @@ def enqueue_welcome_emails(user: UserProfile, realm_creation: bool = False) -> N
delay=onboarding_email_schedule["onboarding_zulip_guide"],
)
# We only send the onboarding_team_to_zulip email to user who created the organization.
if realm_creation:
onboarding_team_to_zulip_context = common_context(user)
onboarding_team_to_zulip_context.update(
unsubscribe_link=unsubscribe_link,
get_organization_started=realm_url
+ "/help/getting-your-organization-started-with-zulip",
invite_users=realm_url + "/help/invite-users-to-join",
trying_out_zulip=realm_url + "/help/trying-out-zulip",
why_zulip="https://zulip.com/why-zulip/",
)
send_future_email(
"zerver/emails/onboarding_team_to_zulip",
user.realm,
to_user_ids=[user.id],
from_name=from_name,
from_address=from_address,
context=onboarding_team_to_zulip_context,
delay=onboarding_email_schedule["onboarding_team_to_zulip"],
)
def convert_html_to_markdown(html: str) -> str:
# html2text is GPL licensed, so run it as a subprocess.

View File

@@ -4635,6 +4635,7 @@ EMAIL_TYPES = {
"account_registered": ScheduledEmail.WELCOME,
"onboarding_zulip_topics": ScheduledEmail.WELCOME,
"onboarding_zulip_guide": ScheduledEmail.WELCOME,
"onboarding_team_to_zulip": ScheduledEmail.WELCOME,
"digest": ScheduledEmail.DIGEST,
"invitation_reminder": ScheduledEmail.INVITATION_REMINDER,
}

View File

@@ -26,7 +26,7 @@ class EmailLogTest(ZulipTestCase):
output_log = (
"INFO:root:Emails sent in development are available at http://testserver/emails"
)
self.assertEqual(m.output, [output_log for i in range(17)])
self.assertEqual(m.output, [output_log for i in range(18)])
def test_forward_address_details(self) -> None:
try:

View File

@@ -482,12 +482,12 @@ class TestFollowupEmails(ZulipTestCase):
def test_followup_emails_for_regular_realms(self) -> None:
cordelia = self.example_user("cordelia")
send_account_registered_email(self.example_user("cordelia"), realm_creation=True)
enqueue_welcome_emails(self.example_user("cordelia"))
enqueue_welcome_emails(self.example_user("cordelia"), realm_creation=True)
scheduled_emails = ScheduledEmail.objects.filter(users=cordelia).order_by(
"scheduled_timestamp"
)
assert scheduled_emails is not None
self.assert_length(scheduled_emails, 2)
self.assert_length(scheduled_emails, 3)
self.assertEqual(
orjson.loads(scheduled_emails[0].data)["template_prefix"],
"zerver/emails/account_registered",
@@ -496,6 +496,10 @@ class TestFollowupEmails(ZulipTestCase):
orjson.loads(scheduled_emails[1].data)["template_prefix"],
"zerver/emails/onboarding_zulip_guide",
)
self.assertEqual(
orjson.loads(scheduled_emails[2].data)["template_prefix"],
"zerver/emails/onboarding_team_to_zulip",
)
deliver_scheduled_emails(scheduled_emails[0])
from django.core.mail import outbox
@@ -513,12 +517,12 @@ class TestFollowupEmails(ZulipTestCase):
)
cordelia.realm.save()
send_account_registered_email(self.example_user("cordelia"), realm_creation=True)
enqueue_welcome_emails(self.example_user("cordelia"))
enqueue_welcome_emails(self.example_user("cordelia"), realm_creation=True)
scheduled_emails = ScheduledEmail.objects.filter(users=cordelia).order_by(
"scheduled_timestamp"
)
assert scheduled_emails is not None
self.assert_length(scheduled_emails, 2)
self.assert_length(scheduled_emails, 3)
self.assertEqual(
orjson.loads(scheduled_emails[0].data)["template_prefix"],
"zerver/emails/account_registered",
@@ -527,6 +531,10 @@ class TestFollowupEmails(ZulipTestCase):
orjson.loads(scheduled_emails[1].data)["template_prefix"],
"zerver/emails/onboarding_zulip_guide",
)
self.assertEqual(
orjson.loads(scheduled_emails[2].data)["template_prefix"],
"zerver/emails/onboarding_team_to_zulip",
)
deliver_scheduled_emails(scheduled_emails[0])
from django.core.mail import outbox
@@ -562,6 +570,7 @@ class TestOnboardingEmailDelay(ZulipTestCase):
date_joined: str,
onboarding_zulip_topics: int,
onboarding_zulip_guide: int,
onboarding_team_to_zulip: int,
) -> None:
DAY_OF_WEEK = {
"Monday": datetime(2018, 1, 1, 1, 0, 0, 0, tzinfo=timezone.utc),
@@ -591,35 +600,43 @@ class TestOnboardingEmailDelay(ZulipTestCase):
self.assertEqual(day_sent, onboarding_zulip_guide)
self.assertNotIn(day_sent, WEEKEND)
# onboarding_team_to_zulip
day_sent = (
DAY_OF_WEEK[date_joined] + onboarding_email_schedule["onboarding_team_to_zulip"]
).isoweekday()
self.assertEqual(day_sent, onboarding_team_to_zulip)
self.assertNotIn(day_sent, WEEKEND)
def test_get_onboarding_email_schedule(self) -> None:
user_profile = self.example_user("hamlet")
# joined Monday: schedule = Wednesday:3, Friday:5,
self.verify_onboarding_email_schedule(user_profile, "Monday", 3, 5)
# joined Monday: schedule = Wednesday:3, Friday:5, Tuesday:2
self.verify_onboarding_email_schedule(user_profile, "Monday", 3, 5, 2)
# joined Tuesday: schedule = Thursday:4, Monday:1
self.verify_onboarding_email_schedule(user_profile, "Tuesday", 4, 1)
# joined Tuesday: schedule = Thursday:4, Monday:1, Wednesday:3
self.verify_onboarding_email_schedule(user_profile, "Tuesday", 4, 1, 3)
# joined Wednesday: schedule = Friday:5, Tuesday:2
self.verify_onboarding_email_schedule(user_profile, "Wednesday", 5, 2)
# joined Wednesday: schedule = Friday:5, Tuesday:2, Thursday:4
self.verify_onboarding_email_schedule(user_profile, "Wednesday", 5, 2, 4)
# joined Thursday: schedule = Monday:1, Wednesday:3
self.verify_onboarding_email_schedule(user_profile, "Thursday", 1, 3)
# joined Thursday: schedule = Monday:1, Wednesday:3, Friday:5
self.verify_onboarding_email_schedule(user_profile, "Thursday", 1, 3, 5)
# joined Friday: schedule = Tuesday:2, Thursday:4
self.verify_onboarding_email_schedule(user_profile, "Friday", 2, 4)
# joined Friday: schedule = Tuesday:2, Thursday:4, Monday:1
self.verify_onboarding_email_schedule(user_profile, "Friday", 2, 4, 1)
# joined Saturday: schedule = Monday:1, Wednesday:3
self.verify_onboarding_email_schedule(user_profile, "Saturday", 1, 3)
# joined Saturday: schedule = Monday:1, Wednesday:3, Friday:5
self.verify_onboarding_email_schedule(user_profile, "Saturday", 1, 3, 5)
# joined Sunday: schedule = Tuesday:2, Thursday:4
self.verify_onboarding_email_schedule(user_profile, "Sunday", 2, 4)
# joined Sunday: schedule = Tuesday:2, Thursday:4, Monday:1
self.verify_onboarding_email_schedule(user_profile, "Sunday", 2, 4, 1)
def test_time_offset_for_onboarding_email_schedule(self) -> None:
user_profile = self.example_user("hamlet")
days_delayed = {
"4": timedelta(days=4, hours=-1),
"6": timedelta(days=6, hours=-1),
"8": timedelta(days=8, hours=-1),
}
# Time offset of America/Phoenix is -07:00
@@ -641,6 +658,12 @@ class TestOnboardingEmailDelay(ZulipTestCase):
days_delayed["6"],
)
# onboarding_team_to_zulip sent on Friday
self.assertEqual(
onboarding_email_schedule["onboarding_team_to_zulip"],
days_delayed["8"],
)
class TestCustomWelcomeEmailSender(ZulipTestCase):
def test_custom_welcome_email_sender(self) -> None:

View File

@@ -402,7 +402,7 @@ class TestDevelopmentEmailsLog(ZulipTestCase):
"INFO:root:Emails sent in development are available at http://testserver/emails"
)
# logger.output is a list of all the log messages captured. Verify it is as expected.
self.assertEqual(logger.output, [output_log] * 17)
self.assertEqual(logger.output, [output_log] * 18)
# Now, lets actually go the URL the above call redirects to, i.e., /emails/
result = self.client_get(result["Location"])

View File

@@ -147,7 +147,7 @@ def generate_all_emails(request: HttpRequest) -> HttpResponse:
send_account_registered_email(get_user_by_delivery_email("iago@zulip.com", realm))
# Onboarding emails for admin user
enqueue_welcome_emails(get_user_by_delivery_email("iago@zulip.com", realm))
enqueue_welcome_emails(get_user_by_delivery_email("iago@zulip.com", realm), realm_creation=True)
# Realm reactivation email
do_send_realm_reactivation_email(realm, acting_user=None)