Merge branch 'staging' into team

This commit is contained in:
yflory 2019-09-10 15:32:05 +02:00
commit 798296489a
19 changed files with 185 additions and 91 deletions

View File

@ -103,7 +103,7 @@ define([
])*/
])
]),
h('div.cp-version-footer', "CryptPad v3.0.1 (Aurochs' revenge)")
h('div.cp-version-footer', "CryptPad v3.1.0 (Baiji)")
]);
};

View File

@ -1,8 +1,7 @@
module.exports = function (f) {
var called;
module.exports = function (f, g) {
return function () {
if (called) { return; }
called = true;
if (!f) { return; }
f.apply(this, Array.prototype.slice.call(arguments));
f = g;
};
};

View File

@ -1,7 +1,7 @@
{
"name": "cryptpad",
"description": "realtime collaborative visual editor with zero knowlege server",
"version": "3.0.1",
"version": "3.1.0",
"license": "AGPL-3.0+",
"repository": {
"type": "git",

36
rpc.js
View File

@ -294,37 +294,33 @@ var getUploadSize = function (Env, channel, cb) { // FIXME FILES
});
};
const batchFileSize = BatchRead("GET_FILE_SIZE");
var getFileSize = function (Env, channel, cb) {
if (!isValidId(channel)) { return void cb('INVALID_CHAN'); }
batchFileSize(channel, cb, function (done) {
if (channel.length === 32) {
if (typeof(Env.msgStore.getChannelSize) !== 'function') {
return done('GET_CHANNEL_SIZE_UNSUPPORTED');
}
return void Env.msgStore.getChannelSize(channel, function (e, size /*:number*/) {
if (e) {
if (e.code === 'ENOENT') { return void done(void 0, 0); }
return void done(e.code);
}
done(void 0, size);
});
if (channel.length === 32) {
if (typeof(Env.msgStore.getChannelSize) !== 'function') {
return cb('GET_CHANNEL_SIZE_UNSUPPORTED');
}
// 'channel' refers to a file, so you need another API
getUploadSize(Env, channel, function (e, size) {
if (typeof(size) === 'undefined') { return void done(e); }
done(void 0, size);
return void Env.msgStore.getChannelSize(channel, function (e, size /*:number*/) {
if (e) {
if (e.code === 'ENOENT') { return void cb(void 0, 0); }
return void cb(e.code);
}
cb(void 0, size);
});
}
// 'channel' refers to a file, so you need another API
getUploadSize(Env, channel, function (e, size) {
if (typeof(size) === 'undefined') { return void cb(e); }
cb(void 0, size);
});
};
const batchMetadata = BatchRead("GET_METADATA");
var getMetadata = function (Env, channel, cb) {
if (!isValidId(channel)) { return void cb('INVALID_CHAN'); }
if (channel.length !== 32) { return cb("INVALID_CHAN"); }
if (channel.length !== 32) { return cb("INVALID_CHAN_LENGTH"); }
batchMetadata(channel, cb, function (done) {
var ref = {};

View File

@ -251,8 +251,7 @@ nThen(function (w) {
return void console.error(err);
}
// XXX validate that the write was actually successful by checking its size
// TODO validate that the write was actually successful by checking its size
response = response;
// shutdown doesn't work, so we need to do this instead
}));

View File

@ -76,7 +76,7 @@ var getMetadataAtPath = function (Env, path, cb) {
catch (e) {
console.log("getMetadataAtPath");
console.error(e);
complete('INVALID_METADATA');
complete('INVALID_METADATA', metadata);
}
});
stream.on('end', function () {

View File

@ -158,14 +158,14 @@ define([
var done = function () {
clearTimeout(timeout);
if (btime) {
console.log("Compiling [" + url + "] took " + (+new Date() - btime) + "ms");
console.info("Compiling [" + url + "] took " + (+new Date() - btime) + "ms");
}
cb();
};
stack.push(url);
cacheGet(url, function (css) {
if (css) { return void loadSubmodulesAndInject(css, url, done, stack); }
console.log('CACHE MISS ' + url);
console.debug('CACHE MISS ' + url);
((/\.less([\?\#].*)?$/.test(url)) ? loadLess : loadCSS)(url, function (err, css) {
if (!css) { return void console.error(err); }
var output = fixAllURLs(css, url);

View File

@ -728,6 +728,15 @@ define([
network.on('reconnect', function () {
if (channel && channel.stopped) { return; }
if (!channels[data.channel]) { return; }
if (!joining[data.channel]) {
joining[data.channel] = function () {
console.log("reconnected to %s", data.channel);
};
} else {
console.error("Reconnected to a chat channel (%s) which was not fully connected", data.channel);
}
network.join(data.channel).then(onOpen, function (err) {
console.error(err);
});

View File

@ -406,7 +406,11 @@ define([
$d.append(UI.dialog.selectable(owners, {
id: 'cp-app-prop-owners',
}));
if (owned) {
var parsed;
if (data.href || data.roHref) {
parsed = Hash.parsePadUrl(data.href || data.roHref);
}
if (owned && data.roHref && parsed.type !== 'drive' && parsed.hashData.type === 'pad') {
var manageOwners = h('button.no-margin', Messages.owner_openModalButton);
$(manageOwners).click(function () {
var modal = createOwnerModal(common, data);
@ -445,7 +449,6 @@ define([
$d.append(password);
}
var parsed = Hash.parsePadUrl(data.href || data.roHref);
if (!data.noEditPassword && owned && parsed.hashData.type === 'pad' && parsed.type !== "sheet") { // FIXME SHEET fix password change for sheets
var sframeChan = common.getSframeChannel();
var changePwTitle = Messages.properties_changePassword;
@ -590,7 +593,7 @@ define([
id: 'cp-app-prop-size',
}));
if (data.sharedFolder) {
if (data.sharedFolder && false) {
$('<label>', {'for': 'cp-app-prop-channel'}).text('Channel ID').appendTo($d);
if (AppConfig.pinBugRecovery) { $d.append(h('p', AppConfig.pinBugRecovery)); }
$d.append(UI.dialog.selectable(data.channel, {

View File

@ -222,13 +222,14 @@
return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
};
Util.noop = function () {};
/* for wrapping async functions such that they can only be called once */
Util.once = function (f) {
var called;
Util.once = function (f, g) {
return function () {
if (called) { return; }
called = true;
if (!f) { return; }
f.apply(this, Array.prototype.slice.call(arguments));
f = g;
};
};

View File

@ -1366,7 +1366,7 @@ define([
console.log(parsed);
return;
} else {
console.log(parsed);
//console.log(parsed);
}
Util.fetch(parsed.href, waitFor(function (err, arraybuffer) {
if (err) { return void console.log(err); }

View File

@ -4,9 +4,10 @@ define(['/api/config'], function (ApiConfig) {
var DEFAULT_MAIN = '/customize/main-favicon.png?' + ApiConfig.requireConf.urlArgs;
var DEFAULT_ALT = '/customize/alt-favicon.png?' + ApiConfig.requireConf.urlArgs;
var document = window.document;
var isSupported = Module.isSupported = function () {
return typeof(window.Notification) === 'function';
return typeof(window.Notification) === 'function' && window.isSecureContext;
};
var hasPermission = Module.hasPermission = function () {
@ -22,9 +23,11 @@ define(['/api/config'], function (ApiConfig) {
};
var create = Module.create = function (msg, title, icon) {
if (!icon) {
if (document && !icon) {
var favicon = document.getElementById('favicon');
icon = favicon.getAttribute('data-main-favicon') || DEFAULT_MAIN;
icon = favicon.getAttribute('data-main-favicon') || DEFAULT_ALT;
} else if (!icon) {
icon = DEFAULT_ALT;
}
return new Notification(title,{
@ -52,7 +55,10 @@ define(['/api/config'], function (ApiConfig) {
};
var createFavicon = function () {
console.log("creating favicon");
if (!document) {
return void console.error('document is not available in this context');
}
console.debug("creating favicon");
var fav = document.createElement('link');
var attrs = {
id: 'favicon',
@ -68,9 +74,12 @@ define(['/api/config'], function (ApiConfig) {
document.head.appendChild(fav);
};
if (!document.getElementById('favicon')) { createFavicon(); }
if (document && !document.getElementById('favicon')) { createFavicon(); }
Module.tab = function (frequency, count) {
if (!document) {
return void console.error('document is not available in this context');
}
var key = '_pendingTabNotification';
var favicon = document.getElementById('favicon');

View File

@ -48,6 +48,7 @@ define([
};
var CHECKPOINT_INTERVAL = 50;
var DISPLAY_RESTORE_BUTTON = false;
var debug = function (x) {
if (!window.CP_DEV_MODE) { return; }
@ -237,6 +238,35 @@ define([
}
};
var onUploaded = function (ev, data, err) {
if (err) {
console.error(err);
return void UI.alert(Messages.oo_saveError);
}
var i = Math.floor(ev.index / CHECKPOINT_INTERVAL);
content.hashes[i] = {
file: data.url,
hash: ev.hash,
index: ev.index
};
oldHashes = JSON.parse(JSON.stringify(content.hashes));
content.saveLock = undefined;
APP.onLocal();
APP.realtime.onSettle(function () {
fixSheets();
UI.log(Messages.saved);
if (ev.callback) {
return void ev.callback();
}
});
sframeChan.query('Q_OO_COMMAND', {
cmd: 'UPDATE_HASH',
data: ev.hash
}, function (err, obj) {
if (err || (obj && obj.error)) { console.error(err || obj.error); }
});
};
var fmConfig = {
noHandlers: true,
noStore: true,
@ -244,32 +274,7 @@ define([
onUploaded: function (ev, data) {
if (!data || !data.url) { return; }
sframeChan.query('Q_OO_SAVE', data, function (err) {
if (err) {
console.error(err);
return void UI.alert(Messages.oo_saveError);
}
var i = Math.floor(ev.index / CHECKPOINT_INTERVAL);
content.hashes[i] = {
file: data.url,
hash: ev.hash,
index: ev.index
};
oldHashes = JSON.parse(JSON.stringify(content.hashes));
content.saveLock = undefined;
APP.onLocal();
APP.realtime.onSettle(function () {
fixSheets();
UI.log(Messages.saved);
if (ev.callback) {
return void ev.callback();
}
});
sframeChan.query('Q_OO_COMMAND', {
cmd: 'UPDATE_HASH',
data: ev.hash
}, function (err, obj) {
if (err || (obj && obj.error)) { console.error(err || obj.error); }
});
onUploaded(ev, data, err);
});
}
};
@ -307,6 +312,19 @@ define([
}
}, to);
};
var restoreLastCp = function () {
content.saveLock = myOOId;
APP.onLocal();
APP.realtime.onSettle(function () {
onUploaded({
hash: ooChannel.lastHash,
index: ooChannel.cpIndex
}, {
url: getLastCp().file,
});
});
};
var openRtChannel = function (cb) {
if (rtChannel.ready) { return void cb(); }
@ -880,6 +898,16 @@ define([
});
$save.appendTo($rightside);
}
if (window.CP_DEV_MODE || DISPLAY_RESTORE_BUTTON) {
common.createButton('', true, {
name: 'restore',
icon: 'fa-history',
hiddenReadOnly: true
}).click(function () {
if (initializing) { return void console.error('initializing'); }
restoreLastCp();
}).attr('title', 'Restore last checkpoint').appendTo($rightside);
}
var $export = common.createButton('export', true, {}, exportFile);
$export.appendTo($rightside);

View File

@ -703,8 +703,13 @@ define([
href: href,
channel: channel,
title: data.driveReadmeTitle,
owners: [ store.proxy.edPublic ],
};
Store.addPad(clientId, fileData, cb);
}, {
metadata: {
owners: [ store.proxy.edPublic ],
},
});
});
};
@ -989,11 +994,18 @@ define([
pad.href = href;
});
// If we've just accepted ownership for a pad stored in a shared folder,
// we need to make a copy of this pad in our drive. We're going to check
// the pad is owned by us BUT is not stored in our main drive
var inMyDrive = datas.some(function (obj) {
return !obj.fId;
});
// XXX owned by one of our teams?
var ownedByMe = Array.isArray(owners) && owners.indexOf(store.proxy.edPublic) !== -1;
// Add the pad if it does not exist in our drive
if (!contains) {
if (!contains || (ownedByMe && !inMyDrive)) {
var autoStore = Util.find(store.proxy, ['settings', 'general', 'autostore']);
// XXX owned by one of our teams?
var ownedByMe = Array.isArray(owners) && owners.indexOf(store.proxy.edPublic) !== -1;
if (autoStore !== 1 && !data.forceSave && !data.path && !ownedByMe) {
// send event to inner to display the corner popup
postMessage(clientId, "AUTOSTORE_DISPLAY_POPUP", {
@ -1920,12 +1932,12 @@ define([
broadcast([], "UPDATE_METADATA");
},
pinPads: function (data, cb) { Store.pinPads(null, data, cb); },
}, waitFor, function (ev, data, clients) {
}, waitFor, function (ev, data, clients, cb) {
clients.forEach(function (cId) {
postMessage(cId, 'MAILBOX_EVENT', {
ev: ev,
data: data
});
}, cb);
});
});
};

View File

@ -148,12 +148,15 @@ define([
return void cb({error: err});
});
network.on('reconnect', function () {
var onReconnect = function () {
if (!ctx.channels[channel]) { console.log("cant reconnect", channel); return; }
network.join(channel).then(onOpen, function (err) {
console.error(err);
});
});
};
ctx.channels[channel] = ctx.channels[channel] || {};
ctx.channels[channel].onReconnect = onReconnect;
network.on('reconnect', onReconnect);
};
var updateCursor = function (ctx, data, client, cb) {
@ -173,6 +176,10 @@ define([
var channel = ctx.channels[cursorChan];
if (channel.padChan !== padChan) { return; }
if (channel.wc) { channel.wc.leave(); }
if (channel.onReconnect) {
var network = ctx.store.network;
network.off('reconnect', channel.onReconnect);
}
delete ctx.channels[cursorChan];
return true;
});
@ -190,6 +197,10 @@ define([
chan.clients = chan.clients.filter(filter);
if (chan.clients.length === 0) {
if (chan.wc) { chan.wc.leave(); }
if (chan.onReconnect) {
var network = ctx.store.network;
network.off('reconnect', chan.onReconnect);
}
delete ctx.channels[k];
}
}

View File

@ -2,10 +2,11 @@ define([
'/common/common-util.js',
'/common/common-hash.js',
'/common/common-realtime.js',
'/common/notify.js',
'/common/outer/mailbox-handlers.js',
'/bower_components/chainpad-netflux/chainpad-netflux.js',
'/bower_components/chainpad-crypto/crypto.js',
], function (Util, Hash, Realtime, Handlers, CpNetflux, Crypto) {
], function (Util, Hash, Realtime, Notify, Handlers, CpNetflux, Crypto) {
var Mailbox = {};
var TYPES = [
@ -54,11 +55,11 @@ proxy.mailboxes = {
return (m.viewed || []).indexOf(hash) === -1 && hash !== m.lastKnownHash;
};
var showMessage = function (ctx, type, msg, cId) {
var showMessage = function (ctx, type, msg, cId, cb) {
ctx.emit('MESSAGE', {
type: type,
content: msg
}, cId ? [cId] : ctx.clients);
}, cId ? [cId] : ctx.clients, cb);
};
var hideMessage = function (ctx, type, hash, clients) {
ctx.emit('VIEWED', {
@ -272,7 +273,11 @@ proxy.mailboxes = {
});
}
box.content[hash] = msg;
showMessage(ctx, type, message);
showMessage(ctx, type, message, null, function (obj) {
if (!box.ready) { return; }
if (!obj || !obj.msg) { return; }
Notify.system(undefined, obj.msg);
});
});
} else {
// Message has already been viewed by the user
@ -320,6 +325,7 @@ proxy.mailboxes = {
view(n);
}
});
box.ready = true;
// Continue
onReady();
};

View File

@ -124,10 +124,16 @@ define([
removeFromHistory(data.type, data.hash);
};
var onMessage = function (data) {
var onMessage = function (data, cb) {
// data = { type: 'type', content: {msg: 'msg', hash: 'hash'} }
console.log(data.type, data.content);
console.debug(data.type, data.content);
pushMessage(data);
if (data.content && typeof (data.content.getFormatText) === "function") {
var text = $('<div>').html(data.content.getFormatText()).text();
cb({
msg: text
});
}
if (!history[data.type]) { history[data.type] = []; }
history[data.type].push(data.content);
};
@ -224,7 +230,7 @@ define([
// CHANNEL WITH WORKER
sframeChan.on('EV_MAILBOX_EVENT', function (obj) {
sframeChan.on('EV_MAILBOX_EVENT', function (obj, cb) {
// obj = { ev: 'type', data: obj }
var ev = obj.ev;
var data = obj.data;
@ -232,7 +238,7 @@ define([
return void onHistory(data);
}
if (ev === 'MESSAGE') {
return void onMessage(data);
return void onMessage(data, cb);
}
if (ev === 'VIEWED') {
return void onViewed(data);

View File

@ -414,8 +414,12 @@ define([
});
});
Cryptpad.mailbox.onEvent.reg(function (data) {
sframeChan.event('EV_MAILBOX_EVENT', data);
Cryptpad.mailbox.onEvent.reg(function (data, cb) {
sframeChan.query('EV_MAILBOX_EVENT', data, function (err, obj) {
if (!cb) { return; }
if (err) { return void cb({error: err}); }
cb(obj);
});
});
sframeChan.on('Q_MAILBOX_COMMAND', function (data, cb) {
Cryptpad.mailbox.execCommand(data, cb);

View File

@ -351,13 +351,13 @@
"fm_info_sharedFolder": "Dieser Ordner ist geteilt. Da du aber nicht eingeloggt bist, hast du nur einen schreibgeschützen Zugriff.<br><a href=\"/register/\">Registriere</a> oder <a href=\"/login/\">logge ich ein</a>, damit du diesen Ordner in dein CryptDrive importieren und bearbeiten kannst.",
"fm_info_owned": "Diese Pads sind deine eigenen. Das heißt, dass du sie jederzeit vom Server entfernen kannst. Wenn du das machst, dann sind sie auch für andere Nutzer nicht mehr zugänglich.",
"fm_alert_backupUrl": "Backup-Link für dieses CryptDrive.<br>Es wird <strong>dringend empfohlen</strong>, diesen Link geheim zu halten.<br>Du kannst ihn benutzen, um deine gesamten Dateien abzurufen, wenn dein Browserspeicher gelöscht wurde.<br>Jede Person, die diesen Link hat, kann die Dateien in deinem CryptDrive bearbeiten oder löschen.<br>",
"fm_alert_anonymous": "Hallo, du benutzt CryptPad anonym. Das ist in Ordnung, aber Dokumente können nach einer längerer Inaktivität gelöscht werden. Wir haben fortgeschrittene Funktionen in anonymen CryptDrives deaktiviert, weil wir deutlich machen wollen, dass es kein sicherer Platz zur Ablage von Daten ist. Du kannst <a href=\"https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/\" target=\"_blank\">nachlesen</a>, weshalb wir das machen und weshalb du dich wirklich <a href=\"/register/\">registrieren</a> oder <a href=\"/login/\">einloggen</a> solltest.",
"fm_alert_anonymous": "Hallo, du benutzt CryptPad anonym. Das ist in Ordnung, aber Dokumente können nach längerer Inaktivität gelöscht werden. Wir haben fortgeschrittene Funktionen in anonymen CryptDrives deaktiviert, weil wir deutlich machen wollen, dass es kein sicherer Platz zur Ablage von Daten ist. Du kannst <a href=\"https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/\" target=\"_blank\">nachlesen</a>, weshalb wir das machen und weshalb du dich wirklich <a href=\"/register/\">registrieren</a> oder <a href=\"/login/\">einloggen</a> solltest.",
"fm_backup_title": "Backup-Link",
"fm_nameFile": "Wie soll diese Datei heißen?",
"fm_error_cantPin": "Interner Serverfehler. Bitte lade die Seite neu und versuche es erneut.",
"fm_viewListButton": "Listenansicht",
"fm_viewGridButton": "Kachelansicht",
"fm_renamedPad": "Du hast einen benutzerdefinierten Namen für dieses Pad gesetzt. Sein geteilter Titel ist:<br><b>{0}</b>",
"fm_renamedPad": "Du hast einen benutzerdefinierten Namen für dieses Pad gesetzt. Der geteilte Titel ist:<br><b>{0}</b>",
"fm_canBeShared": "Dieser Ordner kann geteilt werden",
"fm_prop_tagsList": "Tags",
"fm_burnThisDriveButton": "Alle Informationen löschen, die CryptPad in deinem Browser speichert",
@ -1131,7 +1131,7 @@
"uploadFolder_modal_filesPassword": "Passwort für Dateien",
"uploadFolder_modal_owner": "Eigene Dateien",
"uploadFolder_modal_forceSave": "Dateien im CryptDrive speichern",
"convertFolderToSF_SFParent": "Dieser Ordner kann an seinem aktuellen Ort nicht einen geteilten Ordner umgewandelt werden. Verschiebe ihn zunächst aus dem übergeordneten geteilten Ordner heraus.",
"convertFolderToSF_SFParent": "Dieser Ordner kann an seinem aktuellen Ort nicht in einen geteilten Ordner umgewandelt werden. Verschiebe ihn zunächst aus dem übergeordneten geteilten Ordner heraus.",
"convertFolderToSF_SFChildren": "Dieser Ordner kann nicht in einen geteilten Ordner umgewandelt werden, weil er bereits geteilte Ordner enthält. Verschiebe diese geteilten Ordner zunächst an einen anderen Ort.",
"convertFolderToSF_confirm": "Dieser Ordner muss in einen geteilten Ordner umgewandelt werden, damit ihn andere sehen können. Fortfahren?",
"pricing": "Preise und Konditionen",
@ -1149,5 +1149,16 @@
"owner_removeConfirm": "Bist du sicher, dass die Eigentümerschaft der ausgewählten Benutzer entfernen möchtest? Sie werden über diese Aktion informiert.",
"owner_removeMeConfirm": "Du bist dabei, deine Rechte als Eigentümer aufzugeben. Diese Aktion kannst du nicht rückgängig machen. Bist du sicher?",
"owner_openModalButton": "Eigentümer verwalten",
"owner_add": "{0} möchte ein Eigentümer des Pads <b>{1}</b> sein. Bist du damit einverstanden?"
"owner_add": "{0} möchte ein Eigentümer des Pads <b>{1}</b> sein. Bist du damit einverstanden?",
"owner_removeText": "Einen Eigentümer entfernen",
"owner_removePendingText": "Eine ausstehende Einladung zurückziehen",
"owner_addText": "Einen Freund zur Mit-Eigentümerschaft einladen",
"owner_removePendingButton": "Ausgewählte Einladungen zurückziehen",
"owner_addButton": "Zur Eigentümerschaft einladen",
"owner_addConfirm": "Mit-Eigentümer können den Inhalt bearbeiten und dich als Eigentümer entfernen. Bist du sicher?",
"owner_request": "{0} möchte dich zum Eigentümer von <b>{1}</b> machen",
"owner_request_accepted": "{0} hat deine Einladung angenommen, ein Eigentümer von <b>{1}</b> zu sein",
"owner_request_declined": "{0} hat deine Einladung abgelehnt, ein Eigentümer von <b>{1}</b> zu sein",
"owner_removed": "{0} hat dich als Eigentümer von <b>{1}</b> entfernt",
"owner_removedPending": "{0} hat die Einladung zur Eigentümerschaft von <b>{1}</b> zurückgezogen"
}