Commit 179e1376 authored by Romain Courteaud's avatar Romain Courteaud

WIP [erp5_web_renderjs_ui] Replace appcache by a service worker

Fetch usage can be bypassed to do not use service worker when not needed.

As appcache has been dropped on Firefox, this change will improve the speed on Firefox.

No change is expected on Chrome/Safari.
parent 3e8d70c8
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Manifest" 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>
</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>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<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.appcache</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>rjs_gadget_erp5_appcache</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 Manifest</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content</string> </key>
<value> <string>CACHE MANIFEST\n
# generated on Wed, 21 Nov 2018 10:00:00 GMT+0100\n
# XXX + fonts\n
# images/ajax-loader.gif\n
CACHE:\n
favicon.ico\n
font-awesome/font-awesome-webfont.eot\n
font-awesome/font-awesome-webfont.woff\n
font-awesome/font-awesome-webfont.woff2\n
font-awesome/font-awesome-webfont.ttf\n
font-awesome/font-awesome-webfont.svg\n
gadget_erp5_worklist_empty.svg?format=svg\n
erp5_launcher_nojqm.js\n
gadget_erp5_nojqm.css\n
gadget_erp5_configure_editor.html\n
gadget_erp5_configure_editor.js\n
gadget_erp5_editor_panel.html\n
gadget_erp5_editor_panel.js\n
gadget_erp5_field_checkbox.html\n
gadget_erp5_field_checkbox.js\n
gadget_erp5_field_datetime.html\n
gadget_erp5_field_datetime.js\n
gadget_erp5_field_editor.html\n
gadget_erp5_field_editor.js\n
gadget_erp5_field_email.html\n
gadget_erp5_field_email.js\n
gadget_erp5_field_file.html\n
gadget_erp5_field_file.js\n
gadget_erp5_field_float.html\n
gadget_erp5_field_float.js\n
gadget_erp5_field_formbox.html\n
gadget_erp5_field_formbox.js\n
gadget_erp5_field_gadget.html\n
gadget_erp5_field_gadget.js\n
gadget_erp5_field_image.html\n
gadget_erp5_field_image.js\n
gadget_erp5_field_integer.html\n
gadget_erp5_field_integer.js\n
gadget_erp5_field_list.html\n
gadget_erp5_field_list.js\n
gadget_erp5_field_lines.html\n
gadget_erp5_field_lines.js\n
gadget_erp5_field_listbox.html\n
gadget_erp5_field_listbox.js\n
gadget_erp5_field_matrixbox.html\n
gadget_erp5_field_matrixbox.js\n
gadget_erp5_field_multicheckbox.html\n
gadget_erp5_field_multicheckbox.js\n
gadget_erp5_field_multilist.html\n
gadget_erp5_field_multilist.js\n
gadget_erp5_field_multirelationstring.html\n
gadget_erp5_field_multirelationstring.js\n
gadget_erp5_field_radio.html\n
gadget_erp5_field_radio.js\n
gadget_erp5_field_readonly.html\n
gadget_erp5_field_readonly.js\n
gadget_erp5_field_relationstring.html\n
gadget_erp5_field_relationstring.js\n
gadget_erp5_field_string.html\n
gadget_erp5_field_string.js\n
gadget_erp5_field_password.html\n
gadget_erp5_field_password.js\n
gadget_erp5_field_textarea.html\n
gadget_erp5_field_textarea.js\n
gadget_erp5_form.html\n
gadget_erp5_form.js\n
gadget_erp5_header.html\n
gadget_erp5_header.js\n
gadget_erp5_jio.html\n
gadget_erp5_jio.js\n
gadget_erp5_label_field.html\n
gadget_erp5_label_field.js\n
gadget_erp5_notification.html\n
gadget_erp5_notification.js\n
gadget_erp5_page_action.html\n
gadget_erp5_page_action.js\n
gadget_erp5_page_export.html\n
gadget_erp5_page_export.js\n
gadget_erp5_page_form.html\n
gadget_erp5_page_form.js\n
gadget_erp5_page_front.html\n
gadget_erp5_page_front.js\n
gadget_erp5_page_history.html\n
gadget_erp5_page_history.js\n
gadget_erp5_page_jump.html\n
gadget_erp5_page_jump.js\n
gadget_erp5_page_language.html\n
gadget_erp5_page_language.js\n
gadget_erp5_page_logout.html\n
gadget_erp5_page_logout.js\n
gadget_erp5_page_preference.html\n
gadget_erp5_page_preference.js\n
gadget_erp5_page_relation_search.html\n
gadget_erp5_page_relation_search.js\n
gadget_erp5_page_search.html\n
gadget_erp5_page_search.js\n
gadget_erp5_page_tab.html\n
gadget_erp5_page_tab.js\n
gadget_erp5_page_worklist.html\n
gadget_erp5_page_worklist.js\n
gadget_erp5_panel.html\n
gadget_erp5_panel.js\n
gadget_erp5_panel.png?format=png\n
gadget_erp5_pt_embedded_form_render.html\n
gadget_erp5_pt_embedded_form_render.js\n
gadget_erp5_pt_form_dialog.html\n
gadget_erp5_pt_form_dialog.js\n
gadget_erp5_pt_form_list.html\n
gadget_erp5_pt_form_list.js\n
gadget_erp5_pt_form_view.html\n
gadget_erp5_pt_form_view.js\n
gadget_erp5_pt_form_view_editable.html\n
gadget_erp5_pt_form_view_editable.js\n
gadget_erp5_pt_report_view.html\n
gadget_erp5_pt_report_view.js\n
gadget_erp5_router.html\n
gadget_erp5_router.js\n
gadget_erp5_relation_input.html\n
gadget_erp5_relation_input.js\n
gadget_erp5_search_editor.html\n
gadget_erp5_search_editor.js\n
gadget_erp5_searchfield.html\n
gadget_erp5_searchfield.js\n
gadget_erp5_sort_editor.html\n
gadget_erp5_sort_editor.js\n
gadget_global.js\n
gadget_html5_element.html\n
gadget_html5_element.js\n
gadget_html5_input.html\n
gadget_html5_input.js\n
gadget_html5_textarea.html\n
gadget_html5_textarea.js\n
gadget_html5_select.html\n
gadget_html5_select.js\n
gadget_erp5_global.js\n
gadget_jio.html\n
gadget_jio.js\n
gadget_translation.html\n
gadget_translation.js\n
gadget_translation_data.js\n
gadget_editor.html\n
gadget_editor.js\n
gadget_button_maximize.html\n
gadget_button_maximize.js\n
handlebars.js\n
jiodev.js\n
renderjs.js\n
rsvp.js\n
NETWORK:\n
*</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>RenderJS Gadget ERP5 AppCache</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>
</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>1465381395.69</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>971.39427.41235.63641</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>1542819250.56</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
</ZopeData>
/*jslint indent: 2*/ /*jslint indent: 2*/
/*global self, caches, fetch*/ /*global self, caches, fetch, Promise, URL, location*/
(function (self, caches, fetch) { (function (self, caches, fetch, Promise, URL, location) {
"use strict"; "use strict";
var CACHE_NAME = 'Thu, 12 July 2016 12:00:00 GMT', var prefix = location.toString() + '_',
CACHE_NAME = prefix + 'Tue, 26 Mars 2019 12:00:00 GMT',
// Files required to make this app work offline // Files required to make this app work offline
REQUIRED_FILES = [ REQUIRED_FILES = [
'./', 'favicon.ico',
'https://netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css', 'font-awesome/font-awesome-webfont.eot',
'https://netdna.bootstrapcdn.com/font-awesome/4.2.0/fonts/fontawesome-webfont.eot?v=4.2.0', 'font-awesome/font-awesome-webfont.woff',
'https://netdna.bootstrapcdn.com/font-awesome/4.2.0/fonts/fontawesome-webfont.eot?#iefix&v=4.2.0', 'font-awesome/font-awesome-webfont.woff2',
'https://netdna.bootstrapcdn.com/font-awesome/4.2.0/fonts/fontawesome-webfont.woff?v=4.2.0', 'font-awesome/font-awesome-webfont.ttf',
'https://netdna.bootstrapcdn.com/font-awesome/4.2.0/fonts/fontawesome-webfont.ttf?v=4.2.0', 'font-awesome/font-awesome-webfont.svg',
'https://netdna.bootstrapcdn.com/font-awesome/4.2.0/fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular', 'gadget_erp5_worklist_empty.svg?format=svg',
'URI.js', 'erp5_launcher_nojqm.js',
'erp5_launcher.js', 'gadget_erp5_nojqm.css',
'gadget_erp5.css', 'gadget_erp5_configure_editor.html',
'gadget_erp5_configure_editor.js',
'gadget_erp5_editor_panel.html', 'gadget_erp5_editor_panel.html',
'gadget_erp5_editor_panel.js', 'gadget_erp5_editor_panel.js',
'gadget_erp5_field_checkbox.html', 'gadget_erp5_field_checkbox.html',
'gadget_erp5_field_checkbox.js', 'gadget_erp5_field_checkbox.js',
'gadget_erp5_field_datetime.html', 'gadget_erp5_field_datetime.html',
'gadget_erp5_field_datetime.js', 'gadget_erp5_field_datetime.js',
'gadget_erp5_field_editor.html',
'gadget_erp5_field_editor.js',
'gadget_erp5_field_email.html', 'gadget_erp5_field_email.html',
'gadget_erp5_field_email.js', 'gadget_erp5_field_email.js',
'gadget_erp5_field_file.html', 'gadget_erp5_field_file.html',
'gadget_erp5_field_file.js', 'gadget_erp5_field_file.js',
'gadget_erp5_field_float.html', 'gadget_erp5_field_float.html',
'gadget_erp5_field_float.js', 'gadget_erp5_field_float.js',
'gadget_erp5_field_formbox.html',
'gadget_erp5_field_formbox.js',
'gadget_erp5_field_gadget.html', 'gadget_erp5_field_gadget.html',
'gadget_erp5_field_gadget.js', 'gadget_erp5_field_gadget.js',
'gadget_erp5_field_image.html', 'gadget_erp5_field_image.html',
...@@ -36,8 +42,12 @@ ...@@ -36,8 +42,12 @@
'gadget_erp5_field_integer.js', 'gadget_erp5_field_integer.js',
'gadget_erp5_field_list.html', 'gadget_erp5_field_list.html',
'gadget_erp5_field_list.js', 'gadget_erp5_field_list.js',
'gadget_erp5_field_lines.html',
'gadget_erp5_field_lines.js',
'gadget_erp5_field_listbox.html', 'gadget_erp5_field_listbox.html',
'gadget_erp5_field_listbox.js', 'gadget_erp5_field_listbox.js',
'gadget_erp5_field_matrixbox.html',
'gadget_erp5_field_matrixbox.js',
'gadget_erp5_field_multicheckbox.html', 'gadget_erp5_field_multicheckbox.html',
'gadget_erp5_field_multicheckbox.js', 'gadget_erp5_field_multicheckbox.js',
'gadget_erp5_field_multilist.html', 'gadget_erp5_field_multilist.html',
...@@ -62,8 +72,14 @@ ...@@ -62,8 +72,14 @@
'gadget_erp5_header.js', 'gadget_erp5_header.js',
'gadget_erp5_jio.html', 'gadget_erp5_jio.html',
'gadget_erp5_jio.js', 'gadget_erp5_jio.js',
'gadget_erp5_label_field.html',
'gadget_erp5_label_field.js',
'gadget_erp5_notification.html',
'gadget_erp5_notification.js',
'gadget_erp5_page_action.html', 'gadget_erp5_page_action.html',
'gadget_erp5_page_action.js', 'gadget_erp5_page_action.js',
'gadget_erp5_page_export.html',
'gadget_erp5_page_export.js',
'gadget_erp5_page_form.html', 'gadget_erp5_page_form.html',
'gadget_erp5_page_form.js', 'gadget_erp5_page_form.js',
'gadget_erp5_page_front.html', 'gadget_erp5_page_front.html',
...@@ -72,6 +88,8 @@ ...@@ -72,6 +88,8 @@
'gadget_erp5_page_history.js', 'gadget_erp5_page_history.js',
'gadget_erp5_page_jump.html', 'gadget_erp5_page_jump.html',
'gadget_erp5_page_jump.js', 'gadget_erp5_page_jump.js',
'gadget_erp5_page_language.html',
'gadget_erp5_page_language.js',
'gadget_erp5_page_logout.html', 'gadget_erp5_page_logout.html',
'gadget_erp5_page_logout.js', 'gadget_erp5_page_logout.js',
'gadget_erp5_page_preference.html', 'gadget_erp5_page_preference.html',
...@@ -86,6 +104,9 @@ ...@@ -86,6 +104,9 @@
'gadget_erp5_page_worklist.js', 'gadget_erp5_page_worklist.js',
'gadget_erp5_panel.html', 'gadget_erp5_panel.html',
'gadget_erp5_panel.js', 'gadget_erp5_panel.js',
'gadget_erp5_panel.png?format=png',
'gadget_erp5_pt_embedded_form_render.html',
'gadget_erp5_pt_embedded_form_render.js',
'gadget_erp5_pt_form_dialog.html', 'gadget_erp5_pt_form_dialog.html',
'gadget_erp5_pt_form_dialog.js', 'gadget_erp5_pt_form_dialog.js',
'gadget_erp5_pt_form_list.html', 'gadget_erp5_pt_form_list.html',
...@@ -107,18 +128,39 @@ ...@@ -107,18 +128,39 @@
'gadget_erp5_sort_editor.html', 'gadget_erp5_sort_editor.html',
'gadget_erp5_sort_editor.js', 'gadget_erp5_sort_editor.js',
'gadget_global.js', 'gadget_global.js',
'gadget_html5_element.html',
'gadget_html5_element.js',
'gadget_html5_input.html',
'gadget_html5_input.js',
'gadget_html5_textarea.html',
'gadget_html5_textarea.js',
'gadget_html5_select.html',
'gadget_html5_select.js',
'gadget_erp5_global.js', 'gadget_erp5_global.js',
'gadget_jio.html', 'gadget_jio.html',
'gadget_jio.js', 'gadget_jio.js',
'gadget_translation.html', 'gadget_translation.html',
'gadget_translation.js', 'gadget_translation.js',
'gadget_translation_data.js', 'gadget_translation_data.js',
'gadget_editor.html',
'gadget_editor.js',
'gadget_button_maximize.html',
'gadget_button_maximize.js',
'handlebars.js', 'handlebars.js',
'i18next.js',
'jiodev.js', 'jiodev.js',
'renderjs.js', 'renderjs.js',
'rsvp.js' 'rsvp.js',
]; './'
],
required_url_list = [],
i,
len = REQUIRED_FILES.length;
for (i = 0; i < len; i += 1) {
required_url_list.push(
new URL(REQUIRED_FILES[i], location.toString()).toString()
);
}
self.addEventListener('install', function (event) { self.addEventListener('install', function (event) {
// Perform install step: loading each required file into cache // Perform install step: loading each required file into cache
...@@ -136,14 +178,24 @@ ...@@ -136,14 +178,24 @@
}); });
self.addEventListener('fetch', function (event) { self.addEventListener('fetch', function (event) {
event.respondWith( var url = new URL(event.request.url);
caches.match(event.request) url.hash = '';
if ((event.request.method !== 'GET') ||
(required_url_list.indexOf(url.toString()) === -1)) {
// Try not to use the untrustable fetch function
// It can only be skip synchronously
return;
}
return event.respondWith(
caches.open(CACHE_NAME)
.then(function (cache) {
return cache.match(event.request);
})
.then(function (response) { .then(function (response) {
// Cache hit - return the response from the cached version // Cache hit - return the response from the cached version
if (response) { if (response) {
return response; return response;
} }
// Not in cache - return the result from the live server // Not in cache - return the result from the live server
// `fetch` is essentially a "fallback" // `fetch` is essentially a "fallback"
return fetch(event.request); return fetch(event.request);
...@@ -168,7 +220,8 @@ ...@@ -168,7 +220,8 @@
.filter(function (key) { .filter(function (key) {
// Filter by keys that don't start with the latest version prefix. // Filter by keys that don't start with the latest version prefix.
// return !key.startsWith(version); // return !key.startsWith(version);
return key !== CACHE_NAME; return ((key !== CACHE_NAME) &&
key.startsWith(prefix));
}) })
.map(function (key) { .map(function (key) {
/* Return a promise that's fulfilled /* Return a promise that's fulfilled
...@@ -184,6 +237,4 @@ ...@@ -184,6 +237,4 @@
); );
}); });
}(self, caches, fetch, Promise, URL, location));
\ No newline at end of file
}(self, caches, fetch));
...@@ -228,7 +228,7 @@ ...@@ -228,7 +228,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>952.5891.40125.8465</string> </value> <value> <string>974.39717.56905.47274</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -246,7 +246,7 @@ ...@@ -246,7 +246,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1467035757.8</float> <float>1553593581.07</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -279,6 +279,16 @@ ...@@ -279,6 +279,16 @@
<value> <string>string</string> </value> <value> <string>string</string> </value>
</item> </item>
</dictionary> </dictionary>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>configuration_service_worker_url</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple> </tuple>
</value> </value>
</item> </item>
...@@ -353,12 +363,18 @@ ...@@ -353,12 +363,18 @@
</item> </item>
<item> <item>
<key> <string>configuration_manifest_url</string> </key> <key> <string>configuration_manifest_url</string> </key>
<value> <string>gadget_erp5.appcache</string> </value> <value>
<none/>
</value>
</item> </item>
<item> <item>
<key> <string>configuration_panel_gadget_url</string> </key> <key> <string>configuration_panel_gadget_url</string> </key>
<value> <string>gadget_erp5_panel.html</string> </value> <value> <string>gadget_erp5_panel.html</string> </value>
</item> </item>
<item>
<key> <string>configuration_service_worker_url</string> </key>
<value> <string>gadget_erp5_serviceworker.js</string> </value>
</item>
<item> <item>
<key> <string>configuration_translation_gadget_url</string> </key> <key> <string>configuration_translation_gadget_url</string> </key>
<value> <string>gadget_translation.html</string> </value> <value> <string>gadget_translation.html</string> </value>
...@@ -600,7 +616,7 @@ ...@@ -600,7 +616,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>965.19244.13768.6758</string> </value> <value> <string>973.33482.4166.8669</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -618,7 +634,7 @@ ...@@ -618,7 +634,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1517843997.55</float> <float>1553593373.77</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