0001-admin-add-export-import-on-single-mail-template-4609.patch
tests/test_mail_templates.py | ||
---|---|---|
1 | 1 |
# -*- coding: utf-8 -*- |
2 | 2 | |
3 | 3 |
import base64 |
4 | 4 |
import os |
5 | 5 |
import pytest |
6 |
from webtest import Upload |
|
7 |
import xml.etree.ElementTree as ET |
|
6 | 8 | |
7 | 9 |
from django.utils.encoding import force_bytes |
10 |
from django.utils.six import StringIO |
|
8 | 11 |
from quixote import cleanup |
9 | 12 |
from wcs.formdef import FormDef |
10 | 13 |
from wcs.fields import FileField |
11 | 14 |
from wcs.logged_errors import LoggedError |
12 | 15 |
from wcs.mail_templates import MailTemplate |
13 | 16 |
from wcs.workflows import Workflow, SendmailWorkflowStatusItem |
14 | 17 |
from wcs.qommon.http_request import HTTPRequest |
15 | 18 |
from wcs.qommon.form import PicklableUpload |
... | ... | |
62 | 65 |
if not pub.site_options.has_section('options'): |
63 | 66 |
pub.site_options.add_section('options') |
64 | 67 |
pub.site_options.set('options', 'mail-templates', 'true') |
65 | 68 |
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd: |
66 | 69 |
pub.site_options.write(fd) |
67 | 70 |
return pub |
68 | 71 | |
69 | 72 | |
73 |
@pytest.fixture |
|
74 |
def mail_template(): |
|
75 |
MailTemplate.wipe() |
|
76 |
mail_template = MailTemplate(name='test MT') |
|
77 |
mail_template.subject = 'test subject' |
|
78 |
mail_template.body = 'test body' |
|
79 |
mail_template.store() |
|
80 |
return mail_template |
|
81 | ||
82 | ||
70 | 83 |
def test_mail_templates_disabled(pub, superuser): |
71 | 84 |
if not pub.site_options.has_section('options'): |
72 | 85 |
pub.site_options.add_section('options') |
73 | 86 |
pub.site_options.set('options', 'mail-templates', 'false') |
74 | 87 |
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd: |
75 | 88 |
pub.site_options.write(fd) |
76 | 89 |
app = login(get_app(pub)) |
77 | 90 |
resp = app.get('/backoffice/workflows/') |
... | ... | |
290 | 303 |
emails.empty() |
291 | 304 |
item.attachments = ['form_var_file1_raw'] |
292 | 305 |
item.perform(formdata) |
293 | 306 |
pub.get_request().response.process_after_jobs() |
294 | 307 |
payloads = emails.get('test subject')['msg'].get_payload() |
295 | 308 |
assert len(payloads) == 3 |
296 | 309 |
assert payloads[1].get_content_type() == 'image/jpeg' |
297 | 310 |
assert payloads[2].get_content_type() == 'image/jpeg' |
311 | ||
312 | ||
313 |
def test_mail_templates_export(pub, superuser, mail_template): |
|
314 |
app = login(get_app(pub)) |
|
315 |
resp = app.get('/backoffice/workflows/mail-templates/1/') |
|
316 | ||
317 |
resp = resp.click(href='export') |
|
318 |
xml_export = resp.text |
|
319 | ||
320 |
ds = StringIO(xml_export) |
|
321 |
mail_template2 = MailTemplate.import_from_xml(ds) |
|
322 |
assert mail_template2.name == 'test MT' |
|
323 | ||
324 | ||
325 |
def test_mail_templates_import(pub, superuser, mail_template): |
|
326 |
mail_template.slug = 'foobar' |
|
327 |
mail_template.store() |
|
328 |
mail_template_xml = ET.tostring(mail_template.export_to_xml(include_id=True)) |
|
329 |
MailTemplate.wipe() |
|
330 |
assert MailTemplate.count() == 0 |
|
331 | ||
332 |
app = login(get_app(pub)) |
|
333 |
resp = app.get('/backoffice/workflows/mail-templates/') |
|
334 |
resp = resp.click(href='import') |
|
335 |
resp.forms[0]['file'] = Upload('mail_template.wcs', mail_template_xml) |
|
336 |
resp = resp.forms[0].submit() |
|
337 |
assert MailTemplate.count() == 1 |
|
338 |
assert set([wc.slug for wc in MailTemplate.select()]) == set(['foobar']) |
|
339 | ||
340 |
# check slug |
|
341 |
resp = app.get('/backoffice/workflows/mail-templates/') |
|
342 |
resp = resp.click(href='import') |
|
343 |
resp.forms[0]['file'] = Upload('mail_template.wcs', mail_template_xml) |
|
344 |
resp = resp.forms[0].submit() |
|
345 |
assert MailTemplate.count() == 2 |
|
346 |
assert set([wc.slug for wc in MailTemplate.select()]) == set(['foobar', 'test-mt']) |
|
347 |
resp = app.get('/backoffice/workflows/mail-templates/') |
|
348 |
resp = resp.click(href='import') |
|
349 |
resp.forms[0]['file'] = Upload('mail_template.wcs', mail_template_xml) |
|
350 |
resp = resp.forms[0].submit() |
|
351 |
assert MailTemplate.count() == 3 |
|
352 |
assert set([wc.slug for wc in MailTemplate.select()]) == set(['foobar', 'test-mt', 'test-mt-1']) |
|
353 | ||
354 |
# import an invalid file |
|
355 |
resp = app.get('/backoffice/workflows/mail-templates/') |
|
356 |
resp = resp.click(href='import') |
|
357 |
resp.form['file'] = Upload('mail_template.wcs', b'garbage') |
|
358 |
resp = resp.form.submit() |
|
359 |
assert 'Invalid File' in resp.text |
wcs/admin/mail_templates.py | ||
---|---|---|
9 | 9 |
# This program is distributed in the hope that it will be useful, |
10 | 10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | 11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | 12 |
# GNU General Public License for more details. |
13 | 13 |
# |
14 | 14 |
# You should have received a copy of the GNU General Public License |
15 | 15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
import xml.etree.ElementTree as ET |
|
18 | ||
17 | 19 |
from quixote import get_publisher, get_response, redirect |
18 | 20 |
from quixote.directory import Directory |
19 | 21 |
from quixote.html import TemplateIO, htmltext |
20 | 22 | |
21 |
from wcs.qommon import _, errors, template |
|
23 |
from wcs.qommon import _, errors, force_str, misc, template
|
|
22 | 24 |
from wcs.qommon.backoffice.menu import html_top |
23 | 25 |
from wcs.qommon.form import (Form, HtmlWidget, StringWidget, TextWidget, |
24 |
ComputedExpressionWidget, WidgetList) |
|
26 |
ComputedExpressionWidget, WidgetList, FileWidget, get_session)
|
|
25 | 27 |
from wcs.mail_templates import MailTemplate |
26 | 28 | |
27 | 29 | |
28 | 30 |
class MailTemplatesDirectory(Directory): |
29 |
_q_exports = ['', 'new'] |
|
31 |
_q_exports = ['', 'new', ('import', 'p_import')]
|
|
30 | 32 |
do_not_call_in_templates = True |
31 | 33 | |
32 | 34 |
def _q_traverse(self, path): |
33 | 35 |
get_response().breadcrumb.append(('mail-templates/', _('Mail Templates'))) |
34 | 36 |
return super(MailTemplatesDirectory, self)._q_traverse(path) |
35 | 37 | |
36 | 38 |
def _q_lookup(self, component): |
37 | 39 |
return MailTemplatePage(component) |
... | ... | |
57 | 59 | |
58 | 60 |
get_response().breadcrumb.append(('new', _('New Mail Template'))) |
59 | 61 |
html_top('mail_templates', title=_('New Mail Template')) |
60 | 62 |
r = TemplateIO(html=True) |
61 | 63 |
r += htmltext('<h2>%s</h2>') % _('New Mail Template') |
62 | 64 |
r += form.render() |
63 | 65 |
return r.getvalue() |
64 | 66 | |
67 |
def p_import(self): |
|
68 |
form = Form(enctype='multipart/form-data') |
|
69 |
import_title = _('Import Mail Template call') |
|
70 | ||
71 |
form.add(FileWidget, 'file', title=_('File'), required=True) |
|
72 |
form.add_submit('submit', import_title) |
|
73 |
form.add_submit('cancel', _('Cancel')) |
|
74 | ||
75 |
if form.get_submit() == 'cancel': |
|
76 |
return redirect('.') |
|
77 | ||
78 |
if form.is_submitted() and not form.has_errors(): |
|
79 |
try: |
|
80 |
return self.import_submit(form) |
|
81 |
except ValueError: |
|
82 |
pass |
|
83 | ||
84 |
get_response().breadcrumb.append(('import', _('Import'))) |
|
85 |
html_top('mail_templates', title=import_title) |
|
86 |
r = TemplateIO(html=True) |
|
87 |
r += htmltext('<h2>%s</h2>') % import_title |
|
88 |
r += htmltext('<p>%s</p>') % _( |
|
89 |
'You can install a new mail template by uploading a file.') |
|
90 |
r += form.render() |
|
91 |
return r.getvalue() |
|
92 | ||
93 |
def import_submit(self, form): |
|
94 |
fp = form.get_widget('file').parse().fp |
|
95 | ||
96 |
error = False |
|
97 |
try: |
|
98 |
mail_template = MailTemplate.import_from_xml(fp) |
|
99 |
get_session().message = ( |
|
100 |
'info', _('This mail template has been successfully imported.')) |
|
101 |
except ValueError: |
|
102 |
error = True |
|
103 | ||
104 |
if error: |
|
105 |
form.set_error('file', _('Invalid File')) |
|
106 |
raise ValueError() |
|
107 | ||
108 |
# check slug unicity |
|
109 |
known_slugs = { |
|
110 |
x.slug: x.id for x in MailTemplate.select(ignore_migration=True, ignore_errors=True)} |
|
111 |
if mail_template.slug in known_slugs: |
|
112 |
mail_template.slug = None # a new one will be set in .store() |
|
113 |
mail_template.store() |
|
114 |
return redirect('%s/' % mail_template.id) |
|
115 | ||
65 | 116 | |
66 | 117 |
class MailTemplatePage(Directory): |
67 |
_q_exports = ['', 'edit', 'delete'] |
|
118 |
_q_exports = ['', 'edit', 'delete', 'export', ]
|
|
68 | 119 |
do_not_call_in_templates = True |
69 | 120 | |
70 | 121 |
def __init__(self, mail_template_id): |
71 | 122 |
try: |
72 | 123 |
self.mail_template = MailTemplate.get(mail_template_id) |
73 | 124 |
except KeyError: |
74 | 125 |
raise errors.TraversalError() |
75 | 126 |
get_response().breadcrumb.append((mail_template_id + '/', self.mail_template.name)) |
76 | 127 | |
128 |
def get_sidebar(self): |
|
129 |
r = TemplateIO(html=True) |
|
130 |
r += htmltext('<ul id="sidebar-actions">') |
|
131 |
r += htmltext('<li><a href="export">%s</a></li>') % _('Export') |
|
132 |
r += htmltext('<li><a href="delete" rel="popup">%s</a></li>') % _('Delete') |
|
133 |
r += htmltext('</ul>') |
|
134 |
return r.getvalue() |
|
135 | ||
77 | 136 |
def _q_index(self): |
78 | 137 |
html_top('mail_templates', title=self.mail_template.name) |
138 |
get_response().filter['sidebar'] = self.get_sidebar() |
|
79 | 139 |
return template.QommonTemplateResponse( |
80 | 140 |
templates=['wcs/backoffice/mail-template.html'], |
81 | 141 |
context={'view': self, 'mail_template': self.mail_template}) |
82 | 142 | |
83 | 143 |
def get_form(self): |
84 | 144 |
form = Form(enctype='multipart/form-data', advanced_label=_('Additional options')) |
85 | 145 |
form.add(StringWidget, 'name', title=_('Name'), required=True, size=30, |
86 | 146 |
value=self.mail_template.name) |
... | ... | |
175 | 235 |
html_top('mail_templates', title=_('Delete Mail Template')) |
176 | 236 |
r = TemplateIO(html=True) |
177 | 237 |
r += htmltext('<h2>%s %s</h2>') % (_('Deleting Mail Template:'), self.mail_template.name) |
178 | 238 |
r += form.render() |
179 | 239 |
return r.getvalue() |
180 | 240 |
else: |
181 | 241 |
self.mail_template.remove_self() |
182 | 242 |
return redirect('..') |
243 | ||
244 |
def export(self): |
|
245 |
x = self.mail_template.export_to_xml(include_id=True) |
|
246 |
misc.indent_xml(x) |
|
247 |
response = get_response() |
|
248 |
response.set_content_type('application/x-wcs-mail-template') |
|
249 |
response.set_header( |
|
250 |
'content-disposition', |
|
251 |
'attachment; filename=mail-template-%s.wcs' % self.mail_template.slug) |
|
252 |
return '<?xml version="1.0"?>\n' + force_str(ET.tostring(x)) |
wcs/templates/wcs/backoffice/mail-template.html | ||
---|---|---|
1 | 1 |
{% extends "wcs/backoffice/base.html" %} |
2 | 2 |
{% load i18n %} |
3 | 3 | |
4 | 4 |
{% block appbar-title %}{% trans "Mail Template" %} - {{ mail_template.name }}{% endblock %} |
5 | 5 | |
6 | 6 |
{% block appbar-actions %} |
7 |
<a href="delete">{% trans "Delete" %}</a> |
|
8 | 7 |
<a href="edit">{% trans "Edit" %}</a> |
9 | 8 |
{% endblock %} |
10 | 9 | |
11 | 10 |
{% block content %} |
12 | 11 |
{% if mail_template.description %} |
13 | 12 |
<div class="bo-block">{{ mail_template.description }}</div> |
14 | 13 |
{% endif %} |
15 | 14 |
wcs/templates/wcs/backoffice/mail-templates.html | ||
---|---|---|
1 | 1 |
{% extends "wcs/backoffice/base.html" %} |
2 | 2 |
{% load i18n %} |
3 | 3 | |
4 | 4 |
{% block appbar-title %}{% trans "Mail Templates" %}{% endblock %} |
5 | 5 | |
6 | 6 |
{% block appbar-actions %} |
7 |
<a rel="popup" href="import">{% trans "Import" %}</a> |
|
7 | 8 |
<a rel="popup" href="new">{% trans "New mail template" %}</a> |
8 | 9 |
{% endblock %} |
9 | 10 | |
10 | 11 |
{% block content %} |
11 | 12 |
{% if mail_templates %} |
12 | 13 |
<ul class="objects-list single-links"> |
13 | 14 |
{% for mail_template in mail_templates %} |
14 | 15 |
<li><a href="{{ mail_template.id }}/">{{ mail_template.name }}</a></li> |
15 |
- |