Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
erp5
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Łukasz Nowak
erp5
Commits
4c273cae
Commit
4c273cae
authored
Apr 20, 2018
by
Tomáš Peterka
Committed by
Tomáš Peterka
Apr 25, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[hal_json_style] Refactor for clarity and bug fixes
parent
14ebe535
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
192 additions
and
227 deletions
+192
-227
bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py
...rtal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py
+192
-227
No files found.
bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py
View file @
4c273cae
"""Hello. This will be long because this godly script does almost everything.
"""Hello. This will be long because this godly script does almost everything.
I
n general, it always returns a JSON res
ponse in HATEOAS format specification.
I
t **always** return a JSON re
ponse in HATEOAS format specification.
:param REQUEST: HttpRequest holding GET and/or POST data
:param REQUEST: HttpRequest holding GET and/or POST data
:param response:
:param response:
:param view: either "view" or absolute URL of an ERP5 Action
:param view: either "view" or absolute URL of an ERP5 Action
:param mode: {str} help to decide what user wants from us "form" | "search" ...
:param mode: {str} help to decide what user wants from us "form" | "search" ...
:param relative_url: an URL of `traversed_document` to operate on (it must have an object_view)
:param relative_url: an URL of `traversed_document` to operate on (it must have an object_view)
:param portal_status_message: {str} message to be displayed on the user
:param portal_status_level: {str|int} severity of the message using ERP5Type.Log levels or their names like 'info', 'warn', 'error'
Only in
mode == 'search'
Parameters for
mode == 'search'
:param query: string-serialized Query
:param query: string-serialized Query
:param select_list: list of strings to select from search result object
:param select_list: list of strings to select from search result object
:param limit: tuple(start_index, num_records) which is further passed to list_method BUT not every list_method takes it into account
:param limit: tuple(start_index, num_records) which is further passed to list_method BUT not every list_method takes it into account
:param form_relative_url: {str} relative URL of a form FIELD issuing the search (listbox/relation field...)
:param form_relative_url: {str} relative URL of a form FIELD issuing the search (listbox/relation field...)
it can be None in case of special listboxes like List of Modules
it can be None in case of special listboxes like List of Modules
or relative path like "portal_skins/erp5_ui_test/FooModule_viewFooList/listbox"
or relative path like "portal_skins/erp5_ui_test/FooModule_viewFooList/listbox"
:param default_param_json: {str} BASE64 encoded JSON with parameters intended for the list_method
:param .form_id: In case of page_template = "form" it will be similar to form_relative_url with the exception that it contains
only the form name (e.g. FooModule_viewFooList). In case of dialogs it points to the previous form which is
often more important than the dialog form.
Only in
mode == 'form'
Parameters for
mode == 'form'
:param form:
:param form:
{instace} of a form - obviously this call can be only internal (Script-to-Script)
Only in
mode == 'traverse'
Parameters for
mode == 'traverse'
Traverse renders arbitrary View. It can be a Form or a Script.
Traverse renders arbitrary View. It can be a Form or a Script.
:param relative_url: string, MANDATORY for obtaining the traversed_document. Calling this script directly on an object should be
:param relative_url: string, MANDATORY for obtaining the traversed_document. Calling this script directly on an object should be
forbidden in code (but it is not now).
forbidden in code (but it is not now).
:param view: {str} mandatory. the view reference as defined on a Portal Type (e.g. "view" or "publish_view")
# Form
# Form
When handling form, we can expect field values to be stored in REQUEST.form in two forms
When handling form, we can expect field values to be stored in REQUEST.form in two forms
...
@@ -56,11 +62,13 @@ if REQUEST is None:
...
@@ -56,11 +62,13 @@ if REQUEST is None:
if
response
is
None
:
if
response
is
None
:
response
=
REQUEST
.
RESPONSE
response
=
REQUEST
.
RESPONSE
def
isFieldType
(
field
,
type_name
):
def
isFieldType
(
field
,
type_name
):
if
field
.
meta_type
==
'ProxyField'
:
if
field
.
meta_type
==
'ProxyField'
:
field
=
field
.
getRecursiveTemplateField
()
field
=
field
.
getRecursiveTemplateField
()
return
field
.
meta_type
==
type_name
return
field
.
meta_type
==
type_name
def
toBasicTypes
(
obj
):
def
toBasicTypes
(
obj
):
"""Ensure that obj contains only basic types."""
"""Ensure that obj contains only basic types."""
if
obj
is
None
:
if
obj
is
None
:
...
@@ -79,7 +87,8 @@ def toBasicTypes(obj):
...
@@ -79,7 +87,8 @@ def toBasicTypes(obj):
log
(
'Cannot convert {!s} to basic types {!s}'
.
format
(
type
(
obj
),
obj
),
level
=
100
)
log
(
'Cannot convert {!s} to basic types {!s}'
.
format
(
type
(
obj
),
obj
),
level
=
100
)
return
obj
return
obj
def
addHiddenFieldToForm
(
form
,
name
,
value
):
def
renderHiddenField
(
form
,
name
,
value
):
if
form
==
{}:
if
form
==
{}:
form
[
'_embedded'
]
=
{}
form
[
'_embedded'
]
=
{}
form
[
'_embedded'
][
'_view'
]
=
{}
form
[
'_embedded'
][
'_view'
]
=
{}
...
@@ -90,7 +99,7 @@ def addHiddenFieldToForm(form, name, value):
...
@@ -90,7 +99,7 @@ def addHiddenFieldToForm(form, name, value):
field_dict
=
form
field_dict
=
form
field_dict
[
name
]
=
{
field_dict
[
name
]
=
{
"type"
:
"StringField"
,
"type"
:
"StringField"
,
# must be string field because only this gets send when non-editable
"key"
:
name
,
"key"
:
name
,
"default"
:
value
,
"default"
:
value
,
"editable"
:
0
,
"editable"
:
0
,
...
@@ -101,6 +110,7 @@ def addHiddenFieldToForm(form, name, value):
...
@@ -101,6 +110,7 @@ def addHiddenFieldToForm(form, name, value):
"required"
:
1
,
"required"
:
1
,
}
}
# http://stackoverflow.com/a/13105359
# http://stackoverflow.com/a/13105359
def
byteify
(
string
):
def
byteify
(
string
):
if
isinstance
(
string
,
dict
):
if
isinstance
(
string
,
dict
):
...
@@ -440,12 +450,12 @@ url_template_dict = {
...
@@ -440,12 +450,12 @@ url_template_dict = {
"form_action"
:
"%(traversed_document_url)s/%(action_id)s"
,
"form_action"
:
"%(traversed_document_url)s/%(action_id)s"
,
"traverse_generator"
:
"%(root_url)s/%(script_id)s?mode=traverse"
+
\
"traverse_generator"
:
"%(root_url)s/%(script_id)s?mode=traverse"
+
\
"&relative_url=%(relative_url)s&view=%(view)s"
,
"&relative_url=%(relative_url)s&view=%(view)s"
,
"traverse_generator_non_view"
:
"%(root_url)s/%(script_id)s?mode=traverse"
+
\
"traverse_generator_action"
:
"%(root_url)s/%(script_id)s?mode=traverse"
+
\
"&relative_url=%(relative_url)s&view=%(view)s&form_id=%(form_id)s"
,
"traverse_generator_with_parameter"
:
"%(root_url)s/%(script_id)s?mode=traverse"
+
\
"&relative_url=%(relative_url)s&view=%(view)s&extra_param_json=%(extra_param_json)s"
,
"&relative_url=%(relative_url)s&view=%(view)s&extra_param_json=%(extra_param_json)s"
,
"traverse_template"
:
"%(root_url)s/%(script_id)s?mode=traverse"
+
\
"traverse_template"
:
"%(root_url)s/%(script_id)s?mode=traverse"
+
\
"{&relative_url,view}"
,
"{&relative_url,view}"
,
# Search template will call standard "searchValues" on a document described by `root_url`
"search_template"
:
"%(root_url)s/%(script_id)s?mode=search"
+
\
"search_template"
:
"%(root_url)s/%(script_id)s?mode=search"
+
\
"{&query,select_list*,limit*,sort_on*,local_roles*,selection_domain*}"
,
"{&query,select_list*,limit*,sort_on*,local_roles*,selection_domain*}"
,
"worklist_template"
:
"%(root_url)s/%(script_id)s?mode=worklist"
,
"worklist_template"
:
"%(root_url)s/%(script_id)s?mode=worklist"
,
...
@@ -455,6 +465,9 @@ url_template_dict = {
...
@@ -455,6 +465,9 @@ url_template_dict = {
"&list_method=%(list_method)s"
\
"&list_method=%(list_method)s"
\
"&default_param_json=%(default_param_json)s"
\
"&default_param_json=%(default_param_json)s"
\
"{&query,select_list*,limit*,sort_on*,local_roles*,selection_domain*}"
,
"{&query,select_list*,limit*,sort_on*,local_roles*,selection_domain*}"
,
# Non-editable searches suppose the search results will be rendered as-is and no template
# fields will get involved. Unfortunately, fields need to be resolved because of formatting
# all the time so we abandoned this no_editable version
"custom_search_template_no_editable"
:
"%(root_url)s/%(script_id)s?mode=search"
+
\
"custom_search_template_no_editable"
:
"%(root_url)s/%(script_id)s?mode=search"
+
\
"&relative_url=%(relative_url)s"
\
"&relative_url=%(relative_url)s"
\
"&list_method=%(list_method)s"
\
"&list_method=%(list_method)s"
\
...
@@ -480,6 +493,35 @@ def getRealRelativeUrl(document):
...
@@ -480,6 +493,35 @@ def getRealRelativeUrl(document):
return
'/'
.
join
(
portal
.
portal_url
.
getRelativeContentPath
(
document
))
return
'/'
.
join
(
portal
.
portal_url
.
getRelativeContentPath
(
document
))
def
parseActionUrl
(
url
):
"""Parse usual ERP5 Action URL into components: ~root, context~, view_id, param_dict, url.
:param url: {str} is expected to be in form https://<site_root>/context/view_id?optional=params
"""
param_dict
=
{}
url_and_params
=
url
.
split
(
site_root
.
absolute_url
())[
-
1
].
split
(
'?'
)
_
,
script
=
url_and_params
[
0
].
strip
(
"/ "
).
rsplit
(
'/'
,
1
)
if
len
(
url_and_params
)
>
1
:
for
param
in
url_and_params
[
1
].
split
(
'&'
):
param_name
,
param_value
=
param
.
split
(
'='
)
if
"+"
in
param_value
:
param_value
=
param_value
.
replace
(
"+"
,
" "
)
if
":"
in
param_name
:
param_name
,
param_type
=
param_name
.
split
(
":"
)
if
param_type
==
"int"
:
param_value
=
int
(
param_value
)
elif
param_type
==
"bool"
:
param_value
=
True
if
param_value
.
lower
()
in
(
"true"
,
"1"
)
else
False
else
:
raise
ValueError
(
"Cannot convert param {}={} to type {}. Feel free to add implemetation at the position of this exception."
.
format
(
param_name
,
param_value
,
param_type
))
param_dict
[
param_name
]
=
param_value
return
{
'view_id'
:
script
,
'params'
:
param_dict
,
'url'
:
url
}
def
getFormRelativeUrl
(
form
):
def
getFormRelativeUrl
(
form
):
return
portal
.
portal_catalog
(
return
portal
.
portal_catalog
(
portal_type
=
(
"ERP5 Form"
,
"ERP5 Report"
),
portal_type
=
(
"ERP5 Form"
,
"ERP5 Report"
),
...
@@ -491,10 +533,15 @@ def getFormRelativeUrl(form):
...
@@ -491,10 +533,15 @@ def getFormRelativeUrl(form):
def
getFieldDefault
(
form
,
field
,
key
,
value
=
None
):
def
getFieldDefault
(
form
,
field
,
key
,
value
=
None
):
"""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.
"""
if
value
is
None
:
if
value
is
None
:
value
=
(
REQUEST
.
form
.
get
(
field
.
id
,
REQUEST
.
form
.
get
(
key
,
None
))
or
value
=
REQUEST
.
get
(
key
,
MARKER
)
field
.
get_value
(
'default'
,
request
=
REQUEST
,
REQUEST
=
REQUEST
))
# use marker because default value can be intentionally empty string
if
value
is
MARKER
:
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
:
...
@@ -770,7 +817,6 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
...
@@ -770,7 +817,6 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
# portal_type list can be overriden by selection too
# portal_type list can be overriden by selection too
# since it can be intentionally empty we don't override with non-empty field value
# since it can be intentionally empty we don't override with non-empty field value
portal_type_list
=
selection_params
.
get
(
"portal_type"
,
field
.
get_value
(
'portal_types'
))
portal_type_list
=
selection_params
.
get
(
"portal_type"
,
field
.
get_value
(
'portal_types'
))
# requirement: get only sortable/searchable columns which are already displayed in listbox
# requirement: get only sortable/searchable columns which are already displayed in listbox
# see https://lab.nexedi.com/nexedi/erp5/blob/HEAD/product/ERP5Form/ListBox.py#L1004
# see https://lab.nexedi.com/nexedi/erp5/blob/HEAD/product/ERP5Form/ListBox.py#L1004
# implemented in javascript in the end
# implemented in javascript in the end
...
@@ -905,12 +951,13 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
...
@@ -905,12 +951,13 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
formbox_context
=
traversed_document
formbox_context
=
traversed_document
if
field
.
get_value
(
'context_method_id'
):
if
field
.
get_value
(
'context_method_id'
):
# harness acquisition and call the method right away
# harness acquisition and call the method right away
formbox_context
=
getattr
(
traversed_document
,
field
.
get_value
(
'context_method_id'
))()
formbox_context
=
getattr
(
traversed_document
,
field
.
get_value
(
'context_method_id'
))(
field
=
field
,
REQUEST
=
REQUEST
)
embedded_document
[
'_debug'
]
=
"Different context"
embedded_document
[
'_debug'
]
=
"Different context"
# get embedded form definition
embeded_form
=
getattr
(
formbox_context
,
field
.
get_value
(
'formbox_target_id'
))
embed
d
ed_form
=
getattr
(
formbox_context
,
field
.
get_value
(
'formbox_target_id'
))
# renderForm mutates `embedded_document` therefor no return/assignment
# renderForm mutates `embedded_document` therefor no return/assignment
renderForm
(
formbox_context
,
embeded_form
,
embedded_document
,
key_prefix
=
key
)
renderForm
(
formbox_context
,
embed
d
ed_form
,
embedded_document
,
key_prefix
=
key
)
# fix editability which is hard-coded to 0 in `renderForm` implementation
# fix editability which is hard-coded to 0 in `renderForm` implementation
embedded_document
[
'form_id'
][
'editable'
]
=
field
.
get_value
(
"editable"
)
embedded_document
[
'form_id'
][
'editable'
]
=
field
.
get_value
(
"editable"
)
...
@@ -944,7 +991,7 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
...
@@ -944,7 +991,7 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
"""
"""
Render a `form` in plain python dict.
Render a `form` in plain python dict.
This function sets varibles 'here' and 'form_id' resp. 'dialog_id' for forms resp. form dialogs to REQUEST.
This function sets vari
a
bles 'here' and 'form_id' resp. 'dialog_id' for forms resp. form dialogs to REQUEST.
Any other REQUEST mingling are at the responsability of the callee.
Any other REQUEST mingling are at the responsability of the callee.
:param selection_params: holds parameters to construct ERP5Form.Selection instance
:param selection_params: holds parameters to construct ERP5Form.Selection instance
...
@@ -957,7 +1004,7 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
...
@@ -957,7 +1004,7 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
# Following pop/push of form_id resp. dialog_id is here because of FormBox - an embedded form in a form
# Following pop/push of form_id resp. dialog_id is here because of FormBox - an embedded form in a form
# Fields of forms use form_id in their TALES expressions and obviously FormBox's form_id is different
# Fields of forms use form_id in their TALES expressions and obviously FormBox's form_id is different
# from its parent's form
# from its parent's form
. It is very important that we do not remove form_id in case of a Dialog Form.
if
form
.
pt
==
"form_dialog"
:
if
form
.
pt
==
"form_dialog"
:
previous_request_other
[
'dialog_id'
]
=
REQUEST
.
other
.
pop
(
'dialog_id'
,
None
)
previous_request_other
[
'dialog_id'
]
=
REQUEST
.
other
.
pop
(
'dialog_id'
,
None
)
REQUEST
.
set
(
'dialog_id'
,
form
.
id
)
REQUEST
.
set
(
'dialog_id'
,
form
.
id
)
...
@@ -967,7 +1014,6 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
...
@@ -967,7 +1014,6 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
field_errors
=
REQUEST
.
get
(
'field_errors'
,
{})
field_errors
=
REQUEST
.
get
(
'field_errors'
,
{})
#hardcoded
include_action
=
True
include_action
=
True
if
form
.
pt
==
'form_dialog'
:
if
form
.
pt
==
'form_dialog'
:
action_to_call
=
"Base_callDialogMethod"
action_to_call
=
"Base_callDialogMethod"
...
@@ -989,6 +1035,14 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
...
@@ -989,6 +1035,14 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
"method"
:
form
.
method
,
"method"
:
form
.
method
,
}
}
}
}
if
form
.
pt
==
"form_dialog"
:
# If there is a "form_id" in the REQUEST then it means that last view was actually a form
# and we are most likely in a dialog. We save previous form into `last_form_id` ...
last_form_id
=
extra_param_json
.
pop
(
"form_id"
,
""
)
or
REQUEST
.
get
(
"form_id"
,
""
)
except
AttributeError
:
pass
# Form traversed_document
# Form traversed_document
response_dict
[
'_links'
][
'traversed_document'
]
=
{
response_dict
[
'_links'
][
'traversed_document'
]
=
{
"href"
:
default_document_uri_template
%
{
"href"
:
default_document_uri_template
%
{
...
@@ -1026,25 +1080,25 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
...
@@ -1026,25 +1080,25 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
response_dict
[
field
.
id
]
=
renderField
(
traversed_document
,
field
,
form
,
key_prefix
=
key_prefix
,
selection_params
=
selection_params
,
extra_param_json
=
extra_param_json
)
response_dict
[
field
.
id
]
=
renderField
(
traversed_document
,
field
,
form
,
key_prefix
=
key_prefix
,
selection_params
=
selection_params
,
extra_param_json
=
extra_param_json
)
if
field_errors
.
has_key
(
field
.
id
):
if
field_errors
.
has_key
(
field
.
id
):
response_dict
[
field
.
id
][
"error_text"
]
=
field_errors
[
field
.
id
].
error_text
response_dict
[
field
.
id
][
"error_text"
]
=
field_errors
[
field
.
id
].
error_text
except
AttributeError
:
except
AttributeError
as
error
:
# Do not crash if field configuration is wrong.
# Do not crash if field configuration is wrong.
pass
log
(
"Field {} rendering failed because of {!s}"
.
format
(
field
.
id
,
error
),
level
=
800
)
# Form Edit handler uses form_id to recover the submitted form.
# Form Edit handler uses form_id to recover the submitted form and to control its
# Form Dialog handler uses 'dialog_id' instead and 'form_id'
# properties like editability
# - Some dialog actions (e.g. Print) uses form_id to obtain previous view form
if
form
.
pt
==
'form_dialog'
:
if
(
form
.
pt
==
'form_dialog'
):
addHiddenFieldToForm
(
response_dict
,
'dialog_id'
,
form
.
id
)
# overwrite "form_id" field's value because old UI does that by passing
# overwrite "form_id" field's value because old UI does that by passing
# the form_id in query string and hidden fields
# the form_id in query string and hidden fields
if
REQUEST
.
get
(
'form_id'
,
None
):
renderHiddenField
(
response_dict
,
"form_id"
,
REQUEST
.
get
(
'form_id'
)
or
form
.
id
)
addHiddenFieldToForm
(
response_dict
,
"form_id"
,
REQUEST
.
get
(
'form_id'
))
# dialog_id is a mandatory field in any form_dialog
# some dialog actions (Print Module) use previous selection name
renderHiddenField
(
response_dict
,
'dialog_id'
,
form
.
id
)
if
REQUEST
.
get
(
'selection_name'
,
None
):
# some dialog actions use custom cancel_url
addHiddenFieldToForm
(
response_dict
,
"selection_name"
,
REQUEST
.
get
(
'selection_name'
))
if
REQUEST
.
get
(
'cancel_url'
,
None
):
renderHiddenField
(
response_dict
,
"cancel_url"
,
REQUEST
.
get
(
'cancel_url'
))
else
:
else
:
# In form_view we place only form_id in the request form
# In form_view we place only form_id in the request form
addHiddenFieldToForm
(
response_dict
,
'form_id'
,
form
.
id
)
renderHiddenField
(
response_dict
,
'form_id'
,
form
.
id
)
if
(
form
.
pt
==
'report_view'
):
if
(
form
.
pt
==
'report_view'
):
# reports are expected to return list of ReportSection which is a wrapper
# reports are expected to return list of ReportSection which is a wrapper
...
@@ -1135,52 +1189,27 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
...
@@ -1135,52 +1189,27 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
response_dict
[
'report_section_list'
]
=
report_result_list
response_dict
[
'report_section_list'
]
=
report_result_list
# end-if report_section
# end-if report_section
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
:
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
# metadata about the form (its hash and dynamic fields)
renderHiddenField
(
response_dict
,
'extra_param_json'
,
json
.
dumps
(
extra_param_json
))
for
key
,
value
in
previous_request_other
.
items
():
for
key
,
value
in
previous_request_other
.
items
():
if
value
is
not
None
:
if
value
is
not
None
:
REQUEST
.
set
(
key
,
value
)
REQUEST
.
set
(
key
,
value
)
# XXX form action update, etc
def
renderRawField
(
field
):
meta_type
=
field
.
meta_type
return
{
"meta_type"
:
field
.
meta_type
}
if
meta_type
==
"MethodField"
:
result
=
{
"meta_type"
:
field
.
meta_type
}
else
:
result
=
{
"meta_type"
:
field
.
meta_type
,
"_values"
:
field
.
values
,
# XXX TALES expression is not JSON serializable by default
# "_tales": field.tales
"_overrides"
:
field
.
overrides
}
if
meta_type
==
"ProxyField"
:
result
[
'_delegated_list'
]
=
field
.
delegated_list
# try:
# result['_delegated_list'].pop('list_method')
# except KeyError:
# pass
# XXX ListMethod is not JSON serialized by default
try
:
result
[
'_values'
].
pop
(
'list_method'
)
except
KeyError
:
pass
try
:
result
[
'_overrides'
].
pop
(
'list_method'
)
except
KeyError
:
pass
return
result
def
renderFormDefinition
(
form
,
response_dict
):
def
renderFormDefinition
(
form
,
response_dict
):
"""Form "definition" is configurable in Zope admin: Form -> Order."""
"""Form "definition" is configurable in Zope admin: Form -> Order.
We add some known constants inside Forms such as form_id and into
Dialog Form such as dialog_id.
"""
group_list
=
[]
group_list
=
[]
for
group
in
form
.
Form_getGroupTitleAndId
():
for
group
in
form
.
Form_getGroupTitleAndId
():
...
@@ -1188,7 +1217,7 @@ def renderFormDefinition(form, response_dict):
...
@@ -1188,7 +1217,7 @@ def renderFormDefinition(form, response_dict):
field_list
=
[]
field_list
=
[]
for
field
in
form
.
get_fields_in_group
(
group
[
'goid'
],
include_disabled
=
1
):
for
field
in
form
.
get_fields_in_group
(
group
[
'goid'
],
include_disabled
=
1
):
field_list
.
append
((
field
.
id
,
renderRawField
(
field
)
))
field_list
.
append
((
field
.
id
,
{
'meta_type'
:
field
.
meta_type
}
))
group_list
.
append
((
group
[
'gid'
],
field_list
))
group_list
.
append
((
group
[
'gid'
],
field_list
))
...
@@ -1294,22 +1323,26 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1294,22 +1323,26 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
)
)
response
.
setStatus
(
401
)
response
.
setStatus
(
401
)
return
""
return
""
elif
mime_type
!=
traversed_document
.
Base_handleAcceptHeader
([
mime_type
]):
elif
mime_type
!=
traversed_document
.
Base_handleAcceptHeader
([
mime_type
]):
response
.
setStatus
(
406
)
response
.
setStatus
(
406
)
return
""
return
""
elif
(
mode
==
'root'
)
or
(
mode
==
'traverse'
):
elif
(
mode
==
'root'
)
or
(
mode
==
'traverse'
):
#################################################
##
# Raw document
# Render ERP Document with a `view` specified
#################################################
# `view` contains view's name and we extract view's URL (we suppose form ${object_url}/Form_view)
# which after expansion gives https://<site-root>/context/view_id?optional=params
if
(
REQUEST
is
not
None
)
and
(
REQUEST
.
other
[
'method'
]
!=
"GET"
):
if
(
REQUEST
is
not
None
)
and
(
REQUEST
.
other
[
'method'
]
!=
"GET"
):
response
.
setStatus
(
405
)
response
.
setStatus
(
405
)
return
""
return
""
# Default properties shared by all ERP5 Document and Site
# Default properties shared by all ERP5 Document and Site
action_dict
=
{}
current_action
=
{}
# current action parameters (context, script, URL params)
# result_dict['_relative_url'] = traversed_document.getRelativeUrl()
action_dict
=
{}
# actions available on current `traversed_document`
last_form_id
=
None
# will point to the previous form so we can obtain previous selection
result_dict
[
'title'
]
=
traversed_document
.
getTitle
()
result_dict
[
'title'
]
=
traversed_document
.
getTitle
()
# Add a link to the portal type if possible
# Add a link to the portal type if possible
...
@@ -1342,65 +1375,44 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1342,65 +1375,44 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
"name"
:
Base_translateString
(
container
.
getTitle
()),
"name"
:
Base_translateString
(
container
.
getTitle
()),
}
}
# Extract embedded form in the document view
# Find current action URL and extract embedded view
embedded_url
=
None
erp5_action_dict
=
portal
.
Base_filterDuplicateActions
(
erp5_action_dict
=
portal
.
Base_filterDuplicateActions
(
portal
.
portal_actions
.
listFilteredActionsFor
(
traversed_document
))
portal
.
portal_actions
.
listFilteredActionsFor
(
traversed_document
))
for
erp5_action_key
in
erp5_action_dict
.
keys
():
for
erp5_action_key
in
erp5_action_dict
.
keys
():
for
view_action
in
erp5_action_dict
[
erp5_action_key
]:
for
view_action
in
erp5_action_dict
[
erp5_action_key
]:
# Try to embed the form in the result
# Try to embed the form in the result
if
(
view
==
view_action
[
'id'
]):
if
(
view
==
view_action
[
'id'
]):
embedded_url
=
'%s'
%
view_action
[
'url'
]
current_action
=
parseActionUrl
(
'%s'
%
view_action
[
'url'
])
# current action/view being rendered
# If we have current action definition we are able to render embedded view
# `form_id` should be actually called `dialog_id` in case of form dialogs
# which should be a "ERP5 Form" but in reality can be anything
# so real form_id of a previous view stays untouched.
if
current_action
.
get
(
'view_id'
,
''
):
# Here we save previous form_id to `last_form_id` so it does not get overriden by `dialog_id`
view_instance
=
getattr
(
traversed_document
,
current_action
[
'view_id'
])
last_form_id
=
REQUEST
.
get
(
'form_id'
,
""
)
if
REQUEST
is
not
None
else
""
if
(
view_instance
is
not
None
):
form_id
=
""
if
(
embedded_url
is
not
None
):
# XXX Try to fetch the form in the traversed_document of the document
# Of course, this code will completely crash in many cases (page template
# instead of form, unexpected action TALES expression). Happy debugging.
# renderer_form_relative_url = view_action['url'][len(portal.absolute_url()):]
form_id
=
embedded_url
.
split
(
'?'
,
1
)[
0
].
split
(
"/"
)[
-
1
]
# renderer_form = traversed_document.restrictedTraverse(form_id, None)
# XXX Proxy field are not correctly handled in traversed_document of web site
renderer_form
=
getattr
(
traversed_document
,
form_id
)
if
(
renderer_form
is
not
None
):
embedded_dict
=
{
embedded_dict
=
{
'_links'
:
{
'_links'
:
{
'self'
:
{
'self'
:
{
'href'
:
embedded_url
'href'
:
current_action
[
'url'
]
}
}
}
}
}
}
# Put all query parameters (?reset:int=1&workflow_action=start_action) in request to mimic usual form display
# Put all query parameters (?reset:int=1&workflow_action=start_action) in request to mimic usual form display
query_param_dict
=
{}
# Request is later used for method's arguments discovery so set URL params into REQUEST (just like it was sent by form)
query_split
=
embedded_url
.
split
(
'?'
,
1
)
for
query_key
,
query_value
in
current_action
[
'params'
].
items
():
if
len
(
query_split
)
==
2
:
for
query_parameter
in
query_split
[
1
].
split
(
"&"
):
query_key
,
query_value
=
query_parameter
.
split
(
'='
)
# often + is used instead of %20 so we replace for space here
query_param_dict
[
query_key
]
=
query_value
.
replace
(
"+"
,
" "
)
# set URL params into REQUEST (just like it was sent by form)
for
query_key
,
query_value
in
query_param_dict
.
items
():
REQUEST
.
set
(
query_key
,
query_value
)
REQUEST
.
set
(
query_key
,
query_value
)
# Embedded Form can be a Script or even a class method thus we mitigate here
# If our "form" is actually a Script (nothing is sure in ERP5) then execute it here
try
:
try
:
if
"Script"
in
renderer_form
.
meta_type
:
if
"Script"
in
view_instance
.
meta_type
:
# we suppose that the script takes only what is given in the URL params
# we suppose that the script takes only what is given in the URL params
return
renderer_form
(
**
query_param_dict
)
return
view_instance
(
**
current_action
[
'params'
]
)
except
AttributeError
:
except
AttributeError
:
# if renderer form does not have attr meta_type then it is not a document
# if renderer form does not have attr meta_type then it is not a document
# but most likely bound instance method. Some form_ids do actually point to methods.
# but most likely bound instance method. Some form_ids do actually point to methods.
returned_value
=
renderer_form
(
**
query_param_dict
)
returned_value
=
view_instance
(
**
current_action
[
'params'
]
)
# returned value is usually REQUEST.RESPONSE.redirect()
# returned value is usually REQUEST.RESPONSE.redirect()
log
(
'ERP5Document_getHateoas'
,
'HAL_JSON cannot handle returned value "{!s}" from {}({!s})'
.
format
(
log
(
'ERP5Document_getHateoas'
,
'HAL_JSON cannot handle returned value "{!s}" from {}({!s})'
.
format
(
returned_value
,
form_id
,
query_param_dict
),
100
)
returned_value
,
current_action
[
'view_id'
],
current_action
[
'params'
]
),
100
)
status_message
=
Base_translateString
(
'Operation executed'
)
status_message
=
Base_translateString
(
'Operation executed'
)
if
isinstance
(
returned_value
,
(
str
,
unicode
))
and
returned_value
.
startswith
(
'http'
):
if
isinstance
(
returned_value
,
(
str
,
unicode
))
and
returned_value
.
startswith
(
'http'
):
parsed_url
=
urlparse
(
returned_value
)
parsed_url
=
urlparse
(
returned_value
)
...
@@ -1437,14 +1449,13 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1437,14 +1449,13 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
# select correct URL template based on action_type and form page template
# select correct URL template based on action_type and form page template
url_template_key
=
"traverse_generator"
url_template_key
=
"traverse_generator"
if
erp5_action_key
not
in
(
"view"
,
"object_view"
,
"object_jio_view"
):
if
erp5_action_key
not
in
(
"view"
,
"object_view"
,
"object_jio_view"
):
# previous view's form_id required almost everything but other views
url_template_key
=
"traverse_generator_action"
url_template_key
=
"traverse_generator_non_view"
# but when we do not have the last form id we do not pass is of course
# XXX This line is only optimization for shorter URL and thus is ugly
if
not
(
current_action
.
get
(
'view_id'
,
''
)
or
last_form_id
):
if
not
(
form_id
or
last_form_id
):
url_template_key
=
"traverse_generator"
url_template_key
=
"traverse_generator"
erp5_action_list
[
-
1
][
'href'
]
=
url_template_dict
[
url_template_key
]
%
{
erp5_action_list
[
-
1
][
'href'
]
=
url_template_dict
[
url_template_key
]
%
{
"root_url"
:
site_root
.
absolute_url
(),
"root_url"
:
site_root
.
absolute_url
(),
"script_id"
:
script
.
id
,
"script_id"
:
script
.
id
,
# this script (ERP5Document_getHateoas)
"relative_url"
:
traversed_document
.
getRelativeUrl
().
replace
(
"/"
,
"%2F"
),
"relative_url"
:
traversed_document
.
getRelativeUrl
().
replace
(
"/"
,
"%2F"
),
"view"
:
erp5_action_list
[
-
1
][
'name'
],
"view"
:
erp5_action_list
[
-
1
][
'name'
],
"form_id"
:
form_id
if
form_id
and
renderer_form
.
pt
==
"form_view"
else
last_form_id
"form_id"
:
form_id
if
form_id
and
renderer_form
.
pt
==
"form_view"
else
last_form_id
...
@@ -1662,7 +1673,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1662,7 +1673,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
elif
sort_order
.
lower
().
startswith
(
"desc"
):
elif
sort_order
.
lower
().
startswith
(
"desc"
):
sort_order
=
"DESC"
sort_order
=
"DESC"
else
:
else
:
# should raise a
n
ValueError instead
# should raise a ValueError instead
log
(
'Wrong sort order "{}" in {}! It must start with "asc" or "desc"'
.
format
(
sort_order
,
form_relative_url
),
log
(
'Wrong sort order "{}" in {}! It must start with "asc" or "desc"'
.
format
(
sort_order
,
form_relative_url
),
level
=
200
)
# error
level
=
200
)
# error
return
(
sort_col
,
sort_order
)
return
(
sort_col
,
sort_order
)
...
@@ -1685,7 +1696,6 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1685,7 +1696,6 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
#
#
# for k, v in catalog_kw.items():
# for k, v in catalog_kw.items():
# REQUEST.set(k, v)
# REQUEST.set(k, v)
search_result_iterable
=
callable_list_method
(
**
catalog_kw
)
search_result_iterable
=
callable_list_method
(
**
catalog_kw
)
# Cast to list if only one element is provided
# Cast to list if only one element is provided
...
@@ -1721,9 +1731,6 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1721,9 +1731,6 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
if
'selection'
not
in
catalog_kw
:
if
'selection'
not
in
catalog_kw
:
catalog_kw
[
'selection'
]
=
context
.
getPortalObject
().
portal_selections
.
getSelectionFor
(
selection_name
,
REQUEST
)
catalog_kw
[
'selection'
]
=
context
.
getPortalObject
().
portal_selections
.
getSelectionFor
(
selection_name
,
REQUEST
)
# field TALES expression evaluated by Base_getRelatedObjectParameter requires that
REQUEST
.
other
[
'form_id'
]
=
listbox_form
.
id
for
select
in
select_list
:
for
select
in
select_list
:
# See Listbox.py getValueList --> getEditableField & getColumnAliasList method
# See Listbox.py getValueList --> getEditableField & getColumnAliasList method
# In short: there are Form Field definitions which names start with
# In short: there are Form Field definitions which names start with
...
@@ -1817,7 +1824,9 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1817,7 +1824,9 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
# if there is no tales expr (or is empty) we extract the value from search result
# if there is no tales expr (or is empty) we extract the value from search result
default_field_value
=
getAttrFromAnything
(
search_result
,
select
,
property_getter
,
{})
default_field_value
=
getAttrFromAnything
(
search_result
,
select
,
property_getter
,
{})
contents_item
[
select
]
=
renderField
(
# If the contents_item has field rendering in it, better is to add an
# extra layer of abstraction to not get conflicts
contents_item
[
select
][
'field_gadget_param'
]
=
renderField
(
traversed_document
,
traversed_document
,
editable_field_dict
[
select
],
editable_field_dict
[
select
],
listbox_form
,
listbox_form
,
...
@@ -1830,107 +1839,70 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1830,107 +1839,70 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
# a key name to Python Script with variable number of input parameters.
# a key name to Python Script with variable number of input parameters.
contents_item
[
select
]
=
getAttrFromAnything
(
search_result
,
select
,
property_getter
,
{
'brain'
:
search_result
})
contents_item
[
select
]
=
getAttrFromAnything
(
search_result
,
select
,
property_getter
,
{
'brain'
:
search_result
})
# If the contents_item has field rendering in it, better is to add an
# extra layer of abstraction to not get conflicts
if
isinstance
(
contents_item
[
select
],
dict
):
contents_item
[
select
]
=
{
'field_gadget_param'
:
contents_item
[
select
],
}
# By default, we won't be generating views in the URL
# By default, we won't be generating views in the URL
generate_view
=
False
url_parameter_dict
=
None
url_parameter_dict
=
{}
if
select
in
url_column_dict
:
if
select
in
url_column_dict
:
# Check if we get URL parameters using listbox field `url_columns`
# Check if we get URL parameters using listbox field `url_columns`
url_column_method
=
getattr
(
search_result
,
url_column_dict
[
select
],
None
)
try
:
# If there is empty url_column_method, do nothing and continue. This
url_column_method
=
getattr
(
search_result
,
url_column_dict
[
select
])
# will create no URL in these cases
# Result of `url_column_method` must be a dictionary in the format
if
url_column_method
is
None
:
# {'command': <command_name, ex: 'raw', 'push_history'>,
continue
# 'options': {'url': <Absolute URL>, 'jio_key': <Relative URL of object>, 'view': <id of the view>}}
url_parameter_dict
=
url_column_method
(
url_parameter_dict
=
url_column_method
(
url_dict
=
True
,
url_dict
=
True
,
brain
=
search_result
,
brain
=
search_result
,
selection
=
catalog_kw
[
'selection'
],
selection
=
catalog_kw
[
'selection'
],
selection_name
=
catalog_kw
[
'selection_name'
],
selection_name
=
catalog_kw
[
'selection_name'
],
column_id
=
select
)
column_id
=
select
)
except
AttributeError
as
e
:
# Since, now we are using url_columns for both XHTML UI and renderJS UI,
if
url_column_dict
[
select
]:
# its normal to get string as a result of the `url_column_method`
log
(
"Invalid URL method {!s} on column {}"
.
format
(
url_column_dict
[
select
],
select
),
level
=
800
)
# function call. In that cases, we will do nothing. Take note, the
# result of `url_column_method` function call which is usable here should
# be dictionary in the format :-
# {
# 'command': <command_name, ex: 'raw', 'push_history'>,
# 'options': {
# 'url': <Absolute URL>,
# 'jio_key': <Relative URL of object>,
# 'view': <id of the view>,
# }
# }
if
isinstance
(
url_parameter_dict
,
str
):
continue
elif
getattr
(
search_result
,
'getListItemUrlDict'
,
None
)
is
not
None
:
elif
getattr
(
search_result
,
'getListItemUrlDict'
,
None
)
is
not
None
:
# Check if we can get URL result from the brain
# Check if we can get URL result from the brain
try
:
try
:
url_parameter_dict
=
search_result
.
getListItemUrlDict
(
url_parameter_dict
=
search_result
.
getListItemUrlDict
(
select
,
select
,
result_index
,
catalog_kw
[
'selection_name'
]
result_index
,
)
catalog_kw
[
'selection_name'
]
)
except
(
ConflictError
,
RuntimeError
):
except
(
ConflictError
,
RuntimeError
):
raise
raise
except
:
except
:
log
(
'could not evaluate the url method getListItemUrlDict with %r'
%
search_result
,
log
(
'could not evaluate the url method getListItemUrlDict with %r'
%
search_result
,
level
=
800
)
level
=
800
)
continue
else
:
# Continue in case there is no url_column_dict or brain to get URL
continue
url_result_dict
=
{
select
:
url_parameter_dict
}
# If contents item don't have `field_gadget_param` in it, then add it
# to default
if
not
isinstance
(
contents_item
[
select
],
dict
):
contents_item
[
select
]
=
{
'default'
:
contents_item
[
select
],
}
contents_item
[
select
].
update
({
'url_value'
:
url_result_dict
[
select
]})
if
contents_item
[
select
][
'url_value'
]:
if
isinstance
(
url_parameter_dict
,
dict
):
# We need to put URL into rendered field so just ensure it is a dict
if
not
isinstance
(
contents_item
[
select
],
dict
):
contents_item
[
select
]
=
{
'default'
:
contents_item
[
select
],
'editable'
:
False
}
# We should be generating view if there is extra params for view in
# We should be generating view if there is extra params for view in
# view_kw. These parameters are required to create url at hateoas side
# view_kw. These parameters are required to create url at hateoas side
# using the URL template as necessary
# using the URL template as necessary
if
'view_kw'
in
contents_item
[
select
][
'url_value'
]:
if
'view_kw'
not
in
url_parameter_dict
:
generate_view
=
True
contents_item
[
select
][
'url_value'
]
=
url_parameter_dict
else
:
# Get extra parameters either from url_result_dict or from brain
# Get extra parameters either from url_result_dict or from brain
extra_url_param_dict
=
contents_item
[
select
][
'url_value'
][
'view_kw'
].
get
(
'extra_param_json'
,
{})
extra_url_param_dict
=
url_parameter_dict
[
'view_kw'
].
get
(
'extra_param_json'
,
{})
if
generate_view
:
url_template_id
=
'traverse_generator'
url_template_id
=
'traverse_generator'
if
extra_url_param_dict
:
if
extra_url_param_dict
:
url_template_id
=
'traverse_generator_with_parameter'
url_template_id
=
'traverse_generator_action'
contents_item
[
select
][
'url_value'
][
'options'
][
'view'
]
=
\
contents_item
[
select
][
'url_value'
]
=
{
url_template_dict
[
url_template_id
]
%
{
'command'
:
url_parameter_dict
[
'command'
],
"root_url"
:
site_root
.
absolute_url
(),
'options'
:
{
"script_id"
:
script
.
id
,
'jio_key'
:
url_parameter_dict
.
get
(
'options'
,
{}).
get
(
'jio_key'
,
url_parameter_dict
[
'view_kw'
][
'jio_key'
]),
"relative_url"
:
contents_item
[
select
][
'url_value'
][
'view_kw'
][
'jio_key'
].
replace
(
"/"
,
"%2F"
),
'editable'
:
url_parameter_dict
.
get
(
'options'
,
{}).
get
(
'editable'
,
None
),
"view"
:
contents_item
[
select
][
'url_value'
][
'view_kw'
][
'view'
],
'view'
:
url_template_dict
[
url_template_id
]
%
{
"extra_param_json"
:
urlsafe_b64encode
(
"root_url"
:
site_root
.
absolute_url
(),
json
.
dumps
(
ensureSerializable
(
extra_url_param_dict
)))
"script_id"
:
script
.
id
,
"relative_url"
:
url_parameter_dict
[
'view_kw'
][
'jio_key'
].
replace
(
"/"
,
"%2F"
),
"view"
:
url_parameter_dict
[
'view_kw'
][
'view'
],
"extra_param_json"
:
urlsafe_b64encode
(
json
.
dumps
(
ensureSerializable
(
extra_url_param_dict
)))
}
}
}
}
# Its better to remove the 'view_kw' from the dictionary as it doesn't
# serve any purpose further in the result_dict
if
'view_kw'
in
contents_item
[
select
][
'url_value'
]:
del
contents_item
[
select
][
'url_value'
][
'view_kw'
]
# endfor select
# endfor select
REQUEST
.
other
.
pop
(
'cell'
,
None
)
REQUEST
.
other
.
pop
(
'cell'
,
None
)
contents_list
.
append
(
contents_item
)
contents_list
.
append
(
contents_item
)
...
@@ -1943,30 +1915,23 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1943,30 +1915,23 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
source_field_meta_type
=
source_field
.
getRecursiveTemplateField
().
meta_type
source_field_meta_type
=
source_field
.
getRecursiveTemplateField
().
meta_type
# Lets mingle with editability of fields here
# Lets mingle with editability of fields here
# Original Listbox.py modifies editability during field rendering (method `render`)
# Original Listbox.py modifies editability during field rendering (method `render`)
,
# which
we cannot use here
so we overwrite result's own editability
# which
is done on frontend side,
so we overwrite result's own editability
if
source_field
is
not
None
and
source_field_meta_type
==
"ListBox"
:
if
source_field
is
not
None
and
source_field_meta_type
==
"ListBox"
:
# XXX TODO: should take into account "editable_columns" from listbox own selection
editable_column_set
=
set
(
name
for
name
,
_
in
source_field
.
get_value
(
"editable_columns"
))
editable_column_set
=
set
(
name
for
name
,
_
in
source_field
.
get_value
(
"editable_columns"
))
for
line
in
result_dict
[
'_embedded'
][
'contents'
]:
for
line
in
result_dict
[
'_embedded'
][
'contents'
]:
for
select
in
line
:
for
select
in
line
:
# forbid editability only for fields not specified in editable_columns
# forbid editability only for fields not specified in editable_columns
if
select
in
editable_column_set
:
if
select
in
editable_column_set
:
continue
continue
if
(
isinstance
(
line
[
select
],
dict
)
and
if
isinstance
(
line
[
select
],
dict
)
and
'field_gadget_param'
in
line
[
select
]:
line
[
select
].
get
(
'field_gadget_param'
,
None
)
is
not
None
):
line
[
select
][
'field_gadget_param'
][
'editable'
]
=
False
if
line
[
select
][
'field_gadget_param'
].
get
(
'editable'
):
line
[
select
][
'field_gadget_param'
][
'editable'
]
=
False
if
source_field
is
not
None
and
source_field_meta_type
==
"ListBox"
:
if
source_field
is
not
None
and
source_field_meta_type
==
"ListBox"
:
# Trigger count method if exist
# Trigger count method if exist
# XXX No need to count if no pagination
# XXX No need to count if no pagination
count_method
=
source_field
.
get_value
(
'count_method'
)
or
None
count_method
=
source_field
.
get_value
(
'count_method'
)
count_method_name
=
count_method
.
getMethodName
()
if
count_method
is
not
None
else
""
if
count_method
!=
""
and
count_method
.
getMethodName
()
!=
list_method
:
# Only try to get count method results in case method name exists, in all
# other cases, just pass.
if
count_method_name
!=
""
and
count_method_name
!=
list_method
:
count_kw
=
dict
(
catalog_kw
)
count_kw
=
dict
(
catalog_kw
)
# Drop not needed parameters
# Drop not needed parameters
count_kw
.
pop
(
'selection'
,
None
)
count_kw
.
pop
(
'selection'
,
None
)
...
@@ -1974,7 +1939,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1974,7 +1939,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
count_kw
.
pop
(
"sort_on"
,
None
)
count_kw
.
pop
(
"sort_on"
,
None
)
count_kw
.
pop
(
"limit"
,
None
)
count_kw
.
pop
(
"limit"
,
None
)
try
:
try
:
count_method
=
getattr
(
traversed_document
,
count_method
_name
)
count_method
=
getattr
(
traversed_document
,
count_method
.
getMethodName
()
)
count_method_result
=
count_method
(
REQUEST
=
REQUEST
,
**
count_kw
)
count_method_result
=
count_method
(
REQUEST
=
REQUEST
,
**
count_kw
)
result_dict
[
'_embedded'
][
'count'
]
=
ensureSerializable
(
count_method_result
[
0
][
0
])
result_dict
[
'_embedded'
][
'count'
]
=
ensureSerializable
(
count_method_result
[
0
][
0
])
except
AttributeError
as
error
:
except
AttributeError
as
error
:
...
@@ -1983,7 +1948,6 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1983,7 +1948,6 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
# and just pass. This also ensures we have compatibilty with how old
# and just pass. This also ensures we have compatibilty with how old
# UI behave in these cases.
# UI behave in these cases.
log
(
'Invalid count method %s'
%
error
,
level
=
800
)
log
(
'Invalid count method %s'
%
error
,
level
=
800
)
pass
contents_stat_list
=
[]
contents_stat_list
=
[]
# in case the search was issued by listbox we can provide results of
# in case the search was issued by listbox we can provide results of
...
@@ -1992,6 +1956,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1992,6 +1956,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
stat_method
=
source_field
.
get_value
(
'stat_method'
)
stat_method
=
source_field
.
get_value
(
'stat_method'
)
stat_columns
=
source_field
.
get_value
(
'stat_columns'
)
stat_columns
=
source_field
.
get_value
(
'stat_columns'
)
contents_stat
=
{}
contents_stat
=
{}
if
len
(
stat_columns
)
>
0
:
if
len
(
stat_columns
)
>
0
:
# prefer stat per column (follow original ListBox.py implementation)
# prefer stat per column (follow original ListBox.py implementation)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment