From 7076333f28063b88b9e06e434569be7de085f535 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Sat, 25 Apr 2020 18:12:20 +0200
Subject: [PATCH] api/backoffice: add filter on user (#40050)

---
 tests/test_backoffice_pages.py | 51 ++++++++++++++++++++++++++++++++++
 wcs/backoffice/management.py   | 45 ++++++++++++++++++++++++------
 2 files changed, 88 insertions(+), 8 deletions(-)

diff --git a/tests/test_backoffice_pages.py b/tests/test_backoffice_pages.py
index 23fba1c67..5c5a826e3 100644
--- a/tests/test_backoffice_pages.py
+++ b/tests/test_backoffice_pages.py
@@ -1113,6 +1113,57 @@ def test_backoffice_date_filter(pub):
     assert resp.text.count('<td>2015-05-12</td>') > 0
 
 
+def test_backoffice_user_filter(pub):
+    create_superuser(pub)
+    create_environment(pub)
+    formdef = FormDef.get_by_urlname('form-title')
+    formdef.store()
+
+    user1 = pub.user_class(name='userA')
+    user1.store()
+    user2 = pub.user_class(name='userB')
+    user2.store()
+
+    for i, formdata in enumerate(formdef.data_class().select()):
+        formdata.user_id = user1.id if bool(i % 2) else user2.id
+        formdata.store()
+
+    app = login(get_app(pub))
+    resp = app.get('/backoffice/management/form-title/')
+    # check the filter is hidden
+    assert resp.pyquery.find('li[hidden] input[name=filter-user]')
+
+    resp = app.get('/backoffice/management/form-title/?filter-user=on&filter-user-value=%s' % user1.id)
+    assert resp.text.count('>userA<') > 0
+    assert resp.text.count('>userB<') == 0
+    assert resp.pyquery.find('input[value=userA]')  # displayed in sidebar
+    # check it persits on filter changes
+    resp = resp.forms['listing-settings'].submit()
+    assert resp.text.count('>userA<') > 0
+    assert resp.text.count('>userB<') == 0
+
+    resp = app.get('/backoffice/management/form-title/?filter-user=on&filter-user-value=%s' % user2.id)
+    assert resp.text.count('>userA<') == 0
+    assert resp.text.count('>userB<') > 0
+
+    # filter on uuid
+    user1.name_identifiers = ['0123456789']
+    user1.store()
+    resp = app.get('/backoffice/management/form-title/?filter-user-uuid=0123456789')
+    assert resp.text.count('>userA<') > 0
+    assert resp.text.count('>userB<') == 0
+    assert resp.pyquery.find('input[value=userA]')  # displayed in sidebar
+    # check it persists on filter changes
+    resp = resp.forms['listing-settings'].submit()
+    assert resp.text.count('>userA<') > 0
+    assert resp.text.count('>userB<') == 0
+
+    # check with unknown uuid
+    resp = app.get('/backoffice/management/form-title/?filter-user-uuid=XXX')
+    assert resp.text.count('>userA<') == 0
+    assert resp.text.count('>userB<') == 0
+
+
 def test_backoffice_csv(pub):
     create_superuser(pub)
     create_environment(pub)
diff --git a/wcs/backoffice/management.py b/wcs/backoffice/management.py
index 6e77c770b..168466466 100644
--- a/wcs/backoffice/management.py
+++ b/wcs/backoffice/management.py
@@ -1156,7 +1156,7 @@ class FormPage(Directory):
                 cls=misc.JSONEncoder)
 
     def get_filterable_field_types(self):
-        types = ['string', 'email', 'date', 'item', 'bool', 'items', 'period-date']
+        types = ['string', 'email', 'date', 'item', 'bool', 'items', 'period-date', 'user-id']
         if get_publisher().is_using_postgresql():
             types.append('date')
         return types
@@ -1165,14 +1165,15 @@ class FormPage(Directory):
         r = TemplateIO(html=True)
 
         waitpoint_status = self.formdef.workflow.get_waitpoint_status()
