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

View File

@ -71,6 +71,7 @@ define([
var evStart = Util.mkEvent(true); var evStart = Util.mkEvent(true);
var mediaTagEmbedder; var mediaTagEmbedder;
var fileImporter;
var $embedButton; var $embedButton;
var common; var common;
@ -545,7 +546,8 @@ define([
contentUpdate(newContent, waitFor); contentUpdate(newContent, waitFor);
} }
} else { } 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're getting 'new pad' but there is an existing file
// We don't know exactly why this can happen but under no circumstances // We don't know exactly why this can happen but under no circumstances
// should we overwrite the content, so lets just try again. // should we overwrite the content, so lets just try again.
@ -558,11 +560,16 @@ define([
onCorruptedCache(); onCorruptedCache();
return; 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); title.updateTitle(title.defaultTitle);
evOnDefaultContentNeeded.fire(); evOnDefaultContentNeeded.fire();
} }
}).nThen(function () { }).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 // a corrupted cache
noCache = false; noCache = false;
@ -698,31 +705,32 @@ define([
var setFileImporter = function (options, fi, async) { var setFileImporter = function (options, fi, async) {
if (readOnly) { return; } if (readOnly) { return; }
toolbar.$drawer.append( fileImporter = function (c, f) {
common.createButton('import', true, options, function (c, f) { if (state !== STATE.READY || unsyncMode) {
if (state !== STATE.READY || unsyncMode) { return void UI.warn(Messages.disconnected);
return void UI.warn(Messages.disconnected); }
} if (async) {
if (async) { fi(c, f, function (content) {
fi(c, f, function (content) { nThen(function (waitFor) {
nThen(function (waitFor) { contentUpdate(content, waitFor);
contentUpdate(content, waitFor); }).nThen(function () {
}).nThen(function () { onLocal();
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, href: href,
useCreationScreen: !isIntegration, useCreationScreen: !isIntegration,
messaging: true, messaging: true,
integration: isIntegration integration: isIntegration,
initialState: integration.initialState || undefined
}); });
}); });
}); });

View File

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

View File

@ -426,6 +426,9 @@ define([
funcs.handleNewFile = function (waitFor, config) { funcs.handleNewFile = function (waitFor, config) {
if (window.__CRYPTPAD_TEST__) { return; } if (window.__CRYPTPAD_TEST__) { return; }
var priv = ctx.metadataMgr.getPrivateData(); var priv = ctx.metadataMgr.getPrivateData();
if (priv.isNewFile && priv.initialState) {
return void setTimeout(waitFor());
}
if (priv.isNewFile) { if (priv.isNewFile) {
var c = (priv.settings.general && priv.settings.general.creation) || {}; 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 // 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); 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', { chan.send('START', {
key: key, key: key,
application: config.documentType, application: config.documentType,
document: config.document.url, document: blob,
}, function (obj) { }, function (obj) {
if (obj && obj.error) { reject(obj.error); return console.error(obj.error); } if (obj && obj.error) { reject(obj.error); return console.error(obj.error); }
console.log('OUTER START SUCCESS'); 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', { chan.send('GET_SESSION', {
key: key key: key
}, function (obj) { }, function (obj) {
@ -113,6 +140,7 @@
* @param {object} config The object containing configuration parameters. * @param {object} config The object containing configuration parameters.
* @param {object} config.document The document to load. * @param {object} config.document The document to load.
* @param {string} document.url The document URL. * @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 {string} document.key The collaborative session key.
* @param {object} config.events Event handlers. * @param {object} config.events Event handlers.
* @param {function} events.onSave The save function to store the document when edited. * @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'); } 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) { 'events.onSave', 'events.onNewKey'].some(function (k) {
var s = k.split('.'); var s = k.split('.');
var c = config; var c = config;
@ -148,7 +176,7 @@
} }
c = c[key]; c = c[key];
}); });
}); })) { return; }
cryptpadURL = cryptpadURL.replace(/(\/)+$/, ''); cryptpadURL = cryptpadURL.replace(/(\/)+$/, '');
var url = cryptpadURL + '/integration/'; var url = cryptpadURL + '/integration/';

View File

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