Projet

Général

Profil

0001-workflows-add-mail-template-management-25378.patch

Frédéric Péters, 22 janvier 2020 15:16

Télécharger (30,4 ko)

Voir les différences:

Subject: [PATCH] workflows: add mail template management (#25378)

 tests/test_mail_templates.py                  | 208 ++++++++++++++++++
 wcs/admin/mail_templates.py                   | 170 ++++++++++++++
 wcs/admin/settings.py                         |   7 +-
 wcs/admin/workflows.py                        |   7 +-
 wcs/mail_templates.py                         |  85 +++++++
 wcs/publisher.py                              |   2 +-
 wcs/qommon/static/css/dc2/admin.css           |   5 +
 wcs/templates/wcs/backoffice/base.html        |   1 +
 .../wcs/backoffice/mail-template.html         |  25 +++
 .../wcs/backoffice/mail-templates.html        |  22 ++
 wcs/workflows.py                              |  52 ++++-
 11 files changed, 571 insertions(+), 13 deletions(-)
 create mode 100644 tests/test_mail_templates.py
 create mode 100644 wcs/admin/mail_templates.py
 create mode 100644 wcs/mail_templates.py
 create mode 100644 wcs/templates/wcs/backoffice/mail-template.html
 create mode 100644 wcs/templates/wcs/backoffice/mail-templates.html
tests/test_mail_templates.py
1
# -*- coding: utf-8 -*-
2

  
3
import base64
4
import os
5
import pytest
6

  
7
from django.utils import six
8
from django.utils.encoding import force_bytes
9
from quixote import cleanup
10
from wcs.formdef import FormDef
11
from wcs.mail_templates import MailTemplate
12
from wcs.workflows import Workflow, SendmailWorkflowStatusItem
13
from wcs.qommon.http_request import HTTPRequest
14
from wcs.qommon.ident.password_accounts import PasswordAccount
15

  
16
from utilities import (get_app, login, create_temporary_pub, clean_temporary_pub)
17

  
18

  
19
def setup_module(module):
20
    cleanup()
21

  
22

  
23
def teardown_module(module):
24
    clean_temporary_pub()
25

  
26

  
27
@pytest.fixture
28
def pub(request):
29
    pub = create_temporary_pub()
30
    req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'})
31
    pub.set_app_dir(req)
32
    pub._set_request(req)
33
    pub.cfg['identification'] = {'methods': ['password']}
34
    pub.write_cfg()
35
    return pub
36

  
37

  
38
@pytest.fixture
39
def superuser(pub):
40
    if pub.user_class.select(lambda x: x.name == 'admin'):
41
        user1 = pub.user_class.select(lambda x: x.name == 'admin')[0]
42
        user1.is_admin = True
43
        user1.store()
44
        return user1
45

  
46
    user1 = pub.user_class(name='admin')
47
    user1.is_admin = True
48
    user1.store()
49

  
50
    account1 = PasswordAccount(id='admin')
51
    account1.set_password('admin')
52
    account1.user_id = user1.id
53
    account1.store()
54

  
55
    return user1
56

  
57

  
58
@pytest.fixture
59
def mail_templates_option(pub):
60
    if not pub.site_options.has_section('options'):
61
        pub.site_options.add_section('options')
62
    pub.site_options.set('options', 'mail-templates', 'true')
63
    with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
64
        pub.site_options.write(fd)
65
    return pub
66

  
67

  
68
def test_mail_templates_disabled(pub, superuser):
69
    app = login(get_app(pub))
70
    resp = app.get('/backoffice/workflows/')
71
    assert 'Mail Templates' not in resp
72

  
73

  
74
def test_mail_templates_basics(pub, superuser, mail_templates_option):
75
    app = login(get_app(pub))
76
    resp = app.get('/backoffice/workflows/')
77
    assert 'Mail Templates' in resp
78
    resp = resp.click('Mail Templates')
79
    assert 'There are no mail templates defined.' in resp
80

  
81
    resp = resp.click('New')
82
    resp.form['name'] = 'first mail template'
83
    resp = resp.form.submit('cancel').follow()
84
    assert 'There are no mail templates defined.' in resp
85

  
86
    resp = resp.click('New')
87
    resp.form['name'] = 'first mail template'
88
    resp = resp.form.submit('submit').follow()
89
    resp.form['subject'] = 'mail subject'
90
    resp.form['body'] = 'mail body'
91
    resp = resp.form.submit('submit').follow()
92

  
93
    resp = resp.click('Edit')
94
    resp.form['subject'] = 'edited mail subject'
95
    resp.form['body'] = 'edited mail body'
96
    resp = resp.form.submit('submit').follow()
97

  
98
    resp = resp.click('Delete')
99
    resp = resp.form.submit('cancel').follow()
100
    assert 'first mail template' in resp
101

  
102
    resp = resp.click('Delete')
103
    resp = resp.form.submit('submit').follow()
104
    assert 'first mail template' not in resp
105
    assert 'There are no mail templates defined.' in resp
106

  
107
    resp = resp.click('New')
108
    resp.form['name'] = 'first mail template'
109
    resp = resp.form.submit('submit').follow()
110
    resp.form['subject'] = 'mail subject'
111
    resp.form['body'] = 'mail body'
112
    resp = resp.form.submit('submit').follow()
113

  
114
    resp = app.get('/backoffice/workflows/')
115
    resp = resp.click('Mail Templates')
116
    assert 'first mail template' in resp
117

  
118

  
119
def test_mail_template_in_use(pub, mail_templates_option):
120
    Workflow.wipe()
121
    MailTemplate.wipe()
122
    workflow = Workflow(name='test mail template')
123
    st1 = workflow.add_status('Status1')
124
    item = SendmailWorkflowStatusItem()
125
    item.to = ['_receiver']
126
    item.subject = 'Foobar'
127
    item.body = 'Hello'
128
    st1.items.append(item)
129
    item.parent = st1
130
    workflow.store()
131

  
132
    mail_template = MailTemplate(name='test mail template')
133
    mail_template.subject = 'test subject'
134
    mail_template.body = 'test body'
135
    mail_template.store()
136
    assert mail_template.is_in_use() is False
137

  
138
    item.mail_template = mail_template.slug
139
    workflow.store()
140
    assert mail_template.is_in_use() is True
141

  
142

  
143
def test_admin_workflow_edit(pub, superuser, mail_templates_option):
144
    Workflow.wipe()
145
    MailTemplate.wipe()
146
    mail_template = MailTemplate(name='test mail template')
147
    mail_template.subject = 'test subject'
148
    mail_template.body = 'test body'
149
    mail_template.store()
150

  
151
    workflow = Workflow(name='test mail template')
152
    st1 = workflow.add_status('Status1')
153
    item = SendmailWorkflowStatusItem()
154
    item.to = ['_receiver']
155
    item.subject = 'Foobar'
156
    item.body = 'Hello'
157
    st1.items.append(item)
158
    item.parent = st1
159
    workflow.store()
160

  
161
    app = login(get_app(pub))
162
    resp = app.get('/backoffice/workflows/%s/' % workflow.id)
163
    resp = resp.click('Status1')
164
    resp = resp.click('Email')
165
    resp.form['mail_template'] = 'test-mail-template'
166
    resp = resp.form.submit('submit')
167

  
168
    workflow = Workflow.get(workflow.id)
169
    assert workflow.possible_status[0].items[0].mail_template == 'test-mail-template'
170

  
171

  
172
def test_workflow_send_mail_template(pub, superuser, mail_templates_option, emails):
173
    Workflow.wipe()
174
    MailTemplate.wipe()
175

  
176
    mail_template = MailTemplate(name='test mail template')
177
    mail_template.subject = 'test subject'
178
    mail_template.body = 'test body'
179
    mail_template.store()
180

  
181
    workflow = Workflow(name='test mail template')
182
    st1 = workflow.add_status('Status1')
183
    item = SendmailWorkflowStatusItem()
184
    item.to = 'xyz@localhost'
185
    item.subject = 'Foobar'
186
    item.body = 'Hello'
187
    item.mail_template = mail_template.slug
188
    st1.items.append(item)
189
    item.parent = st1
190
    workflow.store()
191

  
192
    formdef = FormDef()
193
    formdef.name = 'baz'
194
    formdef.fields = []
195
    formdef.store()
196

  
197
    formdata = formdef.data_class()()
198
    formdata.just_created()
199
    formdata.store()
200

  
201
    item.perform(formdata)
202
    pub.get_request().response.process_after_jobs()
203
    assert emails.count() == 1
204
    assert emails.get('test subject')['email_rcpt'] == ['xyz@localhost']
205
    if six.PY2:
206
        assert 'test body' in emails.get('test subject')['msg'].get_payload(0).get_payload()
207
    else:
208
        assert b'test body' in base64.decodestring(force_bytes(emails.get('test subject')['msg'].get_payload(0).get_payload()))
wcs/admin/mail_templates.py
1
# w.c.s. - web application for online forms
2
# Copyright (C) 2005-2020  Entr'ouvert
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, see <http://www.gnu.org/licenses/>.
16

  
17
from quixote import get_response, redirect
18
from quixote.directory import Directory
19
from quixote.html import TemplateIO, htmltext
20

  
21
from wcs.qommon import _, errors, template
22
from wcs.qommon.backoffice.menu import html_top
23
from wcs.qommon.form import Form, HtmlWidget, StringWidget, TextWidget, ComputedExpressionWidget
24
from wcs.mail_templates import MailTemplate
25

  
26

  
27
class MailTemplatesDirectory(Directory):
28
    _q_exports = ['', 'new']
29
    do_not_call_in_templates = True
30

  
31
    def _q_traverse(self, path):
32
        get_response().breadcrumb.append(('workflows/', _('Workflows')))
33
        get_response().breadcrumb.append(('mail-templates/', _('Mail Templates')))
34
        return super(MailTemplatesDirectory, self)._q_traverse(path)
35

  
36
    def _q_lookup(self, component):
37
        return MailTemplatePage(component)
38

  
39
    def _q_index(self):
40
        html_top('mail_templates', title=_('Mail Templates'))
41
        return template.QommonTemplateResponse(
42
                templates=['wcs/backoffice/mail-templates.html'],
43
                context={'view': self, 'mail_templates': MailTemplate.select(order_by='name')})
44

  
45
    def new(self):
46
        form = Form(enctype='multipart/form-data')
47
        form.add(StringWidget, 'name', title=_('Name'), required=True, size=50)
48
        form.add_submit('submit', _('Add'))
49
        form.add_submit('cancel', _('Cancel'))
50
        if form.get_widget('cancel').parse():
51
            return redirect('.')
52

  
53
        if form.is_submitted() and not form.has_errors():
54
            mail_template = MailTemplate(name=form.get_widget('name').parse())
55
            mail_template.store()
56
            return redirect('%s/edit' % mail_template.id)
57

  
58
        get_response().breadcrumb.append(('new', _('New Mail Template')))
59
        html_top('mail_templates', title=_('New Mail Template'))
60
        r = TemplateIO(html=True)
61
        r += htmltext('<h2>%s</h2>') % _('New Mail Template')
62
        r += form.render()
63
        return r.getvalue()
64

  
65

  
66
class MailTemplatePage(Directory):
67
    _q_exports = ['', 'edit', 'delete']
68
    do_not_call_in_templates = True
69

  
70
    def __init__(self, mail_template_id):
71
        try:
72
            self.mail_template = MailTemplate.get(mail_template_id)
73
        except KeyError:
74
            raise errors.TraversalError()
75
        get_response().breadcrumb.append((mail_template_id + '/', self.mail_template.name))
76

  
77
    def _q_index(self):
78
        html_top('mail_templates', title=self.mail_template.name)
79
        return template.QommonTemplateResponse(
80
                templates=['wcs/backoffice/mail-template.html'],
81
                context={'view': self, 'mail_template': self.mail_template})
82

  
83
    def get_form(self):
84
        form = Form(enctype='multipart/form-data', advanced_label=_('Additional options'))
85
        form.add(StringWidget, 'name', title=_('Name'), required=True, size=30,
86
                value=self.mail_template.name)
87
        form.add(TextWidget, 'description', title=_('Description'),
88
                cols=80, rows=3,
89
                value=self.mail_template.description)
90
        form.add(StringWidget, 'subject', title=_('Subject'), required=True, size=40,
91
                value=self.mail_template.subject,
92
                 validation_function=ComputedExpressionWidget.validate_template)
93
        form.add(TextWidget, 'body', title=_('Body'),
94
                 value=self.mail_template.body, cols=80, rows=15, require=True,
95
                 validation_function=ComputedExpressionWidget.validate_template)
96

  
97
        if self.mail_template.slug and not self.mail_template.is_in_use():
98
            form.add(StringWidget, 'slug',
99
                    value=self.mail_template.slug,
100
                    title=_('Identifier'),
101
                    required=True, advanced=True,
102
                    )
103
        form.add_submit('submit', _('Submit'))
104
        form.add_submit('cancel', _('Cancel'))
105
        return form
106

  
107
    def submit_form(self, form):
108
        name = form.get_widget('name').parse()
109
        slug_widget = form.get_widget('slug')
110
        if slug_widget:
111
            slug = form.get_widget('slug').parse()
112

  
113
        for mail_template in MailTemplate.select():
114
            if mail_template.id == self.mail_template.id:
115
                continue
116
            if slug_widget and slug == mail_template.slug:
117
                slug_widget.set_error(_('This value is already used.'))
118
        if form.has_errors():
119
            raise ValueError()
120

  
121
        self.mail_template.name = name
122
        self.mail_template.description = form.get_widget('description').parse()
123
        self.mail_template.subject = form.get_widget('subject').parse()
124
        self.mail_template.body = form.get_widget('body').parse()
125
        if slug_widget:
126
            self.mail_template.slug = slug
127
        self.mail_template.store()
128

  
129
    def edit(self):
130
        form = self.get_form()
131
        if form.get_submit() == 'cancel':
132
            return redirect('.')
133

  
134
        if form.get_submit() == 'submit' and not form.has_errors():
135
            try:
136
                self.submit_form(form)
137
            except ValueError:
138
                pass
139
            else:
140
                return redirect('.')
141

  
142
        get_response().breadcrumb.append(('edit', _('Edit')))
143
        html_top('mail_templates', title=_('Edit Mail Template'))
144
        r = TemplateIO(html=True)
145
        r += htmltext('<h2>%s</h2>') % _('Edit Mail Template')
146
        r += form.render()
147
        return r.getvalue()
148

  
149
    def delete(self):
150
        form = Form(enctype='multipart/form-data')
151
        if not self.mail_template.is_in_use():
152
            form.widgets.append(HtmlWidget('<p>%s</p>' % _(
153
                        'You are about to irrevocably delete this mail template.')))
154
            form.add_submit('delete', _('Submit'))
155
        else:
156
            form.widgets.append(HtmlWidget('<p>%s</p>' % _(
157
                        'This mail template is still used, it cannot be deleted.')))
158
        form.add_submit('cancel', _('Cancel'))
159
        if form.get_widget('cancel').parse():
160
            return redirect('.')
161
        if not form.is_submitted() or form.has_errors():
162
            get_response().breadcrumb.append(('delete', _('Delete')))
163
            html_top('mail_templates', title=_('Delete Mail Template'))
164
            r = TemplateIO(html=True)
165
            r += htmltext('<h2>%s %s</h2>') % (_('Deleting Mail Template:'), self.mail_template.name)
166
            r += form.render()
167
            return r.getvalue()
168
        else:
169
            self.mail_template.remove_self()
170
            return redirect('..')
wcs/admin/settings.py
861 861
        form.add(CheckboxWidget, 'categories', title = _('Categories'), value = True)
862 862
        form.add(CheckboxWidget, 'settings', title = _('Settings'), value = False)
863 863
        form.add(CheckboxWidget, 'datasources', title=_('Data sources'), value=True)
864
        form.add(CheckboxWidget, 'mail-templates', title=_('Mail templates'), value=True)
864 865
        form.add(CheckboxWidget, 'wscalls', title=_('Webservice calls'), value=True)
865 866
        form.add_submit('submit', _('Submit'))
866 867
        form.add_submit('cancel', _('Cancel'))
......
885 886
                c = BytesIO()
886 887
                z = zipfile.ZipFile(c, 'w')
887 888
                for d in self.dirs:
888
                    if d not in ('roles', 'categories', 'datasources', 'wscalls'):
889
                    if d not in ('roles', 'categories', 'datasources', 'wscalls', 'mail-templates'):
889 890
                        continue
890 891
                    path = os.path.join(self.app_dir, d)
891 892
                    if not os.path.exists(path):
......
928 929

  
929 930
        dirs = []
930 931
        for w in ('formdefs', 'carddefs', 'workflows', 'roles', 'categories',
931
                'datasources', 'wscalls'):
932
                'datasources', 'wscalls', 'mail-templates'):
