From 79cdd4ebacae65dbf0be5f2e3cfaddd7ac8e5773 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Tue, 27 Apr 2021 14:37:05 +0200
Subject: [PATCH] backoffice: global view filtered bu category_slugs (#53373)

---
 tests/api/test_formdata.py         |  1 -
 tests/backoffice_pages/test_all.py | 78 ++++++++++++++++++++++++------
 wcs/api.py                         |  6 ---
 wcs/backoffice/management.py       | 37 +++++++++++---
 4 files changed, 93 insertions(+), 29 deletions(-)

diff --git a/tests/api/test_formdata.py b/tests/api/test_formdata.py
index f882c1a2..8398fd24 100644
--- a/tests/api/test_formdata.py
+++ b/tests/api/test_formdata.py
@@ -1090,7 +1090,6 @@ def test_api_global_listing(pub, local_user):
     # check error handling
     get_app(pub).get(sign_uri('/api/forms/?status=done&limit=plop', user=local_user), status=400)
     get_app(pub).get(sign_uri('/api/forms/?status=done&offset=plop', user=local_user), status=400)
-    get_app(pub).get(sign_uri('/api/forms/?category_id=plop', user=local_user), status=400)
 
     # check when there are missing statuses
     for formdata in data_class.select():
diff --git a/tests/backoffice_pages/test_all.py b/tests/backoffice_pages/test_all.py
index 23cd7464..9889aec2 100644
--- a/tests/backoffice_pages/test_all.py
+++ b/tests/backoffice_pages/test_all.py
@@ -3170,39 +3170,89 @@ def test_category_in_global_listing(pub):
         pytest.skip('this requires SQL')
         return
 
+    FormDef.wipe()
+    Category.wipe()
+
     create_user(pub)
-    create_environment(pub)
+
+    formdef = FormDef()
+    formdef.name = 'form-3'
+    formdef.workflow_roles = {'_receiver': 1}
+    formdef.store()
+    formdata = formdef.data_class()()
+    formdata.just_created()
+    formdata.jump_status('new')
+    formdata.store()
 
     app = login(get_app(pub))
     resp = app.get('/backoffice/management/listing?limit=500')
-    assert 'category_id' not in resp.forms['listing-settings'].fields
+    assert 'category_ids$element0' not in resp.forms['listing-settings'].fields
 
     cat1 = Category(name='cat1')
+    cat1.position = 1
     cat1.store()
-    formdef = FormDef.get_by_urlname('form-title')
+    formdef = FormDef()
+    formdef.name = 'form-1'
     formdef.category_id = cat1.id
+    formdef.workflow_roles = {'_receiver': 1}
     formdef.store()
+    formdata = formdef.data_class()()
+    formdata.just_created()
+    formdata.jump_status('new')
+    formdata.store()
 
     cat2 = Category(name='cat2')
+    cat1.position = 2
     cat2.store()
-    formdef = FormDef.get_by_urlname('other-form')
+    formdef = FormDef()
+    formdef.name = 'form-2'
     formdef.category_id = cat2.id
+    formdef.workflow_roles = {'_receiver': 1}
     formdef.store()
+    formdata = formdef.data_class()()
+    formdata.just_created()
+    formdata.jump_status('new')
+    formdata.store()
 
-    resp = app.get('/backoffice/management/listing?limit=500')
-    assert 'category_id' in resp.forms['listing-settings'].fields
-    assert 'management/other-form/' in resp.text
-    assert 'management/form-title/' in resp.text
+    resp = app.get('/backoffice/management/listing')
+    assert 'category_ids$element0' in resp.forms['listing-settings'].fields
+    assert 'management/form-1/' in resp.text
+    assert 'management/form-2/' in resp.text
+    assert 'management/form-3/' in resp.text
+
+    resp.forms['listing-settings']['category_ids$element0'] = cat1.id
+    resp = resp.forms['listing-settings'].submit()
+    assert 'management/form-1/' in resp.text
+    assert 'management/form-2/' not in resp.text
+    assert 'management/form-3/' not in resp.text
 
-    resp.forms['listing-settings']['category_id'] = cat1.id
+    resp.forms['listing-settings']['category_ids$element0'] = cat2.id
     resp = resp.forms['listing-settings'].submit()
-    assert 'management/other-form/' not in resp.text
-    assert 'management/form-title/' in resp.text
+    assert 'management/form-1/' not in resp.text
+    assert 'management/form-2/' in resp.text
+    assert 'management/form-3/' not in resp.text
 
-    resp.forms['listing-settings']['category_id'] = cat2.id
+    resp = resp.forms['listing-settings'].submit('category_ids$add_element')
+    resp.forms['listing-settings']['category_ids$element0'] = cat1.id
+    resp.forms['listing-settings']['category_ids$element1'] = cat2.id
     resp = resp.forms['listing-settings'].submit()
-    assert 'management/other-form/' in resp.text
-    assert 'management/form-title/' not in resp.text
+    assert 'management/form-1/' in resp.text
+    assert 'management/form-2/' in resp.text
+    assert 'management/form-3/' not in resp.text
+
+    resp = app.get('/backoffice/management/listing?category_slugs=cat1')
+    assert resp.forms['listing-settings']['category_ids$element0'].value == cat1.id
+    assert 'category_ids$element1' not in resp.forms['listing-settings'].fields
+    assert 'management/form-1/' in resp.text
+    assert 'management/form-2/' not in resp.text
+    assert 'management/form-3/' not in resp.text
+
+    resp = app.get('/backoffice/management/listing?category_slugs=cat1,cat2')
+    assert resp.forms['listing-settings']['category_ids$element0'].value == cat1.id
+    assert resp.forms['listing-settings']['category_ids$element1'].value == cat2.id
+    assert 'management/form-1/' in resp.text
+    assert 'management/form-2/' in resp.text
+    assert 'management/form-3/' not in resp.text
 
 
 def test_datetime_in_global_listing(pub):
diff --git a/wcs/api.py b/wcs/api.py
index 1770ec4c..b27e0135 100644
--- a/wcs/api.py
+++ b/wcs/api.py
@@ -406,12 +406,6 @@ class ApiFormsDirectory(Directory):
             )
         )
 
-        category_slugs = (get_request().form.get('category_slugs') or '').split(',')
-        category_slugs = [c.strip() for c in category_slugs if c.strip()]
-        if category_slugs:
-            categories = Category.select([Contains('url_name', category_slugs)])
-            criterias.append(Contains('category_id', [c.id for c in categories]))
-
         formdatas = sql.AnyFormData.select(criterias, order_by=order_by, limit=limit, offset=offset)
         if get_query_flag('ignore-roles'):
             # When ignoring roles formdatas will be returned even if they are
diff --git a/wcs/backoffice/management.py b/wcs/backoffice/management.py
index ca10a0f0..bdfbf5b2 100644
--- a/wcs/backoffice/management.py
+++ b/wcs/backoffice/management.py
@@ -63,6 +63,7 @@ from ..qommon.form import (
     SingleSelectWidget,
     StringWidget,
     TextWidget,
+    WidgetList,
     WysiwygTextWidget,
 )
 from ..qommon.misc import C_, ellipsize
@@ -648,12 +649,20 @@ class ManagementDirectory(Directory):
         if categories:
             Category.sort_by_position(categories)
             category_options = [(None, C_('categories|All'), '')] + [(x.id, x.name, x.id) for x in categories]
+            category_slugs = (params.get('category_slugs') or '').split(',')
+            category_slugs = [c.strip() for c in category_slugs if c.strip()]
+            for i, category in enumerate([c for c in categories if c.url_name in category_slugs]):
+                params['category_ids$element%s' % i] = category.id
             form.add(
-                SingleSelectWidget,
-                'category_id',
-                title=_('Category'),
-                options=category_options,
-                value=params.get('category_id'),
+                WidgetList,
+                'category_ids',
+                title=_('Categories'),
+                element_type=SingleSelectWidget,
+                add_element_label=_('Add Category'),
+                element_kwargs={
+                    'render_br': False,
+                    'options': category_options,
+                },
             )
 
         if bool(get_publisher().get_site_option('welco_url', 'variables')):
@@ -868,9 +877,21 @@ class ManagementDirectory(Directory):
                 criterias.append(Null('submission_channel'))
             else:
                 criterias.append(Equal('submission_channel', get_request().form.get('submission_channel')))
-        if get_request().form.get('category_id'):
-            category_id = misc.get_int_or_400(get_request().form.get('category_id'))
-            criterias.append(Equal('category_id', category_id))
+        category_slugs = []
+        category_ids = []
+        if get_request().form:
+            prefix = 'category_ids$element'
+            category_slugs = (get_request().form.get('category_slugs') or '').split(',')
+            category_slugs = [c.strip() for c in category_slugs if c.strip()]
+            if category_slugs:
+                category_ids = [c.id for c in Category.select() if c.url_name in category_slugs]
+            else:
+                category_ids = [
+                    get_request().form.get(k) for k in get_request().form.keys() if k.startswith(prefix)
+                ]
+                category_ids = [v for v in category_ids if v]
+        if category_slugs or category_ids:
+            criterias.append(Contains('category_id', category_ids))
         if get_request().form.get('q'):
             criterias.append(FtsMatch(get_request().form.get('q')))
         return criterias
-- 
2.30.2

