Skip to content

Commit

Permalink
Adaptations to display error message on the field and not at the top …
Browse files Browse the repository at this point in the history
…of the form:

- Use a `constraint` instead an `invariant` to validate `IMeetingCategory.category_mapping_when_cloning_to_other_mc`;
- Raise a `WidgetActionExecutionError` instead a `Invalid` for `IPMDirectory.validate_position_types`.
See #PM-3921
  • Loading branch information
gbastien committed Jun 24, 2022
1 parent 8f1abcb commit 4b823b7
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 48 deletions.
8 changes: 8 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ Changelog
Add items without a meeting date at the top of items so it will be at the top
when inserted into a meeting.
[gbastien]
- Adaptations to display error message on the field and not at the top of the form:

- Use a `constraint` instead an `invariant` to validate
`IMeetingCategory.category_mapping_when_cloning_to_other_mc`;
- Raise a `WidgetActionExecutionError` instead a `Invalid` for
`IPMDirectory.validate_position_types`.

[gbastien]

4.2rc29 (2022-06-17)
--------------------
Expand Down
32 changes: 17 additions & 15 deletions src/Products/PloneMeeting/content/category.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,30 @@
from Products.PloneMeeting.widgets.pm_checkbox import PMCheckBoxFieldWidget
from z3c.form.browser.radio import RadioFieldWidget
from zope import schema
from zope.globalrequest import getRequest
from zope.i18n import translate
from zope.interface import implements
from zope.interface import Invalid
from zope.interface import invariant
from zope.schema.interfaces import IVocabularyFactory
from zope.schema.vocabulary import SimpleTerm
from zope.schema.vocabulary import SimpleVocabulary


def validate_category_mapping_when_cloning_to_other_mc(values):
'''This validates the 'category_mapping_when_cloning_to_other_mc'.
We can only select one single value (category) for a given MC.'''
previousMCValue = 'DummyFalseMCId'
for value in values:
MCValue = value.split('.')[0]
if MCValue.startswith(previousMCValue):
msg = translate(u'error_can_not_select_several_cat_for_same_mc',
domain="PloneMeeting",
context=getRequest())
raise Invalid(msg)
previousMCValue = MCValue
return True


