From e124f8c67a753bd5c1beeb59f499b61199ac9e9d Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 9 Oct 2013 18:27:03 +0200 Subject: [PATCH] Splitting forms.py into multiple modules. --- modoboa/extensions/admin/forms.py | 729 --------------------- modoboa/extensions/admin/forms/__init__.py | 17 + modoboa/extensions/admin/forms/account.py | 314 +++++++++ modoboa/extensions/admin/forms/alias.py | 120 ++++ modoboa/extensions/admin/forms/domain.py | 227 +++++++ modoboa/extensions/admin/forms/export.py | 37 ++ modoboa/extensions/admin/forms/forward.py | 37 ++ modoboa/extensions/admin/forms/import_.py | 29 + modoboa/extensions/webmail/imaputils.py | 2 +- 9 files changed, 782 insertions(+), 730 deletions(-) delete mode 100644 modoboa/extensions/admin/forms.py create mode 100644 modoboa/extensions/admin/forms/__init__.py create mode 100644 modoboa/extensions/admin/forms/account.py create mode 100644 modoboa/extensions/admin/forms/alias.py create mode 100644 modoboa/extensions/admin/forms/domain.py create mode 100644 modoboa/extensions/admin/forms/export.py create mode 100644 modoboa/extensions/admin/forms/forward.py create mode 100644 modoboa/extensions/admin/forms/import_.py diff --git a/modoboa/extensions/admin/forms.py b/modoboa/extensions/admin/forms.py deleted file mode 100644 index 74fea705c..000000000 --- a/modoboa/extensions/admin/forms.py +++ /dev/null @@ -1,729 +0,0 @@ -# coding: utf-8 -from django import forms -from django.utils.translation import ugettext as _, ugettext_lazy -from django.http import QueryDict -from modoboa.lib import events, parameters -from modoboa.lib.exceptions import PermDeniedException -from modoboa.lib.emailutils import split_mailbox -from modoboa.lib.permissions import get_account_roles -from modoboa.lib.formutils import ( - DomainNameField, YesNoField, DynamicForm, TabForms -) -from modoboa.core.models import User -from modoboa.extensions.admin.exceptions import AdminError, BadDestination -from modoboa.extensions.admin.models import ( - Domain, DomainAlias, Mailbox, Alias, Quota -) - - -class DomainFormGeneral(forms.ModelForm, DynamicForm): - aliases = DomainNameField( - label=ugettext_lazy("Alias(es)"), - required=False, - help_text=ugettext_lazy("Alias(es) of this domain. Indicate only one name per input, press ENTER to add a new input.") - ) - - class Meta: - model = Domain - fields = ("name", "quota", "aliases", "enabled") - widgets = dict( - quota=forms.widgets.TextInput(attrs={"class": "span1"}) - ) - - def __init__(self, *args, **kwargs): - self.oldname = None - if "instance" in kwargs: - self.oldname = kwargs["instance"].name - super(DomainFormGeneral, self).__init__(*args, **kwargs) - - if len(args) and isinstance(args[0], QueryDict): - self._load_from_qdict(args[0], "aliases", DomainNameField) - elif "instance" in kwargs: - 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(DomainFormGeneral, 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"] - - 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 update_mailbox_quotas(self, domain): - """Update all quota records associated to this domain - - This method must be called only when a domain gets renamed. As - the primary key used for a quota is an email address, rename a - domain will change all associated email addresses, so it will - change the primary keys used for quotas. The consequence is we - can't issue regular UPDATE queries using the .save() method of - a Quota instance (it will trigger an INSERT as the primary key - has changed). - - So, we use this ugly hack to bypass this behaviour. It is not - perfomant at all as it will generate one query per quota - record to update. - """ - for q in Quota.objects.filter(username__contains="@%s" % self.oldname).values('username'): - username = q['username'].replace('@%s' % self.oldname, '@%s' % domain.name) - Quota.objects.filter(username=q['username']).update(username=username) - - def save(self, user, commit=True): - """Custom save method - - Updating a domain may have consequences on other objects - (domain alias, mailbox, quota). The most tricky part concerns - quotas update. - - """ - d = super(DomainFormGeneral, self).save(commit=False) - if commit: - old_mail_homes = None - if self.oldname is not None and d.name != self.oldname: - d.name = self.oldname - old_mail_homes = \ - dict((mb.id, mb.mail_home) for mb in d.mailbox_set.all()) - d.name = self.cleaned_data['name'] - d.save() - Mailbox.objects.filter(domain=d, use_domain_quota=True) \ - .update(quota=d.quota) - 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(creator=user) - - for dalias in d.domainalias_set.all(): - if not len(filter(lambda name: self.cleaned_data[name] == dalias.name, - self.cleaned_data.keys())): - dalias.delete() - - if old_mail_homes is not None: - self.update_mailbox_quotas(d) - for mb in d.mailbox_set.all(): - mb.rename_dir(old_mail_homes[mb.id]) - - return d - - -class DomainFormOptions(forms.Form): - create_dom_admin = YesNoField( - label=ugettext_lazy("Create a domain administrator"), - initial="no", - help_text=ugettext_lazy("Automatically create an administrator for this domain") - ) - - dom_admin_username = forms.CharField( - label=ugettext_lazy("Name"), - initial="admin", - help_text=ugettext_lazy("The administrator's name. Don't include the domain's name here, it will be automatically appended."), - widget=forms.widgets.TextInput(attrs={"class": "input-small"}), - required=False - ) - - create_aliases = YesNoField( - label=ugettext_lazy("Create aliases"), - initial="yes", - help_text=ugettext_lazy("Automatically create standard aliases for this domain"), - required=False - ) - - def __init__(self, *args, **kwargs): - super(DomainFormOptions, self).__init__(*args, **kwargs) - if args: - if args[0].get("create_dom_admin", "no") == "yes": - self.fields["dom_admin_username"].required = True - self.fields["create_aliases"].required = True - - def clean_dom_admin_username(self): - if '@' in self.cleaned_data["dom_admin_username"]: - raise forms.ValidationError(_("Invalid format")) - return self.cleaned_data["dom_admin_username"] - - def save(self, user, domain): - if self.cleaned_data["create_dom_admin"] == "no": - return - username = "%s@%s" % (self.cleaned_data["dom_admin_username"], domain.name) - try: - da = User.objects.get(username=username) - except User.DoesNotExist: - pass - else: - raise AdminError(_("User '%s' already exists" % username)) - da = User(username=username, email=username, is_active=True) - da.set_password("password") - da.save() - da.set_role("DomainAdmins") - da.post_create(user) - mb = Mailbox(address=self.cleaned_data["dom_admin_username"], domain=domain, - user=da, use_domain_quota=True) - mb.set_quota(override_rules=user.has_perm("admin.change_domain")) - mb.save(creator=user) - - if self.cleaned_data["create_aliases"] == "yes": - al = Alias(address="postmaster", domain=domain, enabled=True) - al.save([mb], [], creator=user) - - domain.add_admin(da) - - -class DomainForm(TabForms): - def __init__(self, user, *args, **kwargs): - self.user = user - self.forms = [] - if user.has_perm("admin.change_domain"): - self.forms.append(dict( - id="general", title=_("General"), formtpl="admin/domain_general_form.html", - cls=DomainFormGeneral, mandatory=True - )) - - cbargs = [user] - if "instances" in kwargs: - cbargs += [kwargs["instances"]["general"]] - self.forms += events.raiseQueryEvent("ExtraDomainForm", *cbargs) - if not self.forms: - self.active_id = "admins" - super(DomainForm, self).__init__(*args, **kwargs) - - def save(self, user): - """Custom save method - - As forms interact with each other, it is easier to make custom - code to save them. - """ - for f in self.forms: - f["instance"].save(user) - - -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).") - ) - recipients = forms.EmailField( - label=ugettext_lazy("Recipients"), required=False, - help_text=ugettext_lazy("Mailbox(es) this alias will point to. Indicate only one address per input, press ENTER to add a new input.") - ) - - class Meta: - model = Alias - fields = ("enabled",) - - def __init__(self, user, *args, **kwargs): - self.user = user - super(AliasForm, self).__init__(*args, **kwargs) - self.fields.keyOrder = ['email', 'recipients', 'enabled'] - - if len(args) and isinstance(args[0], QueryDict): - self._load_from_qdict(args[0], "recipients", forms.EmailField) - elif "instance" in kwargs: - 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) - 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 - - 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): - """Recipients dispatching - - We make a difference between 'local' recipients (the ones hosted - by Modoboa) and 'external' recipients. - """ - self.ext_rcpts = [] - self.int_rcpts = [] - total = 0 - - for k, v in self.cleaned_data.items(): - if not k.startswith("recipients"): - continue - if v == "": - continue - local_part, domname = split_mailbox(v) - if domname is None: - raise AdminError("%s %s" % (_("Invalid mailbox"), v)) - try: - domain = Domain.objects.get(name=domname) - except Domain.DoesNotExist: - domain = None - if domain is not None: - try: - rcpt = Alias.objects.get(domain=domain, address=local_part) - if rcpt.full_address == self.cleaned_data["email"]: - rcpt = None - 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 += [rcpt] - total += 1 - continue - - if v in self.ext_rcpts: - raise AdminError(_("Recipient %s already present" % v)) - self.ext_rcpts += [v] - total += 1 - - if total == 0: - raise AdminError(_("No recipient defined")) - - def save(self, commit=True): - 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) - if commit: - 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 - ) - continue_if_exists = forms.BooleanField( - label=ugettext_lazy("Continue on error"), required=False, - help_text=ugettext_lazy("Don't treat duplicated objects as error") - ) - - def __init__(self, *args, **kwargs): - super(ImportDataForm, self).__init__(*args, **kwargs) - self.fields["sepchar"].widget.attrs = {"class": "span1"} - - def clean_sepchar(self): - if self.cleaned_data["sepchar"] == "": - return ";" - return str(self.cleaned_data["sepchar"]) - - -class ImportIdentitiesForm(ImportDataForm): - crypt_password = forms.BooleanField( - label=ugettext_lazy("Crypt passwords"), required=False, - help_text=ugettext_lazy("Check this option if passwords contained in your file are not crypted") - ) - - -class ExportDataForm(forms.Form): - filename = forms.CharField( - label=ugettext_lazy("File name"), max_length=100, required=False - ) - sepchar = forms.CharField(label=ugettext_lazy("Separator"), max_length=1, required=False) - - def __init__(self, *args, **kwargs): - super(ExportDataForm, self).__init__(*args, **kwargs) - self.fields["sepchar"].widget.attrs = {"class": "span1"} - - def clean_sepchar(self): - if self.cleaned_data["sepchar"] == "": - return ";" - return str(self.cleaned_data["sepchar"]) - - def clean_filename(self): - if self.cleaned_data["filename"] == "": - return self.fields["filename"].initial - return str(self.cleaned_data["filename"]) - - -class ExportDomainsForm(ExportDataForm): - def __init__(self, *args, **kwargs): - super(ExportDomainsForm, self).__init__(*args, **kwargs) - self.fields["filename"].initial = "modoboa-domains.csv" - - -class ExportIdentitiesForm(ExportDataForm): - def __init__(self, *args, **kwargs): - super(ExportIdentitiesForm, self).__init__(*args, **kwargs) - self.fields["filename"].initial = "modoboa-identities.csv" - - -class AccountFormGeneral(forms.ModelForm): - username = forms.CharField(label=ugettext_lazy("Username")) - role = forms.ChoiceField( - label=ugettext_lazy("Role"), - choices=[('', ugettext_lazy("Choose"))], - help_text=ugettext_lazy("What level of permission this user will have") - ) - password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput) - password2 = forms.CharField( - label=ugettext_lazy("Confirmation"), - widget=forms.PasswordInput, - help_text=ugettext_lazy("Enter the same password as above, for verification.") - ) - - class Meta: - model = User - fields = ("username", "first_name", "last_name", "role", "is_active") - - def __init__(self, user, *args, **kwargs): - super(AccountFormGeneral, self).__init__(*args, **kwargs) - self.fields.keyOrder = ['role', 'username', 'first_name', 'last_name', - 'password1', 'password2', 'is_active'] - self.fields["is_active"].label = _("Enabled") - self.user = user - if user.group == "DomainAdmins": - self.fields["role"] = forms.CharField( - label="", - widget=forms.HiddenInput, required=False - ) - else: - self.fields["role"].choices = \ - [('', ugettext_lazy("Choose"))] + get_account_roles(user) - - if "instance" in kwargs: - if len(args) \ - and (args[0].get("password1", "") == "" - and args[0].get("password2", "") == ""): - self.fields["password1"].required = False - self.fields["password2"].required = False - - u = kwargs["instance"] - self.fields["role"].initial = u.group - - if not u.is_local \ - and parameters.get_admin("LDAP_AUTH_METHOD") == "directbind": - del self.fields["password1"] - del self.fields["password2"] - - 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 - if not "role" in self.cleaned_data: - return self.cleaned_data["username"] - if self.cleaned_data["role"] != "SimpleUsers": - return self.cleaned_data["username"] - uname = self.cleaned_data["username"].lower() - validate_email(uname) - return uname - - def clean_password2(self): - password1 = self.cleaned_data.get("password1", "") - password2 = self.cleaned_data["password2"] - if password1 != password2: - raise forms.ValidationError(_("The two password fields didn't match.")) - return password2 - - def save(self, commit=True): - account = super(AccountFormGeneral, self).save(commit=False) - if self.user == account and not self.cleaned_data["is_active"]: - raise AdminError(_("You can't disable your own account")) - if commit: - if "password1" in self.cleaned_data \ - and self.cleaned_data["password1"] != "": - account.set_password(self.cleaned_data["password1"]) - account.save() - account.set_role(self.cleaned_data["role"]) - return account - - -class AccountFormMail(forms.Form, DynamicForm): - email = forms.EmailField(label=ugettext_lazy("E-mail"), required=False) - quota = forms.IntegerField( - label=ugettext_lazy("Quota"), - required=False, - help_text=_("Quota in MB for this mailbox. Define a custom value or use domain's default one. Leave empty to define an unlimited value (not allowed for domain administrators)."), - widget=forms.widgets.TextInput(attrs={"class": "span1"}) - ) - quota_act = forms.BooleanField(required=False) - aliases = forms.EmailField( - label=ugettext_lazy("Alias(es)"), - required=False, - help_text=ugettext_lazy("Alias(es) of this mailbox. Indicate only one address per input, press ENTER to add a new input. Use the '*' character to create a 'catchall' alias (ex: *@domain.tld).") - ) - - def __init__(self, *args, **kwargs): - if "instance" in kwargs: - self.mb = kwargs["instance"] - del kwargs["instance"] - super(AccountFormMail, self).__init__(*args, **kwargs) - if hasattr(self, "mb") and self.mb is not None: - self.fields["email"].required = True - cpt = 1 - for alias in self.mb.alias_set.all(): - if len(alias.get_recipients()) >= 2: - continue - name = "aliases_%d" % cpt - self._create_field(forms.EmailField, name, alias.full_address) - cpt += 1 - self.fields["email"].initial = self.mb.full_address - self.fields["quota_act"].initial = self.mb.use_domain_quota - if not self.mb.use_domain_quota and self.mb.quota: - self.fields["quota"].initial = self.mb.quota - else: - self.fields["quota_act"].initial = True - - if len(args) and isinstance(args[0], QueryDict): - self._load_from_qdict(args[0], "aliases", forms.EmailField) - - def clean_email(self): - """Ensure lower case emails""" - return self.cleaned_data["email"].lower() - - def save(self, user, account): - if self.cleaned_data["email"] == "": - return None - - if self.cleaned_data["quota_act"]: - self.cleaned_data["quota"] = None - - if not hasattr(self, "mb") or self.mb is None: - locpart, domname = split_mailbox(self.cleaned_data["email"]) - try: - domain = Domain.objects.get(name=domname) - except Domain.DoesNotExist: - raise AdminError(_("Domain does not exist")) - if not user.can_access(domain): - raise PermDeniedException - try: - Mailbox.objects.get(address=locpart, domain=domain) - except Mailbox.DoesNotExist: - pass - else: - raise AdminError(_("Mailbox %s already exists" % self.cleaned_data["email"])) - events.raiseEvent("CanCreate", user, "mailboxes") - self.mb = Mailbox(address=locpart, domain=domain, user=account, - use_domain_quota=self.cleaned_data["quota_act"]) - self.mb.set_quota(self.cleaned_data["quota"], - user.has_perm("admin.add_domain")) - self.mb.save(creator=user) - else: - newaddress = None - if self.cleaned_data["email"] != self.mb.full_address: - newaddress = self.cleaned_data["email"] - elif account.group == "SimpleUsers" and account.username != self.mb.full_address: - newaddress = account.username - if newaddress is not None: - local_part, domname = split_mailbox(newaddress) - try: - domain = Domain.objects.get(name=domname) - except Domain.DoesNotExist: - raise AdminError(_("Domain does not exist")) - if not user.can_access(domain): - raise PermDeniedException - self.mb.rename(local_part, domain) - - self.mb.use_domain_quota = self.cleaned_data["quota_act"] - override_rules = True if not self.mb.quota or user.has_perm("admin.add_domain") else False - self.mb.set_quota(self.cleaned_data["quota"], override_rules) - self.mb.save() - - 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], [], creator=user) - - 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())): - alias.delete() - - return self.mb - - -class AccountPermissionsForm(forms.Form, DynamicForm): - domains = DomainNameField( - label=ugettext_lazy("Domain(s)"), - required=False, - help_text=ugettext_lazy("Domain(s) that user administrates") - ) - - def __init__(self, *args, **kwargs): - if "instance" in kwargs: - self.account = kwargs["instance"] - del kwargs["instance"] - - super(AccountPermissionsForm, self).__init__(*args, **kwargs) - - if not hasattr(self, "account") or self.account is None: - return - for pos, domain in enumerate(Domain.objects.get_for_admin(self.account)): - 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 save(self): - current_domains = [dom.name for dom in Domain.objects.get_for_admin(self.account)] - for name, value in self.cleaned_data.items(): - if not name.startswith("domains"): - continue - if value in ["", None]: - continue - if not value in current_domains: - domain = Domain.objects.get(name=value) - domain.add_admin(self.account) - - for domain in Domain.objects.get_for_admin(self.account): - if not len(filter(lambda name: self.cleaned_data[name] == domain.name, - self.cleaned_data.keys())): - domain.remove_admin(self.account) - - -class AccountForm(TabForms): - - def __init__(self, user, *args, **kwargs): - self.user = user - self.forms = [ - dict(id="general", title=_("General"), cls=AccountFormGeneral, - 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 "instances" in kwargs: - 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 self.user.has_perm("admin.add_domain") \ - and account.has_perm("core.add_user") - - 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() - - -class ForwardForm(forms.Form): - dest = forms.CharField( - label=ugettext_lazy("Recipient(s)"), - widget=forms.Textarea, - required=False, - help_text=ugettext_lazy("Indicate one or more recipients separated by a ','") - ) - keepcopies = forms.BooleanField( - label=ugettext_lazy("Keep local copies"), - required=False, - help_text=ugettext_lazy("Forward messages and store copies into your local mailbox") - ) - - def parse_dest(self): - self.dests = [] - rawdata = self.cleaned_data["dest"].strip() - if rawdata == "": - return - for d in rawdata.split(","): - local_part, domname = split_mailbox(d) - if not local_part or not domname or not len(domname): - raise BadDestination("Invalid mailbox syntax for %s" % d) - try: - Domain.objects.get(name=domname) - except Domain.DoesNotExist: - self.dests += [d] - else: - raise BadDestination(_("You can't define a forward to a local destination. Please ask your administrator to create an alias instead.")) diff --git a/modoboa/extensions/admin/forms/__init__.py b/modoboa/extensions/admin/forms/__init__.py new file mode 100644 index 000000000..5bfbed711 --- /dev/null +++ b/modoboa/extensions/admin/forms/__init__.py @@ -0,0 +1,17 @@ +from .domain import DomainFormGeneral, DomainFormOptions, DomainForm +from .account import ( + AccountFormGeneral, AccountFormMail, AccountPermissionsForm, + AccountForm +) +from .alias import AliasForm +from .forward import ForwardForm +from .import_ import ImportDataForm, ImportIdentitiesForm +from .export import ExportDataForm, ExportDomainsForm, ExportIdentitiesForm + +__all__ = [ + 'DomainFormGeneral', 'DomainFormOptions', 'DomainForm', + 'AccountFormGeneral', 'AccountFormMail', 'AccountPermissionsForm', + 'AccountForm', 'AliasForm', 'ImportDataForm', 'ImportIdentitiesForm', + 'ExportDataForm', 'ExportDomainsForm', 'ExportIdentitiesForm', + 'ForwardForm' +] diff --git a/modoboa/extensions/admin/forms/account.py b/modoboa/extensions/admin/forms/account.py new file mode 100644 index 000000000..179dd2ce4 --- /dev/null +++ b/modoboa/extensions/admin/forms/account.py @@ -0,0 +1,314 @@ +from django import forms +from django.utils.translation import ugettext as _, ugettext_lazy +from django.http import QueryDict +from modoboa.lib import events, parameters +from modoboa.lib.exceptions import PermDeniedException +from modoboa.lib.permissions import get_account_roles +from modoboa.lib.emailutils import split_mailbox +from modoboa.lib.formutils import ( + DomainNameField, DynamicForm, TabForms +) +from modoboa.core.models import User +from modoboa.extensions.admin.exceptions import AdminError +from modoboa.extensions.admin.models import ( + Domain, Mailbox, Alias +) + + +class AccountFormGeneral(forms.ModelForm): + username = forms.CharField(label=ugettext_lazy("Username")) + role = forms.ChoiceField( + label=ugettext_lazy("Role"), + choices=[('', ugettext_lazy("Choose"))], + help_text=ugettext_lazy("What level of permission this user will have") + ) + password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput) + password2 = forms.CharField( + label=ugettext_lazy("Confirmation"), + widget=forms.PasswordInput, + help_text=ugettext_lazy("Enter the same password as above, for verification.") + ) + + class Meta: + model = User + fields = ("username", "first_name", "last_name", "role", "is_active") + + def __init__(self, user, *args, **kwargs): + super(AccountFormGeneral, self).__init__(*args, **kwargs) + self.fields.keyOrder = ['role', 'username', 'first_name', 'last_name', + 'password1', 'password2', 'is_active'] + self.fields["is_active"].label = _("Enabled") + self.user = user + if user.group == "DomainAdmins": + self.fields["role"] = forms.CharField( + label="", + widget=forms.HiddenInput, required=False + ) + else: + self.fields["role"].choices = \ + [('', ugettext_lazy("Choose"))] + get_account_roles(user) + + if "instance" in kwargs: + if len(args) \ + and (args[0].get("password1", "") == "" + and args[0].get("password2", "") == ""): + self.fields["password1"].required = False + self.fields["password2"].required = False + + u = kwargs["instance"] + self.fields["role"].initial = u.group + + if not u.is_local \ + and parameters.get_admin("LDAP_AUTH_METHOD") == "directbind": + del self.fields["password1"] + del self.fields["password2"] + + 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 + if not "role" in self.cleaned_data: + return self.cleaned_data["username"] + if self.cleaned_data["role"] != "SimpleUsers": + return self.cleaned_data["username"] + uname = self.cleaned_data["username"].lower() + validate_email(uname) + return uname + + def clean_password2(self): + password1 = self.cleaned_data.get("password1", "") + password2 = self.cleaned_data["password2"] + if password1 != password2: + raise forms.ValidationError(_("The two password fields didn't match.")) + return password2 + + def save(self, commit=True): + account = super(AccountFormGeneral, self).save(commit=False) + if self.user == account and not self.cleaned_data["is_active"]: + raise AdminError(_("You can't disable your own account")) + if commit: + if "password1" in self.cleaned_data \ + and self.cleaned_data["password1"] != "": + account.set_password(self.cleaned_data["password1"]) + account.save() + account.set_role(self.cleaned_data["role"]) + return account + + +class AccountFormMail(forms.Form, DynamicForm): + email = forms.EmailField(label=ugettext_lazy("E-mail"), required=False) + quota = forms.IntegerField( + label=ugettext_lazy("Quota"), + required=False, + help_text=_("Quota in MB for this mailbox. Define a custom value or use domain's default one. Leave empty to define an unlimited value (not allowed for domain administrators)."), + widget=forms.widgets.TextInput(attrs={"class": "span1"}) + ) + quota_act = forms.BooleanField(required=False) + aliases = forms.EmailField( + label=ugettext_lazy("Alias(es)"), + required=False, + help_text=ugettext_lazy("Alias(es) of this mailbox. Indicate only one address per input, press ENTER to add a new input. Use the '*' character to create a 'catchall' alias (ex: *@domain.tld).") + ) + + def __init__(self, *args, **kwargs): + if "instance" in kwargs: + self.mb = kwargs["instance"] + del kwargs["instance"] + super(AccountFormMail, self).__init__(*args, **kwargs) + if hasattr(self, "mb") and self.mb is not None: + self.fields["email"].required = True + cpt = 1 + for alias in self.mb.alias_set.all(): + if len(alias.get_recipients()) >= 2: + continue + name = "aliases_%d" % cpt + self._create_field(forms.EmailField, name, alias.full_address) + cpt += 1 + self.fields["email"].initial = self.mb.full_address + self.fields["quota_act"].initial = self.mb.use_domain_quota + if not self.mb.use_domain_quota and self.mb.quota: + self.fields["quota"].initial = self.mb.quota + else: + self.fields["quota_act"].initial = True + + if len(args) and isinstance(args[0], QueryDict): + self._load_from_qdict(args[0], "aliases", forms.EmailField) + + def clean_email(self): + """Ensure lower case emails""" + return self.cleaned_data["email"].lower() + + def save(self, user, account): + if self.cleaned_data["email"] == "": + return None + + if self.cleaned_data["quota_act"]: + self.cleaned_data["quota"] = None + + if not hasattr(self, "mb") or self.mb is None: + locpart, domname = split_mailbox(self.cleaned_data["email"]) + try: + domain = Domain.objects.get(name=domname) + except Domain.DoesNotExist: + raise AdminError(_("Domain does not exist")) + if not user.can_access(domain): + raise PermDeniedException + try: + Mailbox.objects.get(address=locpart, domain=domain) + except Mailbox.DoesNotExist: + pass + else: + raise AdminError(_("Mailbox %s already exists" % self.cleaned_data["email"])) + events.raiseEvent("CanCreate", user, "mailboxes") + self.mb = Mailbox(address=locpart, domain=domain, user=account, + use_domain_quota=self.cleaned_data["quota_act"]) + self.mb.set_quota(self.cleaned_data["quota"], + user.has_perm("admin.add_domain")) + self.mb.save(creator=user) + else: + newaddress = None + if self.cleaned_data["email"] != self.mb.full_address: + newaddress = self.cleaned_data["email"] + elif account.group == "SimpleUsers" and account.username != self.mb.full_address: + newaddress = account.username + if newaddress is not None: + local_part, domname = split_mailbox(newaddress) + try: + domain = Domain.objects.get(name=domname) + except Domain.DoesNotExist: + raise AdminError(_("Domain does not exist")) + if not user.can_access(domain): + raise PermDeniedException + self.mb.rename(local_part, domain) + + self.mb.use_domain_quota = self.cleaned_data["quota_act"] + override_rules = True if not self.mb.quota or user.has_perm("admin.add_domain") else False + self.mb.set_quota(self.cleaned_data["quota"], override_rules) + self.mb.save() + + 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], [], creator=user) + + 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())): + alias.delete() + + return self.mb + + +class AccountPermissionsForm(forms.Form, DynamicForm): + domains = DomainNameField( + label=ugettext_lazy("Domain(s)"), + required=False, + help_text=ugettext_lazy("Domain(s) that user administrates") + ) + + def __init__(self, *args, **kwargs): + if "instance" in kwargs: + self.account = kwargs["instance"] + del kwargs["instance"] + + super(AccountPermissionsForm, self).__init__(*args, **kwargs) + + if not hasattr(self, "account") or self.account is None: + return + for pos, domain in enumerate(Domain.objects.get_for_admin(self.account)): + 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 save(self): + current_domains = [dom.name for dom in Domain.objects.get_for_admin(self.account)] + for name, value in self.cleaned_data.items(): + if not name.startswith("domains"): + continue + if value in ["", None]: + continue + if not value in current_domains: + domain = Domain.objects.get(name=value) + domain.add_admin(self.account) + + for domain in Domain.objects.get_for_admin(self.account): + if not len(filter(lambda name: self.cleaned_data[name] == domain.name, + self.cleaned_data.keys())): + domain.remove_admin(self.account) + + +class AccountForm(TabForms): + + def __init__(self, user, *args, **kwargs): + self.user = user + self.forms = [ + dict(id="general", title=_("General"), cls=AccountFormGeneral, + 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 "instances" in kwargs: + 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 self.user.has_perm("admin.add_domain") \ + and account.has_perm("core.add_user") + + 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/modoboa/extensions/admin/forms/alias.py b/modoboa/extensions/admin/forms/alias.py new file mode 100644 index 000000000..a68bebfd4 --- /dev/null +++ b/modoboa/extensions/admin/forms/alias.py @@ -0,0 +1,120 @@ +from django import forms +from django.utils.translation import ugettext as _, ugettext_lazy +from django.http import QueryDict +from modoboa.lib.emailutils import split_mailbox +from modoboa.lib.formutils import ( + DynamicForm +) +from modoboa.extensions.admin.exceptions import AdminError +from modoboa.extensions.admin.models import ( + Domain, Mailbox, Alias +) + + +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).") + ) + recipients = forms.EmailField( + label=ugettext_lazy("Recipients"), required=False, + help_text=ugettext_lazy("Mailbox(es) this alias will point to. Indicate only one address per input, press ENTER to add a new input.") + ) + + class Meta: + model = Alias + fields = ("enabled",) + + def __init__(self, user, *args, **kwargs): + self.user = user + super(AliasForm, self).__init__(*args, **kwargs) + self.fields.keyOrder = ['email', 'recipients', 'enabled'] + + if len(args) and isinstance(args[0], QueryDict): + self._load_from_qdict(args[0], "recipients", forms.EmailField) + elif "instance" in kwargs: + 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) + 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 + + 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): + """Recipients dispatching + + We make a difference between 'local' recipients (the ones hosted + by Modoboa) and 'external' recipients. + """ + self.ext_rcpts = [] + self.int_rcpts = [] + total = 0 + + for k, v in self.cleaned_data.items(): + if not k.startswith("recipients"): + continue + if v == "": + continue + local_part, domname = split_mailbox(v) + if domname is None: + raise AdminError("%s %s" % (_("Invalid mailbox"), v)) + try: + domain = Domain.objects.get(name=domname) + except Domain.DoesNotExist: + domain = None + if domain is not None: + try: + rcpt = Alias.objects.get(domain=domain, address=local_part) + if rcpt.full_address == self.cleaned_data["email"]: + rcpt = None + 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 += [rcpt] + total += 1 + continue + + if v in self.ext_rcpts: + raise AdminError(_("Recipient %s already present" % v)) + self.ext_rcpts += [v] + total += 1 + + if total == 0: + raise AdminError(_("No recipient defined")) + + def save(self, commit=True): + 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) + if commit: + alias.save(self.int_rcpts, self.ext_rcpts) + self.save_m2m() + return alias diff --git a/modoboa/extensions/admin/forms/domain.py b/modoboa/extensions/admin/forms/domain.py new file mode 100644 index 000000000..891d23c2e --- /dev/null +++ b/modoboa/extensions/admin/forms/domain.py @@ -0,0 +1,227 @@ +from django import forms +from django.http import QueryDict +from django.utils.translation import ugettext as _, ugettext_lazy +from modoboa.lib import events +from modoboa.lib.formutils import ( + DomainNameField, YesNoField, DynamicForm, TabForms +) +from modoboa.core.models import User +from modoboa.extensions.admin.exceptions import AdminError +from modoboa.extensions.admin.models import ( + Domain, DomainAlias, Mailbox, Alias, Quota +) + + +class DomainFormGeneral(forms.ModelForm, DynamicForm): + aliases = DomainNameField( + label=ugettext_lazy("Alias(es)"), + required=False, + help_text=ugettext_lazy("Alias(es) of this domain. Indicate only one name per input, press ENTER to add a new input.") + ) + + class Meta: + model = Domain + fields = ("name", "quota", "aliases", "enabled") + widgets = dict( + quota=forms.widgets.TextInput(attrs={"class": "span1"}) + ) + + def __init__(self, *args, **kwargs): + self.oldname = None + if "instance" in kwargs: + self.oldname = kwargs["instance"].name + super(DomainFormGeneral, self).__init__(*args, **kwargs) + + if len(args) and isinstance(args[0], QueryDict): + self._load_from_qdict(args[0], "aliases", DomainNameField) + elif "instance" in kwargs: + 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(DomainFormGeneral, 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"] + + 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 update_mailbox_quotas(self, domain): + """Update all quota records associated to this domain + + This method must be called only when a domain gets renamed. As + the primary key used for a quota is an email address, rename a + domain will change all associated email addresses, so it will + change the primary keys used for quotas. The consequence is we + can't issue regular UPDATE queries using the .save() method of + a Quota instance (it will trigger an INSERT as the primary key + has changed). + + So, we use this ugly hack to bypass this behaviour. It is not + perfomant at all as it will generate one query per quota + record to update. + """ + for q in Quota.objects.filter(username__contains="@%s" % self.oldname).values('username'): + username = q['username'].replace('@%s' % self.oldname, '@%s' % domain.name) + Quota.objects.filter(username=q['username']).update(username=username) + + def save(self, user, commit=True): + """Custom save method + + Updating a domain may have consequences on other objects + (domain alias, mailbox, quota). The most tricky part concerns + quotas update. + + """ + d = super(DomainFormGeneral, self).save(commit=False) + if commit: + old_mail_homes = None + if self.oldname is not None and d.name != self.oldname: + d.name = self.oldname + old_mail_homes = \ + dict((mb.id, mb.mail_home) for mb in d.mailbox_set.all()) + d.name = self.cleaned_data['name'] + d.save() + Mailbox.objects.filter(domain=d, use_domain_quota=True) \ + .update(quota=d.quota) + 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(creator=user) + + for dalias in d.domainalias_set.all(): + if not len(filter(lambda name: self.cleaned_data[name] == dalias.name, + self.cleaned_data.keys())): + dalias.delete() + + if old_mail_homes is not None: + self.update_mailbox_quotas(d) + for mb in d.mailbox_set.all(): + mb.rename_dir(old_mail_homes[mb.id]) + + return d + + +class DomainFormOptions(forms.Form): + create_dom_admin = YesNoField( + label=ugettext_lazy("Create a domain administrator"), + initial="no", + help_text=ugettext_lazy("Automatically create an administrator for this domain") + ) + + dom_admin_username = forms.CharField( + label=ugettext_lazy("Name"), + initial="admin", + help_text=ugettext_lazy("The administrator's name. Don't include the domain's name here, it will be automatically appended."), + widget=forms.widgets.TextInput(attrs={"class": "input-small"}), + required=False + ) + + create_aliases = YesNoField( + label=ugettext_lazy("Create aliases"), + initial="yes", + help_text=ugettext_lazy("Automatically create standard aliases for this domain"), + required=False + ) + + def __init__(self, *args, **kwargs): + super(DomainFormOptions, self).__init__(*args, **kwargs) + if args: + if args[0].get("create_dom_admin", "no") == "yes": + self.fields["dom_admin_username"].required = True + self.fields["create_aliases"].required = True + + def clean_dom_admin_username(self): + if '@' in self.cleaned_data["dom_admin_username"]: + raise forms.ValidationError(_("Invalid format")) + return self.cleaned_data["dom_admin_username"] + + def save(self, user, domain): + if self.cleaned_data["create_dom_admin"] == "no": + return + username = "%s@%s" % (self.cleaned_data["dom_admin_username"], domain.name) + try: + da = User.objects.get(username=username) + except User.DoesNotExist: + pass + else: + raise AdminError(_("User '%s' already exists" % username)) + da = User(username=username, email=username, is_active=True) + da.set_password("password") + da.save() + da.set_role("DomainAdmins") + da.post_create(user) + mb = Mailbox(address=self.cleaned_data["dom_admin_username"], domain=domain, + user=da, use_domain_quota=True) + mb.set_quota(override_rules=user.has_perm("admin.change_domain")) + mb.save(creator=user) + + if self.cleaned_data["create_aliases"] == "yes": + al = Alias(address="postmaster", domain=domain, enabled=True) + al.save([mb], [], creator=user) + + domain.add_admin(da) + + +class DomainForm(TabForms): + def __init__(self, user, *args, **kwargs): + self.user = user + self.forms = [] + if user.has_perm("admin.change_domain"): + self.forms.append(dict( + id="general", title=_("General"), formtpl="admin/domain_general_form.html", + cls=DomainFormGeneral, mandatory=True + )) + + cbargs = [user] + if "instances" in kwargs: + cbargs += [kwargs["instances"]["general"]] + self.forms += events.raiseQueryEvent("ExtraDomainForm", *cbargs) + if not self.forms: + self.active_id = "admins" + super(DomainForm, self).__init__(*args, **kwargs) + + def save(self, user): + """Custom save method + + As forms interact with each other, it is easier to make custom + code to save them. + """ + for f in self.forms: + f["instance"].save(user) diff --git a/modoboa/extensions/admin/forms/export.py b/modoboa/extensions/admin/forms/export.py new file mode 100644 index 000000000..7727c5160 --- /dev/null +++ b/modoboa/extensions/admin/forms/export.py @@ -0,0 +1,37 @@ +from django import forms +from django.utils.translation import ugettext_lazy + + +class ExportDataForm(forms.Form): + filename = forms.CharField( + label=ugettext_lazy("File name"), max_length=100, required=False + ) + sepchar = forms.CharField( + label=ugettext_lazy("Separator"), max_length=1, required=False + ) + + def __init__(self, *args, **kwargs): + super(ExportDataForm, self).__init__(*args, **kwargs) + self.fields["sepchar"].widget.attrs = {"class": "span1"} + + def clean_sepchar(self): + if self.cleaned_data["sepchar"] == "": + return ";" + return str(self.cleaned_data["sepchar"]) + + def clean_filename(self): + if self.cleaned_data["filename"] == "": + return self.fields["filename"].initial + return str(self.cleaned_data["filename"]) + + +class ExportDomainsForm(ExportDataForm): + def __init__(self, *args, **kwargs): + super(ExportDomainsForm, self).__init__(*args, **kwargs) + self.fields["filename"].initial = "modoboa-domains.csv" + + +class ExportIdentitiesForm(ExportDataForm): + def __init__(self, *args, **kwargs): + super(ExportIdentitiesForm, self).__init__(*args, **kwargs) + self.fields["filename"].initial = "modoboa-identities.csv" diff --git a/modoboa/extensions/admin/forms/forward.py b/modoboa/extensions/admin/forms/forward.py new file mode 100644 index 000000000..fa1e7a53a --- /dev/null +++ b/modoboa/extensions/admin/forms/forward.py @@ -0,0 +1,37 @@ +from django import forms +from django.utils.translation import ugettext as _, ugettext_lazy +from modoboa.lib.emailutils import split_mailbox +from modoboa.extensions.admin.exceptions import BadDestination +from modoboa.extensions.admin.models import ( + Domain +) + + +class ForwardForm(forms.Form): + dest = forms.CharField( + label=ugettext_lazy("Recipient(s)"), + widget=forms.Textarea, + required=False, + help_text=ugettext_lazy("Indicate one or more recipients separated by a ','") + ) + keepcopies = forms.BooleanField( + label=ugettext_lazy("Keep local copies"), + required=False, + help_text=ugettext_lazy("Forward messages and store copies into your local mailbox") + ) + + def parse_dest(self): + self.dests = [] + rawdata = self.cleaned_data["dest"].strip() + if rawdata == "": + return + for d in rawdata.split(","): + local_part, domname = split_mailbox(d) + if not local_part or not domname or not len(domname): + raise BadDestination("Invalid mailbox syntax for %s" % d) + try: + Domain.objects.get(name=domname) + except Domain.DoesNotExist: + self.dests += [d] + else: + raise BadDestination(_("You can't define a forward to a local destination. Please ask your administrator to create an alias instead.")) diff --git a/modoboa/extensions/admin/forms/import_.py b/modoboa/extensions/admin/forms/import_.py new file mode 100644 index 000000000..fc6ccebe3 --- /dev/null +++ b/modoboa/extensions/admin/forms/import_.py @@ -0,0 +1,29 @@ +from django import forms +from django.utils.translation import ugettext_lazy + + +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 + ) + continue_if_exists = forms.BooleanField( + label=ugettext_lazy("Continue on error"), required=False, + help_text=ugettext_lazy("Don't treat duplicated objects as error") + ) + + def __init__(self, *args, **kwargs): + super(ImportDataForm, self).__init__(*args, **kwargs) + self.fields["sepchar"].widget.attrs = {"class": "span1"} + + def clean_sepchar(self): + if self.cleaned_data["sepchar"] == "": + return ";" + return str(self.cleaned_data["sepchar"]) + + +class ImportIdentitiesForm(ImportDataForm): + crypt_password = forms.BooleanField( + label=ugettext_lazy("Crypt passwords"), required=False, + help_text=ugettext_lazy("Check this option if passwords contained in your file are not crypted") + ) diff --git a/modoboa/extensions/webmail/imaputils.py b/modoboa/extensions/webmail/imaputils.py index 80d2a324e..3afa1f70a 100644 --- a/modoboa/extensions/webmail/imaputils.py +++ b/modoboa/extensions/webmail/imaputils.py @@ -10,7 +10,7 @@ import re import time from functools import wraps from django.utils.translation import ugettext as _ -from modoboa.lib import parameters +from modoboa.lib import parameters, imap_utf7 from modoboa.lib.connections import ConnectionsManager from modoboa.lib.webutils import static_url from exceptions import ImapError, WebmailError