From a889d0f0d475d0a3684de958104ca83383eb3ec9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Tue, 19 Mar 2019 10:14:24 +0100
Subject: [PATCH] fields: change list "show as radio" option into a "display
 mode" option (#31539)

---
 tests/test_backoffice_pages.py |  2 +-
 tests/test_fields.py           | 24 ++++++++++++------------
 tests/test_form_pages.py       |  6 +++---
 tests/test_formdef_import.py   | 32 ++++++++++++++++++++++++++++++++
 wcs/fields.py                  | 32 +++++++++++++++++++++++++++-----
 5 files changed, 75 insertions(+), 21 deletions(-)

diff --git a/tests/test_backoffice_pages.py b/tests/test_backoffice_pages.py
index bc72f1c5d..24213680d 100644
--- a/tests/test_backoffice_pages.py
+++ b/tests/test_backoffice_pages.py
@@ -3543,7 +3543,7 @@ def test_backoffice_workflow_display_form(pub):
             fields.ItemField(
                 id='2', label='Test2', type='item',
                 prefill={'type': 'string', 'value': ''},
-                show_as_radio=True,
+                display_mode='radio',
                 varname='radio',
                 items=['a', 'b', 'c'], required=True))
     status.items.append(display_form)
diff --git a/tests/test_fields.py b/tests/test_fields.py
index 9ec7276a8..6ee672701 100644
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -278,25 +278,25 @@ def test_item_render_as_radio():
         'value': '''['aa', 'ab', 'ac']'''}})
 
     for item_kwargs in items_kwargs:
-        field = fields.ItemField(id='1', label='Foobar', show_as_radio=True, **item_kwargs)
+        field = fields.ItemField(id='1', label='Foobar', display_mode='radio', **item_kwargs)
         form = Form(use_tokens=False)
         field.add_to_form(form)
         assert str(form.render()).count('"radio"') == 3
 
         field = fields.ItemField(id='1', label='Foobar', required=False,
-                show_as_radio=True, **item_kwargs)
+                display_mode='radio', **item_kwargs)
         form = Form(use_tokens=False)
         field.add_to_form(form)
         assert str(form.render()).count('"radio"') == 3
 
         field = fields.ItemField(id='1', label='Foobar',
-                show_as_radio=True, required=False, hint='Bla bla bla', **item_kwargs)
+                display_mode='radio', required=False, hint='Bla bla bla', **item_kwargs)
         form = Form(use_tokens=False)
         field.add_to_form(form)
         assert str(form.render()).count('"radio"') == 3
 
         field = fields.ItemField(id='1', label='Foobar',
-                show_as_radio=True, required=True, hint='Bla bla bla', **item_kwargs)
+                display_mode='radio', required=True, hint='Bla bla bla', **item_kwargs)
         form = Form(use_tokens=False)
         field.add_to_form(form)
         assert str(form.render()).count('"radio"') == 3
@@ -308,52 +308,52 @@ def test_item_render_as_radio():
         'type': 'formula',
         'value': '''[]'''}})
     for item_kwargs in items_kwargs:
-        field = fields.ItemField(id='1', label='Foobar', show_as_radio=True, **item_kwargs)
+        field = fields.ItemField(id='1', label='Foobar', display_mode='radio', **item_kwargs)
         form = Form(use_tokens=False)
         field.add_to_form(form)
         assert str(form.render()).count('"radio"') == 1
 
         field = fields.ItemField(id='1', label='Foobar', required=False,
-                show_as_radio=True, **item_kwargs)
+                display_mode='radio', **item_kwargs)
         form = Form(use_tokens=False)
         field.add_to_form(form)
         assert str(form.render()).count('"radio"') == 1
 
         field = fields.ItemField(id='1', label='Foobar',
-                show_as_radio=True, required=False, hint='Bla bla bla', **item_kwargs)
+                display_mode='radio', required=False, hint='Bla bla bla', **item_kwargs)
         form = Form(use_tokens=False)
         field.add_to_form(form)
         assert str(form.render()).count('"radio"') == 1
 
         field = fields.ItemField(id='1', label='Foobar',
-                show_as_radio=True, required=True, hint='Bla bla bla', **item_kwargs)
+                display_mode='radio', required=True, hint='Bla bla bla', **item_kwargs)
         form = Form(use_tokens=False)
         field.add_to_form(form)
         assert str(form.render()).count('"radio"') == 1
 
 def test_item_radio_lengths():
