Skip to content

Commit

Permalink
Overhaul how we're passing settings back and forth
Browse files Browse the repository at this point in the history
  • Loading branch information
JeffersonBledsoe committed Jan 17, 2024
1 parent 6606bc1 commit 731d331
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 93 deletions.
2 changes: 1 addition & 1 deletion src/collective/volto/formsupport/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

<include file="permissions.zcml" />
<include file="upgrades.zcml" />
<include file="validation.zcml" />
<include file="validation/validation.zcml" />

<genericsetup:registerProfile
name="default"
Expand Down
80 changes: 57 additions & 23 deletions src/collective/volto/formsupport/restapi/deserializer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,29 +51,63 @@ def _process_data(
return data

def _update_validations(self, field):
validations = field.get("validations")
new_settings = field.get("validationSettings")
# The settings were collapsed on the frontend, we need to find the validation it was for
for validation_id in validations:
settings = (
{
setting_name: val
for setting_name, val in new_settings.items()
if setting_name not in IGNORED_VALIDATION_DEFINITION_ARGUMENTS
}
if field.get(validation_id)
else None
)
if settings:
field[validation_id] = settings

validation_to_update = [
validation
for validation in getValidations()
if validation[0] == validation_id
][0][1]
for setting_id, setting_value in settings.items():
validation_to_update._settings[setting_id] = setting_value
validation_ids_on_field = field.get("validations")
all_validation_settings = field.get("validationSettings")

if not validation_ids_on_field:
field["validationSettings"] = {}
return field

# The settings were collapsed to a single control on the frontend, we need to find the validation it was for and tidy things up before continuing
if set(validation_ids_on_field) != set(all_validation_settings):
top_level_settings = {
setting_id: setting_value
for setting_id, setting_value in all_validation_settings.items()
if setting_id not in validation_ids_on_field
}
top_level_setting_ids = []
for validation_id, settings in all_validation_settings.items():
if set(settings) == set(top_level_settings):
all_validation_settings[validation_id] = top_level_settings
for setting_id in top_level_settings.keys():
top_level_setting_ids.append(setting_id)
for setting_id in top_level_setting_ids:
del all_validation_settings[setting_id]

# update the internal definitions for the field settings
for validation_id in validation_ids_on_field:
validation_to_update = [
validation
for validation in getValidations()
if validation[0] == validation_id
][0][1]

validation_settings = all_validation_settings.get(validation_id)

if validation_settings:
for setting_name, setting_value in all_validation_settings[
validation_id
].items():
if setting_name in IGNORED_VALIDATION_DEFINITION_ARGUMENTS:
continue
validation_to_update._settings[setting_name] = setting_value

field["validationSettings"][validation_id] = {
k: v
for k, v in validation_to_update.settings.items()
if k not in IGNORED_VALIDATION_DEFINITION_ARGUMENTS
}

# Remove any old settings
keys_to_delete = []
for key in all_validation_settings.keys():
if key not in validation_ids_on_field:
keys_to_delete.append(key)
for key in keys_to_delete:
del all_validation_settings[key]

# # Finally, update the actual validators with what's left in the validation settings
# breakpoint()

return field

Expand Down
25 changes: 10 additions & 15 deletions src/collective/volto/formsupport/restapi/serializer/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from plone import api
from plone.restapi.behaviors import IBlocks
from plone.restapi.interfaces import IBlockFieldSerializationTransformer
from plone.restapi.serializer.converters import json_compatible
from Products.CMFPlone.interfaces import IPloneSiteRoot
from zope.component import adapter, getMultiAdapter
from zope.interface import implementer
Expand All @@ -13,7 +12,6 @@
ICaptchaSupport,
ICollectiveVoltoFormsupportLayer,
)
from collective.volto.formsupport.validation import getValidations

