From 314cde0bcd3e8a9ff3815e1c1b685dd4435a1b95 Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Thu, 5 Sep 2019 17:35:00 +0200
Subject: [PATCH] fields: add validation on comment field (#30618)

---
 tests/test_admin_pages.py | 74 +++++++++++++++++++++++++++++++++++++++
 wcs/fields.py             |  2 ++
 wcs/qommon/form.py        | 11 ++++--
 3 files changed, 84 insertions(+), 3 deletions(-)

diff --git a/tests/test_admin_pages.py b/tests/test_admin_pages.py
index 7d173f8d..a8bb6b97 100644
--- a/tests/test_admin_pages.py
+++ b/tests/test_admin_pages.py
@@ -1583,6 +1583,80 @@ def test_form_edit_comment_field(pub):
     resp = app.get('/backoffice/forms/1/fields/1/')
     assert 'WysiwygTextWidget' not in resp.body
 
+def test_form_comment_field_textwidget_validation(pub):
+    create_superuser(pub)
+    FormDef.wipe()
+    formdef = FormDef()
+    formdef.name = 'form title'
+    # legacy, ezt syntax in a non-html field will be presented as a textarea
+    formdef.fields = [fields.CommentField(id='1', type='comment',
+        label='[if-any toto]hello world[end]')]
+    formdef.store()
+    app = login(get_app(pub))
+    resp = app.get('/backoffice/forms/1/fields/1/')
+
+    # bad {% %} Django template syntax
+    assert 'WysiwygTextWidget' not in resp.body
+    resp.form.fields['label'][0].value = '{% if cond %}no endif provided'
+    resp = resp.form.submit('submit')
+    assert 'syntax error in Django template: Unclosed tag on line 1' in resp.body
+
+    # bad {{ }} Django template syntax
+    assert 'WysiwygTextWidget' not in resp.body
+    resp.form.fields['label'][0].value = '{{0+0}}'
+    resp = resp.form.submit('submit')
+    assert 'syntax error in Django template: Could not parse' in resp.body
+
+    # bad EZT syntax
+    assert 'WysiwygTextWidget' not in resp.body
+    resp.form.fields['label'][0].value = '[end]'
+    resp = resp.form.submit('submit')
+    assert 'syntax error in ezt template: unmatched [end]' in resp.body
+
+    # good syntax
+    assert 'WysiwygTextWidget' not in resp.body
+    resp.form.fields['label'][0].value = '{{variable}}'
+    resp = resp.form.submit('submit')
+    assert FormDef.get(formdef.id).fields[0].label == '{{variable}}'
+
+def test_form_comment_field_wysiwygtextwidget_validation(pub):
+    create_superuser(pub)
+    create_role()
+
+    FormDef.wipe()
+    formdef = FormDef()
+    formdef.name = 'form title'
+    formdef.fields = [fields.CommentField(id='1', label='a comment field', type='comment')]
+    formdef.store()
+
+    app = login(get_app(pub))
+    resp = app.get('/backoffice/forms/1/fields/1/')
+    assert 'a comment field' in resp.body
+
+    # bad {% %} Django template syntax
+    assert 'WysiwygTextWidget' in resp.body
+    resp.form.fields['label'][0].value = '{% if cond %}no endif provided'
+    resp = resp.form.submit('submit')
+    assert 'syntax error in Django template: Unclosed tag on line 1' in resp.body
+
+    # bad {{ }} Django template syntax
+    assert 'WysiwygTextWidget' in resp.body
+    resp.form.fields['label'][0].value = '{{0+0}}'
+    resp = resp.form.submit('submit')
+    assert 'syntax error in Django template: Could not parse' in resp.body
+
+    # bad EZT syntax
+    assert 'WysiwygTextWidget' in resp.body
+    resp.form.fields['label'][0].value = '[end]'
+    resp = resp.form.submit('submit')
+    assert 'syntax error in ezt template: unmatched [end]' in resp.body
+
+    # good syntax
+    assert 'WysiwygTextWidget' in resp.body
+    resp.form.fields['label'][0].value = '{{variable}}'
+    resp = resp.form.submit('submit')
+    assert FormDef.get(formdef.id).fields[0].label == '{{variable}}'
+
 def test_form_edit_map_field(pub):
     create_superuser(pub)
     create_role()
diff --git a/wcs/fields.py b/wcs/fields.py
index 717cc8c6..cdfab66d 100644
--- a/wcs/fields.py
+++ b/wcs/fields.py
@@ -641,9 +641,11 @@ class CommentField(Field):
     def fill_admin_form(self, form):
         if self.label and (self.label[0] != '<' and '[end]' in self.label):
             form.add(TextWidget, 'label', title=_('Label'), value=self.label,
+                    validation_function=ComputedExpressionWidget.validate_template,
                     required=True, cols=70, rows=3, render_br=False)
         else:
             form.add(WysiwygTextWidget, 'label', title=_('Label'),
+                    validation_function=ComputedExpressionWidget.validate_template,
                     value=self.get_html_content(), required=True)
         form.add(StringWidget, 'extra_css_class', title = _('Extra classes for CSS styling'),
                 value=self.extra_css_class, size=30, advanced=(not self.extra_css_class))
diff --git a/wcs/qommon/form.py b/wcs/qommon/form.py
index c882fbf9..15dddfba 100644
--- a/wcs/qommon/form.py
+++ b/wcs/qommon/form.py
@@ -517,7 +517,7 @@ class TextWidget(quixote.form.TextWidget):
         self.validation_function = kwargs.pop('validation_function', None)
         super(TextWidget, self).__init__(name, *args, **kwargs)
 
-    def _parse(self, request):
+    def _parse(self, request, use_validation_function=True):
         quixote.form.TextWidget._parse(self, request)
         if self.value is not None:
             try:
@@ -528,7 +528,7 @@ class TextWidget(quixote.form.TextWidget):
                 uvalue = self.value.decode(get_publisher().site_charset)
                 if len(uvalue) > maxlength:
                     self.error = _('too many characters (limit is %d)') % maxlength
-            if self.validation_function:
+            if use_validation_function and self.validation_function:
                 try:
                     self.validation_function(self.value)
                 except ValueError as e:
@@ -1480,7 +1480,7 @@ $("#%s").autocompleteArray([
 
 class WysiwygTextWidget(TextWidget):
     def _parse(self, request):
-        TextWidget._parse(self, request)
+        TextWidget._parse(self, request, use_validation_function=False)
         if self.value:
             if _sanitizeHTML:
                 self.value = _sanitizeHTML(self.value, get_request().charset, 'text/html')
@@ -1494,6 +1494,11 @@ class WysiwygTextWidget(TextWidget):
             def unquote_django(matchobj):
                 return parser.unescape(unicode(matchobj.group(0), charset)).encode(charset)
             self.value = re.sub('{[{%](.*?)[%}]}', unquote_django, self.value)
+            if self.validation_function:
+                try:
+                    self.validation_function(self.value)
+                except ValueError as e:
+                    self.error = str(e)
 
     def add_media(self):
         get_response().add_javascript(['qommon.wysiwyg.js'])
-- 
2.23.0.rc1

