From 8ca0896e8c46731f297acc5b34415c0ae980817d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Sun, 8 Dec 2019 15:44:23 +0100
Subject: [PATCH] forms: consider form pages with only inert fields in
 validation pages (#37709)

---
 tests/test_form_pages.py | 22 +++++++++++
 wcs/formdef.py           | 80 ++++++++++++++++++++--------------------
 2 files changed, 62 insertions(+), 40 deletions(-)

diff --git a/tests/test_form_pages.py b/tests/test_form_pages.py
index 767511fb1..c16868ed3 100644
--- a/tests/test_form_pages.py
+++ b/tests/test_form_pages.py
@@ -1270,6 +1270,28 @@ def test_form_display_locations(pub):
     assert 'plop3' in resp.text
     assert 'Bla bla bla' in resp.text
 
+def test_multipage_form_display_locations(pub):
+    formdef = create_formdef()
+    formdef.fields = [
+        fields.PageField(id='0', label='1st page', type='page'),
+        fields.StringField(id='1', label='string1', display_locations=[]),
+        fields.PageField(id='2', label='2nd page', type='page'),
+        fields.CommentField(id='3', label='Bla bla bla', type='comment', display_locations=['validation']),
+    ]
+    formdef.store()
+    formdef.data_class().wipe()
+
+    resp = get_app(pub).get('/test/')
+    resp.form['f1'] = 'plop1'
+    resp = resp.form.submit('submit')  # -> page 2
+    resp = resp.form.submit('submit')  # -> validation
+
+    pq = resp.pyquery.remove_namespaces()
+    assert not '<h3>1st page</h3>' in resp.text  # page 1 title not displayed
+    assert pq('div[style="display: none;"] [name=f1]')  # but page 1 field included, hidden
+    assert '<h3>2nd page</h3>' in resp.text  # page 2 title
+    assert 'Bla bla bla' in resp.text  # and page 2 comment field
+
 def test_form_visit_existing(pub):
     user = create_user(pub)
     formdef = create_formdef()
diff --git a/wcs/formdef.py b/wcs/formdef.py
index ce1b9153e..825986a60 100644
--- a/wcs/formdef.py
+++ b/wcs/formdef.py
@@ -605,62 +605,62 @@ class FormDef(StorableObject):
             form.attrs['style'] = 'display: none;'
         if self.keywords:
             form.attrs['data-keywords'] = ' '.join(self.keywords_list)
-        current_page_fields = []
-        on_disabled_page = False
-        on_page = False
-        for i, field in enumerate(self.fields):
-            if field.type == 'page':
-                on_disabled_page = False
-                if not field.is_visible(dict, self):
-                    on_disabled_page = True
-                form_field = False
-                for f in self.fields[self.fields.index(field)+1:]:
-                    if f.key == 'page':
-                        break
-                    if isinstance(f, fields.WidgetField):
-                        form_field = True
-                        break
-                if form_field is False:
-                    on_disabled_page = True
 
-            if on_disabled_page:
-                continue
+        form_fields = self.fields
+        if form_fields and form_fields[0].type != 'page':
+            # add fake initial page in case it's missing
+            form_fields = [fields.PageField(label='', type='page')] + form_fields
 
+        # 1st pass to group fields on different pages
+        pages = []
+        current_page = {}
+        for field in form_fields:
             if field.type == 'page':
-                if on_page:
-                    form.widgets.append(HtmlWidget(htmltext('</div></div>')))
-                form.widgets.append(HtmlWidget(
-                        htmltext('<div class="page"><h3>%s</h3><div>' % field.label)))
-                on_page = field
-                current_page_fields = []
+                current_page = {'page': field, 'fields': []}
+                current_page['disabled'] = not field.is_visible(dict, self)
+                pages.append(current_page)
                 continue
 
-            if field.type == 'title' and on_page and (
-                    not current_page_fields and
-                    on_page.label == field.label):
+            if current_page['disabled']:
+                continue
+
+            if field.type == 'title' and (
+                    not current_page['fields'] and
+                    current_page['page'].label == field.label):
                 # don't include first title of a page if that title has the
                 # same text as the page.
                 continue
 
-            if field.type == 'comment' and not field.include_in_validation_page:
+            if field.type in ('title', 'subtitle', 'comment') and not field.include_in_validation_page:
                 # don't render field that wouldn't be displayed.
                 continue
 
             if not field.is_visible(dict, self):
                 continue
 
-            current_page_fields.append(field)
-            value = dict.get(field.id)
-
-            if not field.include_in_validation_page:
-                form.widgets.append(HtmlWidget(htmltext('<div style="display: none;">')))
-                field.add_to_view_form(form, value)
-                form.widgets.append(HtmlWidget(htmltext('</div>')))
-            else:
-                field.add_to_view_form(form, value)
+            current_page['fields'].append(field)
+
+        # 2nd pass to create view form
+        for page in pages:
+            visible_contents = False
+            if page['fields'] and any([x.include_in_validation_page for x in page['fields']]):
+                visible_contents = True
+                form.widgets.append(HtmlWidget(htmltext('<div class="page">')))
+                if page['page'].label:
+                    form.widgets.append(HtmlWidget(htmltext('<h3>%s</h3>') % page['page'].label))
+                form.widgets.append(HtmlWidget(htmltext('<div>')))
+
+            for field in page['fields']:
+                value = dict.get(field.id)
+                if not field.include_in_validation_page:
+                    form.widgets.append(HtmlWidget(htmltext('<div style="display: none;">')))
+                    field.add_to_view_form(form, value)
+                    form.widgets.append(HtmlWidget(htmltext('</div>')))
+                else:
+                    field.add_to_view_form(form, value)
 
-        if on_page:
-            form.widgets.append(HtmlWidget(htmltext('</div></div>')))
+            if visible_contents:
+                form.widgets.append(HtmlWidget(htmltext('</div></div>')))
 
         return form
 
-- 
2.24.0

