diff --git a/admin/forms.py b/admin/forms.py
index f0f1bc7c8..5d9a5cb71 100644
--- a/admin/forms.py
+++ b/admin/forms.py
@@ -1,15 +1,25 @@
# 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_noop
+from django.http import QueryDict
from modoboa.admin.templatetags.admin_extras import gender
-from modoboa.lib import tables
from modoboa.lib.emailutils import split_mailbox
+from modoboa.lib.permissions import get_account_roles
+from modoboa.lib.formutils import *
+from modoboa.lib.permissions import *
+
+class DomainForm(forms.ModelForm, DynamicForm):
+ aliases = DomainNameField(
+ label=ugettext_noop("Alias(es)"),
+ required=False,
+ help_text=ugettext_noop("Alias(es) of this domain")
+ )
-class DomainForm(forms.ModelForm):
class Meta:
model = Domain
- fields = ("name", "quota", "enabled")
+ fields = ("name", "quota", "aliases", "enabled")
def __init__(self, *args, **kwargs):
self.oldname = None
@@ -17,240 +27,235 @@ class DomainForm(forms.ModelForm):
self.oldname = kwargs["instance"].name
super(DomainForm, self).__init__(*args, **kwargs)
- def save(self, force_insert=False, force_update=False, commit=True):
+ if len(args) and isinstance(args[0], QueryDict):
+ self._load_from_qdict(args[0], "aliases", DomainNameField)
+ elif kwargs.has_key("instance"):
+ d = kwargs["instance"]
+ for pos, dalias in enumerate(d.domainalias_set.all()):
+ name = "aliases_%d" % (pos + 1)
+ self._create_field(forms.CharField, name, dalias.name, 3)
+
+ def clean(self):
+ super(DomainForm, self).clean()
+ if len(self._errors):
+ raise forms.ValidationError(self._errors)
+
+ cleaned_data = self.cleaned_data
+ name = cleaned_data["name"]
+
+ 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"]
+
+ errors = []
+ 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):
d = super(DomainForm, self).save(commit=False)
if commit:
if self.oldname is not None and d.name != self.oldname:
if not d.rename_dir(self.oldname):
raise AdminError(_("Failed to rename domain, check permissions"))
d.save()
+ for k, v in self.cleaned_data.iteritems():
+ if not k.startswith("aliases"):
+ continue
+ if v in ["", None]:
+ continue
+ try:
+ d.domainalias_set.get(name=v)
+ except DomainAlias.DoesNotExist:
+ pass
+ else:
+ continue
+ events.raiseEvent("CanCreate", user, "domain_aliases")
+ al = DomainAlias(name=v, target=d, enabled=d.enabled)
+ al.save()
+ grant_access_to_object(user, al, is_owner=True)
+ events.raiseEvent("DomainAliasCreated", user, al)
+
+ for dalias in d.domainalias_set.all():
+ if not len(filter(lambda name: self.cleaned_data[name] == dalias.name,
+ self.cleaned_data.keys())):
+ events.raiseEvent("DomainAliasDeleted", dalias)
+ ungrant_access_to_object(dalias)
+ dalias.delete()
return d
-class ProxyForm(forms.ModelForm):
- def __init__(self, user, *args, **kwargs):
- super(ProxyForm, self).__init__(*args, **kwargs)
-
- if not user.is_superuser:
- self._domains = user.get_domains()
- else:
- self._domains = None
-
-class DomainAliasForm(ProxyForm):
- class Meta:
- model = DomainAlias
- fields = ('name', 'target', 'enabled')
-
- def __init__(self, *args, **kwargs):
- super(DomainAliasForm, self).__init__(*args, **kwargs)
- if self._domains is not None:
- #self.fields["target"].queryset = Domain.objects.filter(pk=self._domain.id)
- self.fields["target"].queryset = self._domains
-
-class MailboxForm(ProxyForm):
- name = forms.CharField(
- label=ugettext_noop("Name"), max_length=100,
- help_text=ugettext_noop("First name and last name of mailbox owner")
+class DlistForm(forms.ModelForm, DynamicForm):
+ email = forms.EmailField(
+ label=ugettext_noop("Email address"),
+ help_text=ugettext_noop("")
+ )
+ recipients = forms.EmailField(
+ label=ugettext_noop("Recipients"), required=False,
+ help_text=ugettext_noop("Mailbox(es) this alias will point to")
)
- quota = forms.IntegerField(label=ugettext_noop("Quota"), required=False,
- help_text=ugettext_noop("Mailbox quota in MB (default to domain quota if blank)"))
- enabled = forms.BooleanField(label=gender("Enabled", "f"), required=False,
- initial=True,
- help_text=ugettext_noop("Check to activate this mailbox"))
-
- class Meta:
- model = Mailbox
- fields = ('domain', 'address')
-
- def __init__(self, *args, **kwargs):
- super(MailboxForm, self).__init__(*args, **kwargs)
- for f in ['name', 'address', 'quota']:
- self.fields[f].widget.attrs['size'] = 14
- if self._domains is not None:
- self.fields["domain"].queryset = self._domains
- #self.fields["domain"].queryset = Domain.objects.filter(pk=self._domain.id)
- #self.fields["domain"].initial = self._domain
-
- if kwargs.has_key("instance"):
- mb = kwargs["instance"]
- self.fields['name'].initial = unicode(mb.user)
- self.fields['quota'].initial = mb.quota
- self.fields['enabled'].initial = mb.user.is_active
-
- def clean_address(self):
- if self.cleaned_data["address"].find('@') != -1:
- return self.cleaned_data["address"].rsplit("@", 1)[0]
- return self.cleaned_data["address"]
-
- def commit_save(self, mb, **kwargs):
- mb.save(name=self.cleaned_data["name"],
- enabled=self.cleaned_data["enabled"],
- quota=self.cleaned_data["quota"],
- **kwargs)
-
- def save(self, force_insert=False, force_update=False, commit=True):
- m = super(MailboxForm, self).save(commit=False)
- if commit:
- self.commit_save(m)
- return m
-
-class MailboxWithPasswordForm(MailboxForm):
- password1 = forms.CharField(label=ugettext_noop("Password"),
- widget=forms.PasswordInput(render_value=True),
- help_text=ugettext_noop("Password used to log in"))
- password2 = forms.CharField(label=ugettext_noop("Confirmation"),
- widget=forms.PasswordInput(render_value=True),
- help_text=ugettext_noop("Password confirmation"))
-
- def __init__(self, *args, **kwargs):
- super(MailboxWithPasswordForm, self).__init__(*args, **kwargs)
- for f in ['password1', 'password2']:
- self.fields[f].widget.attrs['size'] = 14
- self.fields.keyOrder = ['name', 'domain', 'address', 'quota',
- 'password1', 'password2', 'enabled']
-
- def clean_password2(self):
- if self.cleaned_data["password1"] != self.cleaned_data["password2"]:
- raise forms.ValidationError(_("Passwords mismatch"))
- return self.cleaned_data["password2"]
-
- def commit_save(self, mb):
- super(MailboxWithPasswordForm, self).commit_save(
- mb, password=self.cleaned_data["password1"]
- )
-
-class AliasForm(ProxyForm):
- targets = forms.CharField(label=ugettext_noop("Target(s)"), required=False,
- help_text=ugettext_noop("Mailbox(es) this alias will point to"))
-
- def __init__(self, *args, **kwargs):
- super(AliasForm, self).__init__(*args, **kwargs)
- self.fields['address'].widget.attrs['size'] = 14
-
- # This part is a little bit tricky. As 'domain' is not a
- # member of Alias, we need to fill it manually. if
- # self._domain exists, it means a DomainAdmin is going to
- # create a new alias, we know how to fill the fields.
- # if self._domain is not None:
- # self.fields["domain"].queryset = Domain.objects.filter(pk=self._domain.id)
-
- if self._domains is not None:
- self.fields["domain"].queryset = self._domains
-
- # if 'instance' exists, it means we are modifying an existing
- # record, we need to select the right value for 'domain' and
- # to fill the first "targets" input (because it is created by the
- # Form object, not by us).
- #domain = None
- # if self._domain:
- # domain = self._domain
- if kwargs.has_key("instance"):
- # if self._domain is None:
- # domain = kwargs["instance"].domain
- self.fields["targets"].initial = kwargs["instance"].first_target()
-
- # if domain is not None:
- # self.fields["domain"].initial = domain
-
- self.fields.keyOrder = ['address', 'domain', 'enabled', 'targets']
class Meta:
model = Alias
- fields = ('address', 'domain', 'enabled')
+ fields = ("enabled",)
- def clean_address(self):
- if self.cleaned_data["address"].find('@') != -1:
- return self.cleaned_data["address"].rsplit("@", 1)[0]
- return self.cleaned_data["address"]
+ def __init__(self, *args, **kwargs):
+ super(DlistForm, self).__init__(*args, **kwargs)
+ self.fields.keyOrder = ['email', 'recipients', 'enabled']
- def set_targets(self, user, values):
- """Targets dispatching
+ if len(args) and isinstance(args[0], QueryDict):
+ self._load_from_qdict(args[0], "recipients", forms.EmailField)
+ elif kwargs.has_key("instance"):
+ dlist = kwargs["instance"]
+ self.fields["email"].initial = dlist.full_address
+ cpt = 1
+ for mb in dlist.mboxes.all():
+ name = "recipients_%d" % (cpt)
+ self._create_field(forms.EmailField, name, mb.full_address, 2)
+ cpt += 1
+ for addr in dlist.extmboxes.split(','):
+ if addr == "":
+ continue
+ name = "recipients_%d" % (cpt)
+ self._create_field(forms.EmailField, name, addr, 2)
+ cpt += 1
- We make a difference between 'local' targets (the ones hosted
- by Modoboa) and 'external targets.
+ def clean_email(self):
+ localpart, domname = split_mailbox(self.cleaned_data["email"])
+ try:
+ Domain.objects.get(name=domname)
+ except Domain.DoesNotExist:
+ raise forms.ValidationError(_("Domain does not exist"))
+ return self.cleaned_data["email"]
+
+ def set_recipients(self, user):
+ """Recipients dispatching
+
+ We make a difference between 'local' recipients (the ones hosted
+ by Modoboa) and 'external' recipients.
"""
- self.ext_targets = []
- self.int_targets = []
+ self.ext_rcpts = []
+ self.int_rcpts = []
+ total = 0
- for addr in values:
- if addr == "":
+ for k, v in self.cleaned_data.iteritems():
+ if not k.startswith("recipients"):
continue
- local_part, domname = split_mailbox(addr)
+ if v == "":
+ continue
+ local_part, domname = split_mailbox(v)
if domname is None:
- raise AdminError("%s %s" % (_("Invalid mailbox"), addr))
+ raise AdminError("%s %s" % (_("Invalid mailbox"), v))
try:
domain = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
domain = None
if domain:
if not user.is_owner(domain):
- raise PermDeniedException(addr)
+ raise PermDeniedException(v)
try:
mb = Mailbox.objects.get(domain=domain, address=local_part)
except Mailbox.DoesNotExist:
- raise AdminError(_("Mailbox %s does not exist" % addr))
- self.int_targets += [mb]
+ raise AdminError(_("Mailbox %s does not exist" % v))
+ self.int_rcpts += [mb]
+ total += 1
continue
- self.ext_targets += [addr]
+ self.ext_rcpts += [v]
+ total += 1
- def save(self, force_insert=False, force_update=False, commit=True):
- a = super(AliasForm, self).save(commit=False)
+ 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:
- a.save(self.int_targets, self.ext_targets)
+ dlist.save(self.int_rcpts, self.ext_rcpts)
self.save_m2m()
- return a
+ return dlist
-class SuperAdminForm(forms.Form):
- def __init__(self, user, *args, **kwargs):
- super(SuperAdminForm, self).__init__(*args, **kwargs)
+class ImportDataForm(forms.Form):
+ sourcefile = forms.FileField(label=_("Select a file"))
+ sepcar = forms.CharField(label=_("Separator"), max_length=1, required=False)
- self.fields["user"] = \
- forms.ModelChoiceField([], label=_("User"), required=True,
- empty_label=_("Select a user"),
- help_text=_("Select a user in the list"))
- self.fields["user"].queryset = User.objects.exclude(pk__in=[1, user.id])
+ def __init__(self, *args, **kwargs):
+ super(ImportDataForm, self).__init__(*args, **kwargs)
+ self.fields["sepcar"].widget.attrs = {"class" : "span1"}
-class DomainAdminForm(forms.Form):
- createfrom = forms.ChoiceField(
- choices=[("new", ugettext_noop("Create a new account")),
- ("select", ugettext_noop("Promote an existing account"))],
- widget=forms.RadioSelect)
+ def clean_sepcar(self):
+ if self.cleaned_data["sepcar"] == "":
+ return ";"
+ return str(self.cleaned_data["sepcar"])
- domain = forms.ModelChoiceField(
- queryset=Domain.objects.all(),
- label=_("Domain"),
- required=True,
- empty_label=ugettext_noop("Select a domain"),
- help_text=ugettext_noop("Select a domain in the list")
- )
-
- user = forms.ChoiceField(
- label=ugettext_noop("User"), required=True,
- choices=[("", ugettext_noop("Empty"))],
- help_text=ugettext_noop("Select a user in the list"))
-
-class UserForm(forms.ModelForm):
- createmb = forms.BooleanField(
- label=ugettext_noop("Create mailbox"), required=False,
- help_text=ugettext_noop("Create a mailbox for this user using the given address")
+class AccountFormGeneral(forms.ModelForm):
+ role = forms.ChoiceField(
+ choices=[('', ugettext_noop("Choose"))]
)
class Meta:
model = User
- fields = ("username", "first_name", "last_name", "email")
+ fields = ("username", "first_name", "last_name", "role", "is_active")
- def save(self, commit=True, group=None):
- user = super(UserForm, self).save(commit=False)
- if self.cleaned_data.has_key("password1"):
- user.set_password(self.cleaned_data["password1"])
+ def __init__(self, user, *args, **kwargs):
+ super(AccountFormGeneral, self).__init__(*args, **kwargs)
+
+ self.user = user
+ if user.belongs_to_group("DomainAdmins"):
+ del self.fields["role"]
+ return
+
+ self.fields["role"].choices = \
+ [('', ugettext_noop("Choose"))] + get_account_roles(user)
+ if kwargs.has_key("instance"):
+ u = kwargs["instance"]
+ if u.is_superuser:
+ role = "SuperAdmins"
+ else:
+ try:
+ role = u.groups.all()[0].name
+ except IndexError:
+ pass
+ self.fields["role"].initial = role
+
+ def save(self, commit=True):
+ account = super(AccountFormGeneral, self).save(commit=False)
if commit:
- user.save()
- if group:
- user.groups.add(Group.objects.get(name=group))
- user.save()
- return user
+ account.save()
+ if self.cleaned_data.has_key("role"):
+ role = self.cleaned_data["role"]
+ else:
+ role = "SimpleUsers"
+ if role == "SuperAdmins":
+ account.is_superuser = True
+ else:
+ account.is_superuser = False
+ account.groups.clear()
+ account.groups.add(Group.objects.get(name=role))
+ account.save()
+ return account
-class UserWithPasswordForm(UserForm):
+class AccountFormGeneralPwd(AccountFormGeneral):
password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
password2 = forms.CharField(
label=ugettext_noop("Confirmation"),
@@ -258,6 +263,11 @@ class UserWithPasswordForm(UserForm):
help_text=ugettext_noop("Enter the same password as above, for verification.")
)
+ class Meta:
+ model = User
+ fields = ("username", "first_name", "last_name",
+ "password1", "password2", "role", "is_active")
+
def clean_password2(self):
password1 = self.cleaned_data.get("password1", "")
password2 = self.cleaned_data["password2"]
@@ -265,38 +275,194 @@ class UserWithPasswordForm(UserForm):
raise forms.ValidationError(_("The two password fields didn't match."))
return password2
-class DomainAdminPromotionForm(forms.Form):
- name = forms.CharField(
- label=ugettext_noop("Name"),
- help_text=ugettext_noop("The username/email address/name of the account to promote")
+ def save(self, commit=True, **kwargs):
+ user = super(AccountFormGeneralPwd, self).save(**kwargs)
+ if self.cleaned_data.has_key("password1"):
+ user.set_password(self.cleaned_data["password1"])
+ if commit:
+ user.save()
+ return user
+
+class AccountFormMail(forms.Form, DynamicForm):
+ # FIXME
+ # * ajout du quota
+ # * Renommage de boite ?
+
+ email = forms.EmailField(label=_("E-mail"), required=False)
+ aliases = forms.EmailField(
+ label=ugettext_noop("Alias(es)"),
+ required=False,
+ help_text=ugettext_noop("Alias(es) of this mailbox")
)
- def clean_name(self):
+ def __init__(self, *args, **kwargs):
+ if kwargs.has_key("instance"):
+ self.mb = kwargs["instance"]
+ del kwargs["instance"]
+ super(AccountFormMail, self).__init__(*args, **kwargs)
+ if not hasattr(self, "mb") or self.mb is None:
+ return
+ self.fields["email"].required = True
+ for pos, alias in enumerate(self.mb.alias_set.all()):
+ if len(alias.get_recipients()) >= 2:
+ continue
+ name = "aliases_%d" % (pos + 1)
+ self._create_field(forms.EmailField, name, alias.full_address)
+ self.fields["email"].initial = self.mb.full_address
+ if len(args) and isinstance(args[0], QueryDict):
+ self._load_from_qdict(args[0], "aliases", forms.EmailField)
+
+ def save(self, user, account):
+ if self.cleaned_data["email"] == "":
+ return None
+
+ locpart, domname = split_mailbox(self.cleaned_data["email"])
try:
- u = User.objects.get(username=self.cleaned_data["name"])
- except User.DoesNotExist:
- raise forms.ValidationError(_("Unknown user"))
- return self.cleaned_data["name"]
+ domain = Domain.objects.get(name=domname)
+ except Domain.DoesNotExist:
+ raise AdminError(_("Domain does not exist"))
-class AssignDomainsForm(forms.Form):
- domains = forms.ModelMultipleChoiceField(
- queryset=None,
- label=ugettext_noop("Domains"),
- required=True,
- help_text=ugettext_noop("Select one or more domains in the list")
+ if not user.can_access(domain):
+ raise PermDeniedException(_("You don't have access to this domain"))
+
+ if not hasattr(self, "mb") or self.mb is None:
+ try:
+ self.mb = Mailbox.objects.get(address=locpart, domain=domain)
+ except Mailbox.DoesNotExist:
+ events.raiseEvent("CanCreate", user, "mailboxes")
+ self.mb = Mailbox()
+ self.mb.save_from_user(locpart, domain, account)
+ grant_access_to_object(user, self.mb, is_owner=True)
+ events.raiseEvent("CreateMailbox", user, self.mb)
+ else:
+ if self.cleaned_data["email"] != self.mb.full_address:
+ if self.mb.rename_dir(domname, locpart):
+ self.mb.domain = domain
+ self.mb.address = locpart
+ self.mb.save()
+ else:
+ raise AdminError(_("Failed to rename mailbox, check permissions"))
+
+ account.email = self.cleaned_data["email"]
+ account.save()
+
+ for name, value in self.cleaned_data.iteritems():
+ if not name.startswith("aliases"):
+ continue
+ if value == "":
+ continue
+ local_part, domname = split_mailbox(value)
+ try:
+ self.mb.alias_set.get(address=local_part, domain__name=domname)
+ except Alias.DoesNotExist:
+ pass
+ else:
+ continue
+ events.raiseEvent("CanCreate", user, "mailbox_aliases")
+ al = Alias(address=local_part, enabled=account.is_active)
+ al.domain = Domain.objects.get(name=domname)
+ al.save([self.mb], [])
+ grant_access_to_object(user, al, is_owner=True)
+ events.raiseEvent("MailboxAliasCreated", user, al)
+
+ for alias in self.mb.alias_set.all():
+ if len(alias.get_recipients()) >= 2:
+ continue
+ if not len(filter(lambda name: self.cleaned_data[name] == alias.full_address,
+ self.cleaned_data.keys())):
+ events.raiseEvent("MailboxAliasDeleted", alias)
+ ungrant_access_to_object(alias)
+ alias.delete()
+
+ return self.mb
+
+class AccountPermissionsForm(forms.Form, DynamicForm):
+ domains = DomainNameField(
+ label=ugettext_noop("Domain(s)"),
+ required=False,
+ help_text=ugettext_noop("Domain(s) that user administrates")
)
- def __init__(self, user, domadmin, *args, **kwargs):
- super(AssignDomainsForm, self).__init__(*args, **kwargs)
- self.fields["domains"].queryset = user.get_domains()
- self.fields["domains"].initial = domadmin.get_domains()
+ def __init__(self, *args, **kwargs):
+ if kwargs.has_key("instance"):
+ self.account = kwargs["instance"]
+ del kwargs["instance"]
+
+ super(AccountPermissionsForm, self).__init__(*args, **kwargs)
-class ImportDataForm(forms.Form):
- sourcefile = forms.FileField(label=_("Select a file"))
- sepcar = forms.CharField(label=_("Separator"), max_length=1, required=False)
+ if not hasattr(self, "account") or self.account is None:
+ return
+ for pos, domain in enumerate(self.account.get_domains()):
+ name = "domains_%d" % (pos + 1)
+ self._create_field(DomainNameField, name, domain.name)
+ if len(args) and isinstance(args[0], QueryDict):
+ self._load_from_qdict(args[0], "domains", DomainNameField)
- def clean_sepcar(self):
- if self.cleaned_data["sepcar"] == "":
- return ";"
- return self.cleaned_data["sepcar"]
+ def save(self):
+ current_domains = map(lambda dom: dom.name, self.account.get_domains())
+ for name, value in self.cleaned_data.iteritems():
+ if not name.startswith("domains"):
+ continue
+ if value in ["", None]:
+ continue
+ if not value in current_domains:
+ domain = Domain.objects.get(name=value)
+ grant_access_to_object(self.account, domain)
+
+ for domain in self.account.get_domains():
+ if not len(filter(lambda name: self.cleaned_data[name] == domain.name,
+ self.cleaned_data.keys())):
+ ungrant_access_to_object(domain, self.account)
+
+class AccountForm(TabForms):
+
+ def __init__(self, user, *args, **kwargs):
+ self.user = user
+ self.forms = [
+ dict(id="general", title=_("General"), cls=AccountFormGeneralPwd,
+ new_args=[user], mandatory=True),
+ dict(id="mail", title=_("Mail"), formtpl="admin/mailform.html",
+ cls=AccountFormMail),
+ dict(id="perms", title=_("Permissions"), formtpl="admin/permsform.html",
+ cls=AccountPermissionsForm)
+ ]
+ cbargs = [user]
+ if kwargs.has_key("instances"):
+ cbargs += [kwargs["instances"]["general"]]
+ self.forms += events.raiseQueryEvent("ExtraAccountForm", *cbargs)
+
+ super(AccountForm, self).__init__(*args, **kwargs)
+
+ def check_perms(self, account):
+ if account.is_superuser:
+ return False
+ return account.has_perm("admin.view_domain")
+
+ def _before_is_valid(self, form):
+ if form["id"] == "general":
+ return True
+
+ if hasattr(self, "check_%s" % form["id"]):
+ if not getattr(self, "check_%s" % form["id"])(self.account):
+ return False
+ return True
+
+ if False in events.raiseQueryEvent("CheckExtraAccountForm", self.account, form):
+ return False
+ return True
+
+ def save_general_form(self):
+ self.account = self.forms[0]["instance"].save()
+
+ def save(self):
+ """Custom save method
+
+ As forms interact with each other, it is simpler to make
+ custom code to save them.
+ """
+ self.forms[1]["instance"].save(self.user, self.account)
+ if len(self.forms) <= 2:
+ return
+ for f in self.forms[2:]:
+ f["instance"].save()
diff --git a/admin/lib.py b/admin/lib.py
index 1ad7d6bda..e1e9a2a57 100644
--- a/admin/lib.py
+++ b/admin/lib.py
@@ -1,10 +1,8 @@
# coding: utf-8
-from functools import wraps
-from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext as _
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
-from models import *
-from modoboa.lib.webutils import _render_error, _render
+from modoboa.lib.webutils import _render
+from modoboa.lib import parameters
from modoboa.admin.tables import *
from exceptions import *
@@ -36,98 +34,10 @@ def render_listing(request, objtype, tplname="admin/listing.html",
except ValueError:
page = 1
try:
- kwargs["objects"] = paginator.page(page)
+ kwargs["page"] = paginator.page(page)
except (EmptyPage, PageNotAnInteger):
- kwargs["objects"] = paginator.page(paginator.num_pages)
- kwargs["last_page"] = paginator.num_pages
- kwargs["total"] = paginator.count
- kwargs["table"] = tblclass(request, kwargs["objects"].object_list)
+ kwargs["page"] = paginator.page(paginator.num_pages)
+ kwargs["table"] = tblclass(request, kwargs["page"].object_list)
+ kwargs["selection"] = objtype
return _render(request, tplname, kwargs)
-
-def grant_access_to_object(user, obj, is_owner=False):
- """Grant access to an object for a given user
-
- There are two different cases where we want to grant access to an
- object for a specific user:
-
- * He is the owner (he's just created the object)
- * He is going to administrate the object (but he is not the owner)
-
- :param user: a ``User`` object
- :param obj: an admin. object (Domain, Mailbox, ...)
- :param is_owner: the user is the unique object's owner
- """
- ct = get_content_type(obj)
- try:
- entry = user.objectaccess_set.get(content_type=ct, object_id=obj.id)
- entry.is_owner = is_owner
- entry.save()
- except ObjectAccess.DoesNotExist:
- pass
- else:
- return
-
- try:
- objaccess = ObjectAccess(user=user, content_type=ct,
- object_id=obj.id, is_owner=is_owner)
- objaccess.save()
- except IntegrityError, e:
- raise AdminError(_("Failed to grant access (%s)" % str(e)))
-
-def ungrant_access_to_object(obj, user=None):
- """Ungrant access to an object for a specific user
-
- If no user is provided, all entries referencing this object are
- deleted from the database.
-
- :param obj: an object inheriting from ``models.Model``
- :param user: a ``User`` object
- """
- ct = get_content_type(obj)
- if user:
- try:
- ObjectAccess.objects.get(user=user, content_type=ct, object_id=obj.id).delete()
- except ObjectAccess.DoesNotExist:
- pass
- else:
- ObjectAccess.objects.filter(content_type=ct, object_id=obj.id).delete()
-
-def get_object_owner(obj):
- """Return the unique owner of this object
-
- :param obj: an object inheriting from ``model.Model``
- :return: a ``User`` object
- """
- ct = get_content_type(obj)
- try:
- entry = ObjectAccess.objects.get(content_type=ct, object_id=obj.id, is_owner=True)
- except ObjectAccess.DoesNotExist:
- return None
- return entry.user
-
-def check_domain_access(f):
- """Decorator to check if the current user can access a domain
-
- This decorator only applies to url that contain a 'domid' or
- 'mbid' parameter. (for a direct access)
-
- Raise a ``PermDeniedError`` if the current user can't view the
- requested object.
- """
- @wraps(f)
- def dec(request, **kwargs):
- if not request.user.is_superuser:
- domain = None
- domid = request.GET.get("domid", None)
- if domid:
- domain = Domain.objects.get(pk=domid)
- else:
- mbid = request.GET.get("mbid", None)
- if mbid:
- domain = Mailbox.objects.get(pk=mbid).domain
- if domain and not request.user.can_access(domain):
- raise PermDeniedError(_("You can't access this domain"))
-
- return f(request, **kwargs)
- return dec
diff --git a/admin/migrations/0004_add_permissions.py b/admin/migrations/0004_add_permissions.py
index f1c0ec735..8b3416ce7 100644
--- a/admin/migrations/0004_add_permissions.py
+++ b/admin/migrations/0004_add_permissions.py
@@ -93,4 +93,4 @@ class Migration(DataMigration):
}
}
- complete_apps = ['admin']
+ complete_apps = ['admin', 'contenttypes']
diff --git a/admin/migrations/0018_auto__add_unique_alias_domain_address.py b/admin/migrations/0018_auto__add_unique_alias_domain_address.py
new file mode 100644
index 000000000..41af5c4b1
--- /dev/null
+++ b/admin/migrations/0018_auto__add_unique_alias_domain_address.py
@@ -0,0 +1,121 @@
+# encoding: 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 unique constraint on 'Alias', fields ['domain', 'address']
+ db.create_unique('admin_alias', ['domain_id', 'address'])
+
+
+ def backwards(self, orm):
+
+ # Removing unique constraint on 'Alias', fields ['domain', 'address']
+ db.delete_unique('admin_alias', ['domain_id', 'address'])
+
+
+ models = {
+ 'admin.alias': {
+ 'Meta': {'unique_together': "(('address', 'domain'),)", 'object_name': 'Alias'},
+ 'address': ('django.db.models.fields.CharField', [], {'max_length': '254'}),
+ 'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}),
+ 'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"}),
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'extmboxes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mboxes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['admin.Mailbox']", 'symmetrical': 'False'})
+ },
+ 'admin.domain': {
+ 'Meta': {'object_name': 'Domain'},
+ 'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}),
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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', [], {})
+ },
+ 'admin.domainalias': {
+ 'Meta': {'object_name': 'DomainAlias'},
+ 'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}),
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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': "orm['admin.Domain']"})
+ },
+ 'admin.extension': {
+ 'Meta': {'object_name': 'Extension'},
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'admin.mailbox': {
+ 'Meta': {'object_name': 'Mailbox'},
+ 'address': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}),
+ 'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"}),
+ 'gid': ('django.db.models.fields.IntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'path': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'quota': ('django.db.models.fields.IntegerField', [], {}),
+ 'uid': ('django.db.models.fields.IntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'admin.objectaccess': {
+ 'Meta': {'unique_together': "(('user', 'content_type', 'object_id'),)", 'object_name': 'ObjectAccess'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ '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': "orm['auth.User']"})
+ },
+ 'admin.objectdates': {
+ 'Meta': {'object_name': 'ObjectDates'},
+ 'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_modification': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
+ },
+ 'admin.user': {
+ 'Meta': {'object_name': 'User', 'db_table': "'auth_user'", '_ormbases': ['auth.User'], 'proxy': 'True'}
+ },
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ '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'}),
+ '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']
diff --git a/admin/models.py b/admin/models.py
index b53a2228b..1e771ac3b 100644
--- a/admin/models.py
+++ b/admin/models.py
@@ -24,15 +24,6 @@ try:
except ImportError:
ldap_available = False
-def get_content_type(obj):
- """Simple function that use the right method to retrieve a content type
-
- :param obj: a django model
- :return: a ``ContentType`` object
- """
- return obj.get_content_type() if hasattr(obj, "get_content_type") \
- else ContentType.objects.get_for_model(obj)
-
class User(DUser):
"""Proxy for the ``User`` model.
@@ -50,6 +41,37 @@ class User(DUser):
def __unicode__(self):
return u"%s %s" % (self.first_name, self.last_name)
+ def delete(self, fromuser, keep_mb_dir, *args, **kwargs):
+ """Custom delete method
+ """
+ from modoboa.lib.permissions import \
+ get_object_owner, grant_access_to_object, ungrant_access_to_object
+
+ if not fromuser.can_access(self):
+ raise PermDeniedException(_("You don't have access to this account"))
+ if fromuser == self:
+ raise AdminError(_("You can't delete your own account"))
+ if self.has_mailbox:
+ mb = self.mailbox_set.all()[0]
+ if not fromuser.can_access(mb.domain):
+ raise PermDeniedException(_("You don't have access to this domain"))
+ events.raiseEvent("DeleteMailbox", mb)
+ ungrant_access_to_object(mb)
+ mb.delete(keepdir=keep_mb_dir)
+
+ owner = get_object_owner(self)
+ for ooentry in self.objectaccess_set.all():
+ if ooentry.is_owner:
+ if ooentry.content_object is not None:
+ grant_access_to_object(owner, ooentry.content_object, True)
+ else:
+ print "ORPHELIN!!!"
+ ooentry.delete()
+
+ events.raiseEvent("AccountDeleted", self)
+ ungrant_access_to_object(self)
+ super(User, self).delete(*args, **kwargs)
+
@staticmethod
def get_content_type():
"""An uggly hack to retrieve the appropriate content_type!
@@ -130,6 +152,27 @@ class User(DUser):
return "%s %s" % (self.first_name, self.last_name)
return self.username
+ @property
+ def identity(self):
+ if self.email != "":
+ return self.email
+ return self.username
+
+ @property
+ def name_or_rcpt(self):
+ if self.first_name != "":
+ return "%s %s" % (self.first_name, self.last_name)
+ return "----"
+
+ @property
+ def group(self):
+ if self.is_superuser:
+ return "SuperAdmin"
+ try:
+ return self.groups.all()[0].name
+ except IndexError:
+ return "?"
+
def belongs_to_group(self, name):
"""Simple shortcut to check if this user is a member of a
specific group.
@@ -216,6 +259,8 @@ class User(DUser):
:param obj: an object inheriting from ``models.Model``
:return: a boolean
"""
+ from modoboa.lib.permissions import get_content_type
+
ct = get_content_type(obj)
try:
ooentry = self.objectaccess_set.get(content_type=ct, object_id=obj.id)
@@ -233,6 +278,8 @@ class User(DUser):
:param obj: a admin object
:return: a boolean
"""
+ from modoboa.lib.permissions import get_content_type
+
if self.is_superuser:
return True
@@ -360,8 +407,8 @@ class Domain(DatesAware):
# Not very optimized but currently, this is the simple way
# I've found to delete all related objects (mailboxes, users
# and aliases)!
- for mb in self.mailbox_set.all():
- mb.delete(keepdir=keepdir)
+ # for mb in self.mailbox_set.all():
+ # mb.delete(keepdir=keepdir)
super(Domain, self).delete(*args, **kwargs)
if not keepdir:
self.delete_dir()
@@ -379,14 +426,19 @@ class DomainAlias(DatesAware):
help_text=ugettext_noop("The alias name"))
target = models.ForeignKey(Domain, verbose_name=ugettext_noop('target'),
help_text=ugettext_noop("The domain this alias points to"))
- enabled = models.BooleanField(ugettext_noop('enabled'),
- help_text=ugettext_noop("Check to activate this alias"))
+ enabled = models.BooleanField(
+ ugettext_noop('enabled'),
+ help_text=ugettext_noop("Check to activate this alias")
+ )
class Meta:
permissions = (
("view_domaliases", "View domain aliases"),
)
+ def __unicode__(self):
+ return self.name
+
class Mailbox(DatesAware):
address = models.CharField(
ugettext_noop('address'), max_length=100,
@@ -456,13 +508,14 @@ class Mailbox(DatesAware):
% (abspath, self.mdirroot, dir, sdir))
return True
- def rename_dir(self, domain, newaddress):
+ def rename_dir(self, newdomain, newaddress):
if parameters.get_admin("CREATE_DIRECTORIES") == "yes":
if self.domain.name == domain and newaddress == self.address:
return True
- path = "%s/%s" % (parameters.get_admin("STORAGE_PATH"), domain)
+ oldpath = "%s/%s" % (parameters.get_admin("STORAGE_PATH"), self.domain.name)
+ newpath = "%s/%s" % (parameters.get_admin("STORAGE_PATH"), newdomain)
code = exec_as_vuser("mv %s/%s %s/%s" \
- % (path, self.address, path, newaddress))
+ % (oldpath, self.address, newpath, newaddress))
if code:
self.path = "%s/" % newaddress
return code
@@ -498,8 +551,8 @@ class Mailbox(DatesAware):
user.set_password(kwargs["password"])
try:
user.save()
- except IntegrityError:
- raise AdminError(_("Mailbox with this address already exists"))
+ except IntegrityError, e:
+ raise AdminError(_("Account '%s' already exists" % user.username))
self.user = user
if len(user.groups.all()) == 0:
@@ -538,7 +591,6 @@ class Mailbox(DatesAware):
:param domain: the associated Domain object
:param user: the associated User object
"""
- self.name = "%s %s" % (user.first_name, user.last_name)
self.address = localpart
self.domain = domain
self.user = user
@@ -556,7 +608,7 @@ class Mailbox(DatesAware):
self.gid = v_gid
else:
self.gid = pwd.getpwnam(parameters.get_admin("VIRTUAL_GID")).pw_gid
- self.quota = self.domain.quota
+ self.quota = self.domain.quota
super(Mailbox, self).save()
def create_from_csv(self, user, line):
@@ -591,18 +643,21 @@ class Mailbox(DatesAware):
alias.mboxes.remove(self)
if len(alias.mboxes.all()) == 0:
alias.delete()
- self.user.delete()
super(Mailbox, self).delete(*args, **kwargs)
if not keepdir:
self.delete_dir()
class Alias(DatesAware):
- address = models.CharField(ugettext_noop('address'), max_length=254,
- help_text=ugettext_noop("The alias address (without the domain part). For a 'catch-all' address, just enter an * character."))
+ address = models.CharField(
+ ugettext_noop('address'), max_length=254,
+ help_text=ugettext_noop("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_noop('mailboxes'),
- help_text=ugettext_noop("The mailboxes this alias points to"))
+ mboxes = models.ManyToManyField(
+ Mailbox, verbose_name=ugettext_noop('mailboxes'),
+ help_text=ugettext_noop("The mailboxes this alias points to")
+ )
extmboxes = models.TextField(blank=True)
enabled = models.BooleanField(ugettext_noop('enabled'),
help_text=ugettext_noop("Check to activate this alias"))
@@ -611,64 +666,43 @@ class Alias(DatesAware):
permissions = (
("view_aliases", "View aliases"),
)
+ unique_together = (("address", "domain"),)
@property
def full_address(self):
return "%s@%s" % (self.address, self.domain.name)
@property
- def targets(self):
- ret = "
".join(map(lambda m: str(m), self.mboxes.all()))
- ret += "
" + self.repr_extmboxes()
- return ret
+ def identity(self):
+ return self.full_address
- def save(self, int_targets, ext_targets, *args, **kwargs):
- if len(ext_targets):
- self.extmboxes = ",".join(ext_targets)
+ @property
+ def name_or_rcpt(self):
+ return "%s, ..." % self.get_recipients()[0]
+
+ def save(self, int_rcpts, ext_rcpts, *args, **kwargs):
+ if len(ext_rcpts):
+ self.extmboxes = ",".join(ext_rcpts)
else:
self.extmboxes = ""
super(Alias, self).save(*args, **kwargs)
curmboxes = self.mboxes.all()
- for t in int_targets:
+ for t in int_rcpts:
if not t in curmboxes:
self.mboxes.add(t)
for t in curmboxes:
- if not t in int_targets:
+ if not t in int_rcpts:
self.mboxes.remove(t)
- def first_target(self):
- """Return the first target of this alias
-
- It is taken from internal addresses, or external addresses.
- """
- if len(self.mboxes.all()):
- return self.mboxes.all()[0]
- if len(self.extmboxes):
- return self.extmboxes.split(",")[0]
- return None
-
- def get_targets(self):
- """Return a list containing targeted mailboxes
+ def get_recipients(self):
+ """Return a the recipients list
Internal and external addresses are mixed into a single list.
"""
- result = []
- if len(self.mboxes.all()):
- for mb in self.mboxes.all()[1:]:
- result += [mb.full_address]
- if len(self.extmboxes):
- result += self.extmboxes.split(",")
- else:
- result = self.extmboxes.split(",")[1:]
+ result = map(lambda mb: mb.full_address, self.mboxes.all())
+ result += self.extmboxes.split(',')
return result
- def repr_extmboxes(self):
- """Return external targets as a string
-
- A ready to print representation of external targets.
- """
- return "
".join(self.extmboxes.split(","))
-
def ui_disabled(self, user):
if user.is_superuser:
return False
@@ -699,11 +733,12 @@ class Extension(models.Model):
module.destroy()
def on(self):
- self.load()
- self.init()
self.enabled = True
self.save()
+ self.load()
+ self.init()
+
extdir = "%s/extensions/%s" % (settings.MODOBOA_DIR, self.name)
scriptdir = "%s/%s" % (extdir, "scripts")
if os.path.exists(scriptdir):
diff --git a/admin/permissions.py b/admin/permissions.py
deleted file mode 100644
index 31b50317d..000000000
--- a/admin/permissions.py
+++ /dev/null
@@ -1,133 +0,0 @@
-# coding: utf-8
-
-import datetime
-from django.utils.translation import ugettext as _, ugettext_noop
-from django.contrib.auth.models import Group
-from modoboa.lib.webutils import _render, getctx, _render_to_string
-from modoboa.lib import events
-from forms import *
-from models import User
-from tables import SuperAdminsTable, DomainAdminsTable
-from lib import grant_access_to_object, ungrant_access_to_object
-
-class Permissions(object):
- submit_label = ugettext_noop("Add")
- formtpl = 'admin/add_permission.html'
-
- @staticmethod
- def is_authorized(user):
- return user.is_superuser
-
- def get_add_form(self, request):
- raise NotImplementedError
-
- def add(self, request):
- raise NotImplementedError
-
- def delete(self, request, selection):
- raise NotImplementedError
-
- def get(self, request):
- raise NotImplementedError
-
- def _render_form(self, request, form, instring=False):
- ctx = dict(form=form, title=self.title, submit_label=self.submit_label,
- role=self.role)
- if instring:
- return _render_to_string(request, self.formtpl, ctx)
- return _render(request, self.formtpl, ctx)
-
-class SuperAdminsPerms(Permissions):
- title = ugettext_noop("Add super administrator")
- role = "super_admins"
-
- def get_add_form(self, request):
- form = SuperAdminForm(request.user)
- return self._render_form(request, form)
-
- def add(self, request):
- form = SuperAdminForm(request.user, request.POST)
- if form.is_valid():
- user = User.objects.get(pk=request.POST["user"])
- user.is_superuser = True
- user.date_joined = datetime.datetime.now()
- events.raiseEvent("SuperAdminPromotion", user)
- user.groups.clear()
- user.save()
- return True, None
-
- content = self._render_form(request, form, True)
- return False, getctx("ko", content=content)
-
- def delete(self, selection):
- for s in selection:
- user = User.objects.get(pk=int(s))
- if user.username == "admin":
- continue
- user.is_superuser = False
- user.save()
-
- def get(self, request):
- admins = User.objects.filter(is_superuser=True)
- return SuperAdminsTable(request, admins).render()
-
-
-class DomainAdminsPerms(Permissions):
- title = ugettext_noop("Add domain administrator")
- role = "domain_admins"
- formtpl = 'admin/add_domain_admin.html'
-
- @staticmethod
- def is_authorized(user):
- return user.has_perm("admin.view_domains")
-
- def get_add_form(self, request):
- form = DomainAdminForm()
- return self._render_form(request, form)
-
- def delete(self, selection):
- dagrp = Group.objects.get(name="DomainAdmins")
- sugrp = Group.objects.get(name="SimpleUsers")
- for uid in selection:
- u = User.objects.get(pk=uid)
- u.groups.remove(dagrp)
- u.save()
- events.raiseEvent("DomainAdminDeleted", u)
- if not len(u.mailbox_set.all()):
- ungrant_access_to_object(u)
- u.delete()
- else:
- u.groups.add(sugrp)
-
- def get(self, request):
- domainadmins = User.objects.filter(groups__name="DomainAdmins")
- domainadmins = filter(lambda da: request.user.is_owner(da), domainadmins)
- return DomainAdminsTable(request, domainadmins).render()
-
-def get_perms_class(user, role):
- """Retrieves the class associated to a given role
-
- The provided user must have the required permission to retrieve
- this class.
-
- If "role" is one of those provided by the admin component, its
- class is directly returned. Otherwise, a "PermsGetClass" event is
- raised to check if one of the active plugins provide the desired
- class.
-
- :param user: a User object
- :param role: the role to look for
- """
- parts = role.split("_")
- cname = ""
- for p in parts:
- cname += p.capitalize()
- cname += "Perms"
- if globals().has_key(cname):
- if globals()[cname].is_authorized(user):
- return globals()[cname]
- return None
- res = events.raiseQueryEvent("PermsGetClass", role)
- if not len(res):
- return None
- return res[0]
diff --git a/admin/tables.py b/admin/tables.py
index 4e6f7f182..b8c9cc304 100644
--- a/admin/tables.py
+++ b/admin/tables.py
@@ -26,7 +26,7 @@ class DomainsTable(tables.Table):
cols_order = ["name", "creation", "modified",
"domaliases", "mboxes", "mbaliases",
- "quota", "enabled", "actions"]
+ "quota", "actions"]
def __init__(self, request, doms):
super(DomainsTable, self).__init__(request)
@@ -38,91 +38,10 @@ class DomainsTable(tables.Table):
def parse_enabled(self, value):
return _("yes") if value else _("no")
-class DomaliasesTable(tables.Table):
- tableid = "objects_table"
- idkey = "id"
-
- name = tables.Column("name", label=ugettext_noop("Name"))
- target = tables.Column("target", label=ugettext_noop("Targeted domain"))
- creation = tables.Column("creation", label=ugettext_noop("Created"), width="170px")
- modified = tables.Column("last_modification", label=ugettext_noop("Last modified"),
- width="170px")
- enabled = tables.Column("enabled", label=gender("Enabled", "m"), width="50px",
- align="center")
- actions = tables.ActionColumn("actions", label=ugettext_noop("Actions"),
- width="50px", align="center",
- defvalue=domalias_actions)
-
- cols_order = ["name", "target", "creation",
- "modified", "enabled", "actions"]
-
- def __init__(self, request, domaliases):
- super(DomaliasesTable, self).__init__(request)
- self.populate(self._rows_from_model(domaliases))
-
- def parse_enabled(self, value):
- return _("yes") if value else _("no")
-
-class MailboxesTable(tables.Table):
- tableid = "objects_table"
- idkey = "id"
-
- address = tables.Column("full_address", label=ugettext_noop("Address"))
- name = tables.Column("user", label=ugettext_noop("Name"))
- creation = tables.Column("creation", label=ugettext_noop("Created"),
- width="160px")
- modified = tables.Column("last_modification", label=ugettext_noop("Last modified"),
- width="160px")
- aliases = tables.Column("alias_count", label=ugettext_noop("Aliases"), width="70px",
- align="center")
- quota = tables.Column("quota", label=ugettext_noop("Quota"),
- width="50px", align="center")
- enabled = tables.Column("enabled", label=gender("Enabled", "f"), width="50px",
- align="center")
- actions = tables.ActionColumn("actions", label=ugettext_noop("Actions"),
- width="70px", align="center", defvalue=mailbox_actions)
-
- cols_order = ["address", "name", "creation", "modified", "aliases",
- "quota", "enabled", "actions"]
-
- def __init__(self, request, mailboxes):
- super(MailboxesTable, self).__init__(request)
- self.populate(self._rows_from_model(mailboxes))
-
- def parse_quota(self, value):
- return "%s %s" % (value, _("MB"))
-
- def parse_enabled(self, value):
- return _("yes") if value else _("no")
-
- def parse_name(self, user):
- return "%s %s" % (user.first_name, user.last_name)
-
-class MbaliasesTable(tables.Table):
- tableid = "objects_table"
- idkey = "id"
-
- address = tables.Column("full_address", label=ugettext_noop("Address"))
- targets = tables.Column("targets", label=ugettext_noop("Target(s)"), safe=True)
- creation = tables.Column("creation", label=ugettext_noop("Created"), width="170px")
- modified = tables.Column("last_modification", label=ugettext_noop("Last modified"),
- width="170px")
- enabled = tables.Column("enabled", label=gender("Enabled", "m"), width="50px",
- align="center")
- actions = tables.ActionColumn("actions", label=ugettext_noop("Actions"), width="50px",
- align="center", defvalue=mbalias_actions)
-
- cols_order = ["address", "targets", "creation", "modified", "enabled", "actions"]
-
- def __init__(self, request, aliases):
- super(MbaliasesTable, self).__init__(request)
- self.populate(self._rows_from_model(aliases))
-
- def rowoptions(self, request, alias):
- return "{ui_disabled : '%s'}" % alias.ui_disabled(request.user)
-
- def parse_enabled(self, value):
- return _("yes") if value else _("no")
+ def row_class(self, request, domain):
+ if not domain.enabled:
+ return "muted"
+ return ""
class ExtensionsTable(tables.Table):
idkey = "id"
@@ -133,43 +52,55 @@ class ExtensionsTable(tables.Table):
cols_order = ["selection", "name", "version", "descr"]
-class SuperAdminsTable(tables.Table):
+class AccountsTable(tables.Table):
idkey = "id"
- selection = tables.SelectionColumn("selection", width="4%")
- username = tables.Column("username", label=ugettext_noop("User name"))
- first_name = tables.Column("first_name", label=ugettext_noop("First name"))
- last_name = tables.Column("last_name", label=ugettext_noop("Last name"))
- date_joined = tables.Column("date_joined", label=ugettext_noop("Defined"))
- enabled = tables.Column("enabled", label=gender("Enabled", "m"), width="10%")
-
- cols_order = ["selection", "username", "first_name", "last_name",
- "date_joined", "enabled"]
-
- def __init__(self, request, users):
- super(SuperAdminsTable, self).__init__(request)
- self.populate(self._rows_from_model(users))
-
- def parse_enabled(self, value):
- return _("yes") if value else _("no")
-
-class DomainAdminsTable(tables.Table):
- idkey = "id"
- selection = tables.SelectionColumn("selection", width="4%")
username = tables.Column("username", label=ugettext_noop("User name"))
first_name = tables.Column("first_name", label=ugettext_noop("First name"))
last_name = tables.Column("last_name", label=ugettext_noop("Last name"))
date_joined = tables.Column("date_joined", label=ugettext_noop("Defined"))
enabled = tables.Column("is_active", label=gender("Enabled", "m"), width="10%")
+ role = tables.Column("group", label=ugettext_noop("Role"))
actions = tables.ActionColumn("actions", label=ugettext_noop("Actions"),
width="70px", align="center",
- defvalue=domain_admin_actions)
+ defvalue=account_actions)
- cols_order = ["selection", "username", "first_name",
- "last_name", "date_joined", "enabled", "actions"]
+ cols_order = ["username", "first_name", "last_name", "date_joined",
+ "role", "actions"]
def __init__(self, request, users):
- super(DomainAdminsTable, self).__init__(request)
+ super(AccountsTable, self).__init__(request)
self.populate(self._rows_from_model(users))
def parse_is_active(self, value):
return _("yes") if value else _("no")
+
+ def parse_group(self, value):
+ if value == "SimpleUsers":
+ return "U"
+ return value[:1]
+
+ def row_class(self, request, obj):
+ if not obj.is_active:
+ return "muted"
+ return ""
+
+class IdentitiesTable(tables.Table):
+ idkey = "id"
+ identity = tables.Column("identity", label=ugettext_noop("Email/Username"))
+ name_or_rcpt = tables.Column("name_or_rcpt", label=ugettext_noop("Fullname/Recipient"))
+ actions = tables.ActionColumn("actions", label=ugettext_noop("Actions"),
+ width="70px", align="center",
+ defvalue=identity_actions)
+
+ cols_order = ["identity", "name_or_rcpt", "actions"]
+
+ def __init__(self, request, identities):
+ super(IdentitiesTable, self).__init__(request)
+ self.populate(self._rows_from_model(identities, True))
+
+ def row_class(self, request, obj):
+ if obj.__class__.__name__ == "User" and not obj.is_active:
+ return "muted"
+ if not obj.enabled:
+ return "muted"
+ return ""
diff --git a/admin/templatetags/admin_extras.py b/admin/templatetags/admin_extras.py
index e71e665cf..c13daa964 100644
--- a/admin/templatetags/admin_extras.py
+++ b/admin/templatetags/admin_extras.py
@@ -13,15 +13,7 @@ genders = {
}
@register.simple_tag
-def admin_menu(user):
- # entries = [
- # {"name" : "admin",
- # "img" : static_url('pics/admin.png'),
- # "label" : _("Admin"),
- # "class" : "topdropdown",
- # "menu" : []
- # }
- # ]
+def admin_menu(selection, user):
entries = []
if user.has_perm("admin.view_domains"):
entries += [
@@ -30,21 +22,14 @@ def admin_menu(user):
"label" : _("Domains"),
"img" : static_url("pics/domains.png")}
]
- elif user.has_perm("admin.view_mailboxes"):
- entries += [
- {"name" : "mailboxes",
- "url" : reverse(admin.views.domains),
- "label" : _("Mailboxes"),
- "img" : static_url("pics/mailbox.png")}
- ]
entries += \
events.raiseQueryEvent("AdminMenuDisplay", "top_menu", user)
- if user.has_perm("auth.view_permissions"):
+ if user.has_perm("auth.add_user") or user.has_perm("admin.add_mailbox"):
entries += [
- {"name" : "permissions",
- "url" : reverse(admin.views.permissions),
- "label" : _("Accounts"),
- "img" : static_url("pics/permissions.png")}
+ {"name" : "identities",
+ "url" : reverse(admin.views.identities),
+ "label" : _("Identities")},
+
]
if user.is_superuser:
entries += [
@@ -57,193 +42,127 @@ def admin_menu(user):
if not len(entries):
return ""
return render_to_string("common/menulist.html",
- {"entries" : entries, "user" : user})
+ {"entries" : entries, "selection" : selection, "user" : user})
@register.simple_tag
def settings_menu(selection, user):
entries = [
- {"name" : "parameters",
- "url" : reverse(admin.views.viewparameters),
- "img" : static_url("pics/domains.png"),
- "label" : _("Parameters")},
{"name" : "extensions",
"url" : reverse(admin.views.viewextensions),
"label" : _("Extensions"),
"img" : static_url("pics/extensions.png")},
+ {"name" : "parameters",
+ "url" : reverse(admin.views.viewparameters),
+ "img" : static_url("pics/domains.png"),
+ "label" : _("Parameters")},
]
- return render_to_string('common/menu.html',
- {"selection" : selection, "entries" : entries,
- "user" : user})
-
-def dommenu_entry(name, label, img, listurl, newurl):
- result = {"name" : name,
- "label" : label,
- "img" : img,
- "url" : listurl,
- "class" : "menubardropdown",
- }
- return result
+ return render_to_string('common/menu.html', {
+ "entries" : entries,
+ "css" : "nav nav-list",
+ "selection" : selection,
+ "user" : user
+ })
@register.simple_tag
-def domains_menu(selection, user, domid):
- args = domid != "" and ("?domid=%s" % domid) or ""
- entries = []
- if selection == "stats":
- disabled = True
- else:
- disabled = False
-
- entries += [
- {"name" : "actions",
- "label" : _("Actions"),
- "img" : static_url("pics/tools.png"),
- "class" : "menubardropdown",
- "menu" : [
- {"name" : "new",
- "label" : _("New"),
- "img" : static_url("pics/add.png"),
- "disabled" : disabled},
- {"name" : "remove",
- "label" : _("Remove"),
- "img" : static_url("pics/remove.png"),
- "disabled" : disabled
- },
- {"name" : "import",
- "label" : _("Import data"),
- "img" : static_url("pics/import.png"),
- "class" : "boxed",
- "rel" : "{handler:'iframe',size:{x:370,y:250}}",
- "url" : reverse(admin.views.importdata)}
- ]
- },
- ]
-
- entries += [{"separator" : True}]
- if user.has_perm("admin.view_domains"):
- entries += dommenu_entry("domains", _("Domains"),
- static_url("pics/domains.png"),
- reverse(admin.views.domains),
- reverse(admin.views.newdomain)),
- entries += [
- dommenu_entry("domaliases", _("Domain aliases"), static_url("pics/alias.png"),
- reverse(admin.views.domaliases) + args,
- reverse(admin.views.newdomalias) + args),
- dommenu_entry("mailboxes", _("Mailboxes"), static_url("pics/mailbox.png"),
- reverse(admin.views.mailboxes) + args,
- reverse(admin.views.newmailbox) + args),
- dommenu_entry("mbaliases", _("Mailbox aliases"), static_url("pics/alias.png"),
- reverse(admin.views.mbaliases) + args,
- reverse(admin.views.newmbalias) + args),
- ]
- entries += events.raiseQueryEvent("AdminMenuDisplay", "admin_menu_box", user)
- return render_to_string('common/menu.html',
- {"entries" : entries,
- "selection" : selection,
- "user" : user})
-
-@register.simple_tag
-def permissions_menu(user):
+def domains_menu(selection, user):
entries = [
- {"name" : "addperm",
- "label" : _("Add permission"),
- "img" : static_url("pics/add.png"),
- "url" : reverse(admin.views.add_permission)},
- {"name" : "delperms",
- "label" : _("Remove permissions"),
- "img" : static_url("pics/remove.png"),
- "url" : reverse(admin.views.delete_permissions)},
+ {"name" : "newdomain",
+ "label" : _("New domain"),
+ "img" : "icon-plus",
+ "modal" : True,
+ "modalcb" : "domainform_cb",
+ "url" : reverse("modoboa.admin.views.newdomain")},
+ {"name" : "import",
+ "label" : _("Import"),
+ "img" : "icon-folder-open",
+ "url" : reverse(admin.views.import_domains),
+ "modal" : True,
+ "modalcb" : "importform_cb"}
]
- return render_to_string('common/menu.html',
- {"entries" : entries, "user" : user})
+ return render_to_string('common/menu.html', {
+ "entries" : entries,
+ "css" : "nav nav-list",
+ "selection" : selection,
+ "user" : user
+ })
+
+@register.simple_tag
+def identities_menu(user):
+ entries = [
+ {"name" : "newaccount",
+ "label" : _("New account"),
+ "img" : "icon-plus",
+ "modal" : True,
+ "modalcb" : "newaccount_cb",
+ "url" : reverse(admin.views.newaccount)},
+ {"name" : "newdlist",
+ "label" : _("New distribution list"),
+ "img" : "icon-plus",
+ "modal" : True,
+ "modalcb" : "dlistform_cb",
+ "url" : reverse(admin.views.newdlist)},
+ {"name" : "import",
+ "label" : _("Import"),
+ "img" : "icon-folder-open",
+ "url" : reverse(admin.views.import_identities),
+ "modal" : True,
+ "modalcb" : "importform_cb"}
+ ]
+
+ return render_to_string('common/menu.html', {
+ "entries" : entries,
+ "css" : "nav nav-list",
+ "user" : user
+ })
@register.simple_tag
def domain_actions(user, domid):
actions = [
{"name" : "editdomain",
"url" : reverse(admin.views.editdomain, args=[domid]),
- "img" : static_url("pics/edit.png"),
+ "img" : "icon-edit",
"title" : _("Edit domain"),
- "class" : "boxed",
- "rel" : "{handler:'iframe',size:{x:310,y:190}}"},
- {"name" : "domaliases",
- "url" : reverse(admin.views.domaliases) + "?domid=%s" % domid,
- "img" : static_url("pics/alias.png"),
- "title" : _("View aliases of this domain")}
+ "modal" : True,
+ "modalcb" : "domainform_cb"},
+ {"name" : "deldomain",
+ "url" : reverse(admin.views.deldomain) + "?selection=%s" % domid,
+ "img" : "icon-remove"},
]
- if user.has_perm('admin.view_domains'):
- actions += [
- {"name" : "viewadmins",
- "url" : reverse(admin.views.view_domain_admins, args=[domid]),
- "img" : static_url("pics/administrators.png"),
- "title" : _("View administrators of this domain"),
- "class" : "boxed",
- "rel" : "{handler:'iframe',size:{x:310,y:190}}"},
- ]
+
return render_actions(actions)
@register.simple_tag
-def domalias_actions(user, aid):
+def account_actions(user, accountid):
actions = [
- {"name" : "editalias",
- "url" : reverse(admin.views.editdomalias, args=[aid]),
- "img" : static_url("pics/edit.png"),
- "title" : _("Edit alias"),
- "class" : "boxed",
- "rel" : "{handler:'iframe',size:{x:310,y:190}}"}
+ {"name" : "editaccount",
+ "url" : reverse(admin.views.editaccount, args=[accountid]),
+ "img" : "icon-edit",
+ "modal" : True,
+ "modalcb" : "editaccount_cb",
+ "title" : _("Edit account")},
+ {"name" : "delaccount",
+ "url" : reverse(admin.views.delaccount) + "?selection=%s" % accountid,
+ "img" : "icon-remove",
+ "title" : _("Delete this account")},
]
return render_actions(actions)
@register.simple_tag
-def mailbox_actions(user, mboxid):
+def identity_actions(user, iid):
+ name, objid = iid.split(':')
+ if name == "User":
+ return account_actions(user, objid)
actions = [
- {"name" : "editmailbox",
- "url" : reverse(admin.views.editmailbox, args=[mboxid]),
- "img" : static_url("pics/edit.png"),
- "title" : _("Edit mailbox"),
- "class" : "boxed",
- "rel" : "{handler:'iframe',size:{x:310,y:320}}"},
- {"name" : "aliases",
- "url" : reverse(admin.views.mbaliases) + "?mbid=%d" % mboxid,
- "img" : static_url("pics/alias.png"),
- "title" : _("View this mailbox aliases")},
- ]
- return render_actions(actions)
-
-@register.simple_tag
-def domain_admin_actions(user, daid):
- from modoboa.admin.models import User
-
- actions = [
- {"name" : "editdomainadmin",
- "url" : reverse(admin.views.edit_domain_admin, args=[daid]),
- "img" : static_url("pics/edit.png"),
- "title" : _("Edit domain admin"),
- "class" : "boxed",
- "rel" : "{handler:'iframe',size:{x:350,y:320}}"},
- {"name" : "assigndomains",
- "url" : reverse(admin.views.assign_domains_to_admin, args=[daid]),
- "img" : static_url("pics/domains.png"),
- "title" : _("Assign domain(s) to this administrator"),
- "class" : "boxed",
- "rel" : "{handler:'iframe',size:{x:350,y:320}}"},
- ]
- actions += events.raiseQueryEvent("DomainAdminActions", User.objects.get(pk=daid))
- return render_actions(actions)
-
-@register.simple_tag
-def mbalias_actions(user, aliasid):
- from modoboa.admin.models import Alias
-
- alias = Alias.objects.get(pk=aliasid)
- if alias.ui_disabled(user):
- return "--"
- actions = [
- {"name" : "editmbalias",
- "url" : reverse(admin.views.editmbalias, args=[aliasid]),
- "img" : static_url("pics/edit.png"),
- "title" : _("Edit mailbox alias"),
- "class" : "boxed",
- "rel" : "{handler:'iframe',size:{x:320,y:300}}"}
+ {"name" : "editdlist",
+ "url" : reverse(admin.views.editdlist, args=[objid]),
+ "img" : "icon-edit",
+ "modal" : True,
+ "modalcb" : "dlistform_cb",
+ "title" : _("Edit distribution list")},
+ {"name" : "deldlist",
+ "url" : reverse(admin.views.deldlist) + "?selection=%s" % objid,
+ "img" : "icon-remove",
+ "title" : _("Delete this distribution list")},
]
return render_actions(actions)
@@ -255,9 +174,11 @@ def loadadminextmenu(user):
@register.simple_tag
def param(app, definition):
- result = """
%s
+""" % definition["help"] + # result += """ + # + #- {% if fs.filters %} -
| {% trans "Actions" %} | -{% trans "Name" %} | -{% trans "Active" %} | -
|---|---|---|
| {% filter_actions fs.name filter forloop.counter forloop.last %} | -{{ filter.name }} | -- - {{ filter.enabled|yesno }} - - | -
| {% trans "Actions" %} | +{% trans "Name" %} | +{% trans "Active" %} | +
|---|---|---|
| {% filter_actions fs.name filter forloop.counter forloop.last %} | +{{ filter.name }} | ++ + {{ filter.enabled|yesno }} + + | +