From 7261d47204217b3a29e4f5253b75dcd316e24652 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Thu, 11 Aug 2022 11:22:37 +0200
Subject: [PATCH 1/8] wcs: add display_mode option for card cell (#68063)

---
 combo/apps/wcs/forms.py                       | 61 +++++++++++------
 .../wcs/migrations/0050_card_display_mode.py  | 21 ++++++
 combo/apps/wcs/models.py                      | 24 ++++++-
 ...html => card-infos-cell-form-display.html} | 21 ++++--
 tests/test_wcs.py                             | 68 +++++++++++++------
 5 files changed, 149 insertions(+), 46 deletions(-)
 create mode 100644 combo/apps/wcs/migrations/0050_card_display_mode.py
 rename combo/apps/wcs/templates/combo/wcs/manager/{card-infos-cell-form.html => card-infos-cell-form-display.html} (88%)

diff --git a/combo/apps/wcs/forms.py b/combo/apps/wcs/forms.py
index 997cbf67..bd796acd 100644
--- a/combo/apps/wcs/forms.py
+++ b/combo/apps/wcs/forms.py
@@ -77,7 +77,6 @@ class WcsCardInfoCellForm(forms.ModelForm):
     with_user = forms.BooleanField(
         label=_('Restrict to cards accessible to the user'), required=False, initial=True
     )
-    customize_display = forms.BooleanField(label=_('Customize display'), required=False)
     related_card_path = forms.ChoiceField(label=_('Card Identifier'), required=False)
 
     class Meta:
@@ -86,35 +85,19 @@ class WcsCardInfoCellForm(forms.ModelForm):
             'carddef_reference',
             'related_card_path',
             'card_ids',
-            'limit',
             'only_for_user',
-            'custom_schema',
         )
         widgets = {
             'card_ids': forms.TextInput(attrs={'class': 'text-wide'}),
-            'custom_schema': forms.HiddenInput(),
         }
 
     def __init__(self, *args, **kwargs):
         instance = kwargs['instance']
         initial = kwargs.pop('initial', {})
         initial['with_user'] = not instance.without_user
-        if kwargs.get('data'):
-            kwargs['data'] = kwargs['data'].copy()  # QueryDict -> dict
-            # make sure there's a value for custom_schema as postgres.forms.jsonb
-            # would crash on None.
-            custom_schema_name = '%s-custom_schema' % kwargs.get('prefix')
-            if custom_schema_name not in kwargs['data']:
-                kwargs['data'][custom_schema_name] = '{}'
         super().__init__(initial=initial, *args, **kwargs)
         card_models = get_wcs_options('/api/cards/@list', include_custom_views=True)
         self.fields['carddef_reference'].widget = forms.Select(choices=card_models)
-        if self.instance.custom_schema:
-            self.initial['customize_display'] = True
-            self.initial['custom_schema'] = self.instance.get_custom_schema()
-        if not self.instance.cached_json:
-            del self.fields['customize_display']
-            del self.fields['custom_schema']
 
         self.fields['related_card_path'].choices = []
         with_sub_slug = any(p.sub_slug for p in self.instance.page.get_parents_and_self())
@@ -132,8 +115,6 @@ class WcsCardInfoCellForm(forms.ModelForm):
 
     def save(self, *args, **kwargs):
         super().save(*args, **kwargs)
-        if not self.cleaned_data.get('customize_display'):
-            self.instance.custom_schema = {}
         if self.instance.related_card_path:
             self.instance.card_ids = ''
         if self.instance.related_card_path == '--':
@@ -151,6 +132,48 @@ class WcsCardInfoCellForm(forms.ModelForm):
         return cleaned_data
 
 