932 933
            if form.get_widget(w) and form.get_widget(w).parse():
933 934
                dirs.append(w)
934 935
        if not dirs and not form.get_widget('settings').parse():
......
1017 1018
                    r += htmltext('<li>%s</li>') % _('Settings')
1018 1019
                if results['datasources']:
1019 1020
                    r += htmltext('<li>%d %s</li>') % (results['datasources'], _('data sources'))
1021
                if results['mail-templates']:
1022
                    r += htmltext('<li>%d %s</li>') % (results['mail-templates'], _('mail templates'))
1020 1023
                if results['wscalls']:
1021 1024
                    r += htmltext('<li>%d %s</li>') % (results['wscalls'], _('webservice calls'))
1022 1025
                r += htmltext('</ul>')
wcs/admin/workflows.py
46 46
from .fields import FieldDefPage, FieldsDirectory
47 47
from .data_sources import NamedDataSourcesDirectory
48 48
from .logged_errors import LoggedErrorsDirectory
49
from .mail_templates import MailTemplatesDirectory
49 50
from wcs.backoffice.studio import StudioDirectory
50 51

  
51 52

  
......
1814 1815

  
1815 1816
class WorkflowsDirectory(Directory):
1816 1817
    _q_exports = ['', 'new', ('import', 'p_import'),
1817
            ('data-sources', 'data_sources')]