class IMeetingCategory(IConfigElement):
"""
MeetingCategory schema
Expand Down Expand Up @@ -55,6 +70,7 @@ class IMeetingCategory(IConfigElement):
"category_mapping_when_cloning_to_other_mc_vocabulary"),
required=False,
default=[],
constraint=validate_category_mapping_when_cloning_to_other_mc,
)

form.widget('groups_in_charge', PMCheckBoxFieldWidget, multiple='multiple')
Expand All @@ -74,20 +90,6 @@ class IMeetingCategory(IConfigElement):
default=True,
required=False,)

@invariant
def validate_category_mapping_when_cloning_to_other_mc(data):
'''This method does validate the 'category_mapping_when_cloning_to_other_mc'.
We can only select one single value (category) for a given MC.'''
previousMCValue = 'DummyFalseMCId'
for value in data.category_mapping_when_cloning_to_other_mc:
MCValue = value.split('.')[0]
if MCValue.startswith(previousMCValue):
msg = translate(u'error_can_not_select_several_cat_for_same_mc',
domain="PloneMeeting",
context=data.__context__.REQUEST)
raise Invalid(msg)
previousMCValue = MCValue


class MeetingCategory(Item):
""" """
Expand Down
5 changes: 3 additions & 2 deletions src/Products/PloneMeeting/content/directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from plone import api
from plone.dexterity.schema import DexteritySchemaPolicy
from Products.PloneMeeting.content.meeting import IMeeting
from z3c.form.interfaces import WidgetActionExecutionError
from zope.i18n import translate
from zope.interface import Invalid
from zope.interface import invariant
Expand Down Expand Up @@ -33,7 +34,7 @@ def validate_position_types(data):
'hp_url': hp.absolute_url()},
domain='PloneMeeting',
context=directory.REQUEST)
raise Invalid(msg)
raise WidgetActionExecutionError('position_types', Invalid(msg))
# check if used as a redefined position_type
# for an attendee on an item, this information is stored on the meeting
meeting_brains = catalog.unrestrictedSearchResults(
Expand All @@ -51,7 +52,7 @@ def validate_position_types(data):
'item_url': item.absolute_url()},
domain='PloneMeeting',
context=directory.REQUEST)
raise Invalid(msg)
raise WidgetActionExecutionError('position_types', Invalid(msg))


class PMDirectorySchemaPolicy(DexteritySchemaPolicy):
Expand Down
11 changes: 7 additions & 4 deletions src/Products/PloneMeeting/tests/testContacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from Products.PloneMeeting.tests.PloneMeetingTestCase import PloneMeetingTestCase
from Products.PloneMeeting.utils import get_prefixed_gn_position_name
from Products.statusmessages.interfaces import IStatusMessage
from z3c.form.interfaces import WidgetActionExecutionError
from zope.event import notify
from zope.i18n import translate
from zope.interface import Invalid
Expand Down Expand Up @@ -2221,15 +2222,16 @@ def __init__(self, context, position_types):
# can not remove used position_type
invariant = IPMDirectory.getTaggedValue('invariants')[0]
data = DummyData(self.portal.contacts, position_types=original_position_types)
with self.assertRaises(Invalid) as cm:
with self.assertRaises(WidgetActionExecutionError) as cm:
invariant(data)
self.assertIsInstance(cm.exception.error, Invalid)
error_msg = translate(
msgid="removed_position_type_in_use_error",
mapping={'removed_position_type': hp.position_type,
'hp_url': hp.absolute_url()},
domain='PloneMeeting',
context=self.request)
self.assertEqual(cm.exception.message, error_msg)
self.assertEqual(cm.exception.error.message, error_msg)
# set back a value present in original_position_types
hp.position_type = original_position_types[0]['token']

Expand All @@ -2250,15 +2252,16 @@ def __init__(self, context, position_types):
form._doApply()
self.assertEqual(meeting.get_attendee_position_for(item_uid, hp_uid),
u"default3")
with self.assertRaises(Invalid) as cm:
with self.assertRaises(WidgetActionExecutionError) as cm:
invariant(data)
self.assertIsInstance(cm.exception.error, Invalid)
error_msg = translate(
msgid="removed_redefined_position_type_in_use_error",
mapping={'removed_position_type': form.position_type,
'item_url': item.absolute_url()},
domain='PloneMeeting',
context=self.request)
self.assertEqual(cm.exception.message, error_msg)
self.assertEqual(cm.exception.error.message, error_msg)

# adding new value or removing an unused one is ok
position_types2 = position_types + [{'token': 'default4', 'name': u'D\xe9faut4'}]
Expand Down
41 changes: 14 additions & 27 deletions src/Products/PloneMeeting/tests/testMeetingCategory.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,40 +159,27 @@ def test_pm_ListCategoriesOfOtherMCs(self):
if cfg.getId() not in term.token])

def test_pm_validate_category_mapping_when_cloning_to_other_mc(self):
'''Test the 'category_mapping_when_cloning_to_other_mc' invariant.
It just validate that we can not define more than one value for the same meetingConfig.'''
aCatInMC = self.meetingConfig.categories.development

class DummyData(object):
def __init__(self, context, category_mapping_when_cloning_to_other_mc):
self.__context__ = context
self.category_mapping_when_cloning_to_other_mc = category_mapping_when_cloning_to_other_mc

# if only passing one value, it works
catmc1_vocab = get_vocab(
aCatInMC, u"Products.PloneMeeting.content.category."
"category_mapping_when_cloning_to_other_mc_vocabulary")
values = catmc1_vocab.by_token.keys()
invariant = IMeetingCategory.getTaggedValue('invariants')[0]
data = DummyData(aCatInMC, category_mapping_when_cloning_to_other_mc=[values[0]])
self.assertIsNone(invariant(data))
'''Test the 'category_mapping_when_cloning_to_other_mc' constraint.
It just validates that we can not define more than one value for the same meetingConfig.'''
dev_cat = self.meetingConfig.categories.development
constraint = IMeetingCategory['category_mapping_when_cloning_to_other_mc'].constraint
dev_cat_vocab = get_vocab(
dev_cat,
u"Products.PloneMeeting.content.category."
u"category_mapping_when_cloning_to_other_mc_vocabulary")
values = dev_cat_vocab.by_token.keys()
# one value is ok
self.assertTrue(constraint([values[0]]))
# but not 2 for the same meetingConfig...
error_msg = translate('error_can_not_select_several_cat_for_same_mc',
domain='PloneMeeting',
context=self.request)
data = DummyData(aCatInMC,
category_mapping_when_cloning_to_other_mc=[values[0], values[1]])
with self.assertRaises(Invalid) as cm:
invariant(data)
constraint(values)
self.assertEqual(cm.exception.message, error_msg)

# simulate a third meetingConfig, select one single value of existing meetingConfig2 and
# one of unexisting meetingConfig3, the validate is ok...
data = DummyData(
aCatInMC,
category_mapping_when_cloning_to_other_mc=[
values[0], 'meeting-config-dummy.category_name'])
self.assertIsNone(invariant(data))
# one of unexisting meetingConfig3, the validation is ok...
self.assertTrue(constraint([values[0], 'meeting-config-dummy.category_name']))

def test_pm_CategoryContainerModifiedOnAnyAction(self):
"""The MeetingCategory container (categories/classifiers) is modified
Expand Down

0 comments on commit 4b823b7

Please sign in to comment.