From faae37b6bcc62305ce2b1ba36ddeba83caa18d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Casaj=C3=BAs?= Date: Fri, 28 Jun 2024 15:34:16 +0200 Subject: [PATCH] Use partner emails when the user has used alias from a partner (#2136) * Update base templates based on the parter user * Update template * Fix missing check * Check if the user is set * Hide flag usage --- app/email_utils.py | 6 + app/models.py | 14 + ...ate_user_flag_alias_create_from_partner.py | 55 ++ static/logo-proton.png | Bin 0 -> 3251 bytes templates/emails/base.html | 625 +---------------- templates/emails/base_partner.html | 646 ++++++++++++++++++ templates/emails/base_sl.html | 623 +++++++++++++++++ tests/models/test_alias.py | 16 +- 8 files changed, 1364 insertions(+), 621 deletions(-) create mode 100644 oneshot/recalculate_user_flag_alias_create_from_partner.py create mode 100644 static/logo-proton.png create mode 100644 templates/emails/base_partner.html create mode 100644 templates/emails/base_sl.html diff --git a/app/email_utils.py b/app/email_utils.py index 0ab5626c..070d2910 100644 --- a/app/email_utils.py +++ b/app/email_utils.py @@ -33,6 +33,7 @@ from flanker.addresslib import address from flanker.addresslib.address import EmailAddress from jinja2 import Environment, FileSystemLoader from sqlalchemy import func +from flask_login import current_user from app import config from app.db import Session @@ -74,11 +75,16 @@ def render(template_name, **kwargs) -> str: template = env.get_template(template_name) + use_partner_template = False + if current_user and current_user.is_authenticated: + use_partner_template = current_user.has_used_alias_from_partner() + return template.render( MAX_NB_EMAIL_FREE_PLAN=config.MAX_NB_EMAIL_FREE_PLAN, URL=config.URL, LANDING_PAGE_URL=config.LANDING_PAGE_URL, YEAR=arrow.now().year, + USE_PARTNER_TEMPLATE=use_partner_template, **kwargs, ) diff --git a/app/models.py b/app/models.py index c7c8f700..3f9952bd 100644 --- a/app/models.py +++ b/app/models.py @@ -330,6 +330,7 @@ class User(Base, ModelMixin, UserMixin, PasswordOracle): FLAG_FREE_DISABLE_CREATE_ALIAS = 1 << 0 FLAG_CREATED_FROM_PARTNER = 1 << 1 FLAG_FREE_OLD_ALIAS_LIMIT = 1 << 2 + FLAG_CREATED_ALIAS_FROM_PARTNER = 1 << 3 email = sa.Column(sa.String(256), unique=True, nullable=False) @@ -1153,6 +1154,13 @@ class User(Base, ModelMixin, UserMixin, PasswordOracle): return True return not config.DISABLE_CREATE_CONTACTS_FOR_FREE_USERS + def has_used_alias_from_partner(self) -> bool: + return ( + self.flags + & (User.FLAG_CREATED_ALIAS_FROM_PARTNER | User.FLAG_CREATED_FROM_PARTNER) + > 0 + ) + def __repr__(self): return f"" @@ -1646,6 +1654,12 @@ class Alias(Base, ModelMixin): ) EventDispatcher.send_event(user, EventContent(alias_created=event)) + if ( + new_alias.flags & cls.FLAG_PARTNER_CREATED > 0 + and new_alias.user.flags & User.FLAG_CREATED_ALIAS_FROM_PARTNER == 0 + ): + user.flags = user.flags | User.FLAG_CREATED_ALIAS_FROM_PARTNER + if commit: Session.commit() diff --git a/oneshot/recalculate_user_flag_alias_create_from_partner.py b/oneshot/recalculate_user_flag_alias_create_from_partner.py new file mode 100644 index 00000000..9f71f3e1 --- /dev/null +++ b/oneshot/recalculate_user_flag_alias_create_from_partner.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +import argparse +import time + +from sqlalchemy import func +from app.models import Alias, User +from app.db import Session + +parser = argparse.ArgumentParser( + prog="Backfill alias", description="Backfill user flags for partner alias created" +) +parser.add_argument( + "-s", "--start_user_id", default=0, type=int, help="Initial user_id" +) +parser.add_argument("-e", "--end_user_id", default=0, type=int, help="Last user_id") + +args = parser.parse_args() +user_id_start = args.start_user_id +max_user_id = args.end_user_id +if max_user_id == 0: + max_user_id = Session.query(func.max(User.id)).scalar() + +print(f"Checking user {user_id_start} to {max_user_id}") +step = 1000 +el_query = "SELECT user_id, count(id) from alias where user_id>=:start AND user_id < :end AND flags & :alias_flag > 0 GROUP BY user_id" +user_update_query = "UPDATE users set flags = flags | :user_flag where id = :user_id" +updated = 0 +start_time = time.time() +for batch_start in range(user_id_start, max_user_id, step): + rows = Session.execute( + el_query, + { + "start": batch_start, + "end": batch_start + step, + "alias_flag": Alias.FLAG_PARTNER_CREATED, + }, + ) + for row in rows: + if row[1] > 0: + Session.execute( + user_update_query, + {"user_id": row[0], "user_flag": User.FLAG_CREATED_ALIAS_FROM_PARTNER}, + ) + Session.commit() + updated += 1 + elapsed = time.time() - start_time + time_per_alias = elapsed / (updated + 1) + last_batch_id = batch_start + step + remaining = max_user_id - last_batch_id + time_remaining = (max_user_id - last_batch_id) * time_per_alias + hours_remaining = time_remaining / 3600.0 + print( + f"\rUser {batch_start}/{max_user_id} {updated} {hours_remaining:.2f}hrs remaining" + ) +print("") diff --git a/static/logo-proton.png b/static/logo-proton.png new file mode 100644 index 0000000000000000000000000000000000000000..a213f55ffa6a3af69956a8fe5c13ce948a112e86 GIT binary patch literal 3251 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 K7WL-n^#1_V5??0( literal 0 HcmV?d00001 diff --git a/templates/emails/base.html b/templates/emails/base.html index 5ae5af94..aacac3b4 100644 --- a/templates/emails/base.html +++ b/templates/emails/base.html @@ -1,623 +1,8 @@ -{% from "_emailhelpers.html" import render_text, text, render_button, raw_url, grey_section, section %} - - - - - - - - - - - - {{ pre_header }} - - - - - - - +{% endif %} diff --git a/templates/emails/base_partner.html b/templates/emails/base_partner.html new file mode 100644 index 00000000..d22a3b06 --- /dev/null +++ b/templates/emails/base_partner.html @@ -0,0 +1,646 @@ +{% from "_emailhelpers.html" import render_text, text, render_button, raw_url, grey_section, section %} + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + +
+ + + + + + +
+ + + + + + +
+ + Proton + +
+
+
+ {% block greeting %}{% endblock %} + {% block content %}{% endblock %} + + {% block sub_copy %}{% endblock %} +
+ + +
+
+ + diff --git a/templates/emails/base_sl.html b/templates/emails/base_sl.html new file mode 100644 index 00000000..5ae5af94 --- /dev/null +++ b/templates/emails/base_sl.html @@ -0,0 +1,623 @@ +{% from "_emailhelpers.html" import render_text, text, render_button, raw_url, grey_section, section %} + + + + + + + + + + + + {{ pre_header }} + + + + + + + diff --git a/tests/models/test_alias.py b/tests/models/test_alias.py index 3354b8bc..c11b596e 100644 --- a/tests/models/test_alias.py +++ b/tests/models/test_alias.py @@ -1,5 +1,5 @@ from app.db import Session -from app.models import Alias, Mailbox, AliasMailbox +from app.models import Alias, Mailbox, AliasMailbox, User from tests.utils import create_new_user, random_email @@ -15,3 +15,17 @@ def test_duplicated_mailbox_is_returned_only_once(): alias_mailbox_id = [mailbox.id for mailbox in alias_mailboxes] assert user.default_mailbox_id in alias_mailbox_id assert other_mailbox.id in alias_mailbox_id + + +def test_alias_create_from_partner_flags_also_the_user(): + user = create_new_user() + Session.flush() + email = random_email() + alias = Alias.create( + user_id=user.id, + email=email, + mailbox_id=user.default_mailbox_id, + flags=Alias.FLAG_PARTNER_CREATED, + flush=True, + ) + assert alias.user.flags & User.FLAG_CREATED_ALIAS_FROM_PARTNER > 0