django-orchestra/orchestra/apps/mailboxes/models.py

163 lines
5.8 KiB
Python
Raw Normal View History

import os
2014-10-06 14:57:02 +00:00
from django.contrib.auth.hashers import make_password
2014-11-13 15:34:00 +00:00
from django.core.validators import RegexValidator, ValidationError
2014-08-22 11:28:46 +00:00
from django.db import models
2014-09-30 14:46:29 +00:00
from django.utils.functional import cached_property
2014-08-22 11:28:46 +00:00
from django.utils.translation import ugettext_lazy as _
from orchestra.core import services
from . import validators, settings
class Mailbox(models.Model):
2014-10-09 17:04:12 +00:00
CUSTOM = 'CUSTOM'
2014-08-22 11:28:46 +00:00
name = models.CharField(_("name"), max_length=64, unique=True,
help_text=_("Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only."),
validators=[
RegexValidator(r'^[\w.@+-]+$', _("Enter a valid mailbox name."))
])
2014-09-29 12:22:45 +00:00
password = models.CharField(_("password"), max_length=128)
2014-08-22 15:31:44 +00:00
account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
related_name='mailboxes')
2014-10-09 17:04:12 +00:00
filtering = models.CharField(max_length=16,
default=settings.MAILBOXES_MAILBOX_DEFAULT_FILTERING,
choices=[
(k, v[0]) for k,v in settings.MAILBOXES_MAILBOX_FILTERINGS.iteritems()
])
2014-08-22 11:28:46 +00:00
custom_filtering = models.TextField(_("filtering"), blank=True,
validators=[validators.validate_sieve],
2014-09-29 14:45:51 +00:00
help_text=_("Arbitrary email filtering in sieve language. "
"This overrides any automatic junk email filtering"))
2014-09-30 10:20:11 +00:00
is_active = models.BooleanField(_("active"), default=True)
2014-08-22 11:28:46 +00:00
class Meta:
verbose_name_plural = _("mailboxes")
def __unicode__(self):
2014-08-22 15:31:44 +00:00
return self.name
2014-09-30 14:46:29 +00:00
@cached_property
def active(self):
2014-10-07 13:08:59 +00:00
try:
return self.is_active and self.account.is_active
except type(self).account.field.rel.to.DoesNotExist:
return self.is_active
2014-10-06 14:57:02 +00:00
def set_password(self, raw_password):
self.password = make_password(raw_password)
def get_home(self):
context = {
'name': self.name,
'username': self.name,
}
return os.path.normpath(settings.MAILBOXES_HOME % context)
2014-10-09 17:04:12 +00:00
def clean(self):
if self.custom_filtering and self.filtering != self.CUSTOM:
self.custom_filtering = ''
def get_filtering(self):
2014-10-17 10:04:47 +00:00
__, filtering = settings.MAILBOXES_MAILBOX_FILTERINGS[self.filtering]
2014-10-09 17:04:12 +00:00
if isinstance(filtering, basestring):
return filtering
return filtering(self)
def delete(self, *args, **kwargs):
super(Mailbox, self).delete(*args, **kwargs)
# Cleanup related addresses
for address in Address.objects.filter(forward__regex=r'.*(^|\s)+%s($|\s)+.*' % self.name):
forward = address.forward.split()
forward.remove(self.name)
address.forward = ' '.join(forward)
if not address.destination:
address.delete()
else:
address.save()
2014-11-27 19:17:26 +00:00
def get_local_address(self):
if not settings.MAILBOXES_LOCAL_ADDRESS_DOMAIN:
raise AttributeError("Mailboxes do not have a defined local address domain")
return '@'.join((self.name, settings.MAILBOXES_LOCAL_ADDRESS_DOMAIN))
2014-08-22 11:28:46 +00:00
class Address(models.Model):
2014-11-02 14:33:55 +00:00
name = models.CharField(_("name"), max_length=64, blank=True,
validators=[validators.validate_emailname],
help_text=_("Address name, left blank for a <i>catch-all</i> address"))
2014-10-17 10:04:47 +00:00
domain = models.ForeignKey(settings.MAILBOXES_DOMAIN_MODEL,
2014-08-22 11:28:46 +00:00
verbose_name=_("domain"),
related_name='addresses')
2014-08-22 15:31:44 +00:00
mailboxes = models.ManyToManyField(Mailbox,
verbose_name=_("mailboxes"),
2014-08-22 11:28:46 +00:00
related_name='addresses', blank=True)
forward = models.CharField(_("forward"), max_length=256, blank=True,
2014-10-09 17:04:12 +00:00
validators=[validators.validate_forward],
help_text=_("Space separated email addresses or mailboxes"))
2014-08-22 11:28:46 +00:00
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
related_name='addresses')
class Meta:
verbose_name_plural = _("addresses")
unique_together = ('name', 'domain')
def __unicode__(self):
return self.email
@property
def email(self):
return "%s@%s" % (self.name, self.domain)
2014-10-06 14:57:02 +00:00
@cached_property
def destination(self):
destinations = list(self.mailboxes.values_list('name', flat=True))
if self.forward:
destinations += self.forward.split()
return ' '.join(destinations)
2014-10-09 17:04:12 +00:00
2014-11-13 15:34:00 +00:00
def clean(self):
if self.account_id:
forward_errors = []
2014-11-13 15:34:00 +00:00
for mailbox in self.get_forward_mailboxes():
if mailbox.account_id == self.account_id:
forward_errors.append(ValidationError(
2014-11-13 15:34:00 +00:00
_("Please use mailboxes field for '%s' mailbox.") % mailbox
))
if forward_errors:
raise ValidationError({
'forward': forward_errors
})
2014-11-13 15:34:00 +00:00
2014-10-09 17:04:12 +00:00
def get_forward_mailboxes(self):
for forward in self.forward.split():
if '@' not in forward:
try:
yield Mailbox.objects.get(name=forward)
except Mailbox.DoesNotExist:
pass
def get_mailboxes(self):
for mailbox in self.mailboxes.all():
yield mailbox
for mailbox in self.get_forward_mailboxes():
yield mailbox
2014-08-22 11:28:46 +00:00
class Autoresponse(models.Model):
address = models.OneToOneField(Address, verbose_name=_("address"),
related_name='autoresponse')
# TODO initial_date
subject = models.CharField(_("subject"), max_length=256)
message = models.TextField(_("message"))
enabled = models.BooleanField(_("enabled"), default=False)
def __unicode__(self):
return self.address
2014-08-22 15:31:44 +00:00
services.register(Mailbox)
2014-08-22 11:28:46 +00:00
services.register(Address)