wip: Per-domain policy support

* The domain edition form was converted to a "TabsForm"
* New events added : "ExtraDomainForm" and "FillDomainInstances"
* Creation and modification of policies work

see #260
This commit is contained in:
Antoine Nguyen
2012-06-07 09:31:51 +02:00
parent 069f90a1bd
commit 4f9bc5ea27
10 changed files with 174 additions and 66 deletions
+30 -4
View File
@@ -10,7 +10,7 @@ from modoboa.lib.permissions import get_account_roles
from modoboa.lib.formutils import *
from modoboa.lib.permissions import *
class DomainForm(forms.ModelForm, DynamicForm):
class DomainFormGeneral(forms.ModelForm, DynamicForm):
aliases = DomainNameField(
label=ugettext_lazy("Alias(es)"),
required=False,
@@ -25,7 +25,7 @@ class DomainForm(forms.ModelForm, DynamicForm):
self.oldname = None
if kwargs.has_key("instance"):
self.oldname = kwargs["instance"].name
super(DomainForm, self).__init__(*args, **kwargs)
super(DomainFormGeneral, self).__init__(*args, **kwargs)
if len(args) and isinstance(args[0], QueryDict):
self._load_from_qdict(args[0], "aliases", DomainNameField)
@@ -36,7 +36,7 @@ class DomainForm(forms.ModelForm, DynamicForm):
self._create_field(forms.CharField, name, dalias.name, 3)
def clean(self):
super(DomainForm, self).clean()
super(DomainFormGeneral, self).clean()
if len(self._errors):
raise forms.ValidationError(self._errors)
@@ -69,7 +69,7 @@ class DomainForm(forms.ModelForm, DynamicForm):
return cleaned_data
def save(self, user, commit=True):
d = super(DomainForm, self).save(commit=False)
d = super(DomainFormGeneral, self).save(commit=False)
if commit:
if self.oldname is not None and d.name != self.oldname:
if not d.rename_dir(self.oldname):
@@ -98,6 +98,32 @@ class DomainForm(forms.ModelForm, DynamicForm):
dalias.delete()
return d
class DomainForm(TabForms):
def __init__(self, user, *args, **kwargs):
self.user = user
self.forms = [
dict(id="general", title=_("General"), formtpl="admin/domain_general_form.html",
cls=DomainFormGeneral, mandatory=True),
]
cbargs = [user]
if kwargs.has_key("instances"):
cbargs += [kwargs["instances"]["general"]]
self.forms += events.raiseQueryEvent("ExtraDomainForm", *cbargs)
super(DomainForm, self).__init__(*args, **kwargs)
def save(self, user):
"""Custom save method
As forms interact with each other, it is simpler to make
custom code to save them.
"""
self.forms[0]["instance"].save(user)
if len(self.forms) <= 1:
return
for f in self.forms[1:]:
f["instance"].save()
class DlistForm(forms.ModelForm, DynamicForm):
email = forms.EmailField(
label=ugettext_lazy("Email address"),
+14 -11
View File
@@ -45,7 +45,7 @@ def domains_list(request):
return ajax_simple_response(doms)
def _validate_domain(request, form, successmsg, commonctx, tplname,
callback=None):
callback=None, tpl_form_name="form"):
"""Domain form validation
Common function shared between creation and modification actions.
@@ -56,15 +56,15 @@ def _validate_domain(request, form, successmsg, commonctx, tplname,
domain = form.save(request.user)
except AdminError, e:
error = str(e)
except IntegrityError:
error = _("An alias with this name already exists")
# except IntegrityError:
# error = _("An alias with this name already exists")
else:
if callback is not None:
callback(request.user, domain)
messages.info(request, successmsg, fail_silently=True)
return ajax_response(request, url=reverse(admin.views.domains))
commonctx["form"] = form
commonctx[tpl_form_name] = form
commonctx["error"] = error
return ajax_response(request, status="ko", template=tplname, **commonctx)
@@ -83,10 +83,10 @@ def newdomain(request, tplname="admin/newdomainform.html"):
"action" : reverse(newdomain),
"formid" : "domform"}
if request.method == "POST":
form = DomainForm(request.POST)
form = DomainFormGeneral(request.POST)
return _validate_domain(request, form, _("Domain created"), commonctx,
tplname, callback=newdomain_cb)
form = DomainForm()
form = DomainFormGeneral()
commonctx["form"] = form
return _render(request, tplname, commonctx)
@@ -103,7 +103,10 @@ def editdomain(request, dom_id, tplname="admin/editdomainform.html"):
)
if not request.user.is_superuser:
domadmins = filter(lambda u: u.group == "DomainAdmins", domadmins)
instances = dict(general=domain)
events.raiseEvent("FillDomainInstances", request.user, domain, instances)
commonctx = {"title" : domain.name,
"action_label" : _("Update"),
"action_classes" : "submit",
@@ -111,11 +114,11 @@ def editdomain(request, dom_id, tplname="admin/editdomainform.html"):
"formid" : "domform",
"domain" : domain}
if request.method == "POST":
form = DomainForm(request.POST, instance=domain)
return _validate_domain(request, form, _("Domain modified"), commonctx, tplname)
form = DomainForm(request.user, request.POST, instances=instances)
return _validate_domain(request, form, _("Domain modified"), commonctx, tplname,
tpl_form_name="tabs")
form = DomainForm(instance=domain)
commonctx["form"] = form
commonctx["tabs"] = DomainForm(request.user, instances=instances)
commonctx["domadmins"] = domadmins
return _render(request, tplname, commonctx)
+29
View File
@@ -92,6 +92,17 @@ def menu(target, user):
]
return []
@events.observe("DeleteDomain")
def on_delete_domain(domain):
from models import Users
try:
u = Users.objects.get(email="@%s" % domain.name)
except Users.DoesNotExist:
return
u.policy.delete()
u.delete()
@events.observe("CreateMailbox")
def create_user_record(user, mailbox):
from models import Users
@@ -170,3 +181,21 @@ def display_requests(user):
return [tpl.render(Context(dict(
label=_("%d pending requests" % nbrequests), url=url, css=css
)))]
@events.observe("ExtraDomainForm")
def extra_domain_form(user, domain):
from forms import DomainPolicyForm
if not user.is_superuser and not user.belongs_to_group("Resellers"):
return []
return [
dict(
id="amavis", title=_("Amavis"), cls=DomainPolicyForm
)
]
@events.observe("FillDomainInstances")
def fill_domain_instances(user, domain, instances):
if not user.is_superuser and not user.belongs_to_group("Resellers"):
return
instances["amavis"] = domain
+46
View File
@@ -0,0 +1,46 @@
# coding: utf-8
from django import forms
from models import Policy, Users
class DomainPolicyForm(forms.ModelForm):
class Meta:
model = Policy
fields = ('bypass_virus_checks', 'bypass_spam_checks', 'spam_modifies_subj',
'spam_tag2_level', 'spam_kill_level')
widgets = {
'bypass_spam_checks' : forms.Select(attrs={'class' : 'span1'}),
'bypass_virus_checks' : forms.Select(attrs={'class' : 'span1'}),
'spam_modifies_subj' : forms.Select(attrs={'class' : 'span1'}),
'spam_tag2_level' : forms.TextInput(attrs={'class' : 'span1'}),
'spam_kill_level' : forms.TextInput(attrs={'class' : 'span1'}),
}
def __init__(self, *args, **kwargs):
if kwargs.has_key("instance"):
self.domain = kwargs["instance"]
try:
policy = Users.objects.get(email="@%s" % self.domain.name).policy
kwargs["instance"] = policy
except (Users.DoesNotExist, Policy.DoesNotExist), e:
del kwargs["instance"]
super(DomainPolicyForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
p = super(DomainPolicyForm, self).save(commit=False)
if commit:
p.save()
print p.id
try:
u = Users.objects.get(policy__id=p.id)
except Users.DoesNotExist:
u = Users()
u.email = "@%s" % self.domain.name
u.fullname = self.domain.name
u.policy = p
u.local = "1"
u.priority = 7
u.save()
p.save()
return p
+22 -8
View File
@@ -10,6 +10,7 @@
# Original Amavis version : 2.6.2
from django.db import models
from django.utils.translation import ugettext as _, ugettext_lazy
class Maddr(models.Model):
partition_tag = models.IntegerField(unique=True, null=True, blank=True)
@@ -68,17 +69,26 @@ class Msgrcpt(models.Model):
unique_together = ("mail", "rid")
class Policy(models.Model):
id = models.IntegerField(primary_key=True)
id = models.AutoField(primary_key=True)
policy_name = models.CharField(max_length=96, blank=True)
virus_lover = models.CharField(max_length=3, blank=True)
spam_lover = models.CharField(max_length=3, blank=True)
banned_files_lover = models.CharField(max_length=3, blank=True)
bad_header_lover = models.CharField(max_length=3, blank=True)
bypass_virus_checks = models.CharField(max_length=3, blank=True)
bypass_spam_checks = models.CharField(max_length=3, blank=True)
bypass_virus_checks = models.CharField(
ugettext_lazy("Bypass virus checks"), max_length=3,
choices=(('Y', 'yes'), ('N', 'no')), blank=True
)
bypass_spam_checks = models.CharField(
ugettext_lazy("Bypass spam checks"), max_length=3,
choices=(('Y', 'yes'), ('N', 'no')), blank=True
)
bypass_banned_checks = models.CharField(max_length=3, blank=True)
bypass_header_checks = models.CharField(max_length=3, blank=True)
spam_modifies_subj = models.CharField(max_length=3, blank=True)
spam_modifies_subj = models.CharField(
ugettext_lazy("Modify spam subject"),
choices=(('Y', 'yes'), ('N', 'no')), max_length=3, blank=True
)
virus_quarantine_to = models.CharField(max_length=192, blank=True)
spam_quarantine_to = models.CharField(max_length=192, blank=True)
banned_quarantine_to = models.CharField(max_length=192, blank=True)
@@ -86,8 +96,12 @@ class Policy(models.Model):
clean_quarantine_to = models.CharField(max_length=192, blank=True)
other_quarantine_to = models.CharField(max_length=192, blank=True)
spam_tag_level = models.FloatField(null=True, blank=True)
spam_tag2_level = models.FloatField(null=True, blank=True)
spam_kill_level = models.FloatField(null=True, blank=True)
spam_tag2_level = models.FloatField(
ugettext_lazy("Spam tag2 level"), null=True, blank=True
)
spam_kill_level = models.FloatField(
ugettext_lazy("Spam kill level"), null=True, blank=True
)
spam_dsn_cutoff_level = models.FloatField(null=True, blank=True)
spam_quarantine_cutoff_level = models.FloatField(null=True, blank=True)
addr_extension_virus = models.CharField(max_length=192, blank=True)
@@ -122,9 +136,9 @@ class Quarantine(models.Model):
unique_together = ("mail", "chunk_ind")
class Users(models.Model):
id = models.IntegerField(primary_key=True)
id = models.AutoField(primary_key=True)
priority = models.IntegerField()
policy_id = models.IntegerField()
policy = models.ForeignKey(Policy)
email = models.CharField(unique=True, max_length=255)
fullname = models.CharField(max_length=765, blank=True)
local = models.CharField(max_length=3, blank=True)
+3
View File
@@ -42,6 +42,9 @@ events = [
"ExtraAccountForm",
"CheckExtraAccountForm",
"FillAccountInstances",
"ExtraDomainForm",
"FillDomainInstances",
"GetExtraRoles",
+1 -5
View File
@@ -28,14 +28,10 @@ class Column:
def __unicode__(self):
try:
label = getattr(self, "label")
except KeyError:
except AttributeError:
label = getattr(self, "name")
return _(label)
def __str__(self):
return self.__unicode__()
class SelectionColumn(Column):
"""Specific column: selection
+5
View File
@@ -0,0 +1,5 @@
{% load libextras %}
{% render_field form.name %}
{% render_field form.quota %}
{% render_fields_group form "aliases" %}
{% render_field form.enabled %}
+18 -30
View File
@@ -1,34 +1,22 @@
{% extends "common/generic_modal.html" %}{% load i18n libextras admin_extras %}
{% block modalbody %}
<ul class="nav nav-pills">
<li class="active"><a href="#general" data-toggle="tab">{% trans "General" %}</a></li>
{% extends "common/tabforms.html" %}{% load i18n libextras admin_extras %}
{% block extratabs %}
<li><a href="#admins" data-toggle="tab">{% trans "Administrators" %}</a></li>
<li><a href="#misc" data-toggle="tab">{% trans "Miscellaneous" %}</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="general">
{% if error %}<div class="alert alert-error">{{ error }}</div>{% endif %}
<form class="form-horizontal" action="{{ action }}" method="POST" id="{{ formid }}" name="{{ formid }}">
{% render_field form.name %}
{% render_field form.quota %}
{% render_fields_group form "aliases" %}
{% render_field form.enabled %}
</form>
</div>
<div class="tab-pane" id="admins">{% if domadmins %}
<table id="domadmins" class="table table-condensed">
<tr><th>{% trans "Username" %}</th><th>{% trans "Name" %}</th><th></th></tr>
{% for da in domadmins %}<tr>
<td>{{ da.username }}</td><td>{{ da.fullname }}</td><td name="actions">{% domadmin_actions da.id domain.id %}</td>
</tr>{% endfor %}
</table>{% else %}<div class="alert alert-info">{% trans "No domain administrator defined" %}</div>{% endif %}
</div>
<div class="tab-pane" id="misc">
<div class="row"><span class="title">{% trans "Creation date" %}</span><span>{{ domain.creation }}</<span></div>
<div class="row"><span class="title">{% trans "Last modification date" %}</span><span>{{ domain.last_modification }}</<span></div>
<div class="row"><span class="title">{% trans "Domain aliases" %}</span><span>{{ domain.domainalias_count }}</<span></div>
<div class="row"><span class="title">{% trans "Mailboxes" %}</span><span>{{ domain.mailbox_count }}</<span></div>
<div class="row"><span class="title">{% trans "Mailbox aliases" %}</span><span>{{ domain.mbalias_count }}</<span></div>
</div>
{% endblock %}
{% block extratabs_content %}
<div class="tab-pane" id="admins">{% if domadmins %}
<table id="domadmins" class="table table-condensed">
<tr><th>{% trans "Username" %}</th><th>{% trans "Name" %}</th><th></th></tr>
{% for da in domadmins %}<tr>
<td>{{ da.username }}</td><td>{{ da.fullname }}</td><td name="actions">{% domadmin_actions da.id domain.id %}</td>
</tr>{% endfor %}
</table>{% else %}<div class="alert alert-info">{% trans "No domain administrator defined" %}</div>{% endif %}
</div>
<div class="tab-pane" id="misc">
<div class="row"><span class="title">{% trans "Creation date" %}</span><span>{{ domain.creation }}</span></div>
<div class="row"><span class="title">{% trans "Last modification date" %}</span><span>{{ domain.last_modification }}</span></div>
<div class="row"><span class="title">{% trans "Domain aliases" %}</span><span>{{ domain.domainalias_count }}</span></div>
<div class="row"><span class="title">{% trans "Mailboxes" %}</span><span>{{ domain.mailbox_count }}</span></div>
<div class="row"><span class="title">{% trans "Mailbox aliases" %}</span><span>{{ domain.mbalias_count }}</span></div>
</div>
{% endblock %}
+6 -8
View File
@@ -2,23 +2,21 @@
{% load i18n libextras %}
{% block modalbody %}
<form id="{{ formid }}" class="form-horizontal" action="{{ action }}" method="POST">
<ul class="nav nav-pills">
{% for tab in tabs %}
<ul class="nav nav-pills">{% for tab in tabs %}
<li class="{% if tabs.active_id == tab.id %}active{% endif %}">
<a href="#{{ tab.id }}" data-toggle="tab">{{ tab.title }}</a>
</li>
{% endfor %}
</li>{% endfor %}
{% block extratabs %}{% endblock %}
</ul>
<div class="tab-content">
{% for tab in tabs %}
<div class="tab-pane{% if tabs.active_id == tab.id %} active{% endif %}" id="{{ tab.id }}">
{% for tab in tabs %}<div class="tab-pane{% if tabs.active_id == tab.id %} active{% endif %}" id="{{ tab.id }}">
{% if tab.formtpl %}
{% render_form tab.instance tab.formtpl %}
{% else %}
{% render_form tab.instance %}
{% endif %}
</div>
{% endfor %}
</div>{% endfor %}
{% block extratabs_content %}{% endblock %}
</div>
</form>
{% endblock %}