use Django/ezt templates on computed expressions (#19442)
This commit is contained in:
parent
f626c45cbb
commit
7d92cf296e
|
@ -1952,15 +1952,25 @@ def test_workflows_edit_email_action(pub):
|
|||
resp = resp.form.submit('submit')
|
||||
assert resp.location
|
||||
|
||||
resp = app.get(item_url)
|
||||
resp.form[field] = 'Hello {% if world %}{{ world }}{% else %}.'
|
||||
resp = resp.form.submit('submit')
|
||||
assert 'syntax error in Django template' in resp.body and 'Unclosed tag' in resp.body
|
||||
|
||||
resp = app.get(item_url)
|
||||
resp.form[field] = 'Hello {% if world %}{{ world }}{% else %}.{% endif %}{% endif %}'
|
||||
resp = resp.form.submit('submit')
|
||||
assert 'syntax error in Django template' in resp.body and 'Invalid block tag' in resp.body
|
||||
|
||||
resp = app.get(item_url)
|
||||
resp.form[field] = 'Hello [if-any world][world][else].'
|
||||
resp = resp.form.submit('submit')
|
||||
assert 'error in template' in resp.body and 'unclosed block' in resp.body
|
||||
assert 'syntax error in ezt template' in resp.body and 'unclosed block' in resp.body
|
||||
|
||||
resp = app.get(item_url)
|
||||
resp.form[field] = 'Hello [if-any world][world][else].[end] [end]'
|
||||
resp = resp.form.submit('submit')
|
||||
assert 'error in template' in resp.body and 'unmatched [end]' in resp.body
|
||||
assert 'syntax error in ezt template' in resp.body and 'unmatched [end]' in resp.body
|
||||
|
||||
# attachments without backoffice fields: python expressions
|
||||
resp = app.get(item_url)
|
||||
|
@ -2203,9 +2213,27 @@ def test_workflows_display_action_ezt_validation(pub):
|
|||
|
||||
resp = app.get('/backoffice/workflows/%s/status/%s/items/1/' % (
|
||||
workflow.id, baz_status.id))
|
||||
resp.form['message'] = '[is test][end]' # invalid ezt
|
||||
resp.form['message'] = '{% if test %}test{% endif %}' # valid Django
|
||||
resp = resp.form.submit('submit')
|
||||
assert 'error in template' in resp.body
|
||||
assert Workflow.get(workflow.id).possible_status[0].items[0].message == '{% if test %}test{% endif %}'
|
||||
|
||||
resp = app.get('/backoffice/workflows/%s/status/%s/items/1/' % (
|
||||
workflow.id, baz_status.id))
|
||||
resp.form['message'] = '{% if test %}test{% end %}' # invalid Django
|
||||
resp = resp.form.submit('submit')
|
||||
assert 'syntax error in Django template' in resp.body
|
||||
|
||||
resp = app.get('/backoffice/workflows/%s/status/%s/items/1/' % (
|
||||
workflow.id, baz_status.id))
|
||||
resp.form['message'] = '[if-any test]test[end]' # valid ezt
|
||||
resp = resp.form.submit('submit')
|
||||
assert Workflow.get(workflow.id).possible_status[0].items[0].message == '[if-any test]test[end]'
|
||||
|
||||
resp = app.get('/backoffice/workflows/%s/status/%s/items/1/' % (
|
||||
workflow.id, baz_status.id))
|
||||
resp.form['message'] = '[is test][end]' # invalid ezt
|
||||
resp = resp.form.submit('submit')
|
||||
assert 'syntax error in ezt template' in resp.body
|
||||
|
||||
def test_workflows_delete_action(pub):
|
||||
create_superuser(pub)
|
||||
|
|
|
@ -454,6 +454,15 @@ def test_computed_expression_widget():
|
|||
assert widget.has_error()
|
||||
assert widget.get_error().startswith('syntax error')
|
||||
|
||||
widget = ComputedExpressionWidget('test')
|
||||
mock_form_submission(req, widget, {'test': '{{ form_var_xxx }}'})
|
||||
assert not widget.has_error()
|
||||
|
||||
widget = ComputedExpressionWidget('test')
|
||||
mock_form_submission(req, widget, {'test': '{% if True %}'})
|
||||
assert widget.has_error()
|
||||
assert widget.get_error().startswith('syntax error in Django template')
|
||||
|
||||
widget = ComputedExpressionWidget('test')
|
||||
mock_form_submission(req, widget, {'test': '[form_var_xxx]'})
|
||||
assert not widget.has_error()
|
||||
|
@ -461,7 +470,7 @@ def test_computed_expression_widget():
|
|||
widget = ComputedExpressionWidget('test')
|
||||
mock_form_submission(req, widget, {'test': '[end]'})
|
||||
assert widget.has_error()
|
||||
assert widget.get_error().startswith('error in template')
|
||||
assert widget.get_error().startswith('syntax error in ezt template')
|
||||
|
||||
def test_wcsextrastringwidget():
|
||||
widget = WcsExtraStringWidget('test', value='foo', required=True)
|
||||
|
|
|
@ -130,10 +130,18 @@ def test_variable_compute(pub):
|
|||
# straight string
|
||||
assert item.compute('blah') == 'blah'
|
||||
|
||||
# django template
|
||||
assert item.compute('{{ form_var_foo }}') == 'hello'
|
||||
assert item.compute('{{ form_var_foo }}', render=False) == '{{ form_var_foo }}'
|
||||
assert item.compute('{% if form_var_foo %}its here{% endif %}') == 'its here'
|
||||
assert item.compute('{% if form_var_foo %}') == '{% if form_var_foo %}'
|
||||
with pytest.raises(Exception):
|
||||
item.compute('{% if form_var_foo %}', raises=True)
|
||||
|
||||
# ezt string
|
||||
assert item.compute('[form_var_foo]') == 'hello'
|
||||
# ezt string, but not ezt asked
|
||||
assert item.compute('[form_var_foo]', do_ezt=False) == '[form_var_foo]'
|
||||
assert item.compute('[form_var_foo]', render=False) == '[form_var_foo]'
|
||||
# ezt string, with an error
|
||||
assert item.compute('[end]', raises=False) == '[end]'
|
||||
with pytest.raises(Exception):
|
||||
|
@ -147,9 +155,13 @@ def test_variable_compute(pub):
|
|||
item.compute('=1/0', raises=True)
|
||||
|
||||
# with context
|
||||
assert item.compute('{{ form_var_foo }} {{ bar }}', context={'bar': 'world'}) == 'hello world'
|
||||
assert item.compute('[form_var_foo] [bar]', context={'bar': 'world'}) == 'hello world'
|
||||
assert item.compute('=form_var_foo + " " + bar', context={'bar': 'world'}) == 'hello world'
|
||||
|
||||
# django wins
|
||||
assert item.compute('{{ form_var_foo }} [bar]', context={'bar': 'world'}) == 'hello [bar]'
|
||||
|
||||
def test_variable_compute_dates(pub):
|
||||
FormDef.wipe()
|
||||
formdef = FormDef()
|
||||
|
@ -908,14 +920,16 @@ def test_webservice_call(pub):
|
|||
item = WebserviceCallStatusItem()
|
||||
item.url = 'http://remote.example.net'
|
||||
item.post = False
|
||||
item.post_data = {'str': 'abcd', 'one': '=1',
|
||||
item.post_data = {'str': 'abcd', 'one': '=1', 'django': '{{ form_number }}',
|
||||
'evalme': '=form_number', 'error':'=1=3'}
|
||||
pub.substitutions.feed(formdata)
|
||||
item.perform(formdata)
|
||||
assert http_requests.get_last('url') == 'http://remote.example.net'
|
||||
assert http_requests.get_last('method') == 'POST'
|
||||
payload = json.loads(http_requests.get_last('body'))
|
||||
assert payload == {'one': 1, 'str': 'abcd', 'evalme': formdata.get_display_id()}
|
||||
assert payload == {'one': 1, 'str': 'abcd',
|
||||
'evalme': formdata.get_display_id(),
|
||||
'django': formdata.get_display_id()}
|
||||
|
||||
item = WebserviceCallStatusItem()
|
||||
item.url = 'http://remote.example.net'
|
||||
|
@ -949,6 +963,20 @@ def test_webservice_call(pub):
|
|||
assert 'signature=' in http_requests.get_last('url')
|
||||
assert http_requests.get_last('method') == 'GET'
|
||||
|
||||
item = WebserviceCallStatusItem()
|
||||
item.url = 'http://remote.example.net'
|
||||
item.post = False
|
||||
item.request_signature_key = '{{ doesntexist }}'
|
||||
item.perform(formdata)
|
||||
assert not 'signature=' in http_requests.get_last('url')
|
||||
|
||||
item = WebserviceCallStatusItem()
|
||||
item.url = 'http://remote.example.net'
|
||||
item.post = False
|
||||
item.request_signature_key = '{{ empty }}'
|
||||
item.perform(formdata)
|
||||
assert not 'signature=' in http_requests.get_last('url')
|
||||
|
||||
item = WebserviceCallStatusItem()
|
||||
item.url = 'http://remote.example.net'
|
||||
item.post = False
|
||||
|
@ -956,6 +984,13 @@ def test_webservice_call(pub):
|
|||
item.perform(formdata)
|
||||
assert not 'signature=' in http_requests.get_last('url')
|
||||
|
||||
item = WebserviceCallStatusItem()
|
||||
item.url = 'http://remote.example.net'
|
||||
item.post = False
|
||||
item.request_signature_key = '{{ bar }}'
|
||||
item.perform(formdata)
|
||||
assert 'signature=' in http_requests.get_last('url')
|
||||
|
||||
item = WebserviceCallStatusItem()
|
||||
item.url = 'http://remote.example.net'
|
||||
item.post = False
|
||||
|
@ -1111,6 +1146,8 @@ def test_webservice_call(pub):
|
|||
'str': 'abcd',
|
||||
'one': '=1',
|
||||
'evalme': '=form_number',
|
||||
'django': '{{ form_number }}',
|
||||
'ezt': '[form_number]',
|
||||
'error': '=1=3',
|
||||
'in_url': '2',
|
||||
}
|
||||
|
@ -1118,10 +1155,12 @@ def test_webservice_call(pub):
|
|||
item.perform(formdata)
|
||||
assert http_requests.get_last('method') == 'GET'
|
||||
qs = urlparse.parse_qs(http_requests.get_last('url').split('?')[1])
|
||||
assert set(qs.keys()) == set(['in_url', 'str', 'one', 'evalme'])
|
||||
assert set(qs.keys()) == set(['in_url', 'str', 'one', 'evalme', 'django', 'ezt'])
|
||||
assert qs['in_url'] == ['1', '2']
|
||||
assert qs['one'] == ['1']
|
||||
assert qs['evalme'] == [formdata.get_display_id()]
|
||||
assert qs['django'] == [formdata.get_display_id()]
|
||||
assert qs['ezt'] == [formdata.get_display_id()]
|
||||
assert qs['str'] == ['abcd']
|
||||
|
||||
def test_webservice_waitpoint(pub):
|
||||
|
@ -2435,6 +2474,13 @@ def test_profile(pub):
|
|||
item.perform(formdata)
|
||||
assert pub.user_class.get(user.id).name == 'Plop'
|
||||
|
||||
item.fields = [{'field_id': '__name', 'value': 'dj{{form_var_foo}}'}]
|
||||
item.perform(formdata)
|
||||
assert pub.user_class.get(user.id).name == 'djPlop'
|
||||
item.fields = [{'field_id': '__name', 'value': 'ezt[form_var_foo]'}]
|
||||
item.perform(formdata)
|
||||
assert pub.user_class.get(user.id).name == 'eztPlop'
|
||||
|
||||
from wcs.admin.settings import UserFieldsFormDef
|
||||
formdef = UserFieldsFormDef(pub)
|
||||
formdef.fields = [StringField(id='3', label='test', type='string', varname='plop')]
|
||||
|
@ -2472,16 +2518,26 @@ def test_set_backoffice_field(two_pubs):
|
|||
|
||||
item = SetBackofficeFieldsWorkflowStatusItem()
|
||||
item.parent = st1
|
||||
item.perform(formdata)
|
||||
|
||||
item = SetBackofficeFieldsWorkflowStatusItem()
|
||||
item.parent = st1
|
||||
item.perform(formdata)
|
||||
formdata = formdef.data_class().get(formdata.id)
|
||||
assert formdata.data.get('bo1') == None
|
||||
|
||||
item.fields = [{'field_id': 'bo1', 'value': '=form_var_string'}]
|
||||
item.perform(formdata)
|
||||
|
||||
formdata = formdef.data_class().get(formdata.id)
|
||||
assert formdata.data['bo1'] == 'HELLO'
|
||||
|
||||
item.fields = [{'field_id': 'bo1', 'value': '{{ form_var_string }} WORLD'}]
|
||||
item.perform(formdata)
|
||||
formdata = formdef.data_class().get(formdata.id)
|
||||
assert formdata.data['bo1'] == 'HELLO WORLD'
|
||||
|
||||
item.fields = [{'field_id': 'bo1', 'value': '[form_var_string] GOODBYE'}]
|
||||
item.perform(formdata)
|
||||
formdata = formdef.data_class().get(formdata.id)
|
||||
assert formdata.data['bo1'] == 'HELLO GOODBYE'
|
||||
|
||||
def test_set_backoffice_field_file(two_pubs):
|
||||
Workflow.wipe()
|
||||
FormDef.wipe()
|
||||
|
@ -2753,6 +2809,16 @@ def test_set_backoffice_field_date(two_pubs):
|
|||
formdata = formdef.data_class().get(formdata.id)
|
||||
assert datetime.date(*formdata.data['bo1'][:3]) == datetime.date.today()
|
||||
|
||||
formdata.data['bo1'] = None
|
||||
formdata.store()
|
||||
item = SetBackofficeFieldsWorkflowStatusItem()
|
||||
item.parent = st1
|
||||
item.fields = [{'field_id': 'bo1', 'value': '{% now "j/n/Y" %}'}]
|
||||
item.perform(formdata)
|
||||
|
||||
formdata = formdef.data_class().get(formdata.id)
|
||||
assert datetime.date(*formdata.data['bo1'][:3]) == datetime.date.today()
|
||||
|
||||
item = SetBackofficeFieldsWorkflowStatusItem()
|
||||
item.parent = st1
|
||||
item.fields = [{'field_id': 'bo1', 'value': "23/3/2017"}]
|
||||
|
|
|
@ -65,7 +65,7 @@ from quixote.util import randbytes
|
|||
from django.template import RequestContext
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from .template import render as render_template
|
||||
from .template import render as render_template, Template, TemplateError
|
||||
from wcs.portfolio import has_portfolio
|
||||
|
||||
from qommon import _, ngettext
|
||||
|
@ -2228,28 +2228,11 @@ class ComputedExpressionWidget(StringWidget):
|
|||
return StringWidget.render_content(self)
|
||||
|
||||
@classmethod
|
||||
def validate_ezt(cls, template):
|
||||
processor = ezt.Template(compress_whitespace=False)
|
||||
def validate_template(cls, template):
|
||||
try:
|
||||
processor.parse(template or '')
|
||||
except ezt.EZTException as e:
|
||||
parts = []
|
||||
parts.append({
|
||||
ezt.ArgCountSyntaxError: _('wrong number of arguments'),
|
||||
ezt.UnknownReference: _('unknown reference'),
|
||||
ezt.NeedSequenceError: _('sequence required'),
|
||||
ezt.UnclosedBlocksError: _('unclosed block'),
|
||||
ezt.UnmatchedEndError: _('unmatched [end]'),
|
||||
ezt.UnmatchedElseError: _('unmatched [else]'),
|
||||
ezt.BaseUnavailableError: _('unavailable base location'),
|
||||
ezt.BadFormatConstantError: _('bad format constant'),
|
||||
ezt.UnknownFormatConstantError: _('unknown format constant'),
|
||||
}.get(e.__class__))
|
||||
if e.line is not None:
|
||||
parts.append(_('at line %(line)d and column %(column)d') % {
|
||||
'line': e.line+1,
|
||||
'column': e.column+1})
|
||||
raise ValidationError(_('error in template (%s)') % ' '.join(parts))
|
||||
Template(template, raises=True)
|
||||
except TemplateError as e:
|
||||
raise ValidationError('%s' % e)
|
||||
|
||||
@classmethod
|
||||
def validate(cls, expression):
|
||||
|
@ -2259,9 +2242,9 @@ class ComputedExpressionWidget(StringWidget):
|
|||
try:
|
||||
compile(expression[1:], '<string>', 'eval')
|
||||
except SyntaxError as e:
|
||||
raise ValidationError(_('syntax error (%s)') % e)
|
||||
raise ValidationError(_('syntax error in Python expression: %s') % e)
|
||||
else:
|
||||
cls.validate_ezt(expression)
|
||||
cls.validate_template(expression)
|
||||
|
||||
def _parse(self, request):
|
||||
StringWidget._parse(self, request)
|
||||
|
|
|
@ -36,6 +36,7 @@ from qommon.humantime import seconds2humanduration
|
|||
from qommon import emails, get_cfg, get_logger
|
||||
from quixote.html import htmltext
|
||||
import qommon.errors
|
||||
from qommon.template import Template, TemplateError
|
||||
|
||||
from wcs.roles import Role, logged_users_role, get_user_roles
|
||||
from wcs.fields import FileField
|
||||
|
@ -1609,11 +1610,11 @@ class WorkflowStatusItem(XmlSerialisable):
|
|||
setattr(self, f, value)
|
||||
|
||||
@classmethod
|
||||
def compute(cls, var, do_ezt=True, raises=False, context=None):
|
||||
def compute(cls, var, render=True, raises=False, context=None):
|
||||
if not isinstance(var, basestring):
|
||||
return var
|
||||
|
||||
if not var.startswith('=') and not do_ezt:
|
||||
if not var.startswith('=') and not render:
|
||||
return var
|
||||
|
||||
vars = get_publisher().substitutions.get_context_variables()
|
||||
|
@ -1621,12 +1622,8 @@ class WorkflowStatusItem(XmlSerialisable):
|
|||
|
||||
if not var.startswith('='):
|
||||
try:
|
||||
processor = ezt.Template(compress_whitespace=False)
|
||||
processor.parse(var)
|
||||
fd = StringIO()
|
||||
processor.generate(fd, vars)
|
||||
return fd.getvalue()
|
||||
except ezt.EZTException:
|
||||
return Template(var, raises=raises).render(vars)
|
||||
except TemplateError:
|
||||
if raises:
|
||||
raise
|
||||
return var
|
||||
|
@ -2117,12 +2114,12 @@ class SendmailWorkflowStatusItem(WorkflowStatusItem):
|
|||
self.get_list_of_roles(include_logged_in_users=False)})
|
||||
if 'subject' in parameters:
|
||||
form.add(StringWidget, '%ssubject' % prefix, title=_('Subject'),
|
||||
validation_function=ComputedExpressionWidget.validate_ezt,
|
||||
validation_function=ComputedExpressionWidget.validate_template,
|
||||
value=self.subject, size=40)
|
||||
if 'body' in parameters:
|
||||
form.add(TextWidget, '%sbody' % prefix, title=_('Body'),
|
||||
value=self.body, cols=80, rows=10,
|
||||
validation_function=ComputedExpressionWidget.validate_ezt,
|
||||
validation_function=ComputedExpressionWidget.validate_template,
|
||||
hint=_('Available variables: url, url_status, details, name, number, comment, field_NAME'))
|
||||
|
||||
if 'attachments' in parameters:
|
||||
|
@ -2160,13 +2157,13 @@ class SendmailWorkflowStatusItem(WorkflowStatusItem):
|
|||
|
||||
url = formdata.get_url()
|
||||
try:
|
||||
mail_body = template_on_formdata(formdata, self.compute(self.body, do_ezt=False))
|
||||
mail_body = template_on_formdata(formdata, self.compute(self.body, render=False))
|
||||
except ezt.EZTException:
|
||||
get_logger().error('error in template for email body [%s], mail could not be generated' % url)
|
||||
return
|
||||
|
||||
try:
|
||||
mail_subject = template_on_formdata(formdata, self.compute(self.subject, do_ezt=False))
|
||||
mail_subject = template_on_formdata(formdata, self.compute(self.subject, render=False))
|
||||
except ezt.EZTException:
|
||||
get_logger().error('error in template for email subject [%s], mail could not be generated' % url)
|
||||
return
|
||||
|
@ -2361,7 +2358,7 @@ class SendSMSWorkflowStatusItem(WorkflowStatusItem):
|
|||
return
|
||||
|
||||
try:
|
||||
sms_body = template_on_formdata(formdata, self.compute(self.body, do_ezt=False))
|
||||
sms_body = template_on_formdata(formdata, self.compute(self.body, render=False))
|
||||
except ezt.EZTException:
|
||||
url = formdata.get_url()
|
||||
get_logger().error('error in template for sms [%s], sms could not be generated' % url)
|
||||
|
@ -2436,7 +2433,7 @@ class DisplayMessageWorkflowStatusItem(WorkflowStatusItem):
|
|||
if 'message' in parameters:
|
||||
form.add(TextWidget, '%smessage' % prefix, title = _('Message'),
|
||||
value=self.message, cols=80, rows=10,
|
||||
validation_function=ComputedExpressionWidget.validate_ezt)
|
||||
validation_function=ComputedExpressionWidget.validate_template)
|
||||
if 'to' in parameters:
|
||||
form.add(WidgetList, '%sto' % prefix, title=_('To'),
|
||||
element_type=SingleSelectWidget,
|
||||
|
|
|
@ -72,7 +72,7 @@ def call_webservice(url, qs_data=None, request_signature_key=None,
|
|||
url = urlparse.urlunparse(parsed[:4] + (qs,) + parsed[5:6])
|
||||
|
||||
if request_signature_key:
|
||||
signature_key = WorkflowStatusItem.compute(request_signature_key)
|
||||
signature_key = str(WorkflowStatusItem.compute(request_signature_key))
|
||||
if signature_key:
|
||||
url = sign_url(url, signature_key)
|
||||
|
||||
|
|
Loading…
Reference in New Issue