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
Tomáš Peterka
erp5
Commits
d2f8c300
Commit
d2f8c300
authored
Dec 05, 2017
by
Tomáš Peterka
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
WWIP
parent
45641950
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
396 additions
and
299 deletions
+396
-299
bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py
...rtal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py
+219
-147
bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_jio_js.js
...athTemplateItem/web_page_module/rjs_gadget_erp5_jio_js.js
+25
-23
bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_jio_js.xml
...thTemplateItem/web_page_module/rjs_gadget_erp5_jio_js.xml
+2
-2
bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_listbox_html.html
...ateItem/web_page_module/rjs_gadget_erp5_listbox_html.html
+5
-9
bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_listbox_html.xml
...lateItem/web_page_module/rjs_gadget_erp5_listbox_html.xml
+2
-2
bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_listbox_js.js
...emplateItem/web_page_module/rjs_gadget_erp5_listbox_js.js
+85
-66
bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_listbox_js.xml
...mplateItem/web_page_module/rjs_gadget_erp5_listbox_js.xml
+2
-2
bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_nojqm_css.css
...emplateItem/web_page_module/rjs_gadget_erp5_nojqm_css.css
+23
-14
bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_nojqm_css.xml
...emplateItem/web_page_module/rjs_gadget_erp5_nojqm_css.xml
+2
-2
bt5/erp5_web_renderjs_ui/SkinTemplateItem/portal_skins/erp5_web_renderjs_ui/erp5css.less.txt
...teItem/portal_skins/erp5_web_renderjs_ui/erp5css.less.txt
+31
-32
No files found.
bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py
View file @
d2f8c300
...
...
@@ -21,8 +21,13 @@ Only in mode == 'form'
Only in mode == 'traverse'
TBD.
# Form
When handling form, we can expect field values to be stored in REQUEST.form in two forms
- raw string value under key "field_" + <field.id>
- python-object parsed from raw values under <field.id>
"""
from
ZTUtils
import
make_query
import
json
from
base64
import
urlsafe_b64encode
,
urlsafe_b64decode
...
...
@@ -35,13 +40,32 @@ import re
from
zExceptions
import
Unauthorized
from
Products.ERP5Type.Utils
import
UpperCase
from
Products.ZSQLCatalog.SQLCatalog
import
Query
,
ComplexQuery
from
Products.ERP5Type.Log
import
log
MARKER
=
[]
if
REQUEST
is
None
:
REQUEST
=
context
.
REQUEST
# raise Unauthorized
if
response
is
None
:
response
=
REQUEST
.
RESPONSE
def
toBasicTypes
(
obj
):
"""Ensure that obj contains only basic types."""
if
obj
is
None
:
return
obj
if
isinstance
(
obj
,
(
bool
,
int
,
float
,
long
,
str
,
unicode
)):
return
obj
if
isinstance
(
obj
,
(
tuple
,
list
)):
return
[
toBasicTypes
(
x
)
for
x
in
obj
]
try
:
return
{
toBasicTypes
(
key
):
toBasicTypes
(
obj
[
key
])
for
key
in
obj
}
except
:
log
(
'Cannot convert {!s} to basic types {!s}'
.
format
(
type
(
obj
),
obj
),
level
=
100
)
return
obj
# http://stackoverflow.com/a/13105359
def
byteify
(
string
):
if
isinstance
(
string
,
dict
):
...
...
@@ -53,11 +77,12 @@ def byteify(string):
else
:
return
string
def
ensure_serializable
(
obj
):
def
ensureSerializable
(
obj
):
"""Ensure obj and all sub-objects are JSON serializable."""
if
isinstance
(
obj
,
dict
):
for
key
in
obj
:
obj
[
key
]
=
ensure
_s
erializable
(
obj
[
key
])
obj
[
key
]
=
ensure
S
erializable
(
obj
[
key
])
# throw away date's type information and later reconstruct as Zope's DateTime
if
isinstance
(
obj
,
DateTime
):
return
obj
.
ISO
()
...
...
@@ -66,17 +91,18 @@ def ensure_serializable(obj):
# we don't check other isinstances - we believe that iterables don't contain unserializable objects
return
obj
datetime_iso_re
=
re
.
compile
(
r'^\
d{
4}-\
d{
2}-\
d{
2} |T\
d{
2}:\
d{
2}:\
d{
2}.*$'
)
time_iso_re
=
re
.
compile
(
r'^(\
d{
2}):(\
d{
2}):(\
d{
2}).*$'
)
def
ensure
_d
eserialized
(
obj
):
"""Deserialize classes serialized by our own `ensure
_s
erializable`.
def
ensure
D
eserialized
(
obj
):
"""Deserialize classes serialized by our own `ensure
S
erializable`.
Method `biteify` must not be called on the result because it would revert out
deserialization by calling __str__ on constructed classes.
"""
if
isinstance
(
obj
,
dict
):
for
key
in
obj
:
obj
[
key
]
=
ensure
_d
eserialized
(
obj
[
key
])
obj
[
key
]
=
ensure
D
eserialized
(
obj
[
key
])
# seems that default __str__ method is good enough
if
isinstance
(
obj
,
str
):
# Zope's DateTime must be good enough for everyone
...
...
@@ -97,16 +123,49 @@ def getProtectedProperty(document, select):
See https://lab.nexedi.com/nexedi/erp5/blob/master/product/ERP5Form/ListBox.py#L2293
"""
try
:
if
"."
in
select
:
#see https://lab.nexedi.com/nexedi/erp5/blob/master/product/ERP5Form/ListBox.py#L2293
try
:
select
=
select
[
select
.
rindex
(
'.'
)
+
1
:]
except
ValueError
:
pass
return
document
.
getProperty
(
select
,
d
=
None
)
except
(
ConflictError
,
RuntimeError
):
raise
except
:
return
None
def
kwargsForCallable
(
func
,
initial_kwargs
,
kwargs_dict
):
"""Create a copy of `kwargs_dict` with only items suitable for `func`.
In case the function cannot state required arguments it throws an AttributeError.
"""
func_param_list
=
[
func_param
.
strip
()
for
func_param
in
func
.
params
().
split
(
","
)]
func_param_name_list
=
[
func_param
if
'='
not
in
func_param
else
func_param
.
split
(
'='
)[
0
]
for
func_param
in
func_param_list
if
'*'
not
in
func_param
]
for
func_param_name
in
func_param_name_list
:
if
func_param_name
in
kwargs_dict
and
func_param_name
not
in
initial_kwargs
:
initial_kwargs
[
func_param_name
]
=
kwargs_dict
.
get
(
func_param_name
)
# MIDDLE-DANGEROUS!
# In case of reports (later even exports) substitute None for unknown
# parameters. We suppose Python syntax for parameters!
# What we do here is literally putting every form field from `kwargs_dict`
# into search method parameters - this is later put back into `kwargs_dict`
# this way we can mimic synchronous rendering when all form field values
# were available in `kwargs_dict`. It is obviously wrong behaviour.
for
func_param
in
func_param_list
:
if
"*"
in
func_param
:
continue
if
"="
in
func_param
:
continue
# now we have only mandatory parameters
func_param
=
func_param
.
strip
()
if
func_param
not
in
initial_kwargs
:
initial_kwargs
[
func_param
]
=
None
return
initial_kwargs
def
object_uids_and_accessors
(
search_result
,
result_index
,
traversed_document
):
def
anythingUidAndAccessor
(
search_result
,
result_index
,
traversed_document
):
"""Return unique ID, unique URL, getter and hasser for any combination of `search_result` and `index`.
You want to use this method when you need a unique reference to random object in iterable (for example
...
...
@@ -121,8 +180,9 @@ def object_uids_and_accessors(search_result, result_index, traversed_document):
result[uid] = {'url': portal.abolute_url() + url}
value = getter(random_object, "value")
"""
context
.
log
(
"anythingUidAndAccessor({!s}#type:{!s}, {:d}, {!s}"
.
format
(
search_result
,
type
(
search_result
),
result_index
,
traversed_document
))
if
hasattr
(
search_result
,
"getObject"
):
#
search_result = search_result.getObject()
#
"Brain" object - which simulates DB Cursor thus result must have UID
contents_uid
=
search_result
.
uid
# every document indexed in catalog has to have relativeUrl
contents_relative_url
=
getRealRelativeUrl
(
search_result
)
...
...
@@ -133,7 +193,7 @@ def object_uids_and_accessors(search_result, result_index, traversed_document):
try
:
return
doc
.
hasProperty
(
attr
)
except
(
AttributeError
,
Unauthorized
)
as
e
:
context
.
log
(
'Cannot state ownership of property "{}" on {!s} because of "{!s}"'
.
format
(
log
(
'Cannot state ownership of property "{}" on {!s} because of "{!s}"'
.
format
(
attr
,
doc
,
e
))
return
False
elif
hasattr
(
search_result
,
"aq_self"
):
...
...
@@ -162,11 +222,12 @@ def object_uids_and_accessors(search_result, result_index, traversed_document):
return
contents_uid
,
contents_relative_url
,
search_property_getter
,
search_property_hasser
def
resolve_field
(
search_result
,
select
,
search_property_getter
,
search_property_hasser
):
"""Given `data_source` extract
fields defined in `field_list` and render them using `field_template_dict`
.
def
getAttrFromAnything
(
search_result
,
select
,
search_property_getter
,
search_property_hasser
,
kwargs
):
"""Given `data_source` extract
value named `select` using helper getter/hasser
.
:param data_source: any dict-like object (usually dict or Brain or Document)
:select: field name (can represent actual Properties or Scripts)
:select: field name (can represent actual attributes, Properties or even Scripts)
:kwargs: available arguments for possible callables hidden under `select`
"""
# if the variable does not have a field template we need to find its
...
...
@@ -175,7 +236,7 @@ def resolve_field(search_result, select, search_property_getter, search_property
contents_value
=
None
if
not
isinstance
(
select
,
(
str
,
unicode
))
or
len
(
select
)
==
0
:
context
.
log
(
'There is an invalid column name "{!s}"!'
.
format
(
select
),
level
=
200
)
log
(
'There is an invalid column name "{!s}"!'
.
format
(
select
),
level
=
200
)
return
None
if
"."
in
select
:
...
...
@@ -211,7 +272,7 @@ def resolve_field(search_result, select, search_property_getter, search_property
# do not call it here - it will be done later in generic call part
contents_value
=
getattr
(
search_result
,
accessor_name
)
except
(
AttributeError
,
KeyError
,
Unauthorized
)
as
error
:
context
.
log
(
"Could not evaluate {} nor {} on {} with error {!s}"
.
format
(
log
(
"Could not evaluate {} nor {} on {} with error {!s}"
.
format
(
select
,
accessor_name
,
search_result
,
error
),
level
=
100
)
# WARNING
if
contents_value
is
None
and
search_property_hasser
(
search_result
,
select
):
...
...
@@ -222,7 +283,7 @@ def resolve_field(search_result, select, search_property_getter, search_property
try
:
contents_value
=
getattr
(
search_result
,
select
,
None
)
except
(
Unauthorized
,
AttributeError
,
KeyError
)
as
error
:
context
.
log
(
"Cannot resolve {} on {!s} because {!s}"
.
format
(
log
(
"Cannot resolve {} on {!s} because {!s}"
.
format
(
select
,
raw_search_result
,
error
),
level
=
100
)
if
callable
(
contents_value
):
...
...
@@ -242,7 +303,7 @@ def resolve_field(search_result, select, search_property_getter, search_property
else
:
contents_value
=
contents_value
()
except
(
AttributeError
,
KeyError
,
Unauthorized
)
as
error
:
context
.
log
(
"Could not evaluate {} on {} with error {!s}"
.
format
(
log
(
"Could not evaluate {} on {} with error {!s}"
.
format
(
contents_value
,
search_result
,
error
),
level
=
100
)
# WARNING
# make resulting value JSON serializable
...
...
@@ -309,12 +370,15 @@ def getFormRelativeUrl(form):
)[
0
].
relative_url
def
getFieldDefault
(
traversed_document
,
field
,
key
,
value
=
None
):
# REQUEST.get(field.id, field.get_value("default"))
result
=
traversed_document
.
Field_getDefaultValue
(
field
,
key
,
value
,
REQUEST
)
if
getattr
(
result
,
'translate'
,
None
)
is
not
None
:
result
=
"%s"
%
result
return
result
def
getFieldDefault
(
form
,
field
,
key
,
value
=
None
):
"""Get available value for `field` preferably in python-object from REQUEST or from field's default."""
if
value
is
None
:
value
=
REQUEST
.
form
.
get
(
field
.
id
,
REQUEST
.
form
.
get
(
key
,
None
))
or
field
.
get_value
(
'default'
)
if
field
.
has_value
(
"unicode"
)
and
field
.
get_value
(
"unicode"
)
and
isinstance
(
value
,
'unicode'
):
value
=
unicode
(
value
,
self
.
get_form_encoding
())
if
getattr
(
value
,
'translate'
,
None
)
is
not
None
:
return
"%s"
%
value
return
value
def
renderField
(
traversed_document
,
field
,
form
,
value
=
None
,
meta_type
=
None
,
key
=
None
,
key_prefix
=
None
,
selection_params
=
None
):
...
...
@@ -328,6 +392,13 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
if
key
is
None
:
key
=
field
.
generate_field_key
(
key_prefix
=
key_prefix
)
if
meta_type
==
"ProxyField"
:
# resolve the base meta_type
meta_type
=
field
.
getRecursiveTemplateField
().
meta_type
# some TALES expressions are using Base_getRelatedObjectParameter which requires that
previous_request_field
=
REQUEST
.
other
.
pop
(
'field_id'
,
None
)
REQUEST
.
other
[
'field_id'
]
=
field
.
id
result
=
{
"type"
:
meta_type
,
"title"
:
Base_translateString
(
field
.
get_value
(
"title"
)),
...
...
@@ -342,15 +413,10 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
# fields have default value and can be required (unlike boxes)
result
.
update
({
"required"
:
field
.
get_value
(
"required"
)
if
field
.
has_value
(
"required"
)
else
None
,
"default"
:
getFieldDefault
(
traversed_document
,
field
,
result
[
"key"
]
,
value
),
"default"
:
getFieldDefault
(
form
,
field
,
key
,
value
),
})
if
meta_type
==
"ProxyField"
:
return
renderField
(
traversed_document
,
field
,
form
,
value
,
meta_type
=
field
.
getRecursiveTemplateField
().
meta_type
,
key
=
key
,
key_prefix
=
key_prefix
,
selection_params
=
selection_params
)
# start the actuall "switch" on field's meta_type here
if
meta_type
in
(
"ListField"
,
"RadioField"
,
"ParallelListField"
,
"MultiListField"
):
result
.
update
({
# XXX Message can not be converted to json as is
...
...
@@ -371,9 +437,8 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
"sub_select_key"
:
traversed_document
.
Field_getSubFieldKeyDict
(
field
,
'default:list'
,
key
=
result
[
"key"
]),
"sub_input_key"
:
"default_"
+
traversed_document
.
Field_getSubFieldKeyDict
(
field
,
'default:list:int'
,
key
=
result
[
"key"
])
})
return
result
if
meta_type
in
(
"StringField"
,
"FloatField"
,
"EmailField"
,
"TextAreaField"
,
el
if
meta_type
in
(
"StringField"
,
"FloatField"
,
"EmailField"
,
"TextAreaField"
,
"LinesField"
,
"ImageField"
,
"FileField"
,
"IntegerField"
,
"PasswordField"
,
"EditorField"
):
if
meta_type
==
"FloatField"
:
...
...
@@ -396,9 +461,8 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
if
v
))
if
parameters
:
result
[
"default"
]
=
'%s?%s'
%
(
result
[
"default"
],
parameters
)
return
result
if
meta_type
==
"DateTimeField"
:
el
if
meta_type
==
"DateTimeField"
:
result
.
update
({
"date_only"
:
field
.
get_value
(
"date_only"
),
"ampm_time_style"
:
field
.
get_value
(
"ampm_time_style"
),
...
...
@@ -407,7 +471,7 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
"hide_day"
:
field
.
get_value
(
'hide_day'
),
"hidden_day_is_last_day"
:
field
.
get_value
(
'hidden_day_is_last_day'
),
})
date_value
=
getFieldDefault
(
traversed_document
,
field
,
result
[
"key"
]
,
value
)
date_value
=
getFieldDefault
(
form
,
field
,
key
,
value
)
if
not
date_value
and
field
.
get_value
(
'default_now'
):
date_value
=
DateTime
()
if
same_type
(
date_value
,
DateTime
()):
...
...
@@ -418,9 +482,8 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
result
[
"default"
]
=
date_value
for
subkey
in
(
"year"
,
"month"
,
"day"
,
"hour"
,
"minute"
,
"ampm"
,
"timezone"
):
result
[
"subfield_%s_key"
%
subkey
]
=
traversed_document
.
Field_getSubFieldKeyDict
(
field
,
subkey
,
key
=
result
[
"key"
])
return
result
if
meta_type
in
(
"RelationStringField"
,
"MultiRelationStringField"
):
el
if
meta_type
in
(
"RelationStringField"
,
"MultiRelationStringField"
):
portal_type_list
=
field
.
get_value
(
'portal_type'
)
translated_portal_type
=
[]
jump_reference_list
=
[]
...
...
@@ -462,11 +525,16 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
for
(
listbox_path
,
listbox_name
)
in
listbox_ids
:
(
listbox_form_name
,
listbox_field_name
)
=
listbox_path
.
split
(
'/'
,
2
)
form
=
getattr
(
context
,
listbox_form_name
)
# do not override "global" `form`
rel_form
=
getattr
(
context
,
listbox_form_name
)
# find listbox field
listbox_form_field
=
filter
(
lambda
f
:
f
.
getId
()
==
listbox_field_name
,
form
.
get_fields
())[
0
]
listbox_form_field
=
filter
(
lambda
f
:
f
.
getId
()
==
listbox_field_name
,
rel_form
.
get_fields
())[
0
]
rel_cache
=
{
'form_id'
:
REQUEST
.
get
(
'form_id'
,
MARKER
),
'field_id'
:
REQUEST
.
get
(
'field_id'
,
MARKER
)}
REQUEST
.
set
(
'form_id'
,
rel_form
.
id
)
REQUEST
.
set
(
'field_id'
,
listbox_form_field
.
id
)
# get original definition
subfield
=
renderField
(
context
,
listbox_form_field
,
form
)
subfield
=
renderField
(
context
,
listbox_form_field
,
rel_
form
)
# overwrite, like Base_getRelatedObjectParameter does
if
subfield
[
"portal_type"
]
==
[]:
subfield
[
"portal_type"
]
=
field
.
get_value
(
'portal_type'
)
...
...
@@ -488,6 +556,11 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
subfield
[
"column_list"
].
append
((
tmp_column
[
0
],
Base_translateString
(
tmp_column
[
1
])))
listbox
[
Base_translateString
(
listbox_name
)]
=
subfield
for
key
in
rel_cache
:
if
rel_cache
[
key
]
is
not
MARKER
:
REQUEST
.
set
(
key
,
rel_cache
[
key
])
result
.
update
({
"url"
:
relative_url
,
"translated_portal_types"
:
translated_portal_type
,
...
...
@@ -508,21 +581,18 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
"relation_item_key"
:
traversed_document
.
Field_getSubFieldKeyDict
(
field
,
"item"
,
key
=
result
[
"key"
]),
"relation_item_relative_url"
:
[
jump_reference
.
getRelativeUrl
()
for
jump_reference
in
jump_reference_list
]
})
return
result
if
meta_type
in
(
"CheckBoxField"
,
"MultiCheckBoxField"
):
el
if
meta_type
in
(
"CheckBoxField"
,
"MultiCheckBoxField"
):
if
meta_type
==
"MultiCheckBoxField"
:
result
[
"items"
]
=
field
.
get_value
(
"items"
),
return
result
if
meta_type
==
"GadgetField"
:
el
if
meta_type
==
"GadgetField"
:
result
.
update
({
"url"
:
field
.
get_value
(
"gadget_url"
),
"sandbox"
:
field
.
get_value
(
"js_sandbox"
)
})
return
result
if
meta_type
==
"ListBox"
:
el
if
meta_type
==
"ListBox"
:
"""Display list of objects with optional search/sort capabilities on columns from catalog.
We might be inside a ReportBox which is inside a parent form BUT we still have access to
...
...
@@ -576,8 +646,8 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
# Search for non-editable documents - all reports goes here
# Reports have custom search scripts which wants parameters from the form
# thus we introspect such parameters and try to find them in REQUEST
list_method
=
None
list_method_name
=
traversed_document
.
Listbox_getListMethodName
(
field
)
list_method
=
field
.
get_value
(
'list_method'
)
or
None
list_method_name
=
list_method
.
getMethodName
()
if
list_method
is
not
None
else
""
if
list_method_name
not
in
(
""
,
"portal_catalog"
,
"searchFolder"
,
"objectValues"
):
# we avoid accessing known protected objects and builtin functions above
try
:
...
...
@@ -585,32 +655,15 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
except
(
Unauthorized
,
AttributeError
,
ValueError
)
as
error
:
# we are touching some specially protected (usually builtin) methods
# which we will not introspect
context
.
log
(
'ListBox {!s} list_method {} is unavailable because of "{!s}"'
.
format
(
log
(
'ListBox {!s} list_method {} is unavailable because of "{!s}"'
.
format
(
field
,
list_method_name
,
error
),
level
=
100
)
else
:
list_method
=
None
# Put all ListBox's search method params from REQUEST to `default_param_json`
# because old code expects synchronous render thus having all form's values
# still in the request which is not our case because we do asynchronous rendering
if
list_method
is
not
None
and
hasattr
(
list_method
,
"ZScriptHTML_tryParams"
):
for
list_method_param
in
list_method
.
ZScriptHTML_tryParams
():
if
list_method_param
in
REQUEST
and
list_method_param
not
in
list_method_query_dict
:
list_method_query_dict
[
list_method_param
]
=
REQUEST
.
get
(
list_method_param
)
# MIDDLE-DANGEROUS!
# In case of reports (later even exports) substitute None for unknown
# parameters. We suppose Python syntax for parameters!
# What we do here is literally putting every form field from REQUEST
# into search method parameters - this is later put back into REQUEST
# this way we can mimic synchronous rendering when all form field values
# were available in REQUEST. It is obviously wrong behaviour.
for
list_method_param
in
list_method
.
params
().
split
(
","
):
if
"*"
in
list_method_param
:
continue
if
"="
in
list_method_param
:
continue
# now we have only mandatory parameters
list_method_param
=
list_method_param
.
strip
()
if
list_method_param
not
in
list_method_query_dict
:
list_method_query_dict
[
list_method_param
]
=
None
if
list_method
is
not
None
:
kwargsForCallable
(
list_method
,
list_method_query_dict
,
REQUEST
)
# Now if the list_method does not specify **kwargs we need to remove
# unwanted parameters like "portal_type" which is everywhere
if
"**"
not
in
list_method
.
params
():
...
...
@@ -627,7 +680,7 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
"form_relative_url"
:
"%s/%s"
%
(
getFormRelativeUrl
(
form
),
field
.
id
),
"list_method"
:
list_method_name
,
"default_param_json"
:
urlsafe_b64encode
(
json
.
dumps
(
ensure
_s
erializable
(
list_method_query_dict
)))
json
.
dumps
(
ensure
S
erializable
(
list_method_query_dict
)))
}
# once we imprint `default_params` into query string of 'list method' we
# don't want them to propagate to the query as well
...
...
@@ -642,29 +695,10 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
"script_id"
:
script
.
id
,
"relative_url"
:
traversed_document
.
getRelativeUrl
().
replace
(
"/"
,
"%2F"
),
"list_method"
:
list_method_name
,
"default_param_json"
:
urlsafe_b64encode
(
json
.
dumps
(
ensure
_s
erializable
(
list_method_query_dict
)))
"default_param_json"
:
urlsafe_b64encode
(
json
.
dumps
(
ensure
S
erializable
(
list_method_query_dict
)))
}
list_method_query_dict
=
{}
# row_list = list_method(limit=lines, portal_type=portal_types,
# **default_params)
# line_list = []
# for row in row_list:
# document = row.getObject()
# line = {
# "url": url_template_dict["document_hal"] % {
# "root_url": site_root.absolute_url(),
# "relative_url": document.getRelativeUrl(),
# "script_id": script.id
# }
# }
# for property, title in columns:
# prop = document.getProperty(property)
# if same_type(prop, DateTime()):
# prop = "XXX Serialize DateTime"
# line[title] = prop
# line["_relative_url"] = document.getRelativeUrl()
# line_list.append(line)
result
.
update
({
"column_list"
:
column_list
,
"search_column_list"
:
search_column_list
,
...
...
@@ -674,7 +708,7 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
"show_anchor"
:
field
.
get_value
(
"anchor"
),
"portal_type"
:
portal_type_list
,
"lines"
:
field
.
get_value
(
'lines'
),
"default_params"
:
ensure
_s
erializable
(
default_params
),
"default_params"
:
ensure
S
erializable
(
default_params
),
"list_method"
:
list_method_name
,
"show_stat"
:
field
.
get_value
(
'stat_method'
)
!=
""
or
len
(
field
.
get_value
(
'stat_columns'
))
>
0
,
"show_count"
:
field
.
get_value
(
'count_method'
)
!=
""
,
...
...
@@ -686,9 +720,8 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
})
if
(
list_method_custom
is
not
None
):
result
[
"list_method_template"
]
=
list_method_custom
return
result
if
meta_type
==
"FormBox"
:
el
if
meta_type
==
"FormBox"
:
embedded_document
=
{
'_links'
:
{},
'_actions'
:
{},
...
...
@@ -711,9 +744,8 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
result
[
'_embedded'
]
=
{
'_view'
:
embedded_document
}
return
result
if
meta_type
==
"MatrixBox"
:
el
if
meta_type
==
"MatrixBox"
:
# data are generated by python code for MatrixBox.py
# template_fields are better rendered here because they can be part of "hidden"
# group which is not rendered in form by default. Including
...
...
@@ -726,10 +758,14 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
for
template_field
in
template_field_names
if
template_field
in
form
},
})
return
result
else
:
# All other fields are not implemented and we'll return only basic info about them
result
[
"_debug"
]
=
"Unknown field type "
+
meta_type
if
previous_request_field
is
not
None
:
REQUEST
.
other
[
'field_id'
]
=
previous_request_field
return
result
...
...
@@ -740,7 +776,12 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
we mitigate the functionality here by overriding ListBox's own values
for columns, editable columns, and sort with those found in `selection_params`
"""
previous_request_other
=
{
'form_id'
:
REQUEST
.
other
.
pop
(
'form_id'
,
None
),
'here'
:
REQUEST
.
other
.
pop
(
'here'
,
None
)
}
REQUEST
.
set
(
'here'
,
traversed_document
)
REQUEST
.
set
(
'form_id'
,
form
.
id
)
field_errors
=
REQUEST
.
get
(
'field_errors'
,
{})
#hardcoded
...
...
@@ -860,6 +901,7 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
if
report_item
.
selection_name
:
selection_name
=
report_prefix
+
"_"
+
report_item
.
selection_name
context
.
log
(
'Report {} defines selection_name {}'
.
format
(
report_title
,
selection_name
))
report_form_params
.
update
(
selection_name
=
selection_name
)
# this should load selections with correct values - since it is modifying
# global state in the backend we have nothing more to do here
...
...
@@ -892,6 +934,11 @@ def renderForm(traversed_document, form, response_dict, key_prefix=None, selecti
report_result
[
'title'
]
=
report_title
report_result_list
.
append
(
report_result
)
response_dict
[
'report_section_list'
]
=
report_result_list
# end-if report_section
for
key
,
value
in
previous_request_other
.
items
():
if
value
is
not
None
:
REQUEST
.
set
(
key
,
value
)
# XXX form action update, etc
def
renderRawField
(
field
):
...
...
@@ -1329,7 +1376,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
#
# Possible call arguments example:
# form_relative_url: portal_skins/erp5_web/WebSite_view/listbox
# list_method:
objectValues
(Script providing items)
# list_method:
"objectValues"
(Script providing items)
# default_param_json: <base64 encoded JSON> (Additional search params)
# query: <str> (term for fulltext search)
# select_list: ['int_index', 'id', 'title', ...] (column names to select)
...
...
@@ -1351,6 +1398,9 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
response
.
setStatus
(
405
)
return
""
# set 'here' for field rendering which contain TALES expressions
REQUEST
.
set
(
'here'
,
traversed_document
)
# in case we have custom list method
catalog_kw
=
{}
...
...
@@ -1374,7 +1424,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
}
if
default_param_json
is
not
None
:
catalog_kw
.
update
(
ensure
_d
eserialized
(
ensure
D
eserialized
(
byteify
(
json
.
loads
(
urlsafe_b64decode
(
default_param_json
)))))
if
query
:
...
...
@@ -1392,7 +1442,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
sort_order
=
"DESC"
else
:
# should raise an ValueError instead
context
.
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
return
(
sort_col
,
sort_order
)
...
...
@@ -1414,7 +1464,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
#
# for k, v in catalog_kw.items():
# REQUEST.set(k, v)
context
.
log
(
'list_method >>> {}({!s})'
.
format
(
list_method
,
catalog_kw
))
search_result_iterable
=
callable_list_method
(
**
catalog_kw
)
# Cast to list if only one element is provided
...
...
@@ -1438,6 +1488,10 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
listbox_field_id
=
source_field
.
id
# XXX Proxy field are not correctly handled in traversed_document of web site
listbox_form
=
getattr
(
traversed_document
,
source_field
.
aq_parent
.
id
)
# field TALES expression evaluated by Base_getRelatedObjectParameter requires that
REQUEST
.
other
[
'form_id'
]
=
listbox_form
.
id
for
select
in
select_list
:
# See Listbox.py getValueList --> getEditableField & getColumnAliasList method
# In short: there are Form Field definitions which names start with
...
...
@@ -1473,13 +1527,15 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
if
result_index
>=
start
+
num_items
:
break
# we can render fields which need 'here' to be set to currently rendered document
#REQUEST.set('here', search_result)
contents_item
=
{}
contents_uid
,
contents_relative_url
,
property_getter
,
property_hasser
=
\
object_uids_and_accessors
(
search_result
,
result_index
,
traversed_document
)
anythingUidAndAccessor
(
search_result
,
result_index
,
traversed_document
)
# this dict will hold all resolved values
contents_item
=
{
'_links'
:
{
# _links.self.href is mandatory for JIO so it can create reference to items alone
# _links.self.href is mandatory for JIO so it can create reference to the
# (listbox) item alone
contents_item
[
'_links'
]
=
{
'self'
:
{
"href"
:
default_document_uri_template
%
{
"root_url"
:
site_root
.
absolute_url
(),
...
...
@@ -1488,7 +1544,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
},
},
}
}
# ERP5 stores&send the list of editable elements in a hidden field called
# only database results can be editable so it belongs here
if
editable_field_dict
and
listbox_field_id
:
...
...
@@ -1505,10 +1561,17 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
REQUEST
.
set
(
'cell'
,
search_result
)
# if default value is given by evaluating Tales expression then we only
# put "cell" to request (expected by tales) and let the field evaluate
default_field_value
=
None
if
getattr
(
editable_field_dict
[
select
].
tales
,
"default"
,
""
)
==
""
:
# if there is no tales expr (or is empty) we extract the value from search result
default_field_value
=
getProtectedProperty
(
search_result
,
select
)
default_field_value
=
getAttrFromAnything
(
search_result
,
select
,
property_getter
,
property_hasser
,
{})
context
.
log
(
'renderField!for"{}"({!s}, field={!s}, form={!s}, value={!s}, key={}'
.
format
(
select
,
traversed_document
,
editable_field_dict
[
select
],
listbox_form
,
default_field_value
,
'field_%s_%s'
%
(
editable_field_dict
[
select
].
id
,
contents_uid
)))
contents_item
[
select
]
=
renderField
(
traversed_document
,
...
...
@@ -1522,13 +1585,11 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
# most of the complicated magic happens here - we need to resolve field names
# given search_result. This name can unfortunately mean almost anything from
# a key name to Python Script with variable number of input parameters.
contents_item
[
select
]
=
resolve_field
(
search_result
,
select
,
property_getter
,
property_hasser
)
contents_item
[
select
]
=
getAttrFromAnything
(
search_result
,
select
,
property_getter
,
property_hasser
,
{
'brain'
:
search_result
}
)
# endfor select
contents_list
.
append
(
contents_item
)
result_dict
[
'_embedded'
].
update
({
'contents'
:
contents_list
})
result_dict
[
'_embedded'
][
'contents'
]
=
ensureSerializable
(
contents_list
)
# Compute statistics if the search issuer was ListBox
# or in future if the stats (SUM) are required by JIO call
...
...
@@ -1536,8 +1597,6 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
if
source_field_meta_type
==
"ProxyField"
:
source_field_meta_type
=
source_field
.
getRecursiveTemplateField
().
meta_type
context
.
log
(
'source_field "{!s}", source_field_meta_type {!s}'
.
format
(
source_field
,
source_field_meta_type
))
if
source_field
is
not
None
and
source_field_meta_type
==
"ListBox"
:
contents_stat_list
=
[]
# in case the search was issued by listbox we can provide results of
...
...
@@ -1546,37 +1605,34 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
stat_method
=
source_field
.
get_value
(
'stat_method'
)
stat_columns
=
source_field
.
get_value
(
'stat_columns'
)
context
.
log
(
'stat_method "{!s}", stat_columns {!s}'
.
format
(
stat_method
,
stat_columns
))
# Selection is unfortunatelly required fot stat methods
# support only selection_name for stat methods because any `selection` is deprecated
# and should be removed
selection_name
=
source_field
.
get_value
(
'selection_name'
)
selection
=
None
if
selection_name
:
selection_tool
=
context
.
getPortalObject
().
portal_selections
selection
=
selection_tool
.
getSelectionFor
(
selection_name
,
REQUEST
)
if
selection_name
and
'selection_name'
not
in
catalog_kw
:
catalog_kw
[
'selection_name'
]
=
selection_name
context
.
log
(
'stat_method will receive selection_name "{}"'
.
format
(
catalog_kw
[
'selection_name'
]))
contents_stat
=
{}
if
len
(
stat_columns
)
>
0
:
# prefer stat per columns as it is in ListBox
# always called on current context
# prefer stat per column (follow original ListBox.py implementation)
for
stat_name
,
stat_script
in
stat_columns
:
contents_stat
[
stat_name
]
=
getattr
(
traversed_document
,
stat_script
)(
selection
=
selection
,
selection_name
=
selection_name
,
**
catalog_kw
)
contents_stat
[
stat_name
]
=
getattr
(
traversed_document
,
stat_script
)(
**
catalog_kw
)
contents_stat_list
.
append
(
contents_stat
)
elif
stat_method
!=
""
and
stat_method
.
getMethodName
()
!=
list_method
:
# general stat_method is second - should return dictionary or list of dictionaries
# where all "fields" should be accessible by their "select" name
contents_stat_list
=
getattr
(
traversed_document
,
stat_method
.
getMethodName
())(
**
catalog_kw
)
# general stat_method is second in priority list - should return dictionary or list of dictionaries
# where all "fields" should be accessible by their "select" name (no "listbox_" prefix)
stat_method_result
=
getattr
(
traversed_document
,
stat_method
.
getMethodName
())(
**
catalog_kw
)
contents_stat_list
=
toBasicTypes
(
stat_method_result
)
or
[]
for
contents_stat
in
contents_stat_list
:
for
key
,
value
in
contents_stat
.
items
():
if
key
in
editable_field_dict
:
contents_stat
[
key
]
=
renderField
(
traversed_document
,
editable_field_dict
[
key
],
listbox_form
,
value
,
key
=
editable_field_dict
[
key
].
id
+
'__sum'
)
context
.
log
(
'contents_stat_list {!s}'
.
format
(
contents_stat_list
))
for
contents_stat
in
contents_stat_list
:
for
key
,
value
in
contents_stat
.
items
():
context
.
log
(
'contents_stat["{}"] = type {!s}, value {!s}'
.
format
(
key
,
type
(
value
),
value
))
if
len
(
contents_stat_list
)
>
0
:
result_dict
[
'_embedded'
].
update
({
'sum'
:
contents_stat_list
})
result_dict
[
'_embedded'
][
'sum'
]
=
ensureSerializable
(
contents_stat_list
)
# We should cleanup the selection if it exists in catalog params BUT
# we cannot because it requires escalated Permission.'modifyPortal' so
...
...
@@ -1683,6 +1739,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
return result_dict
response.setHeader('
Content
-
Type
', mime_type)
context.log('
calculateHateoas
(
traversed_document
=
{
!
s
},
mode
=
"{}"'.format(temp_traversed_document, mode))
hateoas = calculateHateoas(is_portal=temp_is_portal, is_site_root=temp_is_site_root,
traversed_document=temp_traversed_document,
relative_url=relative_url,
...
...
@@ -1692,7 +1749,22 @@ hateoas = calculateHateoas(is_portal=temp_is_portal, is_site_root=temp_is_site_r
default_param_json=default_param_json,
form_relative_url=form_relative_url)
def deepInspection(obj, prefix):
if isinstance(obj, dict):
for key, value in obj.items():
if type(key) != str:
log('
{}
key
"{!s}"
:
type
{
!
s
}
'.format(prefix, key, type(key)))
deepInspection(value, prefix + '
.
' + str(key))
elif isinstance(obj, (tuple, list)):
for value in obj:
deepInspection(value, prefix)
elif obj is None or isinstance(obj, (str, int)):
return
else:
log('
{}
value
"{!s}"
,
type
{
!
s
}
'.format(prefix, obj, type(obj)))
if hateoas == "":
return hateoas
else:
# deepInspection(hateoas, '')
return json.dumps(hateoas, indent=2)
bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_jio_js.js
View file @
d2f8c300
...
...
@@ -178,31 +178,33 @@
]
)
.
push
(
function
(
catalog_json
)
{
var
data
=
catalog_json
.
_embedded
.
contents
,
summary
=
catalog_json
.
_embedded
.
sum
,
count
=
catalog_json
.
_embedded
.
count
,
length
=
data
.
length
,
k
,
uri
,
item
,
result
=
[];
for
(
k
=
0
;
k
<
length
;
k
+=
1
)
{
item
=
data
[
k
];
uri
=
new
URI
(
item
.
_links
.
self
.
href
);
var
data
=
catalog_json
.
_embedded
.
contents
||
[],
summary
=
catalog_json
.
_embedded
.
sum
||
[],
count
=
catalog_json
.
_embedded
.
count
;
return
{
"
data
"
:
{
"
rows
"
:
data
.
map
(
function
(
item
)
{
var
uri
=
new
URI
(
item
.
_links
.
self
.
href
);
delete
item
.
_links
;
result
.
push
({
id
:
uri
.
segment
(
2
),
doc
:
{},
value
:
item
});
}
return
{
data
:
{
rows
:
result
,
total_rows
:
result
.
length
"
id
"
:
uri
.
segment
(
2
),
"
doc
"
:
{},
"
value
"
:
item
};
}),
"
total_rows
"
:
data
.
length
},
"
sum
"
:
{
"
rows
"
:
summary
.
map
(
function
(
item
,
index
)
{
return
{
"
id
"
:
'
/#summary
'
+
index
,
// this is obviously wrong. @Romain help please!
"
doc
"
:
{},
"
value
"
:
item
};
}),
"
total_rows
"
:
summary
.
length
},
sum
:
summary
,
count
:
count
"
count
"
:
count
};
});
})
...
...
bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_jio_js.xml
View file @
d2f8c300
...
...
@@ -230,7 +230,7 @@
</item>
<item>
<key>
<string>
serial
</string>
</key>
<value>
<string>
963.5
0499.50100.12458
</string>
</value>
<value>
<string>
963.5
9331.40212.55432
</string>
</value>
</item>
<item>
<key>
<string>
state
</string>
</key>
...
...
@@ -248,7 +248,7 @@
</tuple>
<state>
<tuple>
<float>
151
1939345.58
</float>
<float>
151
2454358.33
</float>
<string>
UTC
</string>
</tuple>
</state>
...
...
bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_listbox_html.html
View file @
d2f8c300
...
...
@@ -63,7 +63,7 @@
<
/a
>
{{
/
if
}}
{{
else
}}
<
a
href
=
"
{{href}}
"
class
=
"
ui-link
"
>
{{
tex
t
}}
<
/a
>
<
a
href
=
"
{{href}}
"
class
=
"
ui-link
"
>
{{
defaul
t
}}
<
/a
>
{{
/
if
}}
<
/td
>
{{
/
each
}}
...
...
@@ -124,7 +124,7 @@
{{
#
each
row_list
}}
<
tr
>
{{
#
if
..
/
show_anchor
}}
<
td
><
/td
>
<
td
>
Total
<
/td
>
{{
/
if
}}
{{
#
each
cell_list
}}
<
td
>
...
...
@@ -142,13 +142,9 @@
</script>
<script
id=
"listbox-nav-template"
type=
"text/x-handlebars-template"
>
<
nav
class
=
"
ui-bar-inherit ui-controlgroup ui-controlgroup-horizontal ui-corner-all ui-paging-menu
"
>
<
div
class
=
"
ui-controlgroup-controls
"
>
<
a
class
=
"
{{previous_classname}}
"
data
-
i18n
=
"
Previous
"
href
=
"
{{previous_url}}
"
>
Previous
<
/a
>
<
a
class
=
"
{{next_classname}}
"
data
-
i18n
=
"
Next
"
href
=
"
{{next_url}}
"
>
Next
<
/a
>
<
span
class
=
"
ui-btn ui-disabled
"
data
-
i18n
=
"
{{record}}
"
>
{{
record
}}
<
/span
>
<
/div
>
<
/nav
>
<
span
class
=
"
ui-disabled ui-right
"
data
-
i18n
=
"
{{record}}
"
>
{{
record
}}
<
/span
>
</script>
<script
id=
"listbox-template"
type=
"text/x-handlebars-template"
>
...
...
bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_listbox_html.xml
View file @
d2f8c300
...
...
@@ -234,7 +234,7 @@
</item>
<item>
<key>
<string>
serial
</string>
</key>
<value>
<string>
963.5
0750.47688.32426
</string>
</value>
<value>
<string>
963.5
2015.15055.51592
</string>
</value>
</item>
<item>
<key>
<string>
state
</string>
</key>
...
...
@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>
151
1952124.83
</float>
<float>
151
2039069.26
</float>
<string>
UTC
</string>
</tuple>
</state>
...
...
bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_listbox_js.js
View file @
d2f8c300
...
...
@@ -42,20 +42,15 @@
loading_class_list
=
[
'
ui-icon-spinner
'
,
'
ui-btn-icon-left
'
],
disabled_class
=
'
ui-disabled
'
;
function
renderEditableField
(
gadget
,
element
,
column_list
,
field_table
)
{
var
i
,
promise_list
=
[],
uid_value_dict
=
{},
uid_value
,
column
,
line
,
element_list
=
element
.
querySelectorAll
(
"
.editable_div
"
);
gadget
.
props
.
listbox_uid_dict
=
{};
gadget
.
props
.
cell_gadget_list
=
[];
function
renderSubCell
(
element
,
sub_field_json
)
{
sub_field_json
.
editable
=
sub_field_json
.
editable
&&
gadget
.
state
.
editable
;
// XXX
return
gadget
.
declareGadget
(
'
gadget_erp5_label_field.html
'
,
{
element
:
element
,
scope
:
sub_field_json
.
key
})
function
renderSubField
(
gadget
,
element
,
sub_field_json
)
{
sub_field_json
.
editable
=
sub_field_json
.
editable
&&
gadget
.
state
.
editable
;
return
gadget
.
declareGadget
(
'
gadget_erp5_label_field.html
'
,
{
element
:
element
,
scope
:
sub_field_json
.
key
}
)
.
push
(
function
(
cell_gadget
)
{
gadget
.
props
.
cell_gadget_list
.
push
(
cell_gadget
);
return
cell_gadget
.
render
({
...
...
@@ -65,23 +60,23 @@
});
});
}
function
renderEditableField
(
gadget
,
element
,
column_list
,
field_table
)
{
var
i
,
promise_list
=
[],
column
,
line
,
element_list
=
element
.
querySelectorAll
(
"
.editable_div
"
);
for
(
i
=
0
;
i
<
element_list
.
length
;
i
+=
1
)
{
column
=
element_list
[
i
].
getAttribute
(
"
data-column
"
);
line
=
element_list
[
i
].
getAttribute
(
"
data-line
"
);
if
(
gadget
.
props
.
listbox_uid_dict
.
key
===
undefined
)
{
gadget
.
props
.
listbox_uid_dict
.
key
=
gadget
.
state
.
allDocs_result
.
data
.
rows
[
line
].
value
[
"
listbox_uid:list
"
].
key
;
gadget
.
props
.
listbox_uid_dict
.
value
=
[
gadget
.
state
.
allDocs_result
.
data
.
rows
[
line
].
value
[
"
listbox_uid:list
"
].
value
];
uid_value_dict
[
gadget
.
state
.
allDocs_result
.
data
.
rows
[
line
].
value
[
"
listbox_uid:list
"
].
value
]
=
null
;
}
else
{
uid_value
=
gadget
.
state
.
allDocs_result
.
data
.
rows
[
line
].
value
[
"
listbox_uid:list
"
].
value
;
if
(
!
uid_value_dict
.
hasOwnProperty
(
uid_value
))
{
uid_value_dict
[
uid_value
]
=
null
;
gadget
.
props
.
listbox_uid_dict
.
value
.
push
(
uid_value
);
}
}
promise_list
.
push
(
renderSubCell
(
promise_list
.
push
(
renderSubField
(
gadget
,
element_list
[
i
],
field_table
[
line
].
cell_list
[
column
]
||
""
));
field_table
[
line
].
cell_list
[
column
]
||
""
));
}
return
RSVP
.
all
(
promise_list
);
}
...
...
@@ -104,9 +99,9 @@
"
column_list
"
:
column_list
}
))
.
push
(
function
(
my
_html
)
{
.
push
(
function
(
table_part
_html
)
{
container
=
document
.
createElement
(
container_name
);
container
.
innerHTML
=
my
_html
;
container
.
innerHTML
=
table_part
_html
;
return
renderEditableField
(
gadget
,
container
,
column_list
,
row_list
);
})
.
push
(
function
()
{
...
...
@@ -122,8 +117,8 @@
}
function
renderListboxTfoot
(
gadget
,
nav
,
foot
_sum
)
{
return
renderTablePart
(
gadget
,
listbox_tfoot_template
,
foot
_sum
,
"
tfoot
"
)
function
renderListboxTfoot
(
gadget
,
nav
,
foot
)
{
return
renderTablePart
(
gadget
,
listbox_tfoot_template
,
foot
,
"
tfoot
"
)
.
push
(
function
()
{
return
gadget
.
translateHtml
(
listbox_nav_template
(
{
...
...
@@ -153,7 +148,10 @@
// Init local properties
.
ready
(
function
()
{
this
.
props
=
{
// holds references to all editable sub-fields
cell_gadget_list
:
[],
// ERP5 needs listbox_uid:list with UIDs of editable sub-documents
// so it can search for them in REQUEST.form under <field.id>_<sub-document.uid>
listbox_uid_dict
:
{}
};
})
...
...
@@ -194,7 +192,7 @@
}
/** Check whether item is in outer-scoped field_json.column_list */
function
is
_in_column_l
ist
(
item
)
{
function
is
InColumnL
ist
(
item
)
{
for
(
i
=
0
;
i
<
field_json
.
column_list
.
length
;
i
+=
1
)
{
if
(
field_json
.
column_list
[
i
][
0
]
===
item
[
0
]
&&
field_json
.
column_list
[
i
][
1
]
===
item
[
1
])
{
return
true
;
...
...
@@ -205,12 +203,12 @@
// use only visible columns for sort
if
(
field_json
.
sort_column_list
.
length
)
{
sort_column_list
=
field_json
.
sort_column_list
.
filter
(
is
_in_column_l
ist
);
sort_column_list
=
field_json
.
sort_column_list
.
filter
(
is
InColumnL
ist
);
}
// use only visible columns for search
if
(
field_json
.
search_column_list
.
length
)
{
search_column_list
=
field_json
.
search_column_list
.
filter
(
is
_in_column_l
ist
);
search_column_list
=
field_json
.
search_column_list
.
filter
(
is
InColumnL
ist
);
}
search_column_list
.
push
([
"
searchable_text
"
,
"
Searchable Text
"
]);
...
...
@@ -458,7 +456,7 @@
counter
=
Math
.
min
(
allDocs_result
.
data
.
total_rows
,
lines
);
}
sort_list
=
JSON
.
parse
(
gadget
.
state
.
sort_list_json
);
// Every line points to a sub-document so we need those links
for
(
i
=
0
;
i
<
counter
;
i
+=
1
)
{
promise_list
.
push
(
gadget
.
getUrlFor
({
...
...
@@ -479,33 +477,51 @@
return
RSVP
.
all
(
promise_list
);
})
.
push
(
function
(
result
_list
)
{
.
push
(
function
(
line_link
_list
)
{
var
row_list
=
[],
value
,
cell_list
,
tmp_url
,
listbox_tbody_template
;
// reset list of UIDs of editable sub-documents
gadget
.
props
.
listbox_uid_dict
=
{
key
:
undefined
,
value
:
[]
};
// clear list of previous sub-gadgets
gadget
.
props
.
cell_gadget_list
=
[];
for
(
i
=
0
;
i
<
counter
;
i
+=
1
)
{
tmp_url
=
result_list
[
i
];
cell_list
=
[];
for
(
j
=
0
;
j
<
column_list
.
length
;
j
+=
1
)
{
value
=
allDocs_result
.
data
.
rows
[
i
].
value
[
column_list
[
j
][
0
]]
||
""
;
// value can be simple string with value in case of non-editable field
// thus we construct basic "field_json" manually and insert the value in "default"
if
(
typeof
value
===
"
string
"
)
{
value
=
{
'
editable
'
:
0
,
'
default
'
:
value
};
}
value
.
href
=
tmp_url
;
value
.
href
=
line_link_list
[
i
]
;
value
.
editable
=
value
.
editable
&&
gadget
.
state
.
editable
;
value
.
line
=
i
;
value
.
column
=
j
;
cell_list
.
push
(
value
);
}
// note row's editable UID into gadget.props.listbox_uid_dict if exists to send it back to ERP5
// together with ListBox data. The listbox_uid_dict has quite surprising structure {key: <key>, value: <uid-array>}
if
(
allDocs_result
.
data
.
rows
[
i
].
value
[
'
listbox_uid:list
'
]
!==
undefined
)
{
gadget
.
props
.
listbox_uid_dict
.
key
=
allDocs_result
.
data
.
rows
[
i
].
value
[
'
listbox_uid:list
'
].
key
;
gadget
.
props
.
listbox_uid_dict
.
value
.
push
(
allDocs_result
.
data
.
rows
[
i
].
value
[
'
listbox_uid:list
'
].
value
);
// we could come up with better name than "value" for almost everything ^^
}
else
{
// if the document does not have listbox_uid:list then no gadget should be editable
cell_list
.
forEach
(
function
(
cell
)
{
cell
.
editable
=
false
;
});
}
row_list
.
push
({
"
value
"
:
allDocs_result
.
data
.
rows
[
i
].
value
.
uid
,
"
jump
"
:
tmp_url
,
"
uid
"
:
allDocs_result
.
data
.
rows
[
i
].
value
.
uid
,
"
jump
"
:
line_link_list
[
i
]
,
"
cell_list
"
:
cell_list
,
"
line_icon
"
:
gadget
.
state
.
line_icon
});
...
...
@@ -517,7 +533,7 @@
listbox_tbody_template
=
listbox_hidden_tbody_template
;
}
return
renderTablePart
(
gadget
,
listbox_tbody_template
,
row_list
,
"
tbody
"
,
"
tbody
"
);
return
renderTablePart
(
gadget
,
listbox_tbody_template
,
row_list
,
"
tbody
"
);
})
.
push
(
function
()
{
var
prev_param
=
{},
...
...
@@ -541,48 +557,50 @@
})
.
push
(
function
(
url_list
)
{
var
summary
=
gadget
.
state
.
allDocs_result
.
sum
||
[],
// render summary footer if available
tfoot_sum
=
summary
.
map
(
function
(
row
,
row_index
)
{
var
result_sum
=
(
gadget
.
state
.
allDocs_result
.
sum
||
{}).
rows
||
[],
// render summary footer if available
summary
=
result_sum
.
map
(
function
(
row
,
row_index
)
{
var
row_editability
=
row
[
'
listbox_uid:list
'
]
!==
undefined
;
return
{
"
value
"
:
'
summary
'
+
row_index
,
"
uid
"
:
'
summary
'
+
row_index
,
"
cell_list
"
:
column_list
.
map
(
function
(
col_name
,
col_index
)
{
var
field_json
=
row
[
col_name
[
0
]]
||
""
;
if
(
typeof
field_json
==
"
string
"
)
{
var
field_json
=
row
.
value
[
col_name
[
0
]]
||
""
;
if
(
typeof
field_json
==
=
"
string
"
)
{
field_json
=
{
'
default
'
:
'
value
'
,
'
editable
'
:
0
};
}
field_json
.
editable
=
field_json
.
editable
&&
row_editability
;
field_json
.
column
=
col_index
;
field_json
.
line
=
row_index
;
return
field_json
;
})
};
}),
tfoot_count
=
{
navigation
=
{
"
previous_url
"
:
url_list
[
0
],
"
next_url
"
:
url_list
[
1
],
"
previous_classname
"
:
"
ui-btn ui-icon-carat-l ui-btn-icon-left responsive ui-first-child
"
,
"
next_classname
"
:
"
ui-btn ui-icon-carat-r ui-btn-icon-right responsive ui-last-child
"
};
tfoot_count
.
colspan
=
column_list
.
length
+
gadget
.
state
.
show_anchor
+
navigation
.
colspan
=
column_list
.
length
+
gadget
.
state
.
show_anchor
+
(
gadget
.
state
.
line_icon
?
1
:
0
);
if
((
gadget
.
state
.
begin_from
===
0
)
&&
(
counter
===
0
))
{
tfoot_count
.
record
=
variable
.
translated_no_record
;
navigation
.
record
=
variable
.
translated_no_record
;
}
else
if
((
allDocs_result
.
data
.
rows
.
length
<=
lines
)
&&
(
gadget
.
state
.
begin_from
===
0
))
{
tfoot_count
.
record
=
counter
+
"
"
+
variable
.
translated_records
;
navigation
.
record
=
counter
+
"
"
+
variable
.
translated_records
;
}
else
{
tfoot_count
.
record
=
variable
.
translated_records
+
"
"
+
(((
gadget
.
state
.
begin_from
+
lines
)
/
lines
-
1
)
*
lines
+
1
)
+
"
-
"
+
(((
gadget
.
state
.
begin_from
+
lines
)
/
lines
-
1
)
*
lines
+
counter
);
navigation
.
record
=
variable
.
translated_records
+
"
"
+
(((
gadget
.
state
.
begin_from
+
lines
)
/
lines
-
1
)
*
lines
+
1
)
+
"
-
"
+
(((
gadget
.
state
.
begin_from
+
lines
)
/
lines
-
1
)
*
lines
+
counter
);
}
if
(
gadget
.
state
.
begin_from
===
0
)
{
tfoot_count
.
previous_classname
+=
"
ui-disabled
"
;
navigation
.
previous_classname
+=
"
ui-disabled
"
;
}
if
(
allDocs_result
.
data
.
rows
.
length
<=
lines
)
{
tfoot_count
.
next_classname
+=
"
ui-disabled
"
;
navigation
.
next_classname
+=
"
ui-disabled
"
;
}
return
renderListboxTfoot
(
gadget
,
tfoot_count
,
tfoot_sum
);
return
renderListboxTfoot
(
gadget
,
navigation
,
summary
);
})
.
push
(
function
(
my_html
)
{
.
push
(
function
()
{
var
loading_element_classList
=
gadget
.
element
.
querySelector
(
"
.listboxloader
"
).
classList
;
loading_element_classList
.
remove
.
apply
(
loading_element_classList
,
loading_class_list
);
});
...
...
@@ -662,7 +680,7 @@
},
function
(
error
)
{
// do not crash interface if allDocs fails
//this will catch all error, not only search criteria invalid error
//
this will catch all error, not only search criteria invalid error
if
(
error
instanceof
RSVP
.
CancellationError
)
{
throw
error
;
}
...
...
@@ -674,10 +692,10 @@
})
.
declareMethod
(
"
getContent
"
,
function
(
options
)
{
var
form_
gadget
=
this
,
var
gadget
=
this
,
k
,
field
_gadget
,
count
=
form_
gadget
.
props
.
cell_gadget_list
.
length
,
sub
_gadget
,
count
=
gadget
.
props
.
cell_gadget_list
.
length
,
data
=
{},
queue
=
new
RSVP
.
Queue
();
...
...
@@ -691,17 +709,18 @@
}
for
(
k
=
0
;
k
<
count
;
k
+=
1
)
{
field_gadget
=
form_
gadget
.
props
.
cell_gadget_list
[
k
];
sub_gadget
=
gadget
.
props
.
cell_gadget_list
[
k
];
// XXX Hack until better defined
if
(
field
_gadget
.
getContent
!==
undefined
)
{
if
(
sub
_gadget
.
getContent
!==
undefined
)
{
queue
.
push
(
field_gadget
.
getContent
.
bind
(
field
_gadget
,
options
))
.
push
(
sub_gadget
.
getContent
.
bind
(
sub
_gadget
,
options
))
.
push
(
extendData
);
}
}
return
queue
.
push
(
function
()
{
data
[
form_gadget
.
props
.
listbox_uid_dict
.
key
]
=
form_gadget
.
props
.
listbox_uid_dict
.
value
;
// gadget.props.listbox_uid_dict.value is an array of UIDs of all editable documents
data
[
gadget
.
props
.
listbox_uid_dict
.
key
]
=
gadget
.
props
.
listbox_uid_dict
.
value
;
return
data
;
});
})
...
...
bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_listbox_js.xml
View file @
d2f8c300
...
...
@@ -236,7 +236,7 @@
</item>
<item>
<key>
<string>
serial
</string>
</key>
<value>
<string>
963.
50757.35572.58794
</string>
</value>
<value>
<string>
963.
60288.35957.62805
</string>
</value>
</item>
<item>
<key>
<string>
state
</string>
</key>
...
...
@@ -254,7 +254,7 @@
</tuple>
<state>
<tuple>
<float>
151
1952430.52
</float>
<float>
151
2439237.91
</float>
<string>
UTC
</string>
</tuple>
</state>
...
...
bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_nojqm_css.css
View file @
d2f8c300
...
...
@@ -1221,6 +1221,9 @@ div[data-gadget-scope='erp5_searchfield'] .ui-input-text div[data-gadget-scope='
div
[
data-gadget-scope
=
'erp5_searchfield'
]
button
{
padding
:
3pt
;
}
.document_table
{
/* end-of table */
}
.document_table
.ui-table-header
{
display
:
flex
;
padding-bottom
:
6pt
;
...
...
@@ -1264,24 +1267,30 @@ div[data-gadget-scope='erp5_searchfield'] button {
.document_table
table
{
width
:
100%
;
text-align
:
left
;
/* end-of tbody, tfoot*/
}
.document_table
table
th
,
.document_table
table
td
{
vertical-align
:
middle
;
padding
:
3pt
;
}
.document_table
table
thead
{
.document_table
table
thead
,
.document_table
table
tfoot
{
background-color
:
#0E81C2
;
color
:
#FFFFFF
;
}
.document_table
table
thead
a
{
.document_table
table
thead
a
,
.document_table
table
tfoot
a
{
color
:
#FFFFFF
;
text-decoration
:
underline
;
}
.document_table
table
thead
tr
th
{
.document_table
table
thead
tr
th
,
.document_table
table
tfoot
tr
th
{
padding
:
6pt
3pt
;
}
@media
not
screen
and
(
min-width
:
45em
)
{
.document_table
table
thead
{
.document_table
table
thead
,
.document_table
table
tfoot
{
display
:
none
;
}
}
...
...
@@ -1301,7 +1310,6 @@ div[data-gadget-scope='erp5_searchfield'] button {
@media
not
screen
and
(
max-width
:
85em
),
only
screen
and
(
min-width
:
45em
)
and
(
max-width
:
85em
)
{
.document_table
table
tbody
a
{
display
:
block
;
padding
:
3pt
;
}
}
@media
not
screen
and
(
min-width
:
45em
)
{
...
...
@@ -1370,41 +1378,42 @@ div[data-gadget-scope='erp5_searchfield'] button {
content
:
" ~ "
;
}
}
.document_table
table
tfoot
.ui-controlgroup-controls
{
.document_table
nav
{
display
:
flex
;
padding-top
:
6pt
;
border-top
:
2px
solid
rgba
(
0
,
0
,
0
,
0.14902
);
}
.document_table
table
tfoot
.ui-controlgroup-controls
span
{
.document_table
nav
span
{
opacity
:
.3
;
flex
:
2
;
text-align
:
right
;
float
:
right
;
}
.document_table
table
tfoot
.ui-controlgroup-controls
a
{
.document_table
nav
a
{
padding
:
6pt
;
border
:
1px
solid
rgba
(
0
,
0
,
0
,
0.14
);
border-radius
:
0.325em
;
margin-right
:
6pt
;
}
.document_table
table
tfoot
.ui-controlgroup-controls
a
:last-of-type
{
.document_table
nav
a
:last-of-type
{
margin-right
:
0
;
}
.document_table
table
tfoot
.ui-controlgroup-controls
a
:hover
,
.document_table
table
tfoot
.ui-controlgroup-controls
a
:active
{
.document_table
nav
a
:hover
,
.document_table
nav
a
:active
{
background-color
:
#e0e0e0
;
}
@media
not
screen
and
(
min-width
:
45em
)
{
.document_table
table
tfoot
.ui-controlgroup-controls
a
{
.document_table
nav
a
{
overflow
:
hidden
;
text-indent
:
-9999px
;
white-space
:
nowrap
;
}
}
.document_table
table
tfoot
.ui-controlgroup-controls
a
::before
{
.document_table
nav
a
::before
{
margin-right
:
6pt
;
}
@media
not
screen
and
(
min-width
:
45em
)
{
.document_table
table
tfoot
.ui-controlgroup-controls
a
::before
{
.document_table
nav
a
::before
{
float
:
left
;
text-indent
:
6pt
;
}
...
...
bt5/erp5_web_renderjs_ui/PathTemplateItem/web_page_module/rjs_gadget_erp5_nojqm_css.xml
View file @
d2f8c300
...
...
@@ -242,7 +242,7 @@
</item>
<item>
<key>
<string>
serial
</string>
</key>
<value>
<string>
96
2.1204.63259.57958
</string>
</value>
<value>
<string>
96
3.54869.55137.61115
</string>
</value>
</item>
<item>
<key>
<string>
state
</string>
</key>
...
...
@@ -260,7 +260,7 @@
</tuple>
<state>
<tuple>
<float>
15
05133981.01
</float>
<float>
15
12455490.33
</float>
<string>
UTC
</string>
</tuple>
</state>
...
...
bt5/erp5_web_renderjs_ui/SkinTemplateItem/portal_skins/erp5_web_renderjs_ui/erp5css.less.txt
View file @
d2f8c300
...
...
@@ -1458,9 +1458,10 @@ div[data-gadget-scope='erp5_searchfield'] {
th, td {
// line-height: 1.5em;
vertical-align: middle;
padding: @half-margin-size;
}
thead {
thead
, tfoot
{
background-color: @colorsubheaderbackground;
color: @white;
...
...
@@ -1500,7 +1501,6 @@ div[data-gadget-scope='erp5_searchfield'] {
@media @desktop, @tablet {
a {
display: block;
padding: @half-margin-size;
}
}
...
...
@@ -1580,10 +1580,10 @@ div[data-gadget-scope='erp5_searchfield'] {
}
}
}
}
}
/* end-of tbody, tfoot*/
} /* end-of table */
tfoot .ui-controlgroup-controls
{
nav
{
display: flex;
padding-top: @margin-size;
border-top: 2px solid rgba(0, 0, 0, 0.14902);
...
...
@@ -1592,6 +1592,7 @@ div[data-gadget-scope='erp5_searchfield'] {
opacity: .3;
flex: 2;
text-align: right;
float: right;
}
a {
padding: @margin-size;
...
...
@@ -1621,8 +1622,6 @@ div[data-gadget-scope='erp5_searchfield'] {
}
}
}
}
}
/**********************************************
...
...
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