1818
            ('data-sources', 'data_sources'),
1819
            ('mail-templates', 'mail_templates')]
1818 1820

  
1819 1821
    data_sources = NamedDataSourcesDirectoryInWorkflows()
1822
    mail_templates = MailTemplatesDirectory()
1820 1823

  
1821 1824
    def html_top(self, title):
1822 1825
        return html_top('workflows', title)
......
1829 1832
        r += htmltext('<div id="appbar">')
1830 1833
        r += htmltext('<h2>%s</h2>') % _('Workflows')
1831 1834
        r += htmltext('<span class="actions">')
1835
        if get_publisher().has_site_option('mail-templates'):
1836
            r += htmltext('<a href="mail-templates/">%s</a>') % _('Mail Templates')
1832 1837
        r += htmltext('<a href="data-sources/">%s</a>') % _('Data sources')
1833 1838
        r += htmltext('<a href="import" rel="popup">%s</a>') % _('Import')
1834 1839
        r += htmltext('<a class="new-item" rel="popup" href="new">%s</a>') % _('New Workflow')
wcs/mail_templates.py
1
# w.c.s. - web application for online forms
2
# Copyright (C) 2005-2020  Entr'ouvert
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, see <http://www.gnu.org/licenses/>.
16

  
17
from wcs.qommon import misc, get_logger
18
from wcs.qommon.xml_storage import XmlStorableObject
19

  
20

  
21
class MailTemplate(XmlStorableObject):
22
    _names = 'mail-templates'
