Commit 7b6a679f authored by Roque's avatar Roque

erp5_web_monitoring: refactoring and fixes

- prepare import_export page to use multiple slapos-master urls (not fully implemented yet)
- update queries, views, forms and gadgets to include slapos-master-url
- drop gadget_erp5_jio, use jio gadget
- minor graph refactoring
- drop obsolete gadgets
- fix and extend sync redirect options
parent 07fcb8d5
...@@ -10,13 +10,14 @@ ...@@ -10,13 +10,14 @@
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
.declareAcquiredMethod("redirect", "redirect") .declareAcquiredMethod("redirect", "redirect")
.declareAcquiredMethod("getSetting", "getSetting") .declareAcquiredMethod("getSetting", "getSetting")
.declareAcquiredMethod("setSetting", "setSetting")
.declareAcquiredMethod("jio_allDocs", "jio_allDocs") .declareAcquiredMethod("jio_allDocs", "jio_allDocs")
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// declared methods // declared methods
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
.declareMethod("render", function (options) { .declareMethod("render", function (options) {
var gadget = this, portal_type, extended_search, query_parts_list; var gadget = this, portal_type, extended_search, query_parts_list, query_redirect;
if (options.query && options.url && options.username && options.password) { if (options.query && options.url && options.username && options.password) {
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
...@@ -25,33 +26,45 @@ ...@@ -25,33 +26,45 @@
}); });
}) })
.push(function (result) { .push(function (result) {
// URL queries from slapos master / panel rapid space:
// 'portal_type: "Instance Tree" AND title:"my-title"
// or
// 'portal_type: "Software Instance" AND title:"my-title"
// AND specialise_title:"my-parent-title"
query_parts_list = options.query.split('AND');
portal_type = query_parts_list[0].replace('portal_type:', '').replaceAll('"', '').trim();
extended_search = options.query.replace(query_parts_list[0] + 'AND ', '').trim();
if (options.slapos_master_url) {
// disambiguate equal instances by slapos master url
// TODO refactor this if slapos_id disambiguation way is choose
if (result !== undefined && result.data.total_rows > 1) {
extended_search += ' AND slapos_master_url:"' + options.slapos_master_url + '"';
}
}
query_redirect = {
"page": "ojs_local_controller",
"portal_type": portal_type + " Module",
"extended_search": extended_search
};
if (result === undefined || result.data.total_rows === 0) { if (result === undefined || result.data.total_rows === 0) {
return gadget.redirect( return gadget.setSetting('sync_redirect_options', query_redirect)
{"command": "display", .push(function (result) {
"options": { return gadget.redirect(
"url": options.url, {"command": "display",
"username": options.username, "options": {
"password": options.password, "url": options.url,
"page": "ojsm_opml_add" "username": options.username,
} "password": options.password,
"slapos_master_url": options.slapos_master_url || "not-provided",
"page": "ojsm_opml_add"
}
});
}); });
} else { } else {
// URL queries from slapos master / panel rapid space: return gadget.redirect({
// 'portal_type: "Instance Tree" AND title:"my-title"' "command": "display",
// or "options": query_redirect
// 'portal_type: "Software Instance" AND title:"my-title" });
// AND specialise_title:"my-parent-title"'
query_parts_list = options.query.split('AND');
portal_type = query_parts_list[0].replace('portal_type:', '').replaceAll('"', '').trim();
extended_search = options.query.replace(query_parts_list[0] + 'AND ', '').trim();
return gadget.redirect(
{"command": "display",
"options": {
"page": "ojs_local_controller",
"portal_type": portal_type + " Module",
"extended_search": extended_search
}
});
} }
}); });
} else if (options.url && options.username && options.password) { } else if (options.url && options.username && options.password) {
...@@ -61,6 +74,7 @@ ...@@ -61,6 +74,7 @@
"url": options.url, "url": options.url,
"username": options.username, "username": options.username,
"password": options.password, "password": options.password,
"slapos_master_url": options.slapos_master_url || "not-provided",
"page": "ojsm_opml_add" "page": "ojsm_opml_add"
} }
}); });
......
...@@ -246,7 +246,7 @@ ...@@ -246,7 +246,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1016.49507.50003.17561</string> </value> <value> <string>1017.31995.2932.55961</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -266,7 +266,7 @@ ...@@ -266,7 +266,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1716393876.23</float> <float>1719275229.13</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
rJS(window) rJS(window)
.setState({ .setState({
erp5_url: "https://panel.rapid.space/hateoas/" erp5_url_list: "https://panel.rapid.space/hateoas/"
}) })
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// Acquired methods // Acquired methods
...@@ -19,21 +19,21 @@ ...@@ -19,21 +19,21 @@
// Form submit // Form submit
///////////////////////////////////////// /////////////////////////////////////////
.onEvent('submit', function () { .onEvent('submit', function () {
var gadget = this, var gadget = this, i,
master_url; master_url_list;
return gadget.getDeclaredGadget('form_view') return gadget.getDeclaredGadget('form_view')
.push(function (form_gadget) { .push(function (form_gadget) {
return form_gadget.getContent(); return form_gadget.getContent();
}) })
.push(function (content) { .push(function (content) {
master_url = content.erp5_url; master_url_list = content.erp5_url_list.split(/\r?\n|\r|\n/g);
return gadget.setSetting("hateoas_url", master_url); for (i = 0; i < master_url_list.length; i += 1) {
}) master_url_list[i] = master_url_list[i].trim();
.push(function () { }
return gadget.redirect({command: "display", options: { return gadget.redirect({command: "display", options: {
page: "ojsm_import_export", page: "ojsm_import_export",
auto_sync: "erp5", auto_sync: "erp5",
url: master_url url_list: master_url_list
}}); }});
}); });
}) })
...@@ -47,19 +47,20 @@ ...@@ -47,19 +47,20 @@
return gadget.getDeclaredGadget('form_view') return gadget.getDeclaredGadget('form_view')
.push(function (form_gadget) { .push(function (form_gadget) {
//TODO replace textarea by N stringfield inputs
return form_gadget.render({ return form_gadget.render({
erp5_document: { erp5_document: {
"_embedded": {"_view": { "_embedded": {"_view": {
"my_erp5_url": { "my_erp5_url_list": {
"description": "", "description": "",
"title": "Connection Url", "title": "Connection Url List",
"default": gadget.state.erp5_url, "default": gadget.state.erp5_url_list,
"css_class": "", "css_class": "",
"required": 1, "required": 1,
"editable": 1, "editable": 1,
"key": "erp5_url", "key": "erp5_url_list",
"hidden": 0, "hidden": 0,
"type": "StringField" "type": "TextAreaField"
} }
}}, }},
"_links": { "_links": {
...@@ -72,7 +73,7 @@ ...@@ -72,7 +73,7 @@
form_definition: { form_definition: {
group_list: [[ group_list: [[
"top", "top",
[["my_erp5_url"]] [["my_erp5_url_list"]]
]] ]]
} }
}); });
......
...@@ -79,7 +79,9 @@ ...@@ -79,7 +79,9 @@
</item> </item>
<item> <item>
<key> <string>content_type</string> </key> <key> <string>content_type</string> </key>
<value> <string>text/javascript</string> </value> <value>
<none/>
</value>
</item> </item>
<item> <item>
<key> <string>default_reference</string> </key> <key> <string>default_reference</string> </key>
...@@ -244,7 +246,7 @@ ...@@ -244,7 +246,7 @@
</item> </item>
<item> <item>
<key> <string>actor</string> </key> <key> <string>actor</string> </key>
<value> <string>zope</string> </value> <value> <unicode>zope</unicode> </value>
</item> </item>
<item> <item>
<key> <string>comment</string> </key> <key> <string>comment</string> </key>
...@@ -258,7 +260,7 @@ ...@@ -258,7 +260,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>993.44733.59148.57105</string> </value> <value> <string>1016.60960.54632.28569</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -278,7 +280,7 @@ ...@@ -278,7 +280,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1627310085.03</float> <float>1718127515.73</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -63,6 +63,5 @@ ...@@ -63,6 +63,5 @@
<br/> <br/>
<div class="storage-list"> <div class="storage-list">
</div> </div>
<div data-gadget-url="gadget_erp5_jio.html" data-gadget-scope="erp5_gadget" data-gadget-sandbox="public"></div>
</body> </body>
</html> </html>
...@@ -236,7 +236,7 @@ ...@@ -236,7 +236,7 @@
</item> </item>
<item> <item>
<key> <string>actor</string> </key> <key> <string>actor</string> </key>
<value> <string>zope</string> </value> <value> <unicode>zope</unicode> </value>
</item> </item>
<item> <item>
<key> <string>comment</string> </key> <key> <string>comment</string> </key>
...@@ -250,7 +250,7 @@ ...@@ -250,7 +250,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>963.16563.15280.46899</string> </value> <value> <string>1017.31621.18655.52206</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -270,7 +270,7 @@ ...@@ -270,7 +270,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1509979675.96</float> <float>1719252800.71</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -335,22 +335,26 @@ ...@@ -335,22 +335,26 @@
if (limit === undefined) { if (limit === undefined) {
limit = 300; limit = 300;
} }
return gadget.state.erp5_gadget.allDocs({ return gadget.state.jio_gadget.allDocs({
query: '(portal_type:"Instance Tree") AND (validation_state:"validated")', query: '(portal_type:"Instance Tree") AND (validation_state:"validated")',
select_list: ['title', 'default_successor_uid', 'uid', 'slap_state'], select_list: ['title', 'default_successor_uid', 'uid', 'slap_state', 'id'],
limit: [0, limit], limit: [0, limit],
sort_on: [ sort_on: [
["creation_date", "descending"] ["creation_date", "descending"]
] ]
}) })
.push(function (result) { .push(function (result) {
var i, var i, slapos_id,
uid_search_list = []; uid_search_list = [];
for (i = 0; i < result.data.total_rows; i += 1) { for (i = 0; i < result.data.total_rows; i += 1) {
if (result.data.rows[i].value.slap_state !== "destroy_requested") { if (result.data.rows[i].value.slap_state !== "destroy_requested") {
//TODO slapos_id could be used to desambiguate identic title
//instances trees between different storages
slapos_id = result.data.rows[i].value.title;
instance_tree_list.push({ instance_tree_list.push({
title: result.data.rows[i].value.title, title: result.data.rows[i].value.title,
relative_url: result.data.rows[i].id, relative_url: result.data.rows[i].id,
slapos_id: slapos_id,
active: (result.data.rows[i].value.slap_state === active: (result.data.rows[i].value.slap_state ===
"start_requested") ? true : false, "start_requested") ? true : false,
state: (result.data.rows[i].value.slap_state === state: (result.data.rows[i].value.slap_state ===
...@@ -362,7 +366,7 @@ ...@@ -362,7 +366,7 @@
} }
} }
} }
return gadget.state.erp5_gadget.allDocs({ return gadget.state.jio_gadget.allDocs({
query: '(portal_type:"Software Instance") AND ' + query: '(portal_type:"Software Instance") AND ' +
'(successor_related_uid:("' + uid_search_list.join('","') + '"))', '(successor_related_uid:("' + uid_search_list.join('","') + '"))',
select_list: ['uid', 'successor_related_uid', 'connection_xml'], select_list: ['uid', 'successor_related_uid', 'connection_xml'],
...@@ -396,7 +400,8 @@ ...@@ -396,7 +400,8 @@
tmp_parameter.password), tmp_parameter.password),
active: tmp_parameter.opml_url !== undefined && active: tmp_parameter.opml_url !== undefined &&
instance_tree_list[uid_dict[tmp_uid]].active, instance_tree_list[uid_dict[tmp_uid]].active,
state: instance_tree_list[uid_dict[tmp_uid]].state state: instance_tree_list[uid_dict[tmp_uid]].state,
slapos_master_url: ""
}); });
} }
} }
...@@ -414,12 +419,12 @@ ...@@ -414,12 +419,12 @@
config: "", config: "",
is_export: false, is_export: false,
options: "", options: "",
erp5_gadget: "" jio_gadget: ""
}) })
.ready(function (g) { .ready(function (g) {
return g.getDeclaredGadget('erp5_gadget') return g.declareGadget('gadget_jio.html')
.push(function (erp5_gadget) { .push(function (jio_gadget) {
return g.changeState({erp5_gadget: erp5_gadget}); return g.changeState({jio_gadget: jio_gadget});
}); });
}) })
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
...@@ -455,12 +460,17 @@ ...@@ -455,12 +460,17 @@
}) })
.declareMethod("render", function (options) { .declareMethod("render", function (options) {
var gadget = this, var gadget = this, i,
is_exporter = options.exporter === "true", is_exporter = options.exporter === "true",
message_element = gadget.element.querySelector('.ui-message-alert'); message_element = gadget.element.querySelector('.ui-message-alert');
message_element.textContent = ""; message_element.textContent = "";
if (options.url && !options.url.endsWith('/')) { if (options.url_list) {
options.url += '/'; options.url_list = options.url_list.split(",");
for (i = 0; i < options.url_list.length; i += 1) {
if (!options.url_list[i].endsWith('/')) {
options.url_list[i] += '/';
}
}
} }
if (is_exporter) { if (is_exporter) {
return new RSVP.Queue() return new RSVP.Queue()
...@@ -484,7 +494,7 @@ ...@@ -484,7 +494,7 @@
config: "", config: "",
message: message_element, message: message_element,
sync: options.auto_sync, sync: options.auto_sync,
storage_url: options.url storage_url_list: options.url_list
}); });
}) })
.declareJob('deferChangeState', function deferStateChange(state) { .declareJob('deferChangeState', function deferStateChange(state) {
...@@ -588,15 +598,34 @@ ...@@ -588,15 +598,34 @@
}); });
}) })
.push(function () { .push(function () {
var has_failed = false; var has_failed = false, i;
if (gadget.state.sync === "erp5" && gadget.state.storage_url) { if (gadget.state.sync === "erp5" && gadget.state.storage_url_list) {
var storage_definition_list = [];
// start import from erp5 now // start import from erp5 now
return gadget.notifySubmitting() return gadget.notifySubmitting()
.push(function () { .push(function () {
return gadget.setSetting("hateoas_url", gadget.state.storage_url); gadget.getSetting('default_view_reference');
}) })
.push(function () { .push(function (default_view_reference) {
return gadget.state.erp5_gadget.createJio(); for (i = 0; i < gadget.state.storage_url_list.length; i += 1) {
storage_definition_list.push({
type: "erp5",
url: gadget.state.storage_url_list[i],
default_view_reference: default_view_reference
});
}
//TODO fix. union doesn't bring all elements (limit issue?)
/*return gadget.state.jio_gadget.createJio(
{
"type": "union",
"storage_list": storage_definition_list
}
);*/
return gadget.state.jio_gadget.createJio({
type: "erp5",
url: gadget.state.storage_url_list[0],
default_view_reference: default_view_reference
});
}) })
.push(function () { .push(function () {
return gadget.getSetting('opml_import_limit', 300); return gadget.getSetting('opml_import_limit', 300);
...@@ -609,7 +638,7 @@ ...@@ -609,7 +638,7 @@
.innerHTML = notify_msg_template({ .innerHTML = notify_msg_template({
status: 'error', status: 'error',
message: 'Error: Failed to get Monitor Configuration from URL: ' + message: 'Error: Failed to get Monitor Configuration from URL: ' +
gadget.state.storage_url gadget.state.storage_url_list[0]
}); });
has_failed = true; has_failed = true;
return []; return [];
...@@ -637,7 +666,7 @@ ...@@ -637,7 +666,7 @@
.innerHTML = notify_msg_template({ .innerHTML = notify_msg_template({
status: 'error', status: 'error',
message: 'An error occurred while saving Configuration from URL: ' + message: 'An error occurred while saving Configuration from URL: ' +
gadget.state.storage_url gadget.state.storage_url_list[0]
}); });
has_failed = true; has_failed = true;
}) })
......
...@@ -260,7 +260,7 @@ ...@@ -260,7 +260,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1017.21736.40219.1843</string> </value> <value> <string>1017.35724.42699.60194</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -280,7 +280,7 @@ ...@@ -280,7 +280,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1718659745.0</float> <float>1719499012.07</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -246,7 +246,7 @@ ...@@ -246,7 +246,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1016.618.22640.54749</string> </value> <value> <string>1016.7866.18952.22323</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -266,7 +266,7 @@ ...@@ -266,7 +266,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1713546953.85</float> <float>1714398308.5</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -244,7 +244,7 @@ ...@@ -244,7 +244,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1016.49474.6665.29303</string> </value> <value> <string>1016.49501.58649.51319</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -264,7 +264,7 @@ ...@@ -264,7 +264,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1716391830.68</float> <float>1717176193.02</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
.declareAcquiredMethod("updateHeader", "updateHeader") .declareAcquiredMethod("updateHeader", "updateHeader")
.declareAcquiredMethod("getSetting", "getSetting") .declareAcquiredMethod("getSetting", "getSetting")
.declareAcquiredMethod("setSetting", "setSetting")
.declareAcquiredMethod("getUrlFor", "getUrlFor") .declareAcquiredMethod("getUrlFor", "getUrlFor")
.declareAcquiredMethod("redirect", "redirect") .declareAcquiredMethod("redirect", "redirect")
.declareAcquiredMethod("notifySubmitting", "notifySubmitting") .declareAcquiredMethod("notifySubmitting", "notifySubmitting")
...@@ -72,17 +73,27 @@ ...@@ -72,17 +73,27 @@
"options": {page: "ojs_local_controller", "options": {page: "ojs_local_controller",
portal_type: "Promise Module"} portal_type: "Promise Module"}
}; };
if (gadget.state.auto_sync) { return gadget.getSetting('sync_redirect_options')
return gadget.getDeclaredGadget('sync_gadget') .push(function (sync_redirect_options) {
.push(function (sync_gadget) { if (sync_redirect_options) {
// start synchronization now redirect_options.options = sync_redirect_options;
return sync_gadget.register({now: true}); return gadget.setSetting("sync_redirect_options", undefined);
}) }
.push(function () { })
return gadget.redirect(redirect_options); .push(function () {
});
} if (gadget.state.auto_sync) {
return gadget.redirect(redirect_options); return gadget.getDeclaredGadget('sync_gadget')
.push(function (sync_gadget) {
// start synchronization now
return sync_gadget.register({now: true});
})
.push(function () {
return gadget.redirect(redirect_options);
});
}
return gadget.redirect(redirect_options);
});
} }
if (result_list[1].can_force) { if (result_list[1].can_force) {
gadget.element.getElementsByClassName("btn-nopasswd")[0] gadget.element.getElementsByClassName("btn-nopasswd")[0]
...@@ -146,6 +157,17 @@ ...@@ -146,6 +157,17 @@
"hidden": 0, "hidden": 0,
"type": "PasswordField" "type": "PasswordField"
}, },
"my_slapos_master_url": {
"description": "Slapos master URL",
"title": "Slapos master URL",
"default": options.slapos_master_url || "not-provided",
"css_class": "",
"required": 1,
"editable": 1,
"key": "slapos_master_url",
"hidden": 0,
"type": "StringField"
},
"my_portal_type": { "my_portal_type": {
"description": "The name of a document in ERP5", "description": "The name of a document in ERP5",
"title": "Portal Type", "title": "Portal Type",
...@@ -202,7 +224,7 @@ ...@@ -202,7 +224,7 @@
group_list: [[ group_list: [[
"left", "left",
[["my_url"], ["my_username"], ["my_password"], [["my_url"], ["my_username"], ["my_password"],
["my_portal_type"], ["my_active"], ["my_slapos_master_url"], ["my_portal_type"], ["my_active"],
["my_new_password"], ["my_confirm_new_password"]] ["my_new_password"], ["my_confirm_new_password"]]
]] ]]
} }
......
...@@ -260,7 +260,7 @@ ...@@ -260,7 +260,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1016.46878.65388.21691</string> </value> <value> <string>1017.7024.38172.32733</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -280,7 +280,7 @@ ...@@ -280,7 +280,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1716236126.38</float> <float>1717777119.89</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -79,7 +79,9 @@ ...@@ -79,7 +79,9 @@
</item> </item>
<item> <item>
<key> <string>content_type</string> </key> <key> <string>content_type</string> </key>
<value> <string>text/javascript</string> </value> <value>
<none/>
</value>
</item> </item>
<item> <item>
<key> <string>default_reference</string> </key> <key> <string>default_reference</string> </key>
...@@ -244,7 +246,7 @@ ...@@ -244,7 +246,7 @@
</item> </item>
<item> <item>
<key> <string>actor</string> </key> <key> <string>actor</string> </key>
<value> <string>zope</string> </value> <value> <unicode>zope</unicode> </value>
</item> </item>
<item> <item>
<key> <string>comment</string> </key> <key> <string>comment</string> </key>
...@@ -258,7 +260,7 @@ ...@@ -258,7 +260,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>963.27666.8050.30907</string> </value> <value> <string>1016.7854.16646.24046</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -278,7 +280,7 @@ ...@@ -278,7 +280,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1510581396.39</float> <float>1713981022.38</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
<string>Associate</string> <string>Associate</string>
<string>Auditor</string> <string>Auditor</string>
<string>Manager</string> <string>Manager</string>
<string>Owner</string>
</tuple> </tuple>
</value> </value>
</item> </item>
...@@ -24,6 +23,7 @@ ...@@ -24,6 +23,7 @@
<key> <string>_Add_portal_content_Permission</string> </key> <key> <string>_Add_portal_content_Permission</string> </key>
<value> <value>
<tuple> <tuple>
<string>Assignee</string>
<string>Assignor</string> <string>Assignor</string>
<string>Manager</string> <string>Manager</string>
</tuple> </tuple>
...@@ -42,6 +42,8 @@ ...@@ -42,6 +42,8 @@
<key> <string>_Modify_portal_content_Permission</string> </key> <key> <string>_Modify_portal_content_Permission</string> </key>
<value> <value>
<tuple> <tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string> <string>Manager</string>
</tuple> </tuple>
</value> </value>
...@@ -56,7 +58,6 @@ ...@@ -56,7 +58,6 @@
<string>Associate</string> <string>Associate</string>
<string>Auditor</string> <string>Auditor</string>
<string>Manager</string> <string>Manager</string>
<string>Owner</string>
</tuple> </tuple>
</value> </value>
</item> </item>
...@@ -172,11 +173,11 @@ ...@@ -172,11 +173,11 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>action</string> </key> <key> <string>action</string> </key>
<value> <string>publish</string> </value> <value> <string>publish_alive</string> </value>
</item> </item>
<item> <item>
<key> <string>actor</string> </key> <key> <string>actor</string> </key>
<value> <string>zope</string> </value> <value> <unicode>zope</unicode> </value>
</item> </item>
<item> <item>
<key> <string>comment</string> </key> <key> <string>comment</string> </key>
...@@ -200,7 +201,7 @@ ...@@ -200,7 +201,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1454520181.06</float> <float>1717179451.17</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
...@@ -209,7 +210,7 @@ ...@@ -209,7 +210,7 @@
</item> </item>
<item> <item>
<key> <string>validation_state</string> </key> <key> <string>validation_state</string> </key>
<value> <string>published</string> </value> <value> <string>published_alive</string> </value>
</item> </item>
</dictionary> </dictionary>
</list> </list>
...@@ -249,7 +250,7 @@ ...@@ -249,7 +250,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1016.32493.13517.11946</string> </value> <value> <string>1016.62600.58488.20736</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -269,7 +270,7 @@ ...@@ -269,7 +270,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1715373485.61</float> <float>1717179443.83</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1015.60575.57531.48776</string> </value> <value> <string>1016.39499.37502.35754</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -272,7 +272,7 @@ ...@@ -272,7 +272,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1713212157.56</float> <float>1717176362.9</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Monitoring Promises Status</title>
<link href="gadget_officejs_monitoring_custom.css" rel="stylesheet" type="text/css"/>
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<script src="gadget_erp5_page_ojsm_status_list.js"></script>
</head>
<body>
<div data-gadget-url="gadget_erp5_pt_form_list.html" data-gadget-scope="form_list" data-gadget-sandbox="public"></div>
</body>
</html>
/*global window, rJS, RSVP */
/*jslint nomen: true, indent: 2, maxerr: 3*/
(function (window, rJS, RSVP) {
"use strict";
rJS(window)
.declareAcquiredMethod("updateHeader", "updateHeader")
.declareAcquiredMethod("redirect", "redirect")
.declareAcquiredMethod("getSetting", "getSetting")
.declareAcquiredMethod("setSetting", "setSetting")
.declareAcquiredMethod("jio_allDocs", "jio_allDocs")
.allowPublicAcquisition("jio_allDocs", function (param_list) {
var gadget = this;
return gadget.jio_allDocs(param_list[0])
.push(function (result) {
var i, j, tmp, value, len = result.data.total_rows;
for (i = 0; i < len; i += 1) {
if (result.data.rows[i].value.hasOwnProperty("lastBuildDate")) {
result.data.rows[i].value.lastBuildDate = {
field_gadget_param: {
//allow_empty_time: 0,
//ampm_time_style: 0,
css_class: "date_field",
date_only: 0,
description: "The Date",
editable: 0,
hidden: 0,
hidden_day_is_last_day: 0,
"default": result.data.rows[i].value.lastBuildDate,
key: "lastBuildDate",
required: 0,
timezone_style: 1,
title: "Promise Date",
type: "DateTimeField"
}
};
result.data.rows[i].value["listbox_uid:list"] = {
key: "listbox_uid:list",
value: 2713
};
}
if (result.data.rows[i].value.hasOwnProperty("description")) {
tmp = result.data.rows[i].value.description.split('\n');
value = "";
for (j = 1; j < tmp.length; j += 1) {
// first line of text is the date and status
if (!value && tmp[j].trim() !== "") {
value += tmp[j].slice(0, 50);
if (tmp[j].length >= 50 || j + 1 < tmp.length) {
// a part of text is not shown
value += "...";
}
}
}
result.data.rows[i].value.description = value;
}
if (result.data.rows[i].value.hasOwnProperty("category")) {
value = result.data.rows[i].value.category;
result.data.rows[i].value.category = {
field_gadget_param: {
css_class: "",
description: "The Status",
hidden: 0,
"default": value,
key: "category",
url: "gadget_erp5_field_status.html",
title: "Status",
type: "GadgetField"
}
};
result.data.rows[i].value["listbox_uid:list"] = {
key: "listbox_uid:list",
value: 2713
};
}
}
return result;
});
})
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
.declareMethod("triggerSubmit", function () {
var argument_list = arguments;
return this.getDeclaredGadget('form_list')
.push(function (gadget) {
return gadget.triggerSubmit.apply(gadget, argument_list);
});
})
.declareMethod("render", function (options) {
return this.changeState({
options: options,
latest_reload_time: new Date().getTime()
});
})
.onStateChange(function () {
var gadget = this,
lines_limit;
return new RSVP.Queue()
.push(function () {
return gadget.getSetting("latest_sync_time");
})
.push(function (latest_sync_time) {
if (latest_sync_time === undefined) {
// no sync yet
return gadget.redirect({command: "display", options: {page: "ojsm_import_export"}});
}
})
.push(function () {
return gadget.getSetting("listbox_lines_limit", 20);
})
.push(function (listbox_lines_limit) {
lines_limit = listbox_lines_limit;
return gadget.getDeclaredGadget('form_list');
})
.push(function (form_list) {
var column_list = [
['category', 'Status'],
['source', 'Promise'],
['channel_item', 'Software Instance'],
['channel', 'Instance Tree'],
['lastBuildDate', 'Promise Date'],
['description', 'Message']
];
return form_list.render({
erp5_document: {
"_embedded": {"_view": {
"listbox": {
"column_list": column_list,
"show_anchor": 0,
"default_params": {},
"editable": 0,
"editable_column_list": [],
"key": "field_listbox",
"lines": lines_limit,
"list_method": "portal_catalog",
"query": "urn:jio:allDocs?query=portal_type%3A%22" +
"promise" + "%22",
"portal_type": [],
"search_column_list": column_list,
"sort_column_list": column_list,
"sort": [["category", "ascending"], ["channel", "ascending"]],
"title": "Monitoring Promises",
"type": "ListBox"
}
}},
"_links": {
"type": {
// form_list display portal_type in header
name: ""
}
}
},
form_definition: {
group_list: [[
"bottom",
[["listbox"]]
]]
}
});
})
.push(function () {
return gadget.updateHeader({
page_title: "Monitoring Promises Status",
filter_action: true
});
});
})
.onLoop(function () {
var gadget = this;
return gadget.getSetting('latest_sync_time')
.push(function (latest_sync_time) {
if (latest_sync_time > gadget.state.latest_reload_time) {
return gadget.changeState({latest_reload_time: new Date().getTime()});
}
});
}, 30000);
}(window, rJS, RSVP));
...@@ -260,7 +260,7 @@ ...@@ -260,7 +260,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1014.27855.54097.10478</string> </value> <value> <string>1017.6962.52907.14592</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -280,7 +280,7 @@ ...@@ -280,7 +280,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1707263598.02</float> <float>1717773952.0</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -246,7 +246,7 @@ ...@@ -246,7 +246,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1016.27838.64215.37273</string> </value> <value> <string>1017.1125.9926.47069</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -266,7 +266,7 @@ ...@@ -266,7 +266,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1716236037.63</float> <float>1717423120.28</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -79,7 +79,9 @@ ...@@ -79,7 +79,9 @@
</item> </item>
<item> <item>
<key> <string>content_type</string> </key> <key> <string>content_type</string> </key>
<value> <string>text/javascript</string> </value> <value>
<none/>
</value>
</item> </item>
<item> <item>
<key> <string>default_reference</string> </key> <key> <string>default_reference</string> </key>
...@@ -236,7 +238,7 @@ ...@@ -236,7 +238,7 @@
</item> </item>
<item> <item>
<key> <string>actor</string> </key> <key> <string>actor</string> </key>
<value> <string>zope</string> </value> <value> <unicode>zope</unicode> </value>
</item> </item>
<item> <item>
<key> <string>comment</string> </key> <key> <string>comment</string> </key>
...@@ -250,7 +252,7 @@ ...@@ -250,7 +252,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>963.27666.8050.30907</string> </value> <value> <string>1015.45861.47176.27204</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -270,7 +272,7 @@ ...@@ -270,7 +272,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1542277962.83</float> <float>1712329323.76</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -250,7 +250,7 @@ ...@@ -250,7 +250,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1016.40875.18813.28279</string> </value> <value> <string>1016.40876.2382.12151</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -270,7 +270,7 @@ ...@@ -270,7 +270,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1715875922.12</float> <float>1715958907.55</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -249,7 +249,7 @@ ...@@ -249,7 +249,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1015.32779.62576.10939</string> </value> <value> <string>1016.59484.28533.58572</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -269,7 +269,7 @@ ...@@ -269,7 +269,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1711544405.46</float> <float>1717176711.0</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -240,7 +240,7 @@ ...@@ -240,7 +240,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1015.63271.15903.46711</string> </value> <value> <string>1015.63271.39533.51643</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -260,7 +260,7 @@ ...@@ -260,7 +260,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1713373896.11</float> <float>1714059517.01</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -91,8 +91,7 @@ ...@@ -91,8 +91,7 @@
}}), }}),
gadget.getUrlFor({command: 'change', options: { gadget.getUrlFor({command: 'change', options: {
page: 'ojsm_opml_delete', page: 'ojsm_opml_delete',
jio_key: page_options.jio_key, jio_key: page_options.jio_key
return_url: 'settings_configurator'
}}) }})
]); ]);
}) })
......
...@@ -242,7 +242,7 @@ ...@@ -242,7 +242,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1016.7694.22139.25207</string> </value> <value> <string>1016.62553.1098.47598</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -262,7 +262,7 @@ ...@@ -262,7 +262,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1713971424.43</float> <float>1717176544.51</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1014.13373.21627.45107</string> </value> <value> <string>1016.46870.32443.18705</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -272,7 +272,7 @@ ...@@ -272,7 +272,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1706621148.81</float> <float>1716235972.95</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -16,8 +16,69 @@ ...@@ -16,8 +16,69 @@
.declareMethod("handle_submit", function (argument_list, options) { .declareMethod("handle_submit", function (argument_list, options) {
switch (options.options.portal_type) { switch (options.options.portal_type) {
case "Instance Tree": case "Instance Tree":
//XXX do the old parameter gadget save here and fix it //TODO do the old parameter gadget save here and fix it
return this.redirect({command: 'reload'}); return this.redirect({command: 'reload'});
case "Opml":
console.log("opml submit argument_list, options:", argument_list, options);
//TODO fix submit
var gadget = this,
doc,
opml_gadget;
return new RSVP.Queue()
.push(function () {
return gadget.getDeclaredGadget('opml_gadget');
})
.push(function (g) {
opml_gadget = g;
return gadget.getDeclaredGadget('form_view');
})
.push(function (form_gadget) {
return form_gadget.getContent();
})
.push(function (form_doc) {
doc = form_doc;
if (doc.password !== gadget.state.password) {
// password was modified, update on backend
doc.new_password = doc.password;
doc.confirm_new_password = doc.new_password;
doc.password = gadget.state.password;
doc.verify_password = 1;
} else {
doc.verify_password = (doc.verify_password === "on") ? 1 : 0;
}
return opml_gadget.checkOPMLForm(doc);
})
.push(function (state) {
if (state) {
return gadget.notifySubmitting()
.push(function () {
var verify_opml = doc.title === "" || doc.title === undefined ||
doc.verify_password === 1;
if (gadget.state.active === false && doc.active === "on") {
verify_opml = true;
}
doc.title = gadget.state.opml_title;
return opml_gadget.saveOPML(doc, verify_opml);
})
.push(function (state) {
var msg = {message: 'Document Updated', status: 'success'};
if (!state.status) {
msg = {message: 'Document update failed', status: "error"};
}
return RSVP.all([
gadget.notifySubmitted(msg),
state
]);
})
.push(function (result) {
if (result[1].status) {
return gadget.changeState({
"password": doc.password
});
}
});
}
});
default: default:
return this.redirect({command: 'reload'}); return this.redirect({command: 'reload'});
} }
......
...@@ -242,7 +242,7 @@ ...@@ -242,7 +242,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1016.16493.14116.1297</string> </value> <value> <string>1017.1223.8753.47035</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -262,7 +262,7 @@ ...@@ -262,7 +262,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1714499837.65</float> <float>1717428914.0</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
} }
return gadget.getDeclaredGadget('graph_gadget') return gadget.getDeclaredGadget('graph_gadget')
.push(function (graph_gadget) { .push(function (graph_gadget) {
data.new_format = true;
return graph_gadget.render({value: data}); return graph_gadget.render({value: data});
}); });
} }
......
...@@ -246,7 +246,7 @@ ...@@ -246,7 +246,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1016.32237.19427.7099</string> </value> <value> <string>1016.58116.62606.9608</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -266,7 +266,7 @@ ...@@ -266,7 +266,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1715358713.84</float> <float>1716991956.32</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -244,7 +244,7 @@ ...@@ -244,7 +244,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1016.27131.47749.27904</string> </value> <value> <string>1016.27890.23432.33160</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -264,7 +264,7 @@ ...@@ -264,7 +264,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1715051292.75</float> <float>1715096834.79</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -232,7 +232,7 @@ ...@@ -232,7 +232,7 @@
</item> </item>
<item> <item>
<key> <string>actor</string> </key> <key> <string>actor</string> </key>
<value> <string>zope</string> </value> <value> <unicode>zope</unicode> </value>
</item> </item>
<item> <item>
<key> <string>comment</string> </key> <key> <string>comment</string> </key>
...@@ -246,7 +246,7 @@ ...@@ -246,7 +246,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>963.7950.43708.60125</string> </value> <value> <string>1015.24522.26496.3908</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -266,7 +266,7 @@ ...@@ -266,7 +266,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1509446414.84</float> <float>1711049003.02</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -161,6 +161,10 @@ ...@@ -161,6 +161,10 @@
<string>specialise_title</string> <string>specialise_title</string>
<string>title</string> <string>title</string>
</tuple> </tuple>
<tuple>
<string>slapos_master_url</string>
<string>slapos_master_url</string>
</tuple>
</list> </list>
</value> </value>
</item> </item>
......
...@@ -165,6 +165,10 @@ ...@@ -165,6 +165,10 @@
<string>channel_item</string> <string>channel_item</string>
<string>title</string> <string>title</string>
</tuple> </tuple>
<tuple>
<string>slapos_master_url</string>
<string>slapos_master_url</string>
</tuple>
</list> </list>
</value> </value>
</item> </item>
......
...@@ -89,7 +89,6 @@ url_list = [ ...@@ -89,7 +89,6 @@ url_list = [
"gadget_officejs_monitoring_sync.html", "gadget_officejs_monitoring_sync.html",
"gadget_officejs_monitoring_router.html", "gadget_officejs_monitoring_router.html",
"gadget_erp5_page_ojsm_import_export.html", "gadget_erp5_page_ojsm_import_export.html",
"gadget_officejs_jio_opml_view.html",
"gadget_erp5_page_ojsm_opml_add.html", "gadget_erp5_page_ojsm_opml_add.html",
"gadget_officejs_monitoring_panel.html", "gadget_officejs_monitoring_panel.html",
"gadget_erp5_page_ojsm_resources_view.html", "gadget_erp5_page_ojsm_resources_view.html",
...@@ -114,7 +113,6 @@ url_list = [ ...@@ -114,7 +113,6 @@ url_list = [
"gadget_officejs_monitoring_header.js", "gadget_officejs_monitoring_header.js",
"gadget_erp5_page_ojsm_opml_add.js", "gadget_erp5_page_ojsm_opml_add.js",
"gadget_erp5_page_ojsm_opml_delete.js", "gadget_erp5_page_ojsm_opml_delete.js",
"gadget_officejs_jio_opml_view.js",
"gadget_erp5_page_ojsm_synchronize.js", "gadget_erp5_page_ojsm_synchronize.js",
"gadget_erp5_page_ojsm_jump.js", "gadget_erp5_page_ojsm_jump.js",
"gadget_erp5_page_ojsm_erp5_configurator.js", "gadget_erp5_page_ojsm_erp5_configurator.js",
......
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