Commit 3c43b7db authored by iv's avatar iv Committed by Romain Courteaud

OfficeJS drive: Start ERP5 support, switch to 'enclosure' attachment.

parent a5291ef8
...@@ -118,6 +118,9 @@ ...@@ -118,6 +118,9 @@
\n \n
<!-- custom script -->\n <!-- custom script -->\n
<script src="gadget_officejs_drive_jio.js" type="text/javascript"></script>\n <script src="gadget_officejs_drive_jio.js" type="text/javascript"></script>\n
\n
<!-- temporary file, to be included into jIO once clean and tested -->\n
<script src="gadget_officejs_drive_jio_superstorage.js" type="text/javascript"></script>\n
\n \n
</head>\n </head>\n
<body>\n <body>\n
...@@ -259,7 +262,7 @@ ...@@ -259,7 +262,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>948.8664.63043.42717</string> </value> <value> <string>949.758.22453.57804</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -277,7 +280,7 @@ ...@@ -277,7 +280,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1451481186.3</float> <float>1456159009.84</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -101,9 +101,9 @@ ...@@ -101,9 +101,9 @@
<key> <string>text_content</string> </key> <key> <string>text_content</string> </key>
<value> <string encoding="cdata"><![CDATA[ <value> <string encoding="cdata"><![CDATA[
/*global window, rJS, jIO, XMLHttpRequestProgressEvent, UriTemplate */\n /*global window, rJS, jIO, alert, XMLHttpRequestProgressEvent, UriTemplate */\n
/*jslint indent: 2, maxerr: 3 */\n /*jslint indent: 2, maxerr: 3 */\n
(function (window, rJS, jIO, XMLHttpRequestProgressEvent, UriTemplate) {\n (function (window, rJS, jIO, alert, XMLHttpRequestProgressEvent, UriTemplate) {\n
"use strict";\n "use strict";\n
\n \n
// jIO call wrapper for redirection to authentication page if needed\n // jIO call wrapper for redirection to authentication page if needed\n
...@@ -113,10 +113,10 @@ ...@@ -113,10 +113,10 @@
.push(undefined, function (error) {\n .push(undefined, function (error) {\n
if (error instanceof XMLHttpRequestProgressEvent &&\n if (error instanceof XMLHttpRequestProgressEvent &&\n
error.target.status === 401) {\n error.target.status === 401) {\n
if (gadget.state_parameter_dict.jio_storage_name === "ERP5") {\n if (gadget.state_parameter_dict.jio_storage_name === "erp5") {\n
return gadget.redirect({ page: "login" });\n return gadget.redirect({ page: "login" });\n
}\n }\n
if (gadget.state_parameter_dict.jio_storage_name === "DAV") {\n if (gadget.state_parameter_dict.jio_storage_name === "dav") {\n
var regexp = /^Nayookie login_url=(http[s]?:\\/\\/[\\/\\-\\[\\]{}()*+=:?&.,\\\\\\^$|#\\s\\w%]+)$/,\n var regexp = /^Nayookie login_url=(http[s]?:\\/\\/[\\/\\-\\[\\]{}()*+=:?&.,\\\\\\^$|#\\s\\w%]+)$/,\n
auth_page = error.target.getResponseHeader(\'WWW-Authenticate\'),\n auth_page = error.target.getResponseHeader(\'WWW-Authenticate\'),\n
site;\n site;\n
...@@ -126,9 +126,20 @@ ...@@ -126,9 +126,20 @@
).expand({back_url: window.location.href,\n ).expand({back_url: window.location.href,\n
origin: window.location.protocol + \'//\' +\n origin: window.location.protocol + \'//\' +\n
window.location.host});\n window.location.host});\n
return gadget.redirect({ toExternal: true, url: site});\n return gadget.redirect({ toExternal: true, url: site });\n
}\n }\n
}\n }\n
} else if (gadget.state_parameter_dict.jio_storage_name === "dav" &&\n
error instanceof XMLHttpRequestProgressEvent &&\n
error.target.status === 0) {\n
// XXX: need more precision, not all errors with 0 status should be redirected...\n
alert("Unable to access the WebDAV server. It may have an invalid" +\n
" SSL certificate, or is just not running.\\n" +\n
"You will be redirected to this server...");\n
return gadget.redirect({ toExternal: true,\n
url: gadget.state_parameter_dict.jio_storage_url +\n
\'/../redirect?back_url=\' + window.location.href\n
});\n
}\n }\n
throw error;\n throw error;\n
});\n });\n
...@@ -139,21 +150,23 @@ ...@@ -139,21 +150,23 @@
.ready(function (gadget) {\n .ready(function (gadget) {\n
// Initialize the gadget local parameters\n // Initialize the gadget local parameters\n
// XXX Hardcoded\n // XXX Hardcoded\n
gadget.state_parameter_dict = {jio_storage_name: "DAV"};\n gadget.state_parameter_dict = {jio_storage_name: "dav", // "erp5"\n
jio_storage_url: "https://localhost:5000/webdav"}; // for ERP5: <instance>/web_site_module/hateoas/\n
})\n })\n
\n \n
.declareAcquiredMethod("redirect", "redirect")\n .declareAcquiredMethod("redirect", "redirect")\n
.declareAcquiredMethod("getSetting", "getSetting")\n .declareAcquiredMethod("getSetting", "getSetting")\n
\n \n
.declareMethod(\'createJio\', function (jio_options) {\n .declareMethod(\'createJio\', function (jio_options) {\n
if (jio_options !== \'XXX couscous\') {\n
jio_options = {\n jio_options = {\n
type: "dav",\n type: \'daverp5mapping\',\n
// XXX Hardcoded\n sub_storage: {\n
url: "https://localhost:5000/webdav",\n type: this.state_parameter_dict.jio_storage_name,\n
with_credentials: true\n url: this.state_parameter_dict.jio_storage_url,\n
};\n with_credentials: true, // webdav\n
default_view_reference: \'view\' // erp5\n
}\n }\n
};\n
this.state_parameter_dict.jio_storage = jIO.createJIO(jio_options);\n this.state_parameter_dict.jio_storage = jIO.createJIO(jio_options);\n
})\n })\n
.declareMethod(\'allDocs\', function () {\n .declareMethod(\'allDocs\', function () {\n
...@@ -187,7 +200,7 @@ ...@@ -187,7 +200,7 @@
return wrapJioCall(this, \'repair\', arguments);\n return wrapJioCall(this, \'repair\', arguments);\n
});\n });\n
\n \n
}(window, rJS, jIO, XMLHttpRequestProgressEvent, UriTemplate)); }(window, rJS, jIO, alert, XMLHttpRequestProgressEvent, UriTemplate));
]]></string> </value> ]]></string> </value>
</item> </item>
...@@ -324,7 +337,7 @@ ...@@ -324,7 +337,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>948.36185.35957.25497</string> </value> <value> <string>949.20746.21257.48042</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -342,7 +355,7 @@ ...@@ -342,7 +355,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1453132424.59</float> <float>1456138293.9</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -111,9 +111,9 @@ ...@@ -111,9 +111,9 @@
<key> <string>text_content</string> </key> <key> <string>text_content</string> </key>
<value> <string encoding="cdata"><![CDATA[ <value> <string encoding="cdata"><![CDATA[
/*globals window, rJS, RSVP, loopEventListener, document */\n /*globals window, rJS, RSVP, jIO, loopEventListener, document */\n
/*jslint indent: 2, nomen: true, maxlen: 80*/\n /*jslint indent: 2, nomen: true, maxlen: 80*/\n
(function (window, rJS, RSVP, loopEventListener, document) {\n (function (window, rJS, RSVP, jIO, loopEventListener, document) {\n
"use strict";\n "use strict";\n
\n \n
var gadget_klass = rJS(window);\n var gadget_klass = rJS(window);\n
...@@ -127,7 +127,9 @@ ...@@ -127,7 +127,9 @@
});\n });\n
})\n })\n
\n \n
.declareAcquiredMethod("jio_allAttachments", "jio_allAttachments")\n .declareAcquiredMethod("jio_getAttachment", "jio_getAttachment")\n
.declareAcquiredMethod("jio_allDocs", "jio_allDocs")\n
\n
.declareAcquiredMethod("redirect", "redirect")\n .declareAcquiredMethod("redirect", "redirect")\n
\n \n
.declareMethod("parse", function (text) {\n .declareMethod("parse", function (text) {\n
...@@ -137,13 +139,12 @@ ...@@ -137,13 +139,12 @@
index;\n index;\n
// command is the first token\n // command is the first token\n
command = args.shift();\n command = args.shift();\n
index = args.length;\n
\n \n
/* begin from the end because removing some values from the list\n /* begin from the end because removing some values from the list\n
while looping */\n while looping */\n
while (index--) {\n for (index = args.length ; index > 0 ; index --) {\n
// only keep useful args (no options, no emtpy string)\n // remove emply strings from argument list\n
if (args[index] === \'\' || args[index].startsWith(\'-\')) {\n if (args[index] === \'\') {\n
args.splice(index, 1);\n args.splice(index, 1);\n
}\n }\n
}\n }\n
...@@ -151,45 +152,59 @@ ...@@ -151,45 +152,59 @@
})\n })\n
\n \n
.declareMethod("browse", function (command, args) {\n .declareMethod("browse", function (command, args) {\n
var gadget = this;\n var gadget = this,\n
requireSingleArgError = new Error(\n
\'This command requires one argument.\'\n
);\n
\n \n
function absolutePosition(current, requested) {\n function absolutePosition(current, requested) {\n
var pos;\n var pos;\n
if (requested.startsWith(\'/\')) {\n if (requested.startsWith(\'/\')) {\n
pos = \'/\' + requested + \'/\';\n pos = \'/\' + requested;\n
} else {\n } else {\n
pos = \'/\' + current + \'/\' + requested + \'/\';\n pos = current + \'/\' + requested;\n
}\n }\n
return pos.replace(/\\/+/g, \'/\');\n return pos.replace(/\\/+/g, \'/\');\n
}\n }\n
\n
// if command given: proceed\n // if command given: proceed\n
\n
if (command) {\n if (command) {\n
try {\n try {\n
switch (command) {\n switch (command) {\n
case \'cd\':\n case \'cd\':\n
if (args.length === 1) {\n if (args.length === 1) {\n
return gadget.redirect({\n return gadget.redirect({\n
position: absolutePosition(gadget.props.currentPosition, args[0])\n position: absolutePosition(gadget.props.currentPosition,\n
args[0] + \'/\')\n
});\n });\n
}\n }\n
throw new Error(\'Usage of \' + command + \' requires a single argument.\');\n throw requireSingleArgError;\n
\n \n
case \'vim\':\n case \'vim\':\n
case \'vi\':\n case \'vi\':\n
var position = args[0].split(\'/\'),\n
resource = position.pop();\n
if (args.length === 1) {\n if (args.length === 1) {\n
return gadget.redirect({\n return gadget.redirect({\n
page: \'edit\',\n page: \'edit\',\n
position: absolutePosition(\n resource: absolutePosition(gadget.props.currentPosition,\n
args[0]),\n
back: gadget.props.currentPosition\n
});\n
}\n
throw requireSingleArgError;\n
case \'share\':\n
if (args.length === 1) {\n
return gadget.jio_getAttachment(absolutePosition(\n
gadget.props.currentPosition,\n gadget.props.currentPosition,\n
position.join(\'/\')\n args[0]\n
),\n ), \'enclosure\')\n
resource: resource\n .push(function (resp) {\n
return jIO.util.readBlobAsDataURL(resp);\n
})\n
.push(function (e) {\n
gadget.props.element.querySelector(\'.output\').textContent = e.target.result;\n
});\n });\n
}\n }\n
throw new Error(\'Usage of \' + command + \' requires a single argument.\');\n throw requireSingleArgError;\n
default:\n default:\n
throw new Error(\'Unknown command: \' + command);\n throw new Error(\'Unknown command: \' + command);\n
}\n }\n
...@@ -218,23 +233,27 @@ ...@@ -218,23 +233,27 @@
while (ul.hasChildNodes()) {\n while (ul.hasChildNodes()) {\n
ul.removeChild(ul.firstChild);\n ul.removeChild(ul.firstChild);\n
}\n }\n
\n return gadget.jio_allDocs({id: options.position})\n
return gadget.jio_allAttachments(options.position)\n
.push(function (all) {\n .push(function (all) {\n
var key,\n var key,\n
li,\n li,\n
id,\n
liContent,\n liContent,\n
resourceName;\n resourceName;\n
for (key in all) {\n for (key in all.data.rows) {\n
if (all.hasOwnProperty(key)) {\n if (all.data.rows.hasOwnProperty(key)) {\n
id = all.data.rows[key].value.id;\n
li = document.createElement(\'li\');\n li = document.createElement(\'li\');\n
resourceName = document.createTextNode(key);\n resourceName = document.createTextNode(id);\n
if (key.endsWith(\'.txt\') || key.endsWith(\'.js\') ||\n \n
key.endsWith(\'.html\') || key.endsWith(\'.py\')) {\n if (id.endsWith(\'.txt\') || id.endsWith(\'.js\') ||\n
id.endsWith(\'.html\') || id.endsWith(\'.py\') ||\n
id.endsWith(\'_js\')) {\n
liContent = document.createElement(\'a\');\n liContent = document.createElement(\'a\');\n
\n \n
liContent.setAttribute(\'href\', \'#page=edit&resource=\' +\n liContent.setAttribute(\'href\', \'#page=edit&resource=\' +\n
key + \'&position=\' + options.position);\n [options.position, id].join(\'/\') +\n
\'&back=\' + options.position);\n
liContent.appendChild(resourceName);\n liContent.appendChild(resourceName);\n
} else {\n } else {\n
liContent = resourceName;\n liContent = resourceName;\n
...@@ -268,7 +287,7 @@ ...@@ -268,7 +287,7 @@
});\n });\n
});\n });\n
\n \n
}(window, rJS, RSVP, loopEventListener, document)); }(window, rJS, RSVP, jIO, loopEventListener, document));
]]></string> </value> ]]></string> </value>
</item> </item>
...@@ -407,7 +426,7 @@ ...@@ -407,7 +426,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>948.41948.13894.58197</string> </value> <value> <string>949.20889.54493.42188</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -425,7 +444,7 @@ ...@@ -425,7 +444,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1453478465.02</float> <float>1456146835.69</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -127,18 +127,22 @@ ...@@ -127,18 +127,22 @@
<script src="renderjs.js"></script>\n <script src="renderjs.js"></script>\n
<script src="handlebars.js"></script>\n <script src="handlebars.js"></script>\n
<script src="gadget_officejs_drive_page_browse.js"></script>\n <script src="gadget_officejs_drive_page_browse.js"></script>\n
\n
\n \n
</head>\n </head>\n
\n \n
<body>\n <body>\n
<h1>Super OfficeJS Drive</h1>\n <h1>Super OfficeJS Drive</h1>\n
\n
<form>\n <form>\n
<input type="text">\n <input type="text">\n
</form>\n </form>\n
<div><a class="error"></a></div>\n <div>\n
<ul>\n <a class="error"></a>\n
</ul>\n <p class="output"></p>\n
</div>\n
<ul></ul>\n
\n
\n
</body>\n </body>\n
</html>\n </html>\n
...@@ -280,7 +284,7 @@ ...@@ -280,7 +284,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>948.27123.59534.19558</string> </value> <value> <string>949.798.48752.10376</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -298,7 +302,7 @@ ...@@ -298,7 +302,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1452589359.42</float> <float>1454941615.65</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -118,8 +118,6 @@ ...@@ -118,8 +118,6 @@
\n \n
</head>\n </head>\n
<body>\n <body>\n
<noscript>Javascript needed.</noscript>\n
\n
<p class=message></p>\n <p class=message></p>\n
<form class="view-web-page-form">\n <form class="view-web-page-form">\n
<button type="submit" class="ui-btn ui-btn-b ui-btn-inline ui-icon-edit ui-btn-icon-right ui-screen-hidden">Save</button>\n <button type="submit" class="ui-btn ui-btn-b ui-btn-inline ui-icon-edit ui-btn-icon-right ui-screen-hidden">Save</button>\n
...@@ -132,7 +130,7 @@ ...@@ -132,7 +130,7 @@
</item> </item>
<item> <item>
<key> <string>title</string> </key> <key> <string>title</string> </key>
<value> <string>OfficeJS Drive Save</string> </value> <value> <string>OfficeJS Drive Edit</string> </value>
</item> </item>
<item> <item>
<key> <string>version</string> </key> <key> <string>version</string> </key>
...@@ -263,7 +261,7 @@ ...@@ -263,7 +261,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>948.35794.23372.40226</string> </value> <value> <string>949.1983.24454.60228</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -281,7 +279,7 @@ ...@@ -281,7 +279,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1453109726.99</float> <float>1456158941.46</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -98,9 +98,9 @@ ...@@ -98,9 +98,9 @@
</item> </item>
<item> <item>
<key> <string>text_content</string> </key> <key> <string>text_content</string> </key>
<value> <string>/*globals window, rJS, RSVP, loopEventListener*/\n <value> <string>/*globals window, rJS, RSVP, loopEventListener, Blob*/\n
/*jslint indent: 2, nomen: true, maxlen: 80*/\n /*jslint indent: 2, nomen: true, maxlen: 80*/\n
(function (window, RSVP, rJS, loopEventListener) {\n (function (window, RSVP, rJS, loopEventListener, Blob) {\n
"use strict";\n "use strict";\n
\n \n
var gadget_klass = rJS(window);\n var gadget_klass = rJS(window);\n
...@@ -133,11 +133,13 @@ ...@@ -133,11 +133,13 @@
.declareMethod(\'render\', function (options) {\n .declareMethod(\'render\', function (options) {\n
var gadget = this,\n var gadget = this,\n
format = \'text\';\n format = \'text\';\n
gadget.props.position = options.position || \'/\';\n
gadget.props.resourceName = options.resource;\n gadget.props.resourceName = options.resource;\n
return gadget.jio_getAttachment(gadget.props.position,\n \n
gadget.props.resourceName,\n return gadget.jio_getAttachment(\n
{format: format})\n [gadget.props.resourceName].join(\'/\'),\n
\'enclosure\',\n
{format: format}\n
)\n
.push(function (data) {\n .push(function (data) {\n
return gadget.getDeclaredGadget(\'codemirror\')\n return gadget.getDeclaredGadget(\'codemirror\')\n
.push(function (editorGadget) {\n .push(function (editorGadget) {\n
...@@ -148,12 +150,15 @@ ...@@ -148,12 +150,15 @@
});\n });\n
});\n });\n
})\n })\n
\n
\n \n
.push(function () {\n .push(function () {\n
return gadget.updateHeader({\n return gadget.updateHeader({\n
title: \'Editing \' + gadget.props.resourceName +\n title: \'Editing \' + gadget.props.resourceName,\n
\' at \' + gadget.props.position,\n save_action: true,\n
save_action: true\n panel_action: false,\n
back_url: (typeof options.back === \'string\' ?\n
"#position=" + options.back : \'#\')\n
});\n });\n
});\n });\n
})\n })\n
...@@ -173,10 +178,11 @@ ...@@ -173,10 +178,11 @@
function () {\n function () {\n
return gadget.props.editorGadget.getData()\n return gadget.props.editorGadget.getData()\n
.push(function (data) {\n .push(function (data) {\n
var blob = new Blob([data], {"type" : "text/plain"});\n
return gadget.jio_putAttachment(\n return gadget.jio_putAttachment(\n
gadget.props.position,\n
gadget.props.resourceName,\n gadget.props.resourceName,\n
data\n \'enclosure\',\n
blob\n
);\n );\n
});\n });\n
}\n }\n
...@@ -184,11 +190,11 @@ ...@@ -184,11 +190,11 @@
});\n });\n
});\n });\n
\n \n
}(window, RSVP, rJS, loopEventListener));</string> </value> }(window, RSVP, rJS, loopEventListener, Blob));</string> </value>
</item> </item>
<item> <item>
<key> <string>title</string> </key> <key> <string>title</string> </key>
<value> <string>OfficeJS Drive Save JS</string> </value> <value> <string>OfficeJS Drive Edit JS</string> </value>
</item> </item>
<item> <item>
<key> <string>version</string> </key> <key> <string>version</string> </key>
...@@ -321,7 +327,7 @@ ...@@ -321,7 +327,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>948.41958.9547.3754</string> </value> <value> <string>949.20738.20231.13619</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -339,7 +345,7 @@ ...@@ -339,7 +345,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1453478990.59</float> <float>1456137983.0</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -329,7 +329,7 @@ ...@@ -329,7 +329,7 @@
</item> </item>
<item> <item>
<key> <string>configuration_content_security_policy</string> </key> <key> <string>configuration_content_security_policy</string> </key>
<value> <string>default-src \'none\'; img-src \'self\' data:; media-src \'self\' blob:; connect-src \'self\' https://localhost:5000 data:; script-src \'self\' \'unsafe-eval\'; style-src \'self\' \'unsafe-inline\' https://netdna.bootstrapcdn.com data:; frame-src \'self\' data: ; font-src \'self\' https://netdna.bootstrapcdn.com</string> </value> <value> <string>default-src \'none\'; img-src \'self\' data:; media-src \'self\' blob: data:; connect-src \'self\' https://localhost:5000 data:; script-src \'self\' \'unsafe-eval\'; style-src \'self\' \'unsafe-inline\' https://netdna.bootstrapcdn.com data:; frame-src \'self\' data: ; font-src \'self\' https://netdna.bootstrapcdn.com</string> </value>
</item> </item>
<item> <item>
<key> <string>configuration_default_view_action_reference</string> </key> <key> <string>configuration_default_view_action_reference</string> </key>
...@@ -606,7 +606,7 @@ ...@@ -606,7 +606,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>948.37660.22124.61781</string> </value> <value> <string>948.37661.57011.20445</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -624,7 +624,7 @@ ...@@ -624,7 +624,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1453220992.18</float> <float>1453985211.99</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
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