Commit 60c42769 authored by Tomáš Peterka's avatar Tomáš Peterka Committed by Tomáš Peterka

[hal_json] Improve extra_param (aka keep_items) persistency between requests

parent 09c37a35
...@@ -5,10 +5,11 @@ Responsible for validating form data and redirecting to the form action. ...@@ -5,10 +5,11 @@ Responsible for validating form data and redirecting to the form action.
Please note that the new UI has deprecated use of Selections. Your scripts Please note that the new UI has deprecated use of Selections. Your scripts
will no longer receive `selection_name` nor `selection` in their arguments. will no longer receive `selection_name` nor `selection` in their arguments.
There are runtime values hidden in every form (injected by getHateoas Script): There are runtime values hidden in every dialog form (injected by getHateoas Script):
form_id - previous form ID (backward compatibility reasons) form_id - previous form ID (backward compatibility reasons)
dialog_id - current form dialog ID dialog_id - current form dialog ID
dialog_method - method to be called - can be either update_method or dialog_method of the Dialog Form dialog_method - method to be called - can be either update_method or dialog_method of the Dialog Form
extra_param_json - JSON serialized extra parameters for the dialog script
""" """
from Products.ERP5Type.Log import log, DEBUG, INFO, WARNING, ERROR from Products.ERP5Type.Log import log, DEBUG, INFO, WARNING, ERROR
...@@ -110,6 +111,21 @@ try: ...@@ -110,6 +111,21 @@ try:
editable_mode = request.get('editable_mode', 1) editable_mode = request.get('editable_mode', 1)
request.set('editable_mode', 1) request.set('editable_mode', 1)
form_data = form.validate_all_to_request(request) form_data = form.validate_all_to_request(request)
# Notify the underlying script whether user did modifications
form_hash = form.hash_validated_data(form_data)
# Inject `has_changed` parameter to arguments
if "form_hash" in extra_param:
kw['has_changed'] = (form_hash != extra_param.get('form_hash'))
# update form_hash here so we do not rely on developer/Dialog Script
# to pass it correctly
extra_param["form_hash"] = form_hash
# Put extra_param into request so we can pass it behind developers back
# it is picked up at Base_renderForm so the developer does not need to
# know that they should pass it.
# We cannot use **kwargs because all form fields get injected there so
# from our point of view it contains random garbage
request.set('extra_param', extra_param)
request.set('editable_mode', editable_mode) request.set('editable_mode', editable_mode)
default_skin = portal.portal_skins.getDefaultSkin() default_skin = portal.portal_skins.getDefaultSkin()
...@@ -187,7 +203,7 @@ if len(listbox_id_list): ...@@ -187,7 +203,7 @@ if len(listbox_id_list):
# First check for an query in form parameters - if they are there # First check for an query in form parameters - if they are there
# that means previous view was a listbox with selected stuff so recover here # that means previous view was a listbox with selected stuff so recover here
query = extra_param.get("query", None) query = extra_param.get("query", None)
select_all = int(extra_param.pop("_select_all", "0")) select_all = extra_param.get("_select_all", 0)
# inject `uids` into Scripts **kwargs when we got any `query` (empty or filled) # inject `uids` into Scripts **kwargs when we got any `query` (empty or filled)
if query is not None: if query is not None:
...@@ -200,11 +216,12 @@ if query is not None: ...@@ -200,11 +216,12 @@ if query is not None:
# early-stop if user selected all documents # early-stop if user selected all documents
if query == "" and select_all == 0 and dialog_method != update_method: # do not interrupt on UPDATE if query == "" and select_all == 0 and dialog_method != update_method: # do not interrupt on UPDATE
extra_param["_select_all"] = 1
return context.Base_renderForm( return context.Base_renderForm(
dialog_id, dialog_id,
message=translate("All documents are selected! Submit again to proceed or Cancel and narrow down your search."), message=translate("All documents are selected! Submit again to proceed or Cancel and narrow down your search."),
level=WARNING, level=WARNING,
keep_items={'_select_all': 1}, keep_items=extra_param,
query=query, query=query,
form_data=form_data) form_data=form_data)
...@@ -215,13 +232,9 @@ if query == "" and select_all == 0 and dialog_method != update_method: # do not ...@@ -215,13 +232,9 @@ if query == "" and select_all == 0 and dialog_method != update_method: # do not
if dialog_category == "object_search" : if dialog_category == "object_search" :
portal.portal_selections.setSelectionParamsFor(kw['selection_name'], kw) portal.portal_selections.setSelectionParamsFor(kw['selection_name'], kw)
# Notify the underlying script whether user did modifications
form_hash = form.hash_validated_data(form_data)
if "form_hash" in extra_param:
kw['has_changed'] = (form_hash != extra_param.pop('form_hash'))
# Add rest of extra param into arguments of the target method # Add rest of extra param into arguments of the target method
kw.update(extra_param) kw.update(**extra_param)
kw.update(keep_items=extra_param) # better backward compatibility
# Finally we will call the Dialog Method # Finally we will call the Dialog Method
# Handle deferred style, unless we are executing the update action # Handle deferred style, unless we are executing the update action
...@@ -239,7 +252,8 @@ if dialog_method != update_method and kw.get('deferred_style', 0): ...@@ -239,7 +252,8 @@ if dialog_method != update_method and kw.get('deferred_style', 0):
return context.Base_renderForm(dialog_id, return context.Base_renderForm(dialog_id,
message=translate('Deferred reports are possible only with preference '\ message=translate('Deferred reports are possible only with preference '\
'"Report Style" set to "ODT" or "ODS"'), '"Report Style" set to "ODT" or "ODS"'),
level=WARNING) level=WARNING,
keep_items=extra_param)
# If the action form has report_view as it's method, it # If the action form has report_view as it's method, it
if page_template != 'report_view': if page_template != 'report_view':
...@@ -304,7 +318,7 @@ if True: ...@@ -304,7 +318,7 @@ if True:
meta_type = "" meta_type = ""
if meta_type in ("ERP5 Form", "ERP5 Report"): if meta_type in ("ERP5 Form", "ERP5 Report"):
return context.ERP5Document_getHateoas(REQUEST=request, form=dialog_form, mode="form", form_data=form_data) return context.ERP5Document_getHateoas(REQUEST=request, form=dialog_form, mode="form", form_data=form_data, extra_param_json=extra_param)
return dialog_form(**kw) return dialog_form(**kw)
......
...@@ -9,16 +9,21 @@ This script differs from Base_redirect that it keeps the form values in place. ...@@ -9,16 +9,21 @@ This script differs from Base_redirect that it keeps the form values in place.
:param REQUEST: request :param REQUEST: request
:param **kwargs: should contain parameters to ERP5Document_getHateoas such as 'query' to replace Selections :param **kwargs: should contain parameters to ERP5Document_getHateoas such as 'query' to replace Selections
""" """
REQUEST = REQUEST or context.REQUEST
keep_items = keep_items or {}
form = getattr(context, form_id) form = getattr(context, form_id)
if not message and "portal_status_message" in keep_items: # recover "hidden field" from HAL_JSON interface behind developers back (sorry)
message = keep_items.pop("portal_status_message") extra_param_json = REQUEST.get('extra_param', {})
if keep_items is not None:
if not message and "portal_status_message" in keep_items:
message = keep_items.pop("portal_status_message")
if not level and "portal_status_level" in keep_items:
level = keep_items.pop("portal_status_level")
if not level and "portal_status_level" in keep_items: extra_param_json.update(keep_items)
level = keep_items.pop("portal_status_level")
return context.ERP5Document_getHateoas(form=form, mode='form', REQUEST=REQUEST, extra_param_json=keep_items, return context.ERP5Document_getHateoas(form=form, mode='form', REQUEST=REQUEST, extra_param_json=extra_param_json,
portal_status_message=message, portal_status_level=level, **kwargs) portal_status_message=message, portal_status_level=level, **kwargs)
...@@ -1243,11 +1243,6 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti ...@@ -1243,11 +1243,6 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
# end-if report_section # end-if report_section
if form.pt == "form_dialog": if form.pt == "form_dialog":
# Insert hash of current values into the form so scripts can see whether data has
# changed if they provide multi-step process
if form_data is not None:
extra_param_json["form_hash"] = form.hash_validated_data(form_data)
# extra_param_json is a special field in forms (just like form_id). extra_param_json field holds JSON # extra_param_json is a special field in forms (just like form_id). extra_param_json field holds JSON
# metadata about the form (its hash and dynamic fields) # metadata about the form (its hash and dynamic fields)
renderHiddenField(response_dict, 'extra_param_json', json.dumps(extra_param_json)) renderHiddenField(response_dict, 'extra_param_json', json.dumps(extra_param_json))
......
...@@ -2,12 +2,25 @@ from Products.ERP5Type.Log import log ...@@ -2,12 +2,25 @@ from Products.ERP5Type.Log import log
log("Folder method received dialog_id, form_id, uids and {!s}".format(kwargs.keys())) log("Folder method received dialog_id, form_id, uids and {!s}".format(kwargs.keys()))
if 'has_changed' not in kwargs or kwargs['has_changed'] is None: if kwargs.get('has_changed', None) is None:
message = "Did nothing." message = "First submission."
else: else:
if kwargs['has_changed']: if kwargs['has_changed']:
message = "Data has changed." message = "Data has changed."
else: else:
message = "Data the same." message = "Data the same."
if kwargs.get("update_method", ""):
return context.Base_renderForm(dialog_id, message="Updated. " + message)
if _my_confirmation == 0:
# Here is an example of unfriendly confirmation Script which takes
# whole keep_item for itself!
# It should take keep_items from parameters, update it and pass it
# along. But no programmer will ever comply with that so we are ready!
return context.Base_renderForm(dialog_id,
message="Submit again to confirm. " + message,
level='warning',
keep_items={'_my_confirmation': 1})
return context.Base_redirect(form_id, keep_items={"portal_status_message": message}) return context.Base_redirect(form_id, keep_items={"portal_status_message": message})
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <key> <string>_params</string> </key>
<value> <string>dialog_id, form_id, uids, **kwargs</string> </value> <value> <string>dialog_id, form_id, uids, _my_confirmation=0, **kwargs</string> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
......
...@@ -121,7 +121,7 @@ ...@@ -121,7 +121,7 @@
</item> </item>
<item> <item>
<key> <string>update_action</string> </key> <key> <string>update_action</string> </key>
<value> <string>Folder_doNothingDialog</string> </value> <value> <string>Folder_doNothing</string> </value>
</item> </item>
<item> <item>
<key> <string>update_action_title</string> </key> <key> <string>update_action_title</string> </key>
......
...@@ -95,7 +95,7 @@ ...@@ -95,7 +95,7 @@
<tr><th rowspan="1" colspan="3">Updating the dialog must not trigger warning about all selected</th></tr> <tr><th rowspan="1" colspan="3">Updating the dialog must not trigger warning about all selected</th></tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/update_dialog" /> <tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/update_dialog" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" /> <tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block tal:define="notification_configuration python: {'class': 'success', 'text': 'Data received.'}"> <tal:block tal:define="notification_configuration python: {'class': 'success', 'text': 'Updated. First submission.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" /> <tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block> </tal:block>
...@@ -106,10 +106,25 @@ ...@@ -106,10 +106,25 @@
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" /> <tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block> </tal:block>
<tr><td>assertValue</td>
<td>//input[@name="field_custom_variable"]</td>
<td>couscous</td></tr>
<tr><td>type</td> <tr><td>type</td>
<td>//input[@name="field_custom_variable"]</td> <td>//input[@name="field_custom_variable"]</td>
<td>couscous</td></tr> <td>couscous</td></tr>
<tr><th rowspan="1" colspan="3">Submitting, however, must warn the user</th></tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block tal:define="notification_configuration python: {'class': 'error', 'text': 'Submit again to confirm. Data the same.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block>
<tr><td>assertValue</td>
<td>//input[@name="field_custom_variable"]</td>
<td>couscous</td></tr>
<tr><th rowspan="1" colspan="3">Second submission must work as advertised</th></tr> <tr><th rowspan="1" colspan="3">Second submission must work as advertised</th></tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" /> <tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" /> <tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
...@@ -141,6 +156,19 @@ ...@@ -141,6 +156,19 @@
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" /> <tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block> </tal:block>
<tr><td>waitForElementPresent</td>
<td>//input[@name="field_custom_variable"]</td><td></td></tr>
<tr><td>type</td>
<td>//input[@name="field_custom_variable"]</td>
<td>musaka</td></tr>
<tr><th rowspan="1" colspan="3">Now the user-script confirmation should kick in</th></tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block tal:define="notification_configuration python: {'class': 'error', 'text': 'Submit again to confirm. Data has changed.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block>
<tr><td>waitForElementPresent</td> <tr><td>waitForElementPresent</td>
<td>//input[@name="field_custom_variable"]</td><td></td></tr> <td>//input[@name="field_custom_variable"]</td><td></td></tr>
<tr><td>type</td> <tr><td>type</td>
...@@ -189,7 +217,13 @@ ...@@ -189,7 +217,13 @@
<tr><th rowspan="1" colspan="3">Second submission must work as advertised</th></tr> <tr><th rowspan="1" colspan="3">Second submission must work as advertised</th></tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" /> <tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" /> <tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block tal:define="notification_configuration python: {'class': 'success', 'text': 'Did nothing.'}"> <tal:block tal:define="notification_configuration python: {'class': 'error', 'text': 'Submit again to confirm. First submission.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block tal:define="notification_configuration python: {'class': 'success', 'text': 'Data the same.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" /> <tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block> </tal:block>
......
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