Integration API: initialize an app from a Blob

This commit is contained in:
yflory 2023-01-19 16:10:55 +01:00 committed by Wolfgang Ginolas
parent 8b0d80c0c4
commit 5081d5d3c0
7 changed files with 129 additions and 71 deletions

View File

@ -128,14 +128,8 @@ define([
};
var importContent = UIElements.importContent = function (type, f, cfg) {
return function () {
var $files = $('<input>', {type:"file"});
if (cfg && cfg.accept) {
$files.attr('accept', cfg.accept);
}
$files.click();
$files.on('change', function (e) {
var file = e.target.files[0];
return function (_file) {
var todo = function (file) {
var reader = new FileReader();
var parsed = file && file.name && /.+\.([^.]+)$/.exec(file.name);
var ext = parsed && parsed[1];
@ -144,7 +138,19 @@ define([
reader.readAsArrayBuffer(file, type);
} else {
reader.readAsText(file, type);
}
}
};
if (_file) { return void todo(_file); }
var $files = $('<input>', {type:"file"});
if (cfg && cfg.accept) {
$files.attr('accept', cfg.accept);
}
$files.click();
$files.on('change', function (e) {
var file = e.target.files[0];
todo(file);
});
};
};
@ -627,12 +633,16 @@ define([
});
var handler = data.first? function () {
data.first(importer);
data.first(function () {
importer(); // Make sure we don't pass arguments to importer
});
}: importer; //importContent;
button
.click(common.prepareFeedback(type))
.click(handler);
.click(function () {
handler();
});
//}
break;
case 'upload':

View File

@ -71,6 +71,7 @@ define([
var evStart = Util.mkEvent(true);
var mediaTagEmbedder;
var fileImporter;
var $embedButton;
var common;
@ -545,7 +546,8 @@ define([
contentUpdate(newContent, waitFor);
}
} else {
if (!cpNfInner.metadataMgr.getPrivateData().isNewFile) {
var priv = cpNfInner.metadataMgr.getPrivateData();
if (!priv.isNewFile) {
// We're getting 'new pad' but there is an existing file
// We don't know exactly why this can happen but under no circumstances
// should we overwrite the content, so lets just try again.
@ -558,11 +560,16 @@ define([
onCorruptedCache();
return;
}
if (priv.initialState) {
var blob = priv.initialState;
var file = new File([blob], blob.name);
UIElements.importContent('text/plain', fileImporter, {})(file);
}
title.updateTitle(title.defaultTitle);
evOnDefaultContentNeeded.fire();
}
}).nThen(function () {
// We have a valid chainpad, reenable cache fix in case with reconnect with
// We have a valid chainpad, reenable cache fix in case we reconnect with
// a corrupted cache
noCache = false;
@ -698,31 +705,32 @@ define([
var setFileImporter = function (options, fi, async) {
if (readOnly) { return; }
toolbar.$drawer.append(
common.createButton('import', true, options, function (c, f) {
if (state !== STATE.READY || unsyncMode) {
return void UI.warn(Messages.disconnected);
}
if (async) {
fi(c, f, function (content) {
nThen(function (waitFor) {
contentUpdate(content, waitFor);
}).nThen(function () {
onLocal();
});
fileImporter = function (c, f) {
if (state !== STATE.READY || unsyncMode) {
return void UI.warn(Messages.disconnected);
}
if (async) {
fi(c, f, function (content) {
nThen(function (waitFor) {
contentUpdate(content, waitFor);
}).nThen(function () {
onLocal();
});
return;
}
nThen(function (waitFor) {
var content = fi(c, f);
if (typeof(content) === "undefined") {
return void UI.warn(Messages.importError);
}
contentUpdate(content, waitFor);
}).nThen(function () {
onLocal();
});
})
return;
}
nThen(function (waitFor) {
var content = fi(c, f);
if (typeof(content) === "undefined") {
return void UI.warn(Messages.importError);
}
contentUpdate(content, waitFor);
}).nThen(function () {
onLocal();
});
};
toolbar.$drawer.append(
common.createButton('import', true, options, fileImporter)
);
};

View File

@ -28,7 +28,8 @@ define([
href: href,
useCreationScreen: !isIntegration,
messaging: true,
integration: isIntegration
integration: isIntegration,
initialState: integration.initialState || undefined
});
});
});

View File

@ -353,6 +353,13 @@ define([
delete sessionStorage.CP_formExportSheet;
}
// New integrated pad
if (cfg.initialState) {
currentPad.href = cfg.href;
currentPad.hash = cfg.hash;
return void todo();
}
// New pad options
var options = parsed.getOptions();
if (options.newPadOpts) {
@ -697,7 +704,6 @@ define([
burnAfterReading: burnAfterReading,
storeInTeam: Cryptpad.initialTeam || (Cryptpad.initialPath ? -1 : undefined),
supportsWasm: Utils.Util.supportsWasm(),
integration: cfg.integration
};
if (window.CryptPad_newSharedFolder) {
additionalPriv.newSharedFolder = window.CryptPad_newSharedFolder;
@ -718,6 +724,12 @@ define([
additionalPriv.isChannelMuted = true;
}
// Integration
additionalPriv.integration = cfg.integration;
additionalPriv.initialState = cfg.initialState instanceof Blob ?
cfg.initialState : undefined;
// Early access
var priv = metaObj.priv;
var _plan = typeof(priv.plan) === "undefined" ? Utils.LocalStore.getPremium() : priv.plan;
var p = Utils.Util.checkRestrictedApp(parsed.type, AppConfig,
@ -729,6 +741,7 @@ define([
additionalPriv.earlyAccessBlocked = true;
}
// Safe apps
if (isSafe) {
additionalPriv.hashes = hashes;
additionalPriv.password = password;
@ -744,7 +757,7 @@ define([
Utils.LocalStore.setPremium(metaObj.priv.plan);
}
sframeChan.event('EV_METADATA_UPDATE', metaObj);
sframeChan.event('EV_METADATA_UPDATE', metaObj, {raw: true});
});
};
Cryptpad.onMetadataChanged(updateMeta);
@ -1994,6 +2007,15 @@ define([
});
});
}
// Make sure we add the validateKey to channel metadata when we don't use
// the pad creation screen
if (!rtConfig.metadata && secret.keys.validateKey) {
rtConfig.metadata = {
validateKey: secret.keys.validateKey
};
}
var cpNfCfg = {
sframeChan: sframeChan,
channel: secret.channel,
@ -2021,6 +2043,8 @@ define([
Cryptpad.getMetadata(waitFor(function (err, m) {
cpNfCfg.owners = [m.priv.edPublic];
}));
} else if (isNewFile && !cfg.useCreationScreen && cfg.initialState) {
console.log('new file with initial state provided');
} else if (isNewFile && !cfg.useCreationScreen && currentPad.hash) {
console.log("new file with hash in the address bar in an app without pcs and which requires owners");
sframeChan.onReady(function () {

View File

@ -426,6 +426,9 @@ define([
funcs.handleNewFile = function (waitFor, config) {
if (window.__CRYPTPAD_TEST__) { return; }
var priv = ctx.metadataMgr.getPrivateData();
if (priv.isNewFile && priv.initialState) {
return void setTimeout(waitFor());
}
if (priv.isNewFile) {
var c = (priv.settings.general && priv.settings.general.creation) || {};
// If this is a new file but we have a hash in the URL and pad creation screen is

View File

@ -78,11 +78,30 @@
config.events.onSave(data);
});
var onKeyValidated = function () {
var getBlob = function (cb) {
var xhr = new XMLHttpRequest();
xhr.open('GET', config.document.url, true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status == 200) {
var blob = this.response;
// myBlob is now the blob that the object URL pointed to.
cb(null, blob);
} else {
cb(this.status);
}
};
xhr.onerror = function (e) {
cb(e.message);
};
xhr.send();
};
var start = function (blob) {
chan.send('START', {
key: key,
application: config.documentType,
document: config.document.url,
document: blob,
}, function (obj) {
if (obj && obj.error) { reject(obj.error); return console.error(obj.error); }
console.log('OUTER START SUCCESS');
@ -90,6 +109,14 @@
});
};
var onKeyValidated = function () {
getBlob(function (err, blob) {
if (err) { reject(err); return console.error(err); }
blob.name = `document.${config.document.fileType}`;
start(blob);
});
};
chan.send('GET_SESSION', {
key: key
}, function (obj) {
@ -113,6 +140,7 @@
* @param {object} config The object containing configuration parameters.
* @param {object} config.document The document to load.
* @param {string} document.url The document URL.
* @param {string} document.fileType The document extension (md, xml, html, etc.).
* @param {string} document.key The collaborative session key.
* @param {object} config.events Event handlers.
* @param {function} events.onSave The save function to store the document when edited.
@ -137,7 +165,7 @@
}
if (!config) { return reject('Missing args: no data provided'); }
['document.url', 'document.key', 'documentType',
if(['document.url', 'document.fileType', 'document.key', 'documentType',
'events.onSave', 'events.onNewKey'].some(function (k) {
var s = k.split('.');
var c = config;
@ -148,7 +176,7 @@
}
c = c[key];
});
});
})) { return; }
cryptpadURL = cryptpadURL.replace(/(\/)+$/, '');
var url = cryptpadURL + '/integration/';

View File

@ -99,7 +99,7 @@ define([
isNew = true;
return Hash.createRandomHash('integration');
};
var oldKey = data.sessionKey;
var oldKey = data.key;
if (!oldKey) { return void cb({ key: getHash() }); }
checkSession(oldKey, function (obj) {
@ -112,33 +112,17 @@ define([
chan.on('START', function (data) {
console.warn('INNER START', data);
nThen(function (w) {
if (!isNew) { return; }
// XXX initial content TBD
var content = JSON.stringify({
content: data.document,
highlightMode: "gfm"
}); // XXX only for code
console.error('CRYPTPUT', data.key);
Crypt.put(data.key, content, w(), {
metadata: {
selfdestruct: true
}
});
}).nThen(function () {
var href = Hash.hashToHref(data.key, data.application);
console.error(Hash.hrefToHexChannelId(href));
window.CP_integration_outer = {
pathname: `/${data.application}/`,
hash: data.key,
href: href
};
require(['/common/sframe-app-outer.js'], function () {
console.warn('SAO REQUIRED');
delete window.CP_integration_outer;
});
var href = Hash.hashToHref(data.key, data.application);
console.error(Hash.hrefToHexChannelId(href));
window.CP_integration_outer = {
pathname: `/${data.application}/`,
hash: data.key,
href: href,
initialState: isNew ? data.document : undefined
};
require(['/common/sframe-app-outer.js'], function () {
console.warn('SAO REQUIRED');
delete window.CP_integration_outer;
});
});