diff --git a/core/admin/mailu/models.py b/core/admin/mailu/models.py index 901054a8..48f7b91e 100644 --- a/core/admin/mailu/models.py +++ b/core/admin/mailu/models.py @@ -60,6 +60,7 @@ class Domain(Base): max_users = db.Column(db.Integer, nullable=False, default=0) max_aliases = db.Column(db.Integer, nullable=False, default=0) max_quota_bytes = db.Column(db.Integer(), nullable=False, default=0) + signup_enabled = db.Column(db.Boolean(), nullable=False, default=False) @property def dkim_key(self): diff --git a/core/admin/mailu/ui/forms.py b/core/admin/mailu/ui/forms.py index 451db811..1d27141d 100644 --- a/core/admin/mailu/ui/forms.py +++ b/core/admin/mailu/ui/forms.py @@ -47,6 +47,7 @@ class DomainForm(flask_wtf.FlaskForm): max_users = fields_.IntegerField(_('Maximum user count'), default=10) max_aliases = fields_.IntegerField(_('Maximum alias count'), default=10) max_quota_bytes = fields_.IntegerSliderField(_('Maximum user quota'), default=0) + signup_enabled = fields.BooleanField(_('Enable sign-up'), default=False) comment = fields.StringField(_('Comment')) submit = fields.SubmitField(_('Create')) @@ -74,6 +75,13 @@ class UserForm(flask_wtf.FlaskForm): submit = fields.SubmitField(_('Save')) +class UserSignupForm(flask_wtf.FlaskForm): + localpart = fields.StringField(_('Email address')) + pw = fields.PasswordField(_('Password'), [validators.DataRequired()]) + pw2 = fields.PasswordField(_('Confirm password'), [validators.EqualTo('pw')]) + submit = fields.SubmitField(_('Sign up')) + + class UserSettingsForm(flask_wtf.FlaskForm): displayed_name = fields.StringField(_('Displayed name')) spam_enabled = fields.BooleanField(_('Enable spam filter')) diff --git a/core/admin/mailu/ui/templates/sidebar.html b/core/admin/mailu/ui/templates/sidebar.html index 22bbfce5..d107c15f 100644 --- a/core/admin/mailu/ui/templates/sidebar.html +++ b/core/admin/mailu/ui/templates/sidebar.html @@ -100,10 +100,17 @@ {% else %}
  • - + {% trans %}Sign in{% endtrans %}
  • + {% if signup_domains %} +
  • + + {% trans %}Sign up{% endtrans %} + +
  • + {% endif %} {% endif %} diff --git a/core/admin/mailu/ui/templates/user/signup.html b/core/admin/mailu/ui/templates/user/signup.html new file mode 100644 index 00000000..eccd9f67 --- /dev/null +++ b/core/admin/mailu/ui/templates/user/signup.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} + +{% block title %} +{% trans %}Sign up{% endtrans %} +{% endblock %} + +{% block subtitle %} +{{ domain }} +{% endblock %} + +{% block content %} +
    + {{ form.hidden_tag() }} + {% call macros.box() %} + {{ macros.form_field(form.localpart, append='@'+domain.name+'') }} + {{ macros.form_fields((form.pw, form.pw2)) }} + {{ macros.form_field(form.submit) }} + {% endcall %} +
    +{% endblock %} diff --git a/core/admin/mailu/ui/templates/user/signup_domain.html b/core/admin/mailu/ui/templates/user/signup_domain.html new file mode 100644 index 00000000..d5df77dd --- /dev/null +++ b/core/admin/mailu/ui/templates/user/signup_domain.html @@ -0,0 +1,26 @@ +{% extends "base.html" %} + +{% block title %} +{% trans %}Sign up{% endtrans %} +{% endblock %} + +{% block subtitle %} +{% trans %}pick a domain for the new account{% endtrans %} +{% endblock %} + +{% block content %} +{% call macros.table() %} + + {% trans %}Domain{% endtrans %} + {% trans %}Available slots{% endtrans %} + {% trans %}Quota{% endtrans %} + +{% for domain_name, domain in available_domains.items() %} + + {{ domain_name }} + {{ domain.max_users - domain.users if domain.max_users else '∞' }} + {{ domain.max_quota_bytes or config['DEFAULT_QUOTA'] | filesizeformat }} + +{% endfor %} +{% endcall %} +{% endblock %} diff --git a/core/admin/mailu/ui/views/users.py b/core/admin/mailu/ui/views/users.py index 34f7be8f..77499699 100644 --- a/core/admin/mailu/ui/views/users.py +++ b/core/admin/mailu/ui/views/users.py @@ -153,3 +153,35 @@ def user_reply(user_email): return flask.redirect( flask.url_for('.user_list', domain_name=user.domain.name)) return flask.render_template('user/reply.html', form=form, user=user) + + +@ui.route('/user/signup', methods=['GET', 'POST']) +@ui.route('/user/signup/', methods=['GET', 'POST']) +def user_signup(domain_name=None): + available_domains = { + domain.name: domain + for domain in models.Domain.query.filter_by(signup_enabled=True).all() + if not domain.max_users or len(domain.users) < domain.max_users + } + if not available_domains: + flask.flash('No domain available for registration') + if not domain_name: + return flask.render_template('user/signup_domain.html', + available_domains=available_domains) + domain = available_domains.get(domain_name) or flask.abort(404) + quota_bytes = min(config['DEFAULT_QUOTA'], domain.max_quota_bytes) + form = forms.UserSignupForm() + if form.validate_on_submit(): + if domain.has_email(form.localpart.data): + flask.flash('Email is already used', 'error') + else: + user = models.User(domain=domain) + form.populate_obj(user) + user.set_password(form.pw.data) + user.quota_bytes = quota_bytes + db.session.add(user) + db.session.commit() + user.send_welcome() + flask.flash('Successfully signed up %s' % user) + return flask.redirect(flask.url_for('.index')) + return flask.render_template('user/signup.html', domain=domain, form=form)