Commit fbd23eb3 authored by Roque's avatar Roque

erp5_officejs: new storage "configuration"

- new storage layer on top of appcache storage to manage officejs configuration elemnts
- the elements listed in ".configuration" app manifest are got from appcache and stored in current app storage as documents
- storage cleaner to handle data migration
parent 14354bad
/*jslint indent:2, maxlen: 80, nomen: true */
/*global jIO, RSVP, window, URL, atob, btoa, Rusha */
(function (window, jIO, RSVP, URL, atob, btoa, Rusha) {
"use strict";
var rusha = new Rusha();
function decodeDocumentId(id, hateoas_appcache) {
var hateoas_section,
hateoas_section_and_view;
hateoas_section = "./" + hateoas_appcache + "/";
hateoas_section_and_view = hateoas_section + "definition_view/";
id = id.replace(hateoas_section_and_view, "");
id = atob(id);
return id;
}
function encodeDocumentId(id, hateoas_appcache) {
var hateoas_section,
hateoas_section_and_view;
id = btoa(id);
hateoas_section = "./" + hateoas_appcache + "/";
hateoas_section_and_view = hateoas_section + "definition_view/";
id = hateoas_section_and_view + id;
return id;
}
function processHateoasDict(raw_dict) {
var raw_field_list, type, parent, field_key, field_id, return_dict = {};
return_dict.raw_dict = raw_dict;
/*jslint nomen: true*/
if (raw_dict.hasOwnProperty("_embedded") &&
raw_dict._embedded.hasOwnProperty("_view")) {
raw_field_list = raw_dict._embedded._view;
type = raw_dict._links.type.name;
parent = raw_dict._links.parent.name;
return_dict.parent_relative_url = "portal_types/" + parent;
return_dict.portal_type = type;
for (field_key in raw_field_list) {
if (raw_field_list.hasOwnProperty(field_key)) {
field_id = "";
if (raw_field_list[field_key]["default"] !== undefined &&
raw_field_list[field_key]["default"] !== "") {
if (field_key.startsWith("my_")) {
field_id = field_key.replace("my_", "");
} else if (field_key.startsWith("your_")) {
field_id = field_key.replace("your_", "");
} else {
field_id = field_key;
}
return_dict[field_id] = raw_field_list[field_key]["default"];
}
}
}
} else {
// ignore non configuration elements
return raw_dict;
}
return return_dict;
}
function ConfigurationStorage(spec) {
if (spec.sub_storage.type !== "appcache") {
throw new Error("appcache substorage is mandatory for configuration " +
"storage");
}
this._sub_storage = jIO.createJIO(spec.sub_storage);
this._hateoas_appcache = spec.hateoas_appcache;
this._manifest = spec.manifest;
this._origin_url = spec.origin_url !== undefined ?
spec.origin_url : window.location.href;
this._version = spec.version || "";
this._prefix = spec.prefix || "./";
this._version = this._prefix + this._version;
this._hash = "";
}
ConfigurationStorage.prototype.get = function (id) {
var storage = this;
id = encodeDocumentId(id, storage._hateoas_appcache);
return storage._sub_storage.getAttachment(storage._origin_url,
id,
{"format": "json"})
.push(function (content) {
content = processHateoasDict(content);
content.hash = storage._hash;
return content;
});
};
ConfigurationStorage.prototype.hasCapacity = function () {
return true;
};
ConfigurationStorage.prototype.getAttachment = function () {
return this._sub_storage.getAttachment.apply(this._sub_storage, arguments);
};
ConfigurationStorage.prototype.allAttachments = function () {
return this._sub_storage.allAttachments.apply(this._sub_storage, arguments);
};
ConfigurationStorage.prototype.buildQuery = function () {
var storage = this,
result = [],
decoded_id,
attachment_id;
return storage.allAttachments(storage._origin_url)
.push(function (all_attachments) {
for (attachment_id in all_attachments) {
if (all_attachments.hasOwnProperty(attachment_id)) {
if (attachment_id !== storage._version &&
attachment_id !== storage._version + "/" &&
attachment_id !== storage._version + storage._manifest) {
decoded_id = decodeDocumentId(attachment_id,
storage._hateoas_appcache);
result.push({
'id': decoded_id,
'value': {hash: storage._hash},
'doc': {hash: storage._hash}
});
}
}
}
return result;
});
};
ConfigurationStorage.prototype.repair = function (app_version) {
var storage = this,
url = new URL(storage._manifest, new URL(storage._version,
storage._origin_url));
return new RSVP.Queue()
.push(function () {
return jIO.util.ajax({
type: "GET",
url: url
});
})
.push(function (response) {
var text = response.target.responseText;
//hash is attached to manifest text and app version
//if the app version has changed, then a cleanup was done in the storage
//documents must be updated to restore any potential missing document
storage._hash = rusha.digestFromString(text + app_version);
return storage._sub_storage.repair.apply(storage._sub_storage,
arguments);
});
};
jIO.addStorage('configuration', ConfigurationStorage);
}(window, jIO, RSVP, URL, atob, btoa, Rusha));
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Script" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</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>jio_configuration_storage.js</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>gadget_officejs_jio_configurationstorage_js</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value> <string>en</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Web Script</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>jio_configuration_storage.js</string> </value>
</item>
<item>
<key> <string>url_string</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>001</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>document_publication_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>edit_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>processing_status_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>publish_alive</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1567674328.66</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>978.21127.28630.40174</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>1568037934.07</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>detect_converted_file</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_processing_state</string> </key>
<value> <string>converted</string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>0.0.0.0</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="DateTime" module="DateTime.DateTime"/>
</klass>
<tuple>
<none/>
</tuple>
<state>
<tuple>
<float>1567674187.84</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
</ZopeData>
......@@ -12,6 +12,7 @@
<script src="jiodev.js" type="text/javascript"></script>
<script src="jio_ojs_storage.js" type="text/javascript"></script>
<script src="jio_appcachestorage.js"></script>
<script src="jio_configuration_storage.js"></script>
<!-- custom script -->
<script src="gadget_ojs_local_jio.js" type="text/javascript"></script>
......
......@@ -236,7 +236,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>976.52707.23546.23773</string> </value>
<value> <string>977.15823.10779.36625</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -254,7 +254,7 @@
</tuple>
<state>
<tuple>
<float>1563367270.05</float>
<float>1567674891.35</float>
<string>UTC</string>
</tuple>
</state>
......
/*global window, window, rJS, jIO, RSVP, UriTemplate, atob, console */
/*jslint indent: 2, maxerr: 10, maxlen: 80 */
(function (window, rJS, jIO, RSVP, UriTemplate, atob,
console) {
/*global window, window, rJS, jIO, RSVP, UriTemplate, console */
/*jslint indent: 2, maxerr: 10, maxlen: 95 */
(function (window, rJS, jIO, RSVP, UriTemplate, console) {
"use strict";
// jIO call wrapper for redirection to authentication page if needed
......@@ -64,44 +63,20 @@
});
}
function processHateoasDict(raw_dict) {
var raw_field_list, type, parent, field_key, field_id, return_dict = {};
return_dict.raw_dict = raw_dict;
/*jslint nomen: true*/
if (raw_dict.hasOwnProperty("_embedded") &&
raw_dict._embedded.hasOwnProperty("_view")) {
raw_field_list = raw_dict._embedded._view;
type = raw_dict._links.type.name;
parent = raw_dict._links.parent.name;
return_dict.parent_relative_url = "portal_types/" + parent;
return_dict.portal_type = type;
for (field_key in raw_field_list) {
if (raw_field_list.hasOwnProperty(field_key)) {
field_id = "";
if (raw_field_list[field_key]["default"] !== undefined &&
raw_field_list[field_key]["default"] !== "") {
if (field_key.startsWith("my_")) {
field_id = field_key.replace("my_", "");
} else if (field_key.startsWith("your_")) {
field_id = field_key.replace("your_", "");
} else {
field_id = field_key;
}
return_dict[field_id] = raw_field_list[field_key]["default"];
}
function safeJioRemove(storage, id) {
return storage.remove(id)
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return;
}
}
} else {
// ignore non configuration elements
return raw_dict;
}
return return_dict;
throw error;
});
}
rJS(window)
.ready(function (gadget) {
// Initialize the gadget local parameters
gadget.state_parameter_dict = {};
})
......@@ -111,141 +86,131 @@
.declareAcquiredMethod("setSetting", "setSetting")
.declareAcquiredMethod('getUrlFor', 'getUrlFor')
.declareMethod('clean', function (appcache_storage, origin_url) {
if (!appcache_storage) { return; }
var gadget = this,
document_id_list = [origin_url,
"appcache-stored",
"portal_skins/erp5_text_editor/" +
"Base_cloneDocumentForTextEditor",
"portal_skins/erp5_text_editor/" +
"Base_viewNewContentDialogForTextEditor",
"portal_skins/erp5_text_editor/" +
"WebPageModule_viewWebPageListAsJioForTextEditor",
"portal_skins/erp5_text_editor/" +
"WebPage_viewAsTextDocumentForTextEditor",
"portal_types/Web Page",
"portal_types/Web Page Module",
"portal_types/Web Page Module/text_editor_view",
"portal_types/Web Page/text_editor_clone"],
promise_list = [],
i = 0,
current_version,
index;
current_version = window.location.href.replace(window.location.hash, "");
index = current_version.indexOf(window.location.host) + window.location.host.length;
current_version = current_version.substr(index);
return gadget.getSetting("migration_version")
.push(function (migration_version) {
if (migration_version !== current_version) {
for (i = 0; i < document_id_list.length; i += 1) {
promise_list = [safeJioRemove(appcache_storage, document_id_list[i])];
}
return RSVP.all(promise_list);
}
})
.push(function () {
return gadget.setSetting("migration_version", current_version);
})
.push(function () {
return current_version;
});
})
.declareMethod('createJio', function (jio_options) {
if (jio_options === undefined) { return; }
var gadget = this,
appcache_storage,
hateoas_section,
hateoas_section_and_view,
origin_url = window.location.href,
jio_appchache_options = {
type: "replicate",
parallel_operation_attachment_amount: 10,
parallel_operation_amount: 1,
conflict_handling: 2,
signature_hash_key: 'hash',
check_remote_attachment_modification: true,
check_remote_attachment_creation: true,
check_remote_attachment_deletion: true,
check_remote_deletion: true,
check_local_creation: false,
check_local_deletion: false,
check_local_modification: false,
signature_sub_storage: {
type: "query",
sub_storage: {
type: "memory"
}
},
local_sub_storage: {},
remote_sub_storage: {
type: "saferepair",
sub_storage: {
type: "appcache",
manifest: ""
origin_url = window.location.href;
return RSVP.Queue()
.push(function () {
return RSVP.all([
gadget.getSetting('configuration_manifest'),
gadget.getSetting('jio_storage_name')
]);
})
.push(function (result_list) {
var jio_appchache_options = {
type: "replicate",
parallel_operation_attachment_amount: 10,
parallel_operation_amount: 1,
conflict_handling: 2, //keep remote
signature_hash_key: 'hash',
check_remote_attachment_modification: true,
check_remote_attachment_creation: true,
check_remote_attachment_deletion: true,
check_remote_deletion: true,
check_local_creation: false,
check_local_deletion: false,
check_local_modification: false,
signature_sub_storage: {
type: "query",
sub_storage: {
type: "indexeddb",
database: result_list[1] + "-configuration-hash"
}
},
local_sub_storage: JSON.parse(JSON.stringify(jio_options)),
remote_sub_storage: {
type: "saferepair",
sub_storage: {
type: "configuration",
origin_url: origin_url,
hateoas_appcache: "hateoas_appcache",
manifest: result_list[0],
sub_storage: {
type: "appcache",
origin_url: origin_url,
manifest: result_list[0]
}
}
}
};
jio_options = {
type: 'dateupdater',
sub_storage: jio_options,
property_list: ['modification_date']
};
try {
gadget.state_parameter_dict.jio_storage = jIO.createJIO(jio_options);
} catch (error) {
gadget.state_parameter_dict.jio_storage = undefined;
}
},
sync_flag = "appcache-stored",
configuration_ids_list = [];
if (jio_options === undefined) {
return;
}
jio_appchache_options.local_sub_storage = JSON.parse(
JSON.stringify(jio_options)
);
jio_options = {
type: 'dateupdater',
sub_storage: jio_options,
property_list: ['modification_date']
};
try {
this.state_parameter_dict.jio_storage = jIO.createJIO(jio_options);
} catch (error) {
this.state_parameter_dict.jio_storage = undefined;
}
return gadget.getSetting("app_configuration")
.push(function (app_configuration) {
jio_appchache_options.remote_sub_storage.sub_storage.manifest =
app_configuration;
return gadget.getSetting("hateoas_appcache");
if (result_list[0] === undefined) { return; }
if (result_list[1] === undefined) { return; }
appcache_storage = jIO.createJIO(jio_appchache_options);
})
.push(function (hateoas_appcache) {
hateoas_section = "./" + hateoas_appcache + "/";
hateoas_section_and_view = hateoas_section + "definition_view/";
return gadget.getSetting("jio_storage_name");
.push(function () {
return gadget.clean(appcache_storage, origin_url);
})
.push(function (jio_storage_name) {
if (jio_storage_name === undefined) { return; }
appcache_storage = jIO.createJIO(jio_appchache_options);
// verify if appcache-local sync needs to be done
return appcache_storage.get(sync_flag)
.push(undefined, function (error) {
if (error && String(error.status_code) !== "404") {
throw error;
}
return appcache_storage.repair()
.push(function () {
return appcache_storage.allAttachments(origin_url)
.push(function (attachment_dict) {
var id, promise_list = [], i = 0;
for (id in attachment_dict) {
if (attachment_dict.hasOwnProperty(id)) {
if (id.startsWith(hateoas_section)) {
promise_list.push(
appcache_storage
.getAttachment(origin_url, id,
{"format": "json"}
)
);
} else {
promise_list.push(
appcache_storage
.getAttachment(origin_url, id)
);
}
configuration_ids_list[i] = id;
i += 1;
}
}
return RSVP.all(promise_list);
})
.push(function (content_list) {
var i, id, content, promise_list = [];
for (i = 0; i < content_list.length; i += 1) {
id = configuration_ids_list[i];
if (id.startsWith(hateoas_section)) {
id = id.replace(hateoas_section_and_view, "");
id = atob(id);
content = processHateoasDict(content_list[i]);
promise_list.push(appcache_storage.put(id, content));
}
}
return RSVP.all(promise_list);
})
.push(function () {
return appcache_storage.put(sync_flag, {})
.push(undefined);
}, function (error) {
console.log("Error while appcache-local " +
"storage synchronization. Bad " +
"configuration maybe?");
console.log(error);
throw error;
});
}, function (error) {
console.log("Error while appcache-local " +
"storage synchronization");
if (error && error.currentTarget &&
error.currentTarget.status === 401) {
console.log("Unauthorized access to storage," +
"sync cancelled");
gadget.state_parameter_dict.jio_storage_name = "ERP5";
return;
}
console.log(error);
throw error;
});
});
.push(function (current_version) {
if (appcache_storage) {
//if the app version has changed, force configuration storage sync
return appcache_storage.repair(current_version);
}
})
.push(undefined, function (error) {
console.log("Error while appcache-local " +
"storage synchronization");
if (error && error.currentTarget &&
error.currentTarget.status === 401) {
console.log("Unauthorized access to storage," +
"sync cancelled");
gadget.state_parameter_dict.jio_storage_name = "ERP5";
return;
}
console.log(error);
throw error;
});
})
.declareMethod('allDocs', function () {
......@@ -279,5 +244,4 @@
return wrapJioCall(this, 'repair', arguments);
});
}(window, rJS, jIO, RSVP, UriTemplate, atob,
console));
\ No newline at end of file
}(window, rJS, jIO, RSVP, UriTemplate, console));
\ No newline at end of file
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>978.6623.19198.10018</string> </value>
<value> <string>978.21248.62721.2184</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1567499774.35</float>
<float>1568041535.95</float>
<string>UTC</string>
</tuple>
</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