Commit 14354bad authored by Roque's avatar Roque

erp5_officejs: refactoring in new officejs approach

- reorginize and extend app setting and customization
- each portal type has its own setting dictionary
- handling blob types in document render and submit
- remove hardcoded app configuration manifest
- allow custom view in controller
- common configurations refactored
- handle_action handles notify submit and redirect base on custom action
return
- controller default page forces storage sync if app version changes
- fix 401 login redirect in storage selection
- general code refactoring and cleanup
parent 2947b9d1
...@@ -91,6 +91,30 @@ ...@@ -91,6 +91,30 @@
})); }));
} }
function getFormInfo(form_definition) {
var child_gadget_url,
form_type,
action_category = form_definition.action_type;
switch (action_category) {
case 'object_list':
form_type = 'list';
child_gadget_url = 'gadget_erp5_pt_form_list.html';
break;
case 'object_dialog':
form_type = 'dialog';
child_gadget_url = 'gadget_erp5_pt_form_dialog.html';
break;
case 'object_jio_js_script':
form_type = 'dialog';
child_gadget_url = 'gadget_erp5_pt_form_dialog.html';
break;
default:
form_type = 'page';
child_gadget_url = 'gadget_erp5_pt_form_view_editable.html';
}
return [form_type, child_gadget_url];
}
rJS(window) rJS(window)
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// Acquired methods // Acquired methods
...@@ -175,46 +199,72 @@ ...@@ -175,46 +199,72 @@
}); });
}) })
.declareMethod("getFormInfo", function (form_definition) { .declareMethod("getDialogFormDefinition", function (form_name, category) {
var child_gadget_url, var gadget = this,
form_type, form_definition,
action_category = form_definition.action_type; form_info;
switch (action_category) { return gadget.getSetting('portal_skin_folder')
case 'object_list': .push(function (portal_skin_folder) {
form_type = 'list'; return gadget.jio_get("portal_skins/" + portal_skin_folder + "/" +
child_gadget_url = 'gadget_erp5_pt_form_list.html'; form_name);
break; })
case 'object_dialog': .push(function (form_result) {
form_type = 'dialog'; form_definition = form_result.raw_dict._embedded._view
child_gadget_url = 'gadget_erp5_pt_form_dialog.html'; ._embedded.form_definition;
break; form_definition.fields_raw_properties = form_result.raw_dict._embedded
case 'object_jio_js_script': ._view.my_fields_raw_properties["default"];
form_type = 'dialog'; form_definition._actions = form_result.raw_dict._embedded
child_gadget_url = 'gadget_erp5_pt_form_dialog.html'; ._view._actions;
break; form_definition.group_list = form_result.raw_dict.group_list;
default: form_definition.title = form_result.raw_dict.title;
form_type = 'page'; form_definition.portal_type_dict = {};
child_gadget_url = 'gadget_erp5_pt_form_view_editable.html'; form_definition.action_type = category;
} form_info = getFormInfo(form_definition);
return [form_type, child_gadget_url]; form_definition.form_type = form_info[0];
form_definition.child_gadget_url = form_info[1];
return form_definition;
});
}) })
.declareMethod("getFormDefinition", function (portal_type, .declareMethod("getFormDefinition", function (portal_type,
action_reference) { action_reference) {
var gadget = this, var gadget = this,
query = buildSearchQuery(portal_type, action_reference), query = buildSearchQuery(portal_type, action_reference),
portal_type_dict_setting = portal_type.replace(/ /g, '_')
.toLowerCase() + "_dict",
portal_type_dict = {},
action_type, action_type,
action_title, action_title,
form_definition, form_definition,
portal_skin_folder, portal_skin_folder,
app_allowed_sub_types,
form_info,
error; error;
return gadget.getSetting("portal_skin_folder") return RSVP.Queue()
.push(function (result) { .push(function () {
if (!result) { return RSVP.all([
gadget.getSetting(portal_type_dict_setting),
gadget.getSetting("portal_skin_folder"),
gadget.getSetting("app_allowed_sub_types")
]);
})
.push(function (result_list) {
app_allowed_sub_types = result_list[2];
if (!result_list[1]) {
throw new Error("Missing site configuration 'portal_skin_folder'"); throw new Error("Missing site configuration 'portal_skin_folder'");
} }
result = "portal_skins/" + result; portal_skin_folder = "portal_skins/" + result_list[1];
portal_skin_folder = result; if (result_list[0]) {
try {
portal_type_dict = window.JSON.parse(result_list[0]);
} catch (e) {
if (e instanceof SyntaxError) {
throw new Error("Bad JSON dict in configuration setting '" +
portal_type_dict_setting + "'");
}
throw e;
}
}
return gadget.jio_allDocs({query: query}); return gadget.jio_allDocs({query: query});
}) })
.push(function (data) { .push(function (data) {
...@@ -244,7 +294,8 @@ ...@@ -244,7 +294,8 @@
._view._actions; ._view._actions;
//[PATCH] if custom action and anonymous //[PATCH] if custom action and anonymous
// get _actions field from fields_raw_properties // get _actions field from fields_raw_properties
if ("_actions" in form_definition.fields_raw_properties) { if (form_definition.fields_raw_properties
.hasOwnProperty("_actions")) {
if (!form_definition._actions && if (!form_definition._actions &&
action_type === "object_jio_js_script") { action_type === "object_jio_js_script") {
form_definition._actions = form_definition form_definition._actions = form_definition
...@@ -254,28 +305,29 @@ ...@@ -254,28 +305,29 @@
} }
form_definition.group_list = form_result.raw_dict.group_list; form_definition.group_list = form_result.raw_dict.group_list;
form_definition.action_type = action_type; form_definition.action_type = action_type;
form_info = getFormInfo(form_definition);
form_definition.form_type = form_info[0];
form_definition.child_gadget_url = form_info[1];
form_definition.title = action_title; form_definition.title = action_title;
return gadget.getSetting("app_allowed_sub_types"); form_definition.portal_type_dict = portal_type_dict;
}) return formatSettingList(app_allowed_sub_types, portal_type);
.push(function (allowed_sub_types_setting) {
return formatSettingList(allowed_sub_types_setting, portal_type);
}) })
.push(function (allowed_sub_types_pairs) { .push(function (allowed_sub_types_pairs) {
var allowed_sub_types = allowed_sub_types_pairs.map(function (pair) { var allowed_sub_types = allowed_sub_types_pairs.map(function (pair) {
return pair[1]; return pair[1];
}); });
form_definition.allowed_sub_types_list = allowed_sub_types; form_definition.allowed_sub_types_list = allowed_sub_types;
form_definition.new_content_dialog_form = portal_type_dict
.new_content_dialog_form;
form_definition.new_content_category = portal_type_dict
.new_content_category;
return gadget.getViewAndActionDict(portal_type); return gadget.getViewAndActionDict(portal_type);
}) })
.push(function (action_view_dict) { .push(function (action_view_dict) {
form_definition.has_more_views = form_definition.portal_type_dict.has_more_views =
Object.keys(action_view_dict.view_list).length > 1; Object.keys(action_view_dict.view_list).length > 1;
form_definition.has_more_actions = form_definition.portal_type_dict.has_more_actions =
Object.keys(action_view_dict.action_list).length > 0; Object.keys(action_view_dict.action_list).length > 0;
return gadget.getSetting('hide_header_add_button');
})
.push(function (hide_add_button_setting) {
form_definition.hide_add_button = hide_add_button_setting === "1";
return form_definition; return form_definition;
}); });
}); });
......
...@@ -269,7 +269,7 @@ ...@@ -269,7 +269,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>977.16294.5398.40106</string> </value> <value> <string>978.26807.33358.5717</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -287,7 +287,7 @@ ...@@ -287,7 +287,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1563811574.1</float> <float>1568651276.38</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
.declareAcquiredMethod("jio_get", "jio_get") .declareAcquiredMethod("jio_get", "jio_get")
.declareAcquiredMethod("jio_put", "jio_put") .declareAcquiredMethod("jio_put", "jio_put")
.declareAcquiredMethod("redirect", "redirect")
.declareAcquiredMethod("getSetting", "getSetting") .declareAcquiredMethod("getSetting", "getSetting")
.declareAcquiredMethod("notifySubmitted", 'notifySubmitted') .declareAcquiredMethod("notifySubmitted", 'notifySubmitted')
.declareAcquiredMethod("notifySubmitting", "notifySubmitting") .declareAcquiredMethod("notifySubmitting", "notifySubmitting")
...@@ -20,28 +21,38 @@ ...@@ -20,28 +21,38 @@
.declareMethod("render", function (options) { .declareMethod("render", function (options) {
var gadget = this, var gadget = this,
default_view,
app_view, app_view,
form_definition,
gadget_util, gadget_util,
jio_document, jio_document,
portal_type, portal_type,
front_page; current_version,
return RSVP.Queue() 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) {
//if app version has changed, force storage sync
return gadget.redirect({
'command': 'display',
'options': {
'page': 'ojs_sync',
'auto_repair': true
}
});
}
})
.push(function () { .push(function () {
return RSVP.all([ return RSVP.all([
gadget.declareGadget("gadget_officejs_common_util.html"), gadget.declareGadget("gadget_officejs_common_util.html"),
gadget.getSetting('app_view_reference'), gadget.getSetting('app_view_reference')
gadget.getSetting('default_view_reference'),
gadget.getSetting('documents_editable')
]); ]);
}) })
.push(function (result_list) { .push(function (result_list) {
gadget_util = result_list[0]; gadget_util = result_list[0];
app_view = options.action || result_list[1]; app_view = options.action || result_list[1];
default_view = result_list[2];
options.editable = ((result_list[3] == "1") ?
true : options.editable);
return gadget.jio_get(options.jio_key); return gadget.jio_get(options.jio_key);
}) })
.push(function (result) { .push(function (result) {
...@@ -64,46 +75,38 @@ ...@@ -64,46 +75,38 @@
} else { } else {
portal_type = parent_portal_type; portal_type = parent_portal_type;
} }
front_page = portal_type === parent_portal_type;
return gadget_util.getFormDefinition(portal_type, app_view); return gadget_util.getFormDefinition(portal_type, app_view);
}) })
.push(function (result) { .push(function (result) {
return result; return result;
}, function (error) {
if (error.status_code === 400) {
return gadget_util.getFormDefinition(portal_type, default_view);
}
throw error;
}) })
.push(function (result) { .push(function (form_definition) {
form_definition = result;
return gadget_util.getFormInfo(form_definition);
})
.push(function (form_info) {
var form_type = form_info[0],
child_gadget_url = form_info[1];
return gadget.changeState({ return gadget.changeState({
jio_key: options.jio_key, jio_key: options.jio_key,
doc: jio_document, doc: jio_document,
portal_type: portal_type, portal_type: portal_type,
child_gadget_url: child_gadget_url, child_gadget_url: form_definition.child_gadget_url,
form_definition: form_definition, form_definition: form_definition,
form_type: form_type, form_type: form_definition.form_type,
editable: options.editable, view: options.view || app_view
view: options.view || default_view,
front_page: front_page
}); });
}); });
}) })
.onStateChange(function () { .onStateChange(function () {
var fragment = document.createElement('div'), var fragment = document.createElement('div'),
gadget = this; gadget = this,
view_gadget_url = "gadget_officejs_form_view.html",
custom_gadget_url = gadget.state.form_definition.portal_type_dict
.custom_view_gadget;
while (this.element.firstChild) { while (this.element.firstChild) {
this.element.removeChild(this.element.firstChild); this.element.removeChild(this.element.firstChild);
} }
this.element.appendChild(fragment); if (custom_gadget_url) {
return gadget.declareGadget("gadget_officejs_form_view.html", view_gadget_url = custom_gadget_url;
}
gadget.element.appendChild(fragment);
return gadget.declareGadget(view_gadget_url,
{element: fragment, scope: 'form_view'}) {element: fragment, scope: 'form_view'})
.push(function (form_view_gadget) { .push(function (form_view_gadget) {
return form_view_gadget.render(gadget.state); return form_view_gadget.render(gadget.state);
......
...@@ -228,7 +228,7 @@ ...@@ -228,7 +228,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>977.8828.48212.64904</string> </value> <value> <string>978.36723.12245.1757</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>1563366536.83</float> <float>1568969561.34</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
/*global document, window, rJS, RSVP, ensureArray */ /*global document, window, rJS, RSVP, Blob, URL, jIO, ensureArray */
/*jslint nomen: true, indent: 2, maxerr: 10, maxlen: 80 */ /*jslint nomen: true, indent: 2, maxerr: 10, maxlen: 80 */
(function (document, window, rJS, RSVP, ensureArray) { (function (document, window, rJS, RSVP, Blob, URL, jIO, ensureArray) {
"use strict"; "use strict";
function renderField(field_id, field_definition, context_document) { function renderField(field_id, field_definition,
context_document, data, blob_type) {
var key, raw_value, override, final_value, item_list, result = {}; var key, raw_value, override, final_value, item_list, result = {};
for (key in field_definition.values) { for (key in field_definition.values) {
if (field_definition.values.hasOwnProperty(key)) { if (field_definition.values.hasOwnProperty(key)) {
...@@ -42,10 +43,13 @@ ...@@ -42,10 +43,13 @@
result["default"] = context_document[field_id]; result["default"] = context_document[field_id];
} }
} }
if (result.renderjs_extra && blob_type) {
result["default"] = data;
}
return result; return result;
} }
function renderForm(form_definition, context_document) { function renderForm(form_definition, context_document, data, blob_type) {
var i, j, field_list, field_info, my_element, element_id, rendered_field, var i, j, field_list, field_info, my_element, element_id, rendered_field,
raw_properties = form_definition.fields_raw_properties, raw_properties = form_definition.fields_raw_properties,
form_json = { form_json = {
...@@ -69,7 +73,7 @@ ...@@ -69,7 +73,7 @@
if (element_id && raw_properties.hasOwnProperty(my_element)) { if (element_id && raw_properties.hasOwnProperty(my_element)) {
field_info = raw_properties[my_element]; field_info = raw_properties[my_element];
rendered_field = renderField(element_id, field_info, rendered_field = renderField(element_id, field_info,
context_document); context_document, data, blob_type);
form_json.erp5_document._embedded._view[my_element] = form_json.erp5_document._embedded._view[my_element] =
rendered_field; rendered_field;
} }
...@@ -86,10 +90,12 @@ ...@@ -86,10 +90,12 @@
// Acquired methods // Acquired methods
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
.declareAcquiredMethod("updateHeader", "updateHeader") .declareAcquiredMethod("updateHeader", "updateHeader")
.declareAcquiredMethod("isDesktopMedia", "isDesktopMedia")
.declareAcquiredMethod("getUrlForList", "getUrlForList") .declareAcquiredMethod("getUrlForList", "getUrlForList")
.declareAcquiredMethod("getSetting", "getSetting")
.declareAcquiredMethod("jio_allDocs", "jio_allDocs") .declareAcquiredMethod("jio_allDocs", "jio_allDocs")
.declareAcquiredMethod("jio_getAttachment", "jio_getAttachment")
.declareAcquiredMethod("jio_putAttachment", "jio_putAttachment")
.declareAcquiredMethod("notifySubmitting", "notifySubmitting")
.declareAcquiredMethod("notifySubmitted", 'notifySubmitted')
// XXX Hardcoded for modification_date rendering // XXX Hardcoded for modification_date rendering
.allowPublicAcquisition("jio_allDocs", function (param_list) { .allowPublicAcquisition("jio_allDocs", function (param_list) {
...@@ -133,14 +139,15 @@ ...@@ -133,14 +139,15 @@
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
.declareMethod("triggerSubmit", function (argument_list) { .declareMethod("triggerSubmit", function (argument_list) {
return this.getDeclaredGadget('fg') var gadget = this, data, name_list;
.push(function (gadget) { return gadget.notifySubmitting()
if (gadget.state.save_action !== true) { .push(function () {
//rely on child gadget to submit (filter, panels, etc.) return gadget.getDeclaredGadget('erp5_pt_gadget')
return gadget.triggerSubmit(argument_list); .push(function (child_gadget) {
} if (!child_gadget.state.editable) {
var action = gadget.state.erp5_document._embedded._view._actions.put; return child_gadget.triggerSubmit(argument_list);
return gadget.getDeclaredGadget("erp5_form") }
return child_gadget.getDeclaredGadget("erp5_form")
.push(function (sub_gadget) { .push(function (sub_gadget) {
return sub_gadget.checkValidity(); return sub_gadget.checkValidity();
}) })
...@@ -148,41 +155,172 @@ ...@@ -148,41 +155,172 @@
if (!is_valid) { if (!is_valid) {
return null; return null;
} }
return gadget.getContent(); return child_gadget.getContent();
}) })
.push(function (content_dict) { .push(function (content_dict) {
if (content_dict === null) { if (content_dict === null) {
return; return;
} }
return gadget.submitContent( if (gadget.state.blob_type) {
gadget.state.jio_key, data = content_dict.text_content;
action.href, delete content_dict.text_content;
//ONLY OFFICE
if (gadget.state.only_office) {
name_list = gadget.state.doc.filename.split('.');
if (name_list.pop() !== gadget.state.mime_type) {
name_list.push(gadget.state.mime_type);
content_dict.filename = name_list.join('.');
}
content_dict.content_type = gadget.state.blob_type;
}//
}
return child_gadget.submitContent(
child_gadget.state.jio_key,
undefined,
content_dict content_dict
); )
}); // page form handles failures well enough .push(function () {
if (gadget.state.blob_type) {
return gadget
.jio_putAttachment(child_gadget.state.jio_key,
'data',
jIO.util.dataURItoBlob(data))
//ONLY OFFICE
.push(function () {
if (gadget.state.only_office) {
return gadget
.declareGadget("gadget_ojs_cloudooo.html");
}
})
.push(function (cloudooo) {
if (gadget.state.only_office) {
return cloudooo
.putAllCloudoooConvertionOperation({
format: gadget.state.mime_type,
jio_key: child_gadget.state.jio_key
});
}
});//
}
});
});
});
}, function (error) {
console.log(error);
return gadget.notifySubmitted({
message: "Submit failed",
status: "error"
});
}); });
}) })
.declareMethod("render", function (options) { .declareMethod("render", function (options) {
var state_dict = { var gadget = this,
state_dict = {
doc: options.doc, doc: options.doc,
form_definition: options.form_definition, form_definition: options.form_definition,
child_gadget_url: options.child_gadget_url, child_gadget_url: options.child_gadget_url,
options: options options: options
}; },
return this.changeState(state_dict); portal_type_dict = options.form_definition.portal_type_dict,
blob;
//ONLY OFFICE
if (portal_type_dict.only_office && options.doc) {
//TODO doc should come with filename. for now hardcoded if undefined
if (!options.doc.filename) {
options.doc.filename = "default.docx";
}
state_dict.mime_type = portal_type_dict.file_extension;
state_dict.only_office = true;
if (options.doc.action) {
return gadget.changeState(state_dict);
}
state_dict.content_editable = options.doc.content_type === undefined ||
options.doc.content_type.indexOf("application/x-asc") === 0;
return new RSVP.Queue()
.push(function () {
if (!state_dict.content_editable) {
return gadget.jio_getAttachment(options.jio_key, "data");
}
return gadget.declareGadget("gadget_ojs_cloudooo.html")
.push(function (ojs_cloudooo) {
return ojs_cloudooo.getConvertedBlob({
jio_key: options.jio_key,
format: state_dict.mime_type,
filename: options.doc.filename
});
});
})
.push(undefined, function (error) {
if (error instanceof jIO.util.jIOError &&
error.status_code === 404) {
return new Blob();
}
throw error;
})
.push(function (blob) {
if (state_dict.content_editable) {
return jIO.util.readBlobAsDataURL(blob);
}
return jIO.util.readBlobAsText(blob);
})
.push(function (result) {
state_dict.data = result.target.result;
if (portal_type_dict.blob_type) {
state_dict.blob_type = portal_type_dict.blob_type;
} else {
state_dict.blob_type = "ooffice";
}
return gadget.changeState(state_dict);
});
}//
if (portal_type_dict.blob_type) {
if (options.jio_key) {
return gadget.jio_getAttachment(options.jio_key, "data")
.push(undefined, function (error) {
if (error.status_code === 404) {
return new Blob([''], {type: portal_type_dict.blob_type});
}
throw new Error(error);
})
.push(function (result) {
blob = result;
blob.name = options.jio_key;
return jIO.util.readBlobAsDataURL(blob);
})
.push(function (result) {
if (portal_type_dict.blob_create_object_url) {
state_dict.data = URL.createObjectURL(blob);
} else {
state_dict.data = {blob: result.target.result,
name: options.jio_key};
}
state_dict.blob_type = portal_type_dict.blob_type;
return gadget.changeState(state_dict);
});
}
}
return gadget.changeState(state_dict);
}) })
.onStateChange(function onStateChange() { .onStateChange(function onStateChange() {
var fragment = document.createElement('div'), var fragment = document.createElement('div'),
gadget = this, gadget = this,
form_json = renderForm(gadget.state.form_definition, gadget.state.doc); form_json = renderForm(gadget.state.form_definition, gadget.state.doc,
gadget.state.data, gadget.state.blob_type);
while (gadget.element.firstChild) { while (gadget.element.firstChild) {
gadget.element.removeChild(gadget.element.firstChild); gadget.element.removeChild(gadget.element.firstChild);
} }
gadget.element.appendChild(fragment); gadget.element.appendChild(fragment);
return gadget.declareGadget(gadget.state.child_gadget_url, return gadget.declareGadget(gadget.state.child_gadget_url,
{element: fragment, scope: 'fg'}) {element: fragment,
scope: 'erp5_pt_gadget'})
.push(function (form_gadget) { .push(function (form_gadget) {
return gadget.renderSubGadget(gadget.state.options, form_gadget, return gadget.renderSubGadget(gadget.state.options, form_gadget,
form_json); form_json);
...@@ -190,15 +328,23 @@ ...@@ -190,15 +328,23 @@
}) })
.declareMethod("renderSubGadget", function (options, subgadget, form_json) { .declareMethod("renderSubGadget", function (options, subgadget, form_json) {
var this_gadget = this, erp5_document = form_json.erp5_document, var gadget = this, erp5_document = form_json.erp5_document,
page_title = options.portal_type, portal_type_dict = form_json.form_definition.portal_type_dict,
add_url = false; page_title;
if (options.doc && options.doc.title) {
page_title = options.doc.title;
} else if (options.doc && options.doc.header_title) {
page_title = options.doc.header_title;
} else {
page_title = portal_type_dict.title;
}
return subgadget.render({ return subgadget.render({
jio_key: options.jio_key, jio_key: options.jio_key,
doc: options.doc, doc: options.doc,
erp5_document: form_json.erp5_document, erp5_document: form_json.erp5_document,
form_definition: form_json.form_definition, form_definition: form_json.form_definition,
editable: options.editable, editable: portal_type_dict.editable,
save_action: portal_type_dict.editable,
view: options.view, view: options.view,
form_json: form_json form_json: form_json
}) })
...@@ -213,87 +359,68 @@ ...@@ -213,87 +359,68 @@
{command: 'selection_previous'}, {command: 'selection_previous'},
{command: 'selection_next'}, {command: 'selection_next'},
{command: 'change', options: {page: "export"}}, {command: 'change', options: {page: "export"}},
{command: 'display', options: {}} {command: 'display', options: {}},
]; {command: 'change', options: {page: "create_document",
if (options.doc) {
page_title = options.doc.title;
}
erp5_document = form_json.erp5_document;
if (form_json.form_definition.allowed_sub_types_list &&
form_json.form_definition.allowed_sub_types_list.length > 0 &&
!form_json.form_definition.hide_add_button) {
url_for_parameter_list.push({command: 'change',
options: {page: "create_document",
jio_key: options.jio_key, jio_key: options.jio_key,
portal_type: portal_type:
options.portal_type, options.portal_type,
new_content_dialog_form:
form_json.form_definition
.new_content_dialog_form,
new_content_category:
form_json.form_definition
.new_content_category,
allowed_sub_types_list: allowed_sub_types_list:
form_json.form_definition form_json.form_definition
.allowed_sub_types_list .allowed_sub_types_list
}}); }}
add_url = true; ];
} erp5_document = form_json.erp5_document;
return RSVP.all([ return RSVP.all([
this_gadget.getUrlForList(url_for_parameter_list), gadget.getUrlForList(url_for_parameter_list)
this_gadget.isDesktopMedia(),
this_gadget.getSetting('document_title_plural'),
this_gadget.getSetting('upload_dict', false)
]); ]);
}) })
.push(function (result_list) { .push(function (result_list) {
var url_list = result_list[0], header_dict; var url_list = result_list[0],
header_dict = { "page_title": page_title };
if (options.form_type === 'dialog') { if (options.form_type === 'dialog') {
header_dict = {
page_title: page_title,
//TODO: find correct url //TODO: find correct url
cancel_url: url_list[6] header_dict.cancel_url = url_list[6];
};
} else { } else {
if (options.form_type === 'list') { header_dict.panel_action = portal_type_dict.panel_action === 1;
header_dict = { if (portal_type_dict.filter_action) {
panel_action: true, header_dict.filter_action = true;
//TODO which header links/buttons will be displayed
//should be come from the configuration (form_definition)
//jump_url: "",
//fast_input_url: "",
filter_action: true,
page_title: result_list[2]
};
if (!options.front_page) {
header_dict.selection_url = url_list[2];
header_dict.front_url = url_list[6];
} }
} else { if (portal_type_dict.previous_next_button) {
header_dict = { header_dict.previous_url = url_list[3];
selection_url: url_list[2], header_dict.next_url = url_list[4];
previous_url: url_list[3],
next_url: url_list[4],
page_title: page_title
};
if (options.form_definition.has_more_views) {
header_dict.tab_url = url_list[0];
} }
if (options.editable === true || options.editable === "true") { if (portal_type_dict.history_previous_link) {
header_dict.save_action = true; header_dict.selection_url = url_list[2];
} }
if (portal_type_dict.has_more_views) {
header_dict.tab_url = url_list[0];
} }
if (options.form_definition.has_more_actions || header_dict.save_action = portal_type_dict.editable === 1;
options.form_definition.has_more_views) { if (portal_type_dict.has_more_actions ||
portal_type_dict.has_more_views) {
header_dict.actions_url = url_list[1]; header_dict.actions_url = url_list[1];
} }
if (add_url) { if (form_json.form_definition.allowed_sub_types_list &&
header_dict.add_url = url_list[url_list.length - 1]; form_json.form_definition.allowed_sub_types_list.length > 0 &&
!portal_type_dict.hide_add_button) {
header_dict.add_url = url_list[7];
} }
if (result_list[1]) { if (portal_type_dict.export_button) {
header_dict.export_url = ( if (erp5_document._links.action_object_jio_report ||
erp5_document._links.action_object_jio_report ||
erp5_document._links.action_object_jio_exchange || erp5_document._links.action_object_jio_exchange ||
erp5_document._links.action_object_jio_print erp5_document._links.action_object_jio_print) {
) ? url_list[5] : ''; header_dict.export_url = url_list[5];
}
} }
} }
return this_gadget.updateHeader(header_dict); return gadget.updateHeader(header_dict);
}); });
}); });
}(document, window, rJS, RSVP, ensureArray)); }(document, window, rJS, RSVP, Blob, URL, jIO, ensureArray));
...@@ -269,7 +269,7 @@ ...@@ -269,7 +269,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>977.6084.46638.22954</string> </value> <value> <string>978.32380.21754.21845</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -287,7 +287,7 @@ ...@@ -287,7 +287,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1563366620.2</float> <float>1568708910.31</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
/*global window, window, rJS, jIO, RSVP, document, URLSearchParams, UriTemplate, atob, console */ /*global window, window, rJS, jIO, RSVP, UriTemplate, atob, console */
/*jslint indent: 2, maxerr: 10, maxlen: 80 */ /*jslint indent: 2, maxerr: 10, maxlen: 80 */
(function (window, rJS, jIO, RSVP, document, URLSearchParams, UriTemplate, atob, (function (window, rJS, jIO, RSVP, UriTemplate, atob,
console) { console) {
"use strict"; "use strict";
...@@ -112,13 +112,11 @@ ...@@ -112,13 +112,11 @@
.declareAcquiredMethod('getUrlFor', 'getUrlFor') .declareAcquiredMethod('getUrlFor', 'getUrlFor')
.declareMethod('createJio', function (jio_options) { .declareMethod('createJio', function (jio_options) {
var appcache_storage, var gadget = this,
appcache_storage,
hateoas_section,
hateoas_section_and_view,
origin_url = window.location.href, origin_url = window.location.href,
hateoas_section = "./hateoas_appcache/",
hateoas_section_and_view = hateoas_section + "definition_view/",
// TODO manifest should come from gadget.props.cache_file
// add script in html body
manifest = "gadget_officejs_text_editor.configuration",
jio_appchache_options = { jio_appchache_options = {
type: "replicate", type: "replicate",
parallel_operation_attachment_amount: 10, parallel_operation_attachment_amount: 10,
...@@ -143,7 +141,7 @@ ...@@ -143,7 +141,7 @@
type: "saferepair", type: "saferepair",
sub_storage: { sub_storage: {
type: "appcache", type: "appcache",
manifest: manifest manifest: ""
} }
} }
}, },
...@@ -153,7 +151,8 @@ ...@@ -153,7 +151,8 @@
return; return;
} }
jio_appchache_options.local_sub_storage = JSON.parse( jio_appchache_options.local_sub_storage = JSON.parse(
JSON.stringify(jio_options)); JSON.stringify(jio_options)
);
jio_options = { jio_options = {
type: 'dateupdater', type: 'dateupdater',
sub_storage: jio_options, sub_storage: jio_options,
...@@ -164,7 +163,17 @@ ...@@ -164,7 +163,17 @@
} catch (error) { } catch (error) {
this.state_parameter_dict.jio_storage = undefined; this.state_parameter_dict.jio_storage = undefined;
} }
return this.getSetting("jio_storage_name") 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");
})
.push(function (hateoas_appcache) {
hateoas_section = "./" + hateoas_appcache + "/";
hateoas_section_and_view = hateoas_section + "definition_view/";
return gadget.getSetting("jio_storage_name");
})
.push(function (jio_storage_name) { .push(function (jio_storage_name) {
if (jio_storage_name === undefined) { return; } if (jio_storage_name === undefined) { return; }
appcache_storage = jIO.createJIO(jio_appchache_options); appcache_storage = jIO.createJIO(jio_appchache_options);
...@@ -178,18 +187,21 @@ ...@@ -178,18 +187,21 @@
.push(function () { .push(function () {
return appcache_storage.allAttachments(origin_url) return appcache_storage.allAttachments(origin_url)
.push(function (attachment_dict) { .push(function (attachment_dict) {
var id, base64, promise_list = [], i = 0; var id, promise_list = [], i = 0;
for (id in attachment_dict) { for (id in attachment_dict) {
if (attachment_dict.hasOwnProperty(id)) { if (attachment_dict.hasOwnProperty(id)) {
if (id.startsWith(hateoas_section)) { if (id.startsWith(hateoas_section)) {
promise_list.push( promise_list.push(
appcache_storage appcache_storage
.getAttachment(origin_url, id, .getAttachment(origin_url, id,
{"format": "json"})); {"format": "json"}
)
);
} else { } else {
promise_list.push( promise_list.push(
appcache_storage appcache_storage
.getAttachment(origin_url, id)); .getAttachment(origin_url, id)
);
} }
configuration_ids_list[i] = id; configuration_ids_list[i] = id;
i += 1; i += 1;
...@@ -198,7 +210,7 @@ ...@@ -198,7 +210,7 @@
return RSVP.all(promise_list); return RSVP.all(promise_list);
}) })
.push(function (content_list) { .push(function (content_list) {
var i, id, parser, urlParams, content, promise_list = []; var i, id, content, promise_list = [];
for (i = 0; i < content_list.length; i += 1) { for (i = 0; i < content_list.length; i += 1) {
id = configuration_ids_list[i]; id = configuration_ids_list[i];
if (id.startsWith(hateoas_section)) { if (id.startsWith(hateoas_section)) {
...@@ -224,11 +236,13 @@ ...@@ -224,11 +236,13 @@
console.log("Error while appcache-local " + console.log("Error while appcache-local " +
"storage synchronization"); "storage synchronization");
if (error && error.currentTarget && if (error && error.currentTarget &&
error.currentTarget.status === "401") { error.currentTarget.status === 401) {
console.log("Unauthorized access to storage," + console.log("Unauthorized access to storage," +
"sync cancelled"); "sync cancelled");
gadget.state_parameter_dict.jio_storage_name = "ERP5";
return; return;
} }
console.log(error);
throw error; throw error;
}); });
}); });
...@@ -265,5 +279,5 @@ ...@@ -265,5 +279,5 @@
return wrapJioCall(this, 'repair', arguments); return wrapJioCall(this, 'repair', arguments);
}); });
}(window, rJS, jIO, RSVP, document, URLSearchParams, UriTemplate, atob, }(window, rJS, jIO, RSVP, UriTemplate, atob,
console)); console));
\ No newline at end of file
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>976.64179.23426.41403</string> </value> <value> <string>978.6623.19198.10018</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1563367274.51</float> <float>1567499774.35</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
// Acquired methods // Acquired methods
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
.declareAcquiredMethod("jio_get", "jio_get") .declareAcquiredMethod("jio_get", "jio_get")
.declareAcquiredMethod("getSetting", "getSetting")
.declareAcquiredMethod("redirect", "redirect") .declareAcquiredMethod("redirect", "redirect")
.declareAcquiredMethod("jio_post", "jio_post") .declareAcquiredMethod("jio_post", "jio_post")
...@@ -16,32 +15,13 @@ ...@@ -16,32 +15,13 @@
// declared methods // declared methods
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
.declareMethod("createDocument", function (portal_type,
parent_portal_type) {
var gadget = this,
doc = {
title: "Untitled Document",
portal_type: portal_type,
parent_relative_url: parent_portal_type
};
return gadget.jio_post(doc)
.push(function (id) {
return gadget.redirect({
command: 'display',
options: {
jio_key: id,
editable: true
}
});
});
})
.declareMethod("render", function (options) { .declareMethod("render", function (options) {
var gadget = this, var gadget = this,
allowed_sub_types_list = options.allowed_sub_types_list.split(","), allowed_sub_types_list = options.allowed_sub_types_list.split(","),
parent_portal_type = options.portal_type, parent_portal_type = options.portal_type,
dialog_form = options.new_content_dialog_form,
dialog_category = options.new_content_category,
portal_type, portal_type,
form_definition,
document_title; document_title;
return gadget.jio_get(options.jio_key) return gadget.jio_get(options.jio_key)
.push(function (document) { .push(function (document) {
...@@ -53,32 +33,32 @@ ...@@ -53,32 +33,32 @@
}) })
.push(function (portal_type_result) { .push(function (portal_type_result) {
portal_type = portal_type_result; portal_type = portal_type_result;
return gadget.getSetting("new_content_action"); if (dialog_form) {
}) return gadget.declareGadget("gadget_officejs_common_util.html")
.push(function (new_content_action) { .push(function (gadget_util) {
if (!new_content_action) { return gadget_util.getDialogFormDefinition(dialog_form,
throw new Error("Missing site configuration 'new_content_action'"); dialog_category);
}
return gadget.jio_get(new_content_action);
}) })
.push(function (form_result) { .push(function (form_definition) {
form_definition = form_result.raw_dict._embedded._view
._embedded.form_definition;
form_definition.fields_raw_properties = form_result.raw_dict._embedded
._view.my_fields_raw_properties["default"];
form_definition._actions = form_result.raw_dict._embedded
._view._actions;
form_definition.group_list = form_result.raw_dict.group_list;
form_definition.title = "Create Document";
return gadget.changeState({ return gadget.changeState({
doc: { title: document_title, portal_type: allowed_sub_types_list }, doc: { header_title: form_definition.title || document_title,
portal_type: allowed_sub_types_list },
//TODO this should be a portal_dict setting and not global
parent_portal_type: parent_portal_type, parent_portal_type: parent_portal_type,
action_options: options, action_options: options,
child_gadget_url: 'gadget_erp5_pt_form_dialog.html', child_gadget_url: form_definition.child_gadget_url,
form_type: 'dialog', form_type: form_definition.form_type,
form_definition: form_definition, form_definition: form_definition,
view: "view", view: "view",
show_dialog: allowed_sub_types_list.length > 1 show_dialog: true
});
});
}
return gadget.changeState({
doc: { header_title: document_title,
portal_type: allowed_sub_types_list },
parent_portal_type: parent_portal_type,
show_dialog: false
}); });
}); });
}) })
...@@ -96,21 +76,57 @@ ...@@ -96,21 +76,57 @@
.push(function (form_view_gadget) { .push(function (form_view_gadget) {
return form_view_gadget.render(gadget.state); return form_view_gadget.render(gadget.state);
}); });
} else { }
// if there is only one sub portal type // if no form, skip dialog assuming there is only one portal type
// skip create document dialog rendering
return gadget.createDocument(gadget.state.doc.portal_type[0], return gadget.createDocument(gadget.state.doc.portal_type[0],
gadget.state.parent_portal_type gadget.state.parent_portal_type
.replace(/ /g, '_').toLowerCase()); .replace(/ /g, '_').toLowerCase());
})
.declareMethod("createDocument", function (portal_type,
parent_portal_type,
content) {
var gadget = this,
doc = {};
if (!content) {
doc.portal_type = portal_type;
doc.parent_relative_url = parent_portal_type;
} else {
doc = content;
}
if (!doc.title) {
doc.title = "Untitled document";
}
return gadget.jio_post(doc)
.push(function (id) {
return gadget.redirect({
command: 'display',
options: {
jio_key: id,
editable: true
} }
});
});
})
.declareMethod("triggerSubmit", function () {
return this.element.querySelector('button[type="submit"]').click();
}) })
.allowPublicAcquisition('submitContent', function (options) { .allowPublicAcquisition('submitContent', function (options) {
var gadget = this, var gadget = this,
content_dict = options[2]; content_dict = options[2];
return gadget.createDocument(content_dict.portal_type, if (!content_dict.portal_type) {
content_dict.portal_type = gadget.state.doc.portal_type[0];
}
if (!content_dict.parent_relative_url) {
content_dict.parent_relative_url = gadget.state.parent_portal_type
.replace(/ /g, '_').toLowerCase();
}
return gadget.createDocument(gadget.state.doc.portal_type[0],
gadget.state.parent_portal_type gadget.state.parent_portal_type
.replace(/ /g, '_').toLowerCase()); .replace(/ /g, '_').toLowerCase(),
content_dict);
}); });
}(window, document, rJS)); }(window, document, rJS));
\ No newline at end of file
...@@ -228,7 +228,7 @@ ...@@ -228,7 +228,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>977.1486.37580.56729</string> </value> <value> <string>978.12359.56087.21111</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>1562923143.55</float> <float>1567507688.29</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -34,14 +34,13 @@ ...@@ -34,14 +34,13 @@
}) })
.declareMethod("render", function (options) { .declareMethod("render", function (options) {
var gadget = this, action_reference, gadget_util, form_definition; var gadget = this, action_reference;
return RSVP.Queue() return RSVP.Queue()
.push(function () { .push(function () {
return RSVP.all([ return RSVP.all([
gadget.getUrlParameter('portal_type'), gadget.getUrlParameter('portal_type'),
gadget.getUrlParameter('parent_relative_url'), gadget.getUrlParameter('parent_relative_url'),
gadget.getUrlParameter("action"), gadget.getUrlParameter("action")
gadget.declareGadget("gadget_officejs_common_util.html")
]); ]);
}) })
.push(function (result) { .push(function (result) {
...@@ -50,18 +49,13 @@ ...@@ -50,18 +49,13 @@
options.parent_relative_url = result[1]; options.parent_relative_url = result[1];
} }
action_reference = result[2]; action_reference = result[2];
gadget_util = result[3];
return gadget.getActionFormDefinition(action_reference); return gadget.getActionFormDefinition(action_reference);
}) })
.push(function (result) { .push(function (form_definition) {
form_definition = result;
return gadget_util.getFormInfo(form_definition);
})
.push(function (form_info) {
var fragment = document.createElement('div'), var fragment = document.createElement('div'),
action_gadget_url, action_gadget_url,
form_type = form_info[0], form_type = form_definition.form_type,
child_gadget_url = form_info[1], child_gadget_url = form_definition.child_gadget_url,
//an action form must have a GadgetField called //an action form must have a GadgetField called
//"gadget_field_new_action_js_script" //"gadget_field_new_action_js_script"
//this gadget will point the custom action gadget //this gadget will point the custom action gadget
...@@ -90,16 +84,24 @@ ...@@ -90,16 +84,24 @@
element: fragment element: fragment
}) })
.push(function (action_gadget) { .push(function (action_gadget) {
options.form_definition = form_definition;
return action_gadget.preRenderDocument(options); return action_gadget.preRenderDocument(options);
}) })
.push(function (doc) { .push(function (doc) {
state_options.doc = doc; state_options.doc = doc;
state_options.action_gadget_url = action_gadget_url; state_options.action_gadget_url = action_gadget_url;
return gadget.changeState(state_options); return gadget.changeState(state_options);
}, function (error) {
if (error.status === 404) {
return gadget.notifySubmitted({
message: "Error in action",
status: "error"
}); });
} else {
return gadget.changeState(state_options);
} }
throw error;
});
}
return gadget.changeState(state_options);
}); });
}) })
...@@ -129,38 +131,41 @@ ...@@ -129,38 +131,41 @@
//target_url = options[1], //target_url = options[1],
content_dict = options[2], content_dict = options[2],
fragment = document.createElement('div'), fragment = document.createElement('div'),
jio_key; submit_dict;
if (gadget.state.valid_action) { if (gadget.state.valid_action) {
return gadget.notifySubmitting()
.push(function () {
gadget.element.appendChild(fragment); gadget.element.appendChild(fragment);
return gadget.declareGadget(gadget.state.action_gadget_url, { return gadget.declareGadget(gadget.state.action_gadget_url, {
scope: "action_field", scope: "action_field",
element: fragment element: fragment
});
}) })
.push(function (action_gadget) { .push(function (action_gadget) {
return action_gadget.handleSubmit(content_dict, gadget.state); return action_gadget.handleSubmit(content_dict, gadget.state);
}) })
.push(function (id) { .push(function (submit_result) {
jio_key = id; submit_dict = submit_result;
return gadget.notifySubmitting(); //submit_dict must contain:
}) //notify: options_dict for notifySubmitted
.push(function () { //redirect: options_dict for redirect
return gadget.notifySubmitted({message: 'Data Updated', return gadget.notifySubmitted(submit_dict.notify);
status: 'success'});
}) })
.push(function () { .push(function () {
return gadget.redirect({ return gadget.redirect(submit_dict.redirect);
command: 'display', }, function (error) {
options: { if (!(error instanceof RSVP.CancellationError)) {
jio_key: jio_key, return gadget.notifySubmitted({
editable: gadget.state.view === "edit" message: "Action Failed",
} status: "error"
}); });
}
throw error;
}); });
} else { }
return gadget.notifySubmitted( return gadget.notifySubmitted(
{message: 'Could not perform this action: configuration error', {message: 'Could not perform this action: configuration error',
status: 'fail'}); status: 'fail'});
}
}); });
}(window, document, rJS, RSVP)); }(window, document, rJS, RSVP));
...@@ -269,7 +269,7 @@ ...@@ -269,7 +269,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>977.6150.49752.57668</string> </value> <value> <string>978.31411.12952.38502</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -287,7 +287,7 @@ ...@@ -287,7 +287,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1563205637.32</float> <float>1568651040.61</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -3,6 +3,10 @@ ...@@ -3,6 +3,10 @@
<skin_folder>erp5_hal_json_style</skin_folder> <skin_folder>erp5_hal_json_style</skin_folder>
<skin_selection>AppCache</skin_selection> <skin_selection>AppCache</skin_selection>
</skin_folder_selection> </skin_folder_selection>
<skin_folder_selection>
<skin_folder>erp5_officejs_common</skin_folder>
<skin_selection>AppCache,Hal,RJS,RedirectAssist,View</skin_selection>
</skin_folder_selection>
<skin_folder_selection> <skin_folder_selection>
<skin_folder>erp5_text_editor</skin_folder> <skin_folder>erp5_text_editor</skin_folder>
<skin_selection>AppCache,Hal,RJS,RedirectAssist,View</skin_selection> <skin_selection>AppCache,Hal,RJS,RedirectAssist,View</skin_selection>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Folder" module="OFS.Folder"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>business_template_skin_layer_priority</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>float</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>business_template_skin_layer_priority</string> </key>
<value> <float>42.0</float> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>erp5_officejs_common</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
erp5_hal_json_style | AppCache erp5_hal_json_style | AppCache
erp5_officejs_common | AppCache
erp5_officejs_common | Hal
erp5_officejs_common | RJS
erp5_officejs_common | RedirectAssist
erp5_officejs_common | View
erp5_text_editor | AppCache erp5_text_editor | AppCache
erp5_text_editor | Hal erp5_text_editor | Hal
erp5_text_editor | RJS erp5_text_editor | RJS
......
erp5_officejs_common
erp5_text_editor erp5_text_editor
erp5_web_officejs_ui erp5_web_officejs_ui
\ No newline at end of file
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