From cd1b7370a987602e7145667b95ed46d7fab702a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Thu, 11 Aug 2022 17:09:52 +0200
Subject: [PATCH] misc: add special "Nothing" criteria to bypass query and
 return nothing (#67884)

---
 wcs/backoffice/management.py | 15 ++++++++-------
 wcs/carddef.py               |  4 +++-
 wcs/custom_views.py          |  4 ++--
 wcs/qommon/storage.py        |  9 +++++++++
 wcs/sql.py                   |  8 ++++++++
 wcs/variables.py             | 23 ++++++++++++++++++-----
 6 files changed, 48 insertions(+), 15 deletions(-)

diff --git a/wcs/backoffice/management.py b/wcs/backoffice/management.py
index 7480958fe..1002254d4 100644
--- a/wcs/backoffice/management.py
+++ b/wcs/backoffice/management.py
@@ -80,6 +80,7 @@ from ..qommon.storage import (
     Not,
     NotContains,
     NotEqual,
+    Nothing,
     NotNull,
     Null,
     StrictNotEqual,
@@ -917,7 +918,7 @@ class FormPage(Directory):
         criterias.append(StrictNotEqual('status', 'draft'))
         if selected_filter == 'all':
             if selected_filter_operator == 'ne':
-                criterias.append(Equal('status', '_none'))
+                criterias.append(Nothing())
         elif selected_filter in ('waiting', 'pending'):
             statuses = ['wf-%s' % x.id for x in self.formdef.workflow.get_not_endpoint_status()]
             _criterias = [Contains('status', statuses)]
@@ -1782,7 +1783,7 @@ class FormPage(Directory):
                 raise RequestError(error_message)
             if self.view_type == 'table':
                 get_session().message = ('warning', error_message)
-            criterias.append(Equal('status', '_none'))
+            criterias.append(Nothing())
 
         for filter_field in fake_fields + list(self.get_formdef_fields()):
             if filter_field.type not in self.get_filterable_field_types():
@@ -1883,13 +1884,13 @@ class FormPage(Directory):
                         criterias.append(criteria('id', filter_field_value))
                         continue
                     if not compile_templates:
-                        criterias.append(Equal('status', '_none'))
+                        criterias.append(Nothing())
                         continue
                     with get_publisher().complex_data():
                         value = WorkflowStatusItem.compute(filter_field_value, allow_complex=True)
                         filter_field_value = get_publisher().get_cached_complex_data(value)
                     if filter_field_value is None:
-                        criterias.append(Equal('status', '_none'))
+                        criterias.append(Nothing())
                         continue
 
                     def record_error(value, operator):
@@ -1910,7 +1911,7 @@ class FormPage(Directory):
                             [int(v) for v in filter_field_value]
                         except ValueError:
                             record_error(filter_field_value, filter_field_operator)
-                            criterias.append(Equal('status', '_none'))
+                            criterias.append(Nothing())
                             continue
                         if filter_field_operator == 'eq':
                             criterias.append(Contains('id', filter_field_value))
@@ -1918,7 +1919,7 @@ class FormPage(Directory):
                             criterias.append(NotContains('id', filter_field_value))
                         else:
                             record_error(filter_field_value, filter_field_operator)
-                            criterias.append(Equal('status', '_none'))
+                            criterias.append(Nothing())
                         continue
                 try:
                     filter_field_value = int(filter_field_value)
@@ -1948,7 +1949,7 @@ class FormPage(Directory):
                         criterias.append(criteria(filter_field.id, filter_field_value))
                         continue
                     if not compile_templates:
-                        criterias.append(Equal('status', '_none'))
+                        criterias.append(Nothing())
                         continue
                     filter_field_value = WorkflowStatusItem.compute(filter_field_value)
                 try:
diff --git a/wcs/carddef.py b/wcs/carddef.py
index e4fe28dad..4bf74d883 100644
--- a/wcs/carddef.py
+++ b/wcs/carddef.py
@@ -208,7 +208,9 @@ class CardDef(FormDef):
                         user = variables['form_user']
                     except KeyError:
                         user = None
-                    criterias.append(Equal('user_id', str(user.id) if user else '-1'))
+                    if not user:
+                        return []
+                    criterias.append(Equal('user_id', str(user.id)))
             else:
                 if custom_view is None:
                     custom_view = cls.get_data_source_custom_view(data_source_id, carddef=carddef)
diff --git a/wcs/custom_views.py b/wcs/custom_views.py
index a8020e4fa..09995e1c2 100644
--- a/wcs/custom_views.py
+++ b/wcs/custom_views.py
@@ -24,7 +24,7 @@ from wcs.backoffice.data_management import CardPage
 from wcs.carddef import CardDef
 from wcs.formdef import FormDef
 from wcs.qommon.misc import simplify
-from wcs.qommon.storage import Contains, Equal, NotContains, StorableObject
+from wcs.qommon.storage import Contains, Equal, NotContains, Nothing, StorableObject
 
 from .qommon.misc import xml_node_text
 
@@ -177,7 +177,7 @@ class CustomView(StorableObject):
         selected_status_filter_operator = self.get_status_filter_operator()
         if selected_filter and selected_filter == 'all':
             if selected_status_filter_operator == 'ne':
-                criterias.append(Equal('status', '_none'))
+                criterias.append(Nothing())
         elif selected_filter:
             if selected_filter == 'pending':
                 applied_filters = ['wf-%s' % x.id for x in formdef.workflow.get_not_endpoint_status()]
diff --git a/wcs/qommon/storage.py b/wcs/qommon/storage.py
index edaaaee27..f2a32f38a 100644
--- a/wcs/qommon/storage.py
+++ b/wcs/qommon/storage.py
@@ -393,6 +393,15 @@ class ElementIntersects(Criteria):
         )
 
 
+class Nothing(Criteria):
+    def __init__(self, *args, **kwargs):
+        self.attribute = None
+        self.value = None
+
+    def build_lambda(self):
+        return lambda x: False
+
+
 def parse_clause(clause):
     # creates a callable out of a clause
     #  (attribute, operator, value)
diff --git a/wcs/sql.py b/wcs/sql.py
index 09c0a887c..ee9ffcf3d 100644
--- a/wcs/sql.py
+++ b/wcs/sql.py
@@ -403,6 +403,14 @@ class ElementIntersects(ElementEqual):
         )
 
 