23
    _xml_tagname = 'mail-template'
24

  
25
    name = None
26
    slug = None
27
    description = None
28
    subject = None
29
    body = None
30

  
31
    # declarations for serialization
32
    XML_NODES = [
33
            ('name', 'str'),
34
            ('slug', 'str'),
35
            ('description', 'str'),
36
            ('subject', 'str'),
37
            ('body', 'str'),
38
    ]
39

  
40
    def __init__(self, name=None):
41
        XmlStorableObject.__init__(self)
42
        self.name = name
43

  
44
    def store(self):
45
        if self.slug is None:
46
            # set slug if it's not yet there
47
            self.slug = self.get_new_slug()
48
        super(MailTemplate, self).store()
49

  
50
    def get_new_slug(self):
51
        new_slug = misc.simplify(self.name, space='-')
52
        base_new_slug = new_slug
53
        suffix_no = 0
54
        known_slugs = {x.slug: x.id for x in self.select(ignore_migration=True, ignore_errors=True)}
55
        while True:
56
            if not new_slug in known_slugs:
57
                break
58
            suffix_no += 1
59
            new_slug = '%s-%s' % (base_new_slug, suffix_no)
60
        return new_slug
61

  
62
    def is_in_use(self):
63
        from wcs.workflows import Workflow
