Skip to content

Commit

Permalink
Implemented email sending preview, tuned admin
Browse files Browse the repository at this point in the history
  • Loading branch information
Bart van der Schoor committed Feb 2, 2024
1 parent 2a9e6d9 commit 80fb746
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 5 deletions.
10 changes: 6 additions & 4 deletions mail_editor/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from .forms import MailTemplateForm
from .models import MailTemplate
from .settings import settings
from .views import TemplateBrowserPreviewView, TemplateVariableView
from .views import TemplateBrowserPreviewView, TemplateEmailPreviewFormView, TemplateVariableView


@admin.register(MailTemplate)
Expand All @@ -30,7 +30,6 @@ class MailTemplateAdmin(admin.ModelAdmin):
readonly_fields = (
'get_variable_help_text',
'get_preview_link',
'get_preview_url',
)
search_fields = (
'internal_name',
Expand Down Expand Up @@ -66,15 +65,15 @@ def get_preview_link(self, obj=None):
url = self.get_preview_url(obj)
if url:
return format_html('<a href="{}">{}</a>', url, _("Open"))
else:
return _("Save to enable preview")

get_preview_link.short_description = _("Preview")

def get_preview_url(self, obj=None):
if obj:
return reverse("admin:mailtemplate_preview", kwargs={"pk": obj.id})

get_preview_url.short_description = _("Preview URL")

def get_urls(self):
# reminder: when using admin templates also add self.admin_site.each_context(request)
return [
Expand All @@ -83,6 +82,9 @@ def get_urls(self):
name='mailtemplate_variables'),
url(r'^preview/(?P<pk>[0-9]+)/$',
self.admin_site.admin_view(TemplateBrowserPreviewView.as_view()),
name='mailtemplate_render'),
url(r'^email/(?P<pk>[0-9]+)/$',
self.admin_site.admin_view(TemplateEmailPreviewFormView.as_view()),
name='mailtemplate_preview'),
] + super().get_urls()

Expand Down
93 changes: 93 additions & 0 deletions mail_editor/templates/admin/mail_editor/preview_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static admin_modify %}

{# NOTE: this started as a copy of admin/change_form.html but is stripped and modified. #}

{% block extrahead %}{{ block.super }}
<script src="{% url 'admin:jsi18n' %}"></script>
{{ media }}
{% endblock %}

{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}">{% endblock %}

{% block coltype %}colM{% endblock %}

{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-form{% endblock %}

{% if not is_popup %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'change' original.id %}">{{ original|truncatewords:"18" }}</a>
&rsaquo; {% translate "Preview" %}
</div>
{% endblock %}
{% endif %}

{% block content %}<div id="content-main">
{% block object-tools %}
{% endblock %}
<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}{% if form_url %}action="{{ form_url }}" {% endif %}method="post" id="{{ opts.model_name }}_form" novalidate>{% csrf_token %}{% block form_top %}{% endblock %}
<div>
{% if errors %}
<p class="errornote">
{% if errors|length == 1 %}{% translate "Please correct the error below." %}{% else %}{% translate "Please correct the errors below." %}{% endif %}
</p>
{{ form.non_field_errors }}
{% endif %}

{% block field_sets %}
<div>
{{ form.non_field_errors }}
{{ form.recipient.errors }}
<div>
<label for="{{ form.recipient.id_for_label }}">{{ form.recipient.label }}:</label>
&nbsp;
{{ form.recipient }}
<input type="submit" value="{% translate "Send preview to email" %}">
&nbsp;
<a href="{{ render_url }}">{% translate "Open preview in full window" %}</a>
</div>
<br>
</div>
{% endblock %}

</div>
</form>

{% block inline_preview %}
<div>
<style>
#id_recipient {
width: 300px;
}
.mailtemplate__preview {
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
.mailtemplate__preview__subject {
box-sizing: border-box;
border: 2px solid #ddd;
font-size: 120%;
width: 100%;
margin-bottom: 0.5em;
padding: 0.5em;
}
.mailtemplate__preview__frame {
box-sizing: border-box;
border: 2px solid #ddd;
width: 100%;
height: 800px;
}
</style>
<div class="mailtemplate__preview">
<div class="mailtemplate__preview__subject">{{ subject }}</div>
<iframe class="mailtemplate__preview__frame" src="{{ render_url }}"></iframe>
</div>
</div>
{% endblock %}

</div>
{% endblock %}
65 changes: 64 additions & 1 deletion mail_editor/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
from django import forms
from django.contrib import admin
from django.contrib import messages
from django.http import HttpResponse
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from django.views.generic import View
from django.views.generic.detail import SingleObjectMixin
from django.views.generic.detail import DetailView, SingleObjectMixin
from django.views.generic.edit import FormMixin, FormView

from .models import MailTemplate
from .utils import variable_help_text
Expand All @@ -21,3 +27,60 @@ def get(self, request, *args, **kwargs):

subject, body = template.render(body_ctx, subject_ctx)
return HttpResponse(body, content_type="text/html")


class TemplateEmailPreviewForm(forms.Form):
recipient = forms.EmailField(label=_("Recipient"), required=True)


class TemplateEmailPreviewFormView(FormView, DetailView):
model = MailTemplate
form_class = TemplateEmailPreviewForm
context_object_name = "template"
template_name = "admin/mail_editor/preview_form.html"

def form_valid(self, form):
recipient = form.cleaned_data["recipient"]
subject_ctx, body_ctx = self.object.get_preview_contexts()

result = self.object.send_email([recipient], body_ctx, subj_context=subject_ctx)

if result:
messages.success(self.request, _("Email sent to {email}").format(email=recipient))
else:
messages.warning(self.request, _("Email not sent to {email}").format(email=recipient))

return super().form_valid(form)

def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
# context to make the (remaining parts of the) admin template work
ctx.update(admin.site.each_context(self.request))
ctx.update({
"opts": self.model._meta,
"original": self.object,
"title": "{} {}".format(self.model._meta.verbose_name, _("preview")),
})

subject_ctx, body_ctx = self.object.get_preview_contexts()
subject, body = self.object.render(body_ctx, subject_ctx)

# our own context data
ctx.update({
"subject": subject,
"render_url": reverse("admin:mailtemplate_render", kwargs={"pk": self.object.id}),
})
return ctx

def get_success_url(self):
return reverse("admin:mailtemplate_preview", kwargs={"pk": self.object.id})

def get(self, request, *args, **kwargs):
# mixing FormView and DetailView
self.object = self.get_object()
return super().get(request, *args, **kwargs)

def post(self, request, *args, **kwargs):
# mixing FormView and DetailView
self.object = self.get_object()
return super().post(request, *args, **kwargs)
22 changes: 22 additions & 0 deletions tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.contrib.auth.models import User
from django.core import mail
from django.test import TestCase, override_settings
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
Expand Down Expand Up @@ -72,3 +73,24 @@ def test_preview_view(self):
self.client.force_login(self.super_user)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)

self.assertContains(response, _("Important message for --id--"))

# test sending the email
self.client.post(url, {"recipient": "test@example.com"})

self.assertEqual(len(mail.outbox), 1)
message = mail.outbox[0]
self.assertEqual(message.subject, _("Important message for --id--"))
self.assertIn(str(_("Test mail sent from testcase with --id--")), message.body)

def test_render_view(self):
template = find_template("test_template")

url = reverse('admin:mailtemplate_render', args=[template.id])

self.client.force_login(self.super_user)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)

self.assertContains(response, _("Test mail sent from testcase with --id--"))

0 comments on commit 80fb746

Please sign in to comment.