-        period_fake_fields = [
+        fake_fields = [
             FakeField('start', 'period-date', _('Start')),
             FakeField('end', 'period-date', _('End')),
+            FakeField('user', 'user-id', _('User'), addable=False),
         ]
         default_filters = self.get_default_filters(mode)
 
         filter_fields = []
-        for field in period_fake_fields + self.get_formdef_fields():
+        for field in fake_fields + self.get_formdef_fields():
             field.enabled = False
             if field.type not in self.get_filterable_field_types() + ['status']:
                 continue
@@ -1244,6 +1245,16 @@ class FormPage(Directory):
                 r += DateWidget(filter_field_key, title=filter_field.label,
                         value=filter_field_value, render_br=False).render()
 
+            elif filter_field.type == 'user-id':
+                r += HiddenWidget(filter_field_key, value=filter_field_value).render()
+                if filter_field_value:
+                    filtered_user = get_publisher().user_class.get(filter_field_value, ignore_errors=True)
+                    widget = StringWidget('_' + filter_field_key, title=filter_field.label,
+                            value=filtered_user.display_name if filtered_user else _('Unknown'),
+                            readonly=True, render_br=False)
+                    widget._parsed = True  # make sure value is not replaced by request query
+                    r += widget.render()
+
             elif filter_field.type in ('item', 'items'):
                 filter_field.required = False
 
@@ -1307,7 +1318,9 @@ class FormPage(Directory):
         r += htmltext('<div style="display: none;">')
         r += htmltext('<ul id="field-filter">')
         for field in filter_fields:
-            r += htmltext('<li><input type="checkbox" name="filter-%s"') % field.id
+            addable = getattr(field, 'addable', True)
+            r += htmltext('<li %s>') % ('' if addable else 'hidden')
+            r += htmltext('<input type="checkbox" name="filter-%s"') % field.id
             if field.enabled:
                 r += htmltext(' checked="checked"')
             r += htmltext(' id="fields-filter-%s"') % field.id
@@ -1526,13 +1539,13 @@ class FormPage(Directory):
         return 'all'
 
     def get_criterias_from_query(self):
-        period_fake_fields = [
+        fake_fields = [
             FakeField('start', 'period-date', _('Start')),
             FakeField('end', 'period-date', _('End')),
             FakeField('start-mtime', 'period-date', _('Start (modification time)')),
             FakeField('end-mtime', 'period-date', _('End (modification time)')),
+            FakeField('user', 'user-id', _('User')),
         ]
-        filter_fields = []
         criterias = []
 
         filters_dict = {}
@@ -1540,7 +1553,7 @@ class FormPage(Directory):
             filters_dict.update(self.view.get_filters_dict())
         filters_dict.update(get_request().form)
 
-        for filter_field in period_fake_fields + self.get_formdef_fields():
+        for filter_field in fake_fields + self.get_formdef_fields():
             if filter_field.type not in self.get_filterable_field_types():
                 continue
 
@@ -1552,6 +1565,19 @@ class FormPage(Directory):
                 if filters_dict.get('filter-%s' % filter_field.varname):
                     filter_field_key = 'filter-%s' % filter_field.varname
 
+            if filter_field.type == 'user-id':
+                # convert uuid based filter into local id filter
+                name_id = filters_dict.get('filter-user-uuid')
+                if name_id:
+                    nameid_users = get_publisher().user_class.get_users_with_name_identifier(name_id)
+                    get_request().form['filter-user'] = filters_dict['filter-user'] = 'on'
+                    if nameid_users:
+                        filters_dict['filter-user-value'] = str(nameid_users[0].id)
+                        get_request().form['filter-user-value'] = filters_dict['filter-user-value']
+                    else:
+                        filters_dict['filter-user-value'] = '-1'
+                        get_request().form['filter-user-value'] = '-1'
+
             if filters_dict.get('filter-%s' % filter_field.id):
                 # if there's a filter-%(id)s, it is used to enable the actual
                 # filter, and the value will be found in filter-%s-value.
@@ -1579,6 +1605,8 @@ class FormPage(Directory):
                 elif filter_field.id == 'end-mtime':
                     criterias.append(LessOrEqual('last_update_time', filter_date_value))
                 criterias[-1]._label = '%s: %s' % (filter_field.label, filter_field_value)
+            elif filter_field.type == 'user-id':
+                criterias.append(Equal('user_id', filter_field_value))
             elif filter_field.type in ('item', 'items') and filter_field_value not in (None, 'None'):
                 if filter_field.type == 'item':
                     criterias.append(Equal('f%s' % filter_field.id, filter_field_value))
@@ -2928,13 +2956,14 @@ class FormBackOfficeStatusPage(FormStatusPage):
 
 
 class FakeField(object):
-    def __init__(self, id, type_, label):
+    def __init__(self, id, type_, label, addable=True):
         self.id = id
         self.type = type_
         self.label = label
         self.fake = True
         self.varname = id.replace('-', '_')
         self.store_display_value = None
+        self.addable = addable
 
     def get_view_value(self, value):
         # just here to quack like a duck
-- 
2.26.2

