Skip to content

Commit

Permalink
Merge branch 'feature/mailpreview' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
pi-sigma authored Feb 14, 2024
2 parents 016ccba + 34c80a7 commit 6629324
Show file tree
Hide file tree
Showing 18 changed files with 573 additions and 135 deletions.
34 changes: 7 additions & 27 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,9 @@ and edit the actual templates in the front-end. Templates are validated on
syntax *and* required/optional content.

This project also aims to solve the HTML email template problems that you can have when
supporting all email clients. For this we will inject the css as inline styles.
We do this using a node project calles inline-css. This was also used by
supporting all email clients. This was also used by
`foundation for email`_. Foundation for email is a good way to build your initial email
template on development mode. It will generate a separete html file and css file.
template on development mode. It will generate a separate html file and css file.

For e-mail sending and logging, we recommend using a solution such as `Django Yubin`_.

Expand All @@ -51,17 +50,15 @@ Warning

This project is currently in development and not stable.

Used NPM packages
-----------------

This package uses NPM. This is to inject the inline styles and minify the HTML.
This packages are needed to make the email complient for all the email clients.
Installation
------------

.. code:: shell
Install with pip:

npm install --save inline-css
npm install --save html-minifier
.. code:: shell
pip install mail_editor
Add *'mail_editor'* to the installed apps:

Expand Down Expand Up @@ -159,11 +156,6 @@ These settings are usefull to add:

.. code:: python
# These settings are for inlining the css.
MAIL_EDITOR_PACKAGE_JSON_DIR = '/path/to/the/package.json'
MAIL_EDITOR_ADD_BIN_PATH = True or False
MAIL_EDITOR_BIN_PATH = 'path/to/virtualenv/bin'
# These settings make sure that CKEDITOR does not strip any html tags. like <center></center>
CKEDITOR_CONFIGS = {
'mail_editor': {
Expand Down Expand Up @@ -197,18 +189,6 @@ Install with pip:
pip install mail_editor
Add *'mail_editor'* to the installed apps:

.. code:: python
# settings.py
INSTALLED_APPS = [
...
'mail_editor',
...
]
.. _Django Yubin: https://github.com/APSL/django-yubin
.. _Sergei Maertens: https://github.com/sergei-maertens
.. _langerak-gkv: https://github.com/sergei-maertens/langerak-gkv/blob/master/src/langerak_gkv/mailing/mail_template.py
Expand Down
23 changes: 0 additions & 23 deletions bin/inject-inline-styles.js

This file was deleted.

13 changes: 12 additions & 1 deletion mail_editor/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.conf import settings as django_settings
from django.urls import re_path
from django.contrib import admin
from django.contrib import admin, messages
from django.urls import path, reverse
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
Expand Down Expand Up @@ -36,6 +36,9 @@ class MailTemplateAdmin(admin.ModelAdmin):
'template_type',
'subject',
)
actions = [
"reload_templates",
]

form = MailTemplateForm

Expand Down Expand Up @@ -113,3 +116,11 @@ def get_variable_help_text(self, obj):
return obj.get_variable_help_text()

get_variable_help_text.short_description = _('Subject variables')

def reload_templates(self, request, queryset):
for template in queryset:
template.reload_template()
template.save()
self.message_user(request, _("Template '{name}' is reset").format(name=template.template_type), level=messages.SUCCESS)

reload_templates.short_description = _('Reset templates (WARNING: overwrites current content)')
47 changes: 22 additions & 25 deletions mail_editor/models.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import copy
import logging
import os
import subprocess
from tempfile import NamedTemporaryFile
from email.mime.image import MIMEImage

from django.conf import settings as django_settings
from django.core.exceptions import ValidationError
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import EmailMultiAlternatives
from django.db import models
from django.db.models import Q
from django.template import Context, Template, loader
from django.template import Context, Template
from django.utils.html import strip_tags
from django.utils.module_loading import import_string
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _

from .process import process_html
from .settings import get_config, settings
from .mail_template import validate_template
from .node import locate_package_json
from .utils import variable_help_text

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -90,6 +89,12 @@ def clean(self):
_('Mail template with this type and language already exists')
)

def reload_template(self):
from .helpers import get_base_template_path, get_body, get_subject
self.subject = get_subject(self.template_type)
self.body = get_body(self.template_type)
self.base_template_path = get_base_template_path(self.template_type)

def get_base_context(self):
base_context = getattr(django_settings, 'MAIL_EDITOR_BASE_CONTEXT', {}).copy()
dynamic = settings.DYNAMIC_CONTEXT
Expand Down Expand Up @@ -144,26 +149,6 @@ def render(self, context, subj_context=None):
body_context.update({'content': partial_body})
body = template_function(self.base_template_path, body_context)

# TODO: I made the package.json stuff optional. Maybe it should be removed completely since it adds 3 settings,
# seems for a limited use-case, and it uses subproces...
package_json = locate_package_json()
if package_json:
env = os.environ.copy()
if settings.ADD_BIN_PATH:
env['PATH'] = '{}:{}'.format(env['PATH'], settings.BIN_PATH)

with NamedTemporaryFile() as temp_file:
temp_file.write(bytes(body, 'UTF-8'))
process = subprocess.Popen(
"inject-inline-styles.js {}".format(temp_file.name), shell=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
env=env, universal_newlines=True, cwd=os.path.dirname(package_json)
)

body, err = process.communicate()
if err:
raise Exception(err)

return tpl_subject.render(subj_ctx), mark_safe(body)

def build_message(self, to_addresses, context, subj_context=None, txt=False, attachments=None,
Expand All @@ -177,12 +162,16 @@ def build_message(self, to_addresses, context, subj_context=None, txt=False, att
`(<filename>, <content>, [mime type])`
"""
subject, body = self.render(context, subj_context)

body, cid_attachments = process_html(body, settings.BASE_HOST)

text_body = txt or strip_tags(body)

email_message = EmailMultiAlternatives(
subject=subject, body=text_body, from_email=django_settings.DEFAULT_FROM_EMAIL,
to=to_addresses, cc=cc_addresses, bcc=bcc_addresses)
email_message.attach_alternative(body, 'text/html')
email_message.attach_alternative(body, "text/html")
email_message.mixed_subtype = "related"

if attachments:
for attachment in attachments:
Expand All @@ -193,6 +182,14 @@ def build_message(self, to_addresses, context, subj_context=None, txt=False, att
else:
email_message.attach(*attachment)

if cid_attachments:
for cid, content, subtype in cid_attachments:
subtype = subtype.split("/", maxsplit=1)
assert subtype[0] == "image"
mime_image = MIMEImage(content, _subtype=subtype[1])
mime_image.add_header('Content-ID', f"<{cid}>")
email_message.attach(mime_image)

return email_message

def send_email(self, to_addresses, context, subj_context=None, txt=False, attachments=None,
Expand Down
23 changes: 0 additions & 23 deletions mail_editor/node.py

This file was deleted.

Loading

0 comments on commit 6629324

Please sign in to comment.