64
        for workflow in Workflow.select(ignore_errors=True, ignore_migration=True):
65
            for item in workflow.get_all_items():
66
                if item.key != 'sendmail':
67
                    continue
68
                if item.mail_template == self.slug:
69
                    return True
70
        return False
71

  
72
    @classmethod
73
    def get_as_options_list(cls):
74
        options = []
75
        for mail_template in cls.select(order_by='name'):
76
            options.append((mail_template.slug, mail_template.name, mail_template.slug))
77
        return options
78

  
79
    @classmethod
80
    def get_by_slug(cls, slug):
81
        objects = [x for x in cls.select() if x.slug == slug]
82
        if objects:
83
            return objects[0]
84
        get_logger().warn("mail template '%s' does not exist" % slug)
85
        return None
wcs/publisher.py
160 160
    def import_zip(self, fd):
161 161
        z = zipfile.ZipFile(fd)
162 162
        results = {'formdefs': 0, 'carddefs': 0, 'workflows': 0, 'categories': 0, 'roles': 0,
163
                'settings': 0, 'datasources': 0, 'wscalls': 0}
163
                'settings': 0, 'datasources': 0, 'wscalls': 0, 'mail-templates': 0}
164 164

  
165 165
        def _decode_list(data):
166 166
            rv = []
