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
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
Romain Courteaud
erp5
Commits
f6115654
Commit
f6115654
authored
Nov 17, 2020
by
Romain Courteaud
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
erp5_forge: WIP action to commit from erp5js
parent
320e734c
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
1345 additions
and
2 deletions
+1345
-2
bt5/erp5_forge/ActionTemplateItem/portal_types/Business%20Template/git_commit.xml
...plateItem/portal_types/Business%20Template/git_commit.xml
+2
-2
bt5/erp5_forge/ActionTemplateItem/portal_types/Business%20Template/git_commit_action.xml
...em/portal_types/Business%20Template/git_commit_action.xml
+96
-0
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/BusinessTemplate_doVcsDiffAsJson.py
...portal_skins/erp5_vcs/BusinessTemplate_doVcsDiffAsJson.py
+30
-0
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/BusinessTemplate_doVcsDiffAsJson.xml
...ortal_skins/erp5_vcs/BusinessTemplate_doVcsDiffAsJson.xml
+62
-0
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/BusinessTemplate_viewVcsStatusDialog.xml
...l_skins/erp5_vcs/BusinessTemplate_viewVcsStatusDialog.xml
+139
-0
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/BusinessTemplate_viewVcsStatusDialog/your_vcs_status_gadget.xml
...ssTemplate_viewVcsStatusDialog/your_vcs_status_gadget.xml
+153
-0
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/gadget_vcs_status.css.css
...plateItem/portal_skins/erp5_vcs/gadget_vcs_status.css.css
+39
-0
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/gadget_vcs_status.css.xml
...plateItem/portal_skins/erp5_vcs/gadget_vcs_status.css.xml
+32
-0
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/gadget_vcs_status.html.html
...ateItem/portal_skins/erp5_vcs/gadget_vcs_status.html.html
+19
-0
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/gadget_vcs_status.html.xml
...lateItem/portal_skins/erp5_vcs/gadget_vcs_status.html.xml
+32
-0
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/gadget_vcs_status.js.js
...emplateItem/portal_skins/erp5_vcs/gadget_vcs_status.js.js
+630
-0
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/gadget_vcs_status.js.xml
...mplateItem/portal_skins/erp5_vcs/gadget_vcs_status.js.xml
+32
-0
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/gadget_vcs_status.less.bin
...lateItem/portal_skins/erp5_vcs/gadget_vcs_status.less.bin
+50
-0
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/gadget_vcs_status.less.xml
...lateItem/portal_skins/erp5_vcs/gadget_vcs_status.less.xml
+28
-0
bt5/erp5_forge/bt/template_action_path_list
bt5/erp5_forge/bt/template_action_path_list
+1
-0
No files found.
bt5/erp5_forge/ActionTemplateItem/portal_types/Business%20Template/git_commit.xml
View file @
f6115654
...
...
@@ -16,13 +16,13 @@
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
action_type/object_view
</string>
<string>
action_type/object_
onlyxhtml_
view
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
category
</string>
</key>
<value>
<string>
object_view
</string>
</value>
<value>
<string>
object_
onlyxhtml_
view
</string>
</value>
</item>
<item>
<key>
<string>
condition
</string>
</key>
...
...
bt5/erp5_forge/ActionTemplateItem/portal_types/Business%20Template/git_commit_action.xml
0 → 100644
View file @
f6115654
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"ActionInformation"
module=
"Products.CMFCore.ActionInformation"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
action
</string>
</key>
<value>
<persistent>
<string
encoding=
"base64"
>
AAAAAAAAAAI=
</string>
</persistent>
</value>
</item>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
action_type/object_onlyjio_action
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
category
</string>
</key>
<value>
<string>
object_onlyjio_action
</string>
</value>
</item>
<item>
<key>
<string>
condition
</string>
</key>
<value>
<persistent>
<string
encoding=
"base64"
>
AAAAAAAAAAM=
</string>
</persistent>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<key>
<string>
icon
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
git_commit_action
</string>
</value>
</item>
<item>
<key>
<string>
permissions
</string>
</key>
<value>
<tuple>
<string>
Manage portal
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
priority
</string>
</key>
<value>
<float>
4.5
</float>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
Git
</string>
</value>
</item>
<item>
<key>
<string>
visible
</string>
</key>
<value>
<int>
1
</int>
</value>
</item>
</dictionary>
</pickle>
</record>
<record
id=
"2"
aka=
"AAAAAAAAAAI="
>
<pickle>
<global
name=
"Expression"
module=
"Products.CMFCore.Expression"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
text
</string>
</key>
<value>
<string>
string:${object_url}/BusinessTemplate_viewVcsStatusDialog
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
<record
id=
"3"
aka=
"AAAAAAAAAAM="
>
<pickle>
<global
name=
"Expression"
module=
"Products.CMFCore.Expression"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
text
</string>
</key>
<value>
<string>
python: here.getInstallationState() not in (\'installed\', \'replaced\') and here.isVcsType(\'git\')
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/BusinessTemplate_doVcsDiffAsJson.py
0 → 100644
View file @
f6115654
import
json
vcs_tool
=
context
.
getVcsTool
()
result_dict
=
{
'added_list'
:
[],
'modified_list'
:
[],
'deleted_list'
:
[]
}
for
path
in
modified
:
result_dict
[
'modified_list'
].
append
({
'path'
:
path
,
# 'edit_path': vcs_tool.editPath(path, True),
'diff'
:
vcs_tool
.
diff
(
path
)
})
for
path
in
added
:
result_dict
[
'added_list'
].
append
({
'path'
:
path
})
for
path
in
deleted
:
result_dict
[
'deleted_list'
].
append
({
'path'
:
path
})
if
REQUEST
is
not
None
:
REQUEST
.
RESPONSE
.
setHeader
(
'Content-Type'
,
'application/hal+json'
)
return
json
.
dumps
(
result_dict
,
indent
=
2
)
else
:
return
result_dict
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/BusinessTemplate_doVcsDiffAsJson.xml
0 → 100644
View file @
f6115654
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"PythonScript"
module=
"Products.PythonScripts.PythonScript"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
Script_magic
</string>
</key>
<value>
<int>
3
</int>
</value>
</item>
<item>
<key>
<string>
_bind_names
</string>
</key>
<value>
<object>
<klass>
<global
name=
"NameAssignments"
module=
"Shared.DC.Scripts.Bindings"
/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key>
<string>
_asgns
</string>
</key>
<value>
<dictionary>
<item>
<key>
<string>
name_container
</string>
</key>
<value>
<string>
container
</string>
</value>
</item>
<item>
<key>
<string>
name_context
</string>
</key>
<value>
<string>
context
</string>
</value>
</item>
<item>
<key>
<string>
name_m_self
</string>
</key>
<value>
<string>
script
</string>
</value>
</item>
<item>
<key>
<string>
name_subpath
</string>
</key>
<value>
<string>
traverse_subpath
</string>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key>
<string>
_params
</string>
</key>
<value>
<string>
added=(), modified=(), deleted=(), REQUEST=None
</string>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
BusinessTemplate_doVcsDiffAsJson
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/BusinessTemplate_viewVcsStatusDialog.xml
0 → 100644
View file @
f6115654
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"ERP5 Form"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_objects
</string>
</key>
<value>
<tuple/>
</value>
</item>
<item>
<key>
<string>
action
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
action_title
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
edit_order
</string>
</key>
<value>
<list/>
</value>
</item>
<item>
<key>
<string>
encoding
</string>
</key>
<value>
<string>
UTF-8
</string>
</value>
</item>
<item>
<key>
<string>
enctype
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
group_list
</string>
</key>
<value>
<list>
<string>
left
</string>
<string>
right
</string>
<string>
center
</string>
<string>
bottom
</string>
<string>
hidden
</string>
</list>
</value>
</item>
<item>
<key>
<string>
groups
</string>
</key>
<value>
<dictionary>
<item>
<key>
<string>
bottom
</string>
</key>
<value>
<list/>
</value>
</item>
<item>
<key>
<string>
center
</string>
</key>
<value>
<list>
<string>
your_vcs_status_gadget
</string>
</list>
</value>
</item>
<item>
<key>
<string>
hidden
</string>
</key>
<value>
<list/>
</value>
</item>
<item>
<key>
<string>
left
</string>
</key>
<value>
<list/>
</value>
</item>
<item>
<key>
<string>
right
</string>
</key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
BusinessTemplate_viewVcsStatusDialog
</string>
</value>
</item>
<item>
<key>
<string>
method
</string>
</key>
<value>
<string>
POST
</string>
</value>
</item>
<item>
<key>
<string>
name
</string>
</key>
<value>
<string>
BusinessTemplate_viewVcsStatusDialog
</string>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
ERP5 Form
</string>
</value>
</item>
<item>
<key>
<string>
pt
</string>
</key>
<value>
<string>
form_dialog
</string>
</value>
</item>
<item>
<key>
<string>
row_length
</string>
</key>
<value>
<int>
4
</int>
</value>
</item>
<item>
<key>
<string>
stored_encoding
</string>
</key>
<value>
<string>
UTF-8
</string>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
VCS Status
</string>
</value>
</item>
<item>
<key>
<string>
unicode_mode
</string>
</key>
<value>
<int>
0
</int>
</value>
</item>
<item>
<key>
<string>
update_action
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
update_action_title
</string>
</key>
<value>
<string></string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/BusinessTemplate_viewVcsStatusDialog/your_vcs_status_gadget.xml
0 → 100644
View file @
f6115654
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"ProxyField"
module=
"Products.ERP5Form.ProxyField"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
delegated_list
</string>
</key>
<value>
<list>
<string>
gadget_url
</string>
<string>
renderjs_extra
</string>
<string>
title
</string>
</list>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
your_vcs_status_gadget
</string>
</value>
</item>
<item>
<key>
<string>
message_values
</string>
</key>
<value>
<dictionary>
<item>
<key>
<string>
external_validator_failed
</string>
</key>
<value>
<string>
The input failed the external validator.
</string>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key>
<string>
overrides
</string>
</key>
<value>
<dictionary>
<item>
<key>
<string>
field_id
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
form_id
</string>
</key>
<value>
<string></string>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key>
<string>
tales
</string>
</key>
<value>
<dictionary>
<item>
<key>
<string>
field_id
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
form_id
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
gadget_url
</string>
</key>
<value>
<persistent>
<string
encoding=
"base64"
>
AAAAAAAAAAI=
</string>
</persistent>
</value>
</item>
<item>
<key>
<string>
renderjs_extra
</string>
</key>
<value>
<persistent>
<string
encoding=
"base64"
>
AAAAAAAAAAM=
</string>
</persistent>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<persistent>
<string
encoding=
"base64"
>
AAAAAAAAAAQ=
</string>
</persistent>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key>
<string>
values
</string>
</key>
<value>
<dictionary>
<item>
<key>
<string>
field_id
</string>
</key>
<value>
<string>
my_gadget_field
</string>
</value>
</item>
<item>
<key>
<string>
form_id
</string>
</key>
<value>
<string>
Base_viewFieldLibrary
</string>
</value>
</item>
<item>
<key>
<string>
gadget_url
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
renderjs_extra
</string>
</key>
<value>
<list/>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
Gadget
</string>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record
id=
"2"
aka=
"AAAAAAAAAAI="
>
<pickle>
<global
name=
"TALESMethod"
module=
"Products.Formulator.TALESField"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_text
</string>
</key>
<value>
<string>
string: gadget_vcs_status.html
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
<record
id=
"3"
aka=
"AAAAAAAAAAM="
>
<pickle>
<global
name=
"TALESMethod"
module=
"Products.Formulator.TALESField"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_text
</string>
</key>
<value>
<string>
python: [(\'diff_url\', \'%s/BusinessTemplate_doVcsDiffAsJson\' % here.absolute_url()), (\'get_tree_url\', \'%s/tree.xml\' % here.absolute_url()), (\'remote_url\', here.getVcsTool().getRemoteUrl()), (\'remote_comment\', here.getVcsTool().getRemoteComment())]
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
<record
id=
"4"
aka=
"AAAAAAAAAAQ="
>
<pickle>
<global
name=
"TALESMethod"
module=
"Products.Formulator.TALESField"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_text
</string>
</key>
<value>
<string>
string:
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/gadget_vcs_status.css.css
0 → 100644
View file @
f6115654
div
[
data-gadget-url
$
=
"gadget_vcs_status.html"
]
li
li
{
padding-left
:
1em
;
}
div
[
data-gadget-url
$
=
"gadget_vcs_status.html"
]
li
input
.showhide
{
display
:
none
;
}
div
[
data-gadget-url
$
=
"gadget_vcs_status.html"
]
li
input
.showhide
:checked
~
label
.show
{
display
:
none
;
}
div
[
data-gadget-url
$
=
"gadget_vcs_status.html"
]
li
input
.showhide
:checked
~
ul
{
display
:
none
;
}
div
[
data-gadget-url
$
=
"gadget_vcs_status.html"
]
li
input
.showhide
:not
(
:checked
)
~
label
.hide
{
display
:
none
;
}
div
[
data-gadget-url
$
=
"gadget_vcs_status.html"
]
li
label
{
display
:
inline-block
;
}
div
[
data-gadget-url
$
=
"gadget_vcs_status.html"
]
li
label
.showhide
{
width
:
1em
;
}
div
[
data-gadget-url
$
=
"gadget_vcs_status.html"
]
li
label
input
{
margin
:
0
3pt
;
}
div
[
data-gadget-url
$
=
"gadget_vcs_status.html"
]
button
{
color
:
#212529
;
padding
:
3pt
;
border
:
1px
solid
rgba
(
0
,
0
,
0
,
0.14
);
border-radius
:
0.325em
;
display
:
inline-block
;
margin-right
:
6pt
;
}
div
[
data-gadget-url
$
=
"gadget_vcs_status.html"
]
button
:disabled
,
div
[
data-gadget-url
$
=
"gadget_vcs_status.html"
]
button
[
disabled
]
{
color
:
#999999
;
}
div
[
data-gadget-url
$
=
"gadget_vcs_status.html"
]
button
:before
{
padding-right
:
0.2em
;
}
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/gadget_vcs_status.css.xml
0 → 100644
View file @
f6115654
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"File"
module=
"OFS.Image"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Cacheable__manager_id
</string>
</key>
<value>
<string>
must_revalidate_http_cache
</string>
</value>
</item>
<item>
<key>
<string>
__name__
</string>
</key>
<value>
<string>
gadget_vcs_status.css
</string>
</value>
</item>
<item>
<key>
<string>
content_type
</string>
</key>
<value>
<string>
text/css
</string>
</value>
</item>
<item>
<key>
<string>
precondition
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string></string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/gadget_vcs_status.html.html
0 → 100644
View file @
f6115654
<!DOCTYPE html>
<html>
<head>
<meta
http-equiv=
"Content-type"
content=
"text/html; charset=utf-8"
/>
<meta
name=
"viewport"
content=
"width=device-width,height=device-height,initial-scale=1"
/>
<title>
VCS status gadget
</title>
<link
rel=
stylesheet
href=
"gadget_vcs_status.css"
>
<script
src=
"rsvp.js"
type=
"text/javascript"
></script>
<script
src=
"renderjs.js"
type=
"text/javascript"
></script>
<script
src=
"jio.js"
type=
"text/javascript"
></script>
<script
src=
"domsugar.js"
type=
"text/javascript"
></script>
<script
src=
"gadget_vcs_status.js"
type=
"text/javascript"
></script>
</head>
<body>
<div
class=
"vcsheader"
></div>
<div
class=
"vcsbody"
></div>
</body>
</html>
\ No newline at end of file
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/gadget_vcs_status.html.xml
0 → 100644
View file @
f6115654
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"File"
module=
"OFS.Image"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Cacheable__manager_id
</string>
</key>
<value>
<string>
must_revalidate_http_cache
</string>
</value>
</item>
<item>
<key>
<string>
__name__
</string>
</key>
<value>
<string>
gadget_vcs_status.html
</string>
</value>
</item>
<item>
<key>
<string>
content_type
</string>
</key>
<value>
<string>
text/html
</string>
</value>
</item>
<item>
<key>
<string>
precondition
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string></string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/gadget_vcs_status.js.js
0 → 100644
View file @
f6115654
/*global window, rJS, RSVP, domsugar, jIO, console, document, NodeFilter,
FormData */
/*jslint nomen: true, maxlen:80, indent:2*/
(
function
(
window
,
rJS
,
RSVP
,
domsugar
,
jIO
,
console
,
document
,
NodeFilter
)
{
"
use strict
"
;
var
whitelist
=
{
node_list
:
{
P
:
true
,
UL
:
true
,
LI
:
true
,
INPUT
:
true
,
LABEL
:
true
},
attribute_list
:
{
type
:
true
,
class
:
true
,
id
:
true
,
for
:
true
,
name
:
true
,
value
:
true
}
},
DISPLAY_TREE
=
'
display_tree
'
,
DISPLAY_DIFF
=
'
display_diff
'
,
DISPLAY_CHANGELOG
=
'
display_changelog
'
;
function
keepOnlyChildren
(
current_node
)
{
var
fragment
=
document
.
createDocumentFragment
();
while
(
current_node
.
firstChild
)
{
fragment
.
appendChild
(
current_node
.
firstChild
);
}
current_node
.
parentNode
.
replaceChild
(
fragment
,
current_node
);
}
function
renderTreeXml
(
gadget
,
tree_xml_element
)
{
var
iterator
,
current_node
,
next_node
,
parent_node
,
value
,
ul_node
,
attribute
,
attribute_list
,
child_list
,
len
,
finished
=
false
,
id
=
-
1
,
name
,
state_value
=
JSON
.
parse
(
gadget
.
state
.
value
);
// Replace the tree element by a fragment
next_node
=
domsugar
(
'
ul
'
);
while
(
tree_xml_element
.
firstChild
)
{
next_node
.
appendChild
(
tree_xml_element
.
firstChild
);
}
tree_xml_element
=
next_node
;
iterator
=
document
.
createNodeIterator
(
tree_xml_element
,
NodeFilter
.
SHOW_ELEMENT
,
function
()
{
return
NodeFilter
.
FILTER_ACCEPT
;
}
);
while
(
!
finished
)
{
current_node
=
iterator
.
nextNode
();
finished
=
(
current_node
===
null
);
if
(
!
finished
)
{
if
(
current_node
.
nodeName
===
'
item
'
)
{
child_list
=
[];
// Open/hide element
if
(
current_node
.
firstChild
)
{
id
+=
1
;
child_list
.
push
(
domsugar
(
'
input
'
,
{
type
:
'
checkbox
'
,
id
:
'
showhide
'
+
id
,
class
:
'
showhide
'
}),
domsugar
(
'
label
'
,
{
for
:
'
showhide
'
+
id
,
class
:
'
showhide show
'
,
text
:
'
-
'
}),
domsugar
(
'
label
'
,
{
for
:
'
showhide
'
+
id
,
class
:
'
showhide hide
'
,
text
:
'
+
'
})
);
}
else
{
child_list
.
push
(
domsugar
(
'
label
'
,
{
class
:
'
showhide
'
,
text
:
'
'
})
);
}
// Select element
parent_node
=
current_node
.
closest
(
'
ul
'
);
if
(
parent_node
!==
null
)
{
parent_node
=
parent_node
.
parentNode
;
}
if
(
parent_node
!==
null
)
{
parent_node
=
parent_node
.
querySelector
(
'
input.vcs_to_commit
'
);
}
value
=
current_node
.
getAttribute
(
'
text
'
);
if
((
parent_node
!==
null
)
&&
(
parent_node
!==
current_node
))
{
if
(
parent_node
.
value
)
{
value
=
parent_node
.
value
+
'
/
'
+
value
;
}
}
else
{
value
=
''
;
}
name
=
{
'
green
'
:
'
added
'
,
'
orange
'
:
'
modified
'
,
'
red
'
:
'
deleted
'
}[
current_node
.
getAttribute
(
'
aCol
'
)];
child_list
.
push
(
domsugar
(
'
label
'
,
[
domsugar
(
'
input
'
,
{
type
:
'
checkbox
'
,
class
:
'
vcs_to_commit
'
,
value
:
value
,
name
:
name
,
checked
:
((
state_value
[
name
]
===
undefined
)
||
(
state_value
[
name
].
indexOf
(
value
)
===
-
1
))
?
''
:
'
checked
'
}),
current_node
.
getAttribute
(
'
text
'
)
]));
// Child items
if
(
current_node
.
firstChild
)
{
ul_node
=
domsugar
(
'
ul
'
);
while
(
current_node
.
firstChild
)
{
ul_node
.
appendChild
(
current_node
.
firstChild
);
}
child_list
.
push
(
ul_node
);
}
current_node
.
parentNode
.
replaceChild
(
domsugar
(
'
li
'
,
child_list
),
current_node
);
}
else
if
(
!
whitelist
.
node_list
[
current_node
.
nodeName
])
{
keepOnlyChildren
(
current_node
);
}
else
{
// Cleanup attributes
attribute_list
=
current_node
.
attributes
;
len
=
attribute_list
.
length
;
while
(
len
!==
0
)
{
len
=
len
-
1
;
attribute
=
attribute_list
[
len
].
name
;
if
(
!
whitelist
.
attribute_list
[
attribute
])
{
current_node
.
removeAttribute
(
attribute
);
}
}
}
}
}
return
tree_xml_element
;
}
function
renderGadgetHeader
(
gadget
,
loading
)
{
var
element_list
=
[
domsugar
(
'
p
'
,
[
'
Repository:
'
,
domsugar
(
'
a
'
,
{
text
:
gadget
.
state
.
remote_url
,
href
:
gadget
.
state
.
remote_url
}),
'
(
'
+
gadget
.
state
.
remote_comment
+
'
)
'
])
],
tree_icon
=
'
ui-icon-check-square
'
,
diff_icon
=
'
ui-icon-search-plus
'
,
changelog_icon
=
'
ui-icon-git
'
;
if
(
loading
)
{
if
(
gadget
.
state
.
display_step
===
DISPLAY_TREE
)
{
tree_icon
=
'
ui-icon-spinner
'
;
}
else
if
(
gadget
.
state
.
display_step
===
DISPLAY_DIFF
)
{
diff_icon
=
'
ui-icon-spinner
'
;
}
else
if
(
gadget
.
state
.
display_step
===
DISPLAY_CHANGELOG
)
{
changelog_icon
=
'
ui-icon-spinner
'
;
}
else
{
throw
new
Error
(
"
Can't render header state
"
+
gadget
.
state
.
display_step
);
}
}
element_list
.
push
(
domsugar
(
'
button
'
,
{
type
:
'
button
'
,
text
:
'
Tree
'
,
disabled
:
(
gadget
.
state
.
display_step
===
DISPLAY_TREE
),
class
:
'
display-tree-btn ui-btn-icon-left
'
+
tree_icon
}),
domsugar
(
'
button
'
,
{
type
:
'
button
'
,
text
:
'
Diff
'
,
disabled
:
(
gadget
.
state
.
display_step
===
DISPLAY_DIFF
),
class
:
'
diff-tree-btn ui-btn-icon-left
'
+
diff_icon
}),
domsugar
(
'
button
'
,
{
type
:
'
button
'
,
text
:
'
Changelog
'
,
disabled
:
(
gadget
.
state
.
display_step
===
DISPLAY_CHANGELOG
),
class
:
'
changelog-btn ui-btn-icon-left
'
+
changelog_icon
})
);
if
((
!
loading
)
&&
(
gadget
.
state
.
display_step
===
DISPLAY_TREE
))
{
element_list
.
push
(
domsugar
(
'
button
'
,
{
type
:
'
button
'
,
text
:
'
Expand
'
,
class
:
'
expand-tree-btn ui-btn-icon-left ui-icon-arrows-v
'
})
);
}
domsugar
(
gadget
.
element
.
querySelector
(
'
div.vcsheader
'
),
element_list
);
}
function
updateFullTreeCheckbox
(
checkbox
)
{
var
i
,
element_list
,
parent_checkbox
,
is_checked
,
is_indeterminate
;
// https://css-tricks.com/indeterminate-checkboxes/
// Check/uncheck all children checkboxes
element_list
=
checkbox
.
parentElement
.
parentElement
.
querySelectorAll
(
'
input.vcs_to_commit
'
);
for
(
i
=
0
;
i
<
element_list
.
length
;
i
+=
1
)
{
element_list
[
i
].
checked
=
checkbox
.
checked
;
element_list
[
i
].
indeterminate
=
false
;
}
// Check/uncheck/undefine all parent checkboxes
while
(
checkbox
!==
null
)
{
parent_checkbox
=
checkbox
.
closest
(
'
ul
'
)
.
parentNode
.
querySelector
(
'
input.vcs_to_commit
'
);
if
(
parent_checkbox
===
checkbox
)
{
// Top checkbox
checkbox
=
null
;
}
else
{
// check the state of all child chexbox
element_list
=
parent_checkbox
.
closest
(
'
li
'
)
.
querySelector
(
'
ul
'
)
.
children
;
is_checked
=
true
;
is_indeterminate
=
false
;
for
(
i
=
0
;
i
<
element_list
.
length
;
i
+=
1
)
{
is_checked
=
is_checked
&&
element_list
[
i
].
querySelector
(
'
input.vcs_to_commit
'
)
.
checked
;
is_indeterminate
=
is_indeterminate
||
element_list
[
i
].
querySelector
(
'
input.vcs_to_commit
'
)
.
checked
||
element_list
[
i
].
querySelector
(
'
input.vcs_to_commit
'
)
.
indeterminate
;
}
parent_checkbox
.
checked
=
is_checked
;
parent_checkbox
.
indeterminate
=
(
!
is_checked
)
&&
is_indeterminate
;
checkbox
=
parent_checkbox
;
}
}
}
function
renderTreeView
(
gadget
,
extract
)
{
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
renderGadgetHeader
(
gadget
,
true
);
var
form_data
=
new
FormData
();
form_data
.
append
(
'
show_unmodified:int
'
,
0
);
// form_data.append('bt_id', 'erp5');
form_data
.
append
(
'
do_extract:int
'
,
extract
?
1
:
0
);
return
jIO
.
util
.
ajax
({
"
type
"
:
"
POST
"
,
"
url
"
:
gadget
.
state
.
get_tree_url
,
"
xhrFields
"
:
{
withCredentials
:
true
},
"
dataType
"
:
"
document
"
,
"
data
"
:
form_data
});
})
.
push
(
function
(
evt
)
{
renderGadgetHeader
(
gadget
,
false
);
domsugar
(
gadget
.
element
.
querySelector
(
'
div.vcsbody
'
),
[
renderTreeXml
(
gadget
,
evt
.
target
.
response
.
querySelector
(
'
tree
'
))
]);
// Update the tree parent
var
element_list
=
gadget
.
element
.
querySelectorAll
(
'
input.vcs_to_commit
'
),
i
;
for
(
i
=
0
;
i
<
element_list
.
length
;
i
+=
1
)
{
if
(
element_list
[
i
].
checked
)
{
updateFullTreeCheckbox
(
element_list
[
i
]);
}
}
});
}
function
getContentFromTreeView
(
gadget
)
{
var
result
=
JSON
.
parse
(
gadget
.
state
.
value
),
checkbox_list
=
gadget
.
element
.
querySelectorAll
(
'
input.vcs_to_commit
'
),
i
,
name
;
result
.
added
=
[];
result
.
modified
=
[];
result
.
deleted
=
[];
for
(
i
=
0
;
i
<
checkbox_list
.
length
;
i
+=
1
)
{
name
=
checkbox_list
[
i
].
name
;
if
(
name
&&
checkbox_list
[
i
].
checked
)
{
result
[
name
].
push
(
checkbox_list
[
i
].
value
);
}
}
gadget
.
state
.
value
=
JSON
.
stringify
(
result
);
}
function
expandTreeView
(
gadget
)
{
var
element_list
=
gadget
.
element
.
querySelectorAll
(
'
input.showhide
'
),
// Do not crash if no checkbox is displayed
is_checked
=
(
element_list
.
length
!==
0
)
&&
(
!
element_list
[
0
].
checked
),
i
;
for
(
i
=
0
;
i
<
element_list
.
length
;
i
+=
1
)
{
element_list
[
0
].
checked
=
is_checked
;
}
}
function
declareAndRenderDiff
(
gadget
,
path
,
diff
)
{
return
gadget
.
declareGadget
(
'
gadget_erp5_side_by_side_diff.html
'
)
.
push
(
function
(
diff_gadget
)
{
return
RSVP
.
all
([
diff_gadget
.
element
,
path
,
diff_gadget
.
render
({
value
:
diff
})
]);
});
}
function
renderDiffView
(
gadget
)
{
var
result
=
JSON
.
parse
(
gadget
.
state
.
value
),
ajax_result
,
diff_count
=
result
.
modified
.
length
;
return
new
RSVP
.
Queue
()
.
push
(
function
()
{
renderGadgetHeader
(
gadget
,
true
);
var
form_data
=
new
FormData
(),
key_list
=
[
'
modified
'
,
'
added
'
,
'
deleted
'
],
key
,
i
,
j
;
for
(
i
=
0
;
i
<
key_list
.
length
;
i
+=
1
)
{
key
=
key_list
[
i
];
for
(
j
=
0
;
j
<
result
[
key
].
length
;
j
+=
1
)
{
form_data
.
append
(
key
+
'
:list
'
,
result
[
key
][
j
]);
}
}
return
jIO
.
util
.
ajax
({
"
type
"
:
"
POST
"
,
"
url
"
:
gadget
.
state
.
diff_url
,
"
xhrFields
"
:
{
withCredentials
:
true
},
"
dataType
"
:
"
json
"
,
"
data
"
:
form_data
});
})
.
push
(
function
(
evt
)
{
ajax_result
=
evt
.
target
.
response
;
var
promise_list
=
[],
i
;
for
(
i
=
0
;
i
<
diff_count
;
i
+=
1
)
{
promise_list
.
push
(
declareAndRenderDiff
(
gadget
,
ajax_result
.
modified_list
[
i
].
path
,
ajax_result
.
modified_list
[
i
].
diff
)
);
}
return
RSVP
.
all
(
promise_list
);
})
.
push
(
function
(
result_list
)
{
var
i
,
element_list
=
[];
for
(
i
=
0
;
i
<
result_list
.
length
;
i
+=
1
)
{
element_list
.
push
(
domsugar
(
'
label
'
,
[
domsugar
(
'
input
'
,
{
type
:
'
checkbox
'
,
class
:
'
vcs_to_commit
'
,
value
:
result_list
[
i
][
1
],
name
:
'
modified
'
,
checked
:
'
checked
'
}),
result_list
[
i
][
1
]
]),
result_list
[
i
][
0
]
);
}
for
(
i
=
0
;
i
<
ajax_result
.
added_list
.
length
;
i
+=
1
)
{
element_list
.
push
(
domsugar
(
'
label
'
,
[
domsugar
(
'
input
'
,
{
type
:
'
checkbox
'
,
class
:
'
vcs_to_commit
'
,
value
:
ajax_result
.
added_list
[
i
].
path
,
name
:
'
added
'
,
checked
:
'
checked
'
}),
ajax_result
.
added_list
[
i
].
path
])
);
}
for
(
i
=
0
;
i
<
ajax_result
.
deleted_list
.
length
;
i
+=
1
)
{
element_list
.
push
(
domsugar
(
'
label
'
,
[
domsugar
(
'
input
'
,
{
type
:
'
checkbox
'
,
class
:
'
vcs_to_commit
'
,
value
:
ajax_result
.
deleted_list
[
i
].
path
,
name
:
'
deleted
'
,
checked
:
'
checked
'
}),
ajax_result
.
deleted_list
[
i
].
path
])
);
}
renderGadgetHeader
(
gadget
,
false
);
domsugar
(
gadget
.
element
.
querySelector
(
'
div.vcsbody
'
),
element_list
);
});
}
function
renderChangelogView
(
gadget
)
{
var
result
=
JSON
.
parse
(
gadget
.
state
.
value
);
renderGadgetHeader
(
gadget
,
false
);
domsugar
(
gadget
.
element
.
querySelector
(
'
div.vcsbody
'
),
[
domsugar
(
'
textarea
'
,
{
value
:
result
.
changelog
}),
domsugar
(
'
h3
'
,
{
text
:
'
Added Files
'
}),
domsugar
(
'
pre
'
,
{
text
:
result
.
added
.
join
(
'
\n
'
)}),
domsugar
(
'
h3
'
,
{
text
:
'
Modified Files
'
}),
domsugar
(
'
pre
'
,
{
text
:
result
.
modified
.
join
(
'
\n
'
)}),
domsugar
(
'
h3
'
,
{
text
:
'
Deleted Files
'
}),
domsugar
(
'
pre
'
,
{
text
:
result
.
deleted
.
join
(
'
\n
'
)})
]);
}
function
getContentFromChangelogView
(
gadget
)
{
var
result
=
JSON
.
parse
(
gadget
.
state
.
value
);
result
.
changelog
=
gadget
.
element
.
querySelector
(
'
textarea
'
).
value
;
gadget
.
state
.
value
=
JSON
.
stringify
(
result
);
}
rJS
(
window
)
.
declareMethod
(
'
render
'
,
function
(
options
)
{
return
this
.
changeState
({
display_step
:
DISPLAY_TREE
,
// Only build the bt5 during the first query
extract
:
1
,
diff_url
:
options
.
diff_url
,
get_tree_url
:
options
.
get_tree_url
,
remote_comment
:
options
.
remote_comment
,
remote_url
:
options
.
remote_url
,
// key: options.key,
// value: options.value || "",
value
:
JSON
.
stringify
({
added
:
[],
modified
:
[],
deleted
:
[],
changelog
:
''
}),
editable
:
(
options
.
editable
===
undefined
)
?
true
:
options
.
editable
});
})
.
onStateChange
(
function
(
modification_dict
)
{
var
gadget
=
this
;
if
(
gadget
.
state
.
display_step
===
DISPLAY_TREE
)
{
console
.
log
(
modification_dict
);
if
(
modification_dict
.
hasOwnProperty
(
'
display_step
'
))
{
return
renderTreeView
(
gadget
,
modification_dict
.
hasOwnProperty
(
'
extract
'
));
}
if
(
modification_dict
.
hasOwnProperty
(
'
expand_tree
'
))
{
return
expandTreeView
(
gadget
);
}
}
if
(
modification_dict
.
display_step
===
DISPLAY_DIFF
)
{
return
renderDiffView
(
gadget
);
}
if
(
modification_dict
.
display_step
===
DISPLAY_CHANGELOG
)
{
return
renderChangelogView
(
gadget
);
}
throw
new
Error
(
'
Unhandled display step:
'
+
gadget
.
state
.
display_step
);
})
.
onEvent
(
"
change
"
,
function
(
evt
)
{
var
gadget
=
this
,
tag_name
=
evt
.
target
.
tagName
;
// Only handle vcs_to_commit checkbox
if
((
tag_name
!==
'
INPUT
'
)
||
(
evt
.
target
.
className
!==
'
vcs_to_commit
'
))
{
return
;
}
if
(
gadget
.
state
.
display_step
!==
DISPLAY_TREE
)
{
return
;
}
updateFullTreeCheckbox
(
evt
.
target
);
},
false
,
false
)
.
onEvent
(
"
click
"
,
function
(
evt
)
{
// Only handle click on BUTTON and IMG element
var
gadget
=
this
,
tag_name
=
evt
.
target
.
tagName
,
queue
;
if
(
tag_name
!==
'
BUTTON
'
)
{
return
;
}
// Disable any button. It must be managed by this gadget
evt
.
preventDefault
();
// Always get content to ensure the possible displayed form
// is checked and content propagated to the gadget state value
queue
=
gadget
.
getContent
();
if
(
evt
.
target
.
className
.
indexOf
(
"
expand-tree-btn
"
)
!==
-
1
)
{
return
queue
.
push
(
function
()
{
return
gadget
.
changeState
({
display_step
:
DISPLAY_TREE
,
expand_tree
:
new
Date
()
});
});
}
if
(
evt
.
target
.
className
.
indexOf
(
"
display-tree-btn
"
)
!==
-
1
)
{
return
queue
.
push
(
function
()
{
return
gadget
.
changeState
({
display_step
:
DISPLAY_TREE
});
});
}
if
(
evt
.
target
.
className
.
indexOf
(
"
diff-tree-btn
"
)
!==
-
1
)
{
return
queue
.
push
(
function
()
{
return
gadget
.
changeState
({
display_step
:
DISPLAY_DIFF
});
});
}
if
(
evt
.
target
.
className
.
indexOf
(
"
changelog-btn
"
)
!==
-
1
)
{
return
queue
.
push
(
function
()
{
return
gadget
.
changeState
({
display_step
:
DISPLAY_CHANGELOG
});
});
}
throw
new
Error
(
'
Unhandled button:
'
+
evt
.
target
.
textContent
);
},
false
,
false
)
//////////////////////////////////////////////////
// Used when submitting the form
//////////////////////////////////////////////////
.
declareMethod
(
'
getContent
'
,
function
()
{
var
gadget
=
this
,
display_step
=
gadget
.
state
.
display_step
,
queue
;
if
(
gadget
.
state
.
display_step
===
DISPLAY_TREE
)
{
queue
=
new
RSVP
.
Queue
(
getContentFromTreeView
(
gadget
));
}
else
if
(
gadget
.
state
.
display_step
===
DISPLAY_DIFF
)
{
queue
=
new
RSVP
.
Queue
(
getContentFromTreeView
(
gadget
));
}
else
if
(
gadget
.
state
.
display_step
===
DISPLAY_CHANGELOG
)
{
queue
=
new
RSVP
.
Queue
(
getContentFromChangelogView
(
gadget
));
}
else
{
throw
new
Error
(
'
getContent form not handled:
'
+
display_step
);
}
return
queue
.
push
(
function
()
{
var
result
=
{};
if
(
gadget
.
state
.
editable
)
{
result
[
gadget
.
state
.
key
]
=
gadget
.
state
.
value
;
}
return
result
;
});
},
{
mutex
:
'
changestate
'
})
.
declareMethod
(
'
checkValidity
'
,
function
()
{
throw
new
Error
(
'
checkValidity not implemented
'
);
},
{
mutex
:
'
changestate
'
});
}(
window
,
rJS
,
RSVP
,
domsugar
,
jIO
,
console
,
document
,
NodeFilter
));
\ No newline at end of file
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/gadget_vcs_status.js.xml
0 → 100644
View file @
f6115654
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"File"
module=
"OFS.Image"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_Cacheable__manager_id
</string>
</key>
<value>
<string>
must_revalidate_http_cache
</string>
</value>
</item>
<item>
<key>
<string>
__name__
</string>
</key>
<value>
<string>
gadget_vcs_status.js
</string>
</value>
</item>
<item>
<key>
<string>
content_type
</string>
</key>
<value>
<string>
application/javascript
</string>
</value>
</item>
<item>
<key>
<string>
precondition
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string></string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/gadget_vcs_status.less.bin
0 → 100644
View file @
f6115654
div[data-gadget-url$="gadget_vcs_status.html"] {
li {
li {
padding-left: 1em;
}
input.showhide {
display: none;
&:checked ~ label.show {
display: none;
}
&:checked ~ ul {
display: none;
}
&:not(:checked) ~ label.hide {
display: none;
}
}
label {
&.showhide {
width: 1em;
}
display: inline-block;
input {
margin: 0 3pt;
}
}
}
button {
color: #212529;
padding: 3pt;
border: 1px solid rgba(0, 0, 0, 0.14);
border-radius: 0.325em;
display: inline-block;
margin-right: 6pt;
&:disabled, &[disabled] {
color: #999999;
}
&:before {
padding-right: 0.2em;
}
}
}
bt5/erp5_forge/SkinTemplateItem/portal_skins/erp5_vcs/gadget_vcs_status.less.xml
0 → 100644
View file @
f6115654
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"File"
module=
"OFS.Image"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
__name__
</string>
</key>
<value>
<string>
gadget_vcs_status.less
</string>
</value>
</item>
<item>
<key>
<string>
content_type
</string>
</key>
<value>
<string>
text/less
</string>
</value>
</item>
<item>
<key>
<string>
precondition
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string></string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_forge/bt/template_action_path_list
View file @
f6115654
...
...
@@ -15,6 +15,7 @@ Business Template | create_report
Business Template | create_skin_folder
Business Template | create_working_copy
Business Template | git_commit
Business Template | git_commit_action
Business Template | manage_field_library
Business Template | rename_proxy_field
Business Template | svn_cleanup_locks
...
...
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