Fix: Set oneclick link for unsubscribe of the newsletter for tx emails (#1465)

* Feat: Add unsub oneclick to the base transactional email template

* Format

* Removed unused

* Format

Co-authored-by: Adrià Casajús <adria.casajus@proton.ch>
This commit is contained in:
Adrià Casajús 2022-12-13 16:59:14 +01:00 committed by GitHub
parent 21e9fce3ba
commit 73c0429cad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 75 additions and 53 deletions

View file

@ -106,15 +106,15 @@ def render(template_name, **kwargs) -> str:
def send_welcome_email(user): def send_welcome_email(user):
to_email, unsubscribe_link, via_email = user.get_communication_email() comm_alias, unsubscribe_link, via_email = user.get_communication_email()
if not to_email: if not comm_alias:
return return
# whether this email is sent to an alias # 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( send_email(
to_email, comm_alias.email,
f"Welcome to SimpleLogin", f"Welcome to SimpleLogin",
render("com/welcome.txt", user=user, alias=alias), render("com/welcome.txt", user=user, alias=alias),
render("com/welcome.html", user=user, alias=alias), render("com/welcome.html", user=user, alias=alias),

View file

@ -928,7 +928,7 @@ class User(Base, ModelMixin, UserMixin, PasswordOracle):
def two_factor_authentication_enabled(self) -> bool: def two_factor_authentication_enabled(self) -> bool:
return self.enable_otp or self.fido_enabled() 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 Return
- the email that user uses to receive email communication. None if user unsubscribes from newsletter - 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( unsub = UnsubscribeEncoder.encode(
UnsubscribeAction.DisableAlias, alias.id 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 # alias disabled -> user doesn't want to receive newsletter
else: else:
return None, None, False return None, "", False
else: else:
# do not handle http POST unsubscribe # do not handle http POST unsubscribe
if config.UNSUBSCRIBER: if config.UNSUBSCRIBER:
@ -958,7 +958,7 @@ class User(Base, ModelMixin, UserMixin, PasswordOracle):
True, True,
) )
return None, None, False return None, "", False
def available_sl_domains(self) -> [str]: def available_sl_domains(self) -> [str]:
""" """

View file

@ -4,6 +4,7 @@ from jinja2 import Environment, FileSystemLoader
from app.config import ROOT_DIR, URL from app.config import ROOT_DIR, URL
from app.email_utils import send_email from app.email_utils import send_email
from app.handler.unsubscribe_encoder import UnsubscribeEncoder, UnsubscribeAction
from app.log import LOG from app.log import LOG
from app.models import NewsletterUser 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) html_template = env.from_string(newsletter.html)
text_template = env.from_string(newsletter.plain_text) text_template = env.from_string(newsletter.plain_text)
to_email, unsubscribe_link, via_email = user.get_communication_email() comm_alias, unsubscribe_link, via_email = user.get_communication_email()
if not to_email: if not comm_alias:
return False, f"{user} not subscribed to newsletter" 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( send_email(
to_email, comm_alias.email,
newsletter.subject, newsletter.subject,
text_template.render( text_template.render(
user=user, user=user,
@ -30,9 +37,10 @@ def send_newsletter_to_user(newsletter, user) -> (bool, str):
html_template.render( html_template.render(
user=user, user=user,
URL=URL, URL=URL,
unsubscribe_link=unsubscribe_link, unsubscribe_oneclick=unsubscribe_oneclick,
), ),
unsubscribe_link=unsubscribe_link, unsubscribe_link=unsubscribe_link,
unsubscribe_via_email=via_email,
) )
NewsletterUser.create(newsletter_id=newsletter.id, user_id=user.id, commit=True) NewsletterUser.create(newsletter_id=newsletter.id, user_id=user.id, commit=True)

View file

@ -22,15 +22,21 @@ from server import create_light_app
def onboarding_send_from_alias(user): def onboarding_send_from_alias(user):
to_email, unsubscribe_link, via_email = user.get_communication_email() comm_alias, unsubscribe_link, via_email = user.get_communication_email()
if not to_email: if not comm_alias.email:
return return
send_email( send_email(
to_email, comm_alias.email,
"SimpleLogin Tip: Send emails from your alias", "SimpleLogin Tip: Send emails from your alias",
render("com/onboarding/send-from-alias.txt.j2", user=user, to_email=to_email), render(
render("com/onboarding/send-from-alias.html", user=user, to_email=to_email), "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, unsubscribe_link,
via_email, via_email,
retries=3, retries=3,
@ -39,15 +45,15 @@ def onboarding_send_from_alias(user):
def onboarding_pgp(user): def onboarding_pgp(user):
to_email, unsubscribe_link, via_email = user.get_communication_email() comm_alias, unsubscribe_link, via_email = user.get_communication_email()
if not to_email: if not comm_alias:
return return
send_email( send_email(
to_email, comm_alias.email,
"SimpleLogin Tip: Secure your emails with PGP", "SimpleLogin Tip: Secure your emails with PGP",
render("com/onboarding/pgp.txt", 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=to_email), render("com/onboarding/pgp.html", user=user, to_email=comm_alias.email),
unsubscribe_link, unsubscribe_link,
via_email, via_email,
retries=3, retries=3,
@ -56,15 +62,21 @@ def onboarding_pgp(user):
def onboarding_browser_extension(user): def onboarding_browser_extension(user):
to_email, unsubscribe_link, via_email = user.get_communication_email() comm_alias, unsubscribe_link, via_email = user.get_communication_email()
if not to_email: if not comm_alias:
return return
send_email( send_email(
to_email, comm_alias.email,
"SimpleLogin Tip: Chrome/Firefox/Safari extensions and Android/iOS apps", "SimpleLogin Tip: Chrome/Firefox/Safari extensions and Android/iOS apps",
render("com/onboarding/browser-extension.txt", user=user, to_email=to_email), render(
render("com/onboarding/browser-extension.html", user=user, to_email=to_email), "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, unsubscribe_link,
via_email, via_email,
retries=3, retries=3,
@ -73,15 +85,15 @@ def onboarding_browser_extension(user):
def onboarding_mailbox(user): def onboarding_mailbox(user):
to_email, unsubscribe_link, via_email = user.get_communication_email() comm_alias, unsubscribe_link, via_email = user.get_communication_email()
if not to_email: if not comm_alias:
return return
send_email( send_email(
to_email, comm_alias.email,
"SimpleLogin Tip: Multiple mailboxes", "SimpleLogin Tip: Multiple mailboxes",
render("com/onboarding/mailbox.txt", 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=to_email), render("com/onboarding/mailbox.html", user=user, to_email=comm_alias.email),
unsubscribe_link, unsubscribe_link,
via_email, via_email,
retries=3, retries=3,
@ -90,19 +102,23 @@ def onboarding_mailbox(user):
def welcome_proton(user): def welcome_proton(user):
to_email, _, _ = user.get_communication_email() comm_alias, _, _ = user.get_communication_email()
if not to_email: if not comm_alias:
return return
send_email( send_email(
to_email, comm_alias.email,
"Welcome to SimpleLogin, an email masking service provided by Proton", "Welcome to SimpleLogin, an email masking service provided by Proton",
render( render(
"com/onboarding/welcome-proton-user.txt.jinja2", "com/onboarding/welcome-proton-user.txt.jinja2",
user=user, 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, retries=3,
ignore_smtp_error=True, ignore_smtp_error=True,
) )

View file

@ -841,8 +841,8 @@ def register_custom_commands(app):
LOG.i(f"User {user_id} was maybe deleted in the meantime") LOG.i(f"User {user_id} was maybe deleted in the meantime")
continue continue
to_email, unsubscribe_link, via_email = user.get_communication_email() comm_alias, unsubscribe_link, via_email = user.get_communication_email()
if not to_email: if not comm_alias.email:
continue continue
sent, error_msg = send_newsletter_to_user(newsletter, user) sent, error_msg = send_newsletter_to_user(newsletter, user)

View file

@ -591,6 +591,16 @@
<br /> <br />
{% block footer %}{% endblock %} {% block footer %}{% endblock %}
</p> </p>
{% if unsubscribe_oneclick is defined %}
<p class="f-fallback sub align-center"
style="font-size: 13px;
line-height: 1.625;
text-align: center;
margin: .4em 0 1.1875em;">
<a href="{{ unsubscribe_oneclick }}">Unsubscribe from our newsletter</a>
</p>
{% endif %}
<p class="f-fallback sub align-center" <p class="f-fallback sub align-center"
style="font-size: 13px; style="font-size: 13px;
line-height: 1.625; line-height: 1.625;

View file

@ -1,12 +0,0 @@
{% extends "base.html" %}
{% block footer %}
<p class="f-fallback sub align-center"
style="font-size: 13px;
line-height: 1.625;
text-align: center;
margin: .4em 0 1.1875em;">
<a href="{{ unsubscribe_link }}">Unsubscribe from our newsletter</a>
</p>
{% endblock %}

View file

@ -10,5 +10,5 @@ def test_send_welcome_proton_email():
sent_mails = mail_sender.get_stored_emails() sent_mails = mail_sender.get_stored_emails()
assert len(sent_mails) == 1 assert len(sent_mails) == 1
sent_mail = sent_mails[0] sent_mail = sent_mails[0]
to_email, _, _ = user.get_communication_email() comm_alias, _, _ = user.get_communication_email()
sent_mail.envelope_to = to_email sent_mail.envelope_to = comm_alias.email