Add more From: sender formatting options
This commit is contained in:
parent
f577adc0d4
commit
a5f24e0227
|
@ -206,31 +206,30 @@
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="card-title">Sender address format</div>
|
<div class="card-title">Sender address format</div>
|
||||||
<div class="mt-1 mb-3">
|
<div class="mt-1 mb-3">
|
||||||
When your alias receives an email, says from <b>John Wick <john@wick.com></b>,
|
When your alias receives an email, it says from: <b>John Wick <john@wick.com></b>,
|
||||||
SimpleLogin forwards it to your mailbox. <br>
|
SimpleLogin forwards it to your mailbox. <br>
|
||||||
|
|
||||||
Due to some email constraints, SimpleLogin cannot keep the sender email address
|
Due to some email constraints, SimpleLogin cannot keep the sender email address
|
||||||
in the original form and needs to <b>transform</b> it to one of the 2 below formats.
|
in the original form and needs to <b>transform</b> it to one of the formats below.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form method="post" action="#sender-format">
|
<form method="post" action="#sender-format">
|
||||||
<input type="hidden" name="form-name" value="change-sender-format">
|
<input type="hidden" name="form-name" value="change-sender-format">
|
||||||
|
|
||||||
<div class="form-check">
|
<select class="form-control mr-sm-2" name="sender-format">
|
||||||
<input class="form-check-input" type="radio" name="sender-format" id="exampleRadios1" value="1"
|
<option value="{{ SenderFormatEnum.VIA.value }}"
|
||||||
{% if current_user.use_via_format_for_sender %} checked {% endif %}>
|
{% if current_user.sender_format == SenderFormatEnum.VIA.value %} selected {% endif %}>
|
||||||
<label class="form-check-label" for="exampleRadios1">
|
john@wick.com via SimpleLogin</option>
|
||||||
<b>john@wick.com via SimpleLogin</b>
|
<option value="{{ SenderFormatEnum.AT.value }}"
|
||||||
</label>
|
{% if current_user.sender_format == SenderFormatEnum.AT.value %} selected {% endif %}>
|
||||||
</div>
|
John Wick - john at wick.com</option>
|
||||||
|
<option value="{{ SenderFormatEnum.A.value }}"
|
||||||
<div class="form-check">
|
{% if current_user.sender_format == SenderFormatEnum.A.value %} selected {% endif %}>
|
||||||
<input class="form-check-input" type="radio" name="sender-format" id="exampleRadios2" value="0"
|
John Wick - john(a)wick.com</option>
|
||||||
{% if not current_user.use_via_format_for_sender %} checked {% endif %}>
|
<option value="{{ SenderFormatEnum.FULL.value }}"
|
||||||
<label class="form-check-label" for="exampleRadios2">
|
{% if current_user.sender_format == SenderFormatEnum.FULL.value %} selected {% endif %}>
|
||||||
<b>John Wick - john at wick.com</b>
|
John Wick - john@wick.com</option>
|
||||||
</label>
|
</select>
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="btn btn-outline-primary mt-3">Update</button>
|
<button class="btn btn-outline-primary mt-3">Update</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -30,6 +30,7 @@ from app.models import (
|
||||||
Client,
|
Client,
|
||||||
AliasGeneratorEnum,
|
AliasGeneratorEnum,
|
||||||
ManualSubscription,
|
ManualSubscription,
|
||||||
|
SenderFormatEnum,
|
||||||
)
|
)
|
||||||
from app.utils import random_string
|
from app.utils import random_string
|
||||||
|
|
||||||
|
@ -161,12 +162,11 @@ def setting():
|
||||||
return redirect(url_for("dashboard.setting"))
|
return redirect(url_for("dashboard.setting"))
|
||||||
elif request.form.get("form-name") == "change-sender-format":
|
elif request.form.get("form-name") == "change-sender-format":
|
||||||
sender_format = int(request.form.get("sender-format"))
|
sender_format = int(request.form.get("sender-format"))
|
||||||
if sender_format == 0:
|
if SenderFormatEnum.has_value(sender_format):
|
||||||
current_user.use_via_format_for_sender = False
|
current_user.sender_format = sender_format
|
||||||
else:
|
|
||||||
current_user.use_via_format_for_sender = True
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("Your sender format preference has been updated", "success")
|
flash("Your sender format preference has been updated", "success")
|
||||||
|
db.session.commit()
|
||||||
return redirect(url_for("dashboard.setting"))
|
return redirect(url_for("dashboard.setting"))
|
||||||
|
|
||||||
elif request.form.get("form-name") == "export-data":
|
elif request.form.get("form-name") == "export-data":
|
||||||
|
@ -200,6 +200,7 @@ def setting():
|
||||||
"dashboard/setting.html",
|
"dashboard/setting.html",
|
||||||
form=form,
|
form=form,
|
||||||
PlanEnum=PlanEnum,
|
PlanEnum=PlanEnum,
|
||||||
|
SenderFormatEnum=SenderFormatEnum,
|
||||||
promo_form=promo_form,
|
promo_form=promo_form,
|
||||||
change_email_form=change_email_form,
|
change_email_form=change_email_form,
|
||||||
pending_email=pending_email,
|
pending_email=pending_email,
|
||||||
|
|
|
@ -96,19 +96,29 @@ class File(db.Model, ModelMixin):
|
||||||
return s3.get_url(self.path, expires_in)
|
return s3.get_url(self.path, expires_in)
|
||||||
|
|
||||||
|
|
||||||
class PlanEnum(enum.Enum):
|
class EnumE(enum.Enum):
|
||||||
|
@classmethod
|
||||||
|
def has_value(cls, value: int) -> bool:
|
||||||
|
return value in set(item.value for item in cls)
|
||||||
|
|
||||||
|
|
||||||
|
class PlanEnum(EnumE):
|
||||||
monthly = 2
|
monthly = 2
|
||||||
yearly = 3
|
yearly = 3
|
||||||
|
|
||||||
|
|
||||||
class AliasGeneratorEnum(enum.Enum):
|
# Specify the format for sender address
|
||||||
|
class SenderFormatEnum(EnumE):
|
||||||
|
AT = 0 # John Wick - john at wick.com
|
||||||
|
VIA = 1 # john@wick.com via SimpleLogin
|
||||||
|
A = 2 # John Wick - john(a)wick.com
|
||||||
|
FULL = 3 # John Wick - john@wick.com
|
||||||
|
|
||||||
|
|
||||||
|
class AliasGeneratorEnum(EnumE):
|
||||||
word = 1 # aliases are generated based on random words
|
word = 1 # aliases are generated based on random words
|
||||||
uuid = 2 # aliases are generated based on uuid
|
uuid = 2 # aliases are generated based on uuid
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def has_value(cls, value: int) -> bool:
|
|
||||||
return value in set(item.value for item in cls)
|
|
||||||
|
|
||||||
|
|
||||||
class User(db.Model, ModelMixin, UserMixin):
|
class User(db.Model, ModelMixin, UserMixin):
|
||||||
__tablename__ = "users"
|
__tablename__ = "users"
|
||||||
|
@ -172,10 +182,13 @@ class User(db.Model, ModelMixin, UserMixin):
|
||||||
|
|
||||||
profile_picture = db.relationship(File, foreign_keys=[profile_picture_id])
|
profile_picture = db.relationship(File, foreign_keys=[profile_picture_id])
|
||||||
|
|
||||||
# Use the "via" format for sender address, i.e. "name@example.com via SimpleLogin"
|
# Specify the format for sender address
|
||||||
# If False, use the format "Name - name at example.com"
|
# John Wick - john at wick.com -> 0
|
||||||
use_via_format_for_sender = db.Column(
|
# john@wick.com via SimpleLogin -> 1
|
||||||
db.Boolean, default=True, nullable=False, server_default="1"
|
# John Wick - john@wick.com -> 2
|
||||||
|
# John Wick - john@wick.com -> 3
|
||||||
|
sender_format = db.Column(
|
||||||
|
db.Integer, default="1", nullable=False, server_default="1"
|
||||||
)
|
)
|
||||||
|
|
||||||
referral_id = db.Column(
|
referral_id = db.Column(
|
||||||
|
@ -868,21 +881,35 @@ class Contact(db.Model, ModelMixin):
|
||||||
|
|
||||||
def new_addr(self):
|
def new_addr(self):
|
||||||
"""
|
"""
|
||||||
Replace original email by reply_email. 2 possible formats:
|
Replace original email by reply_email. Possible formats:
|
||||||
- first@example.com by SimpleLogin <reply_email> OR
|
- first@example.com via SimpleLogin <reply_email> OR
|
||||||
- First Last - first at example.com <reply_email>
|
- First Last - first at example.com <reply_email> OR
|
||||||
|
- First Last - first(a)example.com <reply_email> OR
|
||||||
|
- First Last - first@example.com <reply_email> OR
|
||||||
And return new address with RFC 2047 format
|
And return new address with RFC 2047 format
|
||||||
|
|
||||||
`new_email` is a special reply address
|
`new_email` is a special reply address
|
||||||
"""
|
"""
|
||||||
user = self.user
|
user = self.user
|
||||||
if user and user.use_via_format_for_sender:
|
if (
|
||||||
|
not user
|
||||||
|
or not SenderFormatEnum.has_value(user.sender_format)
|
||||||
|
or user.sender_format == SenderFormatEnum.VIA.value
|
||||||
|
):
|
||||||
new_name = f"{self.website_email} via SimpleLogin"
|
new_name = f"{self.website_email} via SimpleLogin"
|
||||||
else:
|
elif user.sender_format == SenderFormatEnum.AT.value:
|
||||||
name = self.name or ""
|
name = self.name or ""
|
||||||
new_name = (
|
new_name = (
|
||||||
name + (" - " if name else "") + self.website_email.replace("@", " at ")
|
name + (" - " if name else "") + self.website_email.replace("@", " at ")
|
||||||
).strip()
|
).strip()
|
||||||
|
elif user.sender_format == SenderFormatEnum.A.value:
|
||||||
|
name = self.name or ""
|
||||||
|
new_name = (
|
||||||
|
name + (" - " if name else "") + self.website_email.replace("@", "(a)")
|
||||||
|
).strip()
|
||||||
|
elif user.sender_format == SenderFormatEnum.FULL.value:
|
||||||
|
name = self.name or ""
|
||||||
|
new_name = (name + (" - " if name else "") + self.website_email).strip()
|
||||||
|
|
||||||
new_addr = formataddr((new_name, self.reply_email)).strip()
|
new_addr = formataddr((new_name, self.reply_email)).strip()
|
||||||
return new_addr.strip()
|
return new_addr.strip()
|
||||||
|
|
45
migrations/versions/2020_051515_5cad8fa84386_.py
Normal file
45
migrations/versions/2020_051515_5cad8fa84386_.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 5cad8fa84386
|
||||||
|
Revises: a5e3c6693dc6
|
||||||
|
Create Date: 2020-05-15 15:10:00.096349
|
||||||
|
|
||||||
|
"""
|
||||||
|
import sqlalchemy_utils
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = "5cad8fa84386"
|
||||||
|
down_revision = "a5e3c6693dc6"
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.alter_column("users", "use_via_format_for_sender", server_default=None)
|
||||||
|
op.alter_column(
|
||||||
|
"users",
|
||||||
|
"use_via_format_for_sender",
|
||||||
|
new_column_name="sender_format",
|
||||||
|
type_=sa.Integer(),
|
||||||
|
postgresql_using="use_via_format_for_sender::integer",
|
||||||
|
)
|
||||||
|
op.alter_column("users", "sender_format", server_default="1")
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.alter_column("users", "sender_format", server_default=None)
|
||||||
|
op.alter_column(
|
||||||
|
"users",
|
||||||
|
"sender_format",
|
||||||
|
new_column_name="use_via_format_for_sender",
|
||||||
|
type_=sa.Boolean(),
|
||||||
|
postgresql_using="sender_format::boolean",
|
||||||
|
)
|
||||||
|
op.alter_column("users", "use_via_format_for_sender", server_default="1")
|
||||||
|
# ### end Alembic commands ###
|
|
@ -1,11 +1,18 @@
|
||||||
from uuid import UUID
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
from app.config import EMAIL_DOMAIN, MAX_NB_EMAIL_FREE_PLAN
|
from app.config import EMAIL_DOMAIN, MAX_NB_EMAIL_FREE_PLAN
|
||||||
from app.email_utils import parseaddr_unicode
|
from app.email_utils import parseaddr_unicode
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models import generate_email, User, Alias, Contact, Mailbox, AliasMailbox
|
from app.models import (
|
||||||
|
generate_email,
|
||||||
|
User,
|
||||||
|
Alias,
|
||||||
|
Contact,
|
||||||
|
Mailbox,
|
||||||
|
AliasMailbox,
|
||||||
|
SenderFormatEnum,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_generate_email(flask_client):
|
def test_generate_email(flask_client):
|
||||||
|
@ -104,7 +111,7 @@ def test_new_addr(flask_client):
|
||||||
alias = Alias.create_new_random(user)
|
alias = Alias.create_new_random(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
# use_via_format_for_sender is by default
|
# default sender_format is 'via'
|
||||||
c1 = Contact.create(
|
c1 = Contact.create(
|
||||||
user_id=user.id,
|
user_id=user.id,
|
||||||
alias_id=alias.id,
|
alias_id=alias.id,
|
||||||
|
@ -115,8 +122,8 @@ def test_new_addr(flask_client):
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
assert c1.new_addr() == '"abcd@example.com via SimpleLogin" <rep@SL>'
|
assert c1.new_addr() == '"abcd@example.com via SimpleLogin" <rep@SL>'
|
||||||
|
|
||||||
# use_via_format_for_sender = False
|
# set sender_format = AT
|
||||||
user.use_via_format_for_sender = False
|
user.sender_format = SenderFormatEnum.AT.value
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
assert c1.new_addr() == '"First Last - abcd at example.com" <rep@SL>'
|
assert c1.new_addr() == '"First Last - abcd at example.com" <rep@SL>'
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue