diff --git a/admin/mailu/admin/__init__.py b/admin/mailu/admin/__init__.py
index 77df2052..fc895134 100644
--- a/admin/mailu/admin/__init__.py
+++ b/admin/mailu/admin/__init__.py
@@ -28,5 +28,6 @@ from mailu.admin.views import \
aliases, \
users, \
domains, \
+ relays, \
alternatives, \
fetches
diff --git a/admin/mailu/admin/forms.py b/admin/mailu/admin/forms.py
index 41b924bf..b00f4dac 100644
--- a/admin/mailu/admin/forms.py
+++ b/admin/mailu/admin/forms.py
@@ -56,6 +56,13 @@ class AlternativeForm(flask_wtf.FlaskForm):
submit = fields.SubmitField(_('Create'))
+class RelayForm(flask_wtf.FlaskForm):
+ name = fields.StringField(_('Relayed domain name'), [validators.DataRequired()])
+ smtp = fields.StringField(_('Remote host'))
+ comment = fields.StringField(_('Comment'))
+ submit = fields.SubmitField(_('Create'))
+
+
class UserForm(flask_wtf.FlaskForm):
localpart = fields.StringField(_('E-mail'), [validators.DataRequired()])
pw = fields.PasswordField(_('Password'), [validators.DataRequired()])
diff --git a/admin/mailu/admin/templates/relay/create.html b/admin/mailu/admin/templates/relay/create.html
new file mode 100644
index 00000000..b22cb001
--- /dev/null
+++ b/admin/mailu/admin/templates/relay/create.html
@@ -0,0 +1,5 @@
+{% extends "form.html" %}
+
+{% block title %}
+{% trans %}New relay domain{% endtrans %}
+{% endblock %}
diff --git a/admin/mailu/admin/templates/relay/edit.html b/admin/mailu/admin/templates/relay/edit.html
new file mode 100644
index 00000000..83dafcba
--- /dev/null
+++ b/admin/mailu/admin/templates/relay/edit.html
@@ -0,0 +1,9 @@
+{% extends "form.html" %}
+
+{% block title %}
+{% trans %}Edit relayd domain{% endtrans %}
+{% endblock %}
+
+{% block subtitle %}
+{{ relay }}
+{% endblock %}
diff --git a/admin/mailu/admin/templates/relay/list.html b/admin/mailu/admin/templates/relay/list.html
new file mode 100644
index 00000000..ba72a227
--- /dev/null
+++ b/admin/mailu/admin/templates/relay/list.html
@@ -0,0 +1,39 @@
+{% extends "base.html" %}
+
+{% block title %}
+{% trans %}Relayed domain list{% endtrans %}
+{% endblock %}
+
+{% block main_action %}
+{% if current_user.global_admin %}
+{% trans %}New relayed domain{% endtrans %}
+{% endif %}
+{% endblock %}
+
+{% block box %}
+
+
+
+ {% trans %}Actions{% endtrans %} |
+ {% trans %}Domain name{% endtrans %} |
+ {% trans %}Remote host{% endtrans %} |
+ {% trans %}Comment{% endtrans %} |
+ {% trans %}Created{% endtrans %} |
+ {% trans %}Last edit{% endtrans %} |
+
+ {% for relay in relays %}
+
+
+
+
+ |
+ {{ relay.name }} |
+ {{ relay.smtp or '-' }} |
+ {{ relay.comment or '' }} |
+ {{ relay.created_at }} |
+ {{ relay.updated_at or '' }} |
+
+ {% endfor %}
+
+
+{% endblock %}
diff --git a/admin/mailu/admin/templates/sidebar.html b/admin/mailu/admin/templates/sidebar.html
index bd7c0439..14abe241 100644
--- a/admin/mailu/admin/templates/sidebar.html
+++ b/admin/mailu/admin/templates/sidebar.html
@@ -50,6 +50,11 @@
{% trans %}Administrators{% endtrans %}
+
+
+ {% trans %}Relayed domains{% endtrans %}
+
+
{% endif %}
{% if current_user.manager_of or current_user.global_admin %}
diff --git a/admin/mailu/admin/views/relays.py b/admin/mailu/admin/views/relays.py
new file mode 100644
index 00000000..73a9ad9d
--- /dev/null
+++ b/admin/mailu/admin/views/relays.py
@@ -0,0 +1,60 @@
+from mailu.admin import app, db, models, forms, access
+from mailu import app as flask_app
+
+import flask
+import wtforms_components
+
+
+@app.route('/relay', methods=['GET'])
+@access.global_admin
+def relay_list():
+ relays = models.Relay.query.all()
+ return flask.render_template('relay/list.html', relays=relays)
+
+
+@app.route('/relay/create', methods=['GET', 'POST'])
+@access.global_admin
+def relay_create():
+ form = forms.RelayForm()
+ if form.validate_on_submit():
+ conflicting_domain = models.Domain.query.get(form.name.data)
+ conflicting_alternative = models.Alternative.query.get(form.name.data)
+ conflicting_relay = models.Relay.query.get(form.name.data)
+ if conflicting_domain or conflicting_alternative or conflicting_relay:
+ flask.flash('Domain %s is already used' % form.name.data, 'error')
+ else:
+ relay = models.Relay()
+ form.populate_obj(relay)
+ db.session.add(relay)
+ db.session.commit()
+ flask.flash('Relayed domain %s created' % relay)
+ return flask.redirect(flask.url_for('.relay_list'))
+ return flask.render_template('relay/create.html', form=form)
+
+
+@app.route('/relay/edit/', methods=['GET', 'POST'])
+@access.global_admin
+def relay_edit(relay_name):
+ relay = models.Relay.query.get(relay_name) or flask.abort(404)
+ form = forms.RelayForm(obj=relay)
+ wtforms_components.read_only(form.name)
+ form.name.validators = []
+ if form.validate_on_submit():
+ form.populate_obj(relay)
+ db.session.commit()
+ flask.flash('Relayed domain %s saved' % relay)
+ return flask.redirect(flask.url_for('.relay_list'))
+ return flask.render_template('relay/edit.html', form=form,
+ relay=relay)
+
+
+@app.route('/relay/delete/', methods=['GET', 'POST'])
+@access.global_admin
+@access.confirmation_required("delete {relay_name}")
+def relay_delete(relay_name):
+ relay = models.Relay.query.get(relay_name) or flask.abort(404)
+ db.session.delete(relay)
+ db.session.commit()
+ flask.flash('Relayed domain %s deleted' % relay)
+ return flask.redirect(flask.url_for('.relay_list'))
+