0001-misc-add-new-widget-to-validate-workflow-expressions.patch
tests/test_widgets.py | ||
---|---|---|
382 | 382 |
options=[('apple', 'Apple'), ('pear', 'Pear'), ('peach', 'Peach')]) |
383 | 383 |
mock_form_submission(req, widget, {'test$choice': ['__other'], 'test$other': 'Apricot'}) |
384 | 384 |
assert widget.parse() == 'Apricot' |
385 | ||
386 |
def test_computed_expression_widget(): |
|
387 |
widget = ComputedExpressionWidget('test') |
|
388 |
form = MockHtmlForm(widget) |
|
389 |
mock_form_submission(req, widget, {'test': 'hello world'}) |
|
390 |
assert widget.parse() == 'hello world' |
|
391 |
assert not widget.has_error() |
|
392 | ||
393 |
widget = ComputedExpressionWidget('test') |
|
394 |
mock_form_submission(req, widget, {'test': '=hello world'}) |
|
395 |
assert widget.has_error() |
|
396 |
assert widget.get_error().startswith('syntax error') |
|
397 | ||
398 |
widget = ComputedExpressionWidget('test') |
|
399 |
mock_form_submission(req, widget, {'test': '[form_var_xxx]'}) |
|
400 |
assert not widget.has_error() |
|
401 | ||
402 |
widget = ComputedExpressionWidget('test') |
|
403 |
mock_form_submission(req, widget, {'test': '[end]'}) |
|
404 |
assert widget.has_error() |
|
405 |
assert widget.get_error().startswith('error in template') |
wcs/qommon/form.py | ||
---|---|---|
59 | 59 |
from strftime import strftime |
60 | 60 |
from publisher import get_cfg |
61 | 61 |
from wcs import file_validation |
62 |
from . import ezt |
|
62 | 63 | |
63 | 64 |
QuixoteForm = Form |
64 | 65 | |
... | ... | |
2234 | 2235 |
self.value = self.get('choice') |
2235 | 2236 |
if self.value == '__other': |
2236 | 2237 |
self.value = self.get('other') |
2238 | ||
2239 | ||
2240 |
class ComputedExpressionWidget(StringWidget): |
|
2241 |
'''StringWidget that checks the entered value is a correct workflow |
|
2242 |
expression.''' |
|
2243 | ||
2244 |
@classmethod |
|
2245 |
def validate_ezt(cls, template): |
|
2246 |
processor = ezt.Template(compress_whitespace=False) |
|
2247 |
try: |
|
2248 |
processor.parse(template or '') |
|
2249 |
except ezt.EZTException as e: |
|
2250 |
parts = [] |
|
2251 |
parts.append({ |
|
2252 |
ezt.ArgCountSyntaxError: _('wrong number of arguments'), |
|
2253 |
ezt.UnknownReference: _('unknown reference'), |
|
2254 |
ezt.NeedSequenceError: _('sequence required'), |
|
2255 |
ezt.UnclosedBlocksError: _('unclosed block'), |
|
2256 |
ezt.UnmatchedEndError: _('unmatched [end]'), |
|
2257 |
ezt.BaseUnavailableError: _('unavailable base location'), |
|
2258 |
ezt.BadFormatConstantError: _('bad format constant'), |
|
2259 |
ezt.UnknownFormatConstantError: _('unknown format constant'), |
|
2260 |
}.get(e.__class__)) |
|
2261 |
if e.line: |
|
2262 |
parts.append(_('at line %(line)d and column %(column)d') % { |
|
2263 |
'line': e.line+1, |
|
2264 |
'column': e.column+1}) |
|
2265 |
raise ValueError(_('error in template (%s)') % ' '.join(parts)) |
|
2266 | ||
2267 |
def _parse(self, request): |
|
2268 |
StringWidget._parse(self, request) |
|
2269 |
if self.value: |
|
2270 |
if self.value.startswith('='): |
|
2271 |
# python expression |
|
2272 |
try: |
|
2273 |
compile(self.value[1:], '<string>', 'eval') |
|
2274 |
except SyntaxError as e: |
|
2275 |
self.set_error(_('syntax error (%s)') % e) |
|
2276 |
else: |
|
2277 |
# ezt expression |
|
2278 |
try: |
|
2279 |
self.validate_ezt(self.value) |
|
2280 |
except ValueError as e: |
|
2281 |
self.set_error(str(e)) |
wcs/qommon/static/css/dc2/admin.css | ||
---|---|---|
1170 | 1170 |
width: 25%; |
1171 | 1171 |
} |
1172 | 1172 | |
1173 |
div.WidgetDict div.content div + div.ComputedExpressionWidget, |
|
1173 | 1174 |
div.WidgetDict div.content div + div.StringWidget { |
1174 | 1175 |
width: 70%; |
1175 | 1176 |
padding-left: 1em; |
wcs/wf/geolocate.py | ||
---|---|---|
27 | 27 |
from quixote import get_publisher |
28 | 28 | |
29 | 29 |
from qommon import get_logger |
30 |
from qommon.form import RadiobuttonsWidget, StringWidget, CheckboxWidget
|
|
30 |
from qommon.form import RadiobuttonsWidget, ComputedExpressionWidget, CheckboxWidget
|
|
31 | 31 |
from qommon.misc import http_get_page |
32 | 32 |
from wcs.workflows import WorkflowStatusItem, register_item_class |
33 | 33 | |
... | ... | |
60 | 60 |
value=self.method, |
61 | 61 |
attrs={'data-dynamic-display-parent': 'true'}) |
62 | 62 |
if 'address_string' in parameters: |
63 |
form.add(StringWidget, '%saddress_string' % prefix, size=50,
|
|
63 |
form.add(ComputedExpressionWidget, '%saddress_string' % prefix, size=50,
|
|
64 | 64 |
title=_('Address String'), value=self.address_string, |
65 | 65 |
attrs={ |
66 | 66 |
'data-dynamic-display-child-of': '%smethod' % prefix, |
67 | 67 |
'data-dynamic-display-value': methods.get('address_string'), |
68 | 68 |
}) |
69 | 69 |
if 'map_variable' in parameters: |
70 |
form.add(StringWidget, '%smap_variable' % prefix, size=50,
|
|
70 |
form.add(ComputedExpressionWidget, '%smap_variable' % prefix, size=50,
|
|
71 | 71 |
title=_('Map Variable'), value=self.map_variable, |
72 | 72 |
attrs={ |
73 | 73 |
'data-dynamic-display-child-of': '%smethod' % prefix, |
74 | 74 |
'data-dynamic-display-value': methods.get('map_variable'), |
75 | 75 |
}) |
76 | 76 |
if 'photo_variable' in parameters: |
77 |
form.add(StringWidget, '%sphoto_variable' % prefix, size=50,
|
|
77 |
form.add(ComputedExpressionWidget, '%sphoto_variable' % prefix, size=50,
|
|
78 | 78 |
title=_('Photo Variable'), value=self.photo_variable, |
79 | 79 |
attrs={ |
80 | 80 |
'data-dynamic-display-child-of': '%smethod' % prefix, |
wcs/wf/jump.py | ||
---|---|---|
166 | 166 |
'variables': ', '.join(timewords()), |
167 | 167 |
'granularity': seconds2humanduration(self._granularity)} |
168 | 168 |
if str(self.timeout).startswith('='): |
169 |
form.add(StringWidget, '%stimeout' % prefix, title=_('Timeout'),
|
|
169 |
form.add(ComputedExpressionWidget, '%stimeout' % prefix, title=_('Timeout'),
|
|
170 | 170 |
value=self.timeout, hint=_hint) |
171 | 171 |
else: |
172 | 172 |
form.add(StringWidget, '%stimeout' % prefix, title=_('Timeout'), |
wcs/wf/wscall.py | ||
---|---|---|
147 | 147 |
title=_('URL'), value=self.url, size=80, |
148 | 148 |
hint=_('Common substitution variables are available with the [variable] syntax.')) |
149 | 149 |
if 'request_signature_key' in parameters: |
150 |
form.add(StringWidget, '%srequest_signature_key' % prefix,
|
|
150 |
form.add(ComputedExpressionWidget, '%srequest_signature_key' % prefix,
|
|
151 | 151 |
title=_('Request Signature Key'), |
152 | 152 |
value=self.request_signature_key) |
153 | 153 |
methods = collections.OrderedDict( |
... | ... | |
170 | 170 |
form.add(WidgetDict, '%spost_data' % prefix, |
171 | 171 |
title=_('Post data'), |
172 | 172 |
value=self.post_data or {}, |
173 |
element_value_type=ComputedExpressionWidget, |
|
173 | 174 |
attrs={ |
174 | 175 |
'data-dynamic-display-child-of': '%smethod' % prefix, |
175 | 176 |
'data-dynamic-display-value': methods.get('POST'), |
wcs/workflows.py | ||
---|---|---|
1872 | 1872 |
self.get_list_of_roles(include_logged_in_users=False)}) |
1873 | 1873 |
if 'subject' in parameters: |
1874 | 1874 |
form.add(StringWidget, '%ssubject' % prefix, title=_('Subject'), |
1875 |
validation_function=validate_ezt, |
|
1875 |
validation_function=ComputedExpressionWidget.validate_ezt,
|
|
1876 | 1876 |
value=self.subject, size=40) |
1877 | 1877 |
if 'body' in parameters: |
1878 | 1878 |
form.add(TextWidget, '%sbody' % prefix, title=_('Body'), |
1879 | 1879 |
value=self.body, cols=80, rows=10, |
1880 |
validation_function=validate_ezt, |
|
1880 |
validation_function=ComputedExpressionWidget.validate_ezt,
|
|
1881 | 1881 |
hint=_('Available variables: url, url_status, details, name, number, comment, field_NAME')) |
1882 | 1882 | |
1883 | ||
1884 | 1883 |
def perform(self, formdata): |
1885 | 1884 |
if not self.to: |
1886 | 1885 |
return |
... | ... | |
2006 | 2005 |
return fd.getvalue() |
2007 | 2006 | |
2008 | 2007 | |
2009 |
def validate_ezt(template): |
|
2010 |
processor = ezt.Template(compress_whitespace=False) |
|
2011 |
try: |
|
2012 |
processor.parse(template or '') |
|
2013 |
except ezt.EZTException as e: |
|
2014 |
parts = [] |
|
2015 |
parts.append({ |
|
2016 |
ezt.ArgCountSyntaxError: _('wrong number of arguments'), |
|
2017 |
ezt.UnknownReference: _('unknown reference'), |
|
2018 |
ezt.NeedSequenceError: _('sequence required'), |
|
2019 |
ezt.UnclosedBlocksError: _('unclosed block'), |
|
2020 |
ezt.UnmatchedEndError: _('unmatched [end]'), |
|
2021 |
ezt.BaseUnavailableError: _('unavailable base location'), |
|
2022 |
ezt.BadFormatConstantError: _('bad format constant'), |
|
2023 |
ezt.UnknownFormatConstantError: _('unknown format constant'), |
|
2024 |
}.get(e.__class__)) |
|
2025 |
if e.line: |
|
2026 |
parts.append(_('at line %(line)d and column %(column)d') % { |
|
2027 |
'line': e.line+1, |
|
2028 |
'column': e.column+1}) |
|
2029 |
raise ValueError(_('error in template (%s)') % ' '.join(parts)) |
|
2030 | ||
2031 | 2008 |
class SendSMSWorkflowStatusItem(WorkflowStatusItem): |
2032 | 2009 |
description = N_('Send SMS') |
2033 | 2010 |
key = 'sendsms' |
2034 |
- |