mirror of https://github.com/xwiki-labs/cryptpad
Merge branch 'plugins' into 2024.6-test
This commit is contained in:
commit
179865304d
|
@ -16,6 +16,8 @@ www/accounts
|
|||
www/worker
|
||||
www/todo
|
||||
|
||||
#lib/plugins/
|
||||
|
||||
www/common/hyperscript.js
|
||||
|
||||
www/pad/wysiwygarea-plugin.js
|
||||
|
|
|
@ -22,11 +22,13 @@ define([
|
|||
'/common/outer/login-block.js',
|
||||
'/common/common-hash.js',
|
||||
'/common/outer/http-command.js',
|
||||
'/api/config',
|
||||
|
||||
'/components/tweetnacl/nacl-fast.min.js',
|
||||
'/components/scrypt-async/scrypt-async.min.js', // better load speed
|
||||
], function ($, Listmap, Crypto, Util, NetConfig, Login, Cred, ChainPad, Realtime, Constants, UI,
|
||||
Feedback, h, LocalStore, Messages, nThen, Block, Hash, ServerCommand) {
|
||||
Feedback, h, LocalStore, Messages, nThen, Block, Hash, ServerCommand,
|
||||
ApiConfig) {
|
||||
var Exports = {
|
||||
Cred: Cred,
|
||||
Block: Block,
|
||||
|
@ -218,6 +220,11 @@ define([
|
|||
proxy.edPublic = result.edPublic;
|
||||
}
|
||||
|
||||
if (ApiConfig && Array.isArray(ApiConfig.adminKeys) &&
|
||||
ApiConfig.adminKeys.includes(proxy.edPublic)) {
|
||||
localStorage.CP_admin = "1";
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
Realtime.whenRealtimeSyncs(result.realtime, function () {
|
||||
proceed(result);
|
||||
|
|
|
@ -45,6 +45,7 @@ var getLanguage = Messages._getLanguage = function () {
|
|||
(map[l.split('_')[0]] ? l.split('_')[0] : 'en'));
|
||||
};
|
||||
var language = getLanguage();
|
||||
window.cryptpadLanguage = language;
|
||||
|
||||
// Translations files were migrated from requirejs modules to json.
|
||||
// To avoid asking every administrator to update their customized translation files,
|
||||
|
|
|
@ -15,7 +15,8 @@ define([
|
|||
'/common/outer/local-store.js',
|
||||
'/customize/pages.js',
|
||||
'/common/pad-types.js',
|
||||
], function ($, Config, h, Hash, Constants, Util, TextFit, Msg, AppConfig, LocalStore, Pages, PadTypes) {
|
||||
'/common/extensions.js'
|
||||
], function ($, Config, h, Hash, Constants, Util, TextFit, Msg, AppConfig, LocalStore, Pages, PadTypes, Extensions) {
|
||||
var urlArgs = Config.requireConf.urlArgs;
|
||||
|
||||
var checkEarlyAccess = function (x) {
|
||||
|
@ -164,9 +165,19 @@ define([
|
|||
};
|
||||
|
||||
|
||||
let popup = h('div.cp-extensions-popups');
|
||||
let utils = { h, Util, Hash };
|
||||
Extensions.getExtensions('HOMEPAGE_POPUP').forEach(ext => {
|
||||
if (typeof(ext.check) === "function" && !ext.check()) { return; }
|
||||
ext.getContent(utils, content => {
|
||||
$(popup).append(h('div.cp-extensions-popup', content));
|
||||
});
|
||||
});
|
||||
|
||||
return [
|
||||
h('div#cp-main', [
|
||||
Pages.infopageTopbar(),
|
||||
popup,
|
||||
notice,
|
||||
h('div.container.cp-container', [
|
||||
h('div.row.cp-home-hero', [
|
||||
|
|
|
@ -297,6 +297,29 @@
|
|||
}
|
||||
}
|
||||
|
||||
.cp-extensions-popups {
|
||||
width: 100%;
|
||||
.cp-extensions-popup {
|
||||
background-color: @cp_alertify-bg;
|
||||
border-radius: @infopages-radius-L;
|
||||
padding: 10px;
|
||||
box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.2);
|
||||
width: 400px;
|
||||
max-width: 100%;
|
||||
color: @cryptpad_text_col;
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
.cp-extensions-popups {
|
||||
max-width: 90%;
|
||||
.cp-extensions-popup {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 576px) and (max-width: 767px) {
|
||||
.container {
|
||||
padding-left: 0;
|
||||
|
|
|
@ -78,15 +78,26 @@ COMMANDS.TOTP_REVOKE = TOTP.TOTP_REVOKE;
|
|||
COMMANDS.TOTP_WRITE_BLOCK = TOTP.TOTP_WRITE_BLOCK; // Password change only for now (v5.5.0)
|
||||
COMMANDS.TOTP_REMOVE_BLOCK = TOTP.TOTP_REMOVE_BLOCK;
|
||||
|
||||
try {
|
||||
// SSO plugin may not be installed
|
||||
const SSO = plugins.SSO && plugins.SSO.challenge;
|
||||
COMMANDS.SSO_AUTH = SSO.SSO_AUTH;
|
||||
COMMANDS.SSO_AUTH_CB = SSO.SSO_AUTH_CB;
|
||||
COMMANDS.SSO_WRITE_BLOCK = SSO.SSO_WRITE_BLOCK; // Account creation only
|
||||
COMMANDS.SSO_UPDATE_BLOCK = SSO.SSO_UPDATE_BLOCK; // Password change
|
||||
COMMANDS.SSO_VALIDATE = SSO.SSO_VALIDATE;
|
||||
} catch (e) {}
|
||||
// Load challenges added by plugins
|
||||
Object.keys(plugins || {}).forEach(id => {
|
||||
try {
|
||||
let plugin = plugins[id];
|
||||
if (!plugin.challenge) { return; }
|
||||
let commands = plugin.challenge;
|
||||
Object.keys(commands).forEach(cmd => {
|
||||
if (COMMANDS[cmd]) { return; } // Don't overwrite
|
||||
COMMANDS[cmd] = commands[cmd];
|
||||
});
|
||||
} catch (e) {}
|
||||
});
|
||||
/*
|
||||
const SSO = plugins.SSO && plugins.SSO.challenge;
|
||||
COMMANDS.SSO_AUTH = SSO.SSO_AUTH;
|
||||
COMMANDS.SSO_AUTH_CB = SSO.SSO_AUTH_CB;
|
||||
COMMANDS.SSO_WRITE_BLOCK = SSO.SSO_WRITE_BLOCK; // Account creation only
|
||||
COMMANDS.SSO_UPDATE_BLOCK = SSO.SSO_UPDATE_BLOCK; // Password change
|
||||
COMMANDS.SSO_VALIDATE = SSO.SSO_VALIDATE;
|
||||
*/
|
||||
|
||||
var randomToken = () => Nacl.util.encodeBase64(Nacl.randomBytes(24)).replace(/\//g, '-');
|
||||
|
||||
|
|
|
@ -513,6 +513,11 @@ app.use("/block", (req, res, next) => {
|
|||
next();
|
||||
});
|
||||
|
||||
Object.keys(plugins || {}).forEach(name => {
|
||||
let plugin = plugins[name];
|
||||
if (!plugin.addHttpEndpoints) { return; }
|
||||
plugin.addHttpEndpoints(Env, app);
|
||||
});
|
||||
|
||||
app.use("/customize", Express.static('customize'));
|
||||
app.use("/customize", Express.static('customize.dist'));
|
||||
|
@ -626,6 +631,35 @@ var serveBroadcast = makeRouteCache(function () {
|
|||
app.get('/api/config', serveConfig);
|
||||
app.get('/api/broadcast', serveBroadcast);
|
||||
|
||||
(function () {
|
||||
let extensions = plugins._extensions;
|
||||
let styles = plugins._styles;
|
||||
let str = JSON.stringify(extensions);
|
||||
let str2 = JSON.stringify(styles);
|
||||
let js = `let extensions = ${str};
|
||||
let styles = ${str2};
|
||||
let lang = window.cryptpadLanguage;
|
||||
let paths = [];
|
||||
extensions.forEach(name => {
|
||||
paths.push(\`optional!/\${name}/extensions.js\`);
|
||||
paths.push(\`optional!json!/\${name}/translations/messages.json\`);
|
||||
paths.push(\`optional!json!/\${name}/translations/messages.\${lang}.json\`);
|
||||
});
|
||||
styles.forEach(name => {
|
||||
paths.push(\`optional!less!/\${name}/style.less\`);
|
||||
});
|
||||
define(paths, function () {
|
||||
let args = Array.prototype.slice.apply(arguments);
|
||||
return args;
|
||||
}, function () {
|
||||
// ignore missing files
|
||||
});`;
|
||||
app.get('/extensions.js', (req, res) => {
|
||||
res.setHeader('Content-Type', 'text/javascript');
|
||||
res.send(js);
|
||||
});
|
||||
})();
|
||||
|
||||
var Define = function (obj) {
|
||||
return `define(function (){
|
||||
return ${JSON.stringify(obj, null, '\t')};
|
||||
|
@ -719,7 +753,7 @@ app.post('/api/auth', function (req, res, next) {
|
|||
});
|
||||
|
||||
app.use(function (req, res /*, next */) {
|
||||
if (/^(\/favicon\.ico\/|.*\.js\.map)$/.test(req.url)) {
|
||||
if (/^(\/favicon\.ico\/|.*\.js\.map|.*\/translations\/.*\.json)/.test(req.url)) {
|
||||
// ignore common 404s
|
||||
} else {
|
||||
Log.info('HTTP_404', req.url);
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
const fs = require('node:fs');
|
||||
const plugins = {};
|
||||
const extensions = plugins._extensions = [];
|
||||
const styles = plugins._styles = [];
|
||||
|
||||
try {
|
||||
let pluginsDir = fs.readdirSync(__dirname + '/plugins');
|
||||
|
@ -12,6 +14,18 @@ try {
|
|||
try {
|
||||
let plugin = require(`./plugins/${name}/index`);
|
||||
plugins[plugin.name] = plugin.modules;
|
||||
try {
|
||||
let hasExt = fs.existsSync(`lib/plugins/${name}/client/extensions.js`);
|
||||
if (hasExt) {
|
||||
extensions.push(plugin.name.toLowerCase());
|
||||
}
|
||||
} catch (e) {}
|
||||
try {
|
||||
let hasStyle = fs.existsSync(`lib/plugins/${name}/client/style.less`);
|
||||
if (hasStyle) {
|
||||
styles.push(plugin.name.toLowerCase());
|
||||
}
|
||||
} catch (e) {}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
|
|
@ -169,7 +169,23 @@ define([
|
|||
}
|
||||
};
|
||||
|
||||
const blocks = Sidebar.blocks('admin');
|
||||
const blocks = sidebar.blocks;
|
||||
|
||||
// EXTENSION_POINT:ADMIN_CATEGORY
|
||||
common.getExtensions('ADMIN_CATEGORY').forEach(ext => {
|
||||
if (!ext || !ext.id || !ext.name || !ext.content) {
|
||||
return console.error('Invalid extension point', 'ADMIN_CATEGORY', ext);
|
||||
}
|
||||
if (categories[ext.id]) {
|
||||
return console.error('Extension point ID already used', ext);
|
||||
}
|
||||
console.error(ext);
|
||||
categories[ext.id] = {
|
||||
icon: ext.icon,
|
||||
name: ext.name,
|
||||
content: ext.content
|
||||
};
|
||||
});
|
||||
|
||||
const flushCache = (cb) => {
|
||||
cb = cb || function () {};
|
||||
|
@ -3884,6 +3900,32 @@ define([
|
|||
cb(opts);
|
||||
});
|
||||
|
||||
// EXTENSION_POINT:ADMIN_ITEM
|
||||
let utils = {
|
||||
h, Util, Hash
|
||||
};
|
||||
common.getExtensions('ADMIN_ITEM').forEach(ext => {
|
||||
if (!ext || !ext.id || typeof(ext.getContent) !== "function") {
|
||||
return console.error('Invalid extension point', 'ADMIN_CATEGORY', ext);
|
||||
}
|
||||
if (sidebar.hasItem(ext.id)) {
|
||||
return console.error('Extension point ID already used', ext);
|
||||
}
|
||||
|
||||
sidebar.addItem(ext.id, cb => {
|
||||
ext.getContent(common, blocks, utils, content => {
|
||||
cb(content);
|
||||
});
|
||||
}, {
|
||||
noTitle: !ext.title,
|
||||
noHint: !ext.description,
|
||||
title: ext.title,
|
||||
hint: ext.description
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
sidebar.makeLeftside(categories);
|
||||
};
|
||||
|
||||
|
|
|
@ -2822,6 +2822,15 @@ define([
|
|||
initFeedback(data.feedback);
|
||||
}
|
||||
|
||||
if (data.edPublic) {
|
||||
if (Array.isArray(Config.adminKeys) &&
|
||||
Config.adminKeys.includes(data.edPublic)) {
|
||||
// Doesn't provides extra-rights but may show
|
||||
// additional warnings in the UI
|
||||
localStorage.CP_admin = "1";
|
||||
}
|
||||
}
|
||||
|
||||
if (data.loggedIn) {
|
||||
window.CP_logged_in = true;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
define([
|
||||
'optional!/extensions.js'
|
||||
], (Extensions) => {
|
||||
const ext = {};
|
||||
if (!Array.isArray(Extensions) || !Extensions.length) { return ext; }
|
||||
|
||||
let all = Extensions.slice();
|
||||
while(all.length) {
|
||||
let current = all.splice(0, 3);
|
||||
|
||||
let f = current[0];
|
||||
if (typeof(f) !== "function") {
|
||||
continue;
|
||||
}
|
||||
let defaultLang = current[1];
|
||||
let lang = current[2];
|
||||
if (!Object.keys(lang).length && Object.keys(defaultLang).length) {
|
||||
// If our language doesn't exists, use default
|
||||
lang = defaultLang;
|
||||
} else if (Object.keys(defaultLang).length) {
|
||||
// Otherwise fill our language with missing keys
|
||||
Object.keys(defaultLang).forEach(key => {
|
||||
if (typeof(lang[key]) !== "undefined") { return; }
|
||||
lang[key] = defaultLang[key];
|
||||
});
|
||||
}
|
||||
|
||||
lang._getKey = function (key, argArray) {
|
||||
if (!lang[key]) { return '?'; }
|
||||
var text = lang[key];
|
||||
if (typeof(text) === 'string') {
|
||||
return text.replace(/\{(\d+)\}/g, function (str, p1) {
|
||||
if (typeof(argArray[p1]) === 'string' || typeof(argArray[p1]) === "number") {
|
||||
return argArray[p1];
|
||||
}
|
||||
return '';
|
||||
});
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
let currentExt = f(lang) || {};
|
||||
|
||||
Object.keys(currentExt).forEach(key => {
|
||||
ext[key] = ext[key] || [];
|
||||
Array.prototype.push.apply(ext[key], currentExt[key]); // concat in place
|
||||
});
|
||||
}
|
||||
|
||||
ext.getExtensions = id => {
|
||||
let e = ext[id];
|
||||
if (!Array.isArray(e)) { e = []; }
|
||||
return e;
|
||||
};
|
||||
|
||||
return ext;
|
||||
});
|
|
@ -262,9 +262,9 @@ define([
|
|||
options = options || {};
|
||||
const title = options.noTitle ? undefined : h('label.cp-item-label', {
|
||||
id: `cp-${app}-${key}`
|
||||
}, Messages[`${app}_${safeKey}Title`] || key);
|
||||
}, options.title || Messages[`${app}_${safeKey}Title`] || key);
|
||||
const hint = options.noHint ? undefined : h('span.cp-sidebarlayout-description',
|
||||
Messages[`${app}_${safeKey}Hint`] || 'Coming soon...');
|
||||
options.hint || Messages[`${app}_${safeKey}Hint`] || 'Coming soon...');
|
||||
if (hint && options.htmlHint) {
|
||||
hint.innerHTML = Messages[`${app}_${safeKey}Hint`];
|
||||
}
|
||||
|
@ -281,6 +281,10 @@ define([
|
|||
});
|
||||
};
|
||||
|
||||
sidebar.hasItem = key => {
|
||||
return !key || !!items[key];
|
||||
};
|
||||
|
||||
sidebar.addCheckboxItem = (data) => {
|
||||
const key = data.key;
|
||||
let blocks = sidebar.blocks;
|
||||
|
@ -335,7 +339,7 @@ define([
|
|||
'data-category': key
|
||||
}, [
|
||||
icon,
|
||||
Messages[`${app}_cat_${key}`] || key,
|
||||
category.name || Messages[`${app}_cat_${key}`] || key,
|
||||
]);
|
||||
var $item = $(item).appendTo(container);
|
||||
Util.onClickEnter($item, function () {
|
||||
|
|
|
@ -107,9 +107,11 @@ define([
|
|||
var myOOId;
|
||||
var sessionId = Hash.createChannelId();
|
||||
var cpNfInner;
|
||||
let integrationChannel;
|
||||
|
||||
var evOnPatch = Util.mkEvent();
|
||||
var evOnSync = Util.mkEvent();
|
||||
var evIntegrationSave = Util.mkEvent();
|
||||
|
||||
// This structure is used for caching media data and blob urls for each media cryptpad url
|
||||
var mediasData = {};
|
||||
|
@ -313,7 +315,10 @@ define([
|
|||
isCp: cp
|
||||
}
|
||||
}, function (err, h) {
|
||||
if (!err) { evOnSync.fire(); }
|
||||
if (!err) {
|
||||
evOnSync.fire();
|
||||
evIntegrationSave.fire();
|
||||
}
|
||||
cb(err, h);
|
||||
});
|
||||
},
|
||||
|
@ -1423,6 +1428,9 @@ define([
|
|||
|
||||
debug(obj, 'toOO');
|
||||
chan.event('CMD', obj);
|
||||
if (obj && obj.type === "saveChanges") {
|
||||
evIntegrationSave.fire();
|
||||
}
|
||||
};
|
||||
|
||||
chan.on('CMD', function (obj) {
|
||||
|
@ -1844,6 +1852,11 @@ define([
|
|||
}
|
||||
delete APP.oldCursor;
|
||||
}
|
||||
if (integrationChannel) {
|
||||
APP.onDocumentUnlock = () => {
|
||||
integrationChannel.event('EV_INTEGRATION_READY');
|
||||
};
|
||||
}
|
||||
}
|
||||
delete APP.startNew;
|
||||
|
||||
|
@ -2580,6 +2593,22 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
|
|||
});
|
||||
};
|
||||
|
||||
sframeChan.on('EV_INTEGRATION_DOWNLOADAS', function (format) {
|
||||
console.error('DOWNLOAD AS RECEIVED');
|
||||
var data = getContent();
|
||||
x2tConvertData(data, "document.bin", format, function (xlsData) {
|
||||
UI.removeModals();
|
||||
if (xlsData) {
|
||||
var blob = new Blob([xlsData], {type: "application/bin;charset=utf-8"});
|
||||
if (integrationChannel) {
|
||||
integrationChannel.event('EV_INTEGRATION_ON_DOWNLOADAS',
|
||||
blob, { raw: true });
|
||||
}
|
||||
return;
|
||||
}
|
||||
UI.warn(Messages.error);
|
||||
});
|
||||
});
|
||||
sframeChan.on('EV_OOIFRAME_REFRESH', function (data) {
|
||||
// We want to get the "bin" content of a sheet from its json in order to download
|
||||
// something useful from a non-onlyoffice app (download from drive or settings).
|
||||
|
@ -3132,6 +3161,73 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
|
|||
UI.removeLoadingScreen();
|
||||
};
|
||||
|
||||
let convertImportBlob = (blob, title) => {
|
||||
new Response(blob).arrayBuffer().then(function (buffer) {
|
||||
var u8Xlsx = new Uint8Array(buffer);
|
||||
x2tImportData(u8Xlsx, title, 'bin', function (bin) {
|
||||
if (!bin) {
|
||||
return void UI.errorLoadingScreen(Messages.error);
|
||||
}
|
||||
var blob = new Blob([bin], {type: 'text/plain'});
|
||||
var file = getFileType();
|
||||
resetData(blob, file);
|
||||
//saveToServer(blob, title);
|
||||
Title.updateTitle(title);
|
||||
UI.removeLoadingScreen();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (privateData.integration) {
|
||||
let cfg = privateData.integrationConfig || {};
|
||||
common.openIntegrationChannel(APP.onLocal);
|
||||
integrationChannel = common.getSframeChannel();
|
||||
var integrationSave = function (cb) {
|
||||
var ext = cfg.fileType;
|
||||
|
||||
var upload = Util.once(function (_blob) {
|
||||
integrationChannel.query('Q_INTEGRATION_SAVE', {
|
||||
blob: _blob
|
||||
}, cb, {
|
||||
raw: true
|
||||
});
|
||||
});
|
||||
|
||||
var data = getContent();
|
||||
x2tConvertData(data, "document.bin", ext, function (xlsData) {
|
||||
UI.removeModals();
|
||||
if (xlsData) {
|
||||
var blob = new Blob([xlsData], {type: "application/bin;charset=utf-8"});
|
||||
upload(blob);
|
||||
return;
|
||||
}
|
||||
UI.warn(Messages.error);
|
||||
});
|
||||
};
|
||||
const integrationHasUnsavedChanges = function(unsavedChanges, cb) {
|
||||
integrationChannel.query('Q_INTEGRATION_HAS_UNSAVED_CHANGES', unsavedChanges, cb);
|
||||
};
|
||||
var inte = common.createIntegration(integrationSave,
|
||||
integrationHasUnsavedChanges);
|
||||
if (inte) {
|
||||
evIntegrationSave.reg(function () {
|
||||
inte.changed();
|
||||
});
|
||||
}
|
||||
integrationChannel.on('Q_INTEGRATION_NEEDSAVE', function (data, cb) {
|
||||
integrationSave(function (obj) {
|
||||
if (obj && obj.error) { console.error(obj.error); }
|
||||
cb();
|
||||
});
|
||||
});
|
||||
if (privateData.initialState) {
|
||||
var blob = privateData.initialState;
|
||||
let title = `document.${cfg.fileType}`;
|
||||
console.error(blob, title);
|
||||
return convertImportBlob(blob, title);
|
||||
}
|
||||
}
|
||||
|
||||
if (privateData.isNewFile && privateData.fromFileData) {
|
||||
try {
|
||||
(function () {
|
||||
|
|
|
@ -11,12 +11,15 @@ define([
|
|||
'/common/sframe-common-outer.js'
|
||||
], function (nThen, ApiConfig, DomReady, Hash, SFCommonO) {
|
||||
|
||||
var isIntegration = Boolean(window.CP_integration_outer);
|
||||
var integration = window.CP_integration_outer || {};
|
||||
|
||||
// Loaded in load #2
|
||||
var hash, href, version;
|
||||
nThen(function (waitFor) {
|
||||
DomReady.onReady(waitFor());
|
||||
}).nThen(function (waitFor) {
|
||||
var obj = SFCommonO.initIframe(waitFor, true);
|
||||
var obj = SFCommonO.initIframe(waitFor, true, integration.pathname);
|
||||
href = obj.href;
|
||||
hash = obj.hash;
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
|
@ -24,9 +27,14 @@ define([
|
|||
var opts = parsed.getOptions();
|
||||
version = opts.versionHash;
|
||||
}
|
||||
if (isIntegration) {
|
||||
href = integration.href;
|
||||
hash = integration.hash;
|
||||
}
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
var addData = function (obj) {
|
||||
obj.ooType = window.location.pathname.replace(/^\//, '').replace(/\/$/, '');
|
||||
let path = (integration && integration.pathname) || window.location.pathname;
|
||||
obj.ooType = path.replace(/^\//, '').replace(/\/$/, '');
|
||||
obj.ooVersionHash = version;
|
||||
obj.ooForceVersion = localStorage.CryptPad_ooVersion || "";
|
||||
};
|
||||
|
@ -154,18 +162,21 @@ define([
|
|||
Utils.initUnsafeIframe(obj, cb);
|
||||
});
|
||||
|
||||
|
||||
|
||||
};
|
||||
SFCommonO.start({
|
||||
hash: hash,
|
||||
href: href,
|
||||
type: 'oo',
|
||||
useCreationScreen: true,
|
||||
addData: addData,
|
||||
addRpc: addRpc,
|
||||
getPropChannels: getPropChannels,
|
||||
messaging: true
|
||||
messaging: true,
|
||||
useCreationScreen: !isIntegration,
|
||||
noDrive: true,
|
||||
integration: isIntegration,
|
||||
integrationUtils: integration.utils,
|
||||
integrationConfig: integration.config || {},
|
||||
initialState: integration.initialState || undefined
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3014,6 +3014,8 @@ define([
|
|||
// Make sure we have a valid user object before emitting cacheready
|
||||
if (rt.proxy && !rt.proxy.drive) { return; }
|
||||
|
||||
returned.edPublic = rt.proxy.edPublic;
|
||||
|
||||
onCacheReady(clientId, function () {
|
||||
if (typeof(cb) === "function") { cb(returned); }
|
||||
onCacheReadyEvt.fire();
|
||||
|
@ -3047,6 +3049,8 @@ define([
|
|||
drive[Constants.oldStorageKey] = [];
|
||||
}
|
||||
*/
|
||||
|
||||
returned.edPublic = rt.proxy.edPublic;
|
||||
// Drive already exist: return the existing drive, don't load data from legacy store
|
||||
if (store.manager) {
|
||||
// If a cache is loading, make sure it is complete before calling onReady
|
||||
|
@ -3156,6 +3160,7 @@ define([
|
|||
loadUniversal(Cursor, 'cursor', function () {});
|
||||
loadUniversal(Integration, 'integration', function () {});
|
||||
loadUniversal(Messenger, 'messenger', function () {});
|
||||
loadOnlyOffice();
|
||||
store.messenger = store.modules['messenger'];
|
||||
|
||||
// And now we're ready
|
||||
|
|
|
@ -12,7 +12,7 @@ define([
|
|||
|
||||
var CURRENT_VERSION = X2T.CURRENT_VERSION = CurrentVersion.currentVersion;
|
||||
var debug = function (str) {
|
||||
if (localStorage.CryptPad_dev !== "1") { return; }
|
||||
//if (localStorage.CryptPad_dev !== "1") { return; }
|
||||
console.debug(str);
|
||||
};
|
||||
|
||||
|
|
|
@ -648,8 +648,8 @@ define([
|
|||
const integrationHasUnsavedChanges = function(unsavedChanges, cb) {
|
||||
integrationChannel.query('Q_INTEGRATION_HAS_UNSAVED_CHANGES', unsavedChanges, cb);
|
||||
};
|
||||
var inte = common.createIntegration(onLocal, cpNfInner.chainpad,
|
||||
integrationSave, integrationHasUnsavedChanges);
|
||||
var inte = common.createIntegration(integrationSave,
|
||||
integrationHasUnsavedChanges);
|
||||
if (inte) {
|
||||
integration = true;
|
||||
evIntegrationSave.reg(function () {
|
||||
|
|
|
@ -9,8 +9,6 @@ define([
|
|||
|
||||
module.create = function (
|
||||
Common,
|
||||
onLocal,
|
||||
chainpad,
|
||||
saveHandler,
|
||||
unsavedChangesHandler) {
|
||||
|
||||
|
|
|
@ -2077,6 +2077,16 @@ define([
|
|||
cfg.integrationUtils.save(obj, cb);
|
||||
}
|
||||
});
|
||||
sframeChan.on('EV_INTEGRATION_READY', function () {
|
||||
if (cfg.integrationUtils && cfg.integrationUtils.onReady) {
|
||||
cfg.integrationUtils.onReady();
|
||||
}
|
||||
});
|
||||
sframeChan.on('EV_INTEGRATION_ON_DOWNLOADAS', function (obj) {
|
||||
if (cfg.integrationUtils && cfg.integrationUtils.onDownloadAs) {
|
||||
cfg.integrationUtils.onDownloadAs(obj);
|
||||
}
|
||||
});
|
||||
sframeChan.on('Q_INTEGRATION_HAS_UNSAVED_CHANGES', function (obj, cb) {
|
||||
if (cfg.integrationUtils && cfg.integrationUtils.onHasUnsavedChanges) {
|
||||
cfg.integrationUtils.onHasUnsavedChanges(obj, cb);
|
||||
|
@ -2090,6 +2100,15 @@ define([
|
|||
integrationSave = function (cb) {
|
||||
sframeChan.query('Q_INTEGRATION_NEEDSAVE', null, cb);
|
||||
};
|
||||
|
||||
if (cfg.integrationUtils) {
|
||||
if (cfg.integrationUtils.setDownloadAs) {
|
||||
cfg.integrationUtils.setDownloadAs(format => {
|
||||
sframeChan.event('EV_INTEGRATION_DOWNLOADAS', format);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (cfg.messaging) {
|
||||
|
|
|
@ -34,6 +34,7 @@ define([
|
|||
'/common/common-constants.js',
|
||||
'/components/localforage/dist/localforage.min.js',
|
||||
'/common/hyperscript.js',
|
||||
'/common/extensions.js'
|
||||
], function (
|
||||
$,
|
||||
ApiConfig,
|
||||
|
@ -64,7 +65,8 @@ define([
|
|||
Language,
|
||||
Constants,
|
||||
localForage,
|
||||
h
|
||||
h,
|
||||
Ext
|
||||
) {
|
||||
// Chainpad Netflux Inner
|
||||
var funcs = {};
|
||||
|
@ -775,6 +777,8 @@ define([
|
|||
return Util.checkRestrictedApp(app, AppConfig, ea, priv.plan, priv.loggedIn);
|
||||
};
|
||||
|
||||
funcs.getExtensions = Ext.getExtensions;
|
||||
|
||||
funcs.mailbox = {};
|
||||
|
||||
Object.freeze(funcs);
|
||||
|
|
|
@ -14,6 +14,13 @@
|
|||
var getTxid = function () {
|
||||
return Math.random().toString(16).replace('0.', '');
|
||||
};
|
||||
var getInstanceURL = () => {
|
||||
var scripts = document.getElementsByTagName('script');
|
||||
for (var i = scripts.length - 1; i >= 0; i--) {
|
||||
var match = scripts[i].src.match(/(.*)web-apps\/apps\/api\/documents\/api.js/i);
|
||||
if (match) { return match[1]; }
|
||||
}
|
||||
};
|
||||
|
||||
var makeChan = function (iframe, iOrigin) {
|
||||
var handlers = {};
|
||||
|
@ -75,6 +82,7 @@
|
|||
|
||||
var makeIframe = function () {}; // placeholder
|
||||
|
||||
let onDocumentReady = [];
|
||||
var start = function (config, chan) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
setTimeout(function () {
|
||||
|
@ -83,7 +91,8 @@
|
|||
|
||||
var getBlob = function (cb) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', config.document.url, true);
|
||||
let url = config.document.url;
|
||||
xhr.open('GET', url, true);
|
||||
xhr.responseType = 'blob';
|
||||
xhr.onload = function () {
|
||||
if (this.status === 200) {
|
||||
|
@ -131,9 +140,18 @@
|
|||
|
||||
var getSession = function (cb) {
|
||||
chan.send('GET_SESSION', {
|
||||
key: key
|
||||
key: key,
|
||||
keepOld: !config.events.onNewKey
|
||||
}, function (obj) {
|
||||
if (obj && obj.error) { reject(obj.error); return console.error(obj.error); }
|
||||
|
||||
// OnlyOffice
|
||||
if (!config.events.onNewKey) {
|
||||
key = obj.key;
|
||||
console.error(key, obj);
|
||||
return void cb();
|
||||
}
|
||||
|
||||
if (obj.key !== key) {
|
||||
// The outside app may reject our new key if the "old" one is deprecated.
|
||||
// This will happen if multiple users try to update the key at the same
|
||||
|
@ -153,21 +171,52 @@
|
|||
};
|
||||
getSession(onKeyValidated);
|
||||
|
||||
chan.on('DOCUMENT_READY', function () {
|
||||
if (config.events.onAppReady) {
|
||||
config.events.onAppReady();
|
||||
}
|
||||
if (config.events.onReady) {
|
||||
config.events.onReady();
|
||||
}
|
||||
if (config.events.onDocumentReady) {
|
||||
config.events.onDocumentReady();
|
||||
}
|
||||
onDocumentReady.forEach(f => {
|
||||
try { f(); } catch (e) { console.error(e); }
|
||||
});
|
||||
});
|
||||
|
||||
chan.on('ON_DOWNLOADAS', blob => {
|
||||
let url = URL.createObjectURL(blob);
|
||||
config.events.onDownloadAs({
|
||||
data: {
|
||||
fileType: config.document && config.document.fileType,
|
||||
url
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
chan.on('SAVE', function (data, cb) {
|
||||
blob = data;
|
||||
config.events.onSave(data, cb);
|
||||
});
|
||||
chan.on('RELOAD', function () {
|
||||
config.document.blob = blob;
|
||||
document.getElementById('cryptpad-editor').remove();
|
||||
if (!config.editorConfig) { // Not OnlyOffice shim
|
||||
document.getElementById('cryptpad-editor').remove();
|
||||
}
|
||||
makeIframe(config);
|
||||
});
|
||||
chan.on('HAS_UNSAVED_CHANGES', function(unsavedChanges, cb) {
|
||||
config.events.onHasUnsavedChanges(unsavedChanges);
|
||||
if (config.events.onHasUnsavedChanges) {
|
||||
config.events.onHasUnsavedChanges(unsavedChanges);
|
||||
}
|
||||
cb();
|
||||
});
|
||||
chan.on('ON_INSERT_IMAGE', function(data, cb) {
|
||||
config.events.onInsertImage(data, cb);
|
||||
if (config.events.onIntertImage) {
|
||||
config.events.onInsertImage(data, cb);
|
||||
} else { cb(); }
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -192,7 +241,28 @@
|
|||
* @return {promise}
|
||||
*/
|
||||
var init = function (cryptpadURL, containerId, config) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
// OnlyOffice shim: don't provide a URL
|
||||
if (!config && typeof(containerId) === "object") {
|
||||
config = containerId;
|
||||
containerId = cryptpadURL;
|
||||
cryptpadURL = getInstanceURL();
|
||||
}
|
||||
|
||||
// OnlyOffice shim
|
||||
let url = config.document.url;
|
||||
if (/^http:\/\/localhost\/cache\/files\//.test(url)) {
|
||||
url = url.replace(/(http:\/\/localhost\/cache\/files\/)/, getInstanceURL() + 'ooapi/');
|
||||
}
|
||||
config.document.url = url;
|
||||
if (config.documentType === "spreadsheet") {
|
||||
config.documentType = "sheet";
|
||||
}
|
||||
if (config.documentType === "text") {
|
||||
config.documentType = "doc";
|
||||
}
|
||||
|
||||
let chan;
|
||||
let ret = new Promise(function (resolve, reject) {
|
||||
setTimeout(function () {
|
||||
|
||||
if (!cryptpadURL || typeof(cryptpadURL) !== "string") {
|
||||
|
@ -209,8 +279,8 @@
|
|||
|
||||
if (!config) { return reject('Missing args: no data provided'); }
|
||||
if(['document.url', 'document.fileType', 'documentType',
|
||||
'events.onSave', 'events.onHasUnsavedChanges',
|
||||
'events.onNewKey', 'events.onInsertImage'].some(function (k) {
|
||||
/*'events.onSave', 'events.onHasUnsavedChanges',
|
||||
'events.onNewKey', 'events.onInsertImage'*/].some(function (k) {
|
||||
var s = k.split('.');
|
||||
var c = config;
|
||||
return s.some(function (key) {
|
||||
|
@ -235,14 +305,23 @@
|
|||
makeIframe = function (config) {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('id', 'cryptpad-editor');
|
||||
iframe.setAttribute('name', 'frameEditor');
|
||||
iframe.setAttribute('align', 'top');
|
||||
iframe.setAttribute("src", url);
|
||||
container.appendChild(iframe);
|
||||
iframe.setAttribute("width", config.width);
|
||||
iframe.setAttribute("height", config.height);
|
||||
if (config.editorConfig) { // OnlyOffice
|
||||
container.replaceWith(iframe);
|
||||
container = iframe;
|
||||
} else {
|
||||
container.appendChild(iframe);
|
||||
}
|
||||
|
||||
var onMsg = function (msg) {
|
||||
var data = typeof(msg.data) === "string" ? JSON.parse(msg.data) : msg.data;
|
||||
if (!data || data.q !== 'INTEGRATION_READY') { return; }
|
||||
window.removeEventListener('message', onMsg);
|
||||
var chan = makeChan(iframe, parsed.origin);
|
||||
chan = makeChan(iframe, parsed.origin);
|
||||
start(config, chan).then(resolve).catch(reject);
|
||||
};
|
||||
window.addEventListener('message', onMsg);
|
||||
|
@ -250,8 +329,24 @@
|
|||
makeIframe(config);
|
||||
});
|
||||
});
|
||||
|
||||
ret.downloadAs = (arg) => {
|
||||
if (!chan) {
|
||||
return void onDocumentReady.push(() => {
|
||||
ret.downloadAs(arg);
|
||||
});
|
||||
}
|
||||
|
||||
chan.send('DOWNLOAD_AS', arg);
|
||||
};
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
init.version = () => { return '7.3.0'; };
|
||||
init.DocEditor = init; // OnlyOffice shim
|
||||
|
||||
window.DocsAPI = init;
|
||||
return init;
|
||||
};
|
||||
|
||||
|
|
|
@ -174,6 +174,7 @@ define([
|
|||
edPublic: proxy.edPublic
|
||||
}, function (e) {
|
||||
if (e) { UI.alert(Messages.error); return console.error(e); }
|
||||
localStorage.CP_admin = "1";
|
||||
startOnboarding(data.network, proxy);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -96,6 +96,13 @@ define([
|
|||
http.send();
|
||||
};
|
||||
chan.on('GET_SESSION', function (data, cb) {
|
||||
if (data.keepOld) {
|
||||
var key = data.key + "000000000000000000000000000000000";
|
||||
console.warn('KEY', key);
|
||||
return void cb({
|
||||
key: `/2/integration/edit/${key.slice(0,24)}/`
|
||||
});
|
||||
}
|
||||
var getHash = function () {
|
||||
//isNew = true;
|
||||
return Hash.createRandomHash('integration');
|
||||
|
@ -126,6 +133,24 @@ define([
|
|||
var onInsertImage = function (data, cb) {
|
||||
chan.send('ON_INSERT_IMAGE', data, cb);
|
||||
};
|
||||
var onReady = function () {
|
||||
chan.send('DOCUMENT_READY', {});
|
||||
};
|
||||
|
||||
let downloadAs;
|
||||
chan.on('DOWNLOAD_AS', function (format) {
|
||||
if (typeof(downloadAs) !== "function") {
|
||||
console.error('UNSUPPORTED COMMAND', 'downloadAs');
|
||||
return;
|
||||
}
|
||||
downloadAs(format);
|
||||
});
|
||||
let setDownloadAs = f => {
|
||||
downloadAs = f;
|
||||
};
|
||||
let onDownloadAs = function (blob) { // DownloadAs callback
|
||||
chan.send('ON_DOWNLOADAS', blob);
|
||||
};
|
||||
|
||||
chan.on('START', function (data) {
|
||||
console.warn('INNER START', data);
|
||||
|
@ -141,13 +166,20 @@ define([
|
|||
autosave: data.autosave
|
||||
},
|
||||
utils: {
|
||||
onReady: onReady,
|
||||
onDownloadAs,
|
||||
setDownloadAs,
|
||||
save: save,
|
||||
reload: reload,
|
||||
onHasUnsavedChanges: onHasUnsavedChanges,
|
||||
onInsertImage: onInsertImage
|
||||
}
|
||||
};
|
||||
require(['/common/sframe-app-outer.js'], function () {
|
||||
let path = "/common/sframe-app-outer.js";
|
||||
if (['sheet', 'doc', 'presentation'].includes(data.application)) {
|
||||
path = '/common/onlyoffice/main.js';
|
||||
}
|
||||
require([path], function () {
|
||||
console.warn('SAO REQUIRED');
|
||||
delete window.CP_integration_outer;
|
||||
});
|
||||
|
|
|
@ -13,7 +13,7 @@ define("optional", [], {
|
|||
var onLoadFailure = function(err){
|
||||
// optional module failed to load.
|
||||
var failedId = err.requireModules && err.requireModules[0];
|
||||
console.warn("Could not load optional module: " + failedId);
|
||||
//console.warn("Could not load optional module: " + failedId);
|
||||
|
||||
// Undefine the module to cleanup internal stuff in requireJS
|
||||
requirejs.undef(failedId);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../../../../cryptpad-api.js
|
Loading…
Reference in New Issue