+class WcsCardInfoCellDisplayForm(forms.ModelForm):
+    customize_display = forms.BooleanField(label=_('Customize display'), required=False)
+
+    class Meta:
+        model = WcsCardInfosCell
+        fields = (
+            'limit',
+            'display_mode',
+            'custom_schema',
+        )
+        widgets = {
+            'custom_schema': forms.HiddenInput(),
+        }
+
+    def __init__(self, *args, **kwargs):
+        if kwargs.get('data'):
+            kwargs['data'] = kwargs['data'].copy()  # QueryDict -> dict
+            # make sure there's a value for custom_schema as postgres.forms.jsonb
+            # would crash on None.
+            custom_schema_name = '%s-custom_schema' % kwargs.get('prefix')
+            if custom_schema_name not in kwargs['data']:
+                kwargs['data'][custom_schema_name] = '{}'
+        super().__init__(*args, **kwargs)
+        if callable(self.fields['custom_schema'].initial):
+            self.fields['custom_schema'].initial = {}
+        if self.instance.custom_schema:
+            self.initial['customize_display'] = True
+            self.initial['custom_schema'] = self.instance.get_custom_schema()
+        if not self.instance.cached_json:
+            del self.fields['customize_display']
+            del self.fields['custom_schema']
+
+    def save(self, *args, **kwargs):
+        super().save(*args, **kwargs)
+        if not self.cleaned_data.get('customize_display'):
+            self.instance.custom_schema = {}
+        if self.cleaned_data.get('display_mode') == 'table':
+            self.instance.custom_schema = {}
+        self.instance.save()
+        return self.instance
+
+
 class WcsCategoryCellForm(forms.ModelForm):
     class Meta:
         model = WcsCategoryCell
diff --git a/combo/apps/wcs/migrations/0050_card_display_mode.py b/combo/apps/wcs/migrations/0050_card_display_mode.py
new file mode 100644
index 00000000..1b17e502
--- /dev/null
+++ b/combo/apps/wcs/migrations/0050_card_display_mode.py
@@ -0,0 +1,21 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('wcs', '0049_card_pagination'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='wcscardinfoscell',
+            name='display_mode',
+            field=models.CharField(
+                choices=[('card', 'Card'), ('table', 'Table')],
+                default='card',
+                max_length=10,
+                verbose_name='Display mode',
+            ),
+        ),
+    ]
diff --git a/combo/apps/wcs/models.py b/combo/apps/wcs/models.py
index 28f9dcd2..973e6180 100644
--- a/combo/apps/wcs/models.py
+++ b/combo/apps/wcs/models.py
@@ -946,6 +946,15 @@ class WcsCardInfosCell(CardMixin, CellBase):
         _('Number of cards per page (default 10)'), null=True, blank=True
     )
     custom_schema = JSONField(blank=True, default=dict)
