From 70b5f728a0e48d596251edcbed63c23430bdc3f2 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 21 Nov 2013 14:36:51 +0100 Subject: [PATCH] Administrators can now control auto-reply message states. see #432 --- doc/extending/events.rst | 56 +++++++++++++++---- doc/getting_started/upgrade.rst | 3 +- modoboa/core/__init__.py | 4 +- modoboa/core/templatetags/core_tags.py | 4 +- modoboa/extensions/admin/forms/account.py | 13 ++++- .../admin/templates/admin/mailform.html | 1 + .../postfix_autoreply/app_settings.py | 23 ++++++++ .../postfix_autoreply/general_callbacks.py | 51 +++++++++++++++++ modoboa/extensions/postfix_autoreply/views.py | 4 -- modoboa/lib/formutils.py | 3 +- modoboa/lib/templatetags/lib_tags.py | 10 ++++ 11 files changed, 149 insertions(+), 23 deletions(-) diff --git a/doc/extending/events.rst b/doc/extending/events.rst index 69987bd59..8ffa83b55 100644 --- a/doc/extending/events.rst +++ b/doc/extending/events.rst @@ -129,18 +129,6 @@ Raised when an existing account is modified. * ``newaccount`` is the account after the modification -ExtraAccountActions -=================== - -Raised when the account list is displayed. Let developers define new -actions to act on a specific user. - -*Callback prototype*:: - - def callback(account): pass - -* ``account`` is the account being listed - AdminMenuDisplay ================ @@ -298,6 +286,18 @@ Raised just after an extension has been activated. * ``extension`` is an ``Extension`` instance +ExtraAccountActions +=================== + +Raised when the account list is displayed. Let developers define new +actions to act on a specific user. + +*Callback prototype*:: + + def callback(account): pass + +* ``account`` is the account being listed + ExtraAccountForm ================ @@ -425,6 +425,25 @@ least three keys:: "label": "", "url": ""} +.. _extraformfields: + +ExtraFormFields +=============== + +Raised to request extra fields to include in a django form. + +*Callback prototype*:: + + def callback(form_name, instance=None): pass + +* ``form_name`` is a string used to distinguish a specific form +* ``instance`` is a django model instance related to ``form_name`` + +Must return a list of 2uple, each one containing the following +information:: + + ('field name', ) + ExtraRelayDomainForm ==================== @@ -841,6 +860,19 @@ Raised when the role of an account is about to be changed. * ``account`` is the account being modified * ``role`` is the new role (string) +SaveExtraFormFields +=================== + +Raised to save extra fields declared using :ref:`extraformfields`. + +*Callback prototype*:: + + def callback(form_name, instance, values): pass + +* ``form_name`` is a string used to distinguish a specific form +* ``instance`` is a django model instance related to ``form_name`` +* ``values`` is a dictionary containing the form's values + UserCanSetRole ============== diff --git a/doc/getting_started/upgrade.rst b/doc/getting_started/upgrade.rst index d5c6e1583..0e968d949 100644 --- a/doc/getting_started/upgrade.rst +++ b/doc/getting_started/upgrade.rst @@ -53,8 +53,7 @@ Finally, follow the common procedure:: 1.1.0: FIXME ============ -#. Add ``'modoboa.extensions.postfix_relay_domains'`` to - ``MODOBOA_APPS`` +#. Add ``'modoboa.extensions.postfix_relay_domains'`` to ``MODOBOA_APPS`` Due to code refactoring, some modifications need to be done into :file:`settings.py`: diff --git a/modoboa/core/__init__.py b/modoboa/core/__init__.py index 5e010631a..db00fdef5 100644 --- a/modoboa/core/__init__.py +++ b/modoboa/core/__init__.py @@ -35,7 +35,9 @@ base_events = [ "ExtraUprefsRoutes", "ExtraUprefsJS", - "GetExtraParameters" + "GetExtraParameters", + "ExtraFormFields", + "SaveExtraFormFields" ] diff --git a/modoboa/core/templatetags/core_tags.py b/modoboa/core/templatetags/core_tags.py index 2ae750a6d..e1b5ebce1 100644 --- a/modoboa/core/templatetags/core_tags.py +++ b/modoboa/core/templatetags/core_tags.py @@ -136,7 +136,9 @@ def tohtml(message): @register.simple_tag def visirule(field): - if not hasattr(field.form, "visirules") or not field.html_name in field.form.visirules: + if not hasattr(field, 'form') or \ + not hasattr(field.form, "visirules") or \ + not field.html_name in field.form.visirules: return "" rule = field.form.visirules[field.html_name] return " data-visibility-field='%s' data-visibility-value='%s' " \ diff --git a/modoboa/extensions/admin/forms/account.py b/modoboa/extensions/admin/forms/account.py index 476a3b9d4..674818b3e 100644 --- a/modoboa/extensions/admin/forms/account.py +++ b/modoboa/extensions/admin/forms/account.py @@ -6,7 +6,7 @@ from modoboa.lib.exceptions import PermDeniedException, Conflict, NotFound from modoboa.lib.permissions import get_account_roles from modoboa.lib.emailutils import split_mailbox from modoboa.lib.formutils import ( - DomainNameField, DynamicForm, TabForms + DomainNameField, DynamicForm, TabForms, YesNoField ) from modoboa.core.models import User from modoboa.extensions.admin.models import ( @@ -132,8 +132,14 @@ class AccountFormMail(forms.Form, DynamicForm): if "instance" in kwargs: self.mb = kwargs["instance"] del kwargs["instance"] + else: + self.mb = None super(AccountFormMail, self).__init__(*args, **kwargs) - if hasattr(self, "mb") and self.mb is not None: + self.extra_fields = [] + for fname, field in events.raiseQueryEvent('ExtraFormFields', 'mailform', self.mb): + self.fields[fname] = field + self.extra_fields.append(fname) + if self.mb is not None: self.fields["email"].required = True cpt = 1 for alias in self.mb.alias_set.all(): @@ -213,6 +219,9 @@ class AccountFormMail(forms.Form, DynamicForm): self.create_mailbox(user, account) else: self.update_mailbox(user, account) + events.raiseEvent( + 'SaveExtraFormFields', 'mailform', self.mb, self.cleaned_data + ) account.email = self.cleaned_data["email"] account.save() diff --git a/modoboa/extensions/admin/templates/admin/mailform.html b/modoboa/extensions/admin/templates/admin/mailform.html index 1b9374782..8660bee27 100644 --- a/modoboa/extensions/admin/templates/admin/mailform.html +++ b/modoboa/extensions/admin/templates/admin/mailform.html @@ -1,4 +1,5 @@ {% load lib_tags %} {% render_field form.email %} {% include "common/generic_field.html" with field=form.quota help_display_mode="tooltip" appended_text=_("MB") activator=True activator_value=form.quota_act.value deactivate_if_empty=False activator_label=_("Use domain default value") %} +{% render_extra_fields form %} {% render_fields_group form "aliases" %} diff --git a/modoboa/extensions/postfix_autoreply/app_settings.py b/modoboa/extensions/postfix_autoreply/app_settings.py index 1aec411a4..aba9535e3 100644 --- a/modoboa/extensions/postfix_autoreply/app_settings.py +++ b/modoboa/extensions/postfix_autoreply/app_settings.py @@ -16,3 +16,26 @@ class ParametersForm(AdminParametersForm): help_text=ugettext_lazy("Timeout in seconds between two auto-replies to the same recipient") ) + default_subject = forms.CharField( + label=ugettext_lazy("Default subject"), + initial=ugettext_lazy("I'm off"), + help_text=ugettext_lazy( + "Default subject used when an auto-reply message is created automatically" + ) + ) + + default_content = forms.CharField( + label=ugettext_lazy("Default content"), + initial=ugettext_lazy( + """I'm currently off. I'll answer as soon as I come back. + +Best regards, +%(name)s +"""), + help_text=ugettext_lazy( + "Default content user when an auto-reply message is created " + "automatically. The '%(name)s' macro will be replaced by the " + "user's full name." + ), + widget=forms.widgets.Textarea + ) diff --git a/modoboa/extensions/postfix_autoreply/general_callbacks.py b/modoboa/extensions/postfix_autoreply/general_callbacks.py index 5c6e005b7..192345d44 100644 --- a/modoboa/extensions/postfix_autoreply/general_callbacks.py +++ b/modoboa/extensions/postfix_autoreply/general_callbacks.py @@ -1,6 +1,8 @@ +from django.utils import timezone from django.utils.translation import ugettext_lazy from modoboa.lib import events, parameters from modoboa.extensions.postfix_autoreply.models import Transport, Alias +from .models import ARmessage @events.observe("ExtraUprefsJS") @@ -91,3 +93,52 @@ def onModifyMailbox(mailbox, oldmailbox): alias.autoreply_address = \ "%s@autoreply.%s" % (mailbox.full_address, mailbox.domain.name) alias.save() + + +@events.observe("ExtraFormFields") +def extra_mailform_fields(form_name, mailbox=None): + """Define extra fields to include in mail forms. + + For now, only the auto-reply state can be modified. + + :param str form_name: form name (must be 'mailform') + :param Mailbox mailbox: mailbox + """ + from modoboa.lib.formutils import YesNoField + + if form_name != "mailform": + return [] + status = False + if mailbox is not None and mailbox.armessage_set.count(): + status = mailbox.armessage_set.all()[0].enabled + return [ + ('autoreply', YesNoField( + label=ugettext_lazy("Enable auto-reply"), + initial="yes" if status else "no", + help_text=ugettext_lazy("Enable or disable Postfix auto-reply") + )) + ] + + +@events.observe("SaveExtraFormFields") +def save_extra_mailform_fields(form_name, mailbox, values): + """Set the auto-reply status for a mailbox. + + If a corresponding auto-reply message exists, we update its + status. Otherwise, we create a message using default values. + + :param str form_name: form name (must be 'mailform') + :param Mailbox mailbox: mailbox + :param dict values: form values + """ + if form_name != 'mailform': + return + if mailbox.armessage_set.count(): + arm = mailbox.armessage_set.all()[0] + else: + arm = ARmessage(mbox=mailbox) + arm.subject = parameters.get_admin("DEFAULT_SUBJECT") + arm.content = parameters.get_admin("DEFAULT_CONTENT") + arm.fromdate = timezone.now() + arm.enabled = True if values['autoreply'] == 'yes' else False + arm.save() diff --git a/modoboa/extensions/postfix_autoreply/views.py b/modoboa/extensions/postfix_autoreply/views.py index 4e693ee79..0351a8c18 100644 --- a/modoboa/extensions/postfix_autoreply/views.py +++ b/modoboa/extensions/postfix_autoreply/views.py @@ -39,10 +39,6 @@ def autoreply(request, tplname="postfix_autoreply/autoreply.html"): ) form = ARmessageForm(instance=arm) - # if arm is not None: - # form.fields['untildate'].initial = arm.untildate - # else: - # form.fields['untildate'].initial = date.today() return render_to_json_response({ "content": _render_to_string(request, tplname, {"form": form}), "onload_cb": "autoreply_cb" diff --git a/modoboa/lib/formutils.py b/modoboa/lib/formutils.py index f74fbec32..1b05abe0f 100644 --- a/modoboa/lib/formutils.py +++ b/modoboa/lib/formutils.py @@ -1,8 +1,9 @@ # coding: utf-8 import re from django.utils.translation import ugettext as _, ugettext_lazy +from django.forms import ChoiceField from django.forms.widgets import RadioSelect, RadioInput -from django.forms.fields import CharField, Field, ChoiceField +from django.forms.fields import CharField, Field from django.core.exceptions import ValidationError from django.utils.encoding import force_unicode from django.utils.safestring import mark_safe diff --git a/modoboa/lib/templatetags/lib_tags.py b/modoboa/lib/templatetags/lib_tags.py index dce73a1b3..930d2b3b8 100644 --- a/modoboa/lib/templatetags/lib_tags.py +++ b/modoboa/lib/templatetags/lib_tags.py @@ -94,6 +94,16 @@ def render_fields_group(form, pattern): )) +@register.simple_tag +def render_extra_fields(form): + result = '' + for fname in form.extra_fields: + result += render_to_string("common/generic_field.html", { + 'field': form[fname], 'help_display_mode': 'tooltip' + }) + return result + + @register.simple_tag def pagination_bar(page): return render_to_string("common/pagination_bar.html", dict(