From cff4054f91fc9fd1ed817bae853fc982a861a06c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Fri, 8 Jan 2021 14:39:37 +0100
Subject: [PATCH] fields: ItemsField & items/datasource in admin form (#49953)

---
 tests/admin_pages/test_form.py |  25 +++++++-
 wcs/fields.py                  | 105 +++++++++++++++------------------
 2 files changed, 72 insertions(+), 58 deletions(-)

diff --git a/tests/admin_pages/test_form.py b/tests/admin_pages/test_form.py
index 7d0574c8..310e87c4 100644
--- a/tests/admin_pages/test_form.py
+++ b/tests/admin_pages/test_form.py
@@ -1621,6 +1621,14 @@ def test_form_edit_item_field_data_source(pub):
     resp = resp.form.submit('submit').follow()
     resp = app.get('/backoffice/forms/1/')
 
+    # change configuration for items
+    resp = app.get('/backoffice/forms/1/fields/1/')
+    resp.form['data_mode'].value = 'simple-list'
+    resp.form['items$element0'] = 'XXX'
+    resp = resp.form.submit('submit').follow()
+    assert FormDef.get(1).fields[0].data_source is None
+    assert FormDef.get(1).fields[0].items == ['XXX']
+
 
 def test_form_edit_item_field_geojson_data_source(pub, http_requests):
     NamedDataSource.wipe()
@@ -1691,7 +1699,7 @@ def test_form_edit_items_field(pub):
     resp = resp.follow()
 
     assert FormDef.get(1).fields[0].label == 'changed field'
-    assert FormDef.get(1).fields[0].required == False
+    assert FormDef.get(1).fields[0].required is False
     assert FormDef.get(1).fields[0].items is None
 
     # edit and fill with one item
@@ -1708,6 +1716,21 @@ def test_form_edit_items_field(pub):
     assert resp.forms[0]['prefill$type'].options == [
             (u'None', True, u'None'), (u'Python Expression', False, u'Python Expression')]
 
+    # change configuration for datasource
+    resp = app.get('/backoffice/forms/1/fields/1/')
+    resp.form['data_mode'].value = 'data-source'
+    resp.form['data_source$type'].value = 'json'
+    resp.form['data_source$value'].value = 'http://whatever'
+    resp = resp.form.submit('submit').follow()
+    assert FormDef.get(1).fields[0].data_source == {'type': 'json', 'value': 'http://whatever'}
+
+    # change configuration for items
+    resp = app.get('/backoffice/forms/1/fields/1/')
+    resp.form['data_mode'].value = 'simple-list'
+    resp = resp.form.submit('submit').follow()
+    assert FormDef.get(1).fields[0].data_source is None
+    assert FormDef.get(1).fields[0].items == ['XXX']
+
 
 def test_form_edit_page_field(pub):
     create_superuser(pub)
diff --git a/wcs/fields.py b/wcs/fields.py
index 47a679fb..147f210e 100644
--- a/wcs/fields.py
+++ b/wcs/fields.py
@@ -1475,7 +1475,49 @@ class MapOptionsMixin:
                         _('Initial zoom level must be between minimal and maximal zoom levels.'))
 
 
-class ItemField(WidgetField, MapOptionsMixin):
+class ItemFieldMixin(object):
+    def add_items_fields_admin_form(self, form):
+        real_data_source = data_sources.get_real(self.data_source)
+        form.add(RadiobuttonsWidget, 'data_mode',
+                 title=_('Data'),
+                 options=[('simple-list', _('Simple List'), 'simple-list'),
+                          ('data-source', _('Data Source'), 'data-source')],
+                 value='data-source' if real_data_source else 'simple-list',
+                 attrs={'data-dynamic-display-parent': 'true'},
+                 extra_css_class='no-bottom-margin')
+        form.add(WidgetList, 'items', element_type=StringWidget,
+                 value=self.items,
+                 required=False,
+                 element_kwargs={'render_br': False, 'size': 50},
+                 add_element_label=_('Add item'),
+                 attrs={'data-dynamic-display-child-of': 'data_mode',
+                        'data-dynamic-display-value': 'simple-list'}
+                 )
+        form.add(data_sources.DataSourceSelectionWidget, 'data_source',
+                 value=self.data_source,
+                 required=False,
+                 hint=_('This will get the available items from an external source.'),
+                 attrs={'data-dynamic-display-child-of': 'data_mode',
+                        'data-dynamic-display-value': 'data-source'}
+                 )
+
+    def check_items_admin_form(self, form):
+        data_mode = form.get_widget('data_mode').parse()
+        if data_mode == 'simple-list':
+            items = form.get_widget('items').parse()
+            d = {}
+            for v in (items or []):
+                if v in d:
+                    form.set_error('items', _('Duplicated Items'))
+                    return
+                d[v] = None
+
+            data_source_type = form.get_widget('data_source').get_widget('type')
+            data_source_type.set_value(None)
+            data_source_type.transfer_form_value(get_request())
+
+
+class ItemField(WidgetField, MapOptionsMixin, ItemFieldMixin):
     key = 'item'
     description = N_('List')
     allow_complex = True
