Commit eda23d3c 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.

Replace Base_getListFileFromAppcache with Base_getTranslationSourceFileList.

Collect a list of files from service worker code.

Use cache.add because safari does not support cache.addAll.

Don't give request object itself to cache.match. Firefox's Cache Storage does not work properly when VARY contains Accept-Language. Give URL string instead, then cache.match works on both Firefox and Chrome.

Stop calling skipWaiting() and clients.claim() for the new service worker.

To preserve the consistency of code and data, let the new service worker wait until all tabs and windows of the old version are closed.

New client can use the latest cache without waiting for the new service worker to be activated.

And once client was associated with a cache, client keeps using the same cache.

Fix translation script. Get service worker filename from layout property. Don't hardcode it.

Fix service worker. Don't hardcode the special cache name.

The name must be different per web site, else if the same service worker code is used by multiple web sites in ERP5 web site module, service worker does not work correctly.

Add more comments because service worker is unstable and hard to use safely.

If service worker failed to install cache, unregister this service worker explicitly.

Update service worker code. Client_id is null when it is the first request, in other words if request is navigate mode. Since major web browsers already implement client_id, if client_is is null, let's use the latest cache and don't get cache_key from CACHE_MAP and erp5js_cache.

service worker document may not exist

duplicate files by adding language prefix

experiment base tag on launcher

use relative url with .. to fetch gadgets

Drop base tag, because it will probably break all erp5js forks, which hardcode url strings

remove all hardcoded precache requirement

fetch the wallpaper from the base url

remove all clientIds handling

Waiting for all clients to be closed before activating the new service worker is not usable, as it will lead to user keeping the old code.
Drop the logic for now.

Fetch the list of precache url dynamically in order to decouple the worker code from the list of files.

Calculate dynamically the list of files from a python script, depending on the web site configuration.

Update the service worker as soon as the web site modification date changes.
This (I hope) will simplify ERP5 upgrade handling.

allow to extend the list of precache urls

do not hammer zope during installation

erp5_web_renderjs_ui: browser send GMT suffix

* reload the page only if there is no notification
* delay the sw installation
* do not reload after first worker installation
* embed the dependency list in the worker code
* global are deleted when the worker is stopped, which make the current fetch code less interesting...
* automatically handle service worker update and page refresh
* ensure that user automatically uses the latest code without manually refreshing the page.
* catch worker registration error
* catch worker update error

erp5_web_renderjs_ui: precache: missing dependencies + json export
parent 87d7b9eb
<?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 Thu, 07 Jan 2020 00: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_jump.html\n
gadget_erp5_pt_form_jump.js\n
gadget_erp5_pt_form_python_action.html\n
gadget_erp5_pt_form_python_action.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
*\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.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<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>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<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>981.438.11963.4386</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>1578416140.21</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="google" content="notranslate">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="favicon.ico">
<link rel="shortcut icon" href="${base_prefix}favicon.ico">
<meta name="theme-color" content="#085078">
<title>${application_title}</title>
......@@ -16,6 +16,7 @@
${webapp_manifest_full_link_tag}
<script data-renderjs-configuration="application_title" type="text/x-renderjs-configuration">${application_title}</script>
<script data-renderjs-configuration="base_prefix" type="text/x-renderjs-configuration">${base_prefix}</script>
<script data-renderjs-configuration="panel_gadget" type="text/x-renderjs-configuration">${panel_gadget}</script>
<script data-renderjs-configuration="action_view" type="text/x-renderjs-configuration">${action_view}</script>
<script data-renderjs-configuration="default_view_reference" type="text/x-renderjs-configuration">${default_view_reference}</script>
......@@ -27,16 +28,16 @@
<script data-renderjs-configuration="website_url_set" type="text/x-renderjs-configuration">${website_url_set}</script>
<script data-renderjs-configuration="service_worker_url" type="text/x-renderjs-configuration">${service_worker_url}</script>
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<script src="erp5_launcher_nojqm.js"></script>
<script src="${base_prefix}rsvp.js"></script>
<script src="${base_prefix}renderjs.js"></script>
<script src="${base_prefix}erp5_launcher_nojqm.js"></script>
</head>
<body>
<div data-role="page">
<div data-gadget-url="gadget_jio.html"
<div data-gadget-url="${base_prefix}gadget_jio.html"
data-gadget-scope="setting_gadget"
data-gadget-sandbox="public"></div>
......@@ -44,7 +45,7 @@
data-gadget-scope="router"
data-gadget-sandbox="public"></div>
<div data-gadget-async-url="gadget_erp5_notification.html"
<div data-gadget-async-url="${base_prefix}gadget_erp5_notification.html"
data-gadget-scope="notification"
data-gadget-sandbox="public"></div>
......@@ -61,7 +62,7 @@
data-gadget-sandbox="public"></div>
<div data-gadget-async-url="gadget_erp5_editor_panel.html"
<div data-gadget-async-url="${base_prefix}gadget_erp5_editor_panel.html"
data-gadget-scope="editor_panel"
data-gadget-sandbox="public"></div>
......
......@@ -240,7 +240,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>981.59416.23022.53862</string> </value>
<value> <string>981.33187.14689.53538</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -258,7 +258,7 @@
</tuple>
<state>
<tuple>
<float>1581954760.5</float>
<float>1580399803.54</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -10,13 +10,25 @@
var MAIN_SCOPE = "m",
default_state_json_string = JSON.stringify({
panel_visible: false,
service_worker_claimed: false,
setting_id: "setting/" + document.head.querySelector(
'script[data-renderjs-configuration="application_title"]'
).textContent,
base_prefix: document.head.querySelector(
'script[data-renderjs-configuration="base_prefix"]'
).textContent
});
function renderMainGadget(gadget, url, options) {
var page_gadget;
if (gadget.state.base_prefix) {
// For now, we assume the url is always relative
// Until one router starts to return absolute url
url = new URL(
url,
new URL(gadget.state.base_prefix, window.location.href).href
).href;
}
return gadget.declareGadget(url, {
scope: MAIN_SCOPE
})
......@@ -305,7 +317,8 @@
throw error;
})
.push(function (doc) {
for (var key in setting_dict) {
var key;
for (key in setting_dict) {
if (setting_dict.hasOwnProperty(key)) {
if (!doc.hasOwnProperty(key) ||
doc[key] !== setting_dict[key]) {
......@@ -409,13 +422,7 @@
if (setting.hasOwnProperty('service_worker_url') &&
(setting.service_worker_url !== '')) {
if (navigator.serviceWorker !== undefined) {
// Check if a ServiceWorker already controls the site on load
if (!navigator.serviceWorker.controller) {
// Register the ServiceWorker
navigator.serviceWorker.register(setting.service_worker_url);
}
}
gadget.registerServiceWorker(setting.service_worker_url);
}
return setting_gadget.put(gadget.state.setting_id, setting);
......@@ -680,6 +687,18 @@
.allowPublicAcquisition("renderApplication", function renderApplication(
param_list
) {
if (this.state.service_worker_claimed &&
(this.state.notification_options === undefined)) {
// As a new service worker claimed the client,
// reload the page to ensure it uses the lastest gadget versions
// Do it only if there is no notification to display
return location.reload();
}
if (this.state.service_worker_registration) {
// Mimic service worker update rule
// when a navigation to an in-scope page
this.deferServiceWorkerUpdate(this.state.service_worker_registration);
}
return this.render.apply(this, param_list);
})
......@@ -912,7 +931,76 @@
wallpaper_url = null;
wallpaper_absolute_url = null;
}
})
/////////////////////////////////////////////////////////////////
// Service worker handling
/////////////////////////////////////////////////////////////////
.declareJob('registerServiceWorker',
function registerServiceWorker(service_worker_url) {
var gadget = this,
is_first_worker,
install_delay;
if (navigator.serviceWorker !== undefined) {
is_first_worker = (navigator.serviceWorker.controller === null);
install_delay = is_first_worker ? 20000 : 2000;
return RSVP.all([
// Delay service worker installation, so that:
// * it does not slow down the current page rendering
// * it is not triggered too often if user click on multiple links
// * it is triggered only if the user browse the site
RSVP.delay(install_delay)
.then(function () {
// Register the ServiceWorker
return navigator.serviceWorker
.register(service_worker_url)
.then(function (registration) {
gadget.state.service_worker_registration = registration;
})
.catch(function (error) {
// If service worker installation is rejected,
// do not prevent the site to be usable, even if slower
console.warn("Service worker registration failed", error);
});
}),
// Check when a new worker has been activated from another tab
// XXX Not promise based, but we do not want to add a new dependency
navigator.serviceWorker
.addEventListener('message',
function handleMessage(event) {
if (event.data === 'claim') {
if (is_first_worker) {
// Do not reload if the worker is created after the page
// is loaded, because they probably use the same
// gadgets
is_first_worker = false;
} else {
gadget.state.service_worker_claimed = true;
}
}
})
]);
}
})
.declareJob('deferServiceWorkerUpdate',
function deferServiceWorkerUpdate(registration) {
return new RSVP.Queue()
.push(function () {
// Delay service worker update, so that:
// * it does not slow down the current page rendering
// * it is not triggered too often if user click on multiple links
// * it is triggered only if the user browse the site
return RSVP.delay(20000);
})
.push(function () {
return registration
.update()
.catch(function (error) {
console.warn("Service worker update failed", error);
});
});
});
}(window, document, RSVP, rJS,
XMLHttpRequest, location, console, navigator, Event, URL));
\ No newline at end of file
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>980.43952.30132.41164</string> </value>
<value> <string>981.63449.54271.9625</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1577760448.33</float>
<float>1582196761.8</float>
<string>UTC</string>
</tuple>
</state>
......
/*jslint indent: 2*/
/*global self, caches, fetch*/
(function (self, caches, fetch) {
/*global self, caches, fetch, Promise, URL, location, JSON*/
(function (self, caches, fetch, Promise, URL, location, JSON) {
"use strict";
var CACHE_NAME = 'Thu, 12 July 2016 12:00:00 GMT',
// Files required to make this app work offline
REQUIRED_FILES = [
'./',
'https://netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css',
'https://netdna.bootstrapcdn.com/font-awesome/4.2.0/fonts/fontawesome-webfont.eot?v=4.2.0',
'https://netdna.bootstrapcdn.com/font-awesome/4.2.0/fonts/fontawesome-webfont.eot?#iefix&v=4.2.0',
'https://netdna.bootstrapcdn.com/font-awesome/4.2.0/fonts/fontawesome-webfont.woff?v=4.2.0',
'https://netdna.bootstrapcdn.com/font-awesome/4.2.0/fonts/fontawesome-webfont.ttf?v=4.2.0',
'https://netdna.bootstrapcdn.com/font-awesome/4.2.0/fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular',
'URI.js',
'erp5_launcher.js',
'gadget_erp5.css',
'gadget_erp5_editor_panel.html',
'gadget_erp5_editor_panel.js',
'gadget_erp5_field_checkbox.html',
'gadget_erp5_field_checkbox.js',
'gadget_erp5_field_datetime.html',
'gadget_erp5_field_datetime.js',
'gadget_erp5_field_email.html',
'gadget_erp5_field_email.js',
'gadget_erp5_field_file.html',
'gadget_erp5_field_file.js',
'gadget_erp5_field_float.html',
'gadget_erp5_field_float.js',
'gadget_erp5_field_gadget.html',
'gadget_erp5_field_gadget.js',
'gadget_erp5_field_image.html',
'gadget_erp5_field_image.js',
'gadget_erp5_field_integer.html',
'gadget_erp5_field_integer.js',
'gadget_erp5_field_list.html',
'gadget_erp5_field_list.js',
'gadget_erp5_field_listbox.html',
'gadget_erp5_field_listbox.js',
'gadget_erp5_field_multicheckbox.html',
'gadget_erp5_field_multicheckbox.js',
'gadget_erp5_field_multilist.html',
'gadget_erp5_field_multilist.js',
'gadget_erp5_field_multirelationstring.html',
'gadget_erp5_field_multirelationstring.js',
'gadget_erp5_field_radio.html',
'gadget_erp5_field_radio.js',
'gadget_erp5_field_readonly.html',
'gadget_erp5_field_readonly.js',
'gadget_erp5_field_relationstring.html',
'gadget_erp5_field_relationstring.js',
'gadget_erp5_field_string.html',
'gadget_erp5_field_string.js',
'gadget_erp5_field_password.html',
'gadget_erp5_field_password.js',
'gadget_erp5_field_textarea.html',
'gadget_erp5_field_textarea.js',
'gadget_erp5_form.html',
'gadget_erp5_form.js',
'gadget_erp5_header.html',
'gadget_erp5_header.js',
'gadget_erp5_jio.html',
'gadget_erp5_jio.js',
'gadget_erp5_page_action.html',
'gadget_erp5_page_action.js',
'gadget_erp5_page_form.html',
'gadget_erp5_page_form.js',
'gadget_erp5_page_front.html',
'gadget_erp5_page_front.js',
'gadget_erp5_page_history.html',
'gadget_erp5_page_history.js',
'gadget_erp5_page_jump.html',
'gadget_erp5_page_jump.js',
'gadget_erp5_page_logout.html',
'gadget_erp5_page_logout.js',
'gadget_erp5_page_preference.html',
'gadget_erp5_page_preference.js',
'gadget_erp5_page_relation_search.html',
'gadget_erp5_page_relation_search.js',
'gadget_erp5_page_search.html',
'gadget_erp5_page_search.js',
'gadget_erp5_page_tab.html',
'gadget_erp5_page_tab.js',
'gadget_erp5_page_worklist.html',
'gadget_erp5_page_worklist.js',
'gadget_erp5_panel.html',
'gadget_erp5_panel.js',
'gadget_erp5_pt_form_dialog.html',
'gadget_erp5_pt_form_dialog.js',
'gadget_erp5_pt_form_list.html',
'gadget_erp5_pt_form_list.js',
'gadget_erp5_pt_form_view.html',
'gadget_erp5_pt_form_view.js',
'gadget_erp5_pt_form_view_editable.html',
'gadget_erp5_pt_form_view_editable.js',
'gadget_erp5_pt_report_view.html',
'gadget_erp5_pt_report_view.js',
'gadget_erp5_router.html',
'gadget_erp5_router.js',
'gadget_erp5_relation_input.html',
'gadget_erp5_relation_input.js',
'gadget_erp5_search_editor.html',
'gadget_erp5_search_editor.js',
'gadget_erp5_searchfield.html',
'gadget_erp5_searchfield.js',
'gadget_erp5_sort_editor.html',
'gadget_erp5_sort_editor.js',
'gadget_global.js',
'gadget_erp5_global.js',
'gadget_jio.html',
'gadget_jio.js',
'gadget_translation.html',
'gadget_translation.js',
'gadget_translation_data.js',
'handlebars.js',
'i18next.js',
'jiodev.js',
'renderjs.js',
'rsvp.js'
];
var prefix = location.toString() + '_',
CACHE_NAME = prefix + '${modification_date}',
REQUIRED_FILES = JSON.parse('${required_url_list}'),
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) {
// Perform install step: loading each required file into cache
event.waitUntil(
caches.open(CACHE_NAME)
.then(function (cache) {
var promise = Promise.resolve();
function append(url_to_cache) {
promise = promise
.then(function () {
// Use cache.add because safari does not support cache.addAll.
return cache.add(url_to_cache);
});
}
len = required_url_list.length;
for (i = 0; i < len; i += 1) {
// Add all offline dependencies to the cache
return cache.addAll(REQUIRED_FILES);
// One by one, to not hammer zopes
append(required_url_list[i]);
}
return promise;
})
.then(function () {
// At this point everything has been cached
// When user accesses ERP5JS web site first time, service worker is
// installed but it is not activated yet, service worker is activated
// when the page is refreshed or when a new tab opens the site again.
// If user does not refresh the page and continue to use the site,
// user can't use cache, so everything becomes slow. We must avoid this
// situation.
// So, we want to activate the new service worker immediately if it was
// the first one.
return self.skipWaiting();
})
.catch(function (error) {
// Since we do not allow to override existing cache, if cache installation
// failed, we need to delete the cache completely.
caches.delete(CACHE_NAME);
throw error;
})
);
});
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.match(event.request)
var url = new URL(event.request.url);
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) {
// Don't give request object itself. Firefox's Cache Storage
// does not work properly when VARY contains Accept-Language.
// Give URL string instead, then cache.match works on both Firefox and Chrome.
return cache.match(event.request.url);
})
.then(function (response) {
// Cache hit - return the response from the cached version
if (response) {
return response;
}
// Not in cache - return the result from the live server
// `fetch` is essentially a "fallback"
return fetch(event.request);
......@@ -168,7 +105,8 @@
.filter(function (key) {
// Filter by keys that don't start with the latest version prefix.
// return !key.startsWith(version);
return key !== CACHE_NAME;
return ((key !== CACHE_NAME) &&
key.startsWith(prefix));
})
.map(function (key) {
/* Return a promise that's fulfilled
......@@ -179,11 +117,21 @@
);
})
.then(function () {
self.clients.claim();
return self.clients.claim();
})
.then(function () {
return self.clients.matchAll();
})
.then(function (client_list) {
// Notify all clients that they can reload the page
var j,
client_len = client_list.length;
for (j = 0; j < client_len; j += 1) {
client_list[j].postMessage('claim');
}
})
);
});
}(self, caches, fetch));
}(self, caches, fetch, Promise, URL, location, JSON));
\ No newline at end of file
......@@ -95,6 +95,10 @@
<none/>
</value>
</item>
<item>
<key> <string>text_content_substitution_mapping_method_id</string> </key>
<value> <string>WebPage_getRenderJSSubstitutionMappingDict</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>ERP5 ServiceWorker</string> </value>
......@@ -148,11 +152,13 @@
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<tuple>
<none/>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
......@@ -196,16 +202,20 @@
</item>
</dictionary>
</list>
</tuple>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<tuple>
<none/>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
......@@ -228,7 +238,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>952.5891.40125.8465</string> </value>
<value> <string>981.50754.31879.22749</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -246,7 +256,7 @@
</tuple>
<state>
<tuple>
<float>1467035757.8</float>
<float>1581435049.45</float>
<string>UTC</string>
</tuple>
</state>
......@@ -255,16 +265,20 @@
</item>
</dictionary>
</list>
</tuple>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<tuple>
<none/>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
......@@ -312,7 +326,9 @@
</item>
</dictionary>
</list>
</tuple>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -279,6 +279,16 @@
<value> <string>string</string> </value>
</item>
</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>
</value>
</item>
......@@ -353,12 +363,18 @@
</item>
<item>
<key> <string>configuration_manifest_url</string> </key>
<value> <string>gadget_erp5.appcache</string> </value>
<value>
<none/>
</value>
</item>
<item>
<key> <string>configuration_panel_gadget_url</string> </key>
<value> <string>gadget_erp5_panel.html</string> </value>
</item>
<item>
<key> <string>configuration_service_worker_url</string> </key>
<value> <string>gadget_erp5_serviceworker.js</string> </value>
</item>
<item>
<key> <string>configuration_translation_gadget_url</string> </key>
<value> <string>gadget_translation.html</string> </value>
......@@ -600,7 +616,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>965.19244.13768.6758</string> </value>
<value> <string>973.33482.4166.8669</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -618,7 +634,7 @@
</tuple>
<state>
<tuple>
<float>1517843997.55</float>
<float>1553593373.77</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -10,7 +10,7 @@ attribute_filter_re = re.compile(r"""(data-i18n)=["']?((?:.(?!["']?\s+(?:\S+)=|[
translate_word = []
for web_page in portal.web_page_module.searchFolder(portal_type='Web Page',
reference=context.Base_getListFileFromAppcache(only_html=1)):
reference=context.Base_getTranslationSourceFileList(only_html=1)):
data = attribute_filter_re.findall(web_page.getTextContent())
for attribute in data:
a = re.sub(r'[{|}]', "", attribute[1])
......
import re
service_worker_reference = context.getLayoutProperty("configuration_service_worker_url", default="gadget_erp5_serviceworker.js")
service_worker = context.getPortalObject().portal_catalog.getResultValue(
portal_type='Web Script',
reference=service_worker_reference)
if service_worker is None:
text_content = ''
else:
text_content = service_worker.getTextContent()
filename_pattern = re.compile("'(?P<filename>[a-zA-Z0-9-_\.\?=]*)'")
filename_list = []
start = False
for line in text_content.split('\n'):
if start is False and 'REQUIRED_FILES' in line:
start = True
continue
if not line:
continue
if start:
if ']' in line:
break
matched = filename_pattern.search(line)
if matched is not None:
filename = matched.groupdict().get('filename')
if filename:
filename_list.append(filename)
file_list = []
translation_data_file = []
for filename in filename_list:
if filename.endswith('.html'):
file_list.append(filename)
continue
if filename.endswith('.js') and not only_html:
if filename.endswith('translation_data.js'):
translation_data_file = [filename]
continue
file_list.append(filename)
return translation_data_file + file_list
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>only_html=0</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_getTranslationSourceFileList</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -98,13 +98,15 @@
<value>
<list>
<string>my_configuration_panel_gadget_url</string>
<string>my_configuration_manifest_url</string>
<string>my_configuration_webapp_manifest_url</string>
<string>my_configuration_service_worker_url</string>
<string>my_configuration_jio_gadget_url</string>
<string>my_configuration_translation_gadget_url</string>
<string>my_configuration_router_gadget_url</string>
<string>my_configuration_header_gadget_url</string>
<string>my_configuration_manifest_url</string>
<string>my_configuration_webapp_manifest_url</string>
<string>my_configuration_service_worker_url</string>
<string>my_modification_date</string>
<string>my_configuration_precache_manifest_url_list</string>
</list>
</value>
</item>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
<string>width</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_configuration_precache_manifest_url_list</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_text_area_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Service Worker Precache Manifest</string> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>40</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>date_only</string>
<string>editable</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_modification_date</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>date_only</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_date_time_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Service Worker Cache Key</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -54,6 +54,10 @@
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>first_item</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
......@@ -64,6 +68,10 @@
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
......@@ -107,7 +115,7 @@
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: [ (x,x) for x in here.Base_getListFileFromAppcache()]</string> </value>
<value> <string>python: [ (x,x) for x in here.Base_getTranslationSourceFileList()]</string> </value>
</item>
</dictionary>
</pickle>
......
from DateTime import DateTime
import json
if REQUEST is None:
REQUEST = context.REQUEST
if response is None:
......@@ -11,9 +14,12 @@ response.setHeader("Access-Control-Allow-Origin", "*")
web_page = context
web_section = context.getWebSectionValue()
# Must-Revalidate caching policy uses Base_getWebSiteDrivenModificationDate
if REQUEST.getHeader('If-Modified-Since', '') == web_page.Base_getWebSiteDrivenModificationDate().rfc822():
modification_date_string = web_page.Base_getWebSiteDrivenModificationDate().rfc822()
modified_since = REQUEST.getHeader('If-Modified-Since', '')
if modified_since:
if DateTime(modified_since).rfc822() == modification_date_string:
response.setStatus(304)
return ""
......@@ -23,6 +29,12 @@ web_content = web_page.getTextContent()
# set headers depending on type of script
if (portal_type == "Web Script"):
response.setHeader('Content-Type', 'application/javascript; charset=utf-8')
if web_page.getTextContentSubstitutionMappingMethodId():
web_content = web_page.TextDocument_substituteTextContent(web_content, mapping_dict={
'modification_date': modification_date_string,
# Make JSLint happy for the service worker code
'required_url_list': json.dumps(web_section.WebSection_getPrecacheManifest())
})
elif (portal_type == "Web Style"):
response.setHeader('Content-Type', 'text/css; charset=utf-8')
......
web_section = context
# Add all ERP5JS gadget
url_list = [
'favicon.ico',
'font-awesome/font-awesome-webfont.eot',
'font-awesome/font-awesome-webfont.woff',
'font-awesome/font-awesome-webfont.woff2',
'font-awesome/font-awesome-webfont.ttf',
'font-awesome/font-awesome-webfont.svg',
'gadget_erp5_worklist_empty.svg?format=svg',
'erp5_launcher_nojqm.js',
'gadget_erp5_nojqm.css',
'gadget_erp5_configure_editor.html',
'gadget_erp5_configure_editor.js',
'gadget_erp5_editor_panel.html',
'gadget_erp5_editor_panel.js',
'gadget_erp5_field_checkbox.html',
'gadget_erp5_field_checkbox.js',
'gadget_erp5_field_datetime.html',
'gadget_erp5_field_datetime.js',
'gadget_erp5_field_editor.html',
'gadget_erp5_field_editor.js',
'gadget_erp5_field_email.html',
'gadget_erp5_field_email.js',
'gadget_erp5_field_file.html',
'gadget_erp5_field_file.js',
'gadget_erp5_field_float.html',
'gadget_erp5_field_float.js',
'gadget_erp5_field_formbox.html',
'gadget_erp5_field_formbox.js',
'gadget_erp5_field_gadget.html',
'gadget_erp5_field_gadget.js',
'gadget_erp5_field_image.html',
'gadget_erp5_field_image.js',
'gadget_erp5_field_integer.html',
'gadget_erp5_field_integer.js',
'gadget_erp5_field_list.html',
'gadget_erp5_field_list.js',
'gadget_erp5_field_lines.html',
'gadget_erp5_field_lines.js',
'gadget_erp5_field_listbox.html',
'gadget_erp5_field_listbox.js',
'gadget_erp5_field_matrixbox.html',
'gadget_erp5_field_matrixbox.js',
'gadget_erp5_field_multicheckbox.html',
'gadget_erp5_field_multicheckbox.js',
'gadget_erp5_field_multilist.html',
'gadget_erp5_field_multilist.js',
'gadget_erp5_field_multirelationstring.html',
'gadget_erp5_field_multirelationstring.js',
'gadget_erp5_field_radio.html',
'gadget_erp5_field_radio.js',
'gadget_erp5_field_readonly.html',
'gadget_erp5_field_readonly.js',
'gadget_erp5_field_relationstring.html',
'gadget_erp5_field_relationstring.js',
'gadget_erp5_field_string.html',
'gadget_erp5_field_string.js',
'gadget_erp5_field_password.html',
'gadget_erp5_field_password.js',
'gadget_erp5_field_textarea.html',
'gadget_erp5_field_textarea.js',
'gadget_erp5_form.html',
'gadget_erp5_form.js',
'gadget_erp5_header.html',
'gadget_erp5_header.js',
'gadget_erp5_jio.html',
'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.js',
'gadget_erp5_page_export.html',
'gadget_erp5_page_export.js',
'gadget_erp5_page_form.html',
'gadget_erp5_page_form.js',
'gadget_erp5_page_front.html',
'gadget_erp5_page_front.js',
'gadget_erp5_page_history.html',
'gadget_erp5_page_history.js',
'gadget_erp5_page_jump.html',
'gadget_erp5_page_jump.js',
'gadget_erp5_page_language.html',
'gadget_erp5_page_language.js',
'gadget_erp5_page_logout.html',
'gadget_erp5_page_logout.js',
'gadget_erp5_page_preference.html',
'gadget_erp5_page_preference.js',
'gadget_erp5_page_relation_search.html',
'gadget_erp5_page_relation_search.js',
'gadget_erp5_page_search.html',
'gadget_erp5_page_search.js',
'gadget_erp5_page_tab.html',
'gadget_erp5_page_tab.js',
'gadget_erp5_page_worklist.html',
'gadget_erp5_page_worklist.js',
'gadget_erp5_panel.html',
'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.js',
'gadget_erp5_pt_form_jump.html',
'gadget_erp5_pt_form_jump.js',
'gadget_erp5_pt_form_list.html',
'gadget_erp5_pt_form_list.js',
'gadget_erp5_pt_form_python_action.html',
'gadget_erp5_pt_form_python_action.js',
'gadget_erp5_pt_form_view.html',
'gadget_erp5_pt_form_view.js',
'gadget_erp5_pt_form_view_editable.html',
'gadget_erp5_pt_form_view_editable.js',
'gadget_erp5_pt_report_view.html',
'gadget_erp5_pt_report_view.js',
'gadget_erp5_router.html',
'gadget_erp5_router.js',
'gadget_erp5_relation_input.html',
'gadget_erp5_relation_input.js',
'gadget_erp5_search_editor.html',
'gadget_erp5_search_editor.js',
'gadget_erp5_searchfield.html',
'gadget_erp5_searchfield.js',
'gadget_erp5_sort_editor.html',
'gadget_erp5_sort_editor.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_jio.html',
'gadget_jio.js',
'gadget_translation.html',
'gadget_translation.js',
'gadget_translation_data.js',
'gadget_editor.html',
'gadget_editor.js',
'gadget_button_maximize.html',
'gadget_button_maximize.js',
'handlebars.js',
'jiodev.js',
'renderjs.js',
'rsvp.js',
]
# Add all root gadgets
default_url = './'
available_language_set = web_section.getLayoutProperty("available_language_set", default=['en'])
default_language = web_section.getLayoutProperty("default_available_language", default='en')
for language in available_language_set:
if language == default_language:
url_list.append(default_url)
else:
url_list.append('%s/' % language)
# Add all custom gadgets
url_list.extend([
web_section.getLayoutProperty("configuration_wallpaper_url", default=default_url),
web_section.getLayoutProperty("configuration_panel_gadget_url", default=default_url),
web_section.getLayoutProperty("configuration_router_gadget_url", default=default_url),
web_section.getLayoutProperty("configuration_header_gadget_url", default=default_url),
web_section.getLayoutProperty("configuration_jio_gadget_url", default=default_url),
web_section.getLayoutProperty("configuration_translation_gadget_url", default=default_url),
web_section.getLayoutProperty("configuration_stylesheet_url", default=default_url),
])
# Add all extra dependencies
precache_manifest_url_list = web_section.getLayoutProperty("configuration_precache_manifest_url_list", default='').splitlines()
for precache_manifest_script_id in precache_manifest_url_list:
url_list.extend(web_section.restrictedTraverse(precache_manifest_script_id)())
if REQUEST is not None:
import json
REQUEST.RESPONSE.setHeader('Content-Type', 'application/json')
return json.dumps(dict.fromkeys(url_list), indent=2)
return url_list
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>REQUEST=None</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Anonymous</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>WebSection_getPrecacheManifest</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
import json
import re
from urlparse import urljoin
if REQUEST is None:
REQUEST = context.REQUEST
......@@ -31,23 +32,32 @@ view_as_web_method = default_web_page.getTypeBasedMethod(
fallback_script_id="WebPage_viewAsWeb"
)
selected_language = portal.Localizer.get_selected_language()
# base tag
if selected_language == default_language:
base_prefix = ""
else:
base_prefix = '../'
mapping_dict = {
"base_prefix": base_prefix,
"frontpage_gadget": web_section.getLayoutProperty("configuration_frontpage_gadget_url", default="worklist"),
"jio_document_page_gadget": web_section.getLayoutProperty("configuration_default_jio_document_page_gadget_url", default="form"),
"application_title": web_section.getLayoutProperty("configuration_application_title", default="ERP5"),
"action_view": web_section.getLayoutProperty("configuration_view_action_category", default="object_view"),
"default_view_reference": web_section.getLayoutProperty("configuration_default_view_action_reference", default="view"),
"hateoas_url": web_section.getLayoutProperty("configuration_hateoas_url", default="hateoas/"),
"panel_gadget": web_section.getLayoutProperty("configuration_panel_gadget_url", default="gadget_erp5_panel.html"),
"router_gadget": web_section.getLayoutProperty("configuration_router_gadget_url", default="gadget_erp5_router.html"),
"header_gadget": web_section.getLayoutProperty("configuration_header_gadget_url", default="gadget_erp5_header.html"),
"jio_gadget": web_section.getLayoutProperty("configuration_jio_gadget_url", default="gadget_jio.html"),
"translation_gadget": web_section.getLayoutProperty("configuration_translation_gadget_url", default="gadget_translation.html"),
"panel_gadget": urljoin(base_prefix, web_section.getLayoutProperty("configuration_panel_gadget_url", default="gadget_erp5_panel.html")),
"router_gadget": urljoin(base_prefix, web_section.getLayoutProperty("configuration_router_gadget_url", default="gadget_erp5_router.html")),
"header_gadget": urljoin(base_prefix, web_section.getLayoutProperty("configuration_header_gadget_url", default="gadget_erp5_header.html")),
"jio_gadget": urljoin(base_prefix, web_section.getLayoutProperty("configuration_jio_gadget_url", default="gadget_jio.html")),
"translation_gadget": urljoin(base_prefix, web_section.getLayoutProperty("configuration_translation_gadget_url", default="gadget_translation.html")),
"manifest_url": web_section.getLayoutProperty("configuration_manifest_url", default="gadget_erp5.appcache"),
"stylesheet_url": web_section.getLayoutProperty("configuration_stylesheet_url", default="gadget_erp5_nojqm.css"),
"service_worker_url": web_section.getLayoutProperty("configuration_service_worker_url", default=""),
"stylesheet_url": urljoin(base_prefix, web_section.getLayoutProperty("configuration_stylesheet_url", default="gadget_erp5_nojqm.css")),
"service_worker_url": urljoin(base_prefix, web_section.getLayoutProperty("configuration_service_worker_url", default="")),
"language_map": json.dumps({tmp['id']: portal.Base_translateString(tmp['title'], lang = tmp['id']) for tmp in portal.Localizer.get_languages_map() if tmp['id'] in available_language_set}),
"default_selected_language": portal.Localizer.get_selected_language(),
"default_selected_language": selected_language,
"website_url_set": json.dumps(website_url_set),
"site_description": web_section.getLayoutProperty("description", default=""),
"site_keywords": web_section.getLayoutProperty("subject", default=""),
......@@ -89,6 +99,6 @@ else:
background-attachment: fixed;
background-image: url("%s");
}
""" % wallpaper_url)
""" % urljoin(base_prefix, wallpaper_url))
return view_as_web_method(mapping_dict=mapping_dict)
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