Support added to relay domain aliases.

I've also refactored some code.
see #491
This commit is contained in:
Antoine Nguyen
2013-10-19 09:35:29 +02:00
parent 83c4d9c5ac
commit 6444c71a03
21 changed files with 215 additions and 164 deletions
+8 -8
View File
@@ -144,8 +144,8 @@ this account is concerned by a specific form.
Callbacks listening to this event must return a list containing one
Boolean.
CreateDomain
============
DomainCreated
=============
Raised when a new domain is created.
@@ -156,8 +156,8 @@ Raised when a new domain is created.
* ``user`` corresponds to the ``User`` object creating the domain (its owner)
* ``domain`` is a ``Domain`` instance
CreateMailbox
=============
MailboxCreated
==============
Raised when a new mailbox is created.
@@ -168,8 +168,8 @@ Raised when a new mailbox is created.
* ``user`` is the new mailbox's owner (``User`` instance)
* ``mailbox`` is the new mailbox (``Mailbox`` instance)
DeleteDomain
============
DomainDeleted
=============
Raised when an existing domain is about to be deleted.
@@ -179,8 +179,8 @@ Raised when an existing domain is about to be deleted.
* ``domain`` is a ``Domain`` instance
DeleteMailbox
=============
MailboxDeleted
==============
Raised when an existing mailbox is about to be deleted.
+6 -5
View File
@@ -12,21 +12,22 @@ from modoboa.extensions.admin.models import (
admin_events = [
"CreateDomain",
"DomainCreated",
"DomainModified",
"DeleteDomain",
"DomainDeleted",
"ExtraDomainEntries",
"ExtraDomainMenuEntries",
"ExtraDomainFilters",
"GetDomainActions",
"GetDomainModifyLink",
"GetDomainAliasQuerySet",
"DomainAliasCreated",
"DomainAliasDeleted",
"CreateMailbox",
"DeleteMailbox",
"ModifyMailbox",
"MailboxCreated",
"MailboxDeleted",
"MailboxModified",
"MailboxAliasCreated",
"MailboxAliasDeleted",
+2 -2
View File
@@ -1,4 +1,4 @@
from .base import DatesAware
from .base import AdminObject
from .domain import Domain
from .domain_alias import DomainAlias
from .mailbox import Mailbox, Quota, MailboxOperation
@@ -6,5 +6,5 @@ from .alias import Alias
__all__ = [
'Domain', 'DomainAlias', 'Mailbox', 'Quota', 'Alias',
'MailboxOperation', 'DatesAware'
'MailboxOperation', 'AdminObject'
]
+4 -18
View File
@@ -5,12 +5,12 @@ from modoboa.lib import events
from modoboa.lib.emailutils import split_mailbox
from modoboa.lib.exceptions import PermDeniedException
from modoboa.extensions.admin.exceptions import AdminError
from .base import DatesAware
from .base import AdminObject
from .domain import Domain
from .mailbox import Mailbox
class Alias(DatesAware):
class Alias(AdminObject):
address = models.CharField(
ugettext_lazy('address'), max_length=254,
help_text=ugettext_lazy("The alias address (without the domain part). For a 'catch-all' address, just enter an * character.")
@@ -29,6 +29,7 @@ class Alias(DatesAware):
ugettext_lazy('enabled'),
help_text=ugettext_lazy("Check to activate this alias")
)
_objectname = 'MailboxAlias'
class Meta:
permissions = (
@@ -80,8 +81,7 @@ class Alias(DatesAware):
def post_create(self, creator):
from modoboa.lib.permissions import grant_access_to_object
grant_access_to_object(creator, self, is_owner=True)
events.raiseEvent("MailboxAliasCreated", creator, self)
super(Alias, self).post_create(creator)
if creator.is_superuser:
for admin in self.domain.admins:
grant_access_to_object(admin, self)
@@ -96,14 +96,7 @@ class Alias(DatesAware):
del kwargs['int_rcpts']
else:
int_rcpts = []
if "creator" in kwargs:
creator = kwargs["creator"]
del kwargs["creator"]
else:
creator = None
super(Alias, self).save(*args, **kwargs)
if creator is not None:
self.post_create(creator)
curaliases = self.aliases.all()
curmboxes = self.mboxes.all()
for t in int_rcpts:
@@ -120,13 +113,6 @@ class Alias(DatesAware):
if not t in int_rcpts:
self.mboxes.remove(t)
def delete(self):
from modoboa.lib.permissions import ungrant_access_to_object
events.raiseEvent("MailboxAliasDeleted", self)
ungrant_access_to_object(self)
super(Alias, self).delete()
def get_recipients(self):
"""Return the recipients list
+32 -5
View File
@@ -1,4 +1,8 @@
from django.db import models
from modoboa.lib import events
from modoboa.lib.permissions import (
grant_access_to_object, ungrant_access_to_object
)
class ObjectDates(models.Model):
@@ -27,7 +31,7 @@ class ObjectDates(models.Model):
obj.dates = dates
class DatesAware(models.Model):
class AdminObject(models.Model):
"""Abstract model to support dates
Inherit from this model to automatically add the "dates" feature
@@ -35,14 +39,11 @@ class DatesAware(models.Model):
saves.
"""
dates = models.ForeignKey(ObjectDates)
_objectname = None
class Meta:
abstract = True
def save(self, *args, **kwargs):
ObjectDates.set_for_object(self)
super(DatesAware, self).save(*args, **kwargs)
@property
def creation(self):
return self.dates.creation
@@ -50,3 +51,29 @@ class DatesAware(models.Model):
@property
def last_modification(self):
return self.dates.last_modification
@property
def objectname(self):
if self._objectname is None:
return self.__class__.__name__
return self._objectname
def post_create(self, creator):
grant_access_to_object(creator, self, is_owner=True)
events.raiseEvent("%sCreated" % self.objectname, creator, self)
def save(self, *args, **kwargs):
ObjectDates.set_for_object(self)
if "creator" in kwargs:
creator = kwargs["creator"]
del kwargs["creator"]
else:
creator = None
super(AdminObject, self).save(*args, **kwargs)
if creator is not None:
self.post_create(creator)
def delete(self):
events.raiseEvent("%sDeleted" % self.objectname, self)
ungrant_access_to_object(self)
super(AdminObject, self).delete()
+2 -19
View File
@@ -6,7 +6,7 @@ from django.contrib.contenttypes import generic
from modoboa.lib import events, parameters
from modoboa.core.models import User, ObjectAccess
from modoboa.extensions.admin.exceptions import AdminError
from .base import DatesAware
from .base import AdminObject
class DomainManager(Manager):
@@ -22,7 +22,7 @@ class DomainManager(Manager):
return self.get_query_set().filter(owners__user=admin)
class Domain(DatesAware):
class Domain(AdminObject):
name = models.CharField(ugettext_lazy('name'), max_length=100, unique=True,
help_text=ugettext_lazy("The domain name"))
quota = models.IntegerField(
@@ -116,28 +116,11 @@ class Domain(DatesAware):
if parameters.get_admin("AUTO_ACCOUNT_REMOVAL") == "yes":
for account in User.objects.filter(mailbox__domain__name=self.name):
account.delete(fromuser, keepdir)
events.raiseEvent("DeleteDomain", self)
ungrant_access_to_object(self)
super(Domain, self).delete()
def __str__(self):
return self.name
def post_create(self, creator):
from modoboa.lib.permissions import grant_access_to_object
grant_access_to_object(creator, self, is_owner=True)
events.raiseEvent("CreateDomain", creator, self)
def save(self, *args, **kwargs):
if "creator" in kwargs:
creator = kwargs["creator"]
del kwargs["creator"]
else:
creator = None
super(Domain, self).save(*args, **kwargs)
if creator is not None:
self.post_create(creator)
def from_csv(self, user, row):
if len(row) < 4:
raise AdminError(_("Invalid line"))
@@ -3,11 +3,11 @@ from django.db import models
from django.utils.translation import ugettext as _, ugettext_lazy
from modoboa.lib import events
from modoboa.extensions.admin.exceptions import AdminError
from .base import DatesAware
from .base import AdminObject
from .domain import Domain
class DomainAlias(DatesAware):
class DomainAlias(AdminObject):
name = models.CharField(ugettext_lazy("name"), max_length=100, unique=True,
help_text=ugettext_lazy("The alias name"))
target = models.ForeignKey(
@@ -28,27 +28,6 @@ class DomainAlias(DatesAware):
def __unicode__(self):
return self.name
def post_create(self, creator):
from modoboa.lib.permissions import grant_access_to_object
grant_access_to_object(creator, self, is_owner=True)
events.raiseEvent("DomainAliasCreated", creator, self)
def save(self, *args, **kwargs):
if "creator" in kwargs:
creator = kwargs["creator"]
del kwargs["creator"]
else:
creator = None
super(DomainAlias, self).save(*args, **kwargs)
if creator is not None:
self.post_create(creator)
def delete(self):
from modoboa.lib.permissions import ungrant_access_to_object
events.raiseEvent("DomainAliasDeleted", self)
ungrant_access_to_object(self)
super(DomainAlias, self).delete()
def from_csv(self, user, row):
"""Create a domain alias from a CSV row
+3 -11
View File
@@ -12,7 +12,7 @@ from modoboa.lib import parameters, events
from modoboa.lib.sysutils import exec_cmd
from modoboa.core.models import User
from modoboa.extensions.admin.exceptions import AdminError
from .base import DatesAware
from .base import AdminObject
from .domain import Domain
@@ -46,7 +46,7 @@ class MailboxManager(Manager):
return self.get_query_set().filter(qf)
class Mailbox(DatesAware):
class Mailbox(AdminObject):
address = models.CharField(
ugettext_lazy('address'), max_length=252,
help_text=ugettext_lazy("Mailbox address (without the @domain.tld part)")
@@ -186,8 +186,7 @@ class Mailbox(DatesAware):
def post_create(self, creator):
from modoboa.lib.permissions import grant_access_to_object
grant_access_to_object(creator, self, True)
events.raiseEvent("CreateMailbox", creator, self)
super(Mailbox, self).post_create(creator)
if creator.is_superuser and not self.user.has_perm("admin.add_domain"):
# A super user is creating a new mailbox. Give
# access to that mailbox (and the associated
@@ -199,14 +198,7 @@ class Mailbox(DatesAware):
grant_access_to_object(admin, self.user)
def save(self, *args, **kwargs):
if "creator" in kwargs:
creator = kwargs["creator"]
del kwargs["creator"]
else:
creator = None
super(Mailbox, self).save(*args, **kwargs)
if creator is not None:
self.post_create(creator)
try:
q = self.quota_value
except Quota.DoesNotExist:
@@ -259,18 +259,6 @@ Domains.prototype = {
}, this));
$(document).trigger('domform_init');
},
rdomainform_cb: function() {
$(".submit").one('click', $.proxy(function(e) {
simple_ajax_form_post2(e, {
formid: "rdomform",
error_cb: $.proxy(this.rdomainform_cb, this),
reload_on_success: false,
success_cb: $.proxy(this.reload_listing, this)
});
}, this));
}
};
Domains.prototype = $.extend({}, Admin.prototype, Domains.prototype);
@@ -2,8 +2,8 @@
<table id="{{ tableid }}" class="table {{ styles }}">
<thead>
<tr>
<th id="identity" class="sortable" data-sort_order="identity" width="">{% trans "Name" %}</th>
<th id="name_or_rcpt" class="sortable" data-sort_order="name_or_rcpt" width="">{% trans "Alias(es)" %}</th>
<th id="name" class="sortable" data-sort_order="name" width="">{% trans "Name" %}</th>
<th id="aliases" width="">{% trans "Alias(es)" %}</th>
<th id="tags" class="sortable" data-sort_order="tags" width="">{% trans "Tags" %}</th>
<th id="actions" width="">{% trans "Actions" %}</th>
</tr>
@@ -195,13 +195,19 @@ def domain_aliases(domain):
:rtype: str
"""
if domain.__class__.__name__ == "Domain":
if not domain.domainalias_set.count():
qset = domain.domainalias_set
else:
qset = events.raiseQueryEvent('GetDomainAliasQuerySet', domain)
if not qset:
return '---'
res = ""
for da in domain.domainalias_set.all():
res += "%s<br/>" % da.name
return res
return '---'
qset = qset[0]
if not qset.count():
return '---'
res = ''
for alias in qset.all():
res += '%s<br/>' % alias.name
return res
@register.simple_tag
+1 -1
View File
@@ -43,7 +43,7 @@ def _domains(request):
domainlist = get_domains(
request.user, **filters
)
if sort_order in ["name", "domainalias__name"]:
if sort_order == 'name':
domainlist = sorted(
domainlist,
key=lambda d: getattr(d, sort_order), reverse=sort_dir == '-'
+4 -4
View File
@@ -62,8 +62,8 @@ def menu(target, user):
return []
@events.observe("CreateDomain")
def on_create_domain(user, domain):
@events.observe("DomainCreated")
def on_domain_created(user, domain):
from .models import Users, Policy
p = Policy.objects.create(policy_name=domain.name)
Users.objects.create(email="@%s" % domain.name, fullname=domain.name,
@@ -82,8 +82,8 @@ def on_domain_modified(domain):
u.save()
@events.observe("DeleteDomain")
def on_delete_domain(domain):
@events.observe("DomainDeleted")
def on_domain_deleted(domain):
from .models import Users
try:
+4 -4
View File
@@ -66,12 +66,12 @@ def move_pool_resource(owner, user):
user.limitspool.delete()
@events.observe('CreateDomain')
@events.observe('DomainCreated')
def inc_nb_domains(user, domain):
inc_limit(user, 'domains_limit')
@events.observe('DeleteDomain')
@events.observe('DomainDeleted')
def dec_nb_domains(domain):
owner = get_object_owner(domain)
dec_limit(owner, 'domains_limit')
@@ -99,12 +99,12 @@ def dec_nb_domaliases(domainaliases):
dec_limit(owner, 'domain_aliases_limit')
@events.observe('CreateMailbox')
@events.observe('MailboxCreated')
def inc_nb_mailboxes(user, mailbox):
inc_limit(user, 'mailboxes_limit')
@events.observe('DeleteMailbox')
@events.observe('MailboxDeleted')
def dec_nb_mailboxes(mailboxes):
from modoboa.extensions.admin.models import Mailbox
@@ -25,7 +25,7 @@ class PostfixAutoreply(ModoExtension):
try:
Transport.objects.get(domain="autoreply.%s" % dom.name)
except Transport.DoesNotExist:
onCreateDomain(None, dom)
onDomainCreated(None, dom)
else:
continue
@@ -33,18 +33,18 @@ class PostfixAutoreply(ModoExtension):
try:
Alias.objects.get(full_address=mb.full_address)
except Alias.DoesNotExist:
onCreateMailbox(None, mb)
onMailboxCreated(None, mb)
def load(self):
from modoboa.extensions.postfix_autoreply.app_settings import ParametersForm
parameters.register(ParametersForm, ugettext_lazy("Automatic replies"))
def destroy(self):
events.unregister("CreateDomain", onCreateDomain)
events.unregister("DeleteDomain", onDeleteDomain)
events.unregister("CreateMailbox", onCreateMailbox)
events.unregister("DeleteMailbox", onDeleteMailbox)
events.unregister("ModifyMailbox", onModifyMailbox)
events.unregister("DomainCreated", onDomainCreated)
events.unregister("DomainDeleted", onDomainDeleted)
events.unregister("MailboxCreated", onMailboxCreated)
events.unregister("MailboxDeleted", onMailboxDeleted)
events.unregister("MailboxModified", onModifyMailbox)
events.unregister("UserMenuDisplay", menu)
parameters.unregister()
@@ -80,8 +80,8 @@ def menu(target, user):
]
@events.observe("CreateDomain")
def onCreateDomain(user, domain):
@events.observe("DomainCreated")
def onDomainCreated(user, domain):
transport = Transport()
transport.domain = "autoreply.%s" % domain.name
transport.method = "autoreply:"
@@ -101,13 +101,13 @@ def onDomainModified(domain):
al.save()
@events.observe("DeleteDomain")
def onDeleteDomain(domain):
@events.observe("DomainDeleted")
def onDomainDeleted(domain):
Transport.objects.filter(domain="autoreply.%s" % domain.name).delete()
@events.observe("CreateMailbox")
def onCreateMailbox(user, mailbox):
@events.observe("MailboxCreated")
def onMailboxCreated(user, mailbox):
alias = Alias()
alias.full_address = mailbox.full_address
alias.autoreply_address = \
@@ -115,8 +115,8 @@ def onCreateMailbox(user, mailbox):
alias.save()
@events.observe("DeleteMailbox")
def onDeleteMailbox(mailboxes):
@events.observe("MailboxDeleted")
def onMailboxDeleted(mailboxes):
from modoboa.extensions.admin.models import Mailbox
if isinstance(mailboxes, Mailbox):
@@ -130,7 +130,7 @@ def onDeleteMailbox(mailboxes):
alias.delete()
@events.observe("ModifyMailbox")
@events.observe("MailboxModified")
def onModifyMailbox(mailbox, oldmailbox):
if oldmailbox.full_address == mailbox.full_address:
return
@@ -82,6 +82,7 @@ def extra_domain_entries(user, domfilter, searchquery, **extrafilters):
relay_domains = RelayDomain.objects.get_for_admin(user)
if searchquery is not None:
q = Q(name__contains=searchquery)
q |= Q(relaydomainalias__name__contains=searchquery)
relay_domains = relay_domains.filter(q).distinct()
if 'srvfilter' in extrafilters and extrafilters['srvfilter']:
relay_domains = relay_domains.filter(
@@ -103,6 +104,13 @@ def rdomain_modify_link(domain):
}
@events.observe('GetDomainAliasQuerySet')
def get_da_query_set(domain):
if domain.__class__.__name__ != 'RelayDomain':
return []
return [domain.relaydomainalias_set]
@events.observe('GetDomainActions')
def rdomain_actions(user, domain):
if not isinstance(domain, RelayDomain):
@@ -1,8 +1,20 @@
from django import forms
from .models import RelayDomain
from django.http import QueryDict
from django.utils.translation import ugettext_lazy
from modoboa.lib.formutils import DynamicForm, DomainNameField
from modoboa.extensions.admin.models import Domain
from .models import RelayDomain, RelayDomainAlias
class RelayDomainForm(forms.ModelForm):
class RelayDomainForm(forms.ModelForm, DynamicForm):
aliases = DomainNameField(
label=ugettext_lazy("Alias(es)"),
required=False,
help_text=ugettext_lazy(
"Alias(es) of this relay domain. Indicate only one name per input"
", press ENTER to add a new input."
)
)
class Meta:
model = RelayDomain
@@ -10,3 +22,70 @@ class RelayDomainForm(forms.ModelForm):
widgets = {
'service': forms.Select(attrs={'class': 'span2'})
}
def __init__(self, *args, **kwargs):
super(RelayDomainForm, self).__init__(*args, **kwargs)
if args and isinstance(args[0], QueryDict):
self._load_from_qdict(args[0], "aliases", DomainNameField)
elif 'instance' in kwargs:
rd = kwargs['instance']
for pos, rdalias in enumerate(rd.relaydomainalias_set.all()):
name = "aliases_%d" % (pos + 1)
self._create_field(forms.CharField, name, rdalias.name, 3)
def clean(self):
super(RelayDomainForm, self).clean()
if self._errors:
raise forms.ValidationError(self._errors)
cleaned_data = self.cleaned_data
# try:
# DomainAlias.objects.get(name=name)
# except DomainAlias.DoesNotExist:
# pass
# else:
# self._errors["name"] = self.error_class([_("An alias with this name already exists")])
# del cleaned_data["name"]
for k in cleaned_data.keys():
if not k.startswith("aliases"):
continue
if cleaned_data[k] == "":
del cleaned_data[k]
continue
try:
Domain.objects.get(name=cleaned_data[k])
except Domain.DoesNotExist:
pass
else:
self._errors[k] = self.error_class([_("A domain with this name already exists")])
del cleaned_data[k]
return cleaned_data
def save(self, user, commit=True):
"""Custom save method
"""
rd = super(RelayDomainForm, self).save(commit=False)
if commit:
rd.save()
for k, v in self.cleaned_data.iteritems():
if not k.startswith("aliases"):
continue
if v in ["", None]:
continue
try:
rd.relaydomainalias_set.get(name=v)
except RelayDomainAlias.DoesNotExist:
pass
else:
continue
#events.raiseEvent("CanCreate", user, "domain_aliases")
al = RelayDomainAlias(name=v, target=rd, enabled=rd.enabled)
al.save(creator=user)
for rdalias in rd.relaydomainalias_set.all():
if not filter(lambda name: self.cleaned_data[name] == rdalias.name,
self.cleaned_data.keys()):
rdalias.delete()
return rd
@@ -6,7 +6,7 @@ from django.utils.translation import ugettext as _, ugettext_lazy
from modoboa.core.models import ObjectAccess
from modoboa.lib import events, parameters
from modoboa.lib.permissions import ungrant_access_to_object
from modoboa.extensions.admin.models import DatesAware
from modoboa.extensions.admin.models import AdminObject
class RelayDomainManager(Manager):
@@ -63,7 +63,7 @@ class Service(models.Model):
return self.name
class RelayDomain(DatesAware):
class RelayDomain(AdminObject):
"""Relay domain.
A relay domain differs from a usual domaine because its final
@@ -106,24 +106,24 @@ class RelayDomain(DatesAware):
def __str__(self):
return self.name
def post_create(self, creator):
from modoboa.lib.permissions import grant_access_to_object
grant_access_to_object(creator, self, is_owner=True)
events.raiseEvent("RelayDomainCreated", creator, self)
def save(self, *args, **kwargs):
if "creator" in kwargs:
creator = kwargs["creator"]
del kwargs["creator"]
else:
creator = None
super(RelayDomain, self).save(*args, **kwargs)
if creator is not None:
self.post_create(creator)
def delete(self):
events.raiseEvent("RelayDomainDeleted", self)
ungrant_access_to_object(self)
super(RelayDomain, self).delete()
reversion.register(RelayDomain)
class RelayDomainAlias(AdminObject):
name = models.CharField(
ugettext_lazy("name"), max_length=100, unique=True,
help_text=ugettext_lazy("The alias name")
)
target = models.ForeignKey(
RelayDomain, verbose_name=ugettext_lazy('target'),
help_text=ugettext_lazy("The relay domain this alias points to")
)
enabled = models.BooleanField(
ugettext_lazy('enabled'),
help_text=ugettext_lazy("Check to activate this alias")
)
def __str__(self):
return self.name
reversion.register(RelayDomainAlias)
@@ -51,10 +51,11 @@ RelayDomains.prototype = {
domainform_cb: function() {
$('#btnscan').click(this.launch_scan);
$(".submit").one('click', $.proxy(function(e) {
$('input:text:visible:first').focus();
$("#id_aliases").dynamic_input();
$(".submit").on('click', $.proxy(function(e) {
simple_ajax_form_post2(e, {
formid: "rdomform",
error_cb: $.proxy(this.domainform_cb, this),
reload_on_success: false,
success_cb: $.proxy(admin.reload_listing, admin)
});
@@ -2,6 +2,7 @@
{% render_field form.name %}
{% render_field form.target_host %}
{% include "postfix_relay_domains/service_field.html" with field=form.service %}
{% render_fields_group form "aliases" %}
{% render_field form.enabled %}
{% render_field form.verify_recipients %}
{% endblock %}
@@ -25,7 +25,7 @@ def create(request, tplname="postfix_relay_domains/relaydomain_form.html"):
if request.method == 'POST':
form = RelayDomainForm(request.POST)
if form.is_valid():
rdom = form.save()
rdom = form.save(request.user)
rdom.post_create(request.user)
return render_to_json_response(
{"respmsg": _("Relay domain created")}
@@ -44,7 +44,7 @@ def edit(request, rdom_id, tplname='postfix_relay_domains/relaydomain_form.html'
if request.method == 'POST':
form = RelayDomainForm(request.POST, instance=rdom)
if form.is_valid():
form.save()
form.save(request.user)
events.raiseEvent('RelayDomainModified', rdom)
return render_to_json_response(
{'respmsg': _('Relay domain modified')}