aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/JSZipUtils.js142
-rw-r--r--src/fields/Doc.ts82
-rw-r--r--src/server/ApiManagers/UploadManager.ts28
3 files changed, 216 insertions, 36 deletions
diff --git a/src/JSZipUtils.js b/src/JSZipUtils.js
new file mode 100644
index 000000000..5ce1bd471
--- /dev/null
+++ b/src/JSZipUtils.js
@@ -0,0 +1,142 @@
+var JSZipUtils = {};
+// just use the responseText with xhr1, response with xhr2.
+// The transformation doesn't throw away high-order byte (with responseText)
+// because JSZip handles that case. If not used with JSZip, you may need to
+// do it, see https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data
+JSZipUtils._getBinaryFromXHR = function (xhr) {
+ // for xhr.responseText, the 0xFF mask is applied by JSZip
+ return xhr.response || xhr.responseText;
+};
+
+// taken from jQuery
+function createStandardXHR() {
+ try {
+ return new window.XMLHttpRequest();
+ } catch (e) {}
+}
+
+function createActiveXHR() {
+ try {
+ return new window.ActiveXObject('Microsoft.XMLHTTP');
+ } catch (e) {}
+}
+
+// Create the request object
+var createXHR =
+ typeof window !== 'undefined' && window.ActiveXObject
+ ? /* Microsoft failed to properly
+ * implement the XMLHttpRequest in IE7 (can't request local files),
+ * so we use the ActiveXObject when it is available
+ * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+ * we need a fallback.
+ */
+ function () {
+ return createStandardXHR() || createActiveXHR();
+ }
+ : // For all other browsers, use the standard XMLHttpRequest object
+ createStandardXHR;
+
+/**
+ * @param {string} path The path to the resource to GET.
+ * @param {function|{callback: function, progress: function}} options
+ * @return {Promise|undefined} If no callback is passed then a promise is returned
+ */
+JSZipUtils.getBinaryContent = function (path, options) {
+ var promise, resolve, reject;
+ var callback;
+
+ if (!options) {
+ options = {};
+ }
+
+ // backward compatible callback
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ } else if (typeof options.callback === 'function') {
+ // callback inside options object
+ callback = options.callback;
+ }
+
+ if (!callback && typeof Promise !== 'undefined') {
+ promise = new Promise(function (_resolve, _reject) {
+ resolve = _resolve;
+ reject = _reject;
+ });
+ } else {
+ resolve = function (data) {
+ callback(null, data);
+ };
+ reject = function (err) {
+ callback(err, null);
+ };
+ }
+
+ /*
+ * Here is the tricky part : getting the data.
+ * In firefox/chrome/opera/... setting the mimeType to 'text/plain; charset=x-user-defined'
+ * is enough, the result is in the standard xhr.responseText.
+ * cf https://developer.mozilla.org/En/XMLHttpRequest/Using_XMLHttpRequest#Receiving_binary_data_in_older_browsers
+ * In IE <= 9, we must use (the IE only) attribute responseBody
+ * (for binary data, its content is different from responseText).
+ * In IE 10, the 'charset=x-user-defined' trick doesn't work, only the
+ * responseType will work :
+ * http://msdn.microsoft.com/en-us/library/ie/hh673569%28v=vs.85%29.aspx#Binary_Object_upload_and_download
+ *
+ * I'd like to use jQuery to avoid this XHR madness, but it doesn't support
+ * the responseType attribute : http://bugs.jquery.com/ticket/11461
+ */
+ try {
+ var xhr = createXHR();
+
+ xhr.open('GET', path, true);
+
+ // recent browsers
+ if ('responseType' in xhr) {
+ xhr.responseType = 'arraybuffer';
+ }
+
+ // older browser
+ if (xhr.overrideMimeType) {
+ xhr.overrideMimeType('text/plain; charset=x-user-defined');
+ }
+
+ xhr.onreadystatechange = function (event) {
+ // use `xhr` and not `this`... thanks IE
+ if (xhr.readyState === 4) {
+ if (xhr.status === 200 || xhr.status === 0) {
+ try {
+ resolve(JSZipUtils._getBinaryFromXHR(xhr));
+ } catch (err) {
+ reject(new Error(err));
+ }
+ } else {
+ reject(new Error('Ajax error for ' + path + ' : ' + this.status + ' ' + this.statusText));
+ }
+ }
+ };
+
+ if (options.progress) {
+ xhr.onprogress = function (e) {
+ options.progress({
+ path: path,
+ originalEvent: e,
+ percent: (e.loaded / e.total) * 100,
+ loaded: e.loaded,
+ total: e.total,
+ });
+ };
+ }
+
+ xhr.send();
+ } catch (e) {
+ reject(new Error(e), null);
+ }
+
+ // returns a promise or undefined depending on whether a callback was
+ // provided
+ return promise;
+};
+
+// export
+module.exports = JSZipUtils;
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 6543679ad..deda8aa1f 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -26,6 +26,7 @@ import { Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Ty
import { AudioField, ImageField, MapField, PdfField, VideoField, WebField } from './URLField';
import { deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from './util';
import JSZip = require('jszip');
+import * as JSZipUtils from '../JSZipUtils';
export namespace Field {
export function toKeyValueString(doc: Doc, key: string): string {
const onDelegate = Object.keys(doc).includes(key);
@@ -732,7 +733,7 @@ export namespace Doc {
};
const docAtKey = doc[key];
if (docAtKey instanceof Doc) {
- if (!Doc.IsSystem(docAtKey) && (key === 'annotationOn' || key === 'proto'|| ((key === 'anchor1' || key === 'anchor2') && doc.author === Doc.CurrentUserEmail))) {
+ if (!Doc.IsSystem(docAtKey) && (key === 'annotationOn' || key === 'proto' || ((key === 'anchor1' || key === 'anchor2') && doc.author === Doc.CurrentUserEmail))) {
assignKey(await Doc.makeClone(docAtKey, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch));
} else {
assignKey(docAtKey);
@@ -757,7 +758,7 @@ export namespace Doc {
const linkClone = await Doc.makeClone(link, cloneMap, linkMap, rtfs, exclusions, dontCreate, asBranch);
linkMap.set(link[Id], linkClone);
}
- Doc.SetInPlace(copy, 'title', (asBranch ? 'BRANCH: ' : 'CLONE: ') + doc.title, true);
+ Doc.SetInPlace(copy, 'title', (asBranch ? 'BRANCH: ' : 'CLONE: ') + doc.title, true);
if (!dontCreate) {
asBranch ? (copy.branchOf = doc) : (copy.cloneOf = doc);
if (!Doc.IsPrototype(copy)) {
@@ -768,22 +769,23 @@ export namespace Doc {
Doc.AddFileOrphan(copy);
return copy;
}
- export function repairClone(clone: Doc, cloneMap: Map<string,Doc>, visited: Set<Doc>) {
+ export function repairClone(clone: Doc, cloneMap: Map<string, Doc>, visited: Set<Doc>) {
if (visited.has(clone)) return;
visited.add(clone);
- Object.keys(clone).filter(key => key !== "cloneOf").map(key => {
- const docAtKey = DocCast(clone[key]);
- if (docAtKey && !Doc.IsSystem(docAtKey)) {
- if (!Array.from(cloneMap.values()).includes(docAtKey)) {
- if (cloneMap.has(docAtKey[Id])) {
- clone[key] = cloneMap.get(docAtKey[Id]);
+ Object.keys(clone)
+ .filter(key => key !== 'cloneOf')
+ .map(key => {
+ const docAtKey = DocCast(clone[key]);
+ if (docAtKey && !Doc.IsSystem(docAtKey)) {
+ if (!Array.from(cloneMap.values()).includes(docAtKey)) {
+ if (cloneMap.has(docAtKey[Id])) {
+ clone[key] = cloneMap.get(docAtKey[Id]);
+ } else clone[key] = undefined;
+ } else {
+ repairClone(docAtKey, cloneMap, visited);
}
- else clone[key] = undefined;
- } else {
- repairClone(docAtKey, cloneMap, visited);
}
- }
- });
+ });
}
export async function MakeClone(doc: Doc, dontCreate: boolean = false, asBranch = false, cloneMap: Map<string, Doc> = new Map()) {
const linkMap = new Map<string, Doc>();
@@ -818,8 +820,9 @@ export namespace Doc {
// a.click();
const { clone, map, linkMap } = await Doc.MakeClone(doc, false);
clone.LINKS = new List<Doc>(Array.from(linkMap.values()));
+ const proms = [] as string[];
function replacer(key: any, value: any) {
- if (['branchOf', 'cloneOf', 'cursors'].includes(key)) return undefined;
+ if (key && ['branchOf', 'cloneOf', 'cursors'].includes(key)) return undefined;
else if (value instanceof Doc) {
if (key !== 'field' && Number.isNaN(Number(key))) {
const __fields = value[FieldsSym]();
@@ -829,9 +832,14 @@ export namespace Doc {
}
} else if (value instanceof ScriptField) return { script: value.script, __type: 'script' };
else if (value instanceof RichTextField) return { Data: value.Data, Text: value.Text, __type: 'RichTextField' };
- else if (value instanceof ImageField) return { url: value.url.href, __type: 'image' };
- else if (value instanceof PdfField) return { url: value.url.href, __type: 'pdf' };
- else if (value instanceof AudioField) return { url: value.url.href, __type: 'audio' };
+ else if (value instanceof ImageField) {
+ const extension = value.url.href.replace(/.*\./, '');
+ proms.push(value.url.href.replace('.' + extension, '_o.' + extension));
+ return { url: value.url.href, __type: 'image' };
+ } else if (value instanceof PdfField) {
+ proms.push(value.url.href);
+ return { url: value.url.href, __type: 'pdf' };
+ } else if (value instanceof AudioField) return { url: value.url.href, __type: 'audio' };
else if (value instanceof VideoField) return { url: value.url.href, __type: 'video' };
else if (value instanceof WebField) return { url: value.url.href, __type: 'web' };
else if (value instanceof MapField) return { url: value.url.href, __type: 'map' };
@@ -846,6 +854,32 @@ export namespace Doc {
Array.from(map.entries()).forEach(f => (docs[f[0]] = f[1]));
const docString = JSON.stringify({ id: clone[Id], docs }, decycle(replacer));
+ let generateZIP = (proms: string[]) => {
+ var zip = new JSZip();
+ var count = 0;
+ var zipFilename = 'dashExport.zip';
+
+ proms
+ .filter(url => url.startsWith(window.location.origin))
+ .forEach((url, i) => {
+ var filename = proms[i].replace(window.location.origin + '/', '').replace(/\//g, '%%%');
+ // loading a file and add it in a zip file
+ JSZipUtils.getBinaryContent(url, function (err: any, data: any) {
+ if (err) {
+ throw err; // or handle the error
+ }
+ zip.file(filename, data, { binary: true });
+ count++;
+ if (count == proms.length) {
+ zip.file('doc.json', docString);
+ zip.generateAsync({ type: 'blob' }).then(function (content) {
+ saveAs(content, zipFilename);
+ });
+ }
+ });
+ });
+ };
+ generateZIP(proms);
const zip = new JSZip();
zip.file('doc.json', docString);
@@ -857,10 +891,10 @@ export namespace Doc {
// img.file("smile.gif", imgData, {base64: true});
// Generate the zip file asynchronously
- zip.generateAsync({ type: 'blob' }).then((content: any) => {
- // Force down of the Zip file
- saveAs(content, doc.title + '.zip'); // glr: Possibly change the name of the document to match the title?
- });
+ // zip.generateAsync({ type: 'blob' }).then((content: any) => {
+ // // Force down of the Zip file
+ // saveAs(content, doc.title + '.zip'); // glr: Possibly change the name of the document to match the title?
+ // });
}
//
// Determines whether the layout needs to be expanded (as a template).
@@ -1530,12 +1564,12 @@ export namespace Doc {
formData.append('remap', 'true');
const response = await fetch(upload, { method: 'POST', body: formData });
const json = await response.json();
- console.log(json)
+ console.log(json);
if (json !== 'error') {
await DocServer.GetRefFields(json.docids as string[]);
const doc = DocCast(await DocServer.GetRefField(json.id));
- console.log("Doc = ", doc, doc?.title);
(await DocListCastAsync(doc?.LINKS))?.forEach(link => LinkManager.Instance.addLink(link));
+ doc.LINKS = undefined;
return doc;
}
}
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 5da3dfd3f..6e28268a9 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -235,18 +235,22 @@ export default class UploadManager extends ApiManager {
const path_2 = Array.isArray(f) ? '' : f.path;
const zip = new AdmZip(path_2);
zip.getEntries().forEach((entry: any) => {
- if (!entry.entryName.startsWith('files/')) return;
- let directory = dirname(entry.entryName) + '/';
- const extension = extname(entry.entryName);
- const base = basename(entry.entryName).split('.')[0];
+ let entryName = entry.entryName.replace(/%%%/g, '/');
+ if (!entryName.startsWith('files/')) {
+ return;
+ }
+ const extension = extname(entryName);
+ const pathname = publicDirectory + '/' + entry.entryName;
+ const targetname = publicDirectory + '/' + entryName;
try {
zip.extractEntryTo(entry.entryName, publicDirectory, true, false);
- directory = '/' + directory;
-
- createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + '_o' + extension));
- createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + '_s' + extension));
- createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + '_m' + extension));
- createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + '_l' + extension));
+ createReadStream(pathname).pipe(createWriteStream(targetname));
+ if (extension !== '.pdf') {
+ createReadStream(pathname).pipe(createWriteStream(targetname.replace('_o' + extension, '_s' + extension)));
+ createReadStream(pathname).pipe(createWriteStream(targetname.replace('_o' + extension, '_m' + extension)));
+ createReadStream(pathname).pipe(createWriteStream(targetname.replace('_o' + extension, '_l' + extension)));
+ }
+ unlink(pathname, () => {});
} catch (e) {
console.log(e);
}
@@ -258,7 +262,7 @@ export default class UploadManager extends ApiManager {
id = getId(data.id);
const docs = Object.keys(datadocs).map(key => datadocs[key]);
docs.forEach(mapFn);
- docids = docs.map(doc => doc.id)
+ docids = docs.map(doc => doc.id);
await Promise.all(
docs.map(
(doc: any) =>
@@ -281,7 +285,7 @@ export default class UploadManager extends ApiManager {
unlink(path_2, () => {});
}
SolrManager.update();
- res.send(JSON.stringify({id, docids} || 'error'));
+ res.send(JSON.stringify({ id, docids } || 'error'));
} catch (e) {
console.log(e);
}