import logging from django.conf import settings from django.contrib import admin from django.contrib import messages from django.forms.widgets import TextInput from django.shortcuts import reverse from django.utils import timezone from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ from django_mail_admin.models import Mailbox, IncomingAttachment, IncomingEmail, TemplateVariable, OutgoingEmail, \ Outbox, EmailTemplate, STATUS, Log, Attachment from django_mail_admin.signals import message_received from django_mail_admin.utils import convert_header_to_unicode from .fields import CommaSeparatedEmailField from .forms import OutgoingEmailAdminForm logger = logging.getLogger(__name__) # Admin row actions if 'django_admin_row_actions' in settings.INSTALLED_APPS: try: from django_admin_row_actions import AdminRowActionsMixin except ImportError: admin_row_actions = False else: admin_row_actions = True else: admin_row_actions = False def get_parent(): """ Optionally adds AdminRowActionsMixin to admin.ModelAdmin if django_admin_row_actions is installed :return: class to inherit from """ if admin_row_actions: class BaseAdmin(AdminRowActionsMixin, admin.ModelAdmin): pass else: class BaseAdmin(admin.ModelAdmin): pass return BaseAdmin def get_new_mail(mailbox_admin, request, queryset): for mailbox in queryset.all(): logger.debug('Receiving mail for %s' % mailbox) got_mail = mailbox.get_new_mail() if len(got_mail) > 0: messages.success(request, _('Got {} new letters for mailbox "{}"').format(str(len(got_mail)), mailbox.name)) else: messages.info(request, _('No new mail for mailbox "{}"').format(mailbox.name)) get_new_mail.short_description = _('Get new mail') def switch_active(mailbox_admin, request, queryset): for mailbox in queryset.all(): mailbox.active = not mailbox.active mailbox.save() switch_active.short_description = _('Switch active status') class MailboxAdmin(get_parent()): list_display = ( 'name', 'uri', 'from_email', 'active', 'last_polling', ) readonly_fields = ['last_polling', ] actions = [get_new_mail, switch_active] class IncomingAttachmentInline(admin.TabularInline): model = IncomingAttachment extra = 0 readonly_fields = ['headers', ] def resend_message_received_signal(incoming_email_admin, request, queryset): for message in queryset.all(): logger.debug('Resending \'message_received\' signal for %s' % message) message_received.send(sender=incoming_email_admin, message=message) resend_message_received_signal.short_description = ( _('Re-send message received signal') ) def mark_as_unread(incoming_email_admin, request, queryset): for msg in queryset.all(): msg.read = None msg.save() mark_as_unread.short_description = _('Mark as unread') def mark_as_read(incoming_email_admin, request, queryset): for msg in queryset.all(): msg.read = timezone.now() msg.save() mark_as_read.short_description = _('Mark as read') def custom_titled_filter(title): class Wrapper(admin.FieldListFilter): def __new__(cls, *args, **kwargs): instance = admin.FieldListFilter.create(*args, **kwargs) instance.title = title return instance return Wrapper class IncomingEmailAdmin(admin.ModelAdmin): def html(self, msg): return mark_safe(msg.html) def attachment_count(self, msg): return msg.attachments.count() attachment_count.short_description = _('Attachment count') def subject(self, msg): return convert_header_to_unicode(msg.subject) def mailbox_link(self, msg): return mark_safe('<a href="' + reverse('admin:django_mail_admin_mailbox_change', args=[msg.mailbox.pk]) + '">' + msg.mailbox.name + '</a>') mailbox_link.short_description = _('Mailbox') def reply_link(self, msg): if msg.in_reply_to: return mark_safe( '<a href="' + reverse('admin:django_mail_admin_outgoingemail_change', args=[msg.in_reply_to.pk]) + '">' + msg.in_reply_to.subject + '</a>') else: return '' reply_link.short_description = _('Reply to') def from_address(self, msg): f = msg.from_address if len(f) > 0: return ','.join(f) else: return '' from_address.short_description = _('From') def envelope_headers(self, msg): email = msg.get_email_object() return '\n'.join( [('%s: %s' % (h, v)) for h, v in email.items()] ) inlines = [ IncomingAttachmentInline, ] fieldsets = [ (None, {'fields': [('mailbox', 'message_id'), 'read']}), (None, {'fields': [('from_header', 'in_reply_to'), 'to_header']}), (None, {'fields': ['text', 'html']}), ] list_display = ( 'subject', 'from_address', 'processed', 'read', 'mailbox_link', 'attachment_count', 'reply_link' ) ordering = ['-processed'] list_filter = ( ('mailbox__name', custom_titled_filter(_('Mailbox name'))), 'processed', 'read', ) exclude = ( 'body', ) raw_id_fields = ( 'in_reply_to', ) readonly_fields = ( 'envelope_headers', 'message_id', 'text', 'html', ) search_fields = ['mailbox__name', 'subject', 'from_header', 'in_reply_to__subject'] actions = [resend_message_received_signal, mark_as_unread, mark_as_read] def has_add_permission(self, request): return False def change_view(self, request, object_id, form_url='', extra_context=None): obj = IncomingEmail.objects.filter(id=object_id).first() if obj: if not obj.read: obj.read = timezone.now() obj.save() return super(IncomingEmailAdmin, self).change_view( request, object_id, form_url, extra_context=extra_context, ) class IncomingAttachmentAdmin(admin.ModelAdmin): raw_id_fields = ('message',) list_display = ('message', 'document',) if admin_row_actions: def get_row_actions(self, obj): row_actions = [ { 'label': _('View emails'), 'url': reverse('admin:django_mail_admin_incomingemail_changelist') + '?mailbox__name=' + obj.name, 'tooltip': _('View emails'), }, { 'divided': True, 'label': _('Get new mail'), 'action': 'get_new_mail', # calls model's get_new_mail }, ] row_actions += super(MailboxAdmin, self).get_row_actions(obj) return row_actions MailboxAdmin.get_row_actions = get_row_actions class EmailTemplateAdmin(admin.ModelAdmin): list_display = ('name', 'description', 'subject') class TemplateVariableInline(admin.TabularInline): model = TemplateVariable extra = 1 def get_message_preview(instance): return ('{0}...'.format(instance.message[:25]) if len(instance.message) > 25 else instance.message) get_message_preview.short_description = _('Message') class AttachmentInline(admin.TabularInline): model = Attachment.emails.through extra = 1 verbose_name = _("Attachment") verbose_name_plural = _("Attachments") class CommaSeparatedEmailWidget(TextInput): def __init__(self, *args, **kwargs): super(CommaSeparatedEmailWidget, self).__init__(*args, **kwargs) self.attrs.update({'class': 'vTextField'}) def _format_value(self, value): # If the value is a string wrap it in a list so it does not get sliced. if not value: return '' if isinstance(value, str): value = [value, ] return ','.join([item for item in value]) def requeue(modeladmin, request, queryset): """An admin action to requeue emails.""" queryset.update(status=STATUS.queued) requeue.short_description = _('Requeue selected emails') class OutgoingEmailAdmin(admin.ModelAdmin): inlines = (TemplateVariableInline, AttachmentInline) list_display = ['id', 'to_display', 'subject', 'template', 'from_email', 'status', 'scheduled_time', 'priority'] formfield_overrides = { CommaSeparatedEmailField: {'widget': CommaSeparatedEmailWidget} } actions = [requeue] form = OutgoingEmailAdminForm def to_display(self, instance): return ', '.join(instance.to) to_display.short_description = _('To') def get_form(self, request, obj=None, **kwargs): # Try to get active Outbox and prepopulate from_email field form = super(OutgoingEmailAdmin, self).get_form(request, obj, **kwargs) configurations = Outbox.objects.filter(active=True) if not (len(configurations) > 1 or len(configurations) == 0): form.base_fields['from_email'].initial = configurations.first().email_host_user return form def save_model(self, request, obj, form, change): super(OutgoingEmailAdmin, self).save_model(request, obj, form, change) # If we have an email to reply to, specify replied headers if form.cleaned_data['reply']: if not obj.headers: obj.headers = {} obj.headers.update(form.cleaned_data['reply'].get_reply_headers(obj.headers)) obj.save() # TODO: add setting to only queue emails after pressing a button/etc. obj.queue() class AttachmentAdmin(admin.ModelAdmin): list_display = ('name', 'file',) class OutboxAdmin(admin.ModelAdmin): list_display = ('name', 'email_host', 'email_host_user', 'email_port', 'id', 'active') list_filter = ('active',) class LogAdmin(admin.ModelAdmin): list_display = ('email', 'status', 'date', 'message') if getattr(settings, 'DJANGO_MAILADMIN_ADMIN_ENABLED', True): admin.site.register(IncomingEmail, IncomingEmailAdmin) admin.site.register(IncomingAttachment, IncomingAttachmentAdmin) admin.site.register(Mailbox, MailboxAdmin) admin.site.register(EmailTemplate, EmailTemplateAdmin) # Without this attachment inline won't have add/edit buttons admin.site.register(Attachment, AttachmentAdmin) admin.site.register(OutgoingEmail, OutgoingEmailAdmin) admin.site.register(Outbox, OutboxAdmin) admin.site.register(Log, LogAdmin)