Improvements about aliases:

* Referencing an existing alias as a recipient is now allowed
* Alias type verification is less restrictive

fixes #403
This commit is contained in:
Antoine Nguyen
2013-05-06 17:28:04 +02:00
parent 019565bbca
commit b15e475475
9 changed files with 317 additions and 264 deletions
+49 -136
View File
@@ -1,13 +1,14 @@
# coding: utf-8
from django import forms
from django.contrib.auth.models import Group
from modoboa.admin.models import *
from django.utils.translation import ugettext as _, ugettext_lazy
from django.http import QueryDict
from modoboa.admin.templatetags.admin_extras import gender
from modoboa.lib import events, parameters
from modoboa.lib.emailutils import split_mailbox
from modoboa.lib.permissions import get_account_roles
from modoboa.lib.formutils import *
from modoboa.admin.models import User, Domain, DomainAlias, Mailbox, Alias
class DomainFormGeneral(forms.ModelForm, DynamicForm):
@@ -54,7 +55,6 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
self._errors["name"] = self.error_class([_("An alias with this name already exists")])
del cleaned_data["name"]
errors = []
for k in cleaned_data.keys():
if not k.startswith("aliases"):
continue
@@ -198,7 +198,7 @@ class DomainForm(TabForms):
f["instance"].save(user)
class DlistForm(forms.ModelForm, DynamicForm):
class AliasForm(forms.ModelForm, DynamicForm):
email = forms.EmailField(
label=ugettext_lazy("Email address"),
help_text=ugettext_lazy("The distribution list address. Use the '*' character to create a 'catchall' address (ex: *@domain.tld).")
@@ -214,7 +214,7 @@ class DlistForm(forms.ModelForm, DynamicForm):
def __init__(self, user, *args, **kwargs):
self.user = user
super(DlistForm, self).__init__(*args, **kwargs)
super(AliasForm, self).__init__(*args, **kwargs)
self.fields.keyOrder = ['email', 'recipients', 'enabled']
if len(args) and isinstance(args[0], QueryDict):
@@ -223,6 +223,10 @@ class DlistForm(forms.ModelForm, DynamicForm):
dlist = kwargs["instance"]
self.fields["email"].initial = dlist.full_address
cpt = 1
for al in dlist.aliases.all():
name = "recipients_%d" % cpt
self._create_field(forms.EmailField, name, al.full_address, 2)
cpt += 1
for mb in dlist.mboxes.all():
name = "recipients_%d" % (cpt)
self._create_field(forms.EmailField, name, mb.full_address, 2)
@@ -254,7 +258,7 @@ class DlistForm(forms.ModelForm, DynamicForm):
self.int_rcpts = []
total = 0
for k, v in self.cleaned_data.iteritems():
for k, v in self.cleaned_data.items():
if not k.startswith("recipients"):
continue
if v == "":
@@ -266,16 +270,21 @@ class DlistForm(forms.ModelForm, DynamicForm):
domain = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
domain = None
if domain:
if domain is not None:
if not self.user.can_access(domain):
raise PermDeniedException(v)
try:
mb = Mailbox.objects.get(domain=domain, address=local_part)
except Mailbox.DoesNotExist:
raise AdminError(_("Mailbox %s does not exist" % v))
if mb in self.int_rcpts:
rcpt = Alias.objects.get(domain=domain, address=local_part)
except Alias.DoesNotExist:
rcpt = None
if rcpt is None:
try:
rcpt = Mailbox.objects.get(domain=domain, address=local_part)
except Mailbox.DoesNotExist:
raise AdminError(_("Local recipient %s not found" % v))
if rcpt in self.int_rcpts:
raise AdminError(_("Recipient %s already present" % v))
self.int_rcpts += [mb]
self.int_rcpts += [rcpt]
total += 1
continue
@@ -287,122 +296,22 @@ class DlistForm(forms.ModelForm, DynamicForm):
if total == 0:
raise AdminError(_("No recipient defined"))
if total < 2:
raise AdminError(_("A distribution list must contain at least two recipients"))
def save(self, commit=True):
dlist = super(DlistForm, self).save(commit=False)
localpart, domname = split_mailbox(self.cleaned_data["email"])
dlist.address = localpart
dlist.domain = Domain.objects.get(name=domname)
if commit:
dlist.save(self.int_rcpts, self.ext_rcpts)
self.save_m2m()
return dlist
class GenericAliasForm(forms.ModelForm):
email = forms.EmailField(
label=ugettext_lazy("Address"),
help_text=ugettext_lazy("A valid e-mail address. Use the '*' character to create a 'catchall' address (ex: *@domain.tld).")
)
class Meta:
model = Alias
fields = ("enabled", )
def __init__(self, user, *args, **kwargs):
self.user = user
super(GenericAliasForm, self).__init__(*args, **kwargs)
if "instance" in kwargs:
alias = kwargs["instance"]
self.fields["email"].initial = alias.full_address
def clean_email(self):
localpart, domname = split_mailbox(self.cleaned_data["email"])
try:
domain = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
raise forms.ValidationError(_("Domain does not exist"))
if not self.user.can_access(domain):
raise forms.ValidationError(_("You don't have access to this domain"))
return self.cleaned_data["email"]
def set_recipients(self):
pass
def _save(self):
alias = super(GenericAliasForm, self).save(commit=False)
alias = super(AliasForm, self).save(commit=False)
localpart, domname = split_mailbox(self.cleaned_data["email"])
alias.address = localpart
alias.domain = Domain.objects.get(name=domname)
return alias
class AliasForm(GenericAliasForm):
int_recipient = forms.EmailField(
label=ugettext_lazy("Recipient"),
help_text=ugettext_lazy("A local recipient address")
)
def __init__(self, *args, **kwargs):
super(AliasForm, self).__init__(*args, **kwargs)
self.fields.keyOrder = ['email', 'int_recipient', 'enabled']
if "instance" in kwargs:
alias = kwargs["instance"]
if len(alias.mboxes.all()):
self.fields["int_recipient"].initial = alias.mboxes.all()[0].full_address
def clean_int_recipient(self):
localpart, domname = split_mailbox(self.cleaned_data["int_recipient"])
try:
self.rcpt_mb = Mailbox.objects.get(address=localpart, domain__name=domname)
except Mailbox.DoesNotExist:
raise forms.ValidationError(_("Mailbox does not exist"))
if not self.user.can_access(self.rcpt_mb):
raise forms.ValidationError(_("You can't access this mailbox"))
return self.cleaned_data["int_recipient"]
def save(self, commit=True):
alias = self._save()
if commit:
alias.save([self.rcpt_mb], [])
return alias
class ForwardForm(GenericAliasForm):
ext_recipient = forms.EmailField(
label=ugettext_lazy("Recipient"),
help_text=ugettext_lazy("An external recipient address")
)
def __init__(self, *args, **kwargs):
super(ForwardForm, self).__init__(*args, **kwargs)
self.fields.keyOrder = ['email', 'ext_recipient', 'enabled']
if "instance" in kwargs:
alias = kwargs["instance"]
self.fields["ext_recipient"].initial = alias.extmboxes
def clean_ext_recipient(self):
local_part, domname = split_mailbox(self.cleaned_data["ext_recipient"])
try:
domain = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
pass
else:
raise forms.ValidationError(_("Local recipients are forbidden"))
return self.cleaned_data["ext_recipient"]
def save(self, commit=True):
alias = self._save()
if commit:
alias.save([], [self.cleaned_data["ext_recipient"]])
alias.save(self.int_rcpts, self.ext_rcpts)
self.save_m2m()
return alias
class ImportDataForm(forms.Form):
sourcefile = forms.FileField(label=ugettext_lazy("Select a file"))
sepchar = forms.CharField(label=ugettext_lazy("Separator"), max_length=1, required=False)
sepchar = forms.CharField(
label=ugettext_lazy("Separator"), max_length=1, required=False
)
continue_if_exists = forms.BooleanField(
label=ugettext_lazy("Continue on error"), required=False,
help_text=ugettext_lazy("Don't treat duplicated objects as error")
@@ -483,7 +392,11 @@ class AccountFormGeneral(forms.ModelForm):
self.fields["is_active"].label = _("Enabled")
self.user = user
if user.group == "DomainAdmins":
del self.fields["role"]
#del self.fields["role"]
self.fields["role"] = forms.CharField(
label="",
widget=forms.HiddenInput, required=False
)
else:
self.fields["role"].choices = \
[('', ugettext_lazy("Choose"))] + get_account_roles(user)
@@ -494,16 +407,16 @@ class AccountFormGeneral(forms.ModelForm):
and args[0].get("password2", "") == ""):
self.fields["password1"].required = False
self.fields["password2"].required = False
if user.group != "DomainAdmins":
u = kwargs["instance"]
if u.is_superuser:
role = "SuperAdmins"
else:
try:
role = u.groups.all()[0].name
except IndexError:
role = "SimpleUsers"
self.fields["role"].initial = role
u = kwargs["instance"]
self.fields["role"].initial = u.group
def clean_role(self):
if self.user.group == "DomainAdmins":
if self.instance == self.user:
return "DomainAdmins"
return "SimpleUsers"
return self.cleaned_data["role"]
def clean_username(self):
from django.core.validators import validate_email
@@ -530,12 +443,12 @@ class AccountFormGeneral(forms.ModelForm):
if self.cleaned_data["password1"] != "":
account.set_password(self.cleaned_data["password1"])
account.save()
role = None
if "role" in self.cleaned_data:
role = self.cleaned_data["role"]
elif self.user.group == "DomainAdmins" and self.user != account:
role = "SimpleUsers"
account.set_role(role)
# role = None
# if "role" in self.cleaned_data:
# role = self.cleaned_data["role"]
# elif self.user.group == "DomainAdmins" and self.user != account:
# role = "SimpleUsers"
account.set_role(self.cleaned_data["role"])
return account
@@ -683,8 +596,8 @@ class AccountPermissionsForm(forms.Form, DynamicForm):
self._load_from_qdict(args[0], "domains", DomainNameField)
def save(self):
current_domains = map(lambda dom: dom.name, self.account.get_domains())
for name, value in self.cleaned_data.iteritems():
current_domains = [dom.name for dom in self.account.get_domains()]
for name, value in self.cleaned_data.items():
if not name.startswith("domains"):
continue
if value in ["", None]:
@@ -0,0 +1,128 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding M2M table for field aliases on 'Alias'
db.create_table(u'admin_alias_aliases', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('from_alias', models.ForeignKey(orm[u'admin.alias'], null=False)),
('to_alias', models.ForeignKey(orm[u'admin.alias'], null=False))
))
db.create_unique(u'admin_alias_aliases', ['from_alias_id', 'to_alias_id'])
def backwards(self, orm):
# Removing M2M table for field aliases on 'Alias'
db.delete_table('admin_alias_aliases')
models = {
u'admin.alias': {
'Meta': {'ordering': "['domain__name', 'address']", 'unique_together': "(('address', 'domain'),)", 'object_name': 'Alias'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '254'}),
'aliases': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['admin.Alias']", 'symmetrical': 'False'}),
'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['admin.ObjectDates']"}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['admin.Domain']"}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'extmboxes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'mboxes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['admin.Mailbox']", 'symmetrical': 'False'})
},
u'admin.domain': {
'Meta': {'ordering': "['name']", 'object_name': 'Domain'},
'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['admin.ObjectDates']"}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'quota': ('django.db.models.fields.IntegerField', [], {})
},
u'admin.domainalias': {
'Meta': {'object_name': 'DomainAlias'},
'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['admin.ObjectDates']"}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['admin.Domain']"})
},
u'admin.extension': {
'Meta': {'object_name': 'Extension'},
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
},
u'admin.mailbox': {
'Meta': {'object_name': 'Mailbox'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['admin.ObjectDates']"}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['admin.Domain']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'quota': ('django.db.models.fields.PositiveIntegerField', [], {}),
'use_domain_quota': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['admin.User']"})
},
u'admin.objectaccess': {
'Meta': {'unique_together': "(('user', 'content_type', 'object_id'),)", 'object_name': 'ObjectAccess'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_owner': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['admin.User']"})
},
u'admin.objectdates': {
'Meta': {'object_name': 'ObjectDates'},
'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_modification': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
u'admin.quota': {
'Meta': {'object_name': 'Quota'},
'bytes': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'mbox': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'quota_value'", 'unique': 'True', 'null': 'True', 'to': u"orm['admin.Mailbox']"}),
'messages': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'username': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'primary_key': 'True'})
},
u'admin.user': {
'Meta': {'ordering': "['username']", 'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '254', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '254'})
},
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
}
}
complete_apps = ['admin']
+55 -25
View File
@@ -6,7 +6,9 @@ from django.db.models.signals import pre_delete
from django.dispatch import receiver
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.contrib.auth.models import UserManager, Group, AbstractBaseUser, PermissionsMixin
from django.contrib.auth.models import (
UserManager, Group, AbstractBaseUser, PermissionsMixin
)
from django.utils.translation import ugettext as _, ugettext_lazy
from django.utils.crypto import constant_time_compare
from django.utils import timezone
@@ -220,7 +222,7 @@ class User(AbstractBaseUser, PermissionsMixin):
try:
return self.groups.all()[0].name
except IndexError:
return "?"
return "SimpleUsers"
@property
def enabled(self):
@@ -276,8 +278,8 @@ class User(AbstractBaseUser, PermissionsMixin):
accounts = User.objects.select_related().filter(q)
aliases = []
if not idtfilter or ("alias" in idtfilter
or "forward" in idtfilter
if not idtfilter or ("alias" in idtfilter
or "forward" in idtfilter
or "dlist" in idtfilter):
alct = get_content_type(Alias)
ids = self.objectaccess_set.filter(content_type=alct) \
@@ -294,7 +296,7 @@ class User(AbstractBaseUser, PermissionsMixin):
q &= Q(address__icontains=squery) | Q(domain__name__icontains=squery)
aliases = Alias.objects.select_related().filter(q)
if idtfilter:
aliases = filter(lambda a: a.type in idtfilter, aliases)
aliases = [al for al in aliases if al.type in idtfilter]
return chain(accounts, aliases)
@@ -351,7 +353,13 @@ class User(AbstractBaseUser, PermissionsMixin):
else:
qf = Q(pk__in=ids)
return Mailbox.objects.filter(qf)
def get_aliases(self):
from modoboa.lib.permissions import get_content_type
ids = self.objectaccess_set.filter(content_type=get_content_type(Alias)) \
.values_list('object_id', flat=True)
return Alias.objects.filter(pk__in=ids)
def get_mbaliases(self):
"""Return the mailbox aliases that belong to this user
@@ -446,6 +454,9 @@ class User(AbstractBaseUser, PermissionsMixin):
self.groups.add(Group.objects.get(name=role))
except Group.DoesNotExist:
self.groups.add(Group.objects.get(name="SimpleUsers"))
if self.group != "SimpleUsers" and not self.can_access(self):
from modoboa.lib.permissions import grant_access_to_object
grant_access_to_object(self, self)
self.save()
def post_create(self, creator):
@@ -515,8 +526,8 @@ class User(AbstractBaseUser, PermissionsMixin):
dom.add_admin(self)
def to_csv(self, csvwriter):
row = ["account", self.username.encode("utf-8"), self.password,
self.first_name.encode("utf-8"), self.last_name.encode("utf-8"),
row = ["account", self.username.encode("utf-8"), self.password,
self.first_name.encode("utf-8"), self.last_name.encode("utf-8"),
self.is_active, self.group, self.email]
if self.group == "DomainAdmins":
row += [dom.name for dom in self.get_domains()]
@@ -559,6 +570,7 @@ class ObjectDates(models.Model):
dates.save()
obj.dates = dates
class DatesAware(models.Model):
"""Abstract model to support dates
@@ -954,12 +966,12 @@ def mailbox_deleted_handler(sender, **kwargs):
In order to properly handle deletions (ie. we don't want to leave
orphan records into the db), we define this custom receiver.
It manually removes the mailbox from the aliases it is linked to
and then remove all empty aliases.
"""
from modoboa.lib.permissions import ungrant_access_to_object
mb = kwargs['instance']
events.raiseEvent("DeleteMailbox", mb)
ungrant_access_to_object(mb)
@@ -967,7 +979,7 @@ def mailbox_deleted_handler(sender, **kwargs):
alias.mboxes.remove(mb)
if alias.mboxes.count() == 0:
alias.delete()
class Quota(models.Model):
username = models.EmailField(primary_key=True)
@@ -981,20 +993,26 @@ class Alias(DatesAware):
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.")
)
)
domain = models.ForeignKey(Domain)
mboxes = models.ManyToManyField(
Mailbox, verbose_name=ugettext_lazy('mailboxes'),
help_text=ugettext_lazy("The mailboxes this alias points to")
)
)
aliases = models.ManyToManyField(
'Alias',
help_text=ugettext_lazy("The aliases this alias points to")
)
extmboxes = models.TextField(blank=True)
enabled = models.BooleanField(ugettext_lazy('enabled'),
help_text=ugettext_lazy("Check to activate this alias"))
enabled = models.BooleanField(
ugettext_lazy('enabled'),
help_text=ugettext_lazy("Check to activate this alias")
)
class Meta:
permissions = (
("view_aliases", "View aliases"),
)
)
unique_together = (("address", "domain"),)
ordering = ["domain__name", "address"]
@@ -1028,12 +1046,12 @@ class Alias(DatesAware):
@property
def tags(self):
labels = {
"dlist" : _("distribution list"),
"forward" : _("forward"),
"alias" : _("alias")
}
"dlist": _("distribution list"),
"forward": _("forward"),
"alias": _("alias")
}
altype = self.type
return [{"name" : altype, "label" : labels[altype], "type" : "idt"}]
return [{"name": altype, "label": labels[altype], "type": "idt"}]
def post_create(self, creator):
from modoboa.lib.permissions import grant_access_to_object
@@ -1056,10 +1074,18 @@ class Alias(DatesAware):
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:
if isinstance(t, Alias):
if not t in curaliases:
self.aliases.add(t)
continue
if not t in curmboxes:
self.mboxes.add(t)
for t in curaliases:
if not t in int_rcpts:
self.aliases.remove(t)
for t in curmboxes:
if not t in int_rcpts:
self.mboxes.remove(t)
@@ -1076,15 +1102,17 @@ class Alias(DatesAware):
Internal and external addresses are mixed into a single list.
"""
result = map(lambda mb: mb.full_address, self.mboxes.all())
result = [al.full_address for al in self.aliases.all()]
result += [mb.full_address for mb in self.mboxes.all()]
if self.extmboxes != "":
result += self.extmboxes.split(',')
return result
def get_recipients_count(self):
total = 0
if self.extmboxes != "":
return self.mboxes.count() + len(self.extmboxes.split(','))
return self.mboxes.count()
total += len(self.extmboxes.split(','))
return total + self.aliases.count() + self.mboxes.count()
def ui_disabled(self, user):
if user.is_superuser:
@@ -1129,12 +1157,13 @@ class Alias(DatesAware):
row += self.get_recipients()
csvwriter.writerow(row)
class Extension(models.Model):
name = models.CharField(max_length=150)
enabled = models.BooleanField(
ugettext_lazy('enabled'),
help_text=ugettext_lazy("Check to enable this extension")
)
)
def __init__(self, *args, **kwargs):
super(Extension, self).__init__(*args, **kwargs)
@@ -1185,6 +1214,7 @@ class Extension(models.Model):
events.raiseEvent("ExtDisabled", self)
def populate_callback(user):
"""Populate callback
+5 -3
View File
@@ -359,7 +359,8 @@ Identities.prototype = {
generalform_init: function() {
$("#id_role").change($.proxy(function(e) {
var $this = $(e.target);
if ($this.attr("value") == "SimpleUsers") {
var value = $this.attr("value");
if (value == "SimpleUsers" || value == "") {
this.simpleuser_mode();
} else {
this.normal_mode();
@@ -395,7 +396,8 @@ Identities.prototype = {
},
mailform_prefill: function() {
if (!$("#id_role").length || $("#id_role").attr("value") == "SimpleUsers") {
var $role = $("#id_role");
if (!$role.length || $role.attr("value") == "" || $role.attr("value") == "SimpleUsers") {
$("#id_email").attr("value", $("#id_username").attr("value"));
}
},
@@ -434,7 +436,7 @@ Identities.prototype = {
});
$("#id_recipients").dynamic_input();
$("#id_int_recipient").autocompleter({
choices: get_mboxes_list
choices: get_allowed_recipients
});
$(".submit").one('click', $.proxy(function(e) {
simple_ajax_form_post(e, {
@@ -16,8 +16,8 @@ function get_account_list(container) {
});
}
function get_mboxes_list(container) {
$.getJSON("{% url 'modoboa.admin.views.mboxes_list' %}", function(data) {
function get_allowed_recipients(container) {
$.getJSON("{% url 'modoboa.admin.views.allowed_recipients_list' %}", function(data) {
container.value = data;
});
}
+1 -1
View File
@@ -220,7 +220,7 @@ def identity_modify_link(identity, active_tab='default'):
linkdef["url"] += "?active_tab=%s" % active_tab
linkdef["modalcb"] = "admin.editaccount_cb"
else:
linkdef["url"] = reverse("modoboa.admin.views.editalias_dispatcher", args=[identity.id])
linkdef["url"] = reverse("modoboa.admin.views.editalias", args=[identity.id])
linkdef["modalcb"] = "admin.aliasform_cb"
return render_link(linkdef)
+5 -5
View File
@@ -192,7 +192,7 @@ class AliasTestCase(ModoTestCase):
dlist = Alias.objects.get(address="all", domain__name="test.com")
self.assertEqual(len(dlist.get_recipients()), 3)
del values["recipients_1"]
self.check_ajax_post(reverse("modoboa.admin.views.editalias_dispatcher", args=[dlist.id]),
self.check_ajax_post(reverse("modoboa.admin.views.editalias", args=[dlist.id]),
values)
self.assertEqual(dlist.get_recipients_count(), 2)
@@ -202,13 +202,13 @@ class AliasTestCase(ModoTestCase):
address="all", domain__name="test.com")
def test_forward(self):
values = dict(email="forward@test.com", ext_recipient="rcpt@dest.com")
values = dict(email="forward@test.com", recipients="rcpt@dest.com")
self.check_ajax_post(reverse("modoboa.admin.views.newforward"), values)
fwd = Alias.objects.get(address="forward", domain__name="test.com")
self.assertEqual(len(fwd.get_recipients()), 1)
self.assertEqual(fwd.get_recipients_count(), 1)
values["recipient"] = "rcpt2@dest.com"
self.check_ajax_post(reverse("modoboa.admin.views.editalias_dispatcher", args=[fwd.id]),
values["recipients"] = "rcpt2@dest.com"
self.check_ajax_post(reverse("modoboa.admin.views.editalias", args=[fwd.id]),
values)
self.assertEqual(fwd.get_recipients_count(), 1)
+2 -3
View File
@@ -32,8 +32,6 @@ urlpatterns = patterns(
(r'^accounts/edit/(?P<accountid>\d+)/$', 'editaccount'),
(r'^accounts/delete/$', 'delaccount'),
(r'^mailboxes/list/$', 'mboxes_list'),
(r'^distriblists/new/$', 'newdlist'),
(r'^distriblists/delete/$', 'deldlist'),
@@ -41,6 +39,7 @@ urlpatterns = patterns(
(r'^forwards/delete/$', 'delforward'),
(r'^aliases/new/$', 'newalias'),
(r'^aliases/edit/(?P<alid>\d+)/$', 'editalias_dispatcher'),
(r'^aliases/edit/(?P<alid>\d+)/$', 'editalias'),
(r'^aliases/delete/$', 'delalias'),
(r'^aliases/allowed_recipients/$', 'allowed_recipients_list'),
)
+70 -89
View File
@@ -69,28 +69,6 @@ def domains_list(request):
return ajax_simple_response(doms)
def _validate_domain(request, form, successmsg, commonctx, tplname,
callback=None, tpl_form_name="form"):
"""Domain form validation
Common function shared between creation and modification actions.
"""
error = None
if form.is_valid():
try:
domain = form.save(request.user)
except AdminError, e:
error = str(e)
else:
if callback is not None:
callback(request.user, domain)
return ajax_simple_response({"status" : "ok", "respmsg" : successmsg})
commonctx[tpl_form_name] = form
commonctx["error"] = error
return ajax_response(request, status="ko", template=tplname, **commonctx)
@login_required
@permission_required("admin.add_domain")
@transaction.commit_on_success
@@ -167,13 +145,21 @@ def editdomain(request, dom_id, tplname="admin/editdomainform.html"):
"formid": "domform",
"domain": domain}
if request.method == "POST":
def editdomain_cb(user, newdomain):
events.raiseEvent("DomainModified", domain)
error = None
domain.oldname = domain.name
form = DomainForm(request.user, request.POST, instances=instances)
return _validate_domain(request, form, _("Domain modified"), commonctx, tplname,
tpl_form_name="tabs", callback=editdomain_cb)
if form.is_valid():
try:
domain = form.save(request.user)
except AdminError, e:
error = str(e)
else:
events.raiseEvent("DomainModified", domain)
return ajax_simple_response({"status": "ok", "respmsg": _("Domain modified")})
commonctx["tabs"] = form
commonctx["error"] = error
return ajax_response(request, status="ko", template=tplname, **commonctx)
commonctx["tabs"] = DomainForm(request.user, instances=instances)
commonctx["domadmins"] = domadmins
@@ -227,17 +213,26 @@ def _validate_alias(request, form, successmsg, tplname, commonctx, callback=None
return ajax_response(request, status="ko", template=tplname, **commonctx)
def _new_alias(request, tplname, formclass, ctx, successmsg):
def _new_alias(request, title, action, successmsg,
tplname="common/generic_modal_form.html"):
events.raiseEvent("CanCreate", request.user, "mailbox_aliases")
ctx.update(action_label=_("Create"), action_classes="submit")
ctx = {
"title": title,
"action": action,
"formid": "aliasform",
"action_label": _("Create"),
"action_classes": "submit"
}
if request.method == "POST":
def callback(user, alias):
alias.post_create(user)
form = formclass(request.user, request.POST)
return _validate_alias(request, form, successmsg, tplname, ctx, callback)
form = AliasForm(request.user, request.POST)
return _validate_alias(
request, form, successmsg, tplname, ctx, callback
)
form = formclass(request.user)
form = AliasForm(request.user)
ctx["form"] = form
return render(request, tplname, ctx)
@@ -246,75 +241,58 @@ def _new_alias(request, tplname, formclass, ctx, successmsg):
@permission_required("admin.add_alias")
@transaction.commit_on_success
def newdlist(request):
ctx = dict(title=_("New distribution list"),
action=reverse(newdlist),
formid="aliasform")
return _new_alias(request, "common/generic_modal_form.html",
DlistForm, ctx, _("Distribution list created"))
return _new_alias(
request, _("New distribution list"), reverse(newdlist),
_("Distribution list created")
)
@login_required
@permission_required("admin.add_alias")
@transaction.commit_on_success
def newalias(request):
ctx = dict(title=_("New alias"),
action=reverse(newalias),
formid="aliasform")
return _new_alias(request, "common/generic_modal_form.html",
AliasForm, ctx, _("Alias created"))
return _new_alias(
request, _("New alias"), reverse(newalias),
_("Alias created")
)
@login_required
@permission_required("admin.add_alias")
@transaction.commit_on_success
def newforward(request):
ctx = dict(title=_("New forward"),
action=reverse(newforward),
formid="aliasform")
return _new_alias(request, "common/generic_modal_form.html",
ForwardForm, ctx, _("Forward created"))
def _edit_alias(request, alias, tplname, formclass, ctx, successmsg):
if not request.user.can_access(alias):
raise PermDeniedException
ctx.update(title=alias.full_address, action_label=_("Update"),
action_classes="submit")
if request.method == "POST":
form = formclass(request.user, request.POST, instance=alias)
return _validate_alias(request, form, successmsg, tplname, ctx)
form = formclass(request.user, instance=alias)
ctx["form"] = form
return render(request, tplname, ctx)
def editalias(request, alias, ctx):
return _edit_alias(request, alias, "common/generic_modal_form.html",
AliasForm, ctx, _("Alias modified"))
def editdlist(request, alias, ctx):
return _edit_alias(request, alias, "admin/dlistform.html",
DlistForm, ctx, _("Distribution list modified"))
def editforward(request, alias, ctx):
return _edit_alias(request, alias, "common/generic_modal_form.html",
ForwardForm, ctx, _("Forward modified"))
return _new_alias(
request, _("New forward"), reverse(newforward),
_("Forward created")
)
@login_required
@permission_required("admin.change_alias")
def editalias_dispatcher(request, alid):
def editalias(request, alid, tplname="common/generic_modal_form.html"):
alias = Alias.objects.get(pk=alid)
ctx = dict(action=reverse(editalias_dispatcher,
args=[alias.id]), formid="aliasform")
if not request.user.can_access(alias):
raise PermDeniedException
ctx = dict(
action=reverse(editalias, args=[alias.id]),
formid="aliasform",
title=alias.full_address,
action_label=_("Update"),
action_classes="submit"
)
if len(alias.get_recipients()) >= 2:
return editdlist(request, alias, ctx)
if alias.extmboxes != "":
return editforward(request, alias, ctx)
return editalias(request, alias, ctx)
successmsg = _("Distribution list modified")
elif alias.extmboxes != "":
successmsg = _("Forward modified")
else:
successmsg = _("Alias modified")
if request.method == "POST":
form = AliasForm(request.user, request.POST, instance=alias)
return _validate_alias(request, form, successmsg, tplname, ctx)
form = AliasForm(request.user, instance=alias)
ctx["form"] = form
return render(request, tplname, ctx)
def _del_alias(request, msg, msgs):
@@ -388,16 +366,19 @@ def identities(request, tplname="admin/identities.html"):
@login_required
@permission_required("admin.add_user")
def accounts_list(request):
accs = User.objects.filter(is_superuser=False).exclude(groups__name='SimpleUsers')
accs = User.objects.filter(is_superuser=False) \
.exclude(groups__name='SimpleUsers')
res = [a.username for a in accs.all()]
return ajax_simple_response(res)
@login_required
@permission_required("admin.add_mailbox")
def mboxes_list(request):
mboxes = request.user.get_mailboxes()
return ajax_simple_response([mb.full_address for mb in mboxes])
@user_passes_test(lambda u: u.has_perm("admin.add_user")
or u.has_perm("admin.add_alias"))
def allowed_recipients_list(request):
ret = [mb.full_address for mb in request.user.get_mailboxes()]
ret += [al.full_address for al in request.user.get_aliases()]
return ajax_simple_response(ret)
@login_required
@@ -452,7 +433,7 @@ def newaccount(request, tplname='common/wizard_forms.html'):
action=reverse(newaccount),
formid="newaccount_form",
submit_label=_("Create")
)
)
cwizard = CreationWizard(create_account)
cwizard.add_step(AccountFormGeneral, _("General"),
[dict(classes="btn-inverse next", label=_("Next"))],