mirror of
https://github.com/zulip/zulip.git
synced 2025-10-23 04:52:12 +00:00
Commit 9afde790c6
introduced a bug
concerning outgoing emails inside the development environment. These
emails are not supposed to use a real connection with a mail
server as the send_messages function is overwritten inside the
EmailLogBackEnd class.
The bug was happening inside the initialize_connection function that
was introduced in the above-mentioned commit. This function is used
to refresh the connection with an SMTP server that would have closed
it. As the socket used to communicate with the server is not
initialized inside the development environment this function was
wrongly trying to send no-op commands.
The fix just checks that the connection argument of the function is
an EmailLogBackEnd object before trying the no-op command.
Additionally as it is sometimes useful to be able to send outgoing
emails inside the development environment the get_forward_address
function is used to check if a real connection exists between Zulip
and the server. If it is the case, as EmailLogBackEnd is a subclass
of smtp.EmailBackend, the connection will be nicely refreshed.
This commit was tested manually by checking that the console prints
correctly that an email is sent to the user when it signs in inside
the development environment. It was also tested when a mail provider
is specified and the mails were correctly received.
93 lines
3.6 KiB
Python
93 lines
3.6 KiB
Python
# https://zulip.readthedocs.io/en/latest/subsystems/email.html#testing-in-a-real-email-client
|
|
import configparser
|
|
import logging
|
|
from typing import List
|
|
|
|
from django.conf import settings
|
|
from django.core.mail import EmailMultiAlternatives
|
|
from django.core.mail.backends.smtp import EmailBackend
|
|
from django.template import loader
|
|
|
|
|
|
def get_forward_address() -> str:
|
|
config = configparser.ConfigParser()
|
|
config.read(settings.FORWARD_ADDRESS_CONFIG_FILE)
|
|
try:
|
|
return config.get("DEV_EMAIL", "forward_address")
|
|
except (configparser.NoSectionError, configparser.NoOptionError):
|
|
return ""
|
|
|
|
|
|
def set_forward_address(forward_address: str) -> None:
|
|
config = configparser.ConfigParser()
|
|
config.read(settings.FORWARD_ADDRESS_CONFIG_FILE)
|
|
|
|
if not config.has_section("DEV_EMAIL"):
|
|
config.add_section("DEV_EMAIL")
|
|
config.set("DEV_EMAIL", "forward_address", forward_address)
|
|
|
|
with open(settings.FORWARD_ADDRESS_CONFIG_FILE, "w") as cfgfile:
|
|
config.write(cfgfile)
|
|
|
|
|
|
class EmailLogBackEnd(EmailBackend):
|
|
@staticmethod
|
|
def log_email(email: EmailMultiAlternatives) -> None:
|
|
"""Used in development to record sent emails in a nice HTML log"""
|
|
html_message = "Missing HTML message"
|
|
if len(email.alternatives) > 0:
|
|
html_message = email.alternatives[0][0]
|
|
|
|
context = {
|
|
"subject": email.subject,
|
|
"envelope_from": email.from_email,
|
|
"from_email": email.extra_headers.get("From", email.from_email),
|
|
"reply_to": email.reply_to,
|
|
"recipients": email.to,
|
|
"body": email.body,
|
|
"html_message": html_message,
|
|
}
|
|
|
|
new_email = loader.render_to_string("zerver/email.html", context)
|
|
|
|
# Read in the pre-existing log, so that we can add the new entry
|
|
# at the top.
|
|
try:
|
|
with open(settings.EMAIL_CONTENT_LOG_PATH) as f:
|
|
previous_emails = f.read()
|
|
except FileNotFoundError:
|
|
previous_emails = ""
|
|
|
|
with open(settings.EMAIL_CONTENT_LOG_PATH, "w+") as f:
|
|
f.write(new_email + previous_emails)
|
|
|
|
@staticmethod
|
|
def prepare_email_messages_for_forwarding(email_messages: List[EmailMultiAlternatives]) -> None:
|
|
localhost_email_images_base_uri = settings.ROOT_DOMAIN_URI + "/static/images/emails"
|
|
czo_email_images_base_uri = "https://chat.zulip.org/static/images/emails"
|
|
|
|
for email_message in email_messages:
|
|
html_alternative = list(email_message.alternatives[0])
|
|
# Here, we replace the email addresses used in development
|
|
# with chat.zulip.org, so that web email providers like Gmail
|
|
# will be able to fetch the illustrations used in the emails.
|
|
html_alternative[0] = html_alternative[0].replace(
|
|
localhost_email_images_base_uri, czo_email_images_base_uri
|
|
)
|
|
email_message.alternatives[0] = tuple(html_alternative)
|
|
|
|
email_message.to = [get_forward_address()]
|
|
|
|
def send_messages(self, email_messages: List[EmailMultiAlternatives]) -> int:
|
|
num_sent = len(email_messages)
|
|
if get_forward_address():
|
|
self.prepare_email_messages_for_forwarding(email_messages)
|
|
num_sent = super().send_messages(email_messages)
|
|
|
|
if settings.DEVELOPMENT_LOG_EMAILS:
|
|
for email in email_messages:
|
|
self.log_email(email)
|
|
email_log_url = settings.ROOT_DOMAIN_URI + "/emails"
|
|
logging.info("Emails sent in development are available at %s", email_log_url)
|
|
return num_sent
|