mirror of
https://github.com/zulip/zulip.git
synced 2025-11-03 21:43:21 +00:00
emails: Refactor send_email functions to take both a sender name and address.
This will allow for customized senders for emails, e.g. 'Zulip Digest' for digest emails and 'Zulip Missed Messages' for missed message emails. Also: * Converts the sender name to always be "Zulip", if the from_email used to be settings.NOREPLY_EMAIL_ADDRESS or settings.ZULIP_ADMINISTRATOR. * Changes the default value of settings.NOREPLY_EMAIL_ADDRESS in the prod_setting_template to no longer have a display name. The only use of that display name was in the email pathway.
This commit is contained in:
@@ -640,7 +640,7 @@ def do_start_email_change_process(user_profile, new_email):
|
||||
activation_url = EmailChangeConfirmation.objects.get_link_for_object(obj, host=user_profile.realm.host)
|
||||
context = {'realm': user_profile.realm, 'old_email': old_email, 'new_email': new_email,
|
||||
'activate_url': activation_url}
|
||||
send_email('zerver/emails/confirm_new_email', new_email, from_email=settings.ZULIP_ADMINISTRATOR,
|
||||
send_email('zerver/emails/confirm_new_email', new_email, from_address=settings.ZULIP_ADMINISTRATOR.split()[-1],
|
||||
context=context)
|
||||
|
||||
def compute_irc_user_fullname(email):
|
||||
@@ -3054,7 +3054,7 @@ def do_send_confirmation_email(invitee, referrer, body):
|
||||
"""
|
||||
activation_url = Confirmation.objects.get_link_for_object(invitee, host=referrer.realm.host)
|
||||
context = {'referrer': referrer, 'custom_body': body, 'activate_url': activation_url}
|
||||
send_email('zerver/emails/invitation', invitee.email, from_email=settings.ZULIP_ADMINISTRATOR,
|
||||
send_email('zerver/emails/invitation', invitee.email, from_address=settings.ZULIP_ADMINISTRATOR.split()[-1],
|
||||
context=context)
|
||||
|
||||
def is_inactive(email):
|
||||
|
||||
@@ -309,8 +309,7 @@ def do_send_missedmessage_events_reply_in_zulip(user_profile, missed_messages, m
|
||||
'realm_str': user_profile.realm.name,
|
||||
})
|
||||
|
||||
from_email = None
|
||||
|
||||
from_name, from_address = None, None
|
||||
if len(senders) == 1 and settings.SEND_MISSED_MESSAGE_EMAILS_AS_USER:
|
||||
# If this setting is enabled, you can reply to the Zulip
|
||||
# missed message emails directly back to the original sender.
|
||||
@@ -318,16 +317,16 @@ def do_send_missedmessage_events_reply_in_zulip(user_profile, missed_messages, m
|
||||
# record for the domain, or there will be spam/deliverability
|
||||
# problems.
|
||||
sender = senders[0]
|
||||
from_email = '"%s" <%s>' % (sender.full_name, sender.email)
|
||||
from_name, from_address = (sender.full_name, sender.email)
|
||||
context.update({
|
||||
'reply_warning': False,
|
||||
'reply_to_zulip': False,
|
||||
})
|
||||
|
||||
email_dict = {
|
||||
'template_prefix': 'zerver/emails/missed_message',
|
||||
'to_email': display_email(user_profile),
|
||||
'from_email': from_email,
|
||||
'from_name': from_name,
|
||||
'from_address': from_address,
|
||||
'reply_to_email': address,
|
||||
'context': context}
|
||||
queue_json_publish("missedmessage_email_senders", email_dict, send_email_from_dict)
|
||||
@@ -395,10 +394,11 @@ def enqueue_welcome_emails(email, name):
|
||||
from zerver.context_processors import common_context
|
||||
if settings.WELCOME_EMAIL_SENDER is not None:
|
||||
# line break to avoid triggering lint rule
|
||||
from_email = '%(name)s <%(email)s>' % \
|
||||
settings.WELCOME_EMAIL_SENDER
|
||||
from_name = settings.WELCOME_EMAIL_SENDER['name']
|
||||
from_address = settings.WELCOME_EMAIL_SENDER['email']
|
||||
else:
|
||||
from_email = settings.ZULIP_ADMINISTRATOR
|
||||
from_name = None
|
||||
from_address = settings.ZULIP_ADMINISTRATOR.split()[-1]
|
||||
|
||||
user_profile = get_user_profile_by_email(email)
|
||||
unsubscribe_link = one_click_unsubscribe_link(user_profile, "welcome")
|
||||
@@ -407,11 +407,11 @@ def enqueue_welcome_emails(email, name):
|
||||
'unsubscribe_link': unsubscribe_link
|
||||
})
|
||||
send_future_email(
|
||||
"zerver/emails/followup_day1", '%s <%s>' % (name, email),
|
||||
from_email=from_email, context=context, delay=datetime.timedelta(hours=1))
|
||||
"zerver/emails/followup_day1", '%s <%s>' % (name, email), from_name=from_name,
|
||||
from_address=from_address, context=context, delay=datetime.timedelta(hours=1))
|
||||
send_future_email(
|
||||
"zerver/emails/followup_day2", '%s <%s>' % (name, email),
|
||||
from_email=from_email, context=context, delay=datetime.timedelta(days=1))
|
||||
"zerver/emails/followup_day2", '%s <%s>' % (name, email), from_name=from_name,
|
||||
from_address=from_address, context=context, delay=datetime.timedelta(days=1))
|
||||
|
||||
def convert_html_to_markdown(html):
|
||||
# type: (Text) -> Text
|
||||
|
||||
@@ -5,7 +5,7 @@ from django.utils.timezone import now as timezone_now
|
||||
from zerver.models import UserProfile, ScheduledJob
|
||||
|
||||
import datetime
|
||||
from email.utils import parseaddr
|
||||
from email.utils import parseaddr, formataddr
|
||||
import ujson
|
||||
|
||||
from typing import Any, Dict, Iterable, List, Mapping, Optional, Text
|
||||
@@ -17,8 +17,9 @@ def display_email(user):
|
||||
return user.email
|
||||
|
||||
# Intended only for test code
|
||||
def build_email(template_prefix, to_email, from_email=None, reply_to_email=None, context={}):
|
||||
# type: (str, Text, Optional[Text], Optional[Text], Dict[str, Any]) -> EmailMultiAlternatives
|
||||
def build_email(template_prefix, to_email, from_name=None, from_address=None,
|
||||
reply_to_email=None, context={}):
|
||||
# type: (str, Text, Optional[Text], Optional[Text], Optional[Text], Dict[str, Any]) -> EmailMultiAlternatives
|
||||
context.update({
|
||||
'support_email': settings.ZULIP_ADMINISTRATOR,
|
||||
'verbose_support_offers': settings.VERBOSE_SUPPORT_OFFERS,
|
||||
@@ -28,8 +29,12 @@ def build_email(template_prefix, to_email, from_email=None, reply_to_email=None,
|
||||
message = loader.render_to_string(template_prefix + '.txt',
|
||||
context=context, using='Jinja2_plaintext')
|
||||
html_message = loader.render_to_string(template_prefix + '.html', context)
|
||||
if from_email is None:
|
||||
from_email = settings.NOREPLY_EMAIL_ADDRESS
|
||||
|
||||
if from_name is None:
|
||||
from_name = "Zulip"
|
||||
if from_address is None:
|
||||
from_address = parseaddr(settings.NOREPLY_EMAIL_ADDRESS)[1]
|
||||
from_email = formataddr((from_name, from_address))
|
||||
reply_to = None
|
||||
if reply_to_email is not None:
|
||||
reply_to = [reply_to_email]
|
||||
@@ -39,15 +44,16 @@ def build_email(template_prefix, to_email, from_email=None, reply_to_email=None,
|
||||
mail.attach_alternative(html_message, 'text/html')
|
||||
return mail
|
||||
|
||||
def send_email(template_prefix, to_email, from_email=None, reply_to_email=None, context={}):
|
||||
# type: (str, Text, Optional[Text], Optional[Text], Dict[str, Any]) -> bool
|
||||
mail = build_email(template_prefix, to_email, from_email=from_email,
|
||||
reply_to_email=reply_to_email, context=context)
|
||||
def send_email(template_prefix, to_email, from_name=None, from_address=None, reply_to_email=None, context={}):
|
||||
# type: (str, Text, Optional[Text], Optional[Text], Optional[Text], Dict[str, Any]) -> bool
|
||||
mail = build_email(template_prefix, to_email, from_name=from_name,
|
||||
from_address=from_address, reply_to_email=reply_to_email, context=context)
|
||||
return mail.send() > 0
|
||||
|
||||
def send_email_to_user(template_prefix, user, from_email=None, context={}):
|
||||
# type: (str, UserProfile, Optional[Text], Dict[str, Text]) -> bool
|
||||
return send_email(template_prefix, display_email(user), from_email=from_email, context=context)
|
||||
def send_email_to_user(template_prefix, user, from_name=None, from_address=None, context={}):
|
||||
# type: (str, UserProfile, Optional[Text], Optional[Text], Dict[str, Text]) -> bool
|
||||
return send_email(template_prefix, display_email(user), from_name=from_name,
|
||||
from_address=from_address, context=context)
|
||||
|
||||
# Returns None instead of bool so that the type signature matches the third
|
||||
# argument of zerver.lib.queue.queue_json_publish
|
||||
@@ -55,11 +61,11 @@ def send_email_from_dict(email_dict):
|
||||
# type: (Mapping[str, Any]) -> None
|
||||
send_email(**dict(email_dict))
|
||||
|
||||
def send_future_email(template_prefix, to_email, from_email=None, context={},
|
||||
def send_future_email(template_prefix, to_email, from_name=None, from_address=None, context={},
|
||||
delay=datetime.timedelta(0)):
|
||||
# type: (str, Text, Optional[Text], Dict[str, Any], datetime.timedelta) -> None
|
||||
email_fields = {'template_prefix': template_prefix, 'to_email': to_email, 'from_email': from_email,
|
||||
'context': context}
|
||||
# type: (str, Text, Optional[Text], Optional[Text], Dict[str, Any], datetime.timedelta) -> None
|
||||
email_fields = {'template_prefix': template_prefix, 'to_email': to_email, 'from_name': from_name,
|
||||
'from_address': from_address, 'context': context}
|
||||
ScheduledJob.objects.create(type=ScheduledJob.EMAIL, filter_string=parseaddr(to_email)[1],
|
||||
data=ujson.dumps(email_fields),
|
||||
scheduled_timestamp=timezone_now() + delay)
|
||||
|
||||
27
zerver/migrations/0087_remove_old_scheduled_jobs.py
Normal file
27
zerver/migrations/0087_remove_old_scheduled_jobs.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.5 on 2017-05-10 05:59
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor
|
||||
from django.db.migrations.state import StateApps
|
||||
|
||||
def delete_old_scheduled_jobs(apps, schema_editor):
|
||||
# type: (StateApps, DatabaseSchemaEditor) -> None
|
||||
"""Delete any old scheduled jobs, to handle changes in the format of
|
||||
send_email. Ideally, we'd translate the jobs, but it's not really
|
||||
worth the development effort to save a few invitation reminders
|
||||
and day2 followup emails.
|
||||
"""
|
||||
ScheduledJob = apps.get_model('zerver', 'ScheduledJob')
|
||||
ScheduledJob.objects.all().delete()
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('zerver', '0086_realm_alter_default_org_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(delete_old_scheduled_jobs),
|
||||
]
|
||||
@@ -743,7 +743,7 @@ so we didn't send them an invitation. We did send invitations to everyone else!"
|
||||
with self.settings(EMAIL_BACKEND='django.core.mail.backends.console.EmailBackend'):
|
||||
send_future_email(
|
||||
"zerver/emails/invitation_reminder", data["email"],
|
||||
from_email=settings.ZULIP_ADMINISTRATOR, context=context)
|
||||
from_address=settings.ZULIP_ADMINISTRATOR.split()[-1], context=context)
|
||||
email_jobs_to_deliver = ScheduledJob.objects.filter(
|
||||
type=ScheduledJob.EMAIL,
|
||||
scheduled_timestamp__lte=timezone_now())
|
||||
|
||||
@@ -313,7 +313,7 @@ def send_registration_completion_email(email, request, realm_creation=False):
|
||||
"""
|
||||
prereg_user = create_preregistration_user(email, request, realm_creation)
|
||||
activation_url = Confirmation.objects.get_link_for_object(prereg_user, host=request.get_host())
|
||||
send_email('zerver/emails/confirm_registration', email, from_email=settings.ZULIP_ADMINISTRATOR,
|
||||
send_email('zerver/emails/confirm_registration', email, from_address=settings.ZULIP_ADMINISTRATOR.split()[-1],
|
||||
context={'activate_url': activation_url})
|
||||
if settings.DEVELOPMENT and realm_creation:
|
||||
request.session['confirmation_key'] = {'confirmation_key': activation_url.split('/')[-1]}
|
||||
|
||||
@@ -52,7 +52,7 @@ def confirm_email_change(request, confirmation_key):
|
||||
'new_email': new_email,
|
||||
}
|
||||
send_email('zerver/emails/notify_change_in_email', old_email,
|
||||
from_email=settings.ZULIP_ADMINISTRATOR, context=context)
|
||||
from_address=settings.ZULIP_ADMINISTRATOR.split()[-1], context=context)
|
||||
|
||||
ctx = {
|
||||
'confirmed': confirmed,
|
||||
|
||||
@@ -180,7 +180,7 @@ class ConfirmationEmailWorker(QueueProcessingWorker):
|
||||
send_future_email(
|
||||
"zerver/emails/invitation_reminder",
|
||||
data["email"],
|
||||
from_email=settings.ZULIP_ADMINISTRATOR,
|
||||
from_address=settings.ZULIP_ADMINISTRATOR.split()[-1],
|
||||
context=context,
|
||||
delay=datetime.timedelta(days=2))
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ ALLOWED_HOSTS = [EXTERNAL_HOST.split(":")[0]]
|
||||
# appear on 404 pages, is used as the sender's address for many automated
|
||||
# emails, and is advertised as a support address. An email address like
|
||||
# support@example.com is totally reasonable, as is admin@example.com.
|
||||
# Do not put a display name; e.g. 'support@example.com', not
|
||||
# 'Zulip Support <support@example.com>'.
|
||||
ZULIP_ADMINISTRATOR = 'zulip-admin@example.com'
|
||||
|
||||
# Configure the outgoing SMTP server below. You will need working
|
||||
@@ -76,8 +78,9 @@ EMAIL_PORT = 587
|
||||
EMAIL_USE_TLS = True
|
||||
# The noreply address to be used as the sender for certain generated emails.
|
||||
# Messages sent to this address could contain sensitive user data and should
|
||||
# not be delivered anywhere. (e.g. "Zulip <noreply@example.com>")
|
||||
NOREPLY_EMAIL_ADDRESS = "Zulip <noreply@" + EXTERNAL_HOST.split(":")[0] + ">"
|
||||
# not be delivered anywhere. Do not put a display name;
|
||||
# e.g. 'noreply@example.com', not 'Zulip <noreply@example.com>'.
|
||||
NOREPLY_EMAIL_ADDRESS = "noreply@" + EXTERNAL_HOST.split(":")[0]
|
||||
|
||||
|
||||
## OPTIONAL SETTINGS
|
||||
|
||||
Reference in New Issue
Block a user