From d2176a38bd0b7b38bd5aa2929d051737e58bcc48 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Tue, 6 Dec 2022 09:43:13 +0100
Subject: [PATCH] backoffice: do not reduce options in presence of template
 filters (#72055)

---
 tests/backoffice_pages/test_custom_view.py | 86 ++++++++++++++++++++++
 wcs/backoffice/management.py               | 15 +++-
 2 files changed, 100 insertions(+), 1 deletion(-)

diff --git a/tests/backoffice_pages/test_custom_view.py b/tests/backoffice_pages/test_custom_view.py
index f14354925..268530d9b 100644
--- a/tests/backoffice_pages/test_custom_view.py
+++ b/tests/backoffice_pages/test_custom_view.py
@@ -1114,3 +1114,89 @@ def test_backoffice_custom_view_boolean_filters(pub):
     assert resp.forms['listing-settings']['filter-1-value'].value == 'false'
     assert resp.forms['listing-settings']['filter-1-operator'].value == 'ne'
     assert resp.text.count('data-link=') == 5
+
+
+def test_item_options_in_dynamic_view(pub):
+    pub.user_class.wipe()
+    create_superuser(pub)
+    pub.role_class.wipe()
+    pub.custom_view_class.wipe()
+    role = pub.role_class(name='test')
+    role.store()
+
+    CardDef.wipe()
+    carddef = CardDef()
+    carddef.name = 'card-title'
+    carddef.digest_templates = {'default': '{{ form_var_foo }}'}
+    carddef.fields = [
+        fields.StringField(
+            id='1',
+            label='1st field',
+            type='string',
+        ),
+        fields.ItemField(
+            id='4',
+            label='4th field',
+            type='item',
+            items=['â', 'b', 'c', 'd'],
+            display_locations=['validation', 'summary', 'listings'],
+        ),
+    ]
+    carddef.workflow_roles = {'_editor': role.id}
+    carddef.store()
+
+    data_class = carddef.data_class()
+    data_class.wipe()
+
+    for i in range(0, 12):
+        carddata = data_class()
+        carddata.data = {
+            '1': 'plop%s' % (i % 2),
+            '4': 'a%s' % (i % 4),
+            '4_display': 'a%s' % (i % 4),
+        }
+        carddata.just_created()
+        carddata.store()
+
+    custom_view = pub.custom_view_class()
+    custom_view.title = 'custom test view'
+    custom_view.formdef = carddef
+    custom_view.visibility = 'datasource'
+    custom_view.columns = {'list': [{'id': '1'}]}
+    custom_view.filters = {}
+    custom_view.store()
+
+    app = login(get_app(pub))
+    resp = app.get('/backoffice/data/card-title/custom-test-view/')
+
+    # enable both filters
+    resp.forms['listing-settings']['filter-1'].checked = True
+    resp.forms['listing-settings']['filter-4'].checked = True
+    resp = resp.forms['listing-settings'].submit()
+    # all used options are listed ({} is "custom value")
+    assert [x[0] for x in resp.forms['listing-settings']['filter-4-value'].options] == [
+        '',
+        'a0',
+        'a1',
+        'a2',
+        'a3',
+        '{}',
+    ]
+
+    # plain filter, only used options are listed
+    resp.forms['listing-settings']['filter-1-value'].value = 'plop0'
+    resp.forms['listing-settings']['filter-4-value'].value = 'a0'
+    resp = resp.forms['listing-settings'].submit()
+    assert [x[0] for x in resp.forms['listing-settings']['filter-4-value'].options] == ['', 'a0', 'a2', '{}']
+
+    # template filter, all options are listed
+    resp.forms['listing-settings']['filter-1-value'].value = '{{ test }}'
+    resp = resp.forms['listing-settings'].submit()
+    assert [x[0] for x in resp.forms['listing-settings']['filter-4-value'].options] == [
+        '',
+        'a0',
+        'a1',
+        'a2',
+        'a3',
+        '{}',
+    ]
diff --git a/wcs/backoffice/management.py b/wcs/backoffice/management.py
index 8b0849b02..453dcfc90 100644
--- a/wcs/backoffice/management.py
+++ b/wcs/backoffice/management.py
@@ -966,9 +966,22 @@ class FormPage(FormdefDirectoryBase):
         # for item/items fields, get actual option values from database
         if not getattr(filter_field, 'block_field', None):
             criterias.append(NotNull(sql.get_field_id(filter_field)))
+            if self.view and self.view.visibility == 'datasource':
+                # for custom views used as data sources ignore criterias
+                # that would result in an empty list of options.
+                # (either "Nothing" or a template string)
+                options_criterias = []
+                for criteria in criterias:
+                    if isinstance(criteria, Nothing):
+                        continue
+                    if Template.is_template_string(getattr(criteria, 'value', '')):
+                        continue
+                    options_criterias.append(criteria)
+            else:
+                options_criterias = criterias
             options = self.formdef.data_class().select_distinct(
                 [sql.get_field_id(filter_field), '%s_display' % sql.get_field_id(filter_field)],
-                clause=criterias,
+                clause=options_criterias,
             )
         else:
             # in case of blocks, this requires digging into the jsonb columns,
-- 
2.38.1

