Commit 95509493 authored by Tristan Cavelier's avatar Tristan Cavelier

[erp5_hal_json_style] fix listbox proxy field recursion in getHateoas

To get the listbox cell value : code was getting last proxy target
instead of browsing the full proxy target stack.

/bug #20190111-1F86B7B
https://nexedijs.erp5.net/#/bug_module/20190111-1F86B7B

/reviewed-on !856
parent 2e98da06
......@@ -1693,24 +1693,30 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
if 'selection' not in catalog_kw:
catalog_kw['selection'] = context.getPortalObject().portal_selections.getSelectionFor(selection_name, REQUEST)
# fill the proxy_field_stack
proxy_field_stack = [source_field] # last of the stack should not be a proxy field
loop_protection_dict = {None: True, source_field: True}
field_stack_iteration = source_field
while field_stack_iteration.meta_type == "ProxyField":
next_iteration = field_stack_iteration.getTemplateField()
if next_iteration in loop_protection_dict:
break
loop_protection_dict[next_iteration] = True
field_stack_iteration = next_iteration
proxy_field_stack.append(field_stack_iteration)
# fill editable_field_dict
# See Products.ERP5Form.Listbox ListBoxRenderer.getEditableField method.
# This method can not be used directly as `source_field` is a ProxyField instance (not a ListBoxRenderer instance).
# It is unauthorized to import ListBoxRenderer.
for select in select_list:
# See Listbox.py getValueList --> getEditableField & getColumnAliasList method
# In short: there are Form Field definitions which names start with
# matching ListBox name - those are template fields to be rendered in
# cells with actual values defined by row and column
field_name = "{}_{}".format(listbox_field_id, select.replace(".", "_"))
if listbox_form.has_field(field_name, include_disabled=1):
editable_field_dict[select] = listbox_form.get_field(field_name, include_disabled=1)
# check if proxify
if source_field.meta_type == "ProxyField":
proxy_listbox_field_id = source_field.getRecursiveTemplateField().id
proxy_form = getattr(traversed_document, source_field.getRecursiveTemplateField().Base_aqInner().aq_parent.id)
for select in select_list:
# need also get editable field from proxy form
proxy_field_name = "{}_{}".format(proxy_listbox_field_id, select.replace(".", "_"))
for proxy_field in proxy_field_stack:
proxy_field_id = proxy_field.id
proxy_field_name = "{}_{}".format(proxy_field_id, select.replace(".", "_"))
proxy_form = getattr(traversed_document, proxy_field.Base_aqInner().aq_parent.id)
if proxy_form.has_field(proxy_field_name, include_disabled=1):
editable_field_dict[select] = proxy_form.get_field(proxy_field_name, include_disabled=1)
break
# handle the case when list-scripts are ignoring `limit` - paginate for them
if limit is not None:
......
......@@ -1383,6 +1383,21 @@ return context.getPortalObject().foo_module.contentValues()
@createIndexedDocument()
@changeSkin('Hal')
def test_getHateoas_proxy_listbox_editable_field(self, **kw):
# For this test, we use/build this kind of a proxyfication target chain.
# ie FooModule_viewFooList.listbox targeting to Base_viewFieldLibrary.my_list_mode_listbox
requirement = """ ; FooModule_viewFooList.listbox ; Base_viewFieldLibrary.my_list_mode_listbox
id ; X ;
title ; ;
creation_date ; ; X
modification_date ; X ; X
"""
# checking test requirements
library_field_list = [raw_library_field.strip().split(".") for raw_library_field in requirement.split("\n", 1)[0].split(";")[1:]]
for row in [line.split(";") for line in requirement.split("\n")[1:]]:
for i, X in enumerate(row[1:]):
res = hasattr(getattr(self.portal.foo_module, library_field_list[i][0]), library_field_list[i][1] + "_" + row[0].strip())
assert res if X.strip() else not res, "{0[0]}.{0[1]}_{1} should{2} exist".format(library_field_list[i], row[0].strip(), " " if X.strip() else " not")
self.portal.foo_module.FooModule_viewFooList.proxifyField({'listbox':'Base_viewFieldLibrary.my_list_mode_listbox'})
fake_request = do_fake_request("GET")
result = self.portal.web_site_module.hateoas.ERP5Document_getHateoas(
......@@ -1397,11 +1412,54 @@ return context.getPortalObject().foo_module.contentValues()
#editalble creation date is defined at proxy form
# Test the listbox_uid parameter
self.assertEqual(result_dict['_embedded']['contents'][0]['listbox_uid:list']['key'], 'listbox_uid:list')
self.assertEqual(result_dict['_embedded']['contents'][0]['id']['field_gadget_param']['type'], 'StringField')
self.assertEqual(result_dict['_embedded']['contents'][0]['title'], '')
self.assertEqual(result_dict['_embedded']['contents'][0]['creation_date']['field_gadget_param']['type'], 'DateTimeField')
self.assertEqual(result_dict['_embedded']['contents'][0]['modification_date']['field_gadget_param']['type'], 'DateTimeField')
# There is a count method on this listbox
self.assertEqual(result_dict['_embedded']['count'], 0)
@simulate('Base_getRequestHeader', '*args, **kwargs',
'return "application/hal+json"')
@simulate('Test_listProducts', '*args, **kwargs', """
return context.getPortalObject().foo_module.contentValues()
""")
@createIndexedDocument()
@changeSkin('Hal')
def test_getHateoas_proxy_listbox_editable_field_with_target_chain(self, **kw):
# For this test, we use/build this kind of a proxyfication target chain.
# ie FooModule_viewFooList.listbox targeting to FooModule_viewFieldLibrary.my_list_mode_foo_title_listbox
# and FooModule_viewFieldLibrary.my_list_mode_foo_title_listbox targeting to Base_viewFieldLibrary.my_list_mode_listbox
requirement = """ ; FooModule_viewFooList.listbox ; FooModule_viewFieldLibrary.my_list_mode_foo_title_listbox ; Base_viewFieldLibrary.my_list_mode_listbox
id ; X ; ;
title ; ; X ;
creation_date ; ; ; X
modification_date ; X ; ; X
"""
# checking test requirements
library_field_list = [raw_library_field.strip().split(".") for raw_library_field in requirement.split("\n", 1)[0].split(";")[1:]]
for row in [line.split(";") for line in requirement.split("\n")[1:]]:
for i, X in enumerate(row[1:]):
res = hasattr(getattr(self.portal.foo_module, library_field_list[i][0]), library_field_list[i][1] + "_" + row[0].strip())
assert res if X.strip() else not res, "{0[0]}.{0[1]}_{1} should{2} exist".format(library_field_list[i], row[0].strip(), " " if X.strip() else " not")
self.portal.foo_module.FooModule_viewFooList.proxifyField({'listbox': 'FooModule_viewFieldLibrary.my_list_mode_foo_title_listbox'})
fake_request = do_fake_request("GET")
result = self.portal.web_site_module.hateoas.ERP5Document_getHateoas(
REQUEST=fake_request,
mode="search",
local_roles=["Assignor", "Assignee"],
list_method='Test_listProducts',
select_list=['id', 'title', 'creation_date', 'modification_date'],
form_relative_url='portal_skins/erp5_ui_test/FooModule_viewFooList/listbox'
)
result_dict = json.loads(result)
self.assertEqual(result_dict['_embedded']['contents'][0]['listbox_uid:list']['key'], 'listbox_uid:list')
self.assertEqual(result_dict['_embedded']['contents'][0]['id']['field_gadget_param']['type'], 'StringField')
self.assertEqual(result_dict['_embedded']['contents'][0]['title']['field_gadget_param']['type'], "StringField")
self.assertEqual(result_dict['_embedded']['contents'][0]['creation_date']['field_gadget_param']['type'], 'DateTimeField')
self.assertEqual(result_dict['_embedded']['contents'][0]['modification_date']['field_gadget_param']['type'], 'DateTimeField')
@simulate('Base_getRequestUrl', '*args, **kwargs',
'return "http://example.org/bar"')
@simulate('Base_getRequestHeader', '*args, **kwargs',
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list>
<string>my_list_mode_foo_title_listbox</string>
<string>my_list_mode_foo_title_listbox_title</string>
</list>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>FooModule_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>Foo_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Foo Field Library</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>my_list_mode_foo_title_listbox</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_list_mode_listbox</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_list_mode_foo_title_listbox_title</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Title</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
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