class AliasForm(forms.ModelForm, DynamicForm): """A form to create/modify an alias.""" random_address = forms.BooleanField(label=ugettext_lazy("Random address"), required=False) address = lib_fields.UTF8AndEmptyUserEmailField( label=ugettext_lazy("Email address"), help_text=ugettext_lazy( "The alias address. To create a catchall alias, just enter the " "domain name (@domain.tld)."), widget=forms.TextInput(attrs={"class": "form-control"})) recipients = lib_fields.UTF8AndEmptyUserEmailField( label=ugettext_lazy("Recipients"), required=False, help_text=ugettext_lazy( "Addresses this alias will point to. Indicate only one address " "per input, press ENTER to add a new input."), widget=forms.TextInput(attrs={"class": "form-control"})) class Meta: model = Alias fields = ("address", "domain", "enabled", "expire_at", "description") labels = {"domain": ugettext_lazy("Domain")} widgets = { "domain": forms.widgets.Select(attrs={ "class": "selectpicker", "data-live-search": "true" }) } def __init__(self, user, *args, **kwargs): self.user = user super(AliasForm, self).__init__(*args, **kwargs) self.fields = OrderedDict((key, self.fields[key]) for key in [ "random_address", "address", "domain", "recipients", "enabled", "expire_at", "description" ]) if self.instance.pk: del self.fields["random_address"] del self.fields["domain"] else: self.fields["address"].required = False self.fields["domain"].required = False self.fields["domain"].queryset = Domain.objects.get_for_admin(user) if len(args) and isinstance(args[0], QueryDict): if "instance" in kwargs: if not kwargs["instance"].domain.enabled: del self.fields["enabled"] self._load_from_qdict(args[0], "recipients", forms.EmailField) elif "instance" in kwargs: alias = kwargs["instance"] if not alias.domain.enabled: self.fields["enabled"].widget.attrs["disabled"] = "disabled" cpt = 1 for rcpt in alias.aliasrecipient_set.filter(alias__internal=False): name = "recipients_%d" % cpt self._create_field(forms.EmailField, name, rcpt.address, 2) cpt += 1 def clean_address(self): """Check if address points to a local domain.""" if not self.cleaned_data["address"]: return self.cleaned_data["address"] localpart, domname = split_mailbox(self.cleaned_data["address"]) 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")) if not self.instance.pk: try: core_signals.can_create_object.send( sender=self.__class__, context=domain, object_type="mailbox_aliases") except lib_exceptions.ModoboaException as inst: raise forms.ValidationError(inst) return self.cleaned_data["address"].lower() def clean(self): """Check it there is at least one recipient.""" super(AliasForm, self).clean() random_address = self.cleaned_data.get("random_address") if not random_address: if not self.cleaned_data.get("address"): self.add_error("address", _("This field is required")) elif not self.cleaned_data.get("domain"): self.add_error("domain", _("This field is required")) for field, value in self.cleaned_data.items(): if field.startswith("recipients") and value: return self.cleaned_data self.add_error("recipients", _("No recipient defined")) return self.cleaned_data def save(self, commit=True): """Custom save method.""" alias = super(AliasForm, self).save(commit=False) if self.cleaned_data.get("random_address"): alias.address = "{}@{}".format(Alias.generate_random_address(), alias.domain) else: local_part, domname = split_mailbox(self.cleaned_data["address"]) alias.domain = Domain.objects.get(name=domname) if commit: alias.save() address_list = [ value for field, value in self.cleaned_data.items() if field.startswith("recipients") and value ] alias.set_recipients(address_list) return alias
class AccountFormMail(forms.Form, DynamicForm): """Form to handle mail part.""" email = lib_fields.UTF8EmailField(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": "form-control"})) quota_act = forms.BooleanField(required=False) aliases = lib_fields.UTF8AndEmptyUserEmailField( 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): self.mb = kwargs.pop("instance", None) super(AccountFormMail, self).__init__(*args, **kwargs) self.field_widths = {"quota": 3} self.extra_fields = [] result = events.raiseQueryEvent('ExtraFormFields', 'mailform', self.mb) for fname, field in result: self.fields[fname] = field self.extra_fields.append(fname) if self.mb is not None: self.fields["email"].required = True cpt = 1 qset = self.mb.aliasrecipient_set.select_related("alias") for ralias in qset: name = "aliases_%d" % cpt self._create_field(lib_fields.UTF8AndEmptyUserEmailField, name, ralias.alias.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", lib_fields.UTF8AndEmptyUserEmailField) def clean_email(self): """Ensure lower case emails""" email = self.cleaned_data["email"].lower() self.locpart, domname = split_mailbox(email) if not domname: return email try: self.domain = Domain.objects.get(name=domname) except Domain.DoesNotExist: raise forms.ValidationError(_("Domain does not exist")) if not self.mb: try: core_signals.can_create_object.send(sender=self.__class__, context=self.domain, object_type="mailboxes") except lib_exceptions.ModoboaException as inst: raise forms.ValidationError(inst) return email def clean(self): """Custom fields validation. Check if quota is >= 0 only when the domain value is not used. """ super(AccountFormMail, self).clean() if not self.cleaned_data["quota_act"] \ and self.cleaned_data['quota'] is not None: if self.cleaned_data["quota"] < 0: self.add_error("quota", _("Must be a positive integer")) self.aliases = [] for name, value in self.cleaned_data.iteritems(): if not name.startswith("aliases"): continue if value == "": continue local_part, domname = split_mailbox(value) if not Domain.objects.filter(name=domname).exists(): self.add_error(name, _("Local domain does not exist")) break self.aliases.append(value.lower()) return self.cleaned_data def create_mailbox(self, user, account): """Create a mailbox associated to :kw:`account`.""" if not user.can_access(self.domain): raise lib_exceptions.PermDeniedException core_signals.can_create_object.send(self.__class__, context=user, object_type="mailboxes") self.mb = Mailbox(address=self.locpart, domain=self.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) def _update_aliases(self, user, account): """Update mailbox aliases.""" qset = self.mb.aliasrecipient_set.select_related("alias").filter( alias__internal=False) for ralias in qset: if ralias.alias.address not in self.aliases: alias = ralias.alias ralias.delete() if alias.recipients_count > 0: continue alias.delete() else: self.aliases.remove(ralias.alias.address) if not self.aliases: return core_signals.can_create_object.send(self.__class__, context=user, object_type="mailbox_aliases", count=len(self.aliases)) core_signals.can_create_object.send(self.__class__, context=self.mb.domain, object_type="mailbox_aliases", count=len(self.aliases)) for alias in self.aliases: if self.mb.aliasrecipient_set.select_related("alias").filter( alias__address=alias).exists(): continue local_part, domname = split_mailbox(alias) al = Alias(address=alias, enabled=account.is_active) al.domain = Domain.objects.get(name=domname) al.save() al.set_recipients([self.mb.full_address]) al.post_create(user) def save(self, user, account): """Save or update account mailbox.""" 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: self.create_mailbox(user, account) else: self.cleaned_data["use_domain_quota"] = ( self.cleaned_data["quota_act"]) self.mb.update_from_dict(user, self.cleaned_data) events.raiseEvent('SaveExtraFormFields', 'mailform', self.mb, self.cleaned_data) account.email = self.cleaned_data["email"] account.save() self._update_aliases(user, account) return self.mb
class AccountFormMail(forms.Form, DynamicForm): """Form to handle mail part.""" email = lib_fields.UTF8EmailField( 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": "form-control"}) ) quota_act = forms.BooleanField(required=False) aliases = lib_fields.UTF8AndEmptyUserEmailField( 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. To create a catchall alias, just " "enter the domain name (@domain.tld)." ) ) senderaddress = lib_fields.UTF8AndEmptyUserEmailField( label=ugettext_lazy("Sender addresses"), required=False, help_text=ugettext_lazy( "Additional sender address(es) for this account. The user will be " "allowed to send emails using this address, even if it " "does not exist locally. Indicate one address per input. Press " "ENTER to add a new input." ) ) def __init__(self, user, *args, **kwargs): self.mb = kwargs.pop("instance", None) self.user = user super(AccountFormMail, self).__init__(*args, **kwargs) self.field_widths = { "quota": 3 } if self.mb is not None: self.fields["email"].required = True qset = self.mb.aliasrecipient_set.filter(alias__internal=False) for cpt, ralias in enumerate(qset): name = "aliases_{}".format(cpt + 1) self._create_field( lib_fields.UTF8AndEmptyUserEmailField, name, ralias.alias.address) for cpt, saddress in enumerate(self.mb.senderaddress_set.all()): name = "senderaddress_{}".format(cpt + 1) self._create_field( lib_fields.UTF8AndEmptyUserEmailField, name, saddress.address) 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", lib_fields.UTF8AndEmptyUserEmailField) self._load_from_qdict( args[0], "senderaddress", lib_fields.UTF8AndEmptyUserEmailField) def clean_email(self): """Ensure lower case emails""" email = self.cleaned_data["email"].lower() self.locpart, domname = split_mailbox(email) if not domname: return email try: self.domain = models.Domain.objects.get(name=domname) except models.Domain.DoesNotExist: raise forms.ValidationError(_("Domain does not exist")) if not self.mb: try: core_signals.can_create_object.send( sender=self.__class__, context=self.domain, object_type="mailboxes") except lib_exceptions.ModoboaException as inst: raise forms.ValidationError(inst) return email def clean(self): """Custom fields validation. Check if quota is >= 0 only when the domain value is not used. """ cleaned_data = super(AccountFormMail, self).clean() use_default_domain_quota = cleaned_data["quota_act"] condition = ( not use_default_domain_quota and cleaned_data["quota"] is not None and cleaned_data["quota"] < 0) if condition: self.add_error("quota", _("Must be a positive integer")) self.aliases = [] self.sender_addresses = [] for name, value in list(cleaned_data.items()): if value == "": continue if name.startswith("aliases"): local_part, domname = split_mailbox(value) domain = models.Domain.objects.filter(name=domname).first() if not domain: self.add_error(name, _("Local domain does not exist")) continue if not self.user.can_access(domain): self.add_error( name, _("You don't have access to this domain")) continue self.aliases.append(value.lower()) elif name.startswith("senderaddress"): local_part, domname = split_mailbox(value) domain = models.Domain.objects.filter(name=domname).first() if domain and not self.user.can_access(domain): self.add_error( name, _("You don't have access to this domain")) continue self.sender_addresses.append(value.lower()) return cleaned_data def create_mailbox(self, user, account): """Create a mailbox associated to :kw:`account`.""" if not user.can_access(self.domain): raise lib_exceptions.PermDeniedException core_signals.can_create_object.send( self.__class__, context=user, klass=models.Mailbox) self.mb = models.Mailbox( address=self.locpart, domain=self.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) def _update_aliases(self, user, account): """Update mailbox aliases.""" qset = self.mb.aliasrecipient_set.select_related("alias").filter( alias__internal=False) for ralias in qset: if ralias.alias.address not in self.aliases: alias = ralias.alias ralias.delete() if alias.recipients_count > 0: continue alias.delete() else: self.aliases.remove(ralias.alias.address) if not self.aliases: return core_signals.can_create_object.send( self.__class__, context=user, klass=models.Alias, count=len(self.aliases)) core_signals.can_create_object.send( self.__class__, context=self.mb.domain, object_type="mailbox_aliases", count=len(self.aliases)) for alias in self.aliases: if self.mb.aliasrecipient_set.select_related("alias").filter( alias__address=alias).exists(): continue local_part, domname = split_mailbox(alias) al = models.Alias(address=alias, enabled=account.is_active) al.domain = models.Domain.objects.get(name=domname) al.save() al.set_recipients([self.mb.full_address]) al.post_create(user) def _update_sender_addresses(self): """Update mailbox sender addresses.""" for saddress in self.mb.senderaddress_set.all(): if saddress.address not in self.sender_addresses: saddress.delete() else: self.sender_addresses.remove(saddress.address) if not len(self.sender_addresses): return to_create = [] for saddress in self.sender_addresses: to_create.append( models.SenderAddress(address=saddress, mailbox=self.mb)) models.SenderAddress.objects.bulk_create(to_create) def save(self, user, account): """Save or update account mailbox.""" 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: self.create_mailbox(user, account) else: self.cleaned_data["use_domain_quota"] = ( self.cleaned_data["quota_act"]) self.mb.update_from_dict(user, self.cleaned_data) account.email = self.cleaned_data["email"] account.save() self._update_aliases(user, account) self._update_sender_addresses() return self.mb
class AliasForm(forms.ModelForm, DynamicForm): """A form to create/modify an alias.""" address = lib_fields.UTF8AndEmptyUserEmailField( label=ugettext_lazy("Email address"), help_text=ugettext_lazy( "The alias address. To create a catchall alias, just enter the " "domain name (@domain.tld)." ), widget=forms.TextInput(attrs={"class": "form-control"}) ) recipients = lib_fields.UTF8AndEmptyUserEmailField( label=ugettext_lazy("Recipients"), required=False, help_text=ugettext_lazy( "Addresses this alias will point to. Indicate only one address " "per input, press ENTER to add a new input." ), widget=forms.TextInput(attrs={"class": "form-control"}) ) class Meta: model = Alias fields = ("address", "enabled", ) def __init__(self, user, *args, **kwargs): self.user = user super(AliasForm, self).__init__(*args, **kwargs) self.fields = OrderedDict( (key, self.fields[key]) for key in ['address', 'recipients', 'enabled'] ) if len(args) and isinstance(args[0], QueryDict): if "instance" in kwargs: if not kwargs["instance"].domain.enabled: del self.fields["enabled"] self._load_from_qdict(args[0], "recipients", forms.EmailField) elif "instance" in kwargs: alias = kwargs["instance"] if not alias.domain.enabled: self.fields["enabled"].widget.attrs["disabled"] = "disabled" cpt = 1 for rcpt in alias.aliasrecipient_set.filter(alias__internal=False): name = "recipients_%d" % cpt self._create_field(forms.EmailField, name, rcpt.address, 2) cpt += 1 def clean_address(self): """Check if address points to a local domain.""" localpart, domname = split_mailbox(self.cleaned_data["address"]) 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["address"].lower() def clean(self): """Check it there is at least one recipient.""" super(AliasForm, self).clean() for field, value in self.cleaned_data.items(): if field.startswith("recipients") and value: return self.cleaned_data self.add_error("recipients", _("No recipient defined")) return self.cleaned_data def save(self, commit=True): """Custom save method.""" alias = super(AliasForm, self).save(commit=False) local_part, domname = split_mailbox(self.cleaned_data["address"]) alias.domain = Domain.objects.get(name=domname) if commit: alias.save() address_list = [ value for field, value in self.cleaned_data.items() if field.startswith("recipients") and value ] alias.set_recipients(address_list) return alias