workflows: add mail template management (#25378)
This commit is contained in:
parent
b10d3fdb33
commit
0105a7bd29
|
@ -0,0 +1,217 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import base64
|
||||
import os
|
||||
import pytest
|
||||
|
||||
from django.utils.encoding import force_bytes
|
||||
from quixote import cleanup
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.logged_errors import LoggedError
|
||||
from wcs.mail_templates import MailTemplate
|
||||
from wcs.workflows import Workflow, SendmailWorkflowStatusItem
|
||||
from wcs.qommon.http_request import HTTPRequest
|
||||
from wcs.qommon.ident.password_accounts import PasswordAccount
|
||||
|
||||
from utilities import (get_app, login, create_temporary_pub, clean_temporary_pub)
|
||||
|
||||
|
||||
def setup_module(module):
|
||||
cleanup()
|
||||
|
||||
|
||||
def teardown_module(module):
|
||||
clean_temporary_pub()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pub(request):
|
||||
pub = create_temporary_pub()
|
||||
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'})
|
||||
pub.set_app_dir(req)
|
||||
pub._set_request(req)
|
||||
pub.cfg['identification'] = {'methods': ['password']}
|
||||
pub.write_cfg()
|
||||
return pub
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def superuser(pub):
|
||||
if pub.user_class.select(lambda x: x.name == 'admin'):
|
||||
user1 = pub.user_class.select(lambda x: x.name == 'admin')[0]
|
||||
user1.is_admin = True
|
||||
user1.store()
|
||||
return user1
|
||||
|
||||
user1 = pub.user_class(name='admin')
|
||||
user1.is_admin = True
|
||||
user1.store()
|
||||
|
||||
account1 = PasswordAccount(id='admin')
|
||||
account1.set_password('admin')
|
||||
account1.user_id = user1.id
|
||||
account1.store()
|
||||
|
||||
return user1
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mail_templates_option(pub):
|
||||
if not pub.site_options.has_section('options'):
|
||||
pub.site_options.add_section('options')
|
||||
pub.site_options.set('options', 'mail-templates', 'true')
|
||||
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
|
||||
pub.site_options.write(fd)
|
||||
return pub
|
||||
|
||||
|
||||
def test_mail_templates_disabled(pub, superuser):
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/workflows/')
|
||||
assert 'Mail Templates' not in resp
|
||||
|
||||
|
||||
def test_mail_templates_basics(pub, superuser, mail_templates_option):
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/workflows/')
|
||||
assert 'Mail Templates' in resp
|
||||
resp = resp.click('Mail Templates')
|
||||
assert 'There are no mail templates defined.' in resp
|
||||
|
||||
resp = resp.click('New')
|
||||
resp.form['name'] = 'first mail template'
|
||||
resp = resp.form.submit('cancel').follow()
|
||||
assert 'There are no mail templates defined.' in resp
|
||||
|
||||
resp = resp.click('New')
|
||||
resp.form['name'] = 'first mail template'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
resp.form['subject'] = 'mail subject'
|
||||
resp.form['body'] = 'mail body'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
|
||||
resp = resp.click('Edit')
|
||||
resp.form['subject'] = 'edited mail subject'
|
||||
resp.form['body'] = 'edited mail body'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
|
||||
resp = resp.click('Delete')
|
||||
resp = resp.form.submit('cancel').follow()
|
||||
assert 'first mail template' in resp
|
||||
|
||||
resp = resp.click('Delete')
|
||||
resp = resp.form.submit('submit').follow()
|
||||
assert 'first mail template' not in resp
|
||||
assert 'There are no mail templates defined.' in resp
|
||||
|
||||
resp = resp.click('New')
|
||||
resp.form['name'] = 'first mail template'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
resp.form['subject'] = 'mail subject'
|
||||
resp.form['body'] = 'mail body'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
|
||||
resp = app.get('/backoffice/workflows/')
|
||||
resp = resp.click('Mail Templates')
|
||||
assert 'first mail template' in resp
|
||||
|
||||
|
||||
def test_mail_template_in_use(pub, mail_templates_option):
|
||||
Workflow.wipe()
|
||||
MailTemplate.wipe()
|
||||
workflow = Workflow(name='test mail template')
|
||||
st1 = workflow.add_status('Status1')
|
||||
item = SendmailWorkflowStatusItem()
|
||||
item.to = ['_receiver']
|
||||
item.subject = 'Foobar'
|
||||
item.body = 'Hello'
|
||||
st1.items.append(item)
|
||||
item.parent = st1
|
||||
workflow.store()
|
||||
|
||||
mail_template = MailTemplate(name='test mail template')
|
||||
mail_template.subject = 'test subject'
|
||||
mail_template.body = 'test body'
|
||||
mail_template.store()
|
||||
assert mail_template.is_in_use() is False
|
||||
|
||||
item.mail_template = mail_template.slug
|
||||
workflow.store()
|
||||
assert mail_template.is_in_use() is True
|
||||
|
||||
|
||||
def test_admin_workflow_edit(pub, superuser, mail_templates_option):
|
||||
Workflow.wipe()
|
||||
MailTemplate.wipe()
|
||||
mail_template = MailTemplate(name='test mail template')
|
||||
mail_template.subject = 'test subject'
|
||||
mail_template.body = 'test body'
|
||||
mail_template.store()
|
||||
|
||||
workflow = Workflow(name='test mail template')
|
||||
st1 = workflow.add_status('Status1')
|
||||
item = SendmailWorkflowStatusItem()
|
||||
item.to = ['_receiver']
|
||||
item.subject = 'Foobar'
|
||||
item.body = 'Hello'
|
||||
st1.items.append(item)
|
||||
item.parent = st1
|
||||
workflow.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/workflows/%s/' % workflow.id)
|
||||
resp = resp.click('Status1')
|
||||
resp = resp.click('Email')
|
||||
resp.form['mail_template'] = 'test-mail-template'
|
||||
resp = resp.form.submit('submit')
|
||||
|
||||
workflow = Workflow.get(workflow.id)
|
||||
assert workflow.possible_status[0].items[0].mail_template == 'test-mail-template'
|
||||
|
||||
|
||||
def test_workflow_send_mail_template(pub, superuser, mail_templates_option, emails):
|
||||
Workflow.wipe()
|
||||
MailTemplate.wipe()
|
||||
|
||||
mail_template = MailTemplate(name='test mail template')
|
||||
mail_template.subject = 'test subject'
|
||||
mail_template.body = 'test body'
|
||||
mail_template.store()
|
||||
|
||||
workflow = Workflow(name='test mail template')
|
||||
st1 = workflow.add_status('Status1')
|
||||
item = SendmailWorkflowStatusItem()
|
||||
item.to = 'xyz@localhost'
|
||||
item.subject = 'Foobar'
|
||||
item.body = 'Hello'
|
||||
item.mail_template = mail_template.slug
|
||||
st1.items.append(item)
|
||||
item.parent = st1
|
||||
workflow.store()
|
||||
|
||||
formdef = FormDef()
|
||||
formdef.name = 'baz'
|
||||
formdef.fields = []
|
||||
formdef.store()
|
||||
|
||||
formdata = formdef.data_class()()
|
||||
formdata.just_created()
|
||||
formdata.store()
|
||||
|
||||
item.perform(formdata)
|
||||
pub.get_request().response.process_after_jobs()
|
||||
assert emails.count() == 1
|
||||
assert emails.get('test subject')['email_rcpt'] == ['xyz@localhost']
|
||||
assert b'test body' in base64.decodestring(force_bytes(emails.get('test subject')['msg'].get_payload(0).get_payload()))
|
||||
|
||||
# check nothing is sent and an error is logged if the mail template is
|
||||
# missing
|
||||
emails.empty()
|
||||
LoggedError.wipe()
|
||||
MailTemplate.wipe()
|
||||
item.perform(formdata)
|
||||
pub.get_request().response.process_after_jobs()
|
||||
assert emails.count() == 0
|
||||
assert LoggedError.count() == 1
|
||||
logged_error = LoggedError.select()[0]
|
||||
assert logged_error.summary == 'reference to invalid mail template test-mail-template in status Status1'
|
|
@ -0,0 +1,170 @@
|
|||
# w.c.s. - web application for online forms
|
||||
# Copyright (C) 2005-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from quixote import get_response, redirect
|
||||
from quixote.directory import Directory
|
||||
from quixote.html import TemplateIO, htmltext
|
||||
|
||||
from wcs.qommon import _, errors, template
|
||||
from wcs.qommon.backoffice.menu import html_top
|
||||
from wcs.qommon.form import Form, HtmlWidget, StringWidget, TextWidget, ComputedExpressionWidget
|
||||
from wcs.mail_templates import MailTemplate
|
||||
|
||||
|
||||
class MailTemplatesDirectory(Directory):
|
||||
_q_exports = ['', 'new']
|
||||
do_not_call_in_templates = True
|
||||
|
||||
def _q_traverse(self, path):
|
||||
get_response().breadcrumb.append(('workflows/', _('Workflows')))
|
||||
get_response().breadcrumb.append(('mail-templates/', _('Mail Templates')))
|
||||
return super(MailTemplatesDirectory, self)._q_traverse(path)
|
||||
|
||||
def _q_lookup(self, component):
|
||||
return MailTemplatePage(component)
|
||||
|
||||
def _q_index(self):
|
||||
html_top('mail_templates', title=_('Mail Templates'))
|
||||
return template.QommonTemplateResponse(
|
||||
templates=['wcs/backoffice/mail-templates.html'],
|
||||
context={'view': self, 'mail_templates': MailTemplate.select(order_by='name')})
|
||||
|
||||
def new(self):
|
||||
form = Form(enctype='multipart/form-data')
|
||||
form.add(StringWidget, 'name', title=_('Name'), required=True, size=50)
|
||||
form.add_submit('submit', _('Add'))
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
if form.get_widget('cancel').parse():
|
||||
return redirect('.')
|
||||
|
||||
if form.is_submitted() and not form.has_errors():
|
||||
mail_template = MailTemplate(name=form.get_widget('name').parse())
|
||||
mail_template.store()
|
||||
return redirect('%s/edit' % mail_template.id)
|
||||
|
||||
get_response().breadcrumb.append(('new', _('New Mail Template')))
|
||||
html_top('mail_templates', title=_('New Mail Template'))
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<h2>%s</h2>') % _('New Mail Template')
|
||||
r += form.render()
|
||||
return r.getvalue()
|
||||
|
||||
|
||||
class MailTemplatePage(Directory):
|
||||
_q_exports = ['', 'edit', 'delete']
|
||||
do_not_call_in_templates = True
|
||||
|
||||
def __init__(self, mail_template_id):
|
||||
try:
|
||||
self.mail_template = MailTemplate.get(mail_template_id)
|
||||
except KeyError:
|
||||
raise errors.TraversalError()
|
||||
get_response().breadcrumb.append((mail_template_id + '/', self.mail_template.name))
|
||||
|
||||
def _q_index(self):
|
||||
html_top('mail_templates', title=self.mail_template.name)
|
||||
return template.QommonTemplateResponse(
|
||||
templates=['wcs/backoffice/mail-template.html'],
|
||||
context={'view': self, 'mail_template': self.mail_template})
|
||||
|
||||
def get_form(self):
|
||||
form = Form(enctype='multipart/form-data', advanced_label=_('Additional options'))
|
||||
form.add(StringWidget, 'name', title=_('Name'), required=True, size=30,
|
||||
value=self.mail_template.name)
|
||||
form.add(TextWidget, 'description', title=_('Description'),
|
||||
cols=80, rows=3,
|
||||
value=self.mail_template.description)
|
||||
form.add(StringWidget, 'subject', title=_('Subject'), required=True, size=40,
|
||||
value=self.mail_template.subject,
|
||||
validation_function=ComputedExpressionWidget.validate_template)
|
||||
form.add(TextWidget, 'body', title=_('Body'),
|
||||
value=self.mail_template.body, cols=80, rows=15, require=True,
|
||||
validation_function=ComputedExpressionWidget.validate_template)
|
||||
|
||||
if self.mail_template.slug and not self.mail_template.is_in_use():
|
||||
form.add(StringWidget, 'slug',
|
||||
value=self.mail_template.slug,
|
||||
title=_('Identifier'),
|
||||
required=True, advanced=True,
|
||||
)
|
||||
form.add_submit('submit', _('Submit'))
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
return form
|
||||
|
||||
def submit_form(self, form):
|
||||
name = form.get_widget('name').parse()
|
||||
slug_widget = form.get_widget('slug')
|
||||
if slug_widget:
|
||||
slug = form.get_widget('slug').parse()
|
||||
|
||||
for mail_template in MailTemplate.select():
|
||||
if mail_template.id == self.mail_template.id:
|
||||
continue
|
||||
if slug_widget and slug == mail_template.slug:
|
||||
slug_widget.set_error(_('This value is already used.'))
|
||||
if form.has_errors():
|
||||
raise ValueError()
|
||||
|
||||
self.mail_template.name = name
|
||||
self.mail_template.description = form.get_widget('description').parse()
|
||||
self.mail_template.subject = form.get_widget('subject').parse()
|
||||
self.mail_template.body = form.get_widget('body').parse()
|
||||
if slug_widget:
|
||||
self.mail_template.slug = slug
|
||||
self.mail_template.store()
|
||||
|
||||
def edit(self):
|
||||
form = self.get_form()
|
||||
if form.get_submit() == 'cancel':
|
||||
return redirect('.')
|
||||
|
||||
if form.get_submit() == 'submit' and not form.has_errors():
|
||||
try:
|
||||
self.submit_form(form)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
return redirect('.')
|
||||
|
||||
get_response().breadcrumb.append(('edit', _('Edit')))
|
||||
html_top('mail_templates', title=_('Edit Mail Template'))
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<h2>%s</h2>') % _('Edit Mail Template')
|
||||
r += form.render()
|
||||
return r.getvalue()
|
||||
|
||||
def delete(self):
|
||||
form = Form(enctype='multipart/form-data')
|
||||
if not self.mail_template.is_in_use():
|
||||
form.widgets.append(HtmlWidget('<p>%s</p>' % _(
|
||||
'You are about to irrevocably delete this mail template.')))
|
||||
form.add_submit('delete', _('Submit'))
|
||||
else:
|
||||
form.widgets.append(HtmlWidget('<p>%s</p>' % _(
|
||||
'This mail template is still used, it cannot be deleted.')))
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
if form.get_widget('cancel').parse():
|
||||
return redirect('.')
|
||||
if not form.is_submitted() or form.has_errors():
|
||||
get_response().breadcrumb.append(('delete', _('Delete')))
|
||||
html_top('mail_templates', title=_('Delete Mail Template'))
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<h2>%s %s</h2>') % (_('Deleting Mail Template:'), self.mail_template.name)
|
||||
r += form.render()
|
||||
return r.getvalue()
|
||||
else:
|
||||
self.mail_template.remove_self()
|
||||
return redirect('..')
|
|
@ -861,6 +861,7 @@ class SettingsDirectory(QommonSettingsDirectory):
|
|||
form.add(CheckboxWidget, 'categories', title = _('Categories'), value = True)
|
||||
form.add(CheckboxWidget, 'settings', title = _('Settings'), value = False)
|
||||
form.add(CheckboxWidget, 'datasources', title=_('Data sources'), value=True)
|
||||
form.add(CheckboxWidget, 'mail-templates', title=_('Mail templates'), value=True)
|
||||
form.add(CheckboxWidget, 'wscalls', title=_('Webservice calls'), value=True)
|
||||
form.add_submit('submit', _('Submit'))
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
|
@ -885,7 +886,7 @@ class SettingsDirectory(QommonSettingsDirectory):
|
|||
c = BytesIO()
|
||||
z = zipfile.ZipFile(c, 'w')
|
||||
for d in self.dirs:
|
||||
if d not in ('roles', 'categories', 'datasources', 'wscalls'):
|
||||
if d not in ('roles', 'categories', 'datasources', 'wscalls', 'mail-templates'):
|
||||
continue
|
||||
path = os.path.join(self.app_dir, d)
|
||||
if not os.path.exists(path):
|
||||
|
@ -928,7 +929,7 @@ class SettingsDirectory(QommonSettingsDirectory):
|
|||
|
||||
dirs = []
|
||||
for w in ('formdefs', 'carddefs', 'workflows', 'roles', 'categories',
|
||||
'datasources', 'wscalls'):
|
||||
'datasources', 'wscalls', 'mail-templates'):
|
||||
if form.get_widget(w) and form.get_widget(w).parse():
|
||||
dirs.append(w)
|
||||
if not dirs and not form.get_widget('settings').parse():
|
||||
|
@ -1017,6 +1018,8 @@ class SettingsDirectory(QommonSettingsDirectory):
|
|||
r += htmltext('<li>%s</li>') % _('Settings')
|
||||
if results['datasources']:
|
||||
r += htmltext('<li>%d %s</li>') % (results['datasources'], _('data sources'))
|
||||
if results['mail-templates']:
|
||||
r += htmltext('<li>%d %s</li>') % (results['mail-templates'], _('mail templates'))
|
||||
if results['wscalls']:
|
||||
r += htmltext('<li>%d %s</li>') % (results['wscalls'], _('webservice calls'))
|
||||
r += htmltext('</ul>')
|
||||
|
|
|
@ -46,6 +46,7 @@ from wcs.formdata import Evolution
|
|||
from .fields import FieldDefPage, FieldsDirectory
|
||||
from .data_sources import NamedDataSourcesDirectory
|
||||
from .logged_errors import LoggedErrorsDirectory
|
||||
from .mail_templates import MailTemplatesDirectory
|
||||
from wcs.backoffice.studio import StudioDirectory
|
||||
|
||||
|
||||
|
@ -1810,9 +1811,11 @@ class NamedDataSourcesDirectoryInWorkflows(NamedDataSourcesDirectory):
|
|||
|
||||
class WorkflowsDirectory(Directory):
|
||||
_q_exports = ['', 'new', ('import', 'p_import'),
|
||||
('data-sources', 'data_sources')]
|
||||
('data-sources', 'data_sources'),
|
||||
('mail-templates', 'mail_templates')]
|
||||
|
||||
data_sources = NamedDataSourcesDirectoryInWorkflows()
|
||||
mail_templates = MailTemplatesDirectory()
|
||||
|
||||
def html_top(self, title):
|
||||
return html_top('workflows', title)
|
||||
|
@ -1825,6 +1828,8 @@ class WorkflowsDirectory(Directory):
|
|||
r += htmltext('<div id="appbar">')
|
||||
r += htmltext('<h2>%s</h2>') % _('Workflows')
|
||||
r += htmltext('<span class="actions">')
|
||||
if get_publisher().has_site_option('mail-templates'):
|
||||
r += htmltext('<a href="mail-templates/">%s</a>') % _('Mail Templates')
|
||||
r += htmltext('<a href="data-sources/">%s</a>') % _('Data sources')
|
||||
r += htmltext('<a href="import" rel="popup">%s</a>') % _('Import')
|
||||
r += htmltext('<a class="new-item" rel="popup" href="new">%s</a>') % _('New Workflow')
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
# w.c.s. - web application for online forms
|
||||
# Copyright (C) 2005-2020 Entr'ouvert
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from wcs.qommon import misc, get_logger
|
||||
from wcs.qommon.xml_storage import XmlStorableObject
|
||||
|
||||
|
||||
class MailTemplate(XmlStorableObject):
|
||||
_names = 'mail-templates'
|
||||
_xml_tagname = 'mail-template'
|
||||
|
||||
name = None
|
||||
slug = None
|
||||
description = None
|
||||
subject = None
|
||||
body = None
|
||||
|
||||
# declarations for serialization
|
||||
XML_NODES = [
|
||||
('name', 'str'),
|
||||
('slug', 'str'),
|
||||
('description', 'str'),
|
||||
('subject', 'str'),
|
||||
('body', 'str'),
|
||||
]
|
||||
|
||||
def __init__(self, name=None):
|
||||
XmlStorableObject.__init__(self)
|
||||
self.name = name
|
||||
|
||||
def store(self):
|
||||
if self.slug is None:
|
||||
# set slug if it's not yet there
|
||||
self.slug = self.get_new_slug()
|
||||
super(MailTemplate, self).store()
|
||||
|
||||
def get_new_slug(self):
|
||||
new_slug = misc.simplify(self.name, space='-')
|
||||
base_new_slug = new_slug
|
||||
suffix_no = 0
|
||||
known_slugs = {x.slug: x.id for x in self.select(ignore_migration=True, ignore_errors=True)}
|
||||
while True:
|
||||
if not new_slug in known_slugs:
|
||||
break
|
||||
suffix_no += 1
|
||||
new_slug = '%s-%s' % (base_new_slug, suffix_no)
|
||||
return new_slug
|
||||
|
||||
def is_in_use(self):
|
||||
from wcs.workflows import Workflow
|
||||
for workflow in Workflow.select(ignore_errors=True, ignore_migration=True):
|
||||
for item in workflow.get_all_items():
|
||||
if item.key != 'sendmail':
|
||||
continue
|
||||
if item.mail_template == self.slug:
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def get_as_options_list(cls):
|
||||
options = []
|
||||
for mail_template in cls.select(order_by='name'):
|
||||
options.append((mail_template.slug, mail_template.name, mail_template.slug))
|
||||
return options
|
||||
|
||||
@classmethod
|
||||
def get_by_slug(cls, slug):
|
||||
objects = [x for x in cls.select() if x.slug == slug]
|
||||
if objects:
|
||||
return objects[0]
|
||||
get_logger().warn("mail template '%s' does not exist" % slug)
|
||||
return None
|
|
@ -160,7 +160,7 @@ class WcsPublisher(StubWcsPublisher):
|
|||
def import_zip(self, fd):
|
||||
z = zipfile.ZipFile(fd)
|
||||
results = {'formdefs': 0, 'carddefs': 0, 'workflows': 0, 'categories': 0, 'roles': 0,
|
||||
'settings': 0, 'datasources': 0, 'wscalls': 0}
|
||||
'settings': 0, 'datasources': 0, 'wscalls': 0, 'mail-templates': 0}
|
||||
|
||||
def _decode_list(data):
|
||||
rv = []
|
||||
|
|
|
@ -1878,3 +1878,8 @@ table#listing tr.checked td {
|
|||
width: 40%;
|
||||
min-width: 10em;
|
||||
}
|
||||
|
||||
div.mail-body {
|
||||
margin-top: 1em;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{% block appbar %}
|
||||
<div id="appbar">
|
||||
<h2>{% block appbar-title %}{% endblock %}</h2>
|
||||
<span class="actions">{% block appbar-actions %}{% endblock %}</span>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
{% extends "wcs/backoffice/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block appbar-title %}{% trans "Mail Template" %} - {{ mail_template.name }}{% endblock %}
|
||||
|
||||
{% block appbar-actions %}
|
||||
<a href="delete">{% trans "Delete" %}</a>
|
||||
<a href="edit">{% trans "Edit" %}</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if mail_template.description %}
|
||||
<div class="bo-block">{{ mail_template.description }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if mail_template.subject and mail_template.body %}
|
||||
<div class="bo-block">
|
||||
<div class="mail-subject"><strong>{% trans "Subject:" %} </strong>{{ mail_template.subject }}</div>
|
||||
<div class="mail-body">{{ mail_template.body }}</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="infonotice">{% trans "This mail template still needs to be configured." %}</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,22 @@
|
|||
{% extends "wcs/backoffice/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block appbar-title %}{% trans "Mail Templates" %}{% endblock %}
|
||||
|
||||
{% block appbar-actions %}
|
||||
<a rel="popup" href="new">{% trans "New mail template" %}</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if mail_templates %}
|
||||
<ul class="objects-list single-links">
|
||||
{% for mail_template in mail_templates %}
|
||||
<li><a href="{{ mail_template.id }}/">{{ mail_template.name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<div class="infonotice">
|
||||
{% trans "There are no mail templates defined." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -45,6 +45,7 @@ from .roles import Role, logged_users_role, get_user_roles
|
|||
from .fields import FileField
|
||||
from .formdef import FormDef
|
||||
from .formdata import Evolution
|
||||
from .mail_templates import MailTemplate
|
||||
|
||||
if not __name__.startswith('wcs.') and not __name__ == "__main__":
|
||||
raise ImportError('Import of workflows module must be absolute (import wcs.workflows)')
|
||||
|
@ -423,6 +424,14 @@ class Workflow(StorableObject):
|
|||
return self.backoffice_fields_formdef.fields or []
|
||||
return []
|
||||
|
||||
def get_all_items(self):
|
||||
for status in self.possible_status or []:
|
||||
for item in status.items or []:
|
||||
yield item
|
||||
for action in self.global_actions or []:
|
||||
for item in action.items or []:
|
||||
yield item
|
||||
|
||||
def add_global_action(self, name, id=None):
|
||||
if [x for x in self.global_actions if x.name == name]:
|
||||
raise DuplicateGlobalActionNameError()
|
||||
|
@ -2449,6 +2458,7 @@ class SendmailWorkflowStatusItem(WorkflowStatusItem):
|
|||
|
||||
to = []
|
||||
subject = None
|
||||
mail_template = None
|
||||
body = None
|
||||
custom_from = None
|
||||
attachments = None
|
||||
|
@ -2491,11 +2501,19 @@ class SendmailWorkflowStatusItem(WorkflowStatusItem):
|
|||
return _('not completed')
|
||||
|
||||
def get_parameters(self):
|
||||
return ('to', 'subject', 'body', 'attachments', 'custom_from', 'condition')
|
||||
return ('to', 'mail_template', 'subject', 'body', 'attachments', 'custom_from', 'condition')
|
||||
|
||||
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
|
||||
super(SendmailWorkflowStatusItem, self).add_parameters_widgets(
|
||||
form, parameters, prefix=prefix, formdef=formdef)
|
||||
subject_body_attrs = {}
|
||||
if 'subject' in parameters or 'body' in parameters:
|
||||
if get_publisher().has_site_option('mail-templates') and MailTemplate.count():
|
||||
subject_body_attrs = {
|
||||
'data-dynamic-display-value': '',
|
||||
'data-dynamic-display-child-of': '%smail_template' % prefix,
|
||||
}
|
||||
|
||||
if 'to' in parameters:
|
||||
form.add(WidgetList, '%sto' % prefix, title=_('To'),
|
||||
element_type=SingleSelectWidgetWithOther,
|
||||
|
@ -2507,11 +2525,21 @@ class SendmailWorkflowStatusItem(WorkflowStatusItem):
|
|||
if 'subject' in parameters:
|
||||
form.add(StringWidget, '%ssubject' % prefix, title=_('Subject'),
|
||||
validation_function=ComputedExpressionWidget.validate_template,
|
||||
value=self.subject, size=40)
|
||||
value=self.subject, size=40,
|
||||
attrs=subject_body_attrs)
|
||||
if 'mail_template' in parameters and get_publisher().has_site_option('mail-templates') and MailTemplate.count():
|
||||
form.add(SingleSelectWidget,
|
||||
'%smail_template' % prefix,
|
||||
title=_('Mail Template'),
|
||||
value=self.mail_template,
|
||||
options=[(None, '', '')] + MailTemplate.get_as_options_list(),
|
||||
attrs={'data-dynamic-display-parent': 'true'}
|
||||
)
|
||||
if 'body' in parameters:
|
||||
form.add(TextWidget, '%sbody' % prefix, title=_('Body'),
|
||||
value=self.body, cols=80, rows=10,
|
||||
validation_function=ComputedExpressionWidget.validate_template)
|
||||
validation_function=ComputedExpressionWidget.validate_template,
|
||||
attrs=subject_body_attrs)
|
||||
|
||||
if 'custom_from' in parameters:
|
||||
form.add(ComputedExpressionWidget, '%scustom_from' % prefix,
|
||||
|
@ -2524,16 +2552,30 @@ class SendmailWorkflowStatusItem(WorkflowStatusItem):
|
|||
def perform(self, formdata):
|
||||
if not self.to:
|
||||
return
|
||||
if not self.subject:
|
||||
return
|
||||
if not self.body:
|
||||
if not (self.subject and self.body) and not self.mail_template:
|
||||
return
|
||||
|
||||
url = formdata.get_url()
|
||||
body = self.body
|
||||
subject = self.subject
|
||||
if self.mail_template:
|
||||
mail_template = MailTemplate.get_by_slug(self.mail_template)
|
||||
if mail_template:
|
||||
body = mail_template.body
|
||||
subject = mail_template.subject
|
||||
else:
|
||||
from wcs.logged_errors import LoggedError
|
||||
message = _('reference to invalid mail template %(mail_template)s in status %(status)s') % {
|
||||
'status': self.parent.name,
|
||||
'mail_template': self.mail_template,
|
||||
}
|
||||
LoggedError.record(message, formdata=formdata, status_item=self)
|
||||
return
|
||||
|
||||
try:
|
||||
mail_body = template_on_formdata(formdata,
|
||||
self.compute(self.body, render=False),
|
||||
autoescape=self.body.startswith('<'))
|
||||
self.compute(body, render=False),
|
||||
autoescape=body.startswith('<'))
|
||||
except TemplateError as e:
|
||||
get_logger().error('error in template for email body [%s], '
|
||||
'mail could not be generated: %s' % (url, str(e)))
|
||||
|
@ -2541,7 +2583,7 @@ class SendmailWorkflowStatusItem(WorkflowStatusItem):
|
|||
|
||||
try:
|
||||
mail_subject = template_on_formdata(formdata,
|
||||
self.compute(self.subject, render=False),
|
||||
self.compute(subject, render=False),
|
||||
autoescape=False)
|
||||
except TemplateError as e:
|
||||
get_logger().error('error in template for email subject [%s], '
|
||||
|
|
Loading…
Reference in New Issue