Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 protected]'])
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 protected]'])
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",
]
Loading