This works, but displays the same error in every field set except the default one, and does not indicate visually where the field is that violated the requiredness:
from zope.interface import Invalid
from zope.interface import invariant
from plone import api
class IMyContentType(Interface):
@invariant
def verify_per_state_requiredness(data):
state = api.content.get_state(obj=data.__context__)
if state == 'initial': # this is the ID of any state you want to check
if not data.title: # every field is an attribute of this data object
raise Invalid(_(u'Title is required in the ''%s'' state' % state))
You see the usual "There were some errors" message but on the Default fieldset there is nothing else indicating what the error was (ie. in the above example, the string "Title is required in the `initial' state"):
It was easier with Archetypes because you could add arbitrary attributes to a field .... e.g. this required_by_state = ['seatAssigned'] attribute of a field
which would be checked as part of a transition guard
sound like you found something that could be an enhancement for Dexterity; open an issue.
no, @frapell was the one who wrote most of the code; IIRC, he added some checks to the read_permission and write_permission attributes based on the workflow state.
@tkimnguyen IIRC There are some changes where a zope.interface subclassed Invalid works for zope.formlib based forms but not for z3c.form. But you already found that in the z3c.for docs now.
Here's what I will proceed with. The (somewhat contrived) example code below enforces requiredness for title and description in the state having the ID initial
from plone import api
from z3c.form import validator
class TitleRequiredValidator(validator.SimpleFieldValidator):
def validate(self, value):
state = api.content.get_state(obj=self.context)
if state == 'initial':
if not value or not value.strip():
raise Invalid(_(u'Title is required in state ''%s''' % state))
validator.WidgetValidatorDiscriminators(TitleRequiredValidator, field=IOIEStudyAbroadProgram['title'])
class DescriptionRequiredValidator(validator.SimpleFieldValidator):
def validate(self, value):
state = api.content.get_state(obj=self.context)
if state == 'initial':
if not value or not value.strip():
raise Invalid(_(u'Description is required in state ''%s''' % state))
validator.WidgetValidatorDiscriminators(DescriptionRequiredValidator, field=IOIEStudyAbroadProgram['description'])
If there is a validation error, the usual "There were some errors" message appears, and you have to click onto each field set to find the field that had the problem. Not ideal, but it works.