Skip to content

Commit

Permalink
additional fields in email attachment & black update (#36)
Browse files Browse the repository at this point in the history
* additional fields in email attachment & black update

* tests

Co-authored-by: Łukasz Sitko <lukasz.sitko@deployed.pl>
  • Loading branch information
citos88 and Łukasz Sitko committed Aug 9, 2022
1 parent fb3bfec commit 947c82c
Show file tree
Hide file tree
Showing 30 changed files with 1,039 additions and 423 deletions.
4 changes: 4 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ Assumptions
Changelog
=========

1.1.14
------
* Additional fields for email attachment - https://github.com/deployed/django-emailtemplates/pull/36

1.1.13
------
* Change default auto field to BigAutoField - https://github.com/deployed/django-emailtemplates/pull/35
Expand Down
4 changes: 2 additions & 2 deletions emailtemplates/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
default_app_config = 'emailtemplates.apps.EmailtempatesConfig'
default_app_config = "emailtemplates.apps.EmailtempatesConfig"

VERSION = (1, 1, 13)
VERSION = (1, 1, 14)

# Dynamically calculate the version based on VERSION tuple
if len(VERSION) > 2 and VERSION[2] is not None:
Expand Down
44 changes: 29 additions & 15 deletions emailtemplates/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
from django.utils.translation import gettext_lazy as _

from .forms import EmailTemplateAdminForm, MassEmailMessageForm, MassEmailAttachmentForm
from .models import EmailTemplate, MassEmailMessage, MassEmailAttachment, EmailAttachment
from .models import (
EmailTemplate,
MassEmailMessage,
MassEmailAttachment,
EmailAttachment,
)


class EmailTemplateAttachmentInline(admin.TabularInline):
Expand All @@ -19,33 +24,42 @@ class EmailTemplateAdmin(admin.ModelAdmin):
"""
Admin view of EmailTemplate
"""
list_display = ('title', 'language', 'subject',)
list_display_links = ('title',)
list_filter = ('title', 'language',)
search_fields = ('title', 'subject')

list_display = (
"title",
"language",
"subject",
)
list_display_links = ("title",)
list_filter = (
"title",
"language",
)
search_fields = ("title", "subject")
form = EmailTemplateAdminForm
save_on_top = True
save_as = True
readonly_fields = ['show_links', 'created', 'modified']
readonly_fields = ["show_links", "created", "modified"]
inlines = [EmailTemplateAttachmentInline]

def show_links(self, obj):
if not obj.pk:
return ''
return mark_safe(u'<a href="%s" target="_blank">%s</a>' % (
reverse('email_preview', kwargs={'pk': obj.pk}), _('Show email preview')
))
return ""
return mark_safe(
'<a href="%s" target="_blank">%s</a>'
% (reverse("email_preview", kwargs={"pk": obj.pk}), _("Show email preview"))
)

show_links.allow_tags = True
show_links.short_description = _('Actions')
show_links.short_description = _("Actions")


admin.site.register(EmailTemplate, EmailTemplateAdmin)


class EmailAttachmentAdmin(admin.ModelAdmin):
list_display = ["name"]
search_fields = ["name"]
list_display = ["name", "comment", "ordering"]
search_fields = ["name", "comment"]


admin.site.register(EmailAttachment, EmailAttachmentAdmin)
Expand All @@ -57,8 +71,8 @@ class MassEmailAttachmentInline(admin.TabularInline):


class MassEmailMessageAdmin(admin.ModelAdmin):
list_display = ('subject', 'date_sent')
readonly_fields = ['date_sent']
list_display = ("subject", "date_sent")
readonly_fields = ["date_sent"]
form = MassEmailMessageForm
inlines = [MassEmailAttachmentInline]

Expand Down
6 changes: 3 additions & 3 deletions emailtemplates/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@


class EmailtempatesConfig(AppConfig):
name = 'emailtemplates'
verbose_name = _('E-MAIL TEMPLATES')
default_auto_field = 'django.db.models.BigAutoField'
name = "emailtemplates"
verbose_name = _("E-MAIL TEMPLATES")
default_auto_field = "django.db.models.BigAutoField"
98 changes: 61 additions & 37 deletions emailtemplates/email.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# coding=utf-8
import logging
import os
import re
from smtplib import SMTPException
from urllib.parse import urljoin

from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
Expand All @@ -11,8 +13,6 @@

from .models import now, EmailTemplate
from .registry import email_templates
import re
from urllib.parse import urljoin

logger = logging.getLogger(__name__)

Expand All @@ -28,9 +28,17 @@ class EmailFromTemplate(object):
Site Admins should be familiar with Django Template System.
"""

def __init__(self, name="", from_email=settings.DEFAULT_FROM_EMAIL, base_url="",
language=settings.LANGUAGE_CODE, subject="", template_class=EmailTemplate,
registry_validation=True, template_object=None):
def __init__(
self,
name="",
from_email=settings.DEFAULT_FROM_EMAIL,
base_url="",
language=settings.LANGUAGE_CODE,
subject="",
template_class=EmailTemplate,
registry_validation=True,
template_object=None,
):
"""
Class constructor
Expand All @@ -55,18 +63,18 @@ def __init__(self, name="", from_email=settings.DEFAULT_FROM_EMAIL, base_url="",

self.template = None
self.compiled_template = None # for storing compiled template
self.context = {'date': now()} # default context
self.context = {"date": now()} # default context
self.sent = 0 # number of messages sent
self.message = ""
self.content_subtype = 'html'
self._template_source = 'default'
self.content_subtype = "html"
self._template_source = "default"

@property
def template_source(self):
"""Source of the template. One of the following:
* default
* filesystem
* database
* default
* filesystem
* database
"""
return self._template_source

Expand All @@ -78,9 +86,12 @@ def __get_template_from_file(self):
try:
self.compiled_template = get_template(path)
except (TemplateDoesNotExist, IOError):
logger.warning("Can't find %s template in the filesystem, will use very default one.", path)
logger.warning(
"Can't find %s template in the filesystem, will use very default one.",
path,
)
else:
self._template_source = 'filesystem'
self._template_source = "filesystem"

def build_absolute_uri(self, url: str):
"""
Expand All @@ -106,17 +117,20 @@ def get_object(self):
try:
tmp = self.get_template_object()
except ObjectDoesNotExist:
logger.warning("Can't find EmailTemplate object in database, using default file template.")
logger.warning(
"Can't find EmailTemplate object in database, using default file template."
)
break
except UnicodeError:
logger.warning(
"Can't convert to unicode EmailTemplate object from database, using default file template.")
"Can't convert to unicode EmailTemplate object from database, using default file template."
)
break
else:
self.template = str(tmp.content)
self.subject = self.get_subject(tmp)
self._template_source = 'database'
logger.debug(u"Got template %s from database", self.name)
self._template_source = "database"
logger.debug("Got template %s from database", self.name)
return
# fallback
self.__get_template_from_file()
Expand All @@ -126,9 +140,9 @@ def __compile_template(self):
self.compiled_template = Template(self.template)

def get_context(self):
self.context.update({
"default_attachments": self.get_default_attachments(as_links=True)
})
self.context.update(
{"default_attachments": self.get_default_attachments(as_links=True)}
)
return self.context

def render_message(self):
Expand All @@ -141,26 +155,30 @@ def render_message(self):
self.message = message

def get_message_object(self, send_to, attachment_paths, *args, **kwargs):
if kwargs.get('reply_to') is None:
defaut_reply_to_email = getattr(settings, 'DEFAULT_REPLY_TO_EMAIL', None)
if kwargs.get("reply_to") is None:
defaut_reply_to_email = getattr(settings, "DEFAULT_REPLY_TO_EMAIL", None)
if defaut_reply_to_email:
kwargs['reply_to'] = [defaut_reply_to_email]
kwargs["reply_to"] = [defaut_reply_to_email]

msg = EmailMessage(self.subject, self.message, self.from_email, send_to, *args, **kwargs)
msg = EmailMessage(
self.subject, self.message, self.from_email, send_to, *args, **kwargs
)
if attachment_paths:
for path in attachment_paths:
msg.attach_file(path)
return msg

def send_email(self, send_to, attachment_paths=None, fail_silently=True, *args, **kwargs):
def send_email(
self, send_to, attachment_paths=None, fail_silently=True, *args, **kwargs
):
"""
Sends email to recipient based on self object parameters.
@param fail_silently: When it’s False, msg.send() will raise an smtplib.SMTPException if an error occurs.
@param send_to: recipient email
@param args: additional args passed to EmailMessage
@param kwargs: kwargs passed to EmailMessage
@param attachment_paths: paths to attachments as received by django EmailMessage.attach_file(path) method
@param attachment_paths: paths to attachments as received by django EmailMessage.attach_file(path) method
@return: number of sent messages
"""
msg = self.get_message_object(send_to, attachment_paths, *args, **kwargs)
Expand All @@ -171,7 +189,7 @@ def send_email(self, send_to, attachment_paths=None, fail_silently=True, *args,
except SMTPException as e:
if not fail_silently:
raise
logger.error(u'Problem sending email to %s: %s', send_to, e)
logger.error("Problem sending email to %s: %s", send_to, e)

return self.sent

Expand All @@ -188,29 +206,35 @@ def get_default_attachments(self, as_links=False):
for attachment in tmp.attachments.filter(send_as_link=as_links):
if as_links:
attachments.append(
(attachment.get_name(), self.build_absolute_uri(attachment.attachment_file.url))
(
attachment.get_name(),
self.build_absolute_uri(attachment.attachment_file.url),
)
)
else:
attachments.append(
(os.path.basename(attachment.attachment_file.name), attachment.attachment_file.read())
(
os.path.basename(attachment.attachment_file.name),
attachment.attachment_file.read(),
)
)
return attachments

def send(self, to, attachment_paths=None, *args, **kwargs):
"""This function does all the operations on eft object, that are necessary to send email.
Usually one would use eft object like this:
eft = EmailFromTemplate(name='sth/sth.html')
eft.get_object()
eft.render_message()
eft.send_email(['email@example.com'])
return eft.sent
Usually one would use eft object like this:
eft = EmailFromTemplate(name='sth/sth.html')
eft.get_object()
eft.render_message()
eft.send_email(['email@example.com'])
return eft.sent
"""
attachments = self.get_default_attachments(as_links=False)
attachments.extend(kwargs.pop('attachments', []))
attachments.extend(kwargs.pop("attachments", []))

self.get_object()
self.render_message()
self.send_email(to, attachment_paths, attachments=attachments, *args, **kwargs)
if self.sent:
logger.info(u"Mail has been sent to: %s ", to)
logger.info("Mail has been sent to: %s ", to)
return self.sent
42 changes: 24 additions & 18 deletions emailtemplates/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,49 +16,55 @@


class EmailTemplateAdminForm(forms.ModelForm):
title = forms.ChoiceField(choices=lazy(email_templates.email_template_choices, list), label=_("template"))
title = forms.ChoiceField(
choices=lazy(email_templates.email_template_choices, list), label=_("template")
)

class Meta:
model = EmailTemplate
fields = [
'title',
'subject',
'content',
'language',
'created',
'modified',
"title",
"subject",
"content",
"language",
"created",
"modified",
]

def __init__(self, *args, **kwargs):
super(EmailTemplateAdminForm, self).__init__(*args, **kwargs)
self.fields['title'].help_text = mark_safe(email_templates.get_form_help_text(self.initial.get('title')))
self.fields["title"].help_text = mark_safe(
email_templates.get_form_help_text(self.initial.get("title"))
)
if self.instance.pk:
self.fields['title'].widget = forms.TextInput(attrs={'readonly': 'readonly', 'style': 'width:480px'})
self.fields["title"].widget = forms.TextInput(
attrs={"readonly": "readonly", "style": "width:480px"}
)
else:
self.fields['content'].widget = forms.HiddenInput()
self.fields['content'].required = False
self.fields['subject'].widget = forms.HiddenInput()
self.fields["content"].widget = forms.HiddenInput()
self.fields["content"].required = False
self.fields["subject"].widget = forms.HiddenInput()

def clean_content(self):
content = self.cleaned_data['content']
content = self.cleaned_data["content"]
try:
Template(content)
except TemplateSyntaxError as e:
raise ValidationError(u"Syntax error in custom email template: %s" % e)
raise ValidationError("Syntax error in custom email template: %s" % e)
return content


class MassEmailAttachmentForm(forms.ModelForm):
class Meta:
model = MassEmailAttachment
fields = ['attachment_file']
fields = ["attachment_file"]


class MassEmailMessageForm(forms.ModelForm):
class Meta:
model = MassEmailMessage
fields = [
'subject',
'content',
'date_sent',
"subject",
"content",
"date_sent",
]

0 comments on commit 947c82c

Please sign in to comment.