wcs/qommon/static/css/dc2/admin.css
1859 1859
table#listing tr.checked td {
1860 1860
	background: #ddf;
1861 1861
}
1862

  
1863
div.mail-body {
1864
	margin-top: 1em;
1865
	white-space: pre-line;
1866
}
wcs/templates/wcs/backoffice/base.html
1 1
{% block appbar %}
2 2
<div id="appbar">
3 3
<h2>{% block appbar-title %}{% endblock %}</h2>
4
<span class="actions">{% block appbar-actions %}{% endblock %}</span>
4 5
</div>
5 6
{% endblock %}
6 7

  
wcs/templates/wcs/backoffice/mail-template.html
1
{% extends "wcs/backoffice/base.html" %}
2
{% load i18n %}
3

  
4
{% block appbar-title %}{% trans "Mail Template" %} - {{ mail_template.name }}{% endblock %}
5

  
6
{% block appbar-actions %}
7
<a href="delete">{% trans "Delete" %}</a>
8
<a href="edit">{% trans "Edit" %}</a>
9
{% endblock %}
10

  
11
{% block content %}
12
{% if mail_template.description %}
13
<div class="bo-block">{{ mail_template.description }}</div>
14
{% endif %}
15

  
16
{% if mail_template.subject and mail_template.body %}
17
<div class="bo-block">
18
<div class="mail-subject"><strong>{% trans "Subject:" %} </strong>{{ mail_template.subject }}</div>
19
<div class="mail-body">{{ mail_template.body }}</div>
20
</div>
21
{% else %}
22
<div class="infonotice">{% trans "This mail template still needs to be configured." %}</div>
23
{% endif %}
24

  
25
{% endblock %}
wcs/templates/wcs/backoffice/mail-templates.html
1
{% extends "wcs/backoffice/base.html" %}
2
{% load i18n %}
3

  
4
{% block appbar-title %}{% trans "Mail Templates" %}{% endblock %}
5

  
6
{% block appbar-actions %}
7
<a rel="popup" href="new">{% trans "New mail template" %}</a>
8
{% endblock %}
9

  
10
{% block content %}
11
{% if mail_templates %}
12
<ul class="objects-list single-links">
13
  {% for mail_template in mail_templates %}
14
  <li><a href="{{ mail_template.id }}/">{{ mail_template.name }}</a></li>
15
  {% endfor %}
16
</ul>
17
{% else %}
18
<div class="infonotice">
19
{% trans "There are no mail templates defined." %}
20
</div>
21
{% endif %}
22
{% endblock %}
wcs/workflows.py
46 46
from .fields import FileField
47 47
from .formdef import FormDef
48 48
from .formdata import Evolution
49
from .mail_templates import MailTemplate
49 50

  
50 51
if not __name__.startswith('wcs.') and not __name__ == "__main__":
51 52
    raise ImportError('Import of workflows module must be absolute (import wcs.workflows)')
......
422 423
            return self.backoffice_fields_formdef.fields or []
423 424
        return []
424 425

  
426
    def get_all_items(self):
427
        for status in self.possible_status or []:
428
            for item in status.items or []:
429
                yield item
430
        for action in self.global_actions or []:
431
            for item in action.items or []:
432
                yield item
