From 11cf9c30249c21ca11078e51ac695b30a2e5a933 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Thu, 3 Oct 2019 11:42:52 +0200
Subject: [PATCH] fields: render template for title and subtitle (#36620)

---
 tests/test_fields.py     | 66 ++++++++++++++++++++++++++++++++++++++++
 tests/test_form_pages.py | 27 ++++++++++++++++
 wcs/fields.py            | 19 ++++++------
 wcs/forms/common.py      |  7 +++--
 4 files changed, 107 insertions(+), 12 deletions(-)

diff --git a/tests/test_fields.py b/tests/test_fields.py
index a1f6fae7..e33bd847 100644
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -162,6 +162,39 @@ def test_title():
     field.add_to_form(form)
     assert '<h3 data-field-id="None" class="test">Foobar</h3>' in str(form.render())
 
+    # test for variable substitution
+    pub.substitutions.feed(MockSubstitutionVariables())
+    field = fields.TitleField(label='{{ bar }}')
+    form = Form(use_tokens=False)
+    field.add_to_form(form)
+    assert '<h3 data-field-id="None">Foobar</h3>' in str(form.render())
+
+    pub.substitutions.feed(MockSubstitutionVariables())
+    field = fields.TitleField(label='[bar]')
+    form = Form(use_tokens=False)
+    field.add_to_form(form)
+    assert '<h3 data-field-id="None">Foobar</h3>' in str(form.render())
+
+    # test for proper escaping of substitution variables
+    pub.substitutions.feed(MockSubstitutionVariables())
+    field = fields.TitleField(label='{{ foo }}')
+    form = Form(use_tokens=False)
+    field.add_to_form(form)
+    assert '<h3 data-field-id="None">1 &lt; 3</h3>' in str(form.render())
+
+    pub.substitutions.feed(MockSubstitutionVariables())
+    field = fields.TitleField(label='[foo]')
+    form = Form(use_tokens=False)
+    field.add_to_form(form)
+    assert '<h3 data-field-id="None">1 &lt; 3</h3>' in str(form.render())
+
+    # test for html content
+    field = fields.TitleField(label='<i>Foobar&eacute;</i>')
+    form = Form(use_tokens=False)
+    field.add_to_form(form)
+    assert '&lt;i&gt;Foobar&amp;eacute;&lt;/i&gt;' in str(form.render())
+    assert field.unhtmled_label == 'Foobaré'
+
 def test_subtitle():
     field = fields.SubtitleField(label='Foobar')
     form = Form(use_tokens=False)
@@ -173,6 +206,39 @@ def test_subtitle():
     field.add_to_form(form)
     assert '<h4 data-field-id="None" class="test">Foobar</h4>' in str(form.render())
 
+    # test for variable substitution
+    pub.substitutions.feed(MockSubstitutionVariables())
+    field = fields.SubtitleField(label='{{ bar }}')
+    form = Form(use_tokens=False)
+    field.add_to_form(form)
+    assert '<h4 data-field-id="None">Foobar</h4>' in str(form.render())
+
+    pub.substitutions.feed(MockSubstitutionVariables())
+    field = fields.SubtitleField(label='[bar]')
+    form = Form(use_tokens=False)
+    field.add_to_form(form)
+    assert '<h4 data-field-id="None">Foobar</h4>' in str(form.render())
+
+    # test for proper escaping of substitution variables
+    pub.substitutions.feed(MockSubstitutionVariables())
+    field = fields.SubtitleField(label='{{ foo }}')
+    form = Form(use_tokens=False)
+    field.add_to_form(form)
+    assert '<h4 data-field-id="None">1 &lt; 3</h4>' in str(form.render())
+
+    pub.substitutions.feed(MockSubstitutionVariables())
+    field = fields.SubtitleField(label='[foo]')
+    form = Form(use_tokens=False)
+    field.add_to_form(form)
+    assert '<h4 data-field-id="None">1 &lt; 3</h4>' in str(form.render())
+
+    # test for html content
+    field = fields.SubtitleField(label='<i>Foobar&eacute;</i>')
+    form = Form(use_tokens=False)
+    field.add_to_form(form)
+    assert '&lt;i&gt;Foobar&amp;eacute;&lt;/i&gt;' in str(form.render())
+    assert field.unhtmled_label == 'Foobaré'
+
 def test_comment():
     field = fields.CommentField(label='Foobar')
     form = Form(use_tokens=False)
diff --git a/tests/test_form_pages.py b/tests/test_form_pages.py
index c80bae61..7da4b8bc 100644
--- a/tests/test_form_pages.py
+++ b/tests/test_form_pages.py
@@ -513,6 +513,33 @@ def test_form_multi_page(pub):
         data = formdef.data_class().get(data_id)
         assert data.data == {'1': 'foo', '3': 'bar'}
 
+
+def test_form_multi_page_title_and_subtitle_as_template(pub):
+    formdef = create_formdef()
+    formdef.fields = [fields.PageField(id='0', label='1st page', type='page'),
+            fields.StringField(id='1', label='string', varname='foo'),
+            fields.PageField(id='2', label='2nd page', type='page'),
+            fields.TitleField(id='4', label='<i>title of second page {{ form_var_foo }}</i>',
+                              type='title'),
+            fields.SubtitleField(id='5', label='<i>subtitle of second page {{ form_var_foo }}</i>',
+                                 type='subtitle'),
+            fields.StringField(id='3', label='string 2')]
+    formdef.store()
+    resp = get_app(pub).get('/test/')
+    formdef.data_class().wipe()
+    resp.form['f1'] = '35 < 42'
+    resp = resp.form.submit('submit')
+    resp.form['f3'] = 'bar'
+    resp = resp.form.submit('submit')
+    assert 'Check values then click submit.' in resp.body
+    resp = resp.form.submit('submit').follow()
+    assert 'The form has been recorded' in resp.body
+    expected_label = '&lt;i&gt;title of second page 35 &lt; 42&lt;/i&gt;'
+    assert '<div class="title "><h3>%s</h3></div>' % expected_label in resp.body
+    expected_label = '&lt;i&gt;subtitle of second page 35 &lt; 42&lt;/i&gt;'
+    assert '<div class="subtitle "><h4>%s</h4></div>' % expected_label in resp.body
+
+
 def test_form_multi_page_condition(pub):
     formdef = create_formdef()
     formdef.fields = [fields.PageField(id='0', label='1st page', type='page'),
diff --git a/wcs/fields.py b/wcs/fields.py
index dd96fa8e..5ef48461 100644
--- a/wcs/fields.py
+++ b/wcs/fields.py
@@ -569,12 +569,19 @@ def register_field_class(klass):
 class TitleField(Field):
     key = 'title'
     description = N_('Title')
+    html_tag = 'h3'
 
     def add_to_form(self, form, value = None):
+        import wcs.workflows
         extra_attributes = ' data-field-id="%s"' % self.id
         if self.extra_css_class:
             extra_attributes += ' class="%s"' % self.extra_css_class
-        widget = HtmlWidget(htmltext('<h3%s>%s</h3>' % (extra_attributes, self.label)))
+        title_markup = '<{html_tag}{extra_attributes}>%s</{html_tag}>'.format(
+            html_tag=self.html_tag,
+            extra_attributes=extra_attributes,
+        )
+        label = wcs.workflows.template_on_formdata(None, self.label, autoescape=False)
+        widget = HtmlWidget(htmltext(title_markup) % label)
         form.widgets.append(widget)
         return widget
     add_to_view_form = add_to_form
@@ -597,15 +604,7 @@ register_field_class(TitleField)
 class SubtitleField(TitleField):
     key = 'subtitle'
     description = N_('Subtitle')
-
-    def add_to_form(self, form, value = None):
-        extra_attributes = ' data-field-id="%s"' % self.id
-        if self.extra_css_class:
-            extra_attributes += ' class="%s"' % self.extra_css_class
-        widget = HtmlWidget(htmltext('<h4%s>%s</h4>' % (extra_attributes, self.label)))
-        form.widgets.append(widget)
-        return widget
-    add_to_view_form = add_to_form
+    html_tag = 'h4'
 
 register_field_class(SubtitleField)
 
diff --git a/wcs/forms/common.py b/wcs/forms/common.py
index 76cf5d6e..c80d2ff0 100644
--- a/wcs/forms/common.py
+++ b/wcs/forms/common.py
@@ -384,6 +384,7 @@ class FormStatusPage(Directory, FormTemplateMixin):
         return r.getvalue()
 
     def display_fields(self, fields=None, form_url='', include_unset_required_fields=False):
+        import wcs.workflows
         if fields is None:
             fields = self.formdef.fields
         r = TemplateIO(html=True)
@@ -454,11 +455,13 @@ class FormStatusPage(Directory, FormTemplateMixin):
                 continue
 
             if f.type == 'title':
-                r += htmltext('<div class="title %s"><h3>%s</h3></div>') % (f.extra_css_class or '', f.label)
+                label = wcs.workflows.template_on_formdata(None, f.label, autoescape=False)
+                r += htmltext('<div class="title %s"><h3>%s</h3></div>') % (f.extra_css_class or '', label)
                 continue
 
             if f.type == 'subtitle':
-                r += htmltext('<div class="subtitle %s"><h4>%s</h4></div>') % (f.extra_css_class or '', f.label)
+                label = wcs.workflows.template_on_formdata(None, f.label, autoescape=False)
+                r += htmltext('<div class="subtitle %s"><h4>%s</h4></div>') % (f.extra_css_class or '', label)
                 continue
 
             css_classes = ['field', 'field-type-%s' % f.key]
-- 
2.20.1

