Templated emails

The mails app is used as the (templated) mailing processor of SciPost. Each email is defined using two files: the template and the configuration file.

Each mail is defined using certain general configuration possibilities. These options are defined in the json configuration file or are overwritten in the methods described below. These fields are:

subject {string}

The subject of the mail.

recipient_list and bcc {list}

Both fields are lists of strings. Each string may be either a plain mail address, eg. ` example@scipost.org`, or it may represent a certain relation to the central object. For example, one may define:

>>> sub_1 = Submission.objects.first()
>>> mail_util = DirectMailUtil([...], object=sub_1, recipient_list=['example@scipost.org', 'submitted_by.user.email'])
from_email {string}

For this field, the same flexibility and functionality exists as for the recipient_list and bcc fields. However, this field should always be a single string entry:

>>> mail_util = DirectMailUtil([...], from_email='noreply@scipost.org')
from_name {string}

The representation of the mail sender.

Central object

Using a single Model instance

The “central object” is a django.db.models.__Model__ instance that will be used for the email fields if needed and in the template. The mail engine will try to automatically detect a possible Model instance and save this in the template context as <Model.verbose_name> and object. The keyword you use to send it to the mail engine is not relevant for this method, but will be copied to be used in the template as well.

Example

To make a Submission available to an email template:

>>> sub_1 = Submission.object.first()
>>> mail_util = DirectMailUtil([...], weird_keyword=sub_1)

In the template, the variables weird_keyword, submission and object will all represent the sub_1 instance. For example:

<h1>Dear {{ weird_keyword.submitted_by.get_title_display }} {{ object.submitted_by.user.last_name }},</h1>
<p>Thank you for your submission: {{ submission.title }}.</p>

Using multiple Model instances

If a certain mail requires more than one Model instance, it is required to pass either a instance or object parameter for the mail engine to determine the central object.

Example:

>>> sub_1 = Submission.object.first()
>>> report_1 = Report.object.first()
>>> mail_util = DirectMailUtil([...], submission=sub_1, report=report_1)
ValueError: "Multiple db instances are given."

Here, it is required to pass either the instance or object parameter, eg.:

>>> mail_util = DirectMailUtil([...], object=sub_1, report=report_1)

Configuration file

Each mail is configured with a json file, templates/email/*__<mail_code>.json, which at least contains a subject and recipient_list value. The other fields are optional. An example of all available configuration fields are shown:

{
    "subject": "Foo subject",
    "recipient_list": [
        "noreply@scipost.org"
    ],
    "bcc": [
    "secret@scipost.org"
    ],
    "from_email": "server@scipost.org",
    "from_name": "SciPost Techsupport"
}

Template file

Any mail will be defined in the html template file templates/email/__<mail_code>.html using the conventions as per Django’s default template processor.

Direct mail utility

The fastest, easiest way to use templated emails is using the DirectMailUtil class:

mails.utils.__DirectMailUtil(__*mail_code, delayed_processing=True, subject='', recipient_list=[], bcc=[], from_email='', from_name='', \**template_variables*__)

Attributes

mail_code {string}

The unique code refereeing to a template and configuration file.

delayed_processing {boolean, optional}

Execute template rendering in a cronjob to reduce executing time.

subject {string, optional}

Overwrite the subject field defined in the configuration field.

recipient_list {list, optional}

Overwrite the recipient_list field defined in the configuration field.

bcc {list, optional}

Overwrite the bcc field defined in the configuration field.

from_email {string, optional}

Overwrite the from_email field defined in the configuration field.

from_name {string, optional}

Overwrite the from_name field defined in the configuration field.

**template_variables

Append any keyword argument that may be used in the email template.

Methods

send_mail()

Send the mail as defined on initialization.

Basic example

Directly sending an email:

>>> from mails.utils import DirectMailUtil
>>> mail_util = DirectMailUtil('test_mail_code_1')
>>> mail_util.send_mail()

This utility is protected to prevent double sending. So now, the following has no effect anymore:

>>> mail_util.send_mail()

Class-based view editor

This acts like a regular Django class-based view, but will intercept the post request to load the email form and submit when positively validated.

This view may be used as a generic editing view or DetailView.

mails.views.MailView

This view is a basic class-based view, which may be used as basic editor for a specific templated email.

Attributes

mail_code {string}

The unique code refereeing to a template and configuration file.

mail_config {dict, optional}

Overwrite any of the configuration fields of the configuration file:

  • subject {string}

  • recipient_list {list}

  • bcc {list}

  • from_email {string}

  • from_name {string}

mail_variables {dict, optional}

Append extra variables to the mail template.

fail_silently {boolean, optional}

If set to False, raise PermissionDenied if can_send_mail() returns False on POST request.

Methods

can_send_mail()

Control permission to actually send the mail. Return a boolean, returns True by default.

get_mail_config()

Return an optional explicit mail configuration. Return a dictionary, returns mail_config by default.

mails.views.MailFormView

This view may be used as a generic editing view, and will intercept the POST request to let the user edit the email before saving the original form and sending the templated mail.

Attributes

form_class {django.forms.__ModelForm__ | django.forms.__Form__}

The original form to use as in any regular Django editing view.

mail_code {string}

The unique code refereeing to a template and configuration file.

mail_config {dict, optional}
Overwrite any of the configuration fields of the configuration file:
  • subject {string}

  • recipient_list {list}

  • bcc {list}

  • from_email {string}

  • from_name {string}

mail_variables {dict, optional}

Append extra variables to the mail template.

fail_silently {boolean, optional}

If set to False, raise PermissionDenied if can_send_mail() returns False on POST request.

Methods

can_send_mail()

Control permission to actually send the mail. Return a boolean, returns True by default.

get_mail_config()

Return an optional explicit mail configuration. Return a dictionary, returns mail_config by default.

Basic example

Views file:

# <app>/views.py
from mails.views import MailView

class FooView(MailView):
    mail_code = 'test_mail_code_1'

Urls file:

# <app>/urls.py
from django.conf.urls import url

from .views import FooView

urlpatterns = [
             url(r'^$', FooView.as_view(), name='foo'),
]

Function-based view editor

Similar to the MailView it is possible to have the user edit a templated email before sending in function-based views, using the MailEditorSubview.

mails.views.MailEditorSubview

Attributes

request {django.http.__HttpResponse__}

The HttpResponse which is typically the first parameter in a function-based view.

mail_code {string}

The unique code refereeing to a template and configuration file.

header_template {string, optional}

Any template that may be used in the header of the edit form.

context {dict, optional}

A context dictionary as in any usual Django view, which may be useful combined with header_template.

subject {string, optional}

Overwrite the subject field defined in the configuration field.

recipient_list {list, optional}

Overwrite the recipient_list field defined in the configuration field.

bcc {list, optional}

Overwrite the bcc field defined in the configuration field.

from_email {string, optional}

Overwrite the from_email field defined in the configuration field.

from_name {string, optional}

Overwrite the from_name field defined in the configuration field.

**template_variables

Append any keyword argument that may be used in the email template.

Methods

is_valid()

See if data is returned and valid, similar to Django forms. Returns a __boolean__.

interrupt()

Interrupt request by rendering the templated email form. Returns a HttpResponse.

send_mail()

Send email as edited by the user in the template.

Basic example:

from submissions.models import Submission
from mails.views import MailEditorSubview

def any_method_based_view(request):
    submission = Submission.objects.first()
    mail_request = MailEditorSubview(request, 'test_mail_code_1', object=submission)
    if mail_request.is_valid():
        mail_request.send_mail()
        return redirect('reverse:url')
    else:
        return mail_request.interrupt()

Important epilogue

Every templated mail defined in the templates/email/ folder will be tested for proper configuration. This tests includes tests on the configuration file and existence of the template. Important note: it does not test the content of the templates (read: the variables used in the template). To run these, and all other mail-related unit tests, simple run the following:

(scipostenv) $ ./manage.py test mails.tests -k

A successful test ends by printing “OK”. In any other case, errors will be raised.