Commit fd8eae9a authored by Romain Courteaud's avatar Romain Courteaud

[erp5_hal_json_style] Support listbox cell validator

Return the submitted value in case if present in the REQUEST.

Return the cell error_text if present.

Execute the listbox listmethod in case of ValidationError.
Ensure that it is rendered with the same query parameters.
parent fbef3d9e
...@@ -67,7 +67,10 @@ MARKER = [] ...@@ -67,7 +67,10 @@ MARKER = []
COUNT_LIMIT = 1000 COUNT_LIMIT = 1000
if REQUEST is None: if REQUEST is None:
recursive_call = True
REQUEST = context.REQUEST REQUEST = context.REQUEST
else:
recursive_call = False
if response is None: if response is None:
response = REQUEST.RESPONSE response = REQUEST.RESPONSE
...@@ -373,16 +376,15 @@ def getFormRelativeUrl(form): ...@@ -373,16 +376,15 @@ def getFormRelativeUrl(form):
)[0].relative_url )[0].relative_url
def getFieldDefault(form, field, key, value=None): def getFieldDefault(form, field, key, value=MARKER):
"""Get available value for `field` preferably in python-object from REQUEST or from field's default. """Get available value for `field` preferably in python-object from REQUEST or from field's default.
Previously we used Formulator.Field._get_default which is (for no reason) private. Previously we used Formulator.Field._get_default which is (for no reason) private.
""" """
if value is None: value = REQUEST.form.get(field.id, REQUEST.form.get(key, value))
value = REQUEST.form.get(field.id, REQUEST.form.get(key, MARKER)) if value is MARKER:
# use marker because default value can be intentionally empty string # use marker because default value can be intentionally empty string
if value is MARKER: value = field.get_value('default', request=REQUEST, REQUEST=REQUEST)
value = field.get_value('default', request=REQUEST, REQUEST=REQUEST)
if field.has_value("unicode") and field.get_value("unicode") and isinstance(value, unicode): if field.has_value("unicode") and field.get_value("unicode") and isinstance(value, unicode):
value = unicode(value, form.get_form_encoding()) value = unicode(value, form.get_form_encoding())
if getattr(value, 'translate', None) is not None: if getattr(value, 'translate', None) is not None:
...@@ -425,11 +427,7 @@ def renderField(traversed_document, field, form, value=MARKER, meta_type=None, k ...@@ -425,11 +427,7 @@ def renderField(traversed_document, field, form, value=MARKER, meta_type=None, k
if "Field" in meta_type: if "Field" in meta_type:
# fields have default value and can be required (unlike boxes) # fields have default value and can be required (unlike boxes)
result["required"] = field.get_value("required") if field.has_value("required") else None result["required"] = field.get_value("required") if field.has_value("required") else None
if value is MARKER: result["default"] = getFieldDefault(form, field, key, value=value)
result["default"] = getFieldDefault(form, field, key)
else:
# No need to calculate the field value if provided (used in Listbox)
result["default"] = value
# start the actual "switch" on field's meta_type here # start the actual "switch" on field's meta_type here
if meta_type in ("ListField", "RadioField", "ParallelListField", "MultiListField"): if meta_type in ("ListField", "RadioField", "ParallelListField", "MultiListField"):
...@@ -748,6 +746,18 @@ def renderField(traversed_document, field, form, value=MARKER, meta_type=None, k ...@@ -748,6 +746,18 @@ def renderField(traversed_document, field, form, value=MARKER, meta_type=None, k
if (list_method_custom is not None): if (list_method_custom is not None):
result["list_method_template"] = list_method_custom result["list_method_template"] = list_method_custom
# In the context of a form validation,
# the lines must be synchronously calculated to keep track of the request values
# Reuse the same parameters which were used during the first rendering
query_param_json = REQUEST.get("%s_query_param_json" % field.id, None)
if (query_param_json is not None) and (response.getStatus() == 400):
result["default"] = json.loads(
context.ERP5Document_getHateoas(mode='search',
**ensureDeserialized(byteify(json.loads(urlsafe_b64decode(query_param_json))))
)
)
return result return result
if meta_type == "FormBox": if meta_type == "FormBox":
...@@ -1550,10 +1560,27 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None, ...@@ -1550,10 +1560,27 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
# > We simply do not use them. All Document selection is handled via passing # > We simply do not use them. All Document selection is handled via passing
# > "query" parameter to Base_callDialogMethod or introspecting list_methods. # > "query" parameter to Base_callDialogMethod or introspecting list_methods.
################################################# #################################################
if REQUEST.other['method'] != "GET": if (not recursive_call) and (REQUEST.other['method'] != "GET"):
response.setStatus(405) response.setStatus(405)
return "" return ""
# Those parameter will be send back during the listbox submission
# to ensure fetching the same lines
listbox_query_param_json = urlsafe_b64encode(json.dumps(ensureSerializable({
'form_relative_url': form_relative_url,
'list_method': list_method,
'default_param_json': default_param_json,
'query': query,
'select_list': select_list,
'limit': limit,
'local_roles': local_roles,
'selection_domain': selection_domain,
'extra_param_json': extra_param_json,
'relative_url': relative_url,
'group_by': group_by,
'sort_on': sort_on
})))
# set 'here' for field rendering which contain TALES expressions # set 'here' for field rendering which contain TALES expressions
REQUEST.set('here', traversed_document) REQUEST.set('here', traversed_document)
# Put all items from extra_param_json into the REQUEST. It is the only # Put all items from extra_param_json into the REQUEST. It is the only
...@@ -1755,6 +1782,8 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None, ...@@ -1755,6 +1782,8 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
}) })
Listbox_getBrainValue = traversed_document.Listbox_getBrainValue Listbox_getBrainValue = traversed_document.Listbox_getBrainValue
field_errors = REQUEST.get('field_errors', {})
# Compatibility with Listbox.py ListMethodWrapper # Compatibility with Listbox.py ListMethodWrapper
can_check_local_property = list_method not in ('objectValues', 'contentValues') can_check_local_property = list_method not in ('objectValues', 'contentValues')
# now fill in `contents_list` with actual information # now fill in `contents_list` with actual information
...@@ -1845,6 +1874,10 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None, ...@@ -1845,6 +1874,10 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
listbox_form, listbox_form,
value=default_field_value, value=default_field_value,
key='field_%s_%s' % (editable_field.id, brain_uid)) key='field_%s_%s' % (editable_field.id, brain_uid))
# Include cell error text in case of form validation
if field_errors.has_key('%s_%s' % (editable_field.id, brain_uid)):
contents_item[select]['field_gadget_param']["error_text"] = \
field_errors['%s_%s' % (editable_field.id, brain_uid)].error_text
# Do not generate link for empty value, as it will not be clickable in UI # Do not generate link for empty value, as it will not be clickable in UI
...@@ -1997,6 +2030,13 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None, ...@@ -1997,6 +2030,13 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
if len(contents_stat_list) > 0: if len(contents_stat_list) > 0:
result_dict['_embedded']['sum'] = ensureSerializable(contents_stat_list) result_dict['_embedded']['sum'] = ensureSerializable(contents_stat_list)
# Those parameter will be send back during the listbox submission
# to ensure fetching the same lines
result_dict['_embedded']['listbox_query_param_json'] = {
'key': "%s_query_param_json" % listbox_field_id,
'value': listbox_query_param_json
}
# We should cleanup the selection if it exists in catalog params BUT # We should cleanup the selection if it exists in catalog params BUT
# we cannot because it requires escalated Permission.'modifyPortal' so # we cannot because it requires escalated Permission.'modifyPortal' so
# the correct solution would be to ReportSection.popReport but unfortunately # the correct solution would be to ReportSection.popReport but unfortunately
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment