From 3a0c3f29e462c3308deff8d8241316769d0b3059 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Tue, 9 Aug 2022 15:31:59 +0200
Subject: [PATCH 5/5] wcs: option to list all cards (#68037)

---
 combo/apps/wcs/forms.py  |  1 +
 combo/apps/wcs/models.py | 29 +++++++++++++-----
 tests/test_wcs.py        | 65 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 88 insertions(+), 7 deletions(-)

diff --git a/combo/apps/wcs/forms.py b/combo/apps/wcs/forms.py
index 2047a558..997cbf67 100644
--- a/combo/apps/wcs/forms.py
+++ b/combo/apps/wcs/forms.py
@@ -126,6 +126,7 @@ class WcsCardInfoCellForm(forms.ModelForm):
             if not self.instance.card_ids and not self.instance.related_card_path:
                 self.initial['related_card_path'] = '--'
         self.fields['related_card_path'].choices += [
+            ('__all__', _('All cards')),
             ('', _('Other Card Identifiers')),
         ] + self.instance.get_related_card_paths()
 
diff --git a/combo/apps/wcs/models.py b/combo/apps/wcs/models.py
index 6eec1e09..6c7ea043 100644
--- a/combo/apps/wcs/models.py
+++ b/combo/apps/wcs/models.py
@@ -1014,9 +1014,9 @@ class WcsCardInfosCell(CardMixin, CellBase):
         return super().is_visible(request, **kwargs)
 
     def check_validity(self):
-        if self.related_card_path:
+        if self.get_related_card_path():
             relations = [r[0] for r in self.get_related_card_paths()]
-            if self.related_card_path not in relations:
+            if self.get_related_card_path() not in relations:
                 self.mark_as_invalid('wcs_card_relation_not_found')
                 return
 
@@ -1034,6 +1034,16 @@ class WcsCardInfosCell(CardMixin, CellBase):
     def get_repeat_template(self, context):
         return len(context.get(self.global_context_key) or [])
 
+    def get_related_card_path(self):
+        if self.related_card_path == '__all__':
+            return ''
+        return self.related_card_path
+
+    def get_all(self):
+        if self.related_card_path == '__all__':
+            return True
+        return False
+
     def get_cards_from_ids(self, card_ids, context, synchronous=False):
         # if check_user, get all cards from ids in context, with correct filter-user-uuid and without_user value
         # use custom view if requested
@@ -1046,7 +1056,8 @@ class WcsCardInfosCell(CardMixin, CellBase):
         user = self.get_concerned_user(context)
         if self.only_for_user and user and not user.is_anonymous and user.get_name_id():
             api_url += '&filter-user-uuid=%s' % user.get_name_id()
-        api_url += '&%s' % '&'.join(['filter-internal-id=%s' % cid for cid in card_ids])
+        if not self.get_all():
+            api_url += '&%s' % '&'.join(['filter-internal-id=%s' % cid for cid in card_ids])
         if not synchronous:
             synchronous = bool(context.get('synchronous'))
         wcs_site = get_wcs_services().get(self.wcs_site)
@@ -1313,7 +1324,7 @@ class WcsCardInfosCell(CardMixin, CellBase):
                 parts=parts[1:],
             )
 
-        first_cell_slug = self.related_card_path.split('/')[0]
+        first_cell_slug = self.get_related_card_path().split('/')[0]
         try:
             first_cell = WcsCardInfosCell.objects.get(page=self.page_id, slug=first_cell_slug)
         except (WcsCardInfosCell.DoesNotExist, WcsCardInfosCell.MultipleObjectsReturned):
@@ -1341,7 +1352,7 @@ class WcsCardInfosCell(CardMixin, CellBase):
         if not card_data:
             # card data not found
             return []
-        parts = self.related_card_path.split('/')[1:]
+        parts = self.get_related_card_path().split('/')[1:]
         return follow_data(
             card_data=card_data,
             relations=first_cell.cached_json['relations'],
@@ -1363,7 +1374,7 @@ class WcsCardInfosCell(CardMixin, CellBase):
             # not configured
             return []
 
-        if self.related_card_path:
+        if self.get_related_card_path():
             # look at other cells to get related ids
             card_ids = self.get_card_ids_from_related(original_context, request)
             if card_ids == []:
@@ -1381,6 +1392,10 @@ class WcsCardInfosCell(CardMixin, CellBase):
                     )
                 return self.filter_card_ids(card_ids, original_context)
 
+        if self.get_all():
+            # get all cards
+            return [c['id'] for c in self.get_cards_from_ids([], original_context, synchronous=True)]
+
         if self.card_ids:
             # card ids template is defined
             return self.get_card_ids_from_template(self.card_ids, original_context, request)
@@ -1425,7 +1440,7 @@ class WcsCardInfosCell(CardMixin, CellBase):
         card_id = self.get_card_id(context)
         if not card_id:
             if self.card_ids or self.related_card_path:
-                # template or related_card_path defined, but result is empty or None
+                # all cards, template or related_card_path defined, but result is empty or None
                 extra_context['card_not_found'] = True
             return extra_context
 
diff --git a/tests/test_wcs.py b/tests/test_wcs.py
index c50a14c3..52eb2298 100644
--- a/tests/test_wcs.py
+++ b/tests/test_wcs.py
@@ -2335,10 +2335,23 @@ def test_manager_card_cell(mock_send, app, admin_user):
     # but only one cell on the page, no relations to follow
     assert resp.forms[0]['c%s-related_card_path' % cell.get_reference()].options == [
         ('--', True, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
+        ('', False, 'Other Card Identifiers'),
+    ]
+
+    # all cards
+    cell.related_card_path = '__all__'
+    cell.save()
+    resp = app.get('/manage/pages/%s/' % page.pk)
+    assert resp.forms[0]['c%s-related_card_path' % cell.get_reference()].options == [
+        ('--', False, 'Identifier from page URL'),
+        ('__all__', True, 'All cards'),
         ('', False, 'Other Card Identifiers'),
     ]
 
     # add a second cell, related to the first card model
+    cell.related_card_path = ''
+    cell.save()
     cell2 = WcsCardInfosCell.objects.create(
         page=page, placeholder='content', order=1, carddef_reference='default:card_b'
     )
@@ -2346,11 +2359,13 @@ def test_manager_card_cell(mock_send, app, admin_user):
     # still no relation to follow
     assert resp.forms[0]['c%s-related_card_path' % cell.get_reference()].options == [
         ('--', True, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
         ('', False, 'Other Card Identifiers'),
     ]
     # no cell with id and slug
     assert resp.forms[1]['c%s-related_card_path' % cell2.get_reference()].options == [
         ('--', True, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
         ('', False, 'Other Card Identifiers'),
     ]
 
@@ -2361,11 +2376,13 @@ def test_manager_card_cell(mock_send, app, admin_user):
     # still no relation to follow
     assert resp.forms[0]['c%s-related_card_path' % cell.get_reference()].options == [
         ('--', True, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
         ('', False, 'Other Card Identifiers'),
     ]
     # multiple relations to follow
     assert resp.forms[1]['c%s-related_card_path' % cell2.get_reference()].options == [
         ('--', True, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
         ('', False, 'Other Card Identifiers'),
         ('sluga/cardb', False, 'sluga/cardb'),
         ('sluga/cardsb', False, 'sluga/cardsb'),
@@ -2382,11 +2399,13 @@ def test_manager_card_cell(mock_send, app, admin_user):
     # still no relation to follow
     assert resp.forms[0]['c%s-related_card_path' % cell.get_reference()].options == [
         ('--', False, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
         ('', True, 'Other Card Identifiers'),
     ]
     # can not user cell with multiple ids as reference
     assert resp.forms[1]['c%s-related_card_path' % cell2.get_reference()].options == [
         ('--', True, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
         ('', False, 'Other Card Identifiers'),
     ]
 
@@ -2399,6 +2418,7 @@ def test_manager_card_cell(mock_send, app, admin_user):
     # multiple relations to follow
     assert resp.forms[0]['c%s-related_card_path' % cell.get_reference()].options == [
         ('--', True, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
         ('', False, 'Other Card Identifiers'),
         ('slugb/reverse:cardb', False, 'slugb/cardb (reverse)'),
         ('slugb/reverse:cardsb', False, 'slugb/cardsb (reverse)'),
@@ -2407,6 +2427,7 @@ def test_manager_card_cell(mock_send, app, admin_user):
     # still multiple relations to follow
     assert resp.forms[1]['c%s-related_card_path' % cell2.get_reference()].options == [
         ('--', True, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
         ('', False, 'Other Card Identifiers'),
         ('sluga/cardb', False, 'sluga/cardb'),
         ('sluga/cardsb', False, 'sluga/cardsb'),
@@ -2427,11 +2448,13 @@ def test_manager_card_cell(mock_send, app, admin_user):
     # no more relation to follow
     assert resp.forms[0]['c%s-related_card_path' % cell.get_reference()].options == [
         ('--', True, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
         ('', False, 'Other Card Identifiers'),
     ]
     # still multiple relations to follow
     assert resp.forms[1]['c%s-related_card_path' % cell2.get_reference()].options == [
         ('--', False, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
         ('', False, 'Other Card Identifiers'),
         ('sluga/cardb', True, 'sluga/cardb'),
         ('sluga/cardsb', False, 'sluga/cardsb'),
@@ -2456,12 +2479,14 @@ def test_manager_card_cell(mock_send, app, admin_user):
     resp = app.get('/manage/pages/%s/' % page.pk)
     assert resp.forms[0]['c%s-related_card_path' % cell.get_reference()].options == [
         ('--', True, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
         ('', False, 'Other Card Identifiers'),
         ('slugd/cardd-foo/carde-foo', False, 'slugd/cardd-foo/carde-foo'),
         ('slugd/carde-foo', False, 'slugd/carde-foo'),
     ]
     assert resp.forms[1]['c%s-related_card_path' % cell2.get_reference()].options == [
         ('--', True, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
         ('', False, 'Other Card Identifiers'),
         ('sluge/cardd-bar', False, 'sluge/cardd-bar'),
         ('sluge/reverse:carde-foo', False, 'sluge/carde-foo (reverse)'),
@@ -2477,6 +2502,7 @@ def test_manager_card_cell(mock_send, app, admin_user):
     resp = app.get('/manage/pages/%s/' % page.pk)
     assert resp.forms[0]['c%s-related_card_path' % cell.get_reference()].options == [
         ('--', True, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
         ('', False, 'Other Card Identifiers'),
         ('slugd-bis/cardd-foo', False, 'slugd-bis/cardd-foo'),
         ('slugd-bis/reverse:cardd-foo', False, 'slugd-bis/cardd-foo (reverse)'),
@@ -2485,6 +2511,7 @@ def test_manager_card_cell(mock_send, app, admin_user):
     ]
     assert resp.forms[1]['c%s-related_card_path' % cell2.get_reference()].options == [
         ('--', True, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
         ('', False, 'Other Card Identifiers'),
         ('slugd/cardd-foo', False, 'slugd/cardd-foo'),
         ('slugd/reverse:cardd-foo', False, 'slugd/cardd-foo (reverse)'),
@@ -2502,11 +2529,13 @@ def test_manager_card_cell(mock_send, app, admin_user):
     resp = app.get('/manage/pages/%s/' % page.pk)
     assert resp.forms[0]['c%s-related_card_path' % cell.get_reference()].options == [
         ('--', True, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
         ('', False, 'Other Card Identifiers'),
         ('sluge-bis/cardd-bar/carde-foo', False, 'sluge-bis/cardd-bar/carde-foo'),
     ]
     assert resp.forms[1]['c%s-related_card_path' % cell2.get_reference()].options == [
         ('--', True, 'Identifier from page URL'),
+        ('__all__', False, 'All cards'),
         ('', False, 'Other Card Identifiers'),
         ('sluge/cardd-bar/carde-foo', False, 'sluge/cardd-bar/carde-foo'),
     ]
@@ -3189,6 +3218,42 @@ def test_card_cell_render_custom_schema_link_entry(mock_send, context, app):
     )
 
 
+@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
+def test_card_cell_render_all_cards(mock_send, nocache, app):
+    page = Page.objects.create(title='xxx', slug='foo', template_name='standard')
+    cell = WcsCardInfosCell.objects.create(
+        page=page,
+        placeholder='content',
+        order=0,
+        carddef_reference='default:card_model_1',
+        related_card_path='__all__',
+    )
+
+    cell_url = reverse(
+        'combo-public-ajax-page-cell',
+        kwargs={'page_pk': page.pk, 'cell_reference': cell.get_reference()},
+    )
+
+    # check url called
+    mock_send.reset_mock()
+    resp = app.get(page.get_online_url())
+    assert len(resp.context['cells']) == 3
+    extra_ctx = re.findall(r'data-extra-context="(.*)"', resp.text)
+    for i in range(0, 3):
+        assert resp.context['cells'][i].pk == cell.pk
+        assert resp.context['cells'][i].repeat_index == i
+        cell_resp = app.get(cell_url + '?ctx=' + extra_ctx[i])
+        assert cell_resp.context['repeat_index'] == i
+    assert len(mock_send.call_args_list) == 7
+    # page rendering
+    assert '/api/cards/card_model_1/list' in mock_send.call_args_list[0][0][0].url
+    # cell rendering
+    for i in range(0, 3):
+        assert '/api/cards/card_model_1/list' in mock_send.call_args_list[i * 2 + 1][0][0].url
+        assert '/api/cards/card_model_1/list' in mock_send.call_args_list[i * 2 + 2][0][0].url
+        assert 'filter-internal-id' not in mock_send.call_args_list[i * 2 + 2][0][0].url
+
+
 @mock.patch('requests.Session.send', side_effect=mocked_requests_send)
 def test_card_cell_render_identifier(mock_send, nocache, app):
     page = Page.objects.create(
-- 
2.35.1

