jsbueno
jsbueno

Reputation: 110476

How to abort a workflow transition in Plone

I am working on a complex validation in a dexterity content type which should check the dependencies across several fields at the workflow transition time - I want it to work in the SimplePublicationWorkflow being triggered when the content is sent from "private" to "pending".

I've registered an event listener for IBeforeEvent and hooked it up - but nothing done there short of raising an exception can stop the transition from happening. (and if you raise an exception there, it goes uncaught and the user sees an error page instead of a custom message).

So, what is the recommended way to validate a transition in modern Plone? I've came across documentation suggesting adding External methods to be called on the Guard expression of the transition - but I would not like to use external methods, and if possible, I'd like to keep the default workflow. Creating a custom one is an option provided a clean way to do the check.

Upvotes: 4

Views: 334

Answers (2)

hvelarde
hvelarde

Reputation: 2876

Just for the record; I found another use case for this today and I monkey patched Products.DCWorkflow as a proof-of-concept:

configure.zcml

<configure
    xmlns="http://namespaces.zope.org/zope"
    xmlns:monkey="http://namespaces.plone.org/monkey">

  <monkey:patch
      description="Allow aborting workflow transitions"
      class="Products.DCWorkflow.DCWorkflow.DCWorkflowDefinition"
      original="doActionFor"
      replacement=".patches.doActionFor"
      />

  <subscriber
      for="Products.DCWorkflow.interfaces.IBeforeTransitionEvent"
      handler=".subscribers.validate_workflow_transition"
      />

</configure>

subscribers.py

def validate_workflow_transition(event):
    if not check_something():
        raise MyException

patches.py

def doActionFor(self, ob, action, comment='', **kw):
    ...
    # XXX: above this everything is included without any changes
    #      monkey patch replaces only the last line
    try:
        self._changeStateOf(ob, tdef, kw)
    except MyException:
        # do something
        pass

The proof-of-concept worked as expected but I was not satisfied with the ending UI, so I decided to follow Martijn's advice and re-implement everything as a guard; it will need additional code to set the guard on all workflow transitions involved (and remove them on uninstall), and a browser view and viewlet to display a message explaining why the transition is not available, but it will be cleaner at the end.

Upvotes: 0

Martijn Pieters
Martijn Pieters

Reputation: 1123410

The recommended way is to set a guard instead.

The guard expression should be able to look up a view to facilitate more complex guard code, but when a guard returns False the transition isn't even listed as available.

Upvotes: 3

Related Questions