@@ -1695,29 +1737,7 @@ class ItemField(WidgetField, MapOptionsMixin):
                 options=options,
                 value=self.display_mode,
                 attrs={'data-dynamic-display-parent': 'true'})
-        real_data_source = data_sources.get_real(self.data_source)
-        form.add(RadiobuttonsWidget, 'data_mode',
-                title=_('Data'),
-                options=[('simple-list', _('Simple List'), 'simple-list'),
-                         ('data-source', _('Data Source'), 'data-source')],
-                value='data-source' if real_data_source else 'simple-list',
-                attrs={'data-dynamic-display-parent': 'true'},
-                extra_css_class='no-bottom-margin')
-        form.add(WidgetList, 'items', element_type=StringWidget,
-                value=self.items,
-                required=False,
-                element_kwargs={'render_br': False, 'size': 50},
-                add_element_label=_('Add item'),
-                attrs={'data-dynamic-display-child-of': 'data_mode',
-                       'data-dynamic-display-value': 'simple-list'}
-                )
-        form.add(data_sources.DataSourceSelectionWidget, 'data_source',
-                 value=self.data_source,
-                 required=False,
-                 hint=_('This will get the available items from an external source.'),
-                 attrs={'data-dynamic-display-child-of': 'data_mode',
-                        'data-dynamic-display-value': 'data-source'}
-                 )
+        self.add_items_fields_admin_form(form)
         form.add(CheckboxWidget, 'display_disabled_items',
                 title=_('Display disabled items'),
                 value=self.display_disabled_items,
@@ -1732,20 +1752,7 @@ class ItemField(WidgetField, MapOptionsMixin):
                 'display_disabled_items', 'initial_zoom', 'min_zoom', 'max_zoom']
 
     def check_admin_form(self, form):
-        data_mode = form.get_widget('data_mode').parse()
-        if data_mode == 'simple-list':
-            items = form.get_widget('items').parse()
-            d = {}
-            for v in (items or []):
-                if v in d:
-                    form.set_error('items', _('Duplicated Items'))
-                    return
-                d[v] = None
-
-            data_source_type = form.get_widget('data_source').get_widget('type')
-            data_source_type.set_value(None)
-            data_source_type.transfer_form_value(get_request())
-
+        self.check_items_admin_form(form)
         self.check_zoom_admin_form(form)
 
     def stats(self, values):
@@ -1785,7 +1792,7 @@ class ItemField(WidgetField, MapOptionsMixin):
 register_field_class(ItemField)
 
 
-class ItemsField(WidgetField):
+class ItemsField(WidgetField, ItemFieldMixin):
     key = 'items'
     description = N_('Multiple choice list')
     allow_complex = True
@@ -1831,19 +1838,9 @@ class ItemsField(WidgetField):
         WidgetField.fill_admin_form(self, form)
         form.add(CheckboxWidget, 'in_filters', title=_('Display in default filters'),
                 value=self.in_filters, advanced=True)
-        form.add(WidgetList, 'items', title = _('Items'), element_type = StringWidget,
-                value = self.items, required = False,
-                element_kwargs = {'render_br': False, 'size': 50},
-                add_element_label = _('Add item'))
+        self.add_items_fields_admin_form(form)
         form.add(IntWidget, 'max_choices', title = _('Maximum number of choices'),
                 value = self.max_choices, required = False, size = 4)
-        form.add(data_sources.DataSourceSelectionWidget, 'data_source',
-                 value=self.data_source,
-                 allow_jsonp=False,
-                 title=_('Data Source'),
-                 hint=_('This will get the available items from an external source.'),
-                 required=False,
-                 advanced=is_datasource_advanced(self.data_source))
         form.add(CheckboxWidget, 'display_disabled_items',
                 title=_('Display disabled items'),
                 value=self.display_disabled_items,
@@ -1855,13 +1852,7 @@ class ItemsField(WidgetField):
                 'display_disabled_items']
 
     def check_admin_form(self, form):
-        items = form.get_widget('items').parse()
-        d = {}
-        for v in (items or []):
-            if v in d:
-                form.set_error('items', _('Duplicated Items'))
-                return
-            d[v] = None
+        self.check_items_admin_form(form)
 
     def get_prefill_value(self, user=None, force_string=True):
         return super(ItemsField, self).get_prefill_value(user=user, force_string=False)
-- 
2.29.2

