diff --git a/tests/backoffice_pages/test_custom_view.py b/tests/backoffice_pages/test_custom_view.py index 7ec4778e0..569da953a 100644 --- a/tests/backoffice_pages/test_custom_view.py +++ b/tests/backoffice_pages/test_custom_view.py @@ -1207,7 +1207,7 @@ def test_backoffice_custom_view_boolean_filters(pub): assert resp.text.count('data-link=') == 5 -def test_item_options_in_dynamic_view(pub): +def test_item_options_in_custom_view(pub): pub.user_class.wipe() create_superuser(pub) pub.role_class.wipe() @@ -1223,12 +1223,39 @@ def test_item_options_in_dynamic_view(pub): fields.StringField( id='1', label='1st field', + type='string', + ), + fields.ItemField( + id='2', + label='2nd field', + type='item', + items=list('azertyuiopqsdfghjklmwxcvbn')[:16], + display_locations=['validation', 'summary', 'listings'], + display_mode='list', + ), + fields.ItemField( + id='3', + label='3rd field', + type='item', + items=list('azertyuiopqsdfghjklmwxcvbn')[:15], + display_locations=['validation', 'summary', 'listings'], + display_mode='list', ), fields.ItemField( id='4', label='4th field', - items=['â', 'b', 'c', 'd'], + type='item', + items=list('azertyuiopqsdfghjklmwxcvbn')[:16], display_locations=['validation', 'summary', 'listings'], + display_mode='autocomplete', + ), + fields.ItemField( + id='5', + label='5th field', + type='item', + items=list('azertyuiopqsdfghjklmwxcvbn')[:15], + display_locations=['validation', 'summary', 'listings'], + display_mode='autocomplete', ), ] carddef.workflow_roles = {'_editor': role.id} @@ -1241,55 +1268,102 @@ def test_item_options_in_dynamic_view(pub): carddata = data_class() carddata.data = { '1': 'plop%s' % (i % 2), + '2': 'a%s' % (i % 4), + '2_display': 'a%s' % (i % 4), + '3': 'a%s' % (i % 4), + '3_display': 'a%s' % (i % 4), '4': 'a%s' % (i % 4), '4_display': 'a%s' % (i % 4), + '5': 'a%s' % (i % 4), + '5_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() + datasource_custom_view = pub.custom_view_class() + datasource_custom_view.title = 'custom test view for datasource' + datasource_custom_view.formdef = carddef + datasource_custom_view.visibility = 'datasource' + datasource_custom_view.columns = {'list': [{'id': '1'}]} + datasource_custom_view.filters = {} + datasource_custom_view.store() + + any_custom_view = pub.custom_view_class() + any_custom_view.title = 'custom test view for anyone' + any_custom_view.formdef = carddef + any_custom_view.visibility = 'any' + any_custom_view.columns = {'list': [{'id': '1'}]} + any_custom_view.filters = {} + any_custom_view.store() app = login(get_app(pub)) - resp = app.get('/backoffice/data/card-title/custom-test-view/') - # enable both filters + resp = app.get('/backoffice/data/card-title/custom-test-view-for-anyone/') + # enable filters resp.forms['listing-settings']['filter-1'].checked = True + resp.forms['listing-settings']['filter-2'].checked = True + resp.forms['listing-settings']['filter-3'].checked = True resp.forms['listing-settings']['filter-4'].checked = True + resp.forms['listing-settings']['filter-5'].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] == [ + + # field 2: select - all used options are listed + assert [x[0] for x in resp.forms['listing-settings']['filter-2-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] == [ + # field 3: select - all used options are listed + assert [x[0] for x in resp.forms['listing-settings']['filter-3-value'].options] == [ '', 'a0', 'a1', 'a2', 'a3', - '{}', ] + # field 4: select2 - all used options are listed + assert [x[0] for x in resp.forms['listing-settings']['filter-4-value'].options] == [''] + resp2 = app.get(resp.request.path + 'filter-options?filter_field_id=4&_search=') + assert [x['id'] for x in resp2.json['data']] == ['a0', 'a1', 'a2', 'a3'] + + # field 5: select2 - all used options are listed + assert [x[0] for x in resp.forms['listing-settings']['filter-5-value'].options] == [''] + resp2 = app.get(resp.request.path + 'filter-options?filter_field_id=5&_search=') + assert [x['id'] for x in resp2.json['data']] == ['a0', 'a1', 'a2', 'a3'] + + resp = app.get('/backoffice/data/card-title/custom-test-view-for-datasource/') + # enable filters + resp.forms['listing-settings']['filter-1'].checked = True + resp.forms['listing-settings']['filter-2'].checked = True + resp.forms['listing-settings']['filter-3'].checked = True + resp.forms['listing-settings']['filter-4'].checked = True + resp.forms['listing-settings']['filter-5'].checked = True + resp = resp.forms['listing-settings'].submit() + + # field 2: select2 - all items are listed + assert [x[0] for x in resp.forms['listing-settings']['filter-2-value'].options] == ['', '{}'] + resp2 = app.get(resp.request.path + 'filter-options?filter_field_id=2&_search=') + assert [x['id'] for x in resp2.json['data']] == list('azertyuiopqsdfghjklmwxcvbn')[:15] + ['{}'] + + # field 3: select - all items are listed + assert [x[0] for x in resp.forms['listing-settings']['filter-3-value'].options] == [''] + list( + 'azertyuiopqsdfghjklmwxcvbn' + )[:15] + ['{}'] + + # field 4: select2 - all items are listed + assert [x[0] for x in resp.forms['listing-settings']['filter-4-value'].options] == ['', '{}'] + resp2 = app.get(resp.request.path + 'filter-options?filter_field_id=4&_search=') + assert [x['id'] for x in resp2.json['data']] == list('azertyuiopqsdfghjklmwxcvbn')[:15] + ['{}'] + + # field 5: select - all items are listed + assert [x[0] for x in resp.forms['listing-settings']['filter-5-value'].options] == [''] + list( + 'azertyuiopqsdfghjklmwxcvbn' + )[:15] + ['{}'] + @pytest.mark.parametrize('user_perms', ['admin', 'category_admin', 'category_not_admin', 'agent']) def test_backoffice_hidden_data_source_custom_view(pub, user_perms): diff --git a/tests/backoffice_pages/test_filters.py b/tests/backoffice_pages/test_filters.py index 3e7182cae..308d69a5b 100644 --- a/tests/backoffice_pages/test_filters.py +++ b/tests/backoffice_pages/test_filters.py @@ -382,6 +382,31 @@ def test_backoffice_item_filter(pub): else: assert [x['id'] for x in resp2.json['data']] == ['â', 'b', 'd'] + # item field in autocomplete mode, check that label is weel displayed in option + CardDef.wipe() + carddef = CardDef() + carddef.name = 'foo' + carddef.fields = [ + fields.StringField(id='1', label='Test', type='string', varname='foo'), + ] + carddef.digest_templates = {'default': 'card {{ form_var_foo }}'} + carddef.store() + + card_ids = {} + for label in ('foo', 'bar', 'baz', 'foo, bar'): + card = carddef.data_class()() + card.data = {'1': label} + card.just_created() + card.store() + card_ids[label] = str(card.id) + formdef.fields[0].display_mode = 'autocomplete' + formdef.fields[0].data_source = {'type': 'carddef:foo', 'value': ''} + formdef.store() + + resp.forms['listing-settings']['filter-4-value'].force_value(card_ids['baz']) + resp = resp.forms['listing-settings'].submit() + assert [x[2] for x in resp.forms['listing-settings']['filter-4-value'].options] == ['card baz'] + def test_backoffice_item_double_filter(pub): pub.user_class.wipe() diff --git a/wcs/backoffice/management.py b/wcs/backoffice/management.py index 7b65ead8c..94f07d26b 100644 --- a/wcs/backoffice/management.py +++ b/wcs/backoffice/management.py @@ -937,6 +937,8 @@ class FormPage(FormdefDirectoryBase): criterias=None, anonymised=False, ): + if self.view and self.view.visibility == 'datasource': + return filter_field.get_options() # remove potential filter on self filter_field_id = get_field_id(filter_field) filtered_criterias = [] @@ -981,22 +983,9 @@ 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=options_criterias, + clause=criterias, ) else: # in case of blocks, this requires digging into the jsonb columns, @@ -1046,7 +1035,7 @@ class FormPage(FormdefDirectoryBase): term = get_request().form.get('_search') if term: options = [x for x in options if term.lower() in x[1].lower()] - options = options[:15] + options = options[:15] if self.view and self.view.visibility == 'datasource': options.append(('{}', _('custom value'))) get_response().set_content_type('application/json') @@ -1298,36 +1287,50 @@ class FormPage(FormdefDirectoryBase): elif filter_field.key in ('item', 'items'): filter_field.required = False - # Get options from existing formdatas. + # Get options from existing formdatas, except for custom views with visibility "datasource" # This allows for options that don't appear anymore in the # data source to be listed (for example because the field # is using a parametrized URL depending on unavailable # variables, or simply returning different results now). + is_datasource_customview = self.view and self.view.visibility == 'datasource' + display_mode = 'select' - if filter_field.key == 'item' and filter_field.get_display_mode() == 'autocomplete': + if ( + not is_datasource_customview + and filter_field.key == 'item' + and filter_field.get_display_mode() == 'autocomplete' + ): display_mode = 'select2' + if is_datasource_customview: + options = filter_field.get_options() + if len(options) > 15: + display_mode = 'select2' is_multi_values = filter_field_operator in ['in', 'not_in', 'between'] if display_mode == 'select': - options = self.get_item_filter_options( - filter_field, - selected_filter, - selected_filter_operator, - criterias, - ) - options = [(x[0], x[1], x[0]) for x in options] + if not is_datasource_customview: + options = self.get_item_filter_options( + filter_field, + selected_filter, + selected_filter_operator, + criterias, + ) + options = [(x[0], x[1], x[0]) for x in options] options.insert(0, (None, '', '')) attrs = {'data-refresh-options': str(filter_field.contextual_id)} else: options = [(None, '', '')] if not is_multi_values: - options = [(filter_field_value, filter_field_value or '', filter_field_value or '')] + value_display = filter_field_value or '' + if filter_field_value: + value_display = filter_field.get_display_value(filter_field_value) + options = [(filter_field_value, value_display, filter_field_value or '')] attrs = {'data-remote-options': str(filter_field.contextual_id)} get_response().add_javascript( ['jquery.js', '../../i18n.js', 'qommon.forms.js', 'select2.js'] ) get_response().add_css_include('select2.css') - if self.view and self.view.visibility == 'datasource': + if is_datasource_customview: options.append(('{}', _('custom value'), '{}')) if filter_field_value and filter_field_value not in [x[0] for x in options]: options.append((filter_field_value, filter_field_value, filter_field_value))