-    field = fields.ItemField(id='1', label='Foobar', show_as_radio=True,
+    field = fields.ItemField(id='1', label='Foobar', display_mode='radio',
             items=['aa', 'ab', 'ac'])
     form = Form(use_tokens=False)
     field.add_to_form(form)
     form.render()
     assert str(form.widgets[-1].render()).count('<br') == 0
 
-    field = fields.ItemField(id='1', label='Foobar', show_as_radio=True,
+    field = fields.ItemField(id='1', label='Foobar', display_mode='radio',
             items=['aa'*30, 'ab', 'ac'])
     form = Form(use_tokens=False)
     field.add_to_form(form)
     form.render()
     assert str(form.widgets[-1].render()).count('<br') == 2
 
-    field = fields.ItemField(id='1', label='Foobar', show_as_radio=True,
+    field = fields.ItemField(id='1', label='Foobar', display_mode='radio',
             items=['aa', 'ab'*30, 'ac'])
     form = Form(use_tokens=False)
     field.add_to_form(form)
     form.render()
     assert str(form.widgets[-1].render()).count('<br') == 2
 
-    field = fields.ItemField(id='1', label='Foobar', show_as_radio=True,
+    field = fields.ItemField(id='1', label='Foobar', display_mode='radio',
             items=['aa', 'ab', 'ac', 'ad'])
     form = Form(use_tokens=False)
     field.add_to_form(form)
diff --git a/tests/test_form_pages.py b/tests/test_form_pages.py
index 3e763d8b2..6c61eee83 100644
--- a/tests/test_form_pages.py
+++ b/tests/test_form_pages.py
@@ -4611,7 +4611,7 @@ def test_form_page_profile_verified_radio_item_prefill(pub):
     formdef.data_class().wipe()
     formdef.fields = [fields.ItemField(id='0', label='item', type='item',
         items=['bar@localhost', 'foo@localhost', 'baz@localhost'],
-        show_as_radio=True,
+        display_mode='radio',
         prefill={'type': 'user', 'value': 'email'})]
     formdef.store()
 
@@ -4699,7 +4699,7 @@ def test_item_field_with_disabled_items(http_requests, pub):
 
     formdef.data_class().wipe()
     formdef.fields = [fields.ItemField(id='0', label='string', type='item',
-        data_source=ds, show_as_radio=True, display_disabled_items=True)]
+        data_source=ds, display_mode='radio', display_disabled_items=True)]
     formdef.store()
 
     with mock.patch('qommon.misc.urlopen') as urlopen:
@@ -5651,7 +5651,7 @@ def test_field_condition_on_required_field(pub):
         fields.PageField(id='0', label='1st page', type='page'),
         fields.ItemField(type='item', id='1', label='Bar',
             items=['oui', 'non'],
-            show_as_radio=True,
+            display_mode='radio',
             required=True, varname='bar'),
         fields.ItemField(type='item', id='2', label='Foo', size='40',
             required=True,
diff --git a/tests/test_formdef_import.py b/tests/test_formdef_import.py
index e529f9675..8a81626f9 100644
--- a/tests/test_formdef_import.py
+++ b/tests/test_formdef_import.py
@@ -111,6 +111,38 @@ def test_more_fields():
     assert f2.fields[3].minimum_date == formdef.fields[3].minimum_date
     assert f2.fields[4].items == formdef.fields[4].items
 
+def test_item_radio():
+    formdef = FormDef()
+    formdef.name = 'Blah'
+    formdef.fields = [
+        fields.ItemField(
+            type='item',
+            label='Bar',
+            items=['foo', 'bar', 'baz'],
+            list_mode='radio',
+            id='1',
+        ),
+    ]
+
+    # test new mode
+    assert_json_import_export_works(formdef, include_id=True)
+
+    # test conversion of legacy show_as_radio attribute
+    formdef_xml = formdef.export_to_xml(include_id=True)
+    field = formdef_xml.findall('fields/field')[0]
+    ET.SubElement(field, 'show_as_radio').text = 'True'
+    field.remove(field.find('display_mode'))
+    fd2 = FormDef.import_from_xml_tree(formdef_xml, include_id=True)
+    assert fd2.fields[0].display_mode == 'radio'
+
+    # test conversion of legacy show_as_radio attribute
+    formdef_xml = formdef.export_to_xml(include_id=True)
+    field = formdef_xml.findall('fields/field')[0]
+    ET.SubElement(field, 'show_as_radio').text = 'False'
+    field.remove(field.find('display_mode'))
+    fd2 = FormDef.import_from_xml_tree(formdef_xml, include_id=True)
+    assert fd2.fields[0].display_mode == 'list'
+
 def test_include_id():
     formdef = FormDef()
     formdef.name = 'Blah'
diff --git a/wcs/fields.py b/wcs/fields.py
index 1577009c3..563d3d849 100644
--- a/wcs/fields.py
+++ b/wcs/fields.py
@@ -1189,17 +1189,34 @@ class ItemField(WidgetField):
     description = N_('List')
 
     items = []
-    show_as_radio = False
+    show_as_radio = None
     anonymise = False
     widget_class = SingleSelectHintWidget
     data_source = {}
     in_filters = False
     display_disabled_items = False
+    display_mode = 'list'
 
     def __init__(self, **kwargs):
         self.items = []
         WidgetField.__init__(self, **kwargs)
 
+    def migrate(self):
+        changed = super(ItemField, self).migrate()
+        if isinstance(getattr(self, 'show_as_radio', None), bool):
+            if self.show_as_radio:
+                self.display_mode = 'radio'
+            else:
+                self.display_mode = 'list'
+            self.show_as_radio = None
+            changed = True
+        return changed
+
+    def init_with_xml(self, element, charset, include_id=False):
+        super(ItemField, self).init_with_xml(element, charset, include_id=include_id)
+        if getattr(element.find('show_as_radio'), 'text', None) == 'True':
+            self.display_mode = 'radio'
+
     def get_options(self, mode=None):
         if self.data_source:
             return [x[:3] for x in data_sources.get_items(self.data_source, mode=mode)]
@@ -1223,7 +1240,7 @@ class ItemField(WidgetField):
             kwargs['options'] = self.get_options()
         if not kwargs.get('options'):
             kwargs['options'] = [(None, '---')]
-        if self.show_as_radio:
+        if self.display_mode == 'radio':
             self.widget_class = RadiobuttonsWidget
             if type(kwargs['options'][0]) is str:
                 first_items = [x for x in kwargs['options'][:3]]
@@ -1322,8 +1339,13 @@ class ItemField(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(CheckboxWidget, 'show_as_radio', title = _('Show as radio buttons'),
-                value = self.show_as_radio)
+        options = [('list', _('List')),
+                   ('radio', _('Radio buttons')),
+                  ]
+        form.add(RadiobuttonsWidget, 'display_mode',
+                title=_('Display Mode'),
+                options=options,
+                value=self.display_mode)
         form.add(WidgetList, 'items', title = _('Items'), element_type = StringWidget,
                 value = self.items, required = False,
                 element_kwargs = {'render_br': False, 'size': 50},
@@ -1341,7 +1363,7 @@ class ItemField(WidgetField):
 
     def get_admin_attributes(self):
         return WidgetField.get_admin_attributes(self) + ['items',
-                'show_as_radio', 'data_source', 'in_filters', 'anonymise',
+                'display_mode', 'data_source', 'in_filters', 'anonymise',
                 'display_disabled_items']
 
     def check_admin_form(self, form):
-- 
2.20.1