IGNORED_VALIDATION_DEFINITION_ARGUMENTS = [
"title",
Expand Down Expand Up @@ -62,22 +60,19 @@ def __call__(self, value):
return {k: v for k, v in value.items() if not k.startswith("default_")}

def _expand_validation_field(self, field):
field_validations = field.get("validations")
matched_validation_definitions = [
validation
for validation in getValidations()
if validation[0] in field_validations
]

for validation_id, validation in matched_validation_definitions:
settings = validation.settings
settings = {
setting_name: val
"""Adds the individual validation settings to the `validationSettings` key in the format `{validation_id}-{setting_name}`"""
validation_settings = field.get("validationSettings")
settings_to_add = {}
for validation_id, settings in validation_settings.items():
cleaned_settings = {
f"{validation_id}-{setting_name}": val
for setting_name, val in settings.items()
if setting_name not in IGNORED_VALIDATION_DEFINITION_ARGUMENTS
}
if settings:
field[validation_id] = json_compatible(settings)

if cleaned_settings:
settings_to_add = {**settings_to_add, **cleaned_settings}
field["validationSettings"] = {**validation_settings, **settings_to_add}

return field

Expand Down
22 changes: 0 additions & 22 deletions src/collective/volto/formsupport/validation.zcml

This file was deleted.

34 changes: 10 additions & 24 deletions src/collective/volto/formsupport/validation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
from zope.schema.interfaces import IVocabularyFactory
from zope.schema.vocabulary import SimpleVocabulary

from collective.volto.formsupport.validation.custom_validators import custom_validators
from collective.volto.formsupport.validation.definition import ValidationDefinition

try:
from Products.validation import validation
from Products.validation.validators.BaseValidators import baseValidators
except ImportError: # Products.validation is optional
validation = None
Expand Down Expand Up @@ -35,41 +37,25 @@ def delete_setting(setting):
return settings


class ValidationDefinition:
def __init__(self, validator):
self._name = validator.name
self._settings = vars(validator)

def __call__(self, value, **kwargs):
"""Allow using the class directly as a validator"""
return self.validate(value, **kwargs)

@property
def settings(self):
return self._settings

def validate(self, value, **kwargs):
if value is None:
# Let the system for required take care of None values
return
res = validation(self._name, value, **kwargs)
if res != 1:
return res


def _update_validators():
"""
Add Products.validation validators to the available list of validators
Code taken from collective.easyform . Could lookup based on `IValidator` instead of re-registering?
"""

if validation and baseValidators:
if baseValidators:
for validator in baseValidators:
provideUtility(
ValidationDefinition(validator),
provides=IFieldValidator,
name=validator.name,
)
for validator in custom_validators:
provideUtility(
ValidationDefinition(validator),
provides=IFieldValidator,
name=validator.name,
)


_update_validators()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from Products.validation.interfaces.IValidator import IValidator
from zope.interface import implementer

from collective.volto.formsupport.validation import ValidationDefinition
from collective.volto.formsupport.validation.definition import ValidationDefinition


# TODO: Tidy up code structure so we don't need to be a definition
@implementer(IValidator)
class CharactersValidator(ValidationDefinition):
class CharactersValidator():
def __init__(self, name, title="", description="", characters=0, _internal_type=""):
self.name = name
self.title = title or name
Expand All @@ -25,18 +25,20 @@ def settings(self):

def __call__(self, value="", *args, **kwargs):
if self._internal_type == "max":
if (not value or len(value) > self.characters):
if not value or len(value) > self.characters:
# TODO: i18n
msg = f"Validation failed({self.name}): is more than {self.characters}"
return msg
elif self._internal_type == "min":
if (not value or len(value) < self.characters):
if not value or len(value) < self.characters:
# TODO: i18n
msg = f"Validation failed({self.name}): is less than {self.characters}",
msg = (
f"Validation failed({self.name}): is less than {self.characters}",
)
return msg
else:
# TODO: i18n
msg = f"Validation failed({self.name}): Unknown characters validator type",
msg = (
f"Validation failed({self.name}): Unknown characters validator type",
)
return msg


Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@

maxCharacters = CharactersValidator("maxCharacters", _internal_type="max")
minCharacters = CharactersValidator("minCharacters", _internal_type="min")
custom_validators = [maxCharacters, minCharacters]
27 changes: 27 additions & 0 deletions src/collective/volto/formsupport/validation/definition.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from Products.validation import validation


class ValidationDefinition:
def __init__(self, validator):
self._name = validator.name
self._settings = vars(validator)

def __call__(self, value, **kwargs):
"""Allow using the class directly as a validator"""
return self.validate(value, **kwargs)

@property
def settings(self):
return self._settings

@settings.setter
def settings(self, value):
self._value = value

def validate(self, value, **kwargs):
if value is None:
# Let the system for required take care of None values
return
res = validation(self._name, value, **kwargs)
if res != 1:
return res
10 changes: 10 additions & 0 deletions src/collective/volto/formsupport/validation/validation.zcml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<configure xmlns="http://namespaces.zope.org/zope">

<!-- All validations vocabulary -->
<utility
name="collective.volto.formsupport.Validators"
component=".validation.ValidatorsVocabularyFactory"
/>


</configure>

0 comments on commit 731d331

Please sign in to comment.