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
This commit is contained in:
parent
bebcad8c86
commit
5429055f0d
6 changed files with 269 additions and 26 deletions
|
@ -1,6 +1,7 @@
|
||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
* :feature:`-` implement mail address target editing
|
||||||
* :feature:`-` implement mail address deletion
|
* :feature:`-` implement mail address deletion
|
||||||
* :feature:`-` implement adding mail address to mail domains
|
* :feature:`-` implement adding mail address to mail domains
|
||||||
* :feature:`-` implement adding options to hosting packages
|
* :feature:`-` implement adding options to hosting packages
|
||||||
|
|
|
@ -19,8 +19,6 @@ from crispy_forms.layout import (
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
MailAddress,
|
MailAddress,
|
||||||
MailAddressForward,
|
|
||||||
MailAddressMailbox,
|
|
||||||
Mailbox,
|
Mailbox,
|
||||||
)
|
)
|
||||||
from gvawebcore.forms import PasswordModelFormMixin
|
from gvawebcore.forms import PasswordModelFormMixin
|
||||||
|
@ -106,9 +104,9 @@ def multiple_email_validator(value):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
class AddMailAddressForm(forms.ModelForm):
|
class MailAddressFieldMixin(forms.Form):
|
||||||
"""
|
"""
|
||||||
This form is used to add a new mail address.
|
This mixin defines form fields common to mail address forms.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
mailbox_or_forwards = forms.TypedChoiceField(
|
mailbox_or_forwards = forms.TypedChoiceField(
|
||||||
|
@ -133,6 +131,12 @@ class AddMailAddressForm(forms.ModelForm):
|
||||||
validators=[multiple_email_validator],
|
validators=[multiple_email_validator],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AddMailAddressForm(forms.ModelForm, MailAddressFieldMixin):
|
||||||
|
"""
|
||||||
|
This form is used to add a new mail address.
|
||||||
|
|
||||||
|
"""
|
||||||
class Meta:
|
class Meta:
|
||||||
model = MailAddress
|
model = MailAddress
|
||||||
fields = ['localpart']
|
fields = ['localpart']
|
||||||
|
@ -141,8 +145,8 @@ class AddMailAddressForm(forms.ModelForm):
|
||||||
self.maildomain = kwargs.pop('maildomain')
|
self.maildomain = kwargs.pop('maildomain')
|
||||||
self.hosting_package = kwargs.pop('hostingpackage')
|
self.hosting_package = kwargs.pop('hostingpackage')
|
||||||
super(AddMailAddressForm, self).__init__(*args, **kwargs)
|
super(AddMailAddressForm, self).__init__(*args, **kwargs)
|
||||||
self.fields['mailbox'].queryset = Mailbox.objects.filter(
|
self.fields['mailbox'].queryset = Mailbox.objects.unused(
|
||||||
active=True, osuser=self.hosting_package.osuser,
|
osuser=self.hosting_package.osuser,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
|
@ -186,11 +190,10 @@ class AddMailAddressForm(forms.ModelForm):
|
||||||
data = self.cleaned_data
|
data = self.cleaned_data
|
||||||
if data['mailbox_or_forwards'] == MAILBOX_OR_FORWARDS.mailbox:
|
if data['mailbox_or_forwards'] == MAILBOX_OR_FORWARDS.mailbox:
|
||||||
if not data['mailbox']:
|
if not data['mailbox']:
|
||||||
raise forms.ValidationError(_('No mailbox selected'))
|
self.add_error('mailbox', _('No mailbox selected'))
|
||||||
elif data['mailbox_or_forwards'] == MAILBOX_OR_FORWARDS.forwards:
|
elif data['mailbox_or_forwards'] == MAILBOX_OR_FORWARDS.forwards:
|
||||||
if 'forwards' not in data:
|
if 'forwards' not in data or not data['forwards']:
|
||||||
raise forms.ValidationError(
|
self.add_error('forwards', _('No forward addresses selected'))
|
||||||
_('No forward addresses selected'))
|
|
||||||
else:
|
else:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
_('Illegal choice for target of the mail address'))
|
_('Illegal choice for target of the mail address'))
|
||||||
|
@ -205,21 +208,87 @@ class AddMailAddressForm(forms.ModelForm):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.instance.domain = self.maildomain
|
self.instance.domain = self.maildomain
|
||||||
target_choice = self.cleaned_data['mailbox_or_forwards']
|
data = self.cleaned_data
|
||||||
mailaddress = super(AddMailAddressForm, self).save(commit)
|
target_choice = data['mailbox_or_forwards']
|
||||||
if target_choice == MAILBOX_OR_FORWARDS.mailbox:
|
if target_choice == MAILBOX_OR_FORWARDS.mailbox:
|
||||||
mailbox = MailAddressMailbox(
|
mabox = self.instance.set_mailbox(data['mailbox'], commit=False)
|
||||||
mailaddress=mailaddress,
|
|
||||||
mailbox=self.cleaned_data['mailbox'])
|
|
||||||
if commit:
|
|
||||||
mailbox.save()
|
|
||||||
elif target_choice == MAILBOX_OR_FORWARDS.forwards:
|
elif target_choice == MAILBOX_OR_FORWARDS.forwards:
|
||||||
for address in [
|
targets = [part.strip() for part in data['forwards'].split()]
|
||||||
part.strip() for part in
|
fwds = self.instance.set_forward_addresses(targets, commit=False)
|
||||||
self.cleaned_data['forwards'].split(',')
|
mailaddress = super(AddMailAddressForm, self).save(commit)
|
||||||
]:
|
|
||||||
forward = MailAddressForward(
|
|
||||||
mailaddress=mailaddress, target=part)
|
|
||||||
if commit:
|
if commit:
|
||||||
forward.save()
|
if target_choice == MAILBOX_OR_FORWARDS.mailbox:
|
||||||
|
mabox.mailaddress = mailaddress
|
||||||
|
mabox.save()
|
||||||
|
elif target_choice == MAILBOX_OR_FORWARDS.forwards:
|
||||||
|
for fwd in fwds:
|
||||||
|
fwd.mailaddress = mailaddress
|
||||||
|
fwd.save()
|
||||||
return mailaddress
|
return mailaddress
|
||||||
|
|
||||||
|
|
||||||
|
class EditMailAddressForm(forms.ModelForm, MailAddressFieldMixin):
|
||||||
|
"""
|
||||||
|
This form is used to edit the targets for a mail address.
|
||||||
|
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
model = MailAddress
|
||||||
|
fields = []
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.hosting_package = kwargs.pop('hostingpackage')
|
||||||
|
self.maildomain = kwargs.pop('maildomain')
|
||||||
|
super(EditMailAddressForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['mailbox'].queryset = Mailbox.objects.unused_or_own(
|
||||||
|
self.instance, self.hosting_package.osuser
|
||||||
|
)
|
||||||
|
|
||||||
|
self.helper = FormHelper()
|
||||||
|
self.helper.form_action = reverse(
|
||||||
|
'edit_mailaddress', kwargs={
|
||||||
|
'package': self.hosting_package.id,
|
||||||
|
'domain': self.maildomain.domain,
|
||||||
|
'pk': self.instance.id,
|
||||||
|
})
|
||||||
|
self.helper.layout = Layout(
|
||||||
|
Div(
|
||||||
|
Div(
|
||||||
|
'mailbox_or_forwards',
|
||||||
|
css_class='col-log-2 col-md-2 col-xs-12',
|
||||||
|
),
|
||||||
|
Div(
|
||||||
|
'mailbox',
|
||||||
|
'forwards',
|
||||||
|
css_class='col-lg-10 col-md-10 col-xs-12',
|
||||||
|
),
|
||||||
|
css_class='row',
|
||||||
|
),
|
||||||
|
Submit('submit', _('Change mail address targets')),
|
||||||
|
)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
data = self.cleaned_data
|
||||||
|
if data['mailbox_or_forwards'] == MAILBOX_OR_FORWARDS.mailbox:
|
||||||
|
if not data['mailbox']:
|
||||||
|
self.add_error('mailbox', _('No mailbox selected'))
|
||||||
|
elif data['mailbox_or_forwards'] == MAILBOX_OR_FORWARDS.forwards:
|
||||||
|
if 'forwards' not in data or not data['forwards']:
|
||||||
|
self.add_error('forwards', _('No forward addresses selected'))
|
||||||
|
else:
|
||||||
|
raise forms.ValidationError(
|
||||||
|
_('Illegal choice for target of the mail address'))
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
"""
|
||||||
|
Save the mail address.
|
||||||
|
|
||||||
|
:param boolean commit:
|
||||||
|
"""
|
||||||
|
data = self.cleaned_data
|
||||||
|
if data['mailbox_or_forwards'] == MAILBOX_OR_FORWARDS.mailbox:
|
||||||
|
self.instance.set_mailbox(data['mailbox'], commit)
|
||||||
|
elif data['mailbox_or_forwards'] == MAILBOX_OR_FORWARDS.forwards:
|
||||||
|
targets = [part.strip() for part in data['forwards'].split(',')]
|
||||||
|
self.instance.set_foward_addresses(targets, commit)
|
||||||
|
return self.instance
|
||||||
|
|
|
@ -61,6 +61,30 @@ class MailboxManager(models.Manager):
|
||||||
break
|
break
|
||||||
return mailboxname
|
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
|
@python_2_unicode_compatible
|
||||||
class Mailbox(ActivateAbleMixin, TimeStampedModel):
|
class Mailbox(ActivateAbleMixin, TimeStampedModel):
|
||||||
|
@ -130,6 +154,70 @@ class MailAddress(ActivateAbleMixin, TimeStampedModel, models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{0}@{1}".format(self.localpart, self.domain)
|
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
|
@python_2_unicode_compatible
|
||||||
class MailAddressMailbox(TimeStampedModel, models.Model):
|
class MailAddressMailbox(TimeStampedModel, models.Model):
|
||||||
|
|
|
@ -12,6 +12,7 @@ from .views import (
|
||||||
ChangeMailboxPassword,
|
ChangeMailboxPassword,
|
||||||
CreateMailbox,
|
CreateMailbox,
|
||||||
DeleteMailAddress,
|
DeleteMailAddress,
|
||||||
|
EditMailAddress,
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = patterns(
|
urlpatterns = patterns(
|
||||||
|
@ -22,6 +23,9 @@ urlpatterns = patterns(
|
||||||
ChangeMailboxPassword.as_view(), name='change_mailbox_password'),
|
ChangeMailboxPassword.as_view(), name='change_mailbox_password'),
|
||||||
url(r'^(?P<package>\d+)/mailaddress/(?P<domain>[\w0-9-.]+)/create$',
|
url(r'^(?P<package>\d+)/mailaddress/(?P<domain>[\w0-9-.]+)/create$',
|
||||||
AddMailAddress.as_view(), name='add_mailaddress'),
|
AddMailAddress.as_view(), name='add_mailaddress'),
|
||||||
|
url(r'^(?P<package>\d+)/mailaddress/(?P<domain>[\w0-9-.]+)/(?P<pk>\d+)'
|
||||||
|
r'/edit$',
|
||||||
|
EditMailAddress.as_view(), name='edit_mailaddress'),
|
||||||
url(r'^(?P<package>\d+)/mailaddress/(?P<domain>[\w0-9-.]+)/(?P<pk>\d+)'
|
url(r'^(?P<package>\d+)/mailaddress/(?P<domain>[\w0-9-.]+)/(?P<pk>\d+)'
|
||||||
r'/delete$',
|
r'/delete$',
|
||||||
DeleteMailAddress.as_view(), name='delete_mailaddress'),
|
DeleteMailAddress.as_view(), name='delete_mailaddress'),
|
||||||
|
|
|
@ -23,9 +23,12 @@ from .forms import (
|
||||||
AddMailAddressForm,
|
AddMailAddressForm,
|
||||||
ChangeMailboxPasswordForm,
|
ChangeMailboxPasswordForm,
|
||||||
CreateMailboxForm,
|
CreateMailboxForm,
|
||||||
|
EditMailAddressForm,
|
||||||
|
MAILBOX_OR_FORWARDS,
|
||||||
)
|
)
|
||||||
from .models import (
|
from .models import (
|
||||||
MailAddress,
|
MailAddress,
|
||||||
|
MailAddressMailbox,
|
||||||
Mailbox,
|
Mailbox,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -188,3 +191,55 @@ class DeleteMailAddress(
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return self.get_hosting_package().get_absolute_url()
|
return self.get_hosting_package().get_absolute_url()
|
||||||
|
|
||||||
|
|
||||||
|
class EditMailAddress(
|
||||||
|
HostingPackageAndCustomerMixin, StaffOrSelfLoginRequiredMixin, UpdateView
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
This view is used to edit a mail address' target mailbox or forwarding
|
||||||
|
addresses.
|
||||||
|
|
||||||
|
"""
|
||||||
|
context_object_name = 'mailaddress'
|
||||||
|
form_class = EditMailAddressForm
|
||||||
|
model = MailAddress
|
||||||
|
template_name_suffix = '_edit'
|
||||||
|
|
||||||
|
def get_maildomain(self):
|
||||||
|
return get_object_or_404(MailDomain, domain=self.kwargs['domain'])
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(EditMailAddress, self).get_context_data(**kwargs)
|
||||||
|
context['customer'] = self.get_customer_object()
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_form_kwargs(self):
|
||||||
|
kwargs = super(EditMailAddress, self).get_form_kwargs()
|
||||||
|
kwargs.update({
|
||||||
|
'hostingpackage': self.get_hosting_package(),
|
||||||
|
'maildomain': self.get_maildomain(),
|
||||||
|
})
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
initial = super(EditMailAddress, self).get_initial()
|
||||||
|
mailaddress = self.get_object()
|
||||||
|
if MailAddressMailbox.objects.filter(mailaddress=mailaddress).exists():
|
||||||
|
initial['mailbox'] = mailaddress.mailaddressmailbox.mailbox
|
||||||
|
initial['mailbox_or_forwards'] = MAILBOX_OR_FORWARDS.mailbox
|
||||||
|
elif mailaddress.mailaddressforward_set.exists():
|
||||||
|
initial['forwards'] = ", ".join(
|
||||||
|
fwd.target for fwd in mailaddress.mailaddressforward_set.all()
|
||||||
|
)
|
||||||
|
initial['mailbox_or_forwards'] = MAILBOX_OR_FORWARDS.forwards
|
||||||
|
return initial
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
mailaddress = form.save()
|
||||||
|
messages.success(
|
||||||
|
self.request,
|
||||||
|
_('Successfully updated mail address {mailaddress} '
|
||||||
|
'targets.').format(mailaddress=mailaddress)
|
||||||
|
)
|
||||||
|
return redirect(self.get_hosting_package())
|
||||||
|
|
26
gnuviechadmin/templates/managemails/mailaddress_edit.html
Normal file
26
gnuviechadmin/templates/managemails/mailaddress_edit.html
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{% extends "managemails/base.html" %}
|
||||||
|
{% load i18n crispy_forms_tags %}
|
||||||
|
|
||||||
|
{% block title %}{{ block.super }} - {% spaceless %}
|
||||||
|
{% if user == customer %}
|
||||||
|
{% blocktrans %}Change target of Mail Address{% endblocktrans %}
|
||||||
|
{% else %}
|
||||||
|
{% blocktrans with full_name=customer.get_full_name %}
|
||||||
|
Change target of Mail Address for Customer {{ full_name }}
|
||||||
|
{% endblocktrans %}
|
||||||
|
{% endif %}
|
||||||
|
{% endspaceless %}{% endblock title %}
|
||||||
|
|
||||||
|
{% block page_title %}{% spaceless %}
|
||||||
|
{% if user == customer %}
|
||||||
|
{% blocktrans %}Change target of Mail Address{% endblocktrans %}
|
||||||
|
{% else %}
|
||||||
|
{% blocktrans with full_name=customer.get_full_name %}
|
||||||
|
Change target of Mail Address for Customer {{ full_name }}
|
||||||
|
{% endblocktrans %}
|
||||||
|
{% endif %}
|
||||||
|
{% endspaceless %}{% endblock page_title %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% crispy form %}
|
||||||
|
{% endblock content %}
|
Loading…
Reference in a new issue