diff --git a/app/email_utils.py b/app/email_utils.py index e40a5e14..916aecda 100644 --- a/app/email_utils.py +++ b/app/email_utils.py @@ -106,15 +106,15 @@ def render(template_name, **kwargs) -> str: def send_welcome_email(user): - to_email, unsubscribe_link, via_email = user.get_communication_email() - if not to_email: + comm_alias, unsubscribe_link, via_email = user.get_communication_email() + if not comm_alias: return # whether this email is sent to an alias - alias = to_email if to_email != user.email else None + alias = comm_alias.email if comm_alias.email != user.email else None send_email( - to_email, + comm_alias.email, f"Welcome to SimpleLogin", render("com/welcome.txt", user=user, alias=alias), render("com/welcome.html", user=user, alias=alias), diff --git a/app/models.py b/app/models.py index bf475e3f..059ceef4 100644 --- a/app/models.py +++ b/app/models.py @@ -928,7 +928,7 @@ class User(Base, ModelMixin, UserMixin, PasswordOracle): def two_factor_authentication_enabled(self) -> bool: return self.enable_otp or self.fido_enabled() - def get_communication_email(self) -> (Optional[str], str, bool): + def get_communication_email(self) -> (Optional[Alias], str, bool): """ Return - the email that user uses to receive email communication. None if user unsubscribes from newsletter @@ -942,10 +942,10 @@ class User(Base, ModelMixin, UserMixin, PasswordOracle): unsub = UnsubscribeEncoder.encode( UnsubscribeAction.DisableAlias, alias.id ) - return alias.email, unsub.link, unsub.via_email + return alias, unsub.link, unsub.via_email # alias disabled -> user doesn't want to receive newsletter else: - return None, None, False + return None, "", False else: # do not handle http POST unsubscribe if config.UNSUBSCRIBER: @@ -958,7 +958,7 @@ class User(Base, ModelMixin, UserMixin, PasswordOracle): True, ) - return None, None, False + return None, "", False def available_sl_domains(self) -> [str]: """ diff --git a/app/newsletter_utils.py b/app/newsletter_utils.py index 34887bf7..79e1057f 100644 --- a/app/newsletter_utils.py +++ b/app/newsletter_utils.py @@ -4,6 +4,7 @@ from jinja2 import Environment, FileSystemLoader from app.config import ROOT_DIR, URL from app.email_utils import send_email +from app.handler.unsubscribe_encoder import UnsubscribeEncoder, UnsubscribeAction from app.log import LOG from app.models import NewsletterUser @@ -16,12 +17,18 @@ def send_newsletter_to_user(newsletter, user) -> (bool, str): html_template = env.from_string(newsletter.html) text_template = env.from_string(newsletter.plain_text) - to_email, unsubscribe_link, via_email = user.get_communication_email() - if not to_email: + comm_alias, unsubscribe_link, via_email = user.get_communication_email() + if not comm_alias: return False, f"{user} not subscribed to newsletter" + unsubscribe_oneclick = unsubscribe_link + if via_email: + unsubscribe_oneclick = UnsubscribeEncoder.encode( + UnsubscribeAction.DisableAlias, comm_alias.id + ) + send_email( - to_email, + comm_alias.email, newsletter.subject, text_template.render( user=user, @@ -30,9 +37,10 @@ def send_newsletter_to_user(newsletter, user) -> (bool, str): html_template.render( user=user, URL=URL, - unsubscribe_link=unsubscribe_link, + unsubscribe_oneclick=unsubscribe_oneclick, ), unsubscribe_link=unsubscribe_link, + unsubscribe_via_email=via_email, ) NewsletterUser.create(newsletter_id=newsletter.id, user_id=user.id, commit=True) diff --git a/job_runner.py b/job_runner.py index 34c89706..81aa6203 100644 --- a/job_runner.py +++ b/job_runner.py @@ -22,15 +22,21 @@ from server import create_light_app def onboarding_send_from_alias(user): - to_email, unsubscribe_link, via_email = user.get_communication_email() - if not to_email: + comm_alias, unsubscribe_link, via_email = user.get_communication_email() + if not comm_alias.email: return send_email( - to_email, + comm_alias.email, "SimpleLogin Tip: Send emails from your alias", - render("com/onboarding/send-from-alias.txt.j2", user=user, to_email=to_email), - render("com/onboarding/send-from-alias.html", user=user, to_email=to_email), + render( + "com/onboarding/send-from-alias.txt.j2", + user=user, + to_email=comm_alias.email, + ), + render( + "com/onboarding/send-from-alias.html", user=user, to_email=comm_alias.email + ), unsubscribe_link, via_email, retries=3, @@ -39,15 +45,15 @@ def onboarding_send_from_alias(user): def onboarding_pgp(user): - to_email, unsubscribe_link, via_email = user.get_communication_email() - if not to_email: + comm_alias, unsubscribe_link, via_email = user.get_communication_email() + if not comm_alias: return send_email( - to_email, + comm_alias.email, "SimpleLogin Tip: Secure your emails with PGP", - render("com/onboarding/pgp.txt", user=user, to_email=to_email), - render("com/onboarding/pgp.html", user=user, to_email=to_email), + render("com/onboarding/pgp.txt", user=user, to_email=comm_alias.email), + render("com/onboarding/pgp.html", user=user, to_email=comm_alias.email), unsubscribe_link, via_email, retries=3, @@ -56,15 +62,21 @@ def onboarding_pgp(user): def onboarding_browser_extension(user): - to_email, unsubscribe_link, via_email = user.get_communication_email() - if not to_email: + comm_alias, unsubscribe_link, via_email = user.get_communication_email() + if not comm_alias: return send_email( - to_email, + comm_alias.email, "SimpleLogin Tip: Chrome/Firefox/Safari extensions and Android/iOS apps", - render("com/onboarding/browser-extension.txt", user=user, to_email=to_email), - render("com/onboarding/browser-extension.html", user=user, to_email=to_email), + render( + "com/onboarding/browser-extension.txt", user=user, to_email=comm_alias.email + ), + render( + "com/onboarding/browser-extension.html", + user=user, + to_email=comm_alias.email, + ), unsubscribe_link, via_email, retries=3, @@ -73,15 +85,15 @@ def onboarding_browser_extension(user): def onboarding_mailbox(user): - to_email, unsubscribe_link, via_email = user.get_communication_email() - if not to_email: + comm_alias, unsubscribe_link, via_email = user.get_communication_email() + if not comm_alias: return send_email( - to_email, + comm_alias.email, "SimpleLogin Tip: Multiple mailboxes", - render("com/onboarding/mailbox.txt", user=user, to_email=to_email), - render("com/onboarding/mailbox.html", user=user, to_email=to_email), + render("com/onboarding/mailbox.txt", user=user, to_email=comm_alias.email), + render("com/onboarding/mailbox.html", user=user, to_email=comm_alias.email), unsubscribe_link, via_email, retries=3, @@ -90,19 +102,23 @@ def onboarding_mailbox(user): def welcome_proton(user): - to_email, _, _ = user.get_communication_email() - if not to_email: + comm_alias, _, _ = user.get_communication_email() + if not comm_alias: return send_email( - to_email, + comm_alias.email, "Welcome to SimpleLogin, an email masking service provided by Proton", render( "com/onboarding/welcome-proton-user.txt.jinja2", user=user, - to_email=to_email, + to_email=comm_alias.email, + ), + render( + "com/onboarding/welcome-proton-user.html", + user=user, + to_email=comm_alias.email, ), - render("com/onboarding/welcome-proton-user.html", user=user, to_email=to_email), retries=3, ignore_smtp_error=True, ) diff --git a/server.py b/server.py index 8c16975b..afa8fce1 100644 --- a/server.py +++ b/server.py @@ -841,8 +841,8 @@ def register_custom_commands(app): LOG.i(f"User {user_id} was maybe deleted in the meantime") continue - to_email, unsubscribe_link, via_email = user.get_communication_email() - if not to_email: + comm_alias, unsubscribe_link, via_email = user.get_communication_email() + if not comm_alias.email: continue sent, error_msg = send_newsletter_to_user(newsletter, user) diff --git a/templates/emails/base.html b/templates/emails/base.html index d1b242a5..5ae5af94 100644 --- a/templates/emails/base.html +++ b/templates/emails/base.html @@ -591,6 +591,16 @@
{% block footer %}{% endblock %}

+ {% if unsubscribe_oneclick is defined %} + +

+ Unsubscribe from our newsletter +

+ {% endif %}

- Unsubscribe from our newsletter -

-{% endblock %} diff --git a/tests/jobs/test_send_proton_welcome.py b/tests/jobs/test_send_proton_welcome.py index 6630b252..d2560944 100644 --- a/tests/jobs/test_send_proton_welcome.py +++ b/tests/jobs/test_send_proton_welcome.py @@ -10,5 +10,5 @@ def test_send_welcome_proton_email(): sent_mails = mail_sender.get_stored_emails() assert len(sent_mails) == 1 sent_mail = sent_mails[0] - to_email, _, _ = user.get_communication_email() - sent_mail.envelope_to = to_email + comm_alias, _, _ = user.get_communication_email() + sent_mail.envelope_to = comm_alias.email