diff --git a/HISTORY.rst b/HISTORY.rst index d14468108c..d5c54b33bb 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -10,6 +10,7 @@ - Add support for source column exclusion. - Backport workflow context support. - Backport workflow transitions field support. +- Backport workflow email action. 3.2.5 (2019-07-05) ================== diff --git a/docs/releases/3.3.rst b/docs/releases/3.3.rst index 6a66ffeb19..d542585200 100644 --- a/docs/releases/3.3.rst +++ b/docs/releases/3.3.rst @@ -22,6 +22,7 @@ Changes - Add support for source column exclusion. - Backport workflow context support. - Backport workflow transitions field support. +- Backport workflow email action. Removals -------- diff --git a/mayan/apps/mailer/tests/literals.py b/mayan/apps/mailer/tests/literals.py index ae3b603355..e3ea1038a0 100644 --- a/mayan/apps/mailer/tests/literals.py +++ b/mayan/apps/mailer/tests/literals.py @@ -1,8 +1,10 @@ from __future__ import unicode_literals -TEST_BODY_HTML = 'test body' TEST_EMAIL_ADDRESS = 'test@example.com' +TEST_EMAIL_BODY = 'test body' +TEST_EMAIL_BODY_HTML = 'test body' TEST_EMAIL_FROM_ADDRESS = 'from.test@example.com' +TEST_EMAIL_SUBJECT = 'test subject' TEST_RECIPIENTS_MULTIPLE_COMMA = 'test@example.com,test2@example.com' TEST_RECIPIENTS_MULTIPLE_COMMA_RESULT = [ 'test@example.com', 'test2@example.com' diff --git a/mayan/apps/mailer/tests/test_actions.py b/mayan/apps/mailer/tests/test_actions.py new file mode 100644 index 0000000000..eed6d9f49a --- /dev/null +++ b/mayan/apps/mailer/tests/test_actions.py @@ -0,0 +1,180 @@ +from __future__ import unicode_literals + +import json + +from django.core import mail + +from mayan.apps.common.tests import GenericViewTestCase +from mayan.apps.documents.tests.mixins import DocumentTestMixin +from mayan.apps.document_states.literals import WORKFLOW_ACTION_ON_ENTRY +from mayan.apps.document_states.tests.mixins import WorkflowTestMixin +from mayan.apps.document_states.tests.test_actions import ActionTestCase +from mayan.apps.metadata.tests.mixins import MetadataTypeTestMixin + +from ..permissions import permission_user_mailer_use +from ..workflow_actions import EmailAction + +from .literals import ( + TEST_EMAIL_ADDRESS, TEST_EMAIL_BODY, TEST_EMAIL_FROM_ADDRESS, + TEST_EMAIL_SUBJECT +) +from .mixins import MailerTestMixin + + +class EmailActionTestCase(MailerTestMixin, WorkflowTestMixin, ActionTestCase): + def test_email_action_literal_text(self): + self._create_test_user_mailer() + + action = EmailAction( + form_data={ + 'mailing_profile': self.test_user_mailer.pk, + 'recipient': TEST_EMAIL_ADDRESS, + 'subject': TEST_EMAIL_SUBJECT, + 'body': TEST_EMAIL_BODY, + } + ) + action.execute(context={'document': self.test_document}) + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].from_email, TEST_EMAIL_FROM_ADDRESS) + self.assertEqual(mail.outbox[0].to, [TEST_EMAIL_ADDRESS]) + + def test_email_action_workflow_execute(self): + self._create_test_workflow() + self._create_test_workflow_state() + self._create_test_user_mailer() + + self.test_workflow_state.actions.create( + action_data=json.dumps( + { + 'mailing_profile': self.test_user_mailer.pk, + 'recipient': TEST_EMAIL_ADDRESS, + 'subject': TEST_EMAIL_SUBJECT, + 'body': TEST_EMAIL_BODY, + } + ), + action_path='mayan.apps.mailer.workflow_actions.EmailAction', + label='test email action', when=WORKFLOW_ACTION_ON_ENTRY, + ) + + self.test_workflow_state.initial = True + self.test_workflow_state.save() + self.test_workflow.document_types.add(self.test_document_type) + + self.upload_document() + + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].from_email, TEST_EMAIL_FROM_ADDRESS) + self.assertEqual(mail.outbox[0].to, [TEST_EMAIL_ADDRESS]) + + +class EmailActionTemplateTestCase(MetadataTypeTestMixin, MailerTestMixin, WorkflowTestMixin, ActionTestCase): + def test_email_action_recipient_template(self): + self._create_test_metadata_type() + self.test_document_type.metadata.create(metadata_type=self.test_metadata_type) + self.test_document.metadata.create(metadata_type=self.test_metadata_type, value=TEST_EMAIL_ADDRESS) + + self._create_test_user_mailer() + + action = EmailAction( + form_data={ + 'mailing_profile': self.test_user_mailer.pk, + 'recipient': '{{{{ document.metadata_value_of.{} }}}}'.format(self.test_metadata_type.name), + 'subject': TEST_EMAIL_SUBJECT, + 'body': '', + } + ) + action.execute(context={'document': self.test_document}) + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].from_email, TEST_EMAIL_FROM_ADDRESS) + self.assertEqual(mail.outbox[0].to, [TEST_EMAIL_ADDRESS]) + + def test_email_action_subject_template(self): + self._create_test_metadata_type() + self.test_document_type.metadata.create(metadata_type=self.test_metadata_type) + self.test_document.metadata.create(metadata_type=self.test_metadata_type, value=TEST_EMAIL_SUBJECT) + + self._create_test_user_mailer() + + action = EmailAction( + form_data={ + 'mailing_profile': self.test_user_mailer.pk, + 'recipient': TEST_EMAIL_ADDRESS, + 'subject': '{{{{ document.metadata_value_of.{} }}}}'.format(self.test_metadata_type.name), + 'body': '', + } + ) + action.execute(context={'document': self.test_document}) + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].from_email, TEST_EMAIL_FROM_ADDRESS) + self.assertEqual(mail.outbox[0].to, [TEST_EMAIL_ADDRESS]) + + def test_email_action_body_template(self): + self._create_test_metadata_type() + self.test_document_type.metadata.create(metadata_type=self.test_metadata_type) + self.test_document.metadata.create(metadata_type=self.test_metadata_type, value=TEST_EMAIL_BODY) + + self._create_test_user_mailer() + + action = EmailAction( + form_data={ + 'mailing_profile': self.test_user_mailer.pk, + 'recipient': TEST_EMAIL_ADDRESS, + 'subject': TEST_EMAIL_SUBJECT, + 'body': '{{{{ document.metadata_value_of.{} }}}}'.format(self.test_metadata_type.name), + } + ) + action.execute(context={'document': self.test_document}) + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].from_email, TEST_EMAIL_FROM_ADDRESS) + self.assertEqual(mail.outbox[0].to, [TEST_EMAIL_ADDRESS]) + self.assertEqual(mail.outbox[0].body, TEST_EMAIL_BODY) + + +class EmailActionViewTestCase(DocumentTestMixin, MailerTestMixin, WorkflowTestMixin, GenericViewTestCase): + auto_upload_document = False + + def test_email_action_create_get_view(self): + self._create_test_workflow() + self._create_test_workflow_state() + self._create_test_user_mailer() + + response = self.get( + viewname='document_states:setup_workflow_state_action_create', + kwargs={ + 'pk': self.test_workflow_state.pk, + 'class_path': 'mayan.apps.mailer.workflow_actions.EmailAction', + } + ) + self.assertEqual(response.status_code, 200) + + self.assertEqual(self.test_workflow_state.actions.count(), 0) + + def _request_email_action_create_post_view(self): + return self.post( + viewname='document_states:setup_workflow_state_action_create', + kwargs={ + 'pk': self.test_workflow_state.pk, + 'class_path': 'mayan.apps.mailer.workflow_actions.EmailAction', + }, data={ + 'when': WORKFLOW_ACTION_ON_ENTRY, + 'label': 'test email action', + 'mailing_profile': self.test_user_mailer.pk, + 'recipient': TEST_EMAIL_ADDRESS, + 'subject': TEST_EMAIL_SUBJECT, + 'body': TEST_EMAIL_BODY, + } + ) + + def test_email_action_create_post_view(self): + self._create_test_workflow() + self._create_test_workflow_state() + self._create_test_user_mailer() + + self.grant_access( + obj=self.test_user_mailer, permission=permission_user_mailer_use + ) + + response = self._request_email_action_create_post_view() + self.assertEqual(response.status_code, 302) + + self.assertEqual(self.test_workflow_state.actions.count(), 1) diff --git a/mayan/apps/mailer/tests/test_models.py b/mayan/apps/mailer/tests/test_models.py index 376e4ba669..02f3cab3b1 100644 --- a/mayan/apps/mailer/tests/test_models.py +++ b/mayan/apps/mailer/tests/test_models.py @@ -5,7 +5,7 @@ from django.core import mail from mayan.apps.documents.tests.test_models import GenericDocumentTestCase from .literals import ( - TEST_BODY_HTML, TEST_EMAIL_ADDRESS, TEST_EMAIL_FROM_ADDRESS, + TEST_EMAIL_BODY_HTML, TEST_EMAIL_ADDRESS, TEST_EMAIL_FROM_ADDRESS, TEST_RECIPIENTS_MULTIPLE_COMMA, TEST_RECIPIENTS_MULTIPLE_COMMA_RESULT, TEST_RECIPIENTS_MULTIPLE_SEMICOLON, TEST_RECIPIENTS_MULTIPLE_SEMICOLON_RESULT, TEST_RECIPIENTS_MULTIPLE_MIXED, @@ -25,17 +25,22 @@ class ModelTestCase(MailerTestMixin, GenericDocumentTestCase): def test_send_simple_with_html(self): self._create_test_user_mailer() - self.test_user_mailer.send(to=TEST_EMAIL_ADDRESS, body=TEST_BODY_HTML) + self.test_user_mailer.send( + to=TEST_EMAIL_ADDRESS, body=TEST_EMAIL_BODY_HTML + ) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].from_email, TEST_EMAIL_FROM_ADDRESS) self.assertEqual(mail.outbox[0].to, [TEST_EMAIL_ADDRESS]) - self.assertEqual(mail.outbox[0].alternatives[0][0], TEST_BODY_HTML) + self.assertEqual( + mail.outbox[0].alternatives[0][0], TEST_EMAIL_BODY_HTML + ) def test_send_attachment(self): self._create_test_user_mailer() self.test_user_mailer.send_document( - to=TEST_EMAIL_ADDRESS, document=self.test_document, as_attachment=True + to=TEST_EMAIL_ADDRESS, document=self.test_document, + as_attachment=True ) self.assertEqual(len(mail.outbox), 1) diff --git a/mayan/apps/mailer/workflow_actions.py b/mayan/apps/mailer/workflow_actions.py new file mode 100644 index 0000000000..c344082ac5 --- /dev/null +++ b/mayan/apps/mailer/workflow_actions.py @@ -0,0 +1,124 @@ +from __future__ import absolute_import, unicode_literals + +import logging + +from django.template import Template, Context +from django.utils.translation import ugettext_lazy as _ + +from mayan.apps.acls.models import AccessControlList +from mayan.apps.document_states.classes import WorkflowAction +from mayan.apps.document_states.exceptions import WorkflowStateActionError + +from .models import UserMailer +from .permissions import permission_user_mailer_use + +__all__ = ('EmailAction',) +logger = logging.getLogger(__name__) + + +class EmailAction(WorkflowAction): + fields = { + 'mailing_profile': { + 'label': _('Mailing profile'), + 'class': 'django.forms.ModelChoiceField', 'kwargs': { + 'help_text': _('Mailing profile to use when sending the email.'), + 'queryset': UserMailer.objects.none(), 'required': True + } + }, + 'recipient': { + 'label': _('Recipient'), + 'class': 'django.forms.CharField', 'kwargs': { + 'help_text': _( + 'Email address of the recipient. Can be multiple addresses ' + 'separated by comma or semicolon. A template can be used ' + 'to reference properties of the document.' + ), + 'required': True + } + }, + 'subject': { + 'label': _('Subject'), + 'class': 'django.forms.CharField', 'kwargs': { + 'help_text': _( + 'Subject of the email. Can be a string or a template.' + ), + 'required': True + } + }, + 'body': { + 'label': _('Body'), + 'class': 'django.forms.CharField', 'kwargs': { + 'help_text': _( + 'Body of the email to send. Can be a string or a template.' + ), + 'required': True + } + }, + } + field_order = ('mailing_profile', 'recipient', 'subject', 'body') + label = _('Send email') + widgets = { + 'body': { + 'class': 'django.forms.widgets.Textarea', 'kwargs': {} + } + } + permission = permission_user_mailer_use + + def execute(self, context): + try: + recipient = Template(self.form_data['recipient']).render( + context=Context(context) + ) + except Exception as exception: + raise WorkflowStateActionError( + _('Recipient template error: %s') % exception + ) + else: + logger.debug('Recipient result: %s', recipient) + + try: + subject = Template(self.form_data['subject']).render( + context=Context(context) + ) + except Exception as exception: + raise WorkflowStateActionError( + _('Subject template error: %s') % exception + ) + else: + logger.debug('Subject result: %s', subject) + + try: + body = Template(self.form_data['body']).render( + context=Context(context) + ) + except Exception as exception: + raise WorkflowStateActionError( + _('Body template error: %s') % exception + ) + else: + logger.debug('Body result: %s', body) + + user_mailer = self.get_user_mailer() + user_mailer.send( + to=recipient, subject=subject, body=body, + ) + + def get_form_schema(self, request): + user = request.user + logger.debug('user: %s', user) + + queryset = AccessControlList.objects.restrict_queryset( + permission=self.permission, queryset=UserMailer.objects.all(), + user=user + ) + + self.fields['mailing_profile']['kwargs']['queryset'] = queryset + + return { + 'field_order': self.field_order, + 'fields': self.fields, + 'widgets': self.widgets + } + + def get_user_mailer(self): + return UserMailer.objects.get(pk=self.form_data['mailing_profile'])