+    display_mode = models.CharField(
+        _('Display mode'),
+        max_length=10,
+        default='card',
+        choices=[
+            ('card', _('Card')),
+            ('table', _('Table')),
+        ],
+    )
 
     title_type = models.CharField(
         _('Title'),
@@ -966,7 +975,6 @@ class WcsCardInfosCell(CardMixin, CellBase):
     is_enabled = classmethod(is_wcs_enabled)
 
     default_template_name = 'combo/wcs/card.html'
-    manager_form_template = 'combo/wcs/manager/card-infos-cell-form.html'
     manager_appearance_template = 'combo/wcs/manager/card-infos-cell-form-appearance.html'
 
     class Meta:
@@ -1507,6 +1515,20 @@ class WcsCardInfosCell(CardMixin, CellBase):
     def get_appearance_fields(self):
         return ['title_type', 'custom_title']
 
+    def get_manager_tabs(self):
+        from .forms import WcsCardInfoCellDisplayForm
+
+        tabs = super().get_manager_tabs()
+        tabs.append(
+            {
+                'slug': 'display',
+                'name': _('Display'),
+                'template': 'combo/wcs/manager/card-infos-cell-form-display.html',
+                'form': WcsCardInfoCellDisplayForm,
+            },
+        )
+        return tabs
+
     def get_custom_schema(self):
         custom_schema = self.custom_schema or {}
 
diff --git a/combo/apps/wcs/templates/combo/wcs/manager/card-infos-cell-form.html b/combo/apps/wcs/templates/combo/wcs/manager/card-infos-cell-form-display.html
similarity index 88%
rename from combo/apps/wcs/templates/combo/wcs/manager/card-infos-cell-form.html
rename to combo/apps/wcs/templates/combo/wcs/manager/card-infos-cell-form-display.html
index 1b737a63..306cb859 100644
--- a/combo/apps/wcs/templates/combo/wcs/manager/card-infos-cell-form.html
+++ b/combo/apps/wcs/templates/combo/wcs/manager/card-infos-cell-form-display.html
@@ -1,8 +1,22 @@
-{% extends "combo/cell_form.html" %}
 {% load i18n %}
 
-{% block cell-form %}
-{{ block.super }}
+{{ display_form.as_p }}
+<script>
+  {# display/hide custom_schema fields #}
+  $('#id_cwcs_wcscardinfoscell-{{ cell.pk }}-display_mode').on('change', function() {
+    if ($(this).val() == 'card') {
+      $('#id_cwcs_wcscardinfoscell-{{ cell.pk }}-customize_display').parent().show();
+      if ($('#id_cwcs_wcscardinfoscell-{{ cell.pk }}-customize_display').val()) {
+        $('.wcs-cards-cell--grid', $('#id_cwcs_wcscardinfoscell-4-customize_display').parents('.cell')).show();
+      }
+    } else {
+      $('#id_cwcs_wcscardinfoscell-{{ cell.pk }}-customize_display').parent().hide();
+      $('.wcs-cards-cell--grid', $('#id_cwcs_wcscardinfoscell-4-customize_display').parents('.cell')).hide();
+    }
+  });
+  $('#id_cwcs_wcscardinfoscell-{{ cell.pk }}-display_mode').change();
+</script>
+
 {% if card_schema %}
 {{ card_schema|json_script:card_schema_id }}
 
@@ -166,4 +180,3 @@
 </script>
 
 {% endif %}
-{% endblock %}
diff --git a/tests/test_wcs.py b/tests/test_wcs.py
index 52eb2298..7260ee83 100644
--- a/tests/test_wcs.py
+++ b/tests/test_wcs.py
@@ -21,6 +21,7 @@ from requests.models import Response
 from combo.apps.search.engines import engines
 from combo.apps.search.models import SearchCell
 from combo.apps.search.utils import index_site, search_site
+from combo.apps.wcs.forms import WcsCardInfoCellDisplayForm
 from combo.apps.wcs.models import (
     BackofficeSubmissionCell,
     CategoriesCell,
@@ -2015,39 +2016,41 @@ def test_card_cell_setup(mock_send, app, admin_user):
         ('other:card_d', 'test2 : Card D'),
         ('other:card_e', 'test2 : Card E'),
     ]
-    assert 'customize_display' not in form.fields
-    assert 'custom_schema' not in form.fields
+
+    form_display = WcsCardInfoCellDisplayForm(instance=cell)
+    assert 'customize_display' not in form_display.fields
+    assert 'custom_schema' not in form_display.fields
 
     cell.save()
-    assert 'customize_display' not in form.fields
-    assert 'custom_schema' not in form.fields
+    assert 'customize_display' not in form_display.fields
+    assert 'custom_schema' not in form_display.fields
 
     cell.carddef_reference = 'default:card_model_1'
     cell.save()
-    form = form_class(instance=cell)
-    assert 'customize_display' in form.fields
-    assert 'custom_schema' in form.fields
-    assert 'customize_display' not in form.initial
-    assert form.initial['custom_schema'] == {}
+    form_display = WcsCardInfoCellDisplayForm(instance=cell)
+    assert 'customize_display' in form_display.fields
+    assert 'custom_schema' in form_display.fields
+    assert 'customize_display' not in form_display.initial
+    assert form_display.initial['custom_schema'] == {}
 
     cell.carddef_reference = 'default:card_model_1:foo'
     cell.save()
-    form = form_class(instance=cell)
-    assert 'customize_display' in form.fields
-    assert 'custom_schema' in form.fields
-    assert 'customize_display' not in form.initial
-    assert form.initial['custom_schema'] == {}
+    form_display = WcsCardInfoCellDisplayForm(instance=cell)
+    assert 'customize_display' in form_display.fields
+    assert 'custom_schema' in form_display.fields
+    assert 'customize_display' not in form_display.initial
+    assert form_display.initial['custom_schema'] == {}
 
     cell.carddef_reference = 'default:card_model_1'
     cell.save()
 
     cell.custom_schema = {'cells': [{'varname': 'foo', 'display_mode': 'value'}]}
     cell.save()
-    form = form_class(instance=cell)
-    assert 'customize_display' in form.fields
-    assert 'custom_schema' in form.fields
-    assert form.initial['customize_display'] is True
-    assert form.initial['custom_schema'] == {
+    form_display = WcsCardInfoCellDisplayForm(instance=cell)
+    assert 'customize_display' in form_display.fields
+    assert 'custom_schema' in form_display.fields
+    assert form_display.initial['customize_display'] is True
+    assert form_display.initial['custom_schema'] == {
         'cells': [
             {'varname': 'foo', 'field_content': 'value', 'display_mode': 'text', 'empty_value': '@empty@'}
         ]
@@ -2067,7 +2070,6 @@ def test_card_cell_setup(mock_send, app, admin_user):
     cell.refresh_from_db()
 
     # check getting back to uncustomized display reset the schema
-    cell.customize_display = True
     cell.custom_schema = {
         'cells': [
             {'varname': 'foo', 'field_content': 'value', 'display_mode': 'text', 'empty_value': '@empty@'}
@@ -2082,6 +2084,19 @@ def test_card_cell_setup(mock_send, app, admin_user):
     cell.refresh_from_db()
     assert cell.custom_schema == {}
 
+    cell.custom_schema = {
+        'cells': [
+            {'varname': 'foo', 'field_content': 'value', 'display_mode': 'text', 'empty_value': '@empty@'}
+        ]
+    }
+    cell.save()
+    resp = app.get('/manage/pages/%s/' % page.pk)
+    assert resp.forms[0]['c%s-display_mode' % cell.get_reference()].value == 'card'
+    resp.forms[0]['c%s-display_mode' % cell.get_reference()].value = 'table'
+    manager_submit_cell(resp.forms[0])
+    cell.refresh_from_db()
+    assert cell.custom_schema == {}
+
     assert cell.related_card_path == ''
     assert cell.card_ids == ''
     resp = app.get('/manage/pages/%s/' % page.pk)
@@ -2541,18 +2556,27 @@ def test_manager_card_cell(mock_send, app, admin_user):
     ]
 
 
-def test_manager_card_cell_tabs(app, admin_user):
+@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
+def test_manager_card_cell_tabs(mock_send, app, admin_user):
     page = Page.objects.create(title='xxx', slug='test_cards', template_name='standard', sub_slug='foobar')
     cell = WcsCardInfosCell.objects.create(page=page, placeholder='content', order=0)
 
     app = login(app)
     resp = app.get('/manage/pages/%s/' % page.pk)
-
     assert not resp.pyquery('[data-tab-slug="general"] select[name$="title_type"]')
     assert not resp.pyquery('[data-tab-slug="general"] input[name$="custom_title"]')
+    assert not resp.pyquery('[data-tab-slug="general"] input[name$="limit"]')
     assert not resp.pyquery('#tab-%s-general.pk-tabs--button-marker' % cell.get_reference())
     assert resp.pyquery('[data-tab-slug="appearance"] select[name$="title_type"]')
     assert resp.pyquery('[data-tab-slug="appearance"] input[name$="custom_title"]')
+    assert not resp.pyquery('[data-tab-slug="appearance"] input[name$="customize_display"]')
+    assert resp.pyquery('[data-tab-slug="display"] input[name$="limit"]')
+
+    cell.carddef_reference = 'default:card_model_1'
+    cell.save()
+    resp = app.get('/manage/pages/%s/' % page.pk)
+    assert resp.pyquery('#tab-%s-general.pk-tabs--button-marker' % cell.get_reference())
+    assert resp.pyquery('[data-tab-slug="display"] input[name$="customize_display"]')
 
 
 @mock.patch('requests.Session.send', side_effect=mocked_requests_send)
-- 
2.35.1