433

  
425 434
    def add_global_action(self, name, id=None):
426 435
        if [x for x in self.global_actions if x.name == name]:
427 436
            raise DuplicateGlobalActionNameError()
......
2445 2454

  
2446 2455
    to = []
2447 2456
    subject = None
2457
    mail_template = None
2448 2458
    body = None
2449 2459
    custom_from = None
2450 2460
    attachments = None
......
2487 2497
            return _('not completed')
2488 2498

  
2489 2499
    def get_parameters(self):
2490
        return ('to', 'subject', 'body', 'attachments', 'custom_from', 'condition')
2500
        return ('to', 'mail_template', 'subject', 'body', 'attachments', 'custom_from', 'condition')
2491 2501

  
2492 2502
    def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
2493 2503
        super(SendmailWorkflowStatusItem, self).add_parameters_widgets(
2494 2504
                form, parameters, prefix=prefix, formdef=formdef)
2505
        subject_body_attrs = {}
2506
        if 'subject' in parameters or 'body' in parameters:
2507
            if get_publisher().has_site_option('mail-templates') and MailTemplate.count():
2508
                subject_body_attrs = {
2509
                    'data-dynamic-display-value': '',
2510
                    'data-dynamic-display-child-of': '%smail_template' % prefix,
2511
                }
2512

  
2495 2513
        if 'to' in parameters:
2496 2514
            form.add(WidgetList, '%sto' % prefix, title=_('To'),
2497 2515
                     element_type=SingleSelectWidgetWithOther,
......
2503 2521
        if 'subject' in parameters:
2504 2522
            form.add(StringWidget, '%ssubject' % prefix, title=_('Subject'),
2505 2523
                     validation_function=ComputedExpressionWidget.validate_template,
2506
                     value=self.subject, size=40)
2524
                     value=self.subject, size=40,
2525
                     attrs=subject_body_attrs)
2526
        if 'mail_template' in parameters and get_publisher().has_site_option('mail-templates') and MailTemplate.count():
2527
            form.add(SingleSelectWidget,
2528
                    '%smail_template' % prefix,
2529
                    title=_('Mail Template'),
2530
                    value=self.mail_template,
2531
                    options=[(None, '', '')] + MailTemplate.get_as_options_list(),
2532
                    attrs={'data-dynamic-display-parent': 'true'}
2533
            )
2507 2534
        if 'body' in parameters:
2508 2535
            form.add(TextWidget, '%sbody' % prefix, title=_('Body'),
2509 2536
                     value=self.body, cols=80, rows=10,
2510
                     validation_function=ComputedExpressionWidget.validate_template)
2537
                     validation_function=ComputedExpressionWidget.validate_template,
2538
                     attrs=subject_body_attrs)
2511 2539

  
2512 2540
        if 'custom_from' in parameters:
2513 2541
            form.add(ComputedExpressionWidget, '%scustom_from' % prefix,
......
2520 2548
    def perform(self, formdata):
2521 2549
        if not self.to:
2522 2550
            return
2523
        if not self.subject:
2524
            return
2525
        if not self.body:
2551
        if not (self.subject and self.body) and not self.mail_template:
2526 2552
            return
2527 2553

  
2528 2554
        url = formdata.get_url()
2555
        body = self.body
2556
        subject = self.subject
2557
        if self.mail_template:
2558
            mail_template = MailTemplate.get_by_slug(self.mail_template)
2559
            if mail_template:
2560
                body = mail_template.body
2561
                subject = mail_template.subject
2562

  
2529 2563
        try:
2530 2564
            mail_body = template_on_formdata(formdata,
2531
                    self.compute(self.body, render=False),
2532
                    autoescape=self.body.startswith('<'))
2565
                    self.compute(body, render=False),
2566
                    autoescape=body.startswith('<'))
2533 2567
        except TemplateError as e:
2534 2568
            get_logger().error('error in template for email body [%s], '
2535 2569
                               'mail could not be generated: %s' % (url, str(e)))
......
2537 2571

  
2538 2572
        try:
2539 2573
            mail_subject = template_on_formdata(formdata,
2540
                    self.compute(self.subject, render=False),
2574
                    self.compute(subject, render=False),
2541 2575
                    autoescape=False)
2542 2576
        except TemplateError as e:
2543 2577
            get_logger().error('error in template for email subject [%s], '
2544
-