gva/gnuviechadmin/managemails/models.py
Jan Dittberner 5429055f0d implement mail address target editing
- extract common code into managemails.forms.MailAddressFieldMixin
- move code from forms into managemails.models.MailAddress
- implement managemails.models.MailboxManager.unused and unused_or_own
- implement managemails.forms.EditMailAddressForm
- add managemails.views.EditMailAddress
- add URL pattern 'edit_mailaddress' to managemails.urls
- add template managemails/mailaddress_edit.html
- add changelog entry
2015-01-25 22:12:03 +01:00

248 lines
7.5 KiB
Python

"""
This module defines the database models for mail handling.
"""
from __future__ import unicode_literals
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext as _
from passlib.hash import sha512_crypt
from model_utils.models import TimeStampedModel
from domains.models import MailDomain
from osusers.models import User as OsUser
from fileservertasks.tasks import (
create_file_mailbox,
delete_file_mailbox,
)
class ActivateAbleMixin(models.Model):
"""
Mixin for model classes that can be active or inactive.
"""
active = models.BooleanField(default=True)
class Meta:
abstract = True
class MailboxManager(models.Manager):
"""
This is the default manager class for :py:class:`Mailbox`.
"""
def get_next_mailbox_name(self, osuser):
"""
Get the next available mailbox name for the given operating system
user.
:param osuser: a :py:class:`operating system user <osuser.models.User>`
instance
:return: name of the mailbox
:rtype: str
"""
count = 1
mailboxformat = "{0}p{1:02d}"
mailboxname = mailboxformat.format(osuser.username, count)
for box in self.values('username').filter(osuser=osuser).order_by(
'username'
):
if box['username'] == mailboxname:
count += 1
mailboxname = mailboxformat.format(osuser.username, count)
else:
break
return mailboxname
def unused_or_own(self, mailaddress, osuser):
"""
Returns a queryset that fetches those mailboxes that belong to the
given operating system user and are either not used by any mail address
or used by the given mailaddress itself.
"""
return self.filter(
models.Q(mailaddressmailbox__isnull=True) |
models.Q(mailaddressmailbox__mailaddress=mailaddress),
active=True, osuser=osuser,
)
def unused(self, osuser):
"""
Returns a queryset that fetches unused mailboxes belonging to the
given operating system user.
"""
return self.filter(
mailaddressmailbox__isnull=True,
active=True, osuser=osuser,
)
@python_2_unicode_compatible
class Mailbox(ActivateAbleMixin, TimeStampedModel):
"""
This is the model class for a mailbox.
"""
osuser = models.ForeignKey(OsUser)
username = models.CharField(max_length=128, unique=True)
password = models.CharField(max_length=255)
objects = MailboxManager()
class Meta:
ordering = ['osuser', 'username']
verbose_name = _('Mailbox')
verbose_name_plural = _('Mailboxes')
def set_password(self, password):
"""
Set the hashed password for the mailbox.
:param str password: the clear text password
"""
self.password = sha512_crypt.encrypt(password)
def save(self, *args, **kwargs):
create_file_mailbox.delay(self.osuser.username, self.username).get()
super(Mailbox, self).save(*args, **kwargs)
def delete(self, *args, **kwargs):
delete_file_mailbox.delay(self.osuser.username, self.username).get()
super(Mailbox, self).delete(*args, **kwargs)
def get_mailaddresses(self):
"""
Get a list of mail addresses assigned to this mailbox.
"""
addrs = [
mbadr.mailaddress for mbadr in
self.mailaddressmailbox_set.all()
]
return addrs
mailaddresses = property(get_mailaddresses)
def __str__(self):
return self.username
@python_2_unicode_compatible
class MailAddress(ActivateAbleMixin, TimeStampedModel, models.Model):
"""
This is the model class for a mail address.
"""
localpart = models.CharField(_('local part'), max_length=128)
domain = models.ForeignKey(MailDomain, verbose_name=_('domain'))
class Meta:
ordering = ['domain', 'localpart']
unique_together = ('localpart', 'domain')
verbose_name = _('Mail address')
verbose_name_plural = _('Mail addresses')
def __str__(self):
return "{0}@{1}".format(self.localpart, self.domain)
def set_mailbox(self, mailbox, commit=True):
"""
Set the target for this mail address to the given mailbox. This method
takes care of removing forwarding addresses or changing existing
mailbox relations.
:param mailbox: :py:class:`Mailbox`
:param boolean commit: whether to persist changes or not
:return: :py:class:`MailAddressMailbox` instance
"""
if self.pk is not None:
if MailAddressMailbox.objects.filter(mailaddress=self).exists():
mabox = MailAddressMailbox.objects.get(mailaddress=self)
mabox.mailbox = mailbox
else:
mabox = MailAddressMailbox(mailaddress=self, mailbox=mailbox)
if commit:
mabox.save()
for mafwd in MailAddressForward.objects.filter(mailaddress=self):
mafwd.delete()
else:
mabox = MailAddressMailbox(mailaddress=self, mailbox=mailbox)
if commit:
mabox.save()
return mabox
def set_forward_addresses(self, addresses, commit=True):
"""
Set the forwarding addresses for this mail address to the given
addresses. This method takes care of removing other mail forwards and
mailboxes.
:param addresses: list of email address strings
:param boolean commit: whether to persist changes or not
:return: list of :py:class:`MailAddressForward` instances
"""
retval = []
if self.pk is not None:
if MailAddressMailbox.objects.filter(mailaddress=self).exists():
mabox = MailAddressMailbox.objects.get(mailaddress=self)
mabox.delete()
forwards = MailAddressForward.objects.filter(
mailaddress=self).all()
for item in forwards:
if not item.target in addresses:
item.delete()
else:
retval.append(item)
for target in addresses:
if not target in [item.target for item in forwards]:
mafwd = MailAddressForward(mailaddress=self, target=target)
if commit:
mafwd.save()
retval.append(item)
else:
for target in addresses:
mafwd = MailAddressForward(mailaddress=self, target=target)
if commit:
mafwd.save()
retval.append(item)
return retval
@python_2_unicode_compatible
class MailAddressMailbox(TimeStampedModel, models.Model):
"""
This is the model class to assign a mail address to a mailbox.
"""
mailaddress = models.OneToOneField(
MailAddress, verbose_name=_('mailaddress'), primary_key=True)
mailbox = models.ForeignKey(Mailbox, verbose_name=_('mailbox'))
class Meta:
unique_together = ('mailaddress', 'mailbox')
def __str__(self):
return self.mailbox.username
class MailAddressForward(TimeStampedModel, models.Model):
"""
This is a model class to map mail addresses to forwarding addresses.
"""
mailaddress = models.ForeignKey(MailAddress)
target = models.EmailField(max_length=254)
class Meta:
unique_together = ('mailaddress', 'target')