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) { module.exports = function (f, g) {
var called;
return function () { return function () {
if (called) { return; } if (!f) { return; }
called = true;
f.apply(this, Array.prototype.slice.call(arguments)); f.apply(this, Array.prototype.slice.call(arguments));
f = g;
}; };
}; };

View File

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

18
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) { var getFileSize = function (Env, channel, cb) {
if (!isValidId(channel)) { return void cb('INVALID_CHAN'); } if (!isValidId(channel)) { return void cb('INVALID_CHAN'); }
batchFileSize(channel, cb, function (done) {
if (channel.length === 32) { if (channel.length === 32) {
if (typeof(Env.msgStore.getChannelSize) !== 'function') { if (typeof(Env.msgStore.getChannelSize) !== 'function') {
return done('GET_CHANNEL_SIZE_UNSUPPORTED'); return cb('GET_CHANNEL_SIZE_UNSUPPORTED');
} }
return void Env.msgStore.getChannelSize(channel, function (e, size /*:number*/) { return void Env.msgStore.getChannelSize(channel, function (e, size /*:number*/) {
if (e) { if (e) {
if (e.code === 'ENOENT') { return void done(void 0, 0); } if (e.code === 'ENOENT') { return void cb(void 0, 0); }
return void done(e.code); return void cb(e.code);
} }
done(void 0, size); cb(void 0, size);
}); });
} }
// 'channel' refers to a file, so you need another API // 'channel' refers to a file, so you need another API
getUploadSize(Env, channel, function (e, size) { getUploadSize(Env, channel, function (e, size) {
if (typeof(size) === 'undefined') { return void done(e); } if (typeof(size) === 'undefined') { return void cb(e); }
done(void 0, size); cb(void 0, size);
});
}); });
}; };
const batchMetadata = BatchRead("GET_METADATA"); const batchMetadata = BatchRead("GET_METADATA");
var getMetadata = function (Env, channel, cb) { var getMetadata = function (Env, channel, cb) {
if (!isValidId(channel)) { return void cb('INVALID_CHAN'); } if (!isValidId(channel)) { return void cb('INVALID_CHAN'); }
if (channel.length !== 32) { return cb("INVALID_CHAN_LENGTH"); }
if (channel.length !== 32) { return cb("INVALID_CHAN"); }
batchMetadata(channel, cb, function (done) { batchMetadata(channel, cb, function (done) {
var ref = {}; var ref = {};

View File

@ -251,8 +251,7 @@ nThen(function (w) {
return void console.error(err); 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; response = response;
// shutdown doesn't work, so we need to do this instead // 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) { catch (e) {
console.log("getMetadataAtPath"); console.log("getMetadataAtPath");
console.error(e); console.error(e);
complete('INVALID_METADATA'); complete('INVALID_METADATA', metadata);
} }
}); });
stream.on('end', function () { stream.on('end', function () {

View File

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

View File

@ -728,6 +728,15 @@ define([
network.on('reconnect', function () { network.on('reconnect', function () {
if (channel && channel.stopped) { return; } if (channel && channel.stopped) { return; }
if (!channels[data.channel]) { 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) { network.join(data.channel).then(onOpen, function (err) {
console.error(err); console.error(err);
}); });

View File

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

View File

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

View File

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

View File

@ -48,6 +48,7 @@ define([
}; };
var CHECKPOINT_INTERVAL = 50; var CHECKPOINT_INTERVAL = 50;
var DISPLAY_RESTORE_BUTTON = false;
var debug = function (x) { var debug = function (x) {
if (!window.CP_DEV_MODE) { return; } if (!window.CP_DEV_MODE) { return; }
@ -237,13 +238,7 @@ define([
} }
}; };
var fmConfig = { var onUploaded = function (ev, data, err) {
noHandlers: true,
noStore: true,
body: $('body'),
onUploaded: function (ev, data) {
if (!data || !data.url) { return; }
sframeChan.query('Q_OO_SAVE', data, function (err) {
if (err) { if (err) {
console.error(err); console.error(err);
return void UI.alert(Messages.oo_saveError); return void UI.alert(Messages.oo_saveError);
@ -270,6 +265,16 @@ define([
}, function (err, obj) { }, function (err, obj) {
if (err || (obj && obj.error)) { console.error(err || obj.error); } if (err || (obj && obj.error)) { console.error(err || obj.error); }
}); });
};
var fmConfig = {
noHandlers: true,
noStore: true,
body: $('body'),
onUploaded: function (ev, data) {
if (!data || !data.url) { return; }
sframeChan.query('Q_OO_SAVE', data, function (err) {
onUploaded(ev, data, err);
}); });
} }
}; };
@ -307,6 +312,19 @@ define([
} }
}, to); }, 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) { var openRtChannel = function (cb) {
if (rtChannel.ready) { return void cb(); } if (rtChannel.ready) { return void cb(); }
@ -880,6 +898,16 @@ define([
}); });
$save.appendTo($rightside); $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); var $export = common.createButton('export', true, {}, exportFile);
$export.appendTo($rightside); $export.appendTo($rightside);

View File

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

View File

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

View File

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

View File

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

View File

@ -414,8 +414,12 @@ define([
}); });
}); });
Cryptpad.mailbox.onEvent.reg(function (data) { Cryptpad.mailbox.onEvent.reg(function (data, cb) {
sframeChan.event('EV_MAILBOX_EVENT', data); 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) { sframeChan.on('Q_MAILBOX_COMMAND', function (data, cb) {
Cryptpad.mailbox.execCommand(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_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_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_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_backup_title": "Backup-Link",
"fm_nameFile": "Wie soll diese Datei heißen?", "fm_nameFile": "Wie soll diese Datei heißen?",
"fm_error_cantPin": "Interner Serverfehler. Bitte lade die Seite neu und versuche es erneut.", "fm_error_cantPin": "Interner Serverfehler. Bitte lade die Seite neu und versuche es erneut.",
"fm_viewListButton": "Listenansicht", "fm_viewListButton": "Listenansicht",
"fm_viewGridButton": "Kachelansicht", "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_canBeShared": "Dieser Ordner kann geteilt werden",
"fm_prop_tagsList": "Tags", "fm_prop_tagsList": "Tags",
"fm_burnThisDriveButton": "Alle Informationen löschen, die CryptPad in deinem Browser speichert", "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_filesPassword": "Passwort für Dateien",
"uploadFolder_modal_owner": "Eigene Dateien", "uploadFolder_modal_owner": "Eigene Dateien",
"uploadFolder_modal_forceSave": "Dateien im CryptDrive speichern", "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_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?", "convertFolderToSF_confirm": "Dieser Ordner muss in einen geteilten Ordner umgewandelt werden, damit ihn andere sehen können. Fortfahren?",
"pricing": "Preise und Konditionen", "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_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_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_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"
} }