Commit e9f7d150 authored by Romain Courteaud's avatar Romain Courteaud

[erp5_web_renderjs_ui] Add a label field to simplify field error display

parent cd4e37b2
......@@ -14,8 +14,6 @@
</head>
<body>
<div class="field_container"></div>
</body>
</html>
\ No newline at end of file
......@@ -220,7 +220,7 @@
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>romain</string> </value>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>936.23256.32810.49595</string> </value>
<value> <string>952.64761.25287.18397</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,8 +252,8 @@
</tuple>
<state>
<tuple>
<float>1405948378.28</float>
<string>GMT</string>
<float>1474465679.72</float>
<string>UTC</string>
</tuple>
</state>
</object>
......
......@@ -3,12 +3,6 @@
(function (window, document, rJS, RSVP) {
"use strict";
/////////////////////////////////////////////////////////////////
// Handlebars
/////////////////////////////////////////////////////////////////
// Precompile the templates while loading the first gadget instance
var gadget_klass = rJS(window);
function getFieldTypeGadgetUrl(type) {
var field_url = 'gadget_erp5_field_readonly.html';
if (type === 'ListField') {
......@@ -56,45 +50,11 @@
return field_url;
}
gadget_klass
/////////////////////////////////////////////////////////////////
// ready
/////////////////////////////////////////////////////////////////
// Init local properties
rJS(window)
.ready(function (g) {
g.props = {};
})
// Assign the element to a variable
.ready(function (g) {
return g.getElement()
.push(function (element) {
g.props.element = element;
});
})
.declareAcquiredMethod("translateHtml", "translateHtml")
.allowPublicAcquisition("notifyInvalid", function (param_list, scope) {
return this.getDeclaredGadget(scope)
.push(function (gadget) {
return gadget.getElement();
})
.push(function (gadget_element) {
gadget_element.previousElementSibling.querySelector("span").textContent = " (" + param_list[0] + ")";
});
})
.allowPublicAcquisition("notifyValid", function (param_list, scope) {
/*jslint unparam:true*/
return this.getDeclaredGadget(scope)
.push(function (gadget) {
return gadget.getElement();
})
.push(function (gadget_element) {
gadget_element.previousElementSibling.querySelector("span").textContent = "";
});
})
.allowPublicAcquisition("getFieldTypeGadgetUrl", function (param_list) {
return getFieldTypeGadgetUrl(param_list[0]);
})
......@@ -105,6 +65,7 @@
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
.declareMethod('render', function (options) {
var i,
erp5_document = options.erp5_document,
......@@ -116,10 +77,6 @@
suboption_dict = {},
parent_element = document.createElement("div");
delete options.erp5_document;
delete options.form_definition;
// options = options.form_gadget || {};
form_gadget.state_parameter_dict = options.form_gadget || {};
// XXX Hardcoded for searchfield - remove later!
if (form_definition.extended_search) {
......@@ -129,7 +86,6 @@
suboption_dict.hide_enabled = form_definition.hide_enabled;
form_gadget.props.gadget_list = [];
form_gadget.props.id = options.jio_key;
function addGroup(group) {
queue
......@@ -140,67 +96,44 @@
group_queue = new RSVP.Queue();
function addField(field) {
group_queue.push(function () {
if (rendered_form.hasOwnProperty(field[0])) {
// Field is enabled in this context
var field_queue = new RSVP.Queue(),
sandbox = "public",
field_url,
// Don't change the structure without changing notifyValid and notifyInvalid
field_element = document.createElement("div"),
gadget_element = document.createElement("div"),
label_element = document.createElement("label"),
error_element = document.createElement("span"),
renderered_field = rendered_form[field[0]];
renderered_field = rendered_form[field[0]],
suboptions = options[renderered_field.key] || suboption_dict;
suboptions.field_url = getFieldTypeGadgetUrl(renderered_field.type);
suboptions.label = false;
suboptions.field_json = renderered_field;
suboptions.field_json.view = options.view;
field_element.className = "ui-field-contain";
if (renderered_field.hidden === 1) {
// Hide field
field_element.className = field_element.className + " ui-screen-hidden";
}
// field_element.setAttribute('data-role', 'fieldcontain');
label_element.setAttribute('for', renderered_field.key);
label_element.textContent = renderered_field.title;
label_element.setAttribute('data-i18n', renderered_field.title);
if (renderered_field.hasOwnProperty('error_text')) {
error_element.textContent = " (" + renderered_field.error_text + ")";
}
// error_element.setAttribute('class', 'ui-state-error ui-corner-all');
label_element.appendChild(error_element);
if (group[0] !== "bottom") {
field_element.appendChild(label_element);
suboptions.label = true;
}
field_url = getFieldTypeGadgetUrl(renderered_field.type);
return field_queue
.push(function () {
return form_gadget.translateHtml(field_element.innerHTML);
})
.push(function (my_translate_html) {
field_element.innerHTML = my_translate_html;
field_element.appendChild(gadget_element);
fieldset_element.appendChild(field_element);
})
.push(function () {
return form_gadget.declareGadget(field_url, {
return form_gadget.declareGadget('gadget_erp5_label_field.html', {
scope: renderered_field.key,
element: gadget_element,
element: field_element,
sandbox: sandbox
});
})
.push(function (field_gadget) {
.push(function (label_gadget) {
//XXXXX Hardcoded to get one listbox gadget
//pt form list gadget will get this listbox's info
//then pass to search field gadget
if (field_url === "gadget_erp5_field_listbox.html") {
form_gadget.props.listbox_gadget = field_gadget;
if (suboptions.field_url === "gadget_erp5_field_listbox.html") {
form_gadget.props.listbox_gadget = label_gadget;
}
form_gadget.props.gadget_list.push(field_gadget);
var suboptions = options[renderered_field.key] || suboption_dict;
suboptions.field_json = renderered_field;
suboptions.field_json.view = options.view;
return field_gadget.render(suboptions);
form_gadget.props.gadget_list.push(label_gadget);
return label_gadget.render(suboptions);
})
.push(function () {
fieldset_element.appendChild(field_element);
});
}
});
......@@ -222,7 +155,7 @@
return queue
.push(function () {
var dom_element = form_gadget.props.element
var dom_element = form_gadget.element
.querySelector(".field_container");
while (dom_element.firstChild) {
dom_element.removeChild(dom_element.firstChild);
......@@ -233,6 +166,8 @@
});
})
.declareMethod("getListboxInfo", function () {
//XXXXX get listbox gadget's info
var gadget = this;
......
......@@ -230,7 +230,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>952.26028.18168.33945</string> </value>
<value> <string>954.24492.61471.3703</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -248,7 +248,7 @@
</tuple>
<state>
<tuple>
<float>1467906142.5</float>
<float>1475681076.93</float>
<string>UTC</string>
</tuple>
</state>
......
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<title>ERP5 Label Field</title>
<!-- renderjs -->
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<!-- custom script -->
<script src="gadget_erp5_label_field.js" type="text/javascript"></script>
</head>
<body>
<div class="ui-field-contain">
<label> <span></span></label>
<div></div>
</div>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Page" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Change_local_roles_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>gadget_erp5_label_field.html</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>rjs_gadget_erp5_label_field_html</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value> <string>en</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Web Page</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Gadget ERP5 Label Field</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>001</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>document_publication_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>edit_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>processing_status_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>publish_alive</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1475573362.92</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>published_alive</string> </value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>954.24197.36337.41506</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1475585351.18</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>detect_converted_file</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_processing_state</string> </key>
<value> <string>converted</string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>0.0.0.0</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1475570812.62</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
</ZopeData>
/*global window, rJS, RSVP */
/*jslint indent: 2, maxerr: 3 */
(function (window, rJS, RSVP) {
"use strict";
var SCOPE = 'field';
rJS(window)
.setState({
label_text: '',
error_text: '',
label: true
})
.ready(function () {
return this.changeState({
label_element: this.element.querySelector('label'),
label_text_element: this.element.querySelector('label').firstChild,
error_element: this.element.querySelector('span'),
container_element: this.element.querySelector('div')
});
})
.declareMethod('render', function (options) {
var state_dict = {
label_text: options.field_json.title || '',
label: options.label,
field_url: options.field_url,
error_text: options.field_json.error_text || '',
options: options,
scope: options.field_json.key
};
return this.changeState(state_dict);
})
.declareMethod('updateDOM', function (modification_dict) {
var gadget = this;
if (modification_dict.hasOwnProperty('label_text')) {
this.state.label_text_element.textContent = this.state.label_text;
}
this.state.label_element.setAttribute('for', gadget.state.scope);
if (modification_dict.hasOwnProperty('error_text')) {
if (this.state.error_text) {
this.state.error_element.textContent = " (" + this.state.error_text + ")";
} else {
this.state.error_element.textContent = "";
}
}
// Remove/add label_element from DOM
if (modification_dict.hasOwnProperty('label')) {
if (this.state.label === true) {
this.state.container_element.insertBefore(this.state.label_element, this.state.container_element.firstChild);
} else {
this.state.container_element.removeChild(this.state.label_element);
}
}
if (this.state.field_url) {
return new RSVP.Queue()
.push(function () {
if (modification_dict.hasOwnProperty('field_url')) {
return gadget.declareGadget(gadget.state.field_url, {
scope: SCOPE
})
.push(function (field_gadget) {
gadget.state.container_element.removeChild(
gadget.state.container_element.querySelector('div')
);
gadget.state.container_element.appendChild(field_gadget.element);
return field_gadget;
});
}
return gadget.getDeclaredGadget(SCOPE);
})
.push(function (field_gadget) {
return field_gadget.render(gadget.state.options);
});
}
})
.declareMethod("checkValidity", function () {
return this.getDeclaredGadget(SCOPE)
.push(function (gadget) {
// XXX Implement checkValidity on all fields
if (gadget.checkValidity !== undefined) {
return gadget.checkValidity();
}
return true;
});
})
.declareMethod('getContent', function () {
var argument_list = arguments;
return this.getDeclaredGadget(SCOPE)
.push(function (gadget) {
return gadget.getContent.apply(gadget, argument_list);
});
})
.declareMethod('getListboxInfo', function () {
var argument_list = arguments;
return this.getDeclaredGadget(SCOPE)
.push(function (gadget) {
return gadget.getListboxInfo.apply(gadget, argument_list);
});
})
.allowPublicAcquisition("notifyInvalid", function (param_list) {
return this.changeState({error_text: param_list[0]});
})
.allowPublicAcquisition("notifyValid", function () {
return this.changeState({error_text: ''});
});
}(window, rJS, RSVP));
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Script" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Change_local_roles_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>gadget_erp5_label_field.js</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>rjs_gadget_erp5_label_field_js</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value> <string>en</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Web Script</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Gadget ERP5 Label Field JS</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>001</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>document_publication_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>edit_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>processing_status_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>publish_alive</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1475573354.22</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>published_alive</string> </value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>954.25940.33315.42837</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1475678720.94</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>detect_converted_file</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_processing_state</string> </key>
<value> <string>converted</string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>0.0.0.0</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1475570812.65</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
</ZopeData>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment