Implement a maximum quota per domain, fixes #106

master
Pierre Jaury 8 years ago
parent 18834de56e
commit 38f291bc5d

@ -46,6 +46,7 @@ class DomainForm(flask_wtf.FlaskForm):
name = fields.StringField(_('Domain name'), [validators.DataRequired()]) name = fields.StringField(_('Domain name'), [validators.DataRequired()])
max_users = fields_.IntegerField(_('Maximum user count'), default=10) max_users = fields_.IntegerField(_('Maximum user count'), default=10)
max_aliases = fields_.IntegerField(_('Maximum alias count'), default=10) max_aliases = fields_.IntegerField(_('Maximum alias count'), default=10)
max_quota_bytes = fields_.IntegerSliderField(_('Maximum user quota'), default=0)
comment = fields.StringField(_('Comment')) comment = fields.StringField(_('Comment'))
submit = fields.SubmitField(_('Create')) submit = fields.SubmitField(_('Create'))

@ -57,6 +57,7 @@ class Domain(Base):
backref=db.backref('manager_of'), lazy='dynamic') backref=db.backref('manager_of'), lazy='dynamic')
max_users = db.Column(db.Integer, nullable=False, default=0) max_users = db.Column(db.Integer, nullable=False, default=0)
max_aliases = 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)
@property @property
def dkim_key(self): def dkim_key(self):

@ -9,6 +9,9 @@
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
{{ macros.form_field(form.name) }} {{ macros.form_field(form.name) }}
{{ macros.form_fields((form.max_users, form.max_aliases)) }} {{ macros.form_fields((form.max_users, form.max_aliases)) }}
{{ macros.form_field(form.max_quota_bytes, step=1000000000, max=50000000000,
prepend='<span class="input-group-addon"><span id="quota">'+((form.max_quota_bytes.data//1000000000).__str__() if form.max_quota_bytes.data else '∞')+'</span> GiB</span>',
oninput='$("#quota").text(this.value == 0 ? "∞" : this.value/1000000000);') }}
{{ macros.form_field(form.comment) }} {{ macros.form_field(form.comment) }}
{{ macros.form_field(form.submit) }} {{ macros.form_field(form.submit) }}
</form> </form>

@ -13,7 +13,7 @@
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
{{ macros.form_field(form.localpart, append='<span class="input-group-addon">@'+domain.name+'</span>') }} {{ macros.form_field(form.localpart, append='<span class="input-group-addon">@'+domain.name+'</span>') }}
{{ macros.form_fields((form.pw, form.pw2)) }} {{ macros.form_fields((form.pw, form.pw2)) }}
{{ macros.form_field(form.quota_bytes, step=1000000000, max=50000000000, {{ macros.form_field(form.quota_bytes, step=1000000000, max=(max_quota_bytes or domain.max_quota_bytes or 50000000000),
prepend='<span class="input-group-addon"><span id="quota">'+(form.quota_bytes.data//1000000000).__str__()+'</span> GiB</span>', prepend='<span class="input-group-addon"><span id="quota">'+(form.quota_bytes.data//1000000000).__str__()+'</span> GiB</span>',
oninput='$("#quota").text(this.value/1000000000);') }} oninput='$("#quota").text(this.value/1000000000);') }}
{{ macros.form_field(form.enable_imap) }} {{ macros.form_field(form.enable_imap) }}

@ -2,6 +2,7 @@ from mailu.admin import app, db, models, forms, access
import flask import flask
import flask_login import flask_login
import wtforms
import wtforms_components import wtforms_components
@ -21,6 +22,8 @@ def user_create(domain_name):
return flask.redirect( return flask.redirect(
flask.url_for('.user_list', domain_name=domain.name)) flask.url_for('.user_list', domain_name=domain.name))
form = forms.UserForm() form = forms.UserForm()
form.quota_bytes.validators = [
wtforms.validators.NumberRange(max=domain.max_quota_bytes)]
if form.validate_on_submit(): if form.validate_on_submit():
if domain.has_email(form.localpart.data): if domain.has_email(form.localpart.data):
flask.flash('Email is already used', 'error') flask.flash('Email is already used', 'error')
@ -41,10 +44,17 @@ def user_create(domain_name):
@access.domain_admin(models.User, 'user_email') @access.domain_admin(models.User, 'user_email')
def user_edit(user_email): def user_edit(user_email):
user = models.User.query.get(user_email) or flask.abort(404) user = models.User.query.get(user_email) or flask.abort(404)
# Handle the case where user quota is more than allowed
max_quota_bytes = user.domain.max_quota_bytes
if max_quota_bytes and user.quota_bytes > max_quota_bytes:
max_quota_bytes = user.quota_bytes
# Create the form
form = forms.UserForm(obj=user) form = forms.UserForm(obj=user)
wtforms_components.read_only(form.localpart) wtforms_components.read_only(form.localpart)
form.pw.validators = [] form.pw.validators = []
form.localpart.validators = [] form.localpart.validators = []
form.quota_bytes.validators = [
wtforms.validators.NumberRange(max=max_quota_bytes)]
if form.validate_on_submit(): if form.validate_on_submit():
form.populate_obj(user) form.populate_obj(user)
if form.pw.data: if form.pw.data:
@ -53,7 +63,8 @@ def user_edit(user_email):
flask.flash('User %s updated' % user) flask.flash('User %s updated' % user)
return flask.redirect( return flask.redirect(
flask.url_for('.user_list', domain_name=user.domain.name)) flask.url_for('.user_list', domain_name=user.domain.name))
return flask.render_template('user/edit.html', form=form, user=user, domain=user.domain) return flask.render_template('user/edit.html', form=form, user=user,
domain=user.domain, max_quota_bytes=max_quota_bytes)
@app.route('/user/delete/<user_email>', methods=['GET', 'POST']) @app.route('/user/delete/<user_email>', methods=['GET', 'POST'])

@ -0,0 +1,23 @@
""" Add a maximum quota per domain
Revision ID: 2335c80a6bc3
Revises: 12e9a4f6ed73
Create Date: 2016-12-04 12:57:37.576622
"""
# revision identifiers, used by Alembic.
revision = '2335c80a6bc3'
down_revision = '12e9a4f6ed73'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column('domain', sa.Column('max_quota_bytes', sa.Integer(), nullable=False, server_default='0'))
def downgrade():
with op.batch_alter_table('domain') as batch:
batch.drop_column('max_quota_bytes')
Loading…
Cancel
Save