From c3391cba705806ea7b57ce22858dc6a8823674f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Puiggen=C3=A9?= Date: Sun, 13 Oct 2019 00:54:35 +0200 Subject: [PATCH 1/5] Added support for adapters in guard handler This commit allows to adapt the behavior of core's guard handler. Adapters that implement IGuardAdapter have at least one of the two functions below: pre_guard(action) This function is executed first and the core's guard will only be evaluated if the return of pre_guard is True. post_guard(action) This function is only executed when the core's guard returns True. Therefore, the outcome of guard_handler will depend on the following order: pre_guard > core's guard > post_guard --- CHANGES.rst | 1 + bika/lims/interfaces/__init__.py | 5 +++++ bika/lims/workflow/__init__.py | 24 ++++++++++++++++++++++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index bbb913790d..9df24f60b3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,7 @@ Changelog **Added** +- #1455 Added support for adapters in guard handler - #1436 Setting in setup for auto-reception of samples upon creation - #1433 Added Submitter column in Sample's analyses listing - #1441 Added Auto ID Behavior for Dexterity Contents diff --git a/bika/lims/interfaces/__init__.py b/bika/lims/interfaces/__init__.py index af08447d44..16ccbb9a4c 100644 --- a/bika/lims/interfaces/__init__.py +++ b/bika/lims/interfaces/__init__.py @@ -954,3 +954,8 @@ class IInternalUse(Interface): class IDetachedPartition(Interface): """Marker interface for samples that have been detached from its primary """ + + +class IGuardAdapter(Interface): + """Marker interface for guard adapters + """ diff --git a/bika/lims/workflow/__init__.py b/bika/lims/workflow/__init__.py index 89174e2ba3..a2e8ffa7d9 100644 --- a/bika/lims/workflow/__init__.py +++ b/bika/lims/workflow/__init__.py @@ -28,7 +28,7 @@ from bika.lims import logger from bika.lims.browser import ulocalized_time from bika.lims.decorators import synchronized -from bika.lims.interfaces import IActionHandlerPool +from bika.lims.interfaces import IActionHandlerPool, IGuardAdapter from bika.lims.interfaces import IJSONReadExtender from bika.lims.jsonapi import get_include_fields from bika.lims.utils import changeWorkflowState # noqa @@ -37,6 +37,7 @@ from Products.Archetypes.config import UID_CATALOG from Products.CMFCore.utils import getToolByName from Products.CMFCore.WorkflowCore import WorkflowException +from zope.component import getAdapters from zope.interface import implements from ZPublisher.HTTPRequest import HTTPRequest @@ -386,6 +387,16 @@ def guard_handler(instance, transition_id): """ if not instance: return True + + # Find out if there are adapters registered for this type + adapters = map(lambda ad: ad[1], getAdapters((instance,), IGuardAdapter)) + + # If adapters are found, core's guard will only be evaluated if, and only + # if, ALL "pre-guards" return True + for ad in adapters: + if hasattr(ad, "pre_guard") and not ad.pre_guard(transition_id): + return False + clazz_name = instance.portal_type # Inspect if bika.lims.workflow.. module exists wf_module = _load_wf_module('{0}.guards'.format(clazz_name.lower())) @@ -398,7 +409,16 @@ def guard_handler(instance, transition_id): if not guard: return True - return guard(instance) + if not guard(instance): + return False + + # "post-guards" are only evaluated if core's guard returns True. For the + # guard to return True, all post_guards must return True too. + for ad in adapters: + if hasattr(ad, "post_guard") and not ad.post_guard(transition_id): + return False + + return True def _load_wf_module(module_relative_name): From 99c20c4e40fe9560cb4af4e9f868b2d3295c8f04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Puiggen=C3=A9?= Date: Sun, 13 Oct 2019 12:11:44 +0200 Subject: [PATCH 2/5] Remove `pre-guard` and `post-guard` --- bika/lims/workflow/__init__.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/bika/lims/workflow/__init__.py b/bika/lims/workflow/__init__.py index a2e8ffa7d9..90153da082 100644 --- a/bika/lims/workflow/__init__.py +++ b/bika/lims/workflow/__init__.py @@ -388,13 +388,10 @@ def guard_handler(instance, transition_id): if not instance: return True - # Find out if there are adapters registered for this type - adapters = map(lambda ad: ad[1], getAdapters((instance,), IGuardAdapter)) - # If adapters are found, core's guard will only be evaluated if, and only # if, ALL "pre-guards" return True - for ad in adapters: - if hasattr(ad, "pre_guard") and not ad.pre_guard(transition_id): + for name, ad in getAdapters((instance,), IGuardAdapter): + if not ad.guard(transition_id): return False clazz_name = instance.portal_type @@ -412,12 +409,6 @@ def guard_handler(instance, transition_id): if not guard(instance): return False - # "post-guards" are only evaluated if core's guard returns True. For the - # guard to return True, all post_guards must return True too. - for ad in adapters: - if hasattr(ad, "post_guard") and not ad.post_guard(transition_id): - return False - return True From 7e5d7cab3fba47142d82e296d4a27035696eaf91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Puiggen=C3=A9?= Date: Sun, 13 Oct 2019 13:25:08 +0200 Subject: [PATCH 3/5] Remove redundant conditional --- bika/lims/workflow/__init__.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bika/lims/workflow/__init__.py b/bika/lims/workflow/__init__.py index 90153da082..11e7d4f38f 100644 --- a/bika/lims/workflow/__init__.py +++ b/bika/lims/workflow/__init__.py @@ -406,10 +406,7 @@ def guard_handler(instance, transition_id): if not guard: return True - if not guard(instance): - return False - - return True + return guard(instance) def _load_wf_module(module_relative_name): From 354abd4b615748cd42ee56ae31c02a094dec6f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Puiggen=C3=A9?= Date: Sun, 13 Oct 2019 21:54:57 +0200 Subject: [PATCH 4/5] Update __init__.py --- bika/lims/workflow/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bika/lims/workflow/__init__.py b/bika/lims/workflow/__init__.py index 11e7d4f38f..2a5e777d99 100644 --- a/bika/lims/workflow/__init__.py +++ b/bika/lims/workflow/__init__.py @@ -391,7 +391,7 @@ def guard_handler(instance, transition_id): # If adapters are found, core's guard will only be evaluated if, and only # if, ALL "pre-guards" return True for name, ad in getAdapters((instance,), IGuardAdapter): - if not ad.guard(transition_id): + if ad.guard(transition_id) is False: return False clazz_name = instance.portal_type From 17b4aa98b88a390c218e4b4e1fdca8fffbfad5fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Puiggen=C3=A9?= Date: Sun, 13 Oct 2019 21:58:29 +0200 Subject: [PATCH 5/5] Declare the signature for IGuardAdapter explicitily --- bika/lims/interfaces/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bika/lims/interfaces/__init__.py b/bika/lims/interfaces/__init__.py index 16ccbb9a4c..47b41b1a9b 100644 --- a/bika/lims/interfaces/__init__.py +++ b/bika/lims/interfaces/__init__.py @@ -959,3 +959,7 @@ class IDetachedPartition(Interface): class IGuardAdapter(Interface): """Marker interface for guard adapters """ + + def guard(self, transition): + """Return False if you want to block the transition + """