Merge pull request #91 from simple-login/mailbox2

Improve Mailbox
This commit is contained in:
Son Nguyen Kim 2020-02-23 16:39:44 +07:00 committed by GitHub
commit e8e0923de7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 510 additions and 46 deletions

View File

@ -5,7 +5,7 @@
{% endblock %}
{% block single_content %}
<div class="text-center">
<div class="text-center bg-white p-5" style="max-width: 50rem">
<h1>
An email to validate your email is on its way.
</h1>

View File

@ -16,4 +16,5 @@ from .views import (
directory,
mailbox,
deleted_alias,
mailbox_detail,
)

View File

@ -40,7 +40,7 @@
<div class="col-sm-6 p-1">
<select class="form-control" name="suffix">
<select class="form-control custom-select" name="suffix">
{% for suffix in suffixes %}
<option value="{{ suffix[1] }}">
{% if suffix[0] %}
@ -54,10 +54,10 @@
</div>
</div>
{% if mailboxes|length > 1 %}
{% if mailboxes|length > 1 or current_user.full_mailbox %}
<div class="row mb-2">
<div class="col p-1">
<select class="form-control" name="mailbox">
<select class="form-control custom-select" name="mailbox">
{% for mailbox in mailboxes %}
<option value="{{ mailbox }}">
{{ mailbox }}

View File

@ -57,7 +57,7 @@
<input type="hidden" name="form-name" value="delete">
<input type="hidden" class="dir-name" value="{{ dir.name }}">
<input type="hidden" name="dir-id" value="{{ dir.id }}">
<span class="card-link btn btn-link float-right delete-dir">
<span class="card-link btn btn-link float-right text-danger delete-dir">
Delete
</span>
</form>
@ -85,8 +85,7 @@
{{ new_dir_form.name(class="form-control", placeholder="my-directory",
pattern="[0-9a-z-_]{3,}",
title="Only letter, number, dash (-), underscore (_) can be used. Directory name must be at least 3 characters.",
autofocus="1") }}
title="Only letter, number, dash (-), underscore (_) can be used. Directory name must be at least 3 characters.") }}
{{ render_field_errors(new_dir_form.name) }}
<button class="btn btn-lg btn-success mt-2">Create</button>
</form>

View File

@ -134,12 +134,6 @@
<hr class="my-2">
{% if alias_info.mailbox != None %}
<div class="small-text">
Owned by <b>{{ alias_info.mailbox.email }}</b> mailbox
</div>
{% endif %}
<p class="small-text">
Created {{ gen_email.created_at | dt }}
{% if alias_info.highlight %}
@ -157,10 +151,42 @@
</a>
</div>
{% if current_user.full_mailbox and mailboxes|length > 1 %}
<form method="post">
<div class="small-text mt-2">Current mailbox</div>
<div class="row">
<div class="col-lg-10">
<select class="form-control form-control-sm custom-select" name="mailbox">
{% for mailbox in mailboxes %}
<option value="{{ mailbox }}" {% if mailbox == alias_info.mailbox.email %} selected {% endif %}>
{{ mailbox }}
</option>
{% endfor %}
</select>
</div>
<div class="col-lg-2">
<input type="hidden" name="form-name" value="set-mailbox">
<input type="hidden" name="gen-email-id" value="{{ gen_email.id }}">
<button class="btn btn-sm btn-outline-info w-100">
Update
</button>
</div>
</div>
</form>
{% elif alias_info.mailbox != None and alias_info.mailbox.email != current_user.email %}
<div class="small-text">
Owned by <b>{{ alias_info.mailbox.email }}</b> mailbox
</div>
{% endif %}
<form method="post">
<div class="row mt-2">
<div class="col-10">
<div class="col-lg-10">
<textarea
name="note"
class="form-control"
@ -168,11 +194,11 @@
placeholder="Alias Note.">{{ gen_email.note or "" }}</textarea>
</div>
<div class="col-2">
<div class="col-lg-2">
<input type="hidden" name="form-name" value="set-note">
<input type="hidden" name="gen-email-id" value="{{ gen_email.id }}">
<button class="btn btn-sm btn-outline-success">
<button class="btn btn-sm btn-outline-success w-100">
Save
</button>
</div>

View File

@ -20,13 +20,15 @@
A <em>mailbox</em> is just another personal email address. When creating a new alias, you could choose the
mailbox that <em>owns</em> this alias, i.e: <br>
- all emails sent to this alias will be forwarded to this mailbox <br>
- from this mailbox, you can reply/send emails from the alias. <br>
- from this mailbox, you can reply/send emails from the alias. <br><br>
By default, all aliases are owned by your email <b>{{ current_user.email }}</b>. <br><br>
{% if current_user.full_mailbox %}
When you signed up, a mailbox is automatically created with your email <b>{{ current_user.email }}</b>
<br><br>
{% endif %}
The mailbox doesn't have to be your email: it can be your friend's email
if you want to create aliases for your buddy. <br>
They just need to validate this mailbox when they receive the activation email.
if you want to create aliases for your buddy.
</div>
{% for mailbox in mailboxes %}
@ -41,22 +43,46 @@
🚫
</span>
{% endif %}
{% if mailbox.id == current_user.default_mailbox_id %}
<div class="badge badge-primary float-right" data-toggle="tooltip"
title="When a new random alias is created, it belongs to the default mailbox">Default Mailbox
</div>
{% endif %}
</h5>
<h6 class="card-subtitle mb-2 text-muted">
Created {{ mailbox.created_at | dt }} <br>
<span class="font-weight-bold">{{ mailbox.nb_alias() }}</span> aliases.
<span class="font-weight-bold">{{ mailbox.nb_alias() }}</span> aliases. <br>
</h6>
<a href="{{ url_for('dashboard.mailbox_detail_route', mailbox_id=mailbox.id) }}">Edit ➡</a>
</div>
<div class="card-footer p-0">
<div class="row">
{% if mailbox.verified and current_user.full_mailbox %}
<div class="col">
<form method="post">
<input type="hidden" name="form-name" value="set-default">
<input type="hidden" class="mailbox" value="{{ mailbox.email }}">
<input type="hidden" name="mailbox-id" value="{{ mailbox.id }}">
<button class="card-link btn btn-link
{% if mailbox.id == current_user.default_mailbox_id %} disabled {% endif %}"
>
Set As Default Mailbox
</button>
</form>
</div>
{% endif %}
<div class="col">
<form method="post">
<input type="hidden" name="form-name" value="delete">
<input type="hidden" class="mailbox" value="{{ mailbox.email }}">
<input type="hidden" name="mailbox-id" value="{{ mailbox.id }}">
<span class="card-link btn btn-link float-right delete-mailbox">
<span class="card-link btn btn-link text-danger float-right delete-mailbox
{% if mailbox.id == current_user.default_mailbox_id %} disabled {% endif %}">
Delete
</span>
</form>
@ -81,8 +107,7 @@
A verification email will be sent to this email to make sure you have access to this email.
</div>
{{ new_mailbox_form.email(class="form-control", placeholder="email@example.com",
autofocus="1") }}
{{ new_mailbox_form.email(class="form-control", placeholder="email@example.com") }}
{{ render_field_errors(new_mailbox_form.email) }}
<button class="btn btn-lg btn-success mt-2">Create</button>
</form>

View File

@ -0,0 +1,58 @@
{% extends 'default.html' %}
{% set active_page = "mailbox" %}
{% block title %}
Mailbox {{ mailbox.email }}
{% endblock %}
{% block default_content %}
<div class="col-md-8 offset-md-2 pb-3">
<h1 class="h3">{{ mailbox.email }}
{% if mailbox.verified %}
<span class="cursor" data-toggle="tooltip" data-original-title="Mailbox Verified"></span>
{% else %}
<span class="cursor" data-toggle="tooltip" data-original-title="Mailbox Not Verified">
🚫
</span>
{% endif %}
</h1>
<!-- Change email -->
<div class="card">
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="form-name" value="update-email">
{{ change_email_form.csrf_token }}
<div class="card-body">
<div class="card-title">
Change Mailbox Address
</div>
<div class="form-group">
<label class="form-label">Address</label>
<!-- Not allow user to change mailbox if there's a pending change -->
{{ change_email_form.email(class="form-control", value=mailbox.email, readonly=pending_email != None) }}
{{ render_field_errors(change_email_form.email) }}
{% if pending_email %}
<div class="mt-2">
<span class="text-danger">Pending change: {{ pending_email }}</span>
<a href="{{ url_for('dashboard.cancel_mailbox_change_route', mailbox_id=mailbox.id) }}"
class="btn btn-secondary btn-sm">
Cancel mailbox change
</a>
</div>
{% endif %}
</div>
<button class="btn btn-primary">Change</button>
</div>
</form>
</div>
<!-- END Change email -->
</div>
{% endblock %}

View File

@ -43,9 +43,7 @@ def custom_alias():
)
)
mailboxes = [current_user.email]
for mailbox in Mailbox.query.filter_by(user_id=current_user.id, verified=True):
mailboxes.append(mailbox.email)
mailboxes = current_user.mailboxes()
if request.method == "POST":
alias_prefix = request.form.get("prefix")
@ -85,7 +83,8 @@ def custom_alias():
LOG.d("Set alias %s domain to %s", full_alias, custom_domain)
gen_email.custom_domain_id = custom_domain.id
if mailbox_email != current_user.email:
# assign alias to a mailbox
if current_user.full_mailbox or mailbox_email != current_user.email:
mailbox = Mailbox.get_by(email=mailbox_email)
gen_email.mailbox_id = mailbox.id
LOG.d("Set alias %s mailbox to %s", full_alias, mailbox)

View File

@ -74,6 +74,16 @@ def index():
gen_email = GenEmail.create_new_random(
user_id=current_user.id, scheme=scheme
)
if current_user.full_mailbox:
if not current_user.default_mailbox_id:
LOG.error(
"Full mailbox User %s does not have default mailbox ",
current_user,
)
else:
gen_email.mailbox_id = current_user.default_mailbox_id
db.session.commit()
LOG.d("generate new email %s for user %s", gen_email, current_user)
@ -142,6 +152,31 @@ def index():
)
)
elif request.form.get("form-name") == "set-mailbox":
gen_email_id = request.form.get("gen-email-id")
gen_email: GenEmail = GenEmail.get(gen_email_id)
mailbox_email = request.form.get("mailbox")
mailbox = Mailbox.get_by(email=mailbox_email)
if not mailbox or mailbox.user_id != current_user.id:
flash("Something went wrong, please retry", "warning")
else:
gen_email.mailbox_id = mailbox.id
db.session.commit()
LOG.d("Set alias %s mailbox to %s", gen_email, mailbox)
flash(
f"Update mailbox for {gen_email.email} to {mailbox_email}",
"success",
)
return redirect(
url_for(
"dashboard.index",
highlight_gen_email_id=gen_email.id,
query=query,
)
)
return redirect(url_for("dashboard.index", query=query))
client_users = (
@ -153,6 +188,8 @@ def index():
sorted(client_users, key=lambda cu: cu.client.name)
mailboxes = current_user.mailboxes()
return render_template(
"dashboard/index.html",
client_users=client_users,
@ -160,6 +197,7 @@ def index():
highlight_gen_email_id=highlight_gen_email_id,
query=query,
AliasGeneratorEnum=AliasGeneratorEnum,
mailboxes=mailboxes,
)

View File

@ -27,7 +27,7 @@ class NewMailboxForm(FlaskForm):
@dashboard_bp.route("/mailbox", methods=["GET", "POST"])
@login_required
def mailbox_route():
if not current_user.can_use_multiple_mailbox:
if not current_user.can_use_multiple_mailbox and not current_user.full_mailbox:
flash("You don't have access to this page, redirect to home page", "warning")
return redirect(url_for("dashboard.index"))
@ -44,11 +44,36 @@ def mailbox_route():
flash("Unknown error. Refresh the page", "warning")
return redirect(url_for("dashboard.mailbox_route"))
if mailbox.id == current_user.default_mailbox_id:
flash("You cannot delete default mailbox", "error")
return redirect(url_for("dashboard.mailbox_route"))
email = mailbox.email
Mailbox.delete(mailbox_id)
db.session.commit()
flash(f"Mailbox {email} has been deleted", "success")
return redirect(url_for("dashboard.mailbox_route"))
if request.form.get("form-name") == "set-default":
mailbox_id = request.form.get("mailbox-id")
mailbox = Mailbox.get(mailbox_id)
if not mailbox or mailbox.user_id != current_user.id:
flash("Unknown error. Refresh the page", "warning")
return redirect(url_for("dashboard.mailbox_route"))
if mailbox.id == current_user.default_mailbox_id:
flash("This mailbox is already default one", "error")
return redirect(url_for("dashboard.mailbox_route"))
if not mailbox.verified:
flash("Cannot set unverified mailbox as default", "error")
return redirect(url_for("dashboard.mailbox_route"))
current_user.default_mailbox_id = mailbox.id
db.session.commit()
flash(f"Mailbox {mailbox.email} is set as Default Mailbox", "success")
return redirect(url_for("dashboard.mailbox_route"))
elif request.form.get("form-name") == "create":

View File

@ -0,0 +1,150 @@
from flask import render_template, request, redirect, url_for, flash
from flask_login import login_required, current_user
from flask_wtf import FlaskForm
from itsdangerous import Signer, BadSignature
from wtforms import validators
from wtforms.fields.html5 import EmailField
from app.config import FLASK_SECRET
from app.config import URL
from app.dashboard.base import dashboard_bp
from app.email_utils import can_be_used_as_personal_email, email_already_used
from app.email_utils import (
send_email,
render,
)
from app.extensions import db
from app.log import LOG
from app.models import (
GenEmail,
DeletedAlias,
)
from app.models import Mailbox
class ChangeEmailForm(FlaskForm):
email = EmailField(
"email", validators=[validators.DataRequired(), validators.Email()]
)
@dashboard_bp.route("/mailbox/<int:mailbox_id>/", methods=["GET", "POST"])
@login_required
def mailbox_detail_route(mailbox_id):
mailbox = Mailbox.get(mailbox_id)
if not mailbox or mailbox.user_id != current_user.id:
flash("You cannot see this page", "warning")
return redirect(url_for("dashboard.index"))
change_email_form = ChangeEmailForm()
if mailbox.new_email:
pending_email = mailbox.new_email
else:
pending_email = None
if change_email_form.validate_on_submit():
new_email = change_email_form.email.data
if new_email != mailbox.email and not pending_email:
# check if this email is not already used
if (
email_already_used(new_email)
or GenEmail.get_by(email=new_email)
or DeletedAlias.get_by(email=new_email)
):
flash(f"Email {new_email} already used", "error")
elif not can_be_used_as_personal_email(new_email):
flash(
"You cannot use this email address as your mailbox", "error",
)
else:
mailbox.new_email = new_email
db.session.commit()
s = Signer(FLASK_SECRET)
mailbox_id_signed = s.sign(str(mailbox.id)).decode()
verification_url = (
URL
+ "/dashboard/mailbox/confirm_change"
+ f"?mailbox_id={mailbox_id_signed}"
)
send_email(
new_email,
f"Confirm mailbox change on SimpleLogin",
render(
"transactional/verify-mailbox-change.txt",
user=current_user,
link=verification_url,
mailbox_email=mailbox.email,
mailbox_new_email=new_email,
),
render(
"transactional/verify-mailbox-change.html",
user=current_user,
link=verification_url,
mailbox_email=mailbox.email,
mailbox_new_email=new_email,
),
)
flash(
f"You are going to receive an email to confirm {new_email}.",
"success",
)
return redirect(
url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
)
return render_template("dashboard/mailbox_detail.html", **locals(),)
@dashboard_bp.route(
"/mailbox/<int:mailbox_id>/cancel_email_change", methods=["GET", "POST"]
)
@login_required
def cancel_mailbox_change_route(mailbox_id):
mailbox = Mailbox.get(mailbox_id)
if not mailbox or mailbox.user_id != current_user.id:
flash("You cannot see this page", "warning")
return redirect(url_for("dashboard.index"))
if mailbox.new_email:
mailbox.new_email = None
db.session.commit()
flash("Your mailbox change is cancelled", "success")
return redirect(
url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
)
else:
flash("You have no pending mailbox change", "warning")
return redirect(
url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
)
@dashboard_bp.route("/mailbox/confirm_change")
def mailbox_confirm_change_route():
s = Signer(FLASK_SECRET)
mailbox_id = request.args.get("mailbox_id")
try:
r_id = int(s.unsign(mailbox_id))
except BadSignature:
flash("Invalid link", "error")
else:
mailbox = Mailbox.get(r_id)
mailbox.email = mailbox.new_email
mailbox.new_email = None
# mark mailbox as verified if the change request is sent from an unverified mailbox
mailbox.verified = True
db.session.commit()
LOG.d("Mailbox change %s is verified", mailbox)
flash(
f"The {mailbox.email} is updated", "success",
)
return redirect(
url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox.id)
)

View File

@ -185,6 +185,8 @@ def send_email(
)
return
LOG.d("send email to %s, subject %s", to_email, subject)
# host IP, setup via Docker network
smtp = SMTP(POSTFIX_SERVER, 25)
@ -214,11 +216,9 @@ def send_email(
msg["To"] = to_email
msg_id_header = make_msgid()
LOG.d("message-id %s", msg_id_header)
msg["Message-ID"] = msg_id_header
date_header = formatdate()
LOG.d("Date header: %s", date_header)
msg["Date"] = date_header
# add DKIM

View File

@ -137,6 +137,19 @@ class User(db.Model, ModelMixin, UserMixin):
db.Boolean, default=False, nullable=False, server_default="0"
)
# only use mailbox instead of default to user email
# this requires a migration before to:
# 1. create default mailbox for the user email address
# 2. assign existing aliases to this default mailbox
full_mailbox = db.Column(
db.Boolean, default=False, nullable=False, server_default="0"
)
# the mailbox used when create random alias
default_mailbox_id = db.Column(
db.ForeignKey("mailbox.id"), nullable=True, default=None
)
profile_picture = db.relationship(File)
@classmethod
@ -154,6 +167,14 @@ class User(db.Model, ModelMixin, UserMixin):
GenEmail.create_new(user.id, prefix="my-first-alias")
db.session.flush()
# todo: uncomment when all existing users are full_mailbox
# to run just after migrating all existing user to full mailbox
# so new users are automatically full-mailbox
# mb = Mailbox.create(user_id=user.id, email=user.email, verified=True)
# db.session.flush()
# user.full_mailbox = True
# user.default_mailbox_id = mb.id
# Schedule onboarding emails
Job.create(
name=JOB_ONBOARDING_1,
@ -270,6 +291,18 @@ class User(db.Model, ModelMixin, UserMixin):
def verified_custom_domains(self):
return CustomDomain.query.filter_by(user_id=self.id, verified=True).all()
def mailboxes(self) -> [str]:
"""list of mailbox emails that user own"""
if self.full_mailbox:
mailboxes = []
else:
mailboxes = [self.email]
for mailbox in Mailbox.query.filter_by(user_id=self.id, verified=True):
mailboxes.append(mailbox.email)
return mailboxes
def __repr__(self):
return f"<User {self.id} {self.name} {self.email}>"
@ -491,7 +524,7 @@ class GenEmail(db.Model, ModelMixin):
mailbox = db.relationship("Mailbox")
@classmethod
def create_new(cls, user_id, prefix, note=None):
def create_new(cls, user_id, prefix, note=None, mailbox_id=None):
if not prefix:
raise Exception("alias prefix cannot be empty")
@ -503,7 +536,9 @@ class GenEmail(db.Model, ModelMixin):
if not cls.get_by(email=email):
break
return GenEmail.create(user_id=user_id, email=email, note=note)
return GenEmail.create(
user_id=user_id, email=email, note=note, mailbox_id=mailbox_id
)
@classmethod
def create_new_random(
@ -828,7 +863,8 @@ class Mailbox(db.Model, ModelMixin):
email = db.Column(db.String(256), unique=True, nullable=False)
verified = db.Column(db.Boolean, default=False, nullable=False)
user = db.relationship(User)
# used when user wants to update mailbox email
new_email = db.Column(db.String(256), unique=True)
def nb_alias(self):
return GenEmail.filter_by(mailbox_id=self.id).count()

View File

@ -106,7 +106,7 @@
<div class="col-sm-6"
style="padding-left: 5px">
<select class="form-control" name="suffix">
<select class="form-control custom-select" name="suffix">
{% for suffix in suffixes %}
<option value="{{ suffix[1] }}">
{% if suffix[0] %}

View File

@ -0,0 +1,33 @@
"""empty message
Revision ID: 903ec5f566e8
Revises: 3fa3a648c8e7
Create Date: 2020-02-23 14:11:46.332532
"""
import sqlalchemy_utils
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '903ec5f566e8'
down_revision = '3fa3a648c8e7'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mailbox', sa.Column('new_email', sa.String(length=256), nullable=True))
op.create_unique_constraint(None, 'mailbox', ['new_email'])
op.add_column('users', sa.Column('full_mailbox', sa.Boolean(), server_default='0', nullable=False))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('users', 'full_mailbox')
op.drop_constraint(None, 'mailbox', type_='unique')
op.drop_column('mailbox', 'new_email')
# ### end Alembic commands ###

View File

@ -0,0 +1,31 @@
"""empty message
Revision ID: f580030d9beb
Revises: 903ec5f566e8
Create Date: 2020-02-23 16:03:46.064813
"""
import sqlalchemy_utils
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'f580030d9beb'
down_revision = '903ec5f566e8'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('users', sa.Column('default_mailbox_id', sa.Integer(), nullable=True))
op.create_foreign_key(None, 'users', 'mailbox', ['default_mailbox_id'], ['id'])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'users', type_='foreignkey')
op.drop_column('users', 'default_mailbox_id')
# ### end Alembic commands ###

View File

@ -132,6 +132,7 @@ def fake_data():
is_admin=True,
otp_secret="base32secret3232",
can_use_multiple_mailbox=True,
full_mailbox=True,
)
db.session.commit()
@ -156,9 +157,15 @@ def fake_data():
api_key = ApiKey.create(user_id=user.id, name="Firefox")
api_key.code = "codeFF"
GenEmail.create_new(user.id, "e1@")
GenEmail.create_new(user.id, "e2@")
GenEmail.create_new(user.id, "e3@")
m1 = Mailbox.create(user_id=user.id, email="m1@cd.ef", verified=True)
m2 = Mailbox.create(user_id=user.id, email="m2@zt.com", verified=False)
m3 = Mailbox.create(user_id=user.id, email="m3@cd.ef", verified=True)
db.session.commit()
user.default_mailbox_id = m1.id
GenEmail.create_new(user.id, "e1@", mailbox_id=m1.id)
GenEmail.create_new(user.id, "e2@", mailbox_id=m3.id)
CustomDomain.create(user_id=user.id, domain="ab.cd", verified=True)
CustomDomain.create(
@ -185,10 +192,6 @@ def fake_data():
client2.published = True
db.session.commit()
Mailbox.create(user_id=user.id, email="ab@cd.ef", verified=True)
Mailbox.create(user_id=user.id, email="xy@zt.com", verified=False)
db.session.commit()
DeletedAlias.create(user_id=user.id, email="d1@ab.cd")
DeletedAlias.create(user_id=user.id, email="d2@ab.cd")
db.session.commit()

View File

@ -40,6 +40,24 @@ def send_safari_extension_newsletter():
)
def convert_user_full_mailbox(user):
# create a default mailbox
default_mb = Mailbox.get_by(user_id=user.id, email=user.email)
if not default_mb:
LOG.d("create default mailbox for user %s", user)
default_mb = Mailbox.create(user_id=user.id, email=user.email, verified=True)
db.session.commit()
# assign existing alias to this mailbox
for gen_email in GenEmail.query.filter_by(user_id=user.id):
gen_email.mailbox_id = default_mb.id
# finally set user to full_mailbox
user.full_mailbox = True
user.default_mailbox_id = default_mb.id
db.session.commit()
app = create_app()
with app.app_context():

