django-orchestra/orchestra/contrib/mailboxes/forms.py

105 lines
4.6 KiB
Python
Raw Normal View History

2014-10-06 14:57:02 +00:00
from django import forms
2014-10-21 15:29:36 +00:00
from django.contrib.admin import widgets
2015-10-07 11:44:30 +00:00
from django.core.exceptions import ValidationError
2014-10-21 15:29:36 +00:00
from django.utils.safestring import mark_safe
2014-10-09 17:04:12 +00:00
from django.utils.translation import ugettext_lazy as _
2014-10-06 14:57:02 +00:00
2014-10-09 17:04:12 +00:00
from orchestra.forms import UserCreationForm, UserChangeForm
2014-10-21 15:29:36 +00:00
from orchestra.utils.python import AttrDict
2014-10-06 14:57:02 +00:00
2015-10-07 11:44:30 +00:00
from . import settings
from .models import Address, Mailbox
2014-10-06 14:57:02 +00:00
2014-10-21 15:29:36 +00:00
class MailboxForm(forms.ModelForm):
""" hacky form for adding reverse M2M form field for Mailbox.addresses """
# TODO keep track of this ticket for future reimplementation
# https://code.djangoproject.com/ticket/897
2015-04-05 10:46:24 +00:00
addresses = forms.ModelMultipleChoiceField(queryset=Address.objects.select_related('domain'),
required=False,
widget=widgets.FilteredSelectMultiple(verbose_name=_('addresses'), is_stacked=False))
2014-10-21 15:29:36 +00:00
def __init__(self, *args, **kwargs):
super(MailboxForm, self).__init__(*args, **kwargs)
# Hack the widget in order to display add button
2014-10-21 15:29:36 +00:00
field = AttrDict(**{
'to': Address,
'get_related_field': lambda: AttrDict(name='id'),
})
widget = self.fields['addresses'].widget
self.fields['addresses'].widget = widgets.RelatedFieldWidgetWrapper(widget, field,
self.modeladmin.admin_site, can_add_related=True)
# Filter related addresses by account
2014-10-21 15:29:36 +00:00
old_render = self.fields['addresses'].widget.render
def render(*args, **kwargs):
output = old_render(*args, **kwargs)
args = 'account=%i' % self.modeladmin.account.pk
output = output.replace('/add/?', '/add/?%s&' % args)
return mark_safe(output)
self.fields['addresses'].widget.render = render
queryset = self.fields['addresses'].queryset
2015-10-07 11:44:30 +00:00
realted_addresses = queryset.filter(account_id=self.modeladmin.account.pk).order_by('name')
2015-06-17 10:34:14 +00:00
self.fields['addresses'].queryset = realted_addresses
2014-10-21 15:29:36 +00:00
if self.instance and self.instance.pk:
self.fields['addresses'].initial = self.instance.addresses.all()
2015-10-07 11:44:30 +00:00
def clean(self):
cleaned_data = super(MailboxForm, self).clean()
name = self.instance.name if self.instance.pk else cleaned_data.get('name')
local_domain = settings.MAILBOXES_LOCAL_DOMAIN
if name and local_domain:
try:
addr = Address.objects.get(name=name, domain__name=local_domain, account_id=self.modeladmin.account.pk)
except Address.DoesNotExist:
pass
else:
if addr not in cleaned_data.get('addresses', []):
raise ValidationError({
'addresses': _("This mailbox matches local address '%s', "
"please make explicit this fact by selecting it.") % addr
})
return cleaned_data
2014-10-09 17:04:12 +00:00
2014-10-21 15:29:36 +00:00
class MailboxChangeForm(UserChangeForm, MailboxForm):
2014-10-09 17:04:12 +00:00
pass
2014-10-21 15:29:36 +00:00
class MailboxCreationForm(UserCreationForm, MailboxForm):
2014-10-06 14:57:02 +00:00
def clean_name(self):
# Since model.clean() will check this, this is redundant,
# but it sets a nicer error message than the ORM and avoids conflicts with contrib.auth
name = self.cleaned_data["name"]
try:
self._meta.model._default_manager.get(name=name)
except self._meta.model.DoesNotExist:
return name
2014-10-21 15:29:36 +00:00
raise forms.ValidationError(self.error_messages['duplicate_username'])
2014-10-06 14:57:02 +00:00
class AddressForm(forms.ModelForm):
def clean(self):
cleaned_data = super(AddressForm, self).clean()
2015-10-07 11:44:30 +00:00
forward = cleaned_data.get('forward', '')
if not cleaned_data.get('mailboxes', True) and not forward:
raise ValidationError(_("Mailboxes or forward address should be provided."))
# Check if new addresse matches with a mbox because of having a local domain
if self.instance.pk:
name = self.instance.name
domain = self.instance.domain
else:
name = cleaned_data.get('name')
domain = cleaned_data.get('domain')
if domain and name and domain.name == settings.MAILBOXES_LOCAL_DOMAIN:
if name not in forward.split() and Mailbox.objects.filter(name=name).exists():
for mailbox in cleaned_data.get('mailboxes', []):
if mailbox.name == name:
return
raise ValidationError(
_("This address matches mailbox '%s', please make explicit this fact "
"by adding the mailbox on the mailboxes or forward field.") % name
)
return cleaned_data