+class Nothing(wcs.qommon.storage.Nothing):
+    def as_sql(self):
+        return 'FALSE'
+
+    def as_sql_param(self):
+        return {}
+
+
 def get_name_as_sql_identifier(name):
     name = qommon.misc.simplify(name)
     for char in '<>|{}!?^*+/=\'':  # forbidden chars
diff --git a/wcs/variables.py b/wcs/variables.py
index f6df9a41b..fe16e4baf 100644
--- a/wcs/variables.py
+++ b/wcs/variables.py
@@ -26,7 +26,19 @@ from .formdata import get_workflow_roles_substitution_variables
 from .formdef import FormDef, FormDefDoesNotExist
 from .qommon import _, force_str, misc
 from .qommon.evalutils import make_datetime
-from .qommon.storage import And, Contains, Equal, Intersects, Not, NotEqual, NotNull, Null, Or, StrictNotEqual
+from .qommon.storage import (
+    And,
+    Contains,
+    Equal,
+    Intersects,
+    Not,
+    NotEqual,
+    Nothing,
+    NotNull,
+    Null,
+    Or,
+    StrictNotEqual,
+)
 from .qommon.substitution import CompatibilityNamesDict
 from .qommon.templatetags.qommon import parse_datetime
 
@@ -89,7 +101,7 @@ class LazyFormDefObjectsManager:
         return self._clone(None)
 
     def none(self):
-        return self._clone([Equal('status', '_none')])
+        return self._clone([Nothing()])
 
     def pending(self):
         status_filters = ['wf-%s' % x.id for x in self._formdef.workflow.get_not_endpoint_status()]
@@ -97,13 +109,14 @@ class LazyFormDefObjectsManager:
         return self._clone(self._criterias + criterias)
 
     def current_user(self):  # filter on current user
-        user = get_request().user
-        return self._clone(self._criterias + [Equal('user_id', str(user.id) if user else '-1')])
+        return self.filter_by_user(get_request().user)
 
     def filter_by_user(self, user):
         if isinstance(user, str):
             user = get_publisher().user_class.lookup_by_string(user)
-        return self._clone(self._criterias + [Equal('user_id', str(user.id) if user else '-1')])
+        if not user:
+            return self.none()
+        return self._clone(self._criterias + [Equal('user_id', str(user.id))])
 
     def filter_by_status(self, status):
         for wfs in self._formdef.workflow.possible_status:
-- 
2.36.1

