mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
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:
+49
-136
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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"))],
|
||||
|
||||
Reference in New Issue
Block a user