From 8496ea70729b1442cbaff420a8c11094d68dedf2 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 10 Apr 2018 17:53:15 -0400 Subject: [PATCH] Add support for HTML bodies to the user mailers. Closes GitLab issue #470. Signed-off-by: Roberto Rosario --- HISTORY.rst | 1 + docs/releases/3.0.rst | 3 +- mayan/apps/mailer/models.py | 61 +++++++++++++++++++++----- mayan/apps/mailer/settings.py | 4 +- mayan/apps/mailer/tasks.py | 8 ++-- mayan/apps/mailer/tests/literals.py | 1 + mayan/apps/mailer/tests/test_models.py | 12 ++++- mayan/apps/mailer/tests/test_views.py | 1 - mayan/apps/mailer/views.py | 30 +++---------- 9 files changed, 78 insertions(+), 43 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index eaa3e1838f..a21f143bc1 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -150,6 +150,7 @@ - Add locking for interval sources. This reduces the chance of repeated documents from long running email downloads. - Add the option to enable or disable parsing when uploading a document for each document type. - Add a new setting option to enable automatic parsing for each new document type created. +- Add support for HTML bodies to the user mailers. 2.7.3 (2017-09-11) ================== diff --git a/docs/releases/3.0.rst b/docs/releases/3.0.rst index e34934fbae..3583b4c4ec 100644 --- a/docs/releases/3.0.rst +++ b/docs/releases/3.0.rst @@ -469,7 +469,7 @@ Other changes worth mentioning - Add locking for interval sources. This reduces the chance of repeated documents from long running email downloads. - Add the option to enable or disable parsing when uploading a document for each document type. - Add a new setting option to enable automatic parsing for each new document type created. - +- Add support for HTML bodies to the user mailers. Removals -------- @@ -541,6 +541,7 @@ Bugs fixed or issues closed * `GitLab issue #454 `_ Invalid next month calculation in statistics app, causes failstop * `GitLab issue #467 `_ mail attachments without content-disposition are lost * `GitLab issue #468 `_ plain text e-mails without charset do not work +* `GitLab issue #470 `_ Enable Django variable for HTML encoded emails * `GitHub issue #264 `_ migrate fails on document_states 0004_workflow_internal_name * `GitHub issue #269 `_ Lack of authentication for document previews diff --git a/mayan/apps/mailer/models.py b/mayan/apps/mailer/models.py index d2d8aca45b..c186bade64 100644 --- a/mayan/apps/mailer/models.py +++ b/mayan/apps/mailer/models.py @@ -3,8 +3,11 @@ from __future__ import unicode_literals import json import logging +from django.contrib.sites.models import Site from django.core import mail from django.db import models +from django.template import Context, Template +from django.utils.html import strip_tags from django.utils.module_loading import import_string from django.utils.translation import ugettext_lazy as _ @@ -82,21 +85,28 @@ class UserMailer(models.Model): return super(UserMailer, self).save(*args, **kwargs) - def send(self, subject='', body='', to=None, document=None, as_attachment=False): + def send(self, to, subject='', body='', attachments=None): + """ + Send a simple email. There is no document or template knowledge. + attachments is a list of dictionaries with the keys: + filename, content, and mimetype. + """ recipient_list = split_recipient_list(recipients=[to]) with self.get_connection() as connection: email_message = mail.EmailMultiAlternatives( - subject=subject, body=body, to=recipient_list, - connection=connection + body=strip_tags(body), connection=connection, subject=subject, + to=recipient_list, ) - if as_attachment: - with document.open() as descriptor: - email_message.attach( - filename=document.label, content=descriptor.read(), - mimetype=document.file_mimetype - ) + for attachment in attachments or []: + email_message.attach( + filename=attachment['filename'], + content=attachment['content'], + mimetype=attachment['mimetype'] + ) + + email_message.attach_alternative(body, 'text/html') try: email_message.send() @@ -105,8 +115,39 @@ class UserMailer(models.Model): else: self.error_log.all().delete() + def send_document(self, document, to, subject='', body='', as_attachment=False): + context_dictionary = { + 'link': 'http://%s%s' % ( + Site.objects.get_current().domain, + document.get_absolute_url() + ), + 'document': document + } + + context = Context(context_dictionary) + + body_template = Template(body) + body_html_content = body_template.render(context) + + subject_template = Template(subject) + subject_text = strip_tags(subject_template.render(context)) + + attachments = [] + if as_attachment: + with document.open() as file_object: + attachments.append( + { + 'filename': document.label, 'content': file_object.read(), + 'mimetype': document.file_mimetype + } + ) + + return self.send( + subject=subject_text, body=body_html_content, to=to, attachments=attachments + ) + def test(self, to): - self.send(to=to, subject=_('Test email from Mayan EDMS')) + self.send(subject=_('Test email from Mayan EDMS'), to=to) class UserMailerLogEntry(models.Model): diff --git a/mayan/apps/mailer/settings.py b/mayan/apps/mailer/settings.py index 3d30d33293..da1ea8c527 100644 --- a/mayan/apps/mailer/settings.py +++ b/mayan/apps/mailer/settings.py @@ -17,7 +17,7 @@ setting_link_subject_template = namespace.add_setting( ) setting_link_body_template = namespace.add_setting( default=DEFAULT_LINK_BODY_TEMPLATE, - help_text=_('Template for the document link email form body line.'), + help_text=_('Template for the document link email form body text. Can include HTML.'), global_name='MAILER_LINK_BODY_TEMPLATE', ) setting_document_subject_template = namespace.add_setting( @@ -27,6 +27,6 @@ setting_document_subject_template = namespace.add_setting( ) setting_document_body_template = namespace.add_setting( default=DEFAULT_DOCUMENT_BODY_TEMPLATE, - help_text=_('Template for the document email form body line.'), + help_text=_('Template for the document email form body text. Can include HTML.'), global_name='MAILER_DOCUMENT_BODY_TEMPLATE', ) diff --git a/mayan/apps/mailer/tasks.py b/mayan/apps/mailer/tasks.py index 733e7edca1..58807f5305 100644 --- a/mayan/apps/mailer/tasks.py +++ b/mayan/apps/mailer/tasks.py @@ -6,7 +6,7 @@ from mayan.celery import app @app.task(ignore_result=True) -def task_send_document(subject_text, body_text_content, sender, recipient, user_mailer_id, as_attachment=False, document_id=None): +def task_send_document(body, sender, subject, recipient, user_mailer_id, as_attachment=False, document_id=None): Document = apps.get_model( app_label='documents', model_name='Document' ) @@ -21,7 +21,7 @@ def task_send_document(subject_text, body_text_content, sender, recipient, user_ user_mailer = UserMailer.objects.get(pk=user_mailer_id) - user_mailer.send( - subject=subject_text, body=body_text_content, to=recipient, - document=document, as_attachment=as_attachment + user_mailer.send_document( + as_attachment=as_attachment, body=body, document=document, + subject=subject, to=recipient ) diff --git a/mayan/apps/mailer/tests/literals.py b/mayan/apps/mailer/tests/literals.py index e30ae77e3d..4801e5365c 100644 --- a/mayan/apps/mailer/tests/literals.py +++ b/mayan/apps/mailer/tests/literals.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals +TEST_BODY_HTML = 'test body' TEST_EMAIL_ADDRESS = 'test@example.com' TEST_RECIPIENTS_MULTIPLE_COMMA = 'test@example.com,test2@example.com' TEST_RECIPIENTS_MULTIPLE_SEMICOLON = 'test@example.com;test2@example.com' diff --git a/mayan/apps/mailer/tests/test_models.py b/mayan/apps/mailer/tests/test_models.py index a8eac0089e..56b952a937 100644 --- a/mayan/apps/mailer/tests/test_models.py +++ b/mayan/apps/mailer/tests/test_models.py @@ -7,7 +7,7 @@ from documents.tests.test_models import GenericDocumentTestCase from ..models import UserMailer from .literals import ( - TEST_EMAIL_ADDRESS, TEST_RECIPIENTS_MULTIPLE_COMMA, + TEST_BODY_HTML, TEST_EMAIL_ADDRESS, TEST_RECIPIENTS_MULTIPLE_COMMA, TEST_RECIPIENTS_MULTIPLE_SEMICOLON, TEST_RECIPIENTS_MULTIPLE_MIXED, TEST_RECIPIENTS_MULTIPLE_MIXED_LIST, TEST_USER_MAILER_LABEL, TEST_USER_MAILER_BACKEND_PATH @@ -31,9 +31,17 @@ class ModelTestCase(GenericDocumentTestCase): self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].to, [TEST_EMAIL_ADDRESS]) + def test_send_simple_with_html(self): + self._create_user_mailer() + self.user_mailer.send(to=TEST_EMAIL_ADDRESS, body=TEST_BODY_HTML) + + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].to, [TEST_EMAIL_ADDRESS]) + self.assertEqual(mail.outbox[0].alternatives[0][0], TEST_BODY_HTML) + def test_send_attachment(self): self._create_user_mailer() - self.user_mailer.send( + self.user_mailer.send_document( to=TEST_EMAIL_ADDRESS, document=self.document, as_attachment=True ) diff --git a/mayan/apps/mailer/tests/test_views.py b/mayan/apps/mailer/tests/test_views.py index f2628928f3..870a22faa5 100644 --- a/mayan/apps/mailer/tests/test_views.py +++ b/mayan/apps/mailer/tests/test_views.py @@ -19,7 +19,6 @@ from .mailers import TestBackend class MailerTestMixin(object): def _create_user_mailer(self): - self.user_mailer = UserMailer.objects.create( default=True, enabled=True, diff --git a/mayan/apps/mailer/views.py b/mayan/apps/mailer/views.py index 6a39956a41..c7e8c2baca 100644 --- a/mayan/apps/mailer/views.py +++ b/mayan/apps/mailer/views.py @@ -1,11 +1,8 @@ from __future__ import absolute_import, unicode_literals -from django.contrib.sites.models import Site from django.http import Http404, HttpResponseRedirect from django.shortcuts import get_object_or_404 -from django.template import Context, Template from django.urls import reverse, reverse_lazy -from django.utils.html import strip_tags from django.utils.translation import ungettext, ugettext_lazy as _ from acls.models import AccessControlList @@ -84,33 +81,20 @@ class MailDocumentView(MultipleObjectFormActionView): } def object_action(self, form, instance): - context = Context({ - 'link': 'http://%s%s' % ( - Site.objects.get_current().domain, - instance.get_absolute_url() - ), - 'document': instance - }) - body_template = Template(form.cleaned_data['body']) - body_html_content = body_template.render(context) - body_text_content = strip_tags(body_html_content) - - subject_template = Template(form.cleaned_data['subject']) - subject_text = strip_tags(subject_template.render(context)) - AccessControlList.objects.check_access( permissions=permission_user_mailer_use, user=self.request.user, obj=form.cleaned_data['user_mailer'] ) task_send_document.apply_async( - args=( - subject_text, body_text_content, self.request.user.email, - form.cleaned_data['email'] - ), kwargs={ - 'document_id': instance.pk, + kwargs={ 'as_attachment': self.as_attachment, - 'user_mailer_id': form.cleaned_data['user_mailer'].pk + 'body': form.cleaned_data['body'], + 'document_id': instance.pk, + 'recipient': form.cleaned_data['email'], + 'sender': self.request.user.email, + 'subject': form.cleaned_data['subject'], + 'user_mailer_id': form.cleaned_data['user_mailer'].pk, } )