Skip to content

Commit

Permalink
Add dashboard with emails preview
Browse files Browse the repository at this point in the history
  • Loading branch information
amureki committed Aug 14, 2024
1 parent e755d1f commit 4132446
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 1 deletion.
1 change: 1 addition & 0 deletions emark/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def get_settings():
{
"UTM_PARAMS": {"utm_source": "website", "utm_medium": "email"},
"DOMAIN": None,
"DASHBOARD_HIDDEN_CLASSES": [],
**getattr(settings, "EMARK", {}),
},
)
18 changes: 18 additions & 0 deletions emark/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,21 @@ def render(self, tracking_uuid=None):
)
self.body = self.get_body(self.html)
self.attach_alternative(self.html, "text/html")

@classmethod
def render_preview(cls):
"""Return a preview of the email."""

Check failure on line 259 in emark/message.py

View workflow job for this annotation

GitHub Actions / lint (ruff check --output-format=github .)

Ruff (D202)

emark/message.py:259:9: D202 No blank lines allowed after function docstring (found 1)

markdown_string = loader.get_template(cls.template).template.source
context = {}
html_message = markdown.markdown(
markdown_string,
extensions=[
"markdown.extensions.meta",
"markdown.extensions.tables",
"markdown.extensions.extra",
],
)
context["markdown_string"] = mark_safe(html_message)

Check failure on line 271 in emark/message.py

View workflow job for this annotation

GitHub Actions / lint (ruff check --output-format=github .)

Ruff (S308)

emark/message.py:271:38: S308 Use of `mark_safe` may expose cross-site scripting vulnerabilities
template = loader.get_template(cls.base_html_template)
return template.render(context)
17 changes: 17 additions & 0 deletions emark/templates/emark/dashboard.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<h2>
Dashboard
</h2>
{% regroup emails by app_label as emails_by_app_label %}
{% for app_label in emails_by_app_label %}
<h2>
{{ app_label.grouper }}
</h2>
<ul>
{% for email in app_label.list %}
<li>
<a href="{{ email.detail_url }}">{{ email.class_name }}</a>
{% if email.doc %}-- {{ email.doc }}{% endif %}
</li>
{% endfor %}
</ul>
{% endfor %}
10 changes: 10 additions & 0 deletions emark/templates/emark/preview.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<div style="position: sticky;
padding: 0.1rem;
background: white;
border: 1px solid black;
font-size: small;
opacity: 0.8">
&nbsp; &nbsp;<a href="{% url 'emark:email-dashboard' %}">Dashboard</a> > {{ email_name }} {% if email_doc %}-- {{ email_doc }}{% endif %}
</div>

{{ email_preview|safe }}
2 changes: 2 additions & 0 deletions emark/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@
path("<uuid:pk>/", views.EmailDetailView.as_view(), name="email-detail"),
path("<uuid:pk>/click", views.EmailClickView.as_view(), name="email-click"),
path("<uuid:pk>/open", views.EmailOpenView.as_view(), name="email-open"),
path("dashboard/", views.DashboardView.as_view(), name="email-dashboard"),
path("dashboard/<str:email_class>/", views.EmailPreviewView.as_view(), name="email-preview"),
]
7 changes: 7 additions & 0 deletions emark/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,10 @@ def __str__(self) -> str:
# sanitize all wide vertical or horizontal spaces
text = self.DOUBLE_NEWLINE.sub("\n\n", text.strip())
return self.DOUBLE_SPACE.sub(" ", text)


def get_subclasses(cls):
"""Return all subclasses of a class, without the base classes."""
return set(cls.__subclasses__()).union(
[s for c in cls.__subclasses__() for s in get_subclasses(c)]
)
63 changes: 62 additions & 1 deletion emark/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

from django import http
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.http import Http404
from django.http.request import split_domain_port, validate_host
from django.urls import reverse
from django.views import View
from django.views.generic import TemplateView
from django.views.generic.detail import SingleObjectMixin

from . import models
from . import conf, message, models, utils

# white 1x1 pixel JPEG in bytes:
#
Expand Down Expand Up @@ -93,3 +97,60 @@ def get(self, request, *args, **kwargs):
"Cache-Control": "no-cache, no-store, must-revalidate",
},
)


class DashboardView(LoginRequiredMixin, UserPassesTestMixin, TemplateView):
"""Show a dashboard of available email classes."""

template_name = "emark/dashboard.html"

def test_func(self):
return self.request.user.is_staff

def get_emails(self):
hidden_classes = [message.MarkdownEmail.__name__, *conf.get_settings().DASHBOARD_HIDDEN_CLASSES]
emails = [
{
"app_label": email_class.__module__.split(".")[0],
"class_name": email_class.__name__,
"doc": email_class.__doc__ or "",
"detail_url": reverse("emark:email-preview", args=[email_class.__name__]),
}
for email_class in utils.get_subclasses(message.MarkdownEmail)
if email_class.__name__ not in hidden_classes
]
return sorted(emails, key=lambda email: (email["app_label"], email["class_name"]))

def get_context_data(self, **kwargs):
return super().get_context_data(**kwargs) | {
"emails": self.get_emails(),
}


class EmailPreviewView(LoginRequiredMixin, UserPassesTestMixin, TemplateView):
"""Render a preview of the email."""

template_name = "emark/preview.html"

def test_func(self):
return self.request.user.is_staff

def dispatch(self, request, *args, **kwargs):
self.email_class = self.get_email_class(kwargs["email_class"])
if not self.email_class:
raise Http404()
return super().dispatch(request, *args, **kwargs)

def get_email_class(self, email_class):
for cl in utils.get_subclasses(message.MarkdownEmail):
if cl.__name__ == email_class:
return cl
return None

def get_context_data(self, **kwargs):
email = self.email_class
return super().get_context_data(**kwargs) | {
"email_preview": email.render_preview(),
"email_name": email.__name__,
"email_doc": email.__doc__
}

0 comments on commit 4132446

Please sign in to comment.