import copy
from urllib.parse import parse_qs
from django import forms
from django.contrib import admin, messages
from django.core.urlresolvers import reverse
from django.db.models import F, Value as V
from django.db.models.functions import Concat
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
from orchestra.admin.actions import disable
from orchestra.admin.utils import admin_link, change_url
from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import SelectAccountAdminMixin
from orchestra.contrib.accounts.filters import IsActiveListFilter
from . import settings
from .actions import SendMailboxEmail, SendAddressEmail
from .filters import HasMailboxListFilter, HasForwardListFilter, HasAddressListFilter
from .forms import MailboxCreationForm, MailboxChangeForm, AddressForm
from .models import Mailbox, Address, Autoresponse
from .widgets import OpenCustomFilteringOnSelect
class AutoresponseInline(admin.StackedInline):
model = Autoresponse
verbose_name_plural = _("autoresponse")
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == 'subject':
kwargs['widget'] = forms.TextInput(attrs={'size':'118'})
return super(AutoresponseInline, self).formfield_for_dbfield(db_field, **kwargs)
class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = (
'name', 'account_link', 'display_filtering', 'display_addresses', 'display_active',
)
list_filter = (IsActiveListFilter, HasAddressListFilter, 'filtering')
search_fields = (
'account__username', 'account__short_name', 'account__full_name', 'name',
'addresses__name', 'addresses__domain__name',
)
add_fieldsets = (
(None, {
'fields': ('account_link', 'name', 'password1', 'password2', 'filtering'),
}),
(_("Custom filtering"), {
'classes': ('collapse',),
'fields': ('custom_filtering',),
}),
(_("Addresses"), {
'fields': ('addresses',)
}),
)
fieldsets = (
(None, {
'fields': ('name', 'password', 'is_active', 'account_link', 'filtering'),
}),
(_("Custom filtering"), {
'classes': ('collapse',),
'fields': ('custom_filtering',),
}),
(_("Addresses"), {
'fields': ('addresses',)
}),
)
readonly_fields = ('account_link', 'display_addresses')
change_readonly_fields = ('name',)
add_form = MailboxCreationForm
form = MailboxChangeForm
list_prefetch_related = ('addresses__domain',)
actions = (disable, list_accounts)
def __init__(self, *args, **kwargs):
super(MailboxAdmin, self).__init__(*args, **kwargs)
if settings.MAILBOXES_LOCAL_ADDRESS_DOMAIN:
type(self).actions = self.actions + (SendMailboxEmail(),)
def display_addresses(self, mailbox):
addresses = []
for addr in mailbox.addresses.all():
url = change_url(addr)
addresses.append('%s' % (url, addr.email))
return '
'.join(addresses)
display_addresses.short_description = _("Addresses")
display_addresses.allow_tags = True
def display_filtering(self, mailbox):
""" becacuse of allow_tags = True """
return mailbox.get_filtering_display()
display_filtering.short_description = _("Filtering")
display_filtering.admin_order_field = 'filtering'
display_filtering.allow_tags = True
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == 'filtering':
kwargs['widget'] = OpenCustomFilteringOnSelect()
return super(MailboxAdmin, self).formfield_for_dbfield(db_field, **kwargs)
def get_fieldsets(self, request, obj=None):
fieldsets = super(MailboxAdmin, self).get_fieldsets(request, obj)
if obj and obj.filtering == obj.CUSTOM:
# not collapsed filtering when exists
fieldsets = copy.deepcopy(fieldsets)
fieldsets[1][1]['classes'] = fieldsets[0][1]['fields'] + ('collapse', 'open',)
elif '_to_field' in parse_qs(request.META['QUERY_STRING']):
# remove address from popup
fieldsets = list(copy.deepcopy(fieldsets))
fieldsets.pop(-1)
return fieldsets
def get_form(self, *args, **kwargs):
form = super(MailboxAdmin, self).get_form(*args, **kwargs)
form.modeladmin = self
return form
def get_search_results(self, request, queryset, search_term):
# Remove local domain from the search term if present (implicit local addreƧ)
search_term = search_term.replace('@'+settings.MAILBOXES_LOCAL_DOMAIN, '')
# Split address name from domain in order to support address searching
search_term = search_term.replace('@', ' ')
return super(MailboxAdmin, self).get_search_results(request, queryset, search_term)
def render_change_form(self, request, context, *args, **kwargs):
# Check if there exists an unrelated local Address for this mbox
local_domain = settings.MAILBOXES_LOCAL_DOMAIN
obj = kwargs['obj']
if local_domain and obj.name:
non_mbox_addresses = Address.objects.exclude(mailboxes__name=obj.name).exclude(
forward__regex=r'.*(^|\s)+%s($|\s)+.*' % obj.name)
try:
addr = non_mbox_addresses.get(name=obj.name, domain__name=local_domain)
except Address.DoesNotExist:
pass
else:
url = reverse('admin:mailboxes_address_change', args=(addr.pk,))
msg = _("Address {addr} clashes with this mailbox "
"local address. Consider adding this mailbox to the address.").format(
url=url, addr=addr)
self.message_user(request, mark_safe(msg), level=messages.WARNING)
return super(MailboxAdmin, self).render_change_form(request, context, *args, **kwargs)
def save_model(self, request, obj, form, change):
""" save hacky mailbox.addresses and local domain clashing """
super(MailboxAdmin, self).save_model(request, obj, form, change)
obj.addresses = form.cleaned_data['addresses']
class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = (
'display_email', 'account_link', 'domain_link', 'display_mailboxes', 'display_forward',
)
list_filter = (HasMailboxListFilter, HasForwardListFilter)
fields = ('account_link', 'email_link', 'mailboxes', 'forward')
add_fields = ('account_link', ('name', 'domain'), 'mailboxes', 'forward')
# inlines = [AutoresponseInline]
search_fields = ('forward', 'mailboxes__name', 'account__username', 'computed_email')
readonly_fields = ('account_link', 'domain_link', 'email_link')
actions = (SendAddressEmail(),)
filter_by_account_fields = ('domain', 'mailboxes')
filter_horizontal = ['mailboxes']
form = AddressForm
list_prefetch_related = ('mailboxes', 'domain')
domain_link = admin_link('domain', order='domain__name')
def display_email(self, address):
return address.computed_email
display_email.short_description = _("Email")
display_email.admin_order_field = 'computed_email'
def email_link(self, address):
link = self.domain_link(address)
return "%s@%s" % (address.name, link)
email_link.short_description = _("Email")
email_link.allow_tags = True
def display_mailboxes(self, address):
boxes = []
for mailbox in address.mailboxes.all():
url = change_url(mailbox)
boxes.append('%s' % (url, mailbox.name))
return '
'.join(boxes)
display_mailboxes.short_description = _("Mailboxes")
display_mailboxes.allow_tags = True
def display_forward(self, address):
values = [ dest for dest in address.forward.split() ]
return '
'.join(values)
display_forward.short_description = _("Forward")
display_forward.allow_tags = True
display_forward.admin_order_field = 'forward'
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == 'forward':
kwargs['widget'] = forms.TextInput(attrs={'size':'118'})
return super(AddressAdmin, self).formfield_for_dbfield(db_field, **kwargs)
def get_fields(self, request, obj=None):
""" Remove mailboxes field when creating address from a popup i.e. from mailbox add form """
fields = super(AddressAdmin, self).get_fields(request, obj)
if '_to_field' in parse_qs(request.META['QUERY_STRING']):
# Add address popup
fields = list(fields)
fields.remove('mailboxes')
return fields
def get_queryset(self, request):
qs = super(AddressAdmin, self).get_queryset(request)
return qs.annotate(computed_email=Concat(F('name'), V('@'), F('domain__name')))
admin.site.register(Mailbox, MailboxAdmin)
admin.site.register(Address, AddressAdmin)