View File

@ -21,7 +21,7 @@
and create your <b>business emails</b> using email alias. This is cheaper and more convenient than buying a dedicated solution like GSuite. By the way, all our business emails are actually aliases.') }}
{% if user.in_trial() %}
{{ render_text('You can use all premium features like <em>custom domain</em> or <em>alias directory</em> during the <b>trial period</b>. Your trial will end ' + user.trial_end.humanize() + ".") }}
{{ render_text('You can use all premium features like <em>custom domain</em> or <em>alias directory</em> during the <b>trial period</b>. Your trial will end ' + user.trial_end.humanize() + ". All aliases you create during the trial will continue to work normally when the trial ends.") }}
{% endif %}
{{ render_text('In the next coming days, you are going to receive some onboarding emails to quickly present SimpleLogin features. If you don\'t want to receive these emails, you can disable them on <a href="https://app.simplelogin.io/dashboard/setting#notification">Settings</a>.') }}

View File

@ -22,6 +22,7 @@ and create your business emails backed by your personal email! By the way, all o
{% if user.in_trial() %}
You can use all premium features like custom domain or alias directory during the trial period.
Your trial will end {{ user.trial_end.humanize() }}.
All aliases you create during the trial will continue to work normally when the trial ends.
{% endif %}
If there's anything that's bugging you, even the smallest of issues that could be done better, I want to hear about it - so hit the reply button.

View File

@ -11,7 +11,7 @@
{{ render_text("When the trial ends:") }}
{{ render_text("- All aliases/domains/directories you have created are <b>kept</b> and continue working.") }}
{{ render_text("- All aliases/domains/directories you have created are <b>kept</b> and continue working normally.") }}
{{ render_text("- You cannot create new aliases if you exceed the free plan limit, i.e. have more than 5 aliases.") }}
{{ render_text("- As features like <b>catch-all</b> or <b>directory</b> allow you to create aliases on-the-fly, those aliases cannot be automatically created if you have more than 5 aliases.") }}
{{ render_text("- You cannot add new domain or directory.") }}

View File

@ -0,0 +1,11 @@
{% extends "base.html" %}
{% block content %}
{{ render_text("Hi " + user.name) }}
{{ render_text("You recently requested to change mailbox <b>"+ mailbox_email +"</b> to <b>" + mailbox_new_email + "</b>.") }}
{{ render_text("To confirm, please click on the button below.") }}
{{ render_button("Confirm mailbox change", link) }}
{{ render_text('Thanks, <br />SimpleLogin Team.') }}
{{ raw_url(link) }}
{% endblock %}

View File

@ -0,0 +1,10 @@
Hi {{user.name}}
You recently requested to change mailbox {{mailbox_email}} to {{mailbox_new_email}}
To confirm, please click on this link:
{{link}}
Regards,
Son - SimpleLogin founder.

View File

@ -29,7 +29,7 @@
</a>
</li>
{% if current_user.can_use_multiple_mailbox %}
{% if current_user.can_use_multiple_mailbox or current_user.full_mailbox %}
<li class="nav-item">
<a href="{{ url_for('dashboard.mailbox_route') }}"
class="nav-link {{ 'active' if active_page == 'mailbox' }}">