Commit 8bd87f79 authored by Boris Kocherov's avatar Boris Kocherov

erp5_officejs: schema editor: add upload zip archive containing schema and documents

parent 089ef620
......@@ -191,7 +191,7 @@
tasks.push(gen_element({command: 'display', options: {page: "ojs_configurator"}},
"Storages", "dropbox"));
if (context.state.editable) {
tasks.push(gen_element({command: 'index', options: {page: "ojs_multi_upload"}},
tasks.push(gen_element({command: 'index', options: {page: "ojs_zip_upload"}},
"Upload", "upload"));
}
return RSVP.all(tasks);
......
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>OfficeJS Jio Upload Zip File Containing files or files as is</title>
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<script src="jio_ziptodocuments.js"></script>
<script src="zipfilestorage-with-jszip.js"></script>
<script src="gadget_erp5_page_ojs_zip_upload.js"></script>
</head>
<body>
<div data-gadget-url="gadget_erp5_form.html"
data-gadget-scope="form_view"
data-gadget-sandbox="public">
</div>
</body>
</html>
/*global window, rJS, RSVP, loopEventListener,
jIO, document */
/*jslint nomen: true, indent: 2, maxerr: 3 */
(function (window, rJS, jIO, RSVP) {
"use strict";
var content_type = {
Spreadsheet: 'application/x-asc-spreadsheet',
Presentation: 'application/x-asc-presentation',
Text: 'application/x-asc-text'
};
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
function generateMetadata(id, filename, path, body) {
// it's core of upload of zip gadget
// in this function can be added support another formats
var ret;
if (endsWith(filename, ".json")) {
ret = {
id: id,
content_type: "application/json",
reference: path
};
if (body) {
if (body.$schema && body.$schema !== "") {
ret.portal_type = "JSON Schema";
ret.parent_relative_url = "schema_module";
ret.title = body.title || "";
} else {
// XXX need schema relation property
ret.portal_type = "JSON Document";
ret.parent_relative_url = "document_module";
ret.title = body.filename || "";
}
} else {
ret.format = "json";
}
// used for detect supported extension
return ret;
}
}
rJS(window)
/////////////////////////////////////////////////////////////////
// Acquired methods
/////////////////////////////////////////////////////////////////
.declareAcquiredMethod("updateHeader", "updateHeader")
.declareAcquiredMethod("getUrlFor", "getUrlFor")
.declareAcquiredMethod("getSetting", "getSetting")
.declareAcquiredMethod("notifySubmitting", "notifySubmitting")
.declareAcquiredMethod("notifySubmitted", "notifySubmitted")
.declareAcquiredMethod("jio_putAttachment", "jio_putAttachment")
.declareAcquiredMethod("redirect", "redirect")
.declareAcquiredMethod("jio_post", "jio_post")
.declareAcquiredMethod("jio_put", "jio_put")
.declareAcquiredMethod("jio_putAttachment", "jio_putAttachment")
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
.declareMethod("render", function () {
var gadget = this;
return gadget.changeState({
title: 'Untitled Document'
});
})
.declareMethod('putIntoDB', function (id, file_name, path, blob) {
var gadget = this,
queue = RSVP.Queue(),
// first run generateMetadata for check file support
// and detect document format
file_supported = generateMetadata(id, file_name, path);
if (file_supported) {
if (file_supported.format === "json") {
queue
.push(function () {
return jIO.util.readBlobAsText(blob);
})
.push(function (evt) {
return JSON.parse(evt.target.result);
});
} else {
queue.push(function () {
return blob;
});
}
queue
.push(function (data) {
return gadget.jio_post(generateMetadata(id, file_name, path, data))
.push(function (added_id) {
return gadget.jio_putAttachment(added_id, 'data', blob);
});
});
}
return queue;
})
.declareMethod('triggerSubmit', function () {
var gadget = this;
return gadget.notifySubmitting()
.push(function () {
return gadget.getDeclaredGadget('form_view');
})
.push(function (form_gadget) {
return form_gadget.getContent();
})
.push(function (content) {
return new RSVP.Queue()
.push(function () {
return RSVP.all([
gadget.getSetting('jio_storage_name'),
gadget.getSetting('jio_storage_description')
]);
})
.push(function (result) {
var promiseArray = [],
data_array = content.data,
i,
blob,
storage_name = result[0],
storage_description = result[1],
storage,
local_database,
configuration;
// XXX support other type of storage
if (storage_name === "LOCAL") {
local_database = storage_description.sub_storage.sub_storage.database;
}
for (i = 0; i < data_array.length; i += 1) {
blob = jIO.util.dataURItoBlob(data_array[i].url);
if (endsWith(data_array[i].file_name, ".zip")) {
configuration = {
type: "replicate",
conflict_handling: 2,
check_local_attachment_creation: false,
check_local_creation: false,
check_local_modification: false,
check_local_deletion: false,
check_remote_attachment_creation: true,
check_remote_creation: true,
check_remote_modification: true,
check_remote_deletion: true,
local_sub_storage: {
type: "indexeddb",
database: local_database
},
signature_sub_storage: {
type: "query",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "ziptodocuments",
generateMetadata: generateMetadata,
sub_storage: {
type: "zipfile",
file: blob
}
}
};
storage = jIO.createJIO(configuration);
promiseArray.push(storage.repair());
} else {
promiseArray.push(gadget.putIntoDB("", data_array[i].file_name, data_array[i].file_name, blob));
}
}
return RSVP.all(promiseArray);
});
})
.push(function () {
return RSVP.all([
gadget.notifySubmitted({message: 'Data Updated', status: 'success'}),
gadget.redirect({command: 'history_previous'})
]);
});
})
.onStateChange(function () {
var gadget = this;
return RSVP.Queue()
.push(function () {
return RSVP.all([
gadget.getDeclaredGadget('form_view'),
gadget.getSetting('upload_content_type')
]);
})
.push(function (result) {
return result[0].render({
erp5_document: {
"_embedded": {
"_view": {
"my_file": {
"description": "",
"title": "Upload files and Zip archive containing files",
"default": "",
"css_class": "",
"required": 1,
"editable": 1,
"key": "data",
"hidden": 0,
"multiple": "true",
"accept": "application/zip," + result[1],
"type": "FileField"
}
}
},
"_links": {
"type": {
// form_list display portal_type in header
name: ""
}
}
},
form_definition: {
group_list: [[
"left",
[["my_file"]]
]]
}
});
})
.push(function () {
return gadget.getUrlFor({command: 'history_previous'});
})
.push(function (result) {
return gadget.updateHeader({
page_title: 'Document(s)',
back_field: true,
selection_url: result,
save_action: true
});
});
});
}(window, rJS, jIO, RSVP));
......@@ -126,6 +126,8 @@ URI.js\n
dygraph.js\n
handlebars.js\n
jiodev.js\n
jio_ziptodocuments.js\n
zipfilestorage-with-jszip.js\n
renderjs.js\n
rsvp.js\n
gadget_global.js\n
......@@ -150,8 +152,10 @@ gadget_ojs_jio.html\n
gadget_ojs_jio.js\n
gadget_erp5_page_ojs_controller.html\n
gadget_erp5_page_ojs_controller.js\n
gadget_erp5_page_ojs_add_document.html\n
gadget_erp5_page_ojs_add_document.js\n
gadget_erp5_page_ojs_add_json_schema.html\n
gadget_erp5_page_ojs_add_json_schema.js\n
gadget_erp5_page_ojs_add_json_document.html\n
gadget_erp5_page_ojs_add_json_document.js\n
gadget_erp5_page_ojs_configurator.html\n
gadget_erp5_page_ojs_configurator.js\n
gadget_erp5_page_ojs_dav_configurator.html\n
......@@ -217,6 +221,8 @@ gadget_officejs_jio_json_schema_view.html\n
gadget_officejs_jio_json_schema_view.js\n
gadget_erp5_page_ojs_multi_upload.html\n
gadget_erp5_page_ojs_multi_upload.js\n
gadget_erp5_page_ojs_zip_upload.html\n
gadget_erp5_page_ojs_zip_upload.js\n
gadget_erp5_page_ojs_schema_document_list.html\n
gadget_erp5_page_ojs_schema_document_list.js\n
\n
......@@ -380,7 +386,7 @@ NETWORK:\n
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>968.9700.26907.3566</string> </value>
<value> <string>968.64276.14441.43929</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -398,7 +404,7 @@ NETWORK:\n
</tuple>
<state>
<tuple>
<float>1528717715.13</float>
<float>1531905921.42</float>
<string>UTC</string>
</tuple>
</state>
......
/*jslint nomen: true*/
/*global jIO, RSVP*/
(function (jIO, RSVP) {
"use strict";
function decodeJsonPointer(_str) {
// https://tools.ietf.org/html/rfc6901#section-5
return _str.replace(/~1/g, '/').replace(/~0/g, '~');
}
function encodeJsonPointer(_str) {
// https://tools.ietf.org/html/rfc6901#section-5
return _str.replace(/~/g, '~0').replace(/\//g, '~1');
}
/**
* The jIO ZipToDocumentsBridgeStorage extension
*
* convert set of files provided zip container as
* documents contains metadata for files
* with attachment 'body' as file content
*
* @class ZipToDocumentsBridgeStorage
* @constructor
*/
function ZipToDocumentsBridgeStorage(spec) {
this._sub_storage = jIO.createJIO(spec.sub_storage);
if (spec.generateMetadata) {
this._generateMetadata = spec.generateMetadata;
} else {
throw new jIO.util.jIOError("Need specify generateMetadata function",
400);
/**
* used for generate metadata for files
* based on extension and file content.
* used for determine supported or not
* supported file by return undefined.
*
* @function generateMetadata
*/
// example generateMetadata function
// function generateMetadata(id, filename, path, body) {
// var ret;
// if (endsWith(filename, ".json")) {
// ret = {
// id: id,
// content_type: "application/json",
// reference: path
// };
// if (body) {
// if (body.$schema && body.$schema !== "") {
// ret.portal_type = "JSON Schema";
// ret.parent_relative_url = "schema_module";
// ret.title = body.title;
// } else {
// // XXX need schema relation property
// ret.portal_type = "JSON Document";
// ret.parent_relative_url = "document_module";
// ret.title = body.filename;
// }
// } else {
// ret.format = "json";
// }
// // used for detect supported extension
// return ret;
// }
// }
}
}
ZipToDocumentsBridgeStorage.prototype.get = function (id) {
var context = this,
path = "/" + decodeJsonPointer(id),
idx = path.lastIndexOf('/') + 1,
filename = path.substring(idx),
dirname = path.substring(0, idx),
file_supported;
path = path.substring(1);
file_supported = context._generateMetadata(id, filename, path);
if (!file_supported) {
return new RSVP.Queue()
.push(function () {
throw new jIO.util.jIOError("Cannot find document " + id,
404);
});
}
return context._sub_storage.getAttachment(dirname, filename,
{format: file_supported.format}
)
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
throw new jIO.util.jIOError("Cannot find document " + id,
404);
}
throw error;
})
.push(function (attachment) {
return context._generateMetadata(id, filename, path, attachment);
});
};
ZipToDocumentsBridgeStorage.prototype.allAttachments = function (id) {
var context = this,
path = "/" + decodeJsonPointer(id),
idx = path.lastIndexOf('/') + 1,
filename = path.substring(idx),
dirname = path.substring(0, idx);
return context._sub_storage.getAttachment(dirname, filename)
.push(function () {
return {"data": {}};
}, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
throw new jIO.util.jIOError("Cannot find document " + id,
404);
}
throw error;
});
};
ZipToDocumentsBridgeStorage.prototype.put = function (doc_id) {
// we can not save file metadata(document in upper storage)
// in zip so do nothing
return RSVP.Queue()
.push(function () {
return doc_id;
});
};
ZipToDocumentsBridgeStorage.prototype.remove = function (id) {
var context = this,
path = "/" + decodeJsonPointer(id),
idx = path.lastIndexOf('/') + 1,
filename = path.substring(idx),
dirname = path.substring(0, idx);
return context._sub_storage.removeAttachment(dirname, filename)
.push(undefined, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
throw new jIO.util.jIOError("Cannot find document " + id,
404);
}
throw error;
});
};
ZipToDocumentsBridgeStorage.prototype.hasCapacity = function (capacity) {
return ((capacity === "list") || (capacity === "include"));
};
ZipToDocumentsBridgeStorage.prototype.buildQuery = function (options) {
var result_dict = {},
context = this;
return context._sub_storage.allDocs()
.push(function (result) {
var i,
id,
tasks = [];
function push_doc(k, filename, path) {
return function (json) {
result_dict[k].doc = context._generateMetadata(k, filename, path, json);
};
}
function f(dirname) {
return function (dir) {
var k,
path,
filename,
attachment_tasks = [];
for (filename in dir) {
if (dir.hasOwnProperty(filename)) {
path = dirname.substring(1) + filename;
k = encodeJsonPointer(dirname.substring(1) + filename);
// check file with extension supported
if (context._generateMetadata(k, filename, path)) {
result_dict[k] = {
id: k,
value: {}
};
if (options.include_docs) {
attachment_tasks.push(
context._sub_storage.getAttachment(id, filename)
.push(push_doc(k, filename, path))
);
}
}
}
}
if (attachment_tasks.length > 0) {
return RSVP.all(attachment_tasks);
}
};
}
for (i = 0; i < result.data.rows.length; i += 1) {
id = result.data.rows[i].id;
tasks.push(context._sub_storage.allAttachments(id)
.push(f(id)));
}
return RSVP.all(tasks);
})
.push(function () {
var result = [],
key;
for (key in result_dict) {
if (result_dict.hasOwnProperty(key)) {
result.push(result_dict[key]);
}
}
return result;
});
};
ZipToDocumentsBridgeStorage.prototype.getAttachment = function (id,
name,
options) {
if (name !== "data") {
throw new jIO.util.jIOError("Only support 'data' attachment",
400);
}
var path = "/" + decodeJsonPointer(id),
idx = path.lastIndexOf('/') + 1,
filename = path.substring(idx),
dirname = path.substring(0, idx);
return this._sub_storage.getAttachment(dirname, filename, options);
};
ZipToDocumentsBridgeStorage.prototype.putAttachment = function (id,
name,
blob) {
if (name !== "data") {
throw new jIO.util.jIOError("Only support 'data' attachment",
400);
}
var path = "/" + decodeJsonPointer(id),
idx = path.lastIndexOf('/') + 1,
filename = path.substring(idx),
dirname = path.substring(0, idx);
return this._sub_storage.putAttachment(dirname, filename, blob);
};
ZipToDocumentsBridgeStorage.prototype.removeAttachment = function (id,
name) {
if (name !== "data") {
throw new jIO.util.jIOError("Only support 'data' attachment" +
" in document:" + id,
400);
}
// document(metadata) with attachment === attachment in zip
// so we can do nothing
};
ZipToDocumentsBridgeStorage.prototype.repair = function () {
return this._sub_storage.repair.apply(this._sub_storage, arguments);
};
jIO.addStorage('ziptodocuments', ZipToDocumentsBridgeStorage);
}(jIO, RSVP));
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