mirror of
https://github.com/zulip/zulip.git
synced 2025-11-12 09:58:06 +00:00
send_email: Add support for multiple recipients.
This adds a function that sends provided email to all administrators of a realm, but in a single email. As a result, send_email now takes arguments to_user_ids and to_emails instead of to_user_id and to_email. We adjust other APIs to match, but note that send_future_email does not yet support the multiple recipients model for good reasons. Tweaked by tabbott to modify `manage.py deliver_email` to handle backwards-compatibily for any ScheduledEmail objects already in the database. Fixes #10896.
This commit is contained in:
committed by
Tim Abbott
parent
0fddf9a610
commit
92dc3637df
@@ -250,7 +250,7 @@ class ZulipPasswordResetForm(PasswordResetForm):
|
|||||||
if user is not None:
|
if user is not None:
|
||||||
context['active_account_in_realm'] = True
|
context['active_account_in_realm'] = True
|
||||||
context['reset_url'] = generate_password_reset_url(user, token_generator)
|
context['reset_url'] = generate_password_reset_url(user, token_generator)
|
||||||
send_email('zerver/emails/password_reset', to_user_id=user.id,
|
send_email('zerver/emails/password_reset', to_user_ids=[user.id],
|
||||||
from_name="Zulip Account Security",
|
from_name="Zulip Account Security",
|
||||||
from_address=FromAddress.tokenized_no_reply_address(),
|
from_address=FromAddress.tokenized_no_reply_address(),
|
||||||
context=context)
|
context=context)
|
||||||
@@ -259,7 +259,7 @@ class ZulipPasswordResetForm(PasswordResetForm):
|
|||||||
active_accounts_in_other_realms = UserProfile.objects.filter(email__iexact=email, is_active=True)
|
active_accounts_in_other_realms = UserProfile.objects.filter(email__iexact=email, is_active=True)
|
||||||
if active_accounts_in_other_realms:
|
if active_accounts_in_other_realms:
|
||||||
context['active_accounts_in_other_realms'] = active_accounts_in_other_realms
|
context['active_accounts_in_other_realms'] = active_accounts_in_other_realms
|
||||||
send_email('zerver/emails/password_reset', to_email=email,
|
send_email('zerver/emails/password_reset', to_emails=[email],
|
||||||
from_name="Zulip Account Security",
|
from_name="Zulip Account Security",
|
||||||
from_address=FromAddress.tokenized_no_reply_address(),
|
from_address=FromAddress.tokenized_no_reply_address(),
|
||||||
context=context)
|
context=context)
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ from zerver.lib.message import (
|
|||||||
)
|
)
|
||||||
from zerver.lib.realm_icon import realm_icon_url
|
from zerver.lib.realm_icon import realm_icon_url
|
||||||
from zerver.lib.retention import move_messages_to_archive
|
from zerver.lib.retention import move_messages_to_archive
|
||||||
from zerver.lib.send_email import send_email, FromAddress
|
from zerver.lib.send_email import send_email, FromAddress, send_email_to_admins
|
||||||
from zerver.lib.stream_subscription import (
|
from zerver.lib.stream_subscription import (
|
||||||
get_active_subscriptions_for_stream_id,
|
get_active_subscriptions_for_stream_id,
|
||||||
get_active_subscriptions_for_stream_ids,
|
get_active_subscriptions_for_stream_ids,
|
||||||
@@ -795,7 +795,7 @@ def do_start_email_change_process(user_profile: UserProfile, new_email: str) ->
|
|||||||
'new_email': new_email,
|
'new_email': new_email,
|
||||||
'activate_url': activation_url
|
'activate_url': activation_url
|
||||||
})
|
})
|
||||||
send_email('zerver/emails/confirm_new_email', to_email=new_email,
|
send_email('zerver/emails/confirm_new_email', to_emails=[new_email],
|
||||||
from_name='Zulip Account Security', from_address=FromAddress.tokenized_no_reply_address(),
|
from_name='Zulip Account Security', from_address=FromAddress.tokenized_no_reply_address(),
|
||||||
context=context)
|
context=context)
|
||||||
|
|
||||||
@@ -4429,7 +4429,7 @@ def do_send_confirmation_email(invitee: PreregistrationUser,
|
|||||||
context = {'referrer_full_name': referrer.full_name, 'referrer_email': referrer.email,
|
context = {'referrer_full_name': referrer.full_name, 'referrer_email': referrer.email,
|
||||||
'activate_url': activation_url, 'referrer_realm_name': referrer.realm.name}
|
'activate_url': activation_url, 'referrer_realm_name': referrer.realm.name}
|
||||||
from_name = "%s (via Zulip)" % (referrer.full_name,)
|
from_name = "%s (via Zulip)" % (referrer.full_name,)
|
||||||
send_email('zerver/emails/invitation', to_email=invitee.email, from_name=from_name,
|
send_email('zerver/emails/invitation', to_emails=[invitee.email], from_name=from_name,
|
||||||
from_address=FromAddress.tokenized_no_reply_address(), context=context)
|
from_address=FromAddress.tokenized_no_reply_address(), context=context)
|
||||||
|
|
||||||
def email_not_system_bot(email: str) -> None:
|
def email_not_system_bot(email: str) -> None:
|
||||||
@@ -5185,12 +5185,10 @@ def missing_any_realm_internal_bots() -> bool:
|
|||||||
|
|
||||||
def do_send_realm_reactivation_email(realm: Realm) -> None:
|
def do_send_realm_reactivation_email(realm: Realm) -> None:
|
||||||
confirmation_url = create_confirmation_link(realm, realm.host, Confirmation.REALM_REACTIVATION)
|
confirmation_url = create_confirmation_link(realm, realm.host, Confirmation.REALM_REACTIVATION)
|
||||||
admins = realm.get_admin_users()
|
|
||||||
context = {'confirmation_url': confirmation_url,
|
context = {'confirmation_url': confirmation_url,
|
||||||
'realm_uri': realm.uri,
|
'realm_uri': realm.uri,
|
||||||
'realm_name': realm.name}
|
'realm_name': realm.name}
|
||||||
for admin in admins:
|
send_email_to_admins(
|
||||||
send_email(
|
'zerver/emails/realm_reactivation', realm,
|
||||||
'zerver/emails/realm_reactivation', to_email=admin.email,
|
|
||||||
from_address=FromAddress.tokenized_no_reply_address(),
|
from_address=FromAddress.tokenized_no_reply_address(),
|
||||||
from_name="Zulip Account Security", context=context)
|
from_name="Zulip Account Security", context=context)
|
||||||
|
|||||||
@@ -262,6 +262,6 @@ def handle_digest_email(user_profile_id: int, cutoff: float) -> None:
|
|||||||
new_streams_count, new_users_count):
|
new_streams_count, new_users_count):
|
||||||
logger.info("Sending digest email for %s" % (user_profile.email,))
|
logger.info("Sending digest email for %s" % (user_profile.email,))
|
||||||
# Send now, as a ScheduledEmail
|
# Send now, as a ScheduledEmail
|
||||||
send_future_email('zerver/emails/digest', user_profile.realm, to_user_id=user_profile.id,
|
send_future_email('zerver/emails/digest', user_profile.realm, to_user_ids=[user_profile.id],
|
||||||
from_name="Zulip Digest", from_address=FromAddress.NOREPLY,
|
from_name="Zulip Digest", from_address=FromAddress.NOREPLY,
|
||||||
context=context)
|
context=context)
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ def do_send_missedmessage_events_reply_in_zulip(user_profile: UserProfile,
|
|||||||
|
|
||||||
email_dict = {
|
email_dict = {
|
||||||
'template_prefix': 'zerver/emails/missed_message',
|
'template_prefix': 'zerver/emails/missed_message',
|
||||||
'to_user_id': user_profile.id,
|
'to_user_ids': [user_profile.id],
|
||||||
'from_name': from_name,
|
'from_name': from_name,
|
||||||
'from_address': from_address,
|
'from_address': from_address,
|
||||||
'reply_to_email': formataddr((reply_to_name, reply_to_address)),
|
'reply_to_email': formataddr((reply_to_name, reply_to_address)),
|
||||||
@@ -534,12 +534,12 @@ def enqueue_welcome_emails(user: UserProfile, realm_creation: bool=False) -> Non
|
|||||||
context["ldap_username"] = user.email
|
context["ldap_username"] = user.email
|
||||||
|
|
||||||
send_future_email(
|
send_future_email(
|
||||||
"zerver/emails/followup_day1", user.realm, to_user_id=user.id, from_name=from_name,
|
"zerver/emails/followup_day1", user.realm, to_user_ids=[user.id], from_name=from_name,
|
||||||
from_address=from_address, context=context)
|
from_address=from_address, context=context)
|
||||||
|
|
||||||
if other_account_count == 0:
|
if other_account_count == 0:
|
||||||
send_future_email(
|
send_future_email(
|
||||||
"zerver/emails/followup_day2", user.realm, to_user_id=user.id, from_name=from_name,
|
"zerver/emails/followup_day2", user.realm, to_user_ids=[user.id], from_name=from_name,
|
||||||
from_address=from_address, context=context, delay=followup_day2_email_delay(user))
|
from_address=from_address, context=context, delay=followup_day2_email_delay(user))
|
||||||
|
|
||||||
def convert_html_to_markdown(html: str) -> str:
|
def convert_html_to_markdown(html: str) -> str:
|
||||||
|
|||||||
@@ -33,17 +33,17 @@ class FromAddress:
|
|||||||
return parseaddr(settings.TOKENIZED_NOREPLY_EMAIL_ADDRESS)[1].format(token=generate_key())
|
return parseaddr(settings.TOKENIZED_NOREPLY_EMAIL_ADDRESS)[1].format(token=generate_key())
|
||||||
return FromAddress.NOREPLY
|
return FromAddress.NOREPLY
|
||||||
|
|
||||||
def build_email(template_prefix: str, to_user_id: Optional[int]=None,
|
def build_email(template_prefix: str, to_user_ids: Optional[List[int]]=None,
|
||||||
to_email: Optional[str]=None, from_name: Optional[str]=None,
|
to_emails: Optional[List[str]]=None, from_name: Optional[str]=None,
|
||||||
from_address: Optional[str]=None, reply_to_email: Optional[str]=None,
|
from_address: Optional[str]=None, reply_to_email: Optional[str]=None,
|
||||||
context: Optional[Dict[str, Any]]=None) -> EmailMultiAlternatives:
|
context: Optional[Dict[str, Any]]=None) -> EmailMultiAlternatives:
|
||||||
# Callers should pass exactly one of to_user_id and to_email.
|
# Callers should pass exactly one of to_user_id and to_email.
|
||||||
assert (to_user_id is None) ^ (to_email is None)
|
assert (to_user_ids is None) ^ (to_emails is None)
|
||||||
if to_user_id is not None:
|
if to_user_ids is not None:
|
||||||
to_user = get_user_profile_by_id(to_user_id)
|
to_users = [get_user_profile_by_id(to_user_id) for to_user_id in to_user_ids]
|
||||||
# Change to formataddr((to_user.full_name, to_user.email)) once
|
# Change to formataddr((to_user.full_name, to_user.email)) once
|
||||||
# https://github.com/zulip/zulip/issues/4676 is resolved
|
# https://github.com/zulip/zulip/issues/4676 is resolved
|
||||||
to_email = to_user.delivery_email
|
to_emails = [to_user.delivery_email for to_user in to_users]
|
||||||
|
|
||||||
if context is None:
|
if context is None:
|
||||||
context = {}
|
context = {}
|
||||||
@@ -81,7 +81,7 @@ def build_email(template_prefix: str, to_user_id: Optional[int]=None,
|
|||||||
elif from_address == FromAddress.NOREPLY:
|
elif from_address == FromAddress.NOREPLY:
|
||||||
reply_to = [FromAddress.NOREPLY]
|
reply_to = [FromAddress.NOREPLY]
|
||||||
|
|
||||||
mail = EmailMultiAlternatives(subject, message, from_email, [to_email], reply_to=reply_to)
|
mail = EmailMultiAlternatives(subject, message, from_email, to_emails, reply_to=reply_to)
|
||||||
if html_message is not None:
|
if html_message is not None:
|
||||||
mail.attach_alternative(html_message, 'text/html')
|
mail.attach_alternative(html_message, 'text/html')
|
||||||
return mail
|
return mail
|
||||||
@@ -91,10 +91,11 @@ class EmailNotDeliveredException(Exception):
|
|||||||
|
|
||||||
# When changing the arguments to this function, you may need to write a
|
# When changing the arguments to this function, you may need to write a
|
||||||
# migration to change or remove any emails in ScheduledEmail.
|
# migration to change or remove any emails in ScheduledEmail.
|
||||||
def send_email(template_prefix: str, to_user_id: Optional[int]=None, to_email: Optional[str]=None,
|
def send_email(template_prefix: str, to_user_ids: Optional[List[int]]=None,
|
||||||
from_name: Optional[str]=None, from_address: Optional[str]=None,
|
to_emails: Optional[List[str]]=None, from_name: Optional[str]=None,
|
||||||
reply_to_email: Optional[str]=None, context: Dict[str, Any]={}) -> None:
|
from_address: Optional[str]=None, reply_to_email: Optional[str]=None,
|
||||||
mail = build_email(template_prefix, to_user_id=to_user_id, to_email=to_email, from_name=from_name,
|
context: Dict[str, Any]={}) -> None:
|
||||||
|
mail = build_email(template_prefix, to_user_ids=to_user_ids, to_emails=to_emails, from_name=from_name,
|
||||||
from_address=from_address, reply_to_email=reply_to_email, context=context)
|
from_address=from_address, reply_to_email=reply_to_email, context=context)
|
||||||
template = template_prefix.split("/")[-1]
|
template = template_prefix.split("/")[-1]
|
||||||
logger.info("Sending %s email to %s" % (template, mail.to))
|
logger.info("Sending %s email to %s" % (template, mail.to))
|
||||||
@@ -106,27 +107,39 @@ def send_email(template_prefix: str, to_user_id: Optional[int]=None, to_email: O
|
|||||||
def send_email_from_dict(email_dict: Mapping[str, Any]) -> None:
|
def send_email_from_dict(email_dict: Mapping[str, Any]) -> None:
|
||||||
send_email(**dict(email_dict))
|
send_email(**dict(email_dict))
|
||||||
|
|
||||||
def send_future_email(template_prefix: str, realm: Realm, to_user_id: Optional[int]=None,
|
def send_future_email(template_prefix: str, realm: Realm, to_user_ids: Optional[List[int]]=None,
|
||||||
to_email: Optional[str]=None, from_name: Optional[str]=None,
|
to_emails: Optional[List[str]]=None, from_name: Optional[str]=None,
|
||||||
from_address: Optional[str]=None, context: Dict[str, Any]={},
|
from_address: Optional[str]=None, context: Dict[str, Any]={},
|
||||||
delay: datetime.timedelta=datetime.timedelta(0)) -> None:
|
delay: datetime.timedelta=datetime.timedelta(0)) -> None:
|
||||||
|
# WARNING: Be careful when using this with multiple recipients;
|
||||||
|
# because the current ScheduledEmail model (used primarily for
|
||||||
|
# cancelling planned emails) does not support multiple recipients,
|
||||||
|
# this is only valid to use for such emails where we don't expect
|
||||||
|
# the cancellation feature to be relevant.
|
||||||
|
#
|
||||||
|
# For that reason, we currently assert that the list of
|
||||||
|
# to_user_ids/to_emails is 1 below, but in theory that could be
|
||||||
|
# changed as long as the callers are in a situation where the
|
||||||
|
# above problem is not relevant.
|
||||||
template_name = template_prefix.split('/')[-1]
|
template_name = template_prefix.split('/')[-1]
|
||||||
email_fields = {'template_prefix': template_prefix, 'to_user_id': to_user_id, 'to_email': to_email,
|
email_fields = {'template_prefix': template_prefix, 'to_user_ids': to_user_ids, 'to_emails': to_emails,
|
||||||
'from_name': from_name, 'from_address': from_address, 'context': context}
|
'from_name': from_name, 'from_address': from_address, 'context': context}
|
||||||
|
|
||||||
if settings.DEVELOPMENT and not settings.TEST_SUITE:
|
if settings.DEVELOPMENT and not settings.TEST_SUITE:
|
||||||
send_email(template_prefix, to_user_id=to_user_id, to_email=to_email, from_name=from_name,
|
send_email(template_prefix, to_user_ids=to_user_ids, to_emails=to_emails, from_name=from_name,
|
||||||
from_address=from_address, context=context)
|
from_address=from_address, context=context)
|
||||||
# For logging the email
|
# For logging the email
|
||||||
|
|
||||||
assert (to_user_id is None) ^ (to_email is None)
|
assert (to_user_ids is None) ^ (to_emails is None)
|
||||||
if to_user_id is not None:
|
if to_user_ids is not None:
|
||||||
# The realm is redundant if we have a to_user_id; this assert just
|
# The realm is redundant if we have a to_user_id; this assert just
|
||||||
# expresses that fact
|
# expresses that fact
|
||||||
assert(UserProfile.objects.filter(id=to_user_id, realm=realm).exists())
|
assert(len(to_user_ids) == 1)
|
||||||
to_field = {'user_id': to_user_id} # type: Dict[str, Any]
|
assert(UserProfile.objects.filter(id__in=to_user_ids, realm=realm).exists())
|
||||||
|
to_field = {'user_id': to_user_ids[0]} # type: Dict[str, Any]
|
||||||
else:
|
else:
|
||||||
to_field = {'address': parseaddr(to_email)[1]}
|
assert(len(to_emails) == 1)
|
||||||
|
to_field = {'address': parseaddr(to_emails[0])[1]}
|
||||||
|
|
||||||
ScheduledEmail.objects.create(
|
ScheduledEmail.objects.create(
|
||||||
type=EMAIL_TYPES[template_name],
|
type=EMAIL_TYPES[template_name],
|
||||||
@@ -134,3 +147,10 @@ def send_future_email(template_prefix: str, realm: Realm, to_user_id: Optional[i
|
|||||||
realm=realm,
|
realm=realm,
|
||||||
data=ujson.dumps(email_fields),
|
data=ujson.dumps(email_fields),
|
||||||
**to_field)
|
**to_field)
|
||||||
|
|
||||||
|
def send_email_to_admins(template_prefix: str, realm: Realm, from_name: Optional[str]=None,
|
||||||
|
from_address: Optional[str]=None, context: Dict[str, Any]={}) -> None:
|
||||||
|
admins = realm.get_admin_users()
|
||||||
|
admin_user_ids = [admin.id for admin in admins]
|
||||||
|
send_email(template_prefix, to_user_ids=admin_user_ids, from_name=from_name,
|
||||||
|
from_address=from_address, context=context)
|
||||||
|
|||||||
@@ -48,6 +48,14 @@ Usage: ./manage.py deliver_email
|
|||||||
scheduled_timestamp__lte=timezone_now())
|
scheduled_timestamp__lte=timezone_now())
|
||||||
if email_jobs_to_deliver:
|
if email_jobs_to_deliver:
|
||||||
for job in email_jobs_to_deliver:
|
for job in email_jobs_to_deliver:
|
||||||
|
# Reformat any jobs that used the old to_email
|
||||||
|
# and to_user_ids argument formats.
|
||||||
|
if 'to_email' in job:
|
||||||
|
job['to_emails'] = [job['to_email']]
|
||||||
|
del job['to_email']
|
||||||
|
if 'to_user_ids' in job:
|
||||||
|
job['to_user_ids'] = [job['to_user_id']]
|
||||||
|
del job['to_user_ids']
|
||||||
try:
|
try:
|
||||||
send_email(**loads(job.data))
|
send_email(**loads(job.data))
|
||||||
job.delete()
|
job.delete()
|
||||||
|
|||||||
@@ -47,6 +47,6 @@ class Command(ZulipBaseCommand):
|
|||||||
'realm_uri': user_profile.realm.uri,
|
'realm_uri': user_profile.realm.uri,
|
||||||
'active_account_in_realm': True,
|
'active_account_in_realm': True,
|
||||||
}
|
}
|
||||||
send_email('zerver/emails/password_reset', to_user_id=user_profile.id,
|
send_email('zerver/emails/password_reset', to_user_ids=[user_profile.id],
|
||||||
from_address=FromAddress.tokenized_no_reply_address(),
|
from_address=FromAddress.tokenized_no_reply_address(),
|
||||||
from_name="Zulip Account Security", context=context)
|
from_name="Zulip Account Security", context=context)
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ def email_on_new_login(sender: Any, user: UserProfile, request: Any, **kwargs: A
|
|||||||
|
|
||||||
email_dict = {
|
email_dict = {
|
||||||
'template_prefix': 'zerver/emails/notify_new_login',
|
'template_prefix': 'zerver/emails/notify_new_login',
|
||||||
'to_user_id': user.id,
|
'to_user_ids': [user.id],
|
||||||
'from_name': 'Zulip Account Security',
|
'from_name': 'Zulip Account Security',
|
||||||
'from_address': FromAddress.NOREPLY,
|
'from_address': FromAddress.NOREPLY,
|
||||||
'context': context}
|
'context': context}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class TestDigestEmailMessages(ZulipTestCase):
|
|||||||
self.assertEqual(mock_send_future_email.call_count, 1)
|
self.assertEqual(mock_send_future_email.call_count, 1)
|
||||||
|
|
||||||
kwargs = mock_send_future_email.call_args[1]
|
kwargs = mock_send_future_email.call_args[1]
|
||||||
self.assertEqual(kwargs['to_user_id'], user_profile.id)
|
self.assertEqual(kwargs['to_user_ids'], [user_profile.id])
|
||||||
html = kwargs['context']['unread_pms'][0]['header']['html']
|
html = kwargs['context']['unread_pms'][0]['header']['html']
|
||||||
expected_url = "'http://zulip.testserver/#narrow/pm-with/{id}-hamlet'".format(id=hamlet.id)
|
expected_url = "'http://zulip.testserver/#narrow/pm-with/{id}-hamlet'".format(id=hamlet.id)
|
||||||
self.assertIn(expected_url, html)
|
self.assertIn(expected_url, html)
|
||||||
@@ -74,7 +74,7 @@ class TestDigestEmailMessages(ZulipTestCase):
|
|||||||
self.assertEqual(mock_send_future_email.call_count, 1)
|
self.assertEqual(mock_send_future_email.call_count, 1)
|
||||||
|
|
||||||
kwargs = mock_send_future_email.call_args[1]
|
kwargs = mock_send_future_email.call_args[1]
|
||||||
self.assertEqual(kwargs['to_user_id'], user_profile.id)
|
self.assertEqual(kwargs['to_user_ids'], [user_profile.id])
|
||||||
html = kwargs['context']['unread_pms'][0]['header']['html']
|
html = kwargs['context']['unread_pms'][0]['header']['html']
|
||||||
|
|
||||||
other_user_ids = sorted([
|
other_user_ids = sorted([
|
||||||
@@ -125,7 +125,7 @@ class TestDigestEmailMessages(ZulipTestCase):
|
|||||||
|
|
||||||
self.assertEqual(mock_send_future_email.call_count, 1)
|
self.assertEqual(mock_send_future_email.call_count, 1)
|
||||||
kwargs = mock_send_future_email.call_args[1]
|
kwargs = mock_send_future_email.call_args[1]
|
||||||
self.assertEqual(kwargs['to_user_id'], othello.id)
|
self.assertEqual(kwargs['to_user_ids'], [othello.id])
|
||||||
|
|
||||||
hot_convo = kwargs['context']['hot_conversations'][0]
|
hot_convo = kwargs['context']['hot_conversations'][0]
|
||||||
|
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ class WorkerTest(ZulipTestCase):
|
|||||||
|
|
||||||
data = {
|
data = {
|
||||||
'template_prefix': 'zerver/emails/confirm_new_email',
|
'template_prefix': 'zerver/emails/confirm_new_email',
|
||||||
'to_email': self.example_email("hamlet"),
|
'to_emails': [self.example_email("hamlet")],
|
||||||
'from_name': 'Zulip Account Security',
|
'from_name': 'Zulip Account Security',
|
||||||
'from_address': FromAddress.NOREPLY,
|
'from_address': FromAddress.NOREPLY,
|
||||||
'context': {}
|
'context': {}
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ class RealmTest(ZulipTestCase):
|
|||||||
def test_do_deactivate_realm_clears_scheduled_jobs(self) -> None:
|
def test_do_deactivate_realm_clears_scheduled_jobs(self) -> None:
|
||||||
user = self.example_user('hamlet')
|
user = self.example_user('hamlet')
|
||||||
send_future_email('zerver/emails/followup_day1', user.realm,
|
send_future_email('zerver/emails/followup_day1', user.realm,
|
||||||
to_user_id=user.id, delay=datetime.timedelta(hours=1))
|
to_user_ids=[user.id], delay=datetime.timedelta(hours=1))
|
||||||
self.assertEqual(ScheduledEmail.objects.count(), 1)
|
self.assertEqual(ScheduledEmail.objects.count(), 1)
|
||||||
do_deactivate_realm(user.realm)
|
do_deactivate_realm(user.realm)
|
||||||
self.assertEqual(ScheduledEmail.objects.count(), 0)
|
self.assertEqual(ScheduledEmail.objects.count(), 0)
|
||||||
|
|||||||
@@ -1159,8 +1159,9 @@ so we didn't send them an invitation. We did send invitations to everyone else!"
|
|||||||
'referrer_realm_name': referrer.realm.name,
|
'referrer_realm_name': referrer.realm.name,
|
||||||
})
|
})
|
||||||
with self.settings(EMAIL_BACKEND='django.core.mail.backends.console.EmailBackend'):
|
with self.settings(EMAIL_BACKEND='django.core.mail.backends.console.EmailBackend'):
|
||||||
|
email = data["email"]
|
||||||
send_future_email(
|
send_future_email(
|
||||||
"zerver/emails/invitation_reminder", referrer.realm, to_email=data["email"],
|
"zerver/emails/invitation_reminder", referrer.realm, to_emails=[email],
|
||||||
from_address=FromAddress.NOREPLY, context=context)
|
from_address=FromAddress.NOREPLY, context=context)
|
||||||
email_jobs_to_deliver = ScheduledEmail.objects.filter(
|
email_jobs_to_deliver = ScheduledEmail.objects.filter(
|
||||||
scheduled_timestamp__lte=timezone_now())
|
scheduled_timestamp__lte=timezone_now())
|
||||||
@@ -1553,7 +1554,7 @@ class EmailUnsubscribeTests(ZulipTestCase):
|
|||||||
context = {'name': '', 'realm_uri': '', 'unread_pms': [], 'hot_conversations': [],
|
context = {'name': '', 'realm_uri': '', 'unread_pms': [], 'hot_conversations': [],
|
||||||
'new_users': [], 'new_streams': {'plain': []}, 'unsubscribe_link': ''}
|
'new_users': [], 'new_streams': {'plain': []}, 'unsubscribe_link': ''}
|
||||||
send_future_email('zerver/emails/digest', user_profile.realm,
|
send_future_email('zerver/emails/digest', user_profile.realm,
|
||||||
to_user_id=user_profile.id, context=context)
|
to_user_ids=[user_profile.id], context=context)
|
||||||
|
|
||||||
self.assertEqual(1, ScheduledEmail.objects.filter(user=user_profile).count())
|
self.assertEqual(1, ScheduledEmail.objects.filter(user=user_profile).count())
|
||||||
|
|
||||||
|
|||||||
@@ -810,7 +810,7 @@ class ActivateTest(ZulipTestCase):
|
|||||||
def test_clear_scheduled_jobs(self) -> None:
|
def test_clear_scheduled_jobs(self) -> None:
|
||||||
user = self.example_user('hamlet')
|
user = self.example_user('hamlet')
|
||||||
send_future_email('zerver/emails/followup_day1', user.realm,
|
send_future_email('zerver/emails/followup_day1', user.realm,
|
||||||
to_user_id=user.id, delay=datetime.timedelta(hours=1))
|
to_user_ids=[user.id], delay=datetime.timedelta(hours=1))
|
||||||
self.assertEqual(ScheduledEmail.objects.count(), 1)
|
self.assertEqual(ScheduledEmail.objects.count(), 1)
|
||||||
do_deactivate_user(user)
|
do_deactivate_user(user)
|
||||||
self.assertEqual(ScheduledEmail.objects.count(), 0)
|
self.assertEqual(ScheduledEmail.objects.count(), 0)
|
||||||
|
|||||||
@@ -360,7 +360,7 @@ def prepare_activation_url(email: str, request: HttpRequest,
|
|||||||
return activation_url
|
return activation_url
|
||||||
|
|
||||||
def send_confirm_registration_email(email: str, activation_url: str) -> None:
|
def send_confirm_registration_email(email: str, activation_url: str) -> None:
|
||||||
send_email('zerver/emails/confirm_registration', to_email=email,
|
send_email('zerver/emails/confirm_registration', to_emails=[email],
|
||||||
from_address=FromAddress.tokenized_no_reply_address(),
|
from_address=FromAddress.tokenized_no_reply_address(),
|
||||||
context={'activate_url': activation_url})
|
context={'activate_url': activation_url})
|
||||||
|
|
||||||
@@ -490,7 +490,7 @@ def find_account(request: HttpRequest) -> HttpResponse:
|
|||||||
'realm_uri': user_profile.realm.uri,
|
'realm_uri': user_profile.realm.uri,
|
||||||
'realm_name': user_profile.realm.name,
|
'realm_name': user_profile.realm.name,
|
||||||
}
|
}
|
||||||
send_email('zerver/emails/find_team', to_user_id=user_profile.id, context=ctx)
|
send_email('zerver/emails/find_team', to_user_ids=[user_profile.id], context=ctx)
|
||||||
|
|
||||||
# Note: Show all the emails in the result otherwise this
|
# Note: Show all the emails in the result otherwise this
|
||||||
# feature can be used to ascertain which email addresses
|
# feature can be used to ascertain which email addresses
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ def confirm_email_change(request: HttpRequest, confirmation_key: str) -> HttpRes
|
|||||||
do_change_user_email(user_profile, new_email)
|
do_change_user_email(user_profile, new_email)
|
||||||
|
|
||||||
context = {'realm_name': user_profile.realm.name, 'new_email': new_email}
|
context = {'realm_name': user_profile.realm.name, 'new_email': new_email}
|
||||||
send_email('zerver/emails/notify_change_in_email', to_email=old_email,
|
send_email('zerver/emails/notify_change_in_email', to_emails=[old_email],
|
||||||
from_name="Zulip Account Security", from_address=FromAddress.SUPPORT,
|
from_name="Zulip Account Security", from_address=FromAddress.SUPPORT,
|
||||||
context=context)
|
context=context)
|
||||||
|
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ class ConfirmationEmailWorker(QueueProcessingWorker):
|
|||||||
send_future_email(
|
send_future_email(
|
||||||
"zerver/emails/invitation_reminder",
|
"zerver/emails/invitation_reminder",
|
||||||
referrer.realm,
|
referrer.realm,
|
||||||
to_email=invitee.email,
|
to_emails=[invitee.email],
|
||||||
from_address=FromAddress.tokenized_no_reply_address(),
|
from_address=FromAddress.tokenized_no_reply_address(),
|
||||||
context=context,
|
context=context,
|
||||||
delay=datetime.timedelta(days=2))
|
delay=datetime.timedelta(days=2))
|
||||||
|
|||||||
Reference in New Issue
Block a user