From d8cf12eaaa59d45b3e98061532217b4236947b5d Mon Sep 17 00:00:00 2001 From: Prakhar Pratyush Date: Sat, 24 Dec 2022 22:01:48 +0530 Subject: [PATCH] send_email: Improve configurability for outgoing email sender name. Currently, the sender names for outgoing emails sent by Zulip are hardcoded. It should be configurable for self-hosted systems. This commit makes the 'Zulip' part a variable in the following email sender names: 'Zulip Account Security', 'Zulip Digest', and 'Zulip Notifications' by introducing a settings variable 'SERVICE_NAME' with the default value as f"{EXTERNAL_HOST} Zulip". Fixes: #23857 --- zerver/lib/digest.py | 3 ++- zerver/lib/email_notifications.py | 4 +++- zerver/lib/send_email.py | 4 +++- zerver/tests/test_email_change.py | 4 ++-- zerver/tests/test_management_commands.py | 2 +- zerver/tests/test_message_notification_emails.py | 4 +++- zerver/tests/test_realm.py | 2 +- zerver/tests/test_signup.py | 3 +-- zproject/default_settings.py | 2 ++ zproject/prod_settings_template.py | 7 +++++++ 10 files changed, 25 insertions(+), 10 deletions(-) diff --git a/zerver/lib/digest.py b/zerver/lib/digest.py index af23e200f3..4197999fc6 100644 --- a/zerver/lib/digest.py +++ b/zerver/lib/digest.py @@ -9,6 +9,7 @@ from django.conf import settings from django.db import transaction from django.db.models import Exists, OuterRef from django.utils.timezone import now as timezone_now +from django.utils.translation import gettext as _ from typing_extensions import TypeAlias from confirmation.models import one_click_unsubscribe_link @@ -408,7 +409,7 @@ def bulk_handle_digest_email(user_ids: List[int], cutoff: float) -> None: "zerver/emails/digest", user.realm, to_user_ids=[user.id], - from_name="Zulip Digest", + from_name=_("{service_name} digest").format(service_name=settings.INSTALLATION_NAME), from_address=FromAddress.no_reply_placeholder, context=context, ) diff --git a/zerver/lib/email_notifications.py b/zerver/lib/email_notifications.py index 5d591b0143..11e59340eb 100644 --- a/zerver/lib/email_notifications.py +++ b/zerver/lib/email_notifications.py @@ -574,7 +574,9 @@ def do_send_missedmessage_events_reply_in_zulip( ) with override_language(user_profile.default_language): - from_name: str = _("Zulip notifications") + from_name: str = _("{service_name} notifications").format( + service_name=settings.INSTALLATION_NAME + ) from_address = FromAddress.NOREPLY email_dict = { diff --git a/zerver/lib/send_email.py b/zerver/lib/send_email.py index a6487df81a..79a9e89bfd 100644 --- a/zerver/lib/send_email.py +++ b/zerver/lib/send_email.py @@ -70,7 +70,9 @@ class FromAddress: language = user_profile.default_language with override_language(language): - return _("Zulip Account Security") + return _("{service_name} account security").format( + service_name=settings.INSTALLATION_NAME + ) def build_email( diff --git a/zerver/tests/test_email_change.py b/zerver/tests/test_email_change.py index e2b4d8f38c..dd4126c650 100644 --- a/zerver/tests/test_email_change.py +++ b/zerver/tests/test_email_change.py @@ -194,7 +194,7 @@ class EmailChangeTestCase(ZulipTestCase): self.assertEqual(self.email_envelope_from(email_message), settings.NOREPLY_EMAIL_ADDRESS) self.assertRegex( self.email_display_from(email_message), - rf"^Zulip Account Security <{self.TOKENIZED_NOREPLY_REGEX}>\Z", + rf"^testserver account security <{self.TOKENIZED_NOREPLY_REGEX}>\Z", ) self.assertEqual(email_message.extra_headers["List-Id"], "Zulip Dev ") @@ -392,7 +392,7 @@ class EmailChangeTestCase(ZulipTestCase): self.assertEqual(self.email_envelope_from(email_message), settings.NOREPLY_EMAIL_ADDRESS) self.assertRegex( self.email_display_from(email_message), - rf"^Zulip Account Security <{self.TOKENIZED_NOREPLY_REGEX}>\Z", + rf"^testserver account security <{self.TOKENIZED_NOREPLY_REGEX}>\Z", ) self.assertEqual(email_message.extra_headers["List-Id"], "Zulip Dev ") diff --git a/zerver/tests/test_management_commands.py b/zerver/tests/test_management_commands.py index 8542f92b0a..4aad5a9e13 100644 --- a/zerver/tests/test_management_commands.py +++ b/zerver/tests/test_management_commands.py @@ -402,7 +402,7 @@ class TestPasswordRestEmail(ZulipTestCase): self.assertEqual(self.email_envelope_from(outbox[0]), settings.NOREPLY_EMAIL_ADDRESS) self.assertRegex( self.email_display_from(outbox[0]), - rf"^Zulip Account Security <{self.TOKENIZED_NOREPLY_REGEX}>\Z", + rf"^testserver account security <{self.TOKENIZED_NOREPLY_REGEX}>\Z", ) self.assertIn("reset your password", outbox[0].body) diff --git a/zerver/tests/test_message_notification_emails.py b/zerver/tests/test_message_notification_emails.py index dd230d23dd..ac3fda6d6a 100644 --- a/zerver/tests/test_message_notification_emails.py +++ b/zerver/tests/test_message_notification_emails.py @@ -131,7 +131,9 @@ class TestMessageNotificationEmails(ZulipTestCase): reply_to_emails = ["noreply@testserver"] msg = mail.outbox[0] assert isinstance(msg, EmailMultiAlternatives) - from_email = str(Address(display_name="Zulip notifications", addr_spec=FromAddress.NOREPLY)) + from_email = str( + Address(display_name="testserver notifications", addr_spec=FromAddress.NOREPLY) + ) self.assert_length(mail.outbox, 1) self.assertEqual(self.email_envelope_from(msg), settings.NOREPLY_EMAIL_ADDRESS) self.assertEqual(self.email_display_from(msg), from_email) diff --git a/zerver/tests/test_realm.py b/zerver/tests/test_realm.py index c8c38e71f3..bc7f5108e3 100644 --- a/zerver/tests/test_realm.py +++ b/zerver/tests/test_realm.py @@ -452,7 +452,7 @@ class RealmTest(ZulipTestCase): self.assertEqual(self.email_envelope_from(outbox[0]), settings.NOREPLY_EMAIL_ADDRESS) self.assertRegex( self.email_display_from(outbox[0]), - rf"^Zulip Account Security <{self.TOKENIZED_NOREPLY_REGEX}>\Z", + rf"^testserver account security <{self.TOKENIZED_NOREPLY_REGEX}>\Z", ) self.assertIn("Reactivate your Zulip organization", outbox[0].subject) self.assertIn("Dear former administrators", outbox[0].body) diff --git a/zerver/tests/test_signup.py b/zerver/tests/test_signup.py index 857017b23a..99722d8d12 100644 --- a/zerver/tests/test_signup.py +++ b/zerver/tests/test_signup.py @@ -16,7 +16,6 @@ from django.http import HttpResponse, HttpResponseBase from django.template.response import TemplateResponse from django.test import Client, override_settings from django.utils import translation -from django.utils.translation import gettext as _ from confirmation import settings as confirmation_settings from confirmation.models import Confirmation, one_click_unsubscribe_link @@ -360,7 +359,7 @@ class PasswordResetTest(ZulipTestCase): # The email might be sent in different languages for i18n testing self.assertRegex( self.email_display_from(message), - rf'^{_("Zulip Account Security")} <{self.TOKENIZED_NOREPLY_REGEX}>\Z', + rf"^testserver account security <{self.TOKENIZED_NOREPLY_REGEX}>\Z", ) self.assertIn(f"{subdomain}.testserver", message.extra_headers["List-Id"]) diff --git a/zproject/default_settings.py b/zproject/default_settings.py index 3f4160b0fb..a56a440a7f 100644 --- a/zproject/default_settings.py +++ b/zproject/default_settings.py @@ -485,6 +485,8 @@ WELCOME_EMAIL_SENDER: Optional[Dict[str, str]] = None # Whether to send periodic digests of activity. SEND_DIGEST_EMAILS = True +# The variable part of email sender names to be used for outgoing emails. +INSTALLATION_NAME = EXTERNAL_HOST # Used to change the Zulip logo in portico pages. CUSTOM_LOGO_URL: Optional[str] = None diff --git a/zproject/prod_settings_template.py b/zproject/prod_settings_template.py index 412f5f26aa..63969f5efc 100644 --- a/zproject/prod_settings_template.py +++ b/zproject/prod_settings_template.py @@ -101,6 +101,13 @@ EXTERNAL_HOST = "zulip.example.com" ## confirmation emails when ADD_TOKENS_TO_NOREPLY_ADDRESS=False. # NOREPLY_EMAIL_ADDRESS = "noreply@example.com" +## Emails sent by the Zulip server will use a sender name starting +## with INSTALLATION_NAME. The default is EXTERNAL_HOST. If INSTALLATION_NAME is +## "zulip.example.com", email senders names will include: +## * "zulip.example.com notifications" (message notification emails). +## * "zulip.example.com account security" (account security emails). +# INSTALLATION_NAME = "My Zulip Server" + ## Many countries and bulk mailers require certain types of email to display ## a physical mailing address to comply with anti-spam legislation. ## Non-commercial and non-public-facing installations are unlikely to need