From 3a4703b7641431601e005e9de5187b7ae35ca3f5 Mon Sep 17 00:00:00 2001 From: Pierre Jaury Date: Wed, 20 Apr 2016 21:20:02 +0200 Subject: [PATCH] Implement auto-forward and auto-reply --- admin/freeposte/admin/forms.py | 6 ++++- .../admin/templates/user/forward.html | 15 +++++++++++- .../freeposte/admin/templates/user/reply.html | 17 +++++++++++++- admin/freeposte/admin/views/users.py | 5 +++- dovecot/Dockerfile | 3 +++ dovecot/conf/dovecot.conf | 7 ++++++ dovecot/conf/pigeonhole-sieve.dict | 22 ++++++++++++++++++ ...dovecot-pigeonhole-plugin-extdata-1-r0.apk | Bin 0 -> 26808 bytes dovecot/sieve/before.sieve | 14 +++++++++++ 9 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 dovecot/conf/pigeonhole-sieve.dict create mode 100644 dovecot/dovecot-pigeonhole-plugin-extdata-1-r0.apk diff --git a/admin/freeposte/admin/forms.py b/admin/freeposte/admin/forms.py index 06d12a11..b6ad7414 100644 --- a/admin/freeposte/admin/forms.py +++ b/admin/freeposte/admin/forms.py @@ -42,11 +42,15 @@ class UserPasswordForm(Form): class UserForwardForm(Form): - forward = fields.StringField('Destination', [validators.Email()]) + forward_enabled = fields.BooleanField('Enable forwarding', default=False) + forward = fields.StringField( + 'Destination', [validators.Optional(), validators.Email()] + ) submit = fields.SubmitField('Update') class UserReplyForm(Form): + reply_enabled = fields.BooleanField('Enable reply', default=False) reply_subject = fields.StringField('Reply subject') reply_body = fields.StringField('Reply body', widget=widgets.TextArea()) submit = fields.SubmitField('Update') diff --git a/admin/freeposte/admin/templates/user/forward.html b/admin/freeposte/admin/templates/user/forward.html index 94f38ee7..efd0f672 100644 --- a/admin/freeposte/admin/templates/user/forward.html +++ b/admin/freeposte/admin/templates/user/forward.html @@ -1,4 +1,4 @@ -{% extends "form.html" %} +{% extends "base.html" %} {% block title %} Forward emails @@ -7,3 +7,16 @@ Forward emails {% block subtitle %} {{ user }} {% endblock %} + +{% block box_content %} +
+ {{ form.hidden_tag() }} + {{ macros.form_field(form.forward_enabled, + onchange="if(this.checked){$('#forward').removeAttr('disabled')} + else{$('#forward').attr('disabled', '').val('')}", + **{("checked" if user.forward else "unchecked"): ""}) }} + {{ macros.form_field(form.forward, + **{("enabled" if user.forward else "disabled"): ""}) }} + {{ macros.form_field(form.submit) }} +
+{% endblock %} diff --git a/admin/freeposte/admin/templates/user/reply.html b/admin/freeposte/admin/templates/user/reply.html index 50864129..4a0a6153 100644 --- a/admin/freeposte/admin/templates/user/reply.html +++ b/admin/freeposte/admin/templates/user/reply.html @@ -1,4 +1,4 @@ -{% extends "form.html" %} +{% extends "base.html" %} {% block title %} Automatic reply @@ -7,3 +7,18 @@ Automatic reply {% block subtitle %} {{ user }} {% endblock %} + +{% block box_content %} +
+ {{ form.hidden_tag() }} + {{ macros.form_field(form.reply_enabled, + onchange="if(this.checked){$('#reply_subject,#reply_body').removeAttr('disabled')} + else{$('#reply_subject,#reply_body').attr('disabled', '').val('')}", + **{("checked" if user.forward else "unchecked"): ""}) }} + {{ macros.form_field(form.reply_subject, + **{("enabled" if user.reply_subject else "disabled"): ""}) }} + {{ macros.form_field(form.reply_body, rows=10, + **{("enabled" if user.reply_subject else "disabled"): ""}) }} + {{ macros.form_field(form.submit) }} +
+{% endblock %} diff --git a/admin/freeposte/admin/views/users.py b/admin/freeposte/admin/views/users.py index 048bc93a..2511e9a2 100644 --- a/admin/freeposte/admin/views/users.py +++ b/admin/freeposte/admin/views/users.py @@ -120,7 +120,10 @@ def user_forward(user_email): user = utils.get_user(user_email) form = forms.UserForwardForm(obj=user) if form.validate_on_submit(): - user.forward = form.forward.data + if form.forward_enabled: + user.forward = form.forward.data + else: + user.forward = None db.session.add(user) db.session.commit() flask.flash('Forward destination updated for %s' % user) diff --git a/dovecot/Dockerfile b/dovecot/Dockerfile index 5884c1eb..88a1d903 100644 --- a/dovecot/Dockerfile +++ b/dovecot/Dockerfile @@ -9,6 +9,9 @@ RUN echo "@testing http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/re dovecot-antispam-plugin@testing \ && rm -rf /var/cache/apk/* +COPY dovecot-pigeonhole-plugin-extdata-1-r0.apk / +RUN apk add --allow-untrusted --force dovecot-pigeonhole-plugin-extdata-1-r0.apk + COPY conf /etc/dovecot COPY sieve /var/lib/dovecot diff --git a/dovecot/conf/dovecot.conf b/dovecot/conf/dovecot.conf index ab3b4768..9e740549 100644 --- a/dovecot/conf/dovecot.conf +++ b/dovecot/conf/dovecot.conf @@ -15,6 +15,10 @@ service dict { } } +dict { + sieve = sqlite:/etc/dovecot/pigeonhole-sieve.dict +} + ############### # Mailboxes ############### @@ -137,7 +141,10 @@ service managesieve-login { plugin { sieve_dir = ~/sieve + sieve_plugins = sieve_extdata + sieve_extensions = +vnd.dovecot.extdata sieve_before = /var/lib/dovecot/before.sieve sieve_default = /var/lib/dovecot/default.sieve sieve_after = /var/lib/dovecot/after.sieve + sieve_extdata_dict_uri = proxy::sieve } diff --git a/dovecot/conf/pigeonhole-sieve.dict b/dovecot/conf/pigeonhole-sieve.dict new file mode 100644 index 00000000..62f5c410 --- /dev/null +++ b/dovecot/conf/pigeonhole-sieve.dict @@ -0,0 +1,22 @@ +connect = /data/freeposte.db + +map { + pattern = priv/reply_subject + table = user + username_field = address + value_field = reply_subject +} + +map { + pattern = priv/reply_body + table = user + username_field = address + value_field = reply_body +} + +map { + pattern = priv/forward + table = user + username_field = address + value_field = forward +} diff --git a/dovecot/dovecot-pigeonhole-plugin-extdata-1-r0.apk b/dovecot/dovecot-pigeonhole-plugin-extdata-1-r0.apk new file mode 100644 index 0000000000000000000000000000000000000000..12ad1a864f95b6e85cc0eb75f71f43284aaef6f4 GIT binary patch literal 26808 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