Merge branch 'staging' into soon
This commit is contained in:
@@ -60,6 +60,23 @@ var factory = function (Util, Crypto, Nacl) {
|
||||
return '/2/' + secret.type + '/view/' + Crypto.b64RemoveSlashes(data.viewKeyStr) + '/' + pass;
|
||||
}
|
||||
};
|
||||
|
||||
Hash.getHiddenHashFromKeys = function (type, secret, opts) {
|
||||
opts = opts || {};
|
||||
var canEdit = (secret.keys && secret.keys.editKeyStr) || secret.key;
|
||||
var mode = (!opts.view && canEdit) ? 'edit/' : 'view/';
|
||||
var pass = secret.password ? 'p/' : '';
|
||||
|
||||
if (secret.keys && secret.keys.fileKeyStr) { mode = ''; }
|
||||
|
||||
var hash = '/3/' + type + '/' + mode + secret.channel + '/' + pass;
|
||||
var hashData = Hash.parseTypeHash(type, hash);
|
||||
if (hashData && hashData.getHash) {
|
||||
return hashData.getHash(opts || {});
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
|
||||
var getFileHashFromKeys = Hash.getFileHashFromKeys = function (secret) {
|
||||
var version = secret.version;
|
||||
var data = secret.keys;
|
||||
@@ -160,12 +177,28 @@ Version 1
|
||||
};
|
||||
var parseTypeHash = Hash.parseTypeHash = function (type, hash) {
|
||||
if (!hash) { return; }
|
||||
var options;
|
||||
var options = [];
|
||||
var parsed = {};
|
||||
var hashArr = fixDuplicateSlashes(hash).split('/');
|
||||
|
||||
var addOptions = function () {
|
||||
parsed.password = options.indexOf('p') !== -1;
|
||||
parsed.present = options.indexOf('present') !== -1;
|
||||
parsed.embed = options.indexOf('embed') !== -1;
|
||||
parsed.ownerKey = getOwnerKey(options);
|
||||
};
|
||||
|
||||
if (['media', 'file', 'user', 'invite'].indexOf(type) === -1) {
|
||||
parsed.type = 'pad';
|
||||
parsed.getHash = function () { return hash; };
|
||||
parsed.getOptions = function () {
|
||||
return {
|
||||
embed: parsed.embed,
|
||||
present: parsed.present,
|
||||
ownerKey: parsed.ownerKey,
|
||||
password: parsed.password
|
||||
};
|
||||
};
|
||||
if (hash.slice(0,1) !== '/' && hash.length >= 56) { // Version 0
|
||||
// Old hash
|
||||
parsed.channel = hash.slice(0, 32);
|
||||
@@ -173,6 +206,18 @@ Version 1
|
||||
parsed.version = 0;
|
||||
return parsed;
|
||||
}
|
||||
|
||||
// Version >= 1: more hash options
|
||||
parsed.getHash = function (opts) {
|
||||
var hash = hashArr.slice(0, 5).join('/') + '/';
|
||||
var owner = typeof(opts.ownerKey) !== "undefined" ? opts.ownerKey : parsed.ownerKey;
|
||||
if (owner) { hash += owner + '/'; }
|
||||
if (parsed.password || opts.password) { hash += 'p/'; }
|
||||
if (opts.embed) { hash += 'embed/'; }
|
||||
if (opts.present) { hash += 'present/'; }
|
||||
return hash;
|
||||
};
|
||||
|
||||
if (hashArr[1] && hashArr[1] === '1') { // Version 1
|
||||
parsed.version = 1;
|
||||
parsed.mode = hashArr[2];
|
||||
@@ -180,18 +225,8 @@ Version 1
|
||||
parsed.key = Crypto.b64AddSlashes(hashArr[4]);
|
||||
|
||||
options = hashArr.slice(5);
|
||||
parsed.present = options.indexOf('present') !== -1;
|
||||
parsed.embed = options.indexOf('embed') !== -1;
|
||||
parsed.ownerKey = getOwnerKey(options);
|
||||
addOptions();
|
||||
|
||||
parsed.getHash = function (opts) {
|
||||
var hash = hashArr.slice(0, 5).join('/') + '/';
|
||||
var owner = typeof(opts.ownerKey) !== "undefined" ? opts.ownerKey : parsed.ownerKey;
|
||||
if (owner) { hash += owner + '/'; }
|
||||
if (opts.embed) { hash += 'embed/'; }
|
||||
if (opts.present) { hash += 'present/'; }
|
||||
return hash;
|
||||
};
|
||||
return parsed;
|
||||
}
|
||||
if (hashArr[1] && hashArr[1] === '2') { // Version 2
|
||||
@@ -201,20 +236,19 @@ Version 1
|
||||
parsed.key = hashArr[4];
|
||||
|
||||
options = hashArr.slice(5);
|
||||
parsed.password = options.indexOf('p') !== -1;
|
||||
parsed.present = options.indexOf('present') !== -1;
|
||||
parsed.embed = options.indexOf('embed') !== -1;
|
||||
parsed.ownerKey = getOwnerKey(options);
|
||||
addOptions();
|
||||
|
||||
return parsed;
|
||||
}
|
||||
if (hashArr[1] && hashArr[1] === '3') { // Version 3: hidden hash
|
||||
parsed.version = 3;
|
||||
parsed.app = hashArr[2];
|
||||
parsed.mode = hashArr[3];
|
||||
parsed.channel = hashArr[4];
|
||||
|
||||
options = hashArr.slice(5);
|
||||
addOptions();
|
||||
|
||||
parsed.getHash = function (opts) {
|
||||
var hash = hashArr.slice(0, 5).join('/') + '/';
|
||||
var owner = typeof(opts.ownerKey) !== "undefined" ? opts.ownerKey : parsed.ownerKey;
|
||||
if (owner) { hash += owner + '/'; }
|
||||
if (parsed.password) { hash += 'p/'; }
|
||||
if (opts.embed) { hash += 'embed/'; }
|
||||
if (opts.present) { hash += 'present/'; }
|
||||
return hash;
|
||||
};
|
||||
return parsed;
|
||||
}
|
||||
return parsed;
|
||||
@@ -222,34 +256,54 @@ Version 1
|
||||
parsed.getHash = function () { return hashArr.join('/'); };
|
||||
if (['media', 'file'].indexOf(type) !== -1) {
|
||||
parsed.type = 'file';
|
||||
|
||||
parsed.getOptions = function () {
|
||||
return {
|
||||
embed: parsed.embed,
|
||||
present: parsed.present,
|
||||
ownerKey: parsed.ownerKey,
|
||||
password: parsed.password
|
||||
};
|
||||
};
|
||||
|
||||
parsed.getHash = function (opts) {
|
||||
var hash = hashArr.slice(0, 4).join('/') + '/';
|
||||
var owner = typeof(opts.ownerKey) !== "undefined" ? opts.ownerKey : parsed.ownerKey;
|
||||
if (owner) { hash += owner + '/'; }
|
||||
if (parsed.password || opts.password) { hash += 'p/'; }
|
||||
if (opts.embed) { hash += 'embed/'; }
|
||||
if (opts.present) { hash += 'present/'; }
|
||||
return hash;
|
||||
};
|
||||
|
||||
if (hashArr[1] && hashArr[1] === '1') {
|
||||
parsed.version = 1;
|
||||
parsed.channel = hashArr[2].replace(/-/g, '/');
|
||||
parsed.key = hashArr[3].replace(/-/g, '/');
|
||||
options = hashArr.slice(4);
|
||||
parsed.ownerKey = getOwnerKey(options);
|
||||
addOptions();
|
||||
return parsed;
|
||||
}
|
||||
|
||||
if (hashArr[1] && hashArr[1] === '2') { // Version 2
|
||||
parsed.version = 2;
|
||||
parsed.app = hashArr[2];
|
||||
parsed.key = hashArr[3];
|
||||
|
||||
options = hashArr.slice(4);
|
||||
parsed.password = options.indexOf('p') !== -1;
|
||||
parsed.present = options.indexOf('present') !== -1;
|
||||
parsed.embed = options.indexOf('embed') !== -1;
|
||||
parsed.ownerKey = getOwnerKey(options);
|
||||
addOptions();
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
if (hashArr[1] && hashArr[1] === '3') { // Version 3: hidden hash
|
||||
parsed.version = 3;
|
||||
parsed.app = hashArr[2];
|
||||
parsed.channel = hashArr[3];
|
||||
|
||||
options = hashArr.slice(4);
|
||||
addOptions();
|
||||
|
||||
parsed.getHash = function (opts) {
|
||||
var hash = hashArr.slice(0, 4).join('/') + '/';
|
||||
var owner = typeof(opts.ownerKey) !== "undefined" ? opts.ownerKey : parsed.ownerKey;
|
||||
if (owner) { hash += owner + '/'; }
|
||||
if (parsed.password) { hash += 'p/'; }
|
||||
if (opts.embed) { hash += 'embed/'; }
|
||||
if (opts.present) { hash += 'present/'; }
|
||||
return hash;
|
||||
};
|
||||
return parsed;
|
||||
}
|
||||
return parsed;
|
||||
@@ -303,6 +357,10 @@ Version 1
|
||||
url += '#' + hash;
|
||||
return url;
|
||||
};
|
||||
ret.getOptions = function () {
|
||||
if (!ret.hashData || !ret.hashData.getOptions) { return {}; }
|
||||
return ret.hashData.getOptions();
|
||||
};
|
||||
|
||||
if (!/^https*:\/\//.test(href)) {
|
||||
idx = href.indexOf('/#');
|
||||
@@ -325,6 +383,14 @@ Version 1
|
||||
return ret;
|
||||
};
|
||||
|
||||
Hash.hashToHref = function (hash, type) {
|
||||
return '/' + type + '/#' + hash;
|
||||
};
|
||||
Hash.hrefToHash = function (href) {
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
return parsed.hash;
|
||||
};
|
||||
|
||||
Hash.getRelativeHref = function (href) {
|
||||
if (!href) { return; }
|
||||
if (href.indexOf('#') === -1) { return; }
|
||||
@@ -345,7 +411,7 @@ Version 1
|
||||
secret.version = 2;
|
||||
secret.type = type;
|
||||
};
|
||||
if (!secretHash && !window.location.hash) { //!/#/.test(window.location.href)) {
|
||||
if (!secretHash) {
|
||||
generate();
|
||||
return secret;
|
||||
} else {
|
||||
@@ -355,12 +421,7 @@ Version 1
|
||||
if (!type) { throw new Error("getSecrets with a hash requires a type parameter"); }
|
||||
parsed = parseTypeHash(type, secretHash);
|
||||
hash = secretHash;
|
||||
} else {
|
||||
var pHref = parsePadUrl(window.location.href);
|
||||
parsed = pHref.hashData;
|
||||
hash = pHref.hash;
|
||||
}
|
||||
//var hash = secretHash || window.location.hash.slice(1);
|
||||
if (hash.length === 0) {
|
||||
generate();
|
||||
return secret;
|
||||
@@ -496,8 +557,8 @@ Version 1
|
||||
if (typeof(parsed.hashData.version) === "undefined") { return; }
|
||||
// pads and files should have a base64 (or hex) key
|
||||
if (parsed.hashData.type === 'pad' || parsed.hashData.type === 'file') {
|
||||
if (!parsed.hashData.key) { return; }
|
||||
if (!/^[a-zA-Z0-9+-/=]+$/.test(parsed.hashData.key)) { return; }
|
||||
if (!parsed.hashData.key && !parsed.hashData.channel) { return; }
|
||||
if (parsed.hashData.key && !/^[a-zA-Z0-9+-/=]+$/.test(parsed.hashData.key)) { return; }
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -70,6 +70,7 @@ define([
|
||||
if (typeof(yes) === 'function') { yes(e); }
|
||||
break;
|
||||
}
|
||||
$(el || window).off('keydown', handler);
|
||||
};
|
||||
|
||||
$(el || window).keydown(handler);
|
||||
@@ -491,6 +492,11 @@ define([
|
||||
$ok.focus();
|
||||
Notifier.notify();
|
||||
});
|
||||
|
||||
return {
|
||||
element: frame,
|
||||
delete: close
|
||||
};
|
||||
};
|
||||
|
||||
UI.prompt = function (msg, def, cb, opt, force) {
|
||||
@@ -582,7 +588,7 @@ define([
|
||||
$ok.click();
|
||||
}, function () {
|
||||
$cancel.click();
|
||||
}, ok);
|
||||
}, frame);
|
||||
|
||||
document.body.appendChild(frame);
|
||||
setTimeout(function () {
|
||||
@@ -1050,39 +1056,36 @@ define([
|
||||
return radio;
|
||||
};
|
||||
|
||||
var corner = {
|
||||
queue: [],
|
||||
state: false
|
||||
};
|
||||
UI.cornerPopup = function (text, actions, footer, opts) {
|
||||
opts = opts || {};
|
||||
|
||||
var minimize = h('div.cp-corner-minimize.fa.fa-window-minimize');
|
||||
var maximize = h('div.cp-corner-maximize.fa.fa-window-maximize');
|
||||
var dontShowAgain = h('div.cp-corner-dontshow', [
|
||||
h('span.fa.fa-times'),
|
||||
Messages.dontShowAgain
|
||||
]);
|
||||
|
||||
var popup = h('div.cp-corner-container', [
|
||||
minimize,
|
||||
maximize,
|
||||
h('div.cp-corner-filler', { style: "width:110px;" }),
|
||||
h('div.cp-corner-filler', { style: "width:80px;" }),
|
||||
h('div.cp-corner-filler', { style: "width:60px;" }),
|
||||
h('div.cp-corner-filler', { style: "width:40px;" }),
|
||||
h('div.cp-corner-filler', { style: "width:20px;" }),
|
||||
setHTML(h('div.cp-corner-text'), text),
|
||||
h('div.cp-corner-actions', actions),
|
||||
setHTML(h('div.cp-corner-footer'), footer)
|
||||
setHTML(h('div.cp-corner-footer'), footer),
|
||||
opts.dontShowAgain ? dontShowAgain : undefined
|
||||
]);
|
||||
|
||||
var $popup = $(popup);
|
||||
|
||||
$(minimize).click(function () {
|
||||
$popup.addClass('cp-minimized');
|
||||
});
|
||||
$(maximize).click(function () {
|
||||
$popup.removeClass('cp-minimized');
|
||||
});
|
||||
|
||||
if (opts.hidden) {
|
||||
$popup.addClass('cp-minimized');
|
||||
}
|
||||
if (opts.big) {
|
||||
$popup.addClass('cp-corner-big');
|
||||
}
|
||||
if (opts.alt) {
|
||||
$popup.addClass('cp-corner-alt');
|
||||
}
|
||||
|
||||
var hide = function () {
|
||||
$popup.hide();
|
||||
@@ -1092,9 +1095,28 @@ define([
|
||||
};
|
||||
var deletePopup = function () {
|
||||
$popup.remove();
|
||||
if (!corner.queue.length) {
|
||||
corner.state = false;
|
||||
return;
|
||||
}
|
||||
setTimeout(function () {
|
||||
$('body').append(corner.queue.pop());
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
$('body').append(popup);
|
||||
$(dontShowAgain).click(function () {
|
||||
deletePopup();
|
||||
if (typeof(opts.dontShowAgain) === "function") {
|
||||
opts.dontShowAgain();
|
||||
}
|
||||
});
|
||||
|
||||
if (corner.state) {
|
||||
corner.queue.push(popup);
|
||||
} else {
|
||||
corner.state = true;
|
||||
$('body').append(popup);
|
||||
}
|
||||
|
||||
return {
|
||||
popup: popup,
|
||||
@@ -1104,5 +1126,36 @@ define([
|
||||
};
|
||||
};
|
||||
|
||||
UI.makeSpinner = function ($container) {
|
||||
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide();
|
||||
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide();
|
||||
|
||||
var spin = function () {
|
||||
$ok.hide();
|
||||
$spinner.show();
|
||||
};
|
||||
var hide = function () {
|
||||
$ok.hide();
|
||||
$spinner.hide();
|
||||
};
|
||||
var done = function () {
|
||||
$ok.show();
|
||||
$spinner.hide();
|
||||
};
|
||||
|
||||
if ($container && $container.append) {
|
||||
$container.append($ok);
|
||||
$container.append($spinner);
|
||||
}
|
||||
|
||||
return {
|
||||
ok: $ok[0],
|
||||
spinner: $spinner[0],
|
||||
spin: spin,
|
||||
hide: hide,
|
||||
done: done
|
||||
};
|
||||
};
|
||||
|
||||
return UI;
|
||||
});
|
||||
|
||||
@@ -53,10 +53,18 @@ define([
|
||||
return list;
|
||||
};
|
||||
|
||||
Msg.declineFriendRequest = function (store, data, cb) {
|
||||
store.mailbox.sendTo('DECLINE_FRIEND_REQUEST', {}, {
|
||||
channel: data.notifications,
|
||||
curvePublic: data.curvePublic
|
||||
}, function (obj) {
|
||||
cb(obj);
|
||||
});
|
||||
};
|
||||
Msg.acceptFriendRequest = function (store, data, cb) {
|
||||
var friend = getFriend(store.proxy, data.curvePublic) || {};
|
||||
var myData = createData(store.proxy, friend.channel || data.channel);
|
||||
store.mailbox.sendTo('ACCEPT_FRIEND_REQUEST', myData, {
|
||||
store.mailbox.sendTo('ACCEPT_FRIEND_REQUEST', { user: myData }, {
|
||||
channel: data.notifications,
|
||||
curvePublic: data.curvePublic
|
||||
}, function (obj) {
|
||||
@@ -110,7 +118,7 @@ define([
|
||||
var proxy = store.proxy;
|
||||
var friend = proxy.friends[curvePublic];
|
||||
if (!friend) { return void cb({error: 'ENOENT'}); }
|
||||
if (!friend.notifications || !friend.channel) { return void cb({error: 'EINVAL'}); }
|
||||
if (!friend.notifications) { return void cb({error: 'EINVAL'}); }
|
||||
|
||||
store.mailbox.sendTo('UNFRIEND', {
|
||||
curvePublic: proxy.curvePublic
|
||||
|
||||
@@ -56,6 +56,21 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
var dcAlert;
|
||||
UIElements.disconnectAlert = function () {
|
||||
if (dcAlert && $(dcAlert.element).length) { return; }
|
||||
dcAlert = UI.alert(Messages.common_connectionLost, undefined, true);
|
||||
};
|
||||
UIElements.reconnectAlert = function () {
|
||||
if (!dcAlert) { return; }
|
||||
if (!dcAlert.delete) {
|
||||
dcAlert = undefined;
|
||||
return;
|
||||
}
|
||||
dcAlert.delete();
|
||||
dcAlert = undefined;
|
||||
};
|
||||
|
||||
var importContent = function (type, f, cfg) {
|
||||
return function () {
|
||||
var $files = $('<input>', {type:"file"});
|
||||
@@ -212,15 +227,7 @@ define([
|
||||
common.mailbox.sendTo("RM_OWNER", {
|
||||
channel: channel,
|
||||
title: data.title,
|
||||
pending: pending,
|
||||
user: {
|
||||
displayName: user.name,
|
||||
avatar: user.avatar,
|
||||
profile: user.profile,
|
||||
notifications: user.notifications,
|
||||
curvePublic: user.curvePublic,
|
||||
edPublic: priv.edPublic
|
||||
}
|
||||
pending: pending
|
||||
}, {
|
||||
channel: friend.notifications,
|
||||
curvePublic: friend.curvePublic
|
||||
@@ -363,15 +370,7 @@ define([
|
||||
channel: channel,
|
||||
href: data.href,
|
||||
password: data.password,
|
||||
title: data.title,
|
||||
user: {
|
||||
displayName: user.name,
|
||||
avatar: user.avatar,
|
||||
profile: user.profile,
|
||||
notifications: user.notifications,
|
||||
curvePublic: user.curvePublic,
|
||||
edPublic: priv.edPublic
|
||||
}
|
||||
title: data.title
|
||||
}, {
|
||||
channel: friend.notifications,
|
||||
curvePublic: friend.curvePublic
|
||||
@@ -548,21 +547,27 @@ define([
|
||||
|
||||
if (!data.noPassword) {
|
||||
var hasPassword = data.password;
|
||||
var $pwLabel = $('<label>', {'for': 'cp-app-prop-password'}).text(Messages.creation_passwordValue)
|
||||
.hide().appendTo($d);
|
||||
var password = UI.passwordInput({
|
||||
id: 'cp-app-prop-password',
|
||||
readonly: 'readonly'
|
||||
});
|
||||
var $password = $(password).hide();
|
||||
var $pwInput = $password.find('.cp-password-input');
|
||||
$pwInput.val(data.password).click(function () {
|
||||
$pwInput[0].select();
|
||||
});
|
||||
$d.append(password);
|
||||
|
||||
if (hasPassword) {
|
||||
$('<label>', {'for': 'cp-app-prop-password'}).text(Messages.creation_passwordValue)
|
||||
.appendTo($d);
|
||||
var password = UI.passwordInput({
|
||||
id: 'cp-app-prop-password',
|
||||
readonly: 'readonly'
|
||||
});
|
||||
var $pwInput = $(password).find('.cp-password-input');
|
||||
$pwInput.val(data.password).click(function () {
|
||||
$pwInput[0].select();
|
||||
});
|
||||
$d.append(password);
|
||||
$pwLabel.show();
|
||||
$password.css('display', 'flex');
|
||||
}
|
||||
|
||||
if (!data.noEditPassword && owned) { // FIXME SHEET fix password change for sheets
|
||||
// In the properties, we should have the edit href if we know it.
|
||||
// We should know it because the pad is stored, but it's better to check...
|
||||
if (!data.noEditPassword && owned && data.href) { // FIXME SHEET fix password change for sheets
|
||||
var sframeChan = common.getSframeChannel();
|
||||
|
||||
var isOO = parsed.type === 'sheet';
|
||||
@@ -622,7 +627,7 @@ define([
|
||||
|
||||
sframeChan.query(q, {
|
||||
teamId: typeof(owned) !== "boolean" ? owned : undefined,
|
||||
href: data.href || data.roHref,
|
||||
href: data.href,
|
||||
password: newPass
|
||||
}, function (err, data) {
|
||||
$(passwordOk).text(Messages.properties_changePasswordButton);
|
||||
@@ -632,24 +637,41 @@ define([
|
||||
return void UI.alert(Messages.properties_passwordError);
|
||||
}
|
||||
UI.findOKButton().click();
|
||||
if (isFile) {
|
||||
onProgress.stop();
|
||||
|
||||
$pwLabel.show();
|
||||
$password.css('display', 'flex');
|
||||
$pwInput.val(newPass);
|
||||
|
||||
// If the current document is a file or if we're changing the password from a drive,
|
||||
// we don't have to reload the page at the end.
|
||||
// Tell the user the password change was successful and abort
|
||||
if (isFile || priv.app !== parsed.type) {
|
||||
if (onProgress && onProgress.stop) { onProgress.stop(); }
|
||||
$(passwordOk).text(Messages.properties_changePasswordButton);
|
||||
var alertMsg = data.warning ? Messages.properties_passwordWarningFile
|
||||
: Messages.properties_passwordSuccessFile;
|
||||
return void UI.alert(alertMsg, undefined, {force: true});
|
||||
}
|
||||
// If we didn't have a password, we have to add the /p/
|
||||
// If we had a password and we changed it to a new one, we just have to reload
|
||||
// If we had a password and we removed it, we have to remove the /p/
|
||||
|
||||
// Pad password changed: update the href
|
||||
// Use hidden hash if needed (we're an owner of this pad so we know it is stored)
|
||||
var useUnsafe = Util.find(priv, ['settings', 'security', 'unsafeLinks']);
|
||||
var href = (priv.readOnly && data.roHref) ? data.roHref : data.href;
|
||||
if (useUnsafe === false) {
|
||||
var newParsed = Hash.parsePadUrl(href);
|
||||
var newSecret = Hash.getSecrets(newParsed.type, newParsed.hash, newPass);
|
||||
var newHash = Hash.getHiddenHashFromKeys(parsed.type, newSecret, {});
|
||||
href = Hash.hashToHref(newHash, parsed.type);
|
||||
}
|
||||
|
||||
if (data.warning) {
|
||||
return void UI.alert(Messages.properties_passwordWarning, function () {
|
||||
common.gotoURL(hasPassword && newPass ? undefined : (data.href || data.roHref));
|
||||
common.gotoURL(href);
|
||||
}, {force: true});
|
||||
}
|
||||
return void UI.alert(Messages.properties_passwordSuccess, function () {
|
||||
if (!isSharedFolder) {
|
||||
common.gotoURL(hasPassword && newPass ? undefined : (data.href || data.roHref));
|
||||
common.gotoURL(href);
|
||||
}
|
||||
}, {force: true});
|
||||
});
|
||||
@@ -3737,7 +3759,7 @@ define([
|
||||
]);
|
||||
|
||||
var settings = h('div.cp-creation-remember', [
|
||||
UI.createCheckbox('cp-creation-remember', Messages.creation_saveSettings, false),
|
||||
UI.createCheckbox('cp-creation-remember', Messages.dontShowAgain, false),
|
||||
createHelper('/settings/#creation', Messages.creation_settings),
|
||||
h('div.cp-creation-remember-help.cp-creation-slider', [
|
||||
h('span.fa.fa-exclamation-circle.cp-creation-warning'),
|
||||
@@ -4103,52 +4125,68 @@ define([
|
||||
};
|
||||
|
||||
var crowdfundingState = false;
|
||||
UIElements.displayCrowdfunding = function (common) {
|
||||
UIElements.displayCrowdfunding = function (common, force) {
|
||||
if (crowdfundingState) { return; }
|
||||
if (AppConfig.disableCrowdfundingMessages) { return; }
|
||||
var priv = common.getMetadataMgr().getPrivateData();
|
||||
|
||||
|
||||
var todo = function () {
|
||||
crowdfundingState = true;
|
||||
// Display the popup
|
||||
var text = Messages.crowdfunding_popup_text;
|
||||
var yes = h('button.cp-corner-primary', [
|
||||
h('span.fa.fa-external-link'),
|
||||
'OpenCollective'
|
||||
]);
|
||||
var no = h('button.cp-corner-cancel', Messages.crowdfunding_popup_no);
|
||||
var actions = h('div', [no, yes]);
|
||||
|
||||
var dontShowAgain = function () {
|
||||
common.setAttribute(['general', 'crowdfunding'], false);
|
||||
Feedback.send('CROWDFUNDING_NEVER');
|
||||
};
|
||||
|
||||
var modal = UI.cornerPopup(text, actions, null, {
|
||||
big: true,
|
||||
alt: true,
|
||||
dontShowAgain: dontShowAgain
|
||||
});
|
||||
|
||||
$(yes).click(function () {
|
||||
modal.delete();
|
||||
common.openURL(priv.accounts.donateURL);
|
||||
Feedback.send('CROWDFUNDING_YES');
|
||||
});
|
||||
$(modal.popup).find('a').click(function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
modal.delete();
|
||||
common.openURL(priv.accounts.donateURL);
|
||||
Feedback.send('CROWDFUNDING_LINK');
|
||||
});
|
||||
$(no).click(function () {
|
||||
modal.delete();
|
||||
Feedback.send('CROWDFUNDING_NO');
|
||||
});
|
||||
};
|
||||
|
||||
if (force) {
|
||||
crowdfundingState = true;
|
||||
return void todo();
|
||||
}
|
||||
|
||||
if (AppConfig.disableCrowdfundingMessages) { return; }
|
||||
if (priv.plan) { return; }
|
||||
|
||||
crowdfundingState = true;
|
||||
setTimeout(function () {
|
||||
common.getAttribute(['general', 'crowdfunding'], function (err, val) {
|
||||
if (err || val === false) { return; }
|
||||
common.getSframeChannel().query('Q_GET_PINNED_USAGE', null, function (err, obj) {
|
||||
var quotaMb = obj.quota / (1024 * 1024);
|
||||
if (quotaMb < 10) { return; }
|
||||
// Display the popup
|
||||
var text = Messages.crowdfunding_popup_text;
|
||||
var yes = h('button.cp-corner-primary', Messages.crowdfunding_popup_yes);
|
||||
var no = h('button.cp-corner-primary', Messages.crowdfunding_popup_no);
|
||||
var never = h('button.cp-corner-cancel', Messages.crowdfunding_popup_never);
|
||||
var actions = h('div', [yes, no, never]);
|
||||
|
||||
var modal = UI.cornerPopup(text, actions, null, {big: true});
|
||||
|
||||
$(yes).click(function () {
|
||||
modal.delete();
|
||||
common.openURL(priv.accounts.donateURL);
|
||||
Feedback.send('CROWDFUNDING_YES');
|
||||
});
|
||||
$(modal.popup).find('a').click(function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
modal.delete();
|
||||
common.openURL(priv.accounts.donateURL);
|
||||
Feedback.send('CROWDFUNDING_LINK');
|
||||
});
|
||||
$(no).click(function () {
|
||||
modal.delete();
|
||||
Feedback.send('CROWDFUNDING_NO');
|
||||
});
|
||||
$(never).click(function () {
|
||||
modal.delete();
|
||||
common.setAttribute(['general', 'crowdfunding'], false);
|
||||
Feedback.send('CROWDFUNDING_NEVER');
|
||||
});
|
||||
});
|
||||
common.getAttribute(['general', 'crowdfunding'], function (err, val) {
|
||||
if (err || val === false) { return; }
|
||||
common.getSframeChannel().query('Q_GET_PINNED_USAGE', null, function (err, obj) {
|
||||
var quotaMb = obj.quota / (1024 * 1024);
|
||||
if (quotaMb < 10) { return; }
|
||||
todo();
|
||||
});
|
||||
}, 5000);
|
||||
});
|
||||
};
|
||||
|
||||
var storePopupState = false;
|
||||
@@ -4170,7 +4208,7 @@ define([
|
||||
|
||||
var hide = h('button.cp-corner-cancel', Messages.autostore_hide);
|
||||
var store = h('button.cp-corner-primary', Messages.autostore_store);
|
||||
var actions = h('div', [store, hide]);
|
||||
var actions = h('div', [hide, store]);
|
||||
|
||||
var initialHide = data && data.autoStore && data.autoStore === -1;
|
||||
var modal = UI.cornerPopup(text, actions, footer, {hidden: initialHide});
|
||||
@@ -4325,7 +4363,8 @@ define([
|
||||
|
||||
UIElements.displayFriendRequestModal = function (common, data) {
|
||||
var msg = data.content.msg;
|
||||
var text = Messages._getKey('contacts_request', [Util.fixHTML(msg.content.displayName)]);
|
||||
var userData = msg.content.user;
|
||||
var text = Messages._getKey('contacts_request', [Util.fixHTML(userData.displayName)]);
|
||||
|
||||
var todo = function (yes) {
|
||||
common.getSframeChannel().query("Q_ANSWER_FRIEND_REQUEST", {
|
||||
@@ -4352,7 +4391,6 @@ define([
|
||||
|
||||
UIElements.displayAddOwnerModal = function (common, data) {
|
||||
var priv = common.getMetadataMgr().getPrivateData();
|
||||
var user = common.getMetadataMgr().getUserData();
|
||||
var sframeChan = common.getSframeChannel();
|
||||
var msg = data.content.msg;
|
||||
|
||||
@@ -4387,15 +4425,7 @@ define([
|
||||
href: msg.content.href,
|
||||
password: msg.content.password,
|
||||
title: msg.content.title,
|
||||
answer: yes,
|
||||
user: {
|
||||
displayName: user.name,
|
||||
avatar: user.avatar,
|
||||
profile: user.profile,
|
||||
notifications: user.notifications,
|
||||
curvePublic: user.curvePublic,
|
||||
edPublic: priv.edPublic
|
||||
}
|
||||
answer: yes
|
||||
}, {
|
||||
channel: msg.content.user.notifications,
|
||||
curvePublic: msg.content.user.curvePublic
|
||||
@@ -4476,7 +4506,6 @@ define([
|
||||
};
|
||||
UIElements.displayAddTeamOwnerModal = function (common, data) {
|
||||
var priv = common.getMetadataMgr().getPrivateData();
|
||||
var user = common.getMetadataMgr().getUserData();
|
||||
var sframeChan = common.getSframeChannel();
|
||||
var msg = data.content.msg;
|
||||
|
||||
@@ -4493,15 +4522,7 @@ define([
|
||||
common.mailbox.sendTo("ADD_OWNER_ANSWER", {
|
||||
teamChannel: msg.content.teamChannel,
|
||||
title: msg.content.title,
|
||||
answer: yes,
|
||||
user: {
|
||||
displayName: user.name,
|
||||
avatar: user.avatar,
|
||||
profile: user.profile,
|
||||
notifications: user.notifications,
|
||||
curvePublic: user.curvePublic,
|
||||
edPublic: priv.edPublic
|
||||
}
|
||||
answer: yes
|
||||
}, {
|
||||
channel: msg.content.user.notifications,
|
||||
curvePublic: msg.content.user.curvePublic
|
||||
@@ -4608,17 +4629,15 @@ define([
|
||||
var f = priv.friends[curve];
|
||||
$verified.append(h('span.fa.fa-certificate'));
|
||||
var $avatar = $(h('span.cp-avatar')).appendTo($verified);
|
||||
$verified.append(h('p', Messages._getKey('requestEdit_fromFriend', [f.displayName])));
|
||||
$verified.append(h('p', Messages._getKey('isContact', [f.displayName])));
|
||||
common.displayAvatar($avatar, f.avatar, f.displayName);
|
||||
} else {
|
||||
$verified.append(Messages._getKey('requestEdit_fromStranger', [name]));
|
||||
$verified.append(Messages._getKey('isNotContact', [name]));
|
||||
}
|
||||
return verified;
|
||||
};
|
||||
|
||||
UIElements.displayInviteTeamModal = function (common, data) {
|
||||
var priv = common.getMetadataMgr().getPrivateData();
|
||||
var user = common.getMetadataMgr().getUserData();
|
||||
var msg = data.content.msg;
|
||||
|
||||
var name = Util.fixHTML(msg.content.user.displayName) || Messages.anonymous;
|
||||
@@ -4639,15 +4658,7 @@ define([
|
||||
common.mailbox.sendTo("INVITE_TO_TEAM_ANSWER", {
|
||||
answer: yes,
|
||||
teamChannel: msg.content.team.channel,
|
||||
teamName: teamName,
|
||||
user: {
|
||||
displayName: user.name,
|
||||
avatar: user.avatar,
|
||||
profile: user.profile,
|
||||
notifications: user.notifications,
|
||||
curvePublic: user.curvePublic,
|
||||
edPublic: priv.edPublic
|
||||
}
|
||||
teamName: teamName
|
||||
}, {
|
||||
channel: msg.content.user.notifications,
|
||||
curvePublic: msg.content.user.curvePublic
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
};
|
||||
|
||||
Util.mkAsync = function (f) {
|
||||
if (typeof(f) !== 'function') {
|
||||
throw new Error('EXPECTED_FUNCTION');
|
||||
}
|
||||
return function () {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
setTimeout(function () {
|
||||
|
||||
@@ -49,6 +49,12 @@ define([
|
||||
account: {},
|
||||
};
|
||||
|
||||
// Store the href in memory
|
||||
// This is a placeholder value overriden in common.ready from sframe-common-outer
|
||||
var currentPad = common.currentPad = {
|
||||
href: window.location.href
|
||||
};
|
||||
|
||||
// COMMON
|
||||
common.getLanguage = function () {
|
||||
return Messages._languageUsed;
|
||||
@@ -374,7 +380,7 @@ define([
|
||||
|
||||
|
||||
common.getMetadata = function (cb) {
|
||||
var parsed = Hash.parsePadUrl(window.location.href);
|
||||
var parsed = Hash.parsePadUrl(currentPad.href);
|
||||
postMessage("GET_METADATA", parsed && parsed.type, function (obj) {
|
||||
if (obj && obj.error) { return void cb(obj.error); }
|
||||
cb(null, obj);
|
||||
@@ -394,7 +400,7 @@ define([
|
||||
|
||||
common.setPadAttribute = function (attr, value, cb, href) {
|
||||
cb = cb || function () {};
|
||||
href = Hash.getRelativeHref(href || window.location.href);
|
||||
href = Hash.getRelativeHref(href || currentPad.href);
|
||||
postMessage("SET_PAD_ATTRIBUTE", {
|
||||
href: href,
|
||||
attr: attr,
|
||||
@@ -405,7 +411,7 @@ define([
|
||||
});
|
||||
};
|
||||
common.getPadAttribute = function (attr, cb, href) {
|
||||
href = Hash.getRelativeHref(href || window.location.href);
|
||||
href = Hash.getRelativeHref(href || currentPad.href);
|
||||
if (!href) {
|
||||
return void cb('E404');
|
||||
}
|
||||
@@ -505,7 +511,7 @@ define([
|
||||
};
|
||||
|
||||
common.saveAsTemplate = function (Cryptput, data, cb) {
|
||||
var p = Hash.parsePadUrl(window.location.href);
|
||||
var p = Hash.parsePadUrl(currentPad.href);
|
||||
if (!p.type) { return; }
|
||||
// PPP: password for the new template?
|
||||
var hash = Hash.createRandomHash(p.type);
|
||||
@@ -543,7 +549,7 @@ define([
|
||||
var href = data.href;
|
||||
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
var parsed2 = Hash.parsePadUrl(window.location.href);
|
||||
var parsed2 = Hash.parsePadUrl(currentPad.href);
|
||||
if(!parsed) { throw new Error("Cannot get template hash"); }
|
||||
postMessage("INCREMENT_TEMPLATE_USE", href);
|
||||
|
||||
@@ -601,7 +607,7 @@ define([
|
||||
var fileHost = Config.fileHost || window.location.origin;
|
||||
var data = common.fromFileData;
|
||||
var parsed = Hash.parsePadUrl(data.href);
|
||||
var parsed2 = Hash.parsePadUrl(window.location.href);
|
||||
var parsed2 = Hash.parsePadUrl(currentPad.href);
|
||||
var hash = parsed.hash;
|
||||
var name = data.title;
|
||||
var secret = Hash.getSecrets('file', hash, data.password);
|
||||
@@ -660,7 +666,7 @@ define([
|
||||
|
||||
// Forget button
|
||||
common.moveToTrash = function (cb, href) {
|
||||
href = href || window.location.href;
|
||||
href = href || currentPad.href;
|
||||
postMessage("MOVE_TO_TRASH", { href: href }, cb);
|
||||
};
|
||||
|
||||
@@ -668,7 +674,7 @@ define([
|
||||
common.setPadTitle = function (data, cb) {
|
||||
if (!data || typeof (data) !== "object") { return cb ('Data is not an object'); }
|
||||
|
||||
var href = data.href || window.location.href;
|
||||
var href = data.href || currentPad.href;
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
if (!parsed.hash) { return cb ('Invalid hash'); }
|
||||
data.href = parsed.getUrl({present: parsed.present});
|
||||
@@ -698,7 +704,7 @@ define([
|
||||
if (obj.error !== "EAUTH") { console.log("unable to set pad title"); }
|
||||
return void cb(obj.error);
|
||||
}
|
||||
cb();
|
||||
cb(null, obj);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -755,6 +761,13 @@ define([
|
||||
cb(void 0, data);
|
||||
});
|
||||
};
|
||||
// Get data about a given channel: use with hidden hashes
|
||||
common.getPadDataFromChannel = function (obj, cb) {
|
||||
if (!obj || !obj.channel) { return void cb('EINVAL'); }
|
||||
postMessage("GET_PAD_DATA_FROM_CHANNEL", obj, function (data) {
|
||||
cb(void 0, data);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Admin
|
||||
@@ -832,6 +845,7 @@ define([
|
||||
pad.onConnectEvent = Util.mkEvent();
|
||||
pad.onErrorEvent = Util.mkEvent();
|
||||
pad.onMetadataEvent = Util.mkEvent();
|
||||
pad.onChannelDeleted = Util.mkEvent();
|
||||
|
||||
pad.requestAccess = function (data, cb) {
|
||||
postMessage("REQUEST_PAD_ACCESS", data, cb);
|
||||
@@ -1023,11 +1037,12 @@ define([
|
||||
}, waitFor());
|
||||
}
|
||||
}).nThen(function () {
|
||||
common.drive.onChange.fire({path: ['drive', Constants.storageKey]});
|
||||
cb({
|
||||
warning: warning,
|
||||
hash: newHash,
|
||||
href: newHref,
|
||||
roHref: newRoHref
|
||||
roHref: newRoHref,
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1156,6 +1171,7 @@ define([
|
||||
channel: newSecret.channel
|
||||
}, waitFor());
|
||||
}).nThen(function () {
|
||||
common.drive.onChange.fire({path: ['drive', Constants.storageKey]});
|
||||
cb({
|
||||
warning: warning,
|
||||
hash: newHash,
|
||||
@@ -1390,6 +1406,7 @@ define([
|
||||
}, waitFor());
|
||||
}));
|
||||
}).nThen(function () {
|
||||
common.drive.onChange.fire({path: ['drive', Constants.storageKey]});
|
||||
cb({
|
||||
warning: warning,
|
||||
hash: newHash,
|
||||
@@ -1608,7 +1625,7 @@ define([
|
||||
hashes = Hash.getHashes(secret);
|
||||
return void cb(null, hashes);
|
||||
}
|
||||
var parsed = Hash.parsePadUrl(window.location.href);
|
||||
var parsed = Hash.parsePadUrl(currentPad.href);
|
||||
if (!parsed.type || !parsed.hashData) { return void cb('E_INVALID_HREF'); }
|
||||
hashes = Hash.getHashes(secret);
|
||||
|
||||
@@ -1679,7 +1696,7 @@ define([
|
||||
LocalStore.logout();
|
||||
|
||||
// redirect them to log in, and come back when they're done.
|
||||
sessionStorage.redirectTo = window.location.href;
|
||||
sessionStorage.redirectTo = currentPad.href;
|
||||
window.location.href = '/login/';
|
||||
};
|
||||
|
||||
@@ -1740,6 +1757,7 @@ define([
|
||||
PAD_CONNECT: common.padRpc.onConnectEvent.fire,
|
||||
PAD_ERROR: common.padRpc.onErrorEvent.fire,
|
||||
PAD_METADATA: common.padRpc.onMetadataEvent.fire,
|
||||
CHANNEL_DELETED: common.padRpc.onChannelDeleted.fire,
|
||||
// Drive
|
||||
DRIVE_LOG: common.drive.onLog.fire,
|
||||
DRIVE_CHANGE: common.drive.onChange.fire,
|
||||
@@ -1780,6 +1798,11 @@ define([
|
||||
|
||||
return function (f, rdyCfg) {
|
||||
rdyCfg = rdyCfg || {};
|
||||
|
||||
if (rdyCfg.currentPad) {
|
||||
currentPad = common.currentPad = rdyCfg.currentPad;
|
||||
}
|
||||
|
||||
if (initialized) {
|
||||
return void setTimeout(function () { f(void 0, env); });
|
||||
}
|
||||
@@ -1878,7 +1901,7 @@ define([
|
||||
anonHash: LocalStore.getFSHash(),
|
||||
localToken: tryParsing(localStorage.getItem(Constants.tokenKey)), // TODO move this to LocalStore ?
|
||||
language: common.getLanguage(),
|
||||
driveEvents: rdyCfg.driveEvents // Boolean
|
||||
driveEvents: true //rdyCfg.driveEvents // Boolean
|
||||
};
|
||||
// if a pad is created from a file
|
||||
if (sessionStorage[Constants.newPadFileData]) {
|
||||
@@ -2101,7 +2124,10 @@ define([
|
||||
var parsedNew = Hash.parsePadUrl(newHref);
|
||||
if (parsedOld.hashData && parsedNew.hashData &&
|
||||
parsedOld.getUrl() !== parsedNew.getUrl()) {
|
||||
if (!parsedOld.hashData.key) { oldHref = newHref; return; }
|
||||
if (parsedOld.hashData.version !== 3 && !parsedOld.hashData.key) {
|
||||
oldHref = newHref;
|
||||
return;
|
||||
}
|
||||
// If different, reload
|
||||
document.location.reload();
|
||||
return;
|
||||
|
||||
@@ -55,7 +55,6 @@ define([
|
||||
var a = h('a.cp-md-toc-link', {
|
||||
href: '#',
|
||||
'data-href': obj.id,
|
||||
title: obj.title
|
||||
});
|
||||
a.innerHTML = obj.title;
|
||||
content.push(h('p.cp-md-toc-'+level, ['• ', a]));
|
||||
|
||||
@@ -587,7 +587,7 @@ define([
|
||||
var displayedCategories = [ROOT, TRASH, SEARCH, RECENT];
|
||||
|
||||
// PCS enabled: display owned pads
|
||||
if (AppConfig.displayCreationScreen) { displayedCategories.push(OWNED); }
|
||||
//if (AppConfig.displayCreationScreen) { displayedCategories.push(OWNED); }
|
||||
// Templates enabled: display template category
|
||||
if (AppConfig.enableTemplates) { displayedCategories.push(TEMPLATE); }
|
||||
// Tags used: display Tags category
|
||||
@@ -1029,15 +1029,26 @@ define([
|
||||
return ret;
|
||||
};
|
||||
|
||||
var openFile = function (el, href) {
|
||||
if (!href) {
|
||||
var data = manager.getFileData(el);
|
||||
if (!data || (!data.href && !data.roHref)) {
|
||||
return void logError("Missing data for the file", el, data);
|
||||
}
|
||||
href = data.href || data.roHref;
|
||||
var openFile = function (el, isRo) {
|
||||
var data = manager.getFileData(el);
|
||||
if (!data || (!data.href && !data.roHref)) {
|
||||
return void logError("Missing data for the file", el, data);
|
||||
}
|
||||
window.open(APP.origin + href);
|
||||
var href = isRo ? data.roHref : (data.href || data.roHref);
|
||||
var priv = metadataMgr.getPrivateData();
|
||||
var useUnsafe = Util.find(priv, ['settings', 'security', 'unsafeLinks']);
|
||||
if (useUnsafe !== false) { // true of undefined: use unsafe links
|
||||
return void window.open(APP.origin + href);
|
||||
}
|
||||
|
||||
// Get hidden hash
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
var secret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
|
||||
var opts = {};
|
||||
if (isRo) { opts.view = true; }
|
||||
var hash = Hash.getHiddenHashFromKeys(parsed.type, secret, opts);
|
||||
var hiddenHref = Hash.hashToHref(hash, parsed.type);
|
||||
window.open(APP.origin + hiddenHref);
|
||||
};
|
||||
|
||||
var refresh = APP.refresh = function () {
|
||||
@@ -1164,13 +1175,9 @@ define([
|
||||
} else if ($element.is('.cp-app-drive-element-noreadonly')) {
|
||||
hide.push('openro'); // Remove open 'view' mode
|
||||
}
|
||||
// if it's not a plain text file
|
||||
// XXX: there is a bug with this code in anon shared folder, so we disable it
|
||||
if (APP.loggedIn || !APP.newSharedFolder) {
|
||||
var metadata = manager.getFileData(manager.find(path));
|
||||
if (!metadata || !Util.isPlainTextFile(metadata.fileType, metadata.title)) {
|
||||
hide.push('openincode');
|
||||
}
|
||||
var metadata = manager.getFileData(manager.find(path));
|
||||
if (!metadata || !Util.isPlainTextFile(metadata.fileType, metadata.title)) {
|
||||
hide.push('openincode');
|
||||
}
|
||||
} else if ($element.is('.cp-app-drive-element-sharedf')) {
|
||||
if (containsFolder) {
|
||||
@@ -1193,8 +1200,7 @@ define([
|
||||
hide.push('collapseall');
|
||||
}
|
||||
containsFolder = true;
|
||||
hide.push('share'); // XXX CONVERT
|
||||
hide.push('savelocal'); // XXX CONVERT
|
||||
hide.push('savelocal');
|
||||
hide.push('openro');
|
||||
hide.push('openincode');
|
||||
hide.push('properties');
|
||||
@@ -1930,6 +1936,44 @@ define([
|
||||
};
|
||||
var getIcon = UI.getIcon;
|
||||
|
||||
var createShareButton = function (id, $container) {
|
||||
var $shareBlock = $('<button>', {
|
||||
'class': 'cp-toolbar-share-button',
|
||||
title: Messages.shareButton
|
||||
});
|
||||
$sharedIcon.clone().appendTo($shareBlock);
|
||||
$('<span>').text(Messages.shareButton).appendTo($shareBlock);
|
||||
var data = manager.getSharedFolderData(id);
|
||||
var parsed = (data.href && data.href.indexOf('#') !== -1) ? Hash.parsePadUrl(data.href) : {};
|
||||
var roParsed = Hash.parsePadUrl(data.roHref) || {};
|
||||
if (!parsed.hash && !roParsed.hash) { return void console.error("Invalid href: "+(data.href || data.roHref)); }
|
||||
var friends = common.getFriends();
|
||||
var ro = folders[id] && folders[id].version >= 2;
|
||||
var modal = UIElements.createShareModal({
|
||||
teamId: APP.team,
|
||||
origin: APP.origin,
|
||||
pathname: "/drive/",
|
||||
friends: friends,
|
||||
title: data.title,
|
||||
password: data.password,
|
||||
sharedFolder: true,
|
||||
common: common,
|
||||
hashes: {
|
||||
editHash: parsed.hash,
|
||||
viewHash: ro && roParsed.hash,
|
||||
}
|
||||
});
|
||||
// If we're a viewer and this is an old shared folder (no read-only mode), we
|
||||
// can't share the read-only URL and we don't have access to the edit one.
|
||||
// We should hide the share button.
|
||||
if (!modal) { return; }
|
||||
$shareBlock.click(function () {
|
||||
UI.openCustomModal(modal);
|
||||
});
|
||||
$container.append($shareBlock);
|
||||
return $shareBlock;
|
||||
};
|
||||
|
||||
// Create the "li" element corresponding to the file/folder located in "path"
|
||||
var createElement = function (path, elPath, root, isFolder) {
|
||||
// Forbid drag&drop inside the trash
|
||||
@@ -2010,6 +2054,15 @@ define([
|
||||
});
|
||||
delete APP.newFolder;
|
||||
}
|
||||
|
||||
if (isSharedFolder && APP.convertedFolder === element) {
|
||||
setTimeout(function () {
|
||||
var $fakeButton = createShareButton(element, $('<div>'));
|
||||
if (!$fakeButton) { return; }
|
||||
$fakeButton.click();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
return $element;
|
||||
};
|
||||
|
||||
@@ -2547,43 +2600,6 @@ define([
|
||||
$container.append($block);
|
||||
};
|
||||
|
||||
var createShareButton = function (id, $container) {
|
||||
var $shareBlock = $('<button>', {
|
||||
'class': 'cp-toolbar-share-button',
|
||||
title: Messages.shareButton
|
||||
});
|
||||
$sharedIcon.clone().appendTo($shareBlock);
|
||||
$('<span>').text(Messages.shareButton).appendTo($shareBlock);
|
||||
var data = manager.getSharedFolderData(id);
|
||||
var parsed = (data.href && data.href.indexOf('#') !== -1) ? Hash.parsePadUrl(data.href) : {};
|
||||
var roParsed = Hash.parsePadUrl(data.roHref) || {};
|
||||
if (!parsed.hash && !roParsed.hash) { return void console.error("Invalid href: "+(data.href || data.roHref)); }
|
||||
var friends = common.getFriends();
|
||||
var ro = folders[id] && folders[id].version >= 2;
|
||||
var modal = UIElements.createShareModal({
|
||||
teamId: APP.team,
|
||||
origin: APP.origin,
|
||||
pathname: "/drive/",
|
||||
friends: friends,
|
||||
title: data.title,
|
||||
password: data.password,
|
||||
sharedFolder: true,
|
||||
common: common,
|
||||
hashes: {
|
||||
editHash: parsed.hash,
|
||||
viewHash: ro && roParsed.hash,
|
||||
}
|
||||
});
|
||||
// If we're a viewer and this is an old shared folder (no read-only mode), we
|
||||
// can't share the read-only URL and we don't have access to the edit one.
|
||||
// We should hide the share button.
|
||||
if (!modal) { return; }
|
||||
$shareBlock.click(function () {
|
||||
UI.openCustomModal(modal);
|
||||
});
|
||||
$container.append($shareBlock);
|
||||
};
|
||||
|
||||
var SORT_FOLDER_DESC = 'sortFoldersDesc';
|
||||
var SORT_FILE_BY = 'sortFilesBy';
|
||||
var SORT_FILE_DESC = 'sortFilesDesc';
|
||||
@@ -3034,7 +3050,7 @@ define([
|
||||
$icon.append(getFileIcon(r.id));
|
||||
$type.text(Messages.type[parsed.type] || parsed.type);
|
||||
$title.click(function () {
|
||||
openFile(null, r.data.href);
|
||||
openFile(r.id);
|
||||
});
|
||||
$atimeName.text(Messages.fm_lastAccess);
|
||||
$atime.text(new Date(r.data.atime).toLocaleString());
|
||||
@@ -3228,21 +3244,23 @@ define([
|
||||
var path = currentPath.slice(1);
|
||||
var root = Util.find(data, path);
|
||||
|
||||
var realPath = [ROOT, SHARED_FOLDER].concat(path);
|
||||
|
||||
if (manager.hasSubfolder(root)) { $list.append($folderHeader); }
|
||||
// display sub directories
|
||||
var keys = Object.keys(root);
|
||||
var sortedFolders = sortElements(true, currentPath, keys, null, !getSortFolderDesc());
|
||||
var sortedFiles = sortElements(false, currentPath, keys, APP.store[SORT_FILE_BY], !getSortFileDesc());
|
||||
var sortedFolders = sortElements(true, realPath, keys, null, !getSortFolderDesc());
|
||||
var sortedFiles = sortElements(false, realPath, keys, APP.store[SORT_FILE_BY], !getSortFileDesc());
|
||||
sortedFolders.forEach(function (key) {
|
||||
if (manager.isFile(root[key])) { return; }
|
||||
var $element = createElement(currentPath, key, root, true);
|
||||
var $element = createElement(realPath, key, root, true);
|
||||
$element.appendTo($list);
|
||||
});
|
||||
if (manager.hasFile(root)) { $list.append($fileHeader); }
|
||||
// display files
|
||||
sortedFiles.forEach(function (key) {
|
||||
if (manager.isFolder(root[key])) { return; }
|
||||
var $element = createElement(currentPath, key, root, false);
|
||||
var $element = createElement(realPath, key, root, false);
|
||||
if (!$element) { return; }
|
||||
$element.appendTo($list);
|
||||
});
|
||||
@@ -3327,7 +3345,9 @@ define([
|
||||
// in history mode we want to focus the version number input
|
||||
if (!history.isHistoryMode && !APP.mobile()) {
|
||||
var st = $tree.scrollTop() || 0;
|
||||
$tree.find('#cp-app-drive-tree-search-input').focus();
|
||||
if (!$('.alertify').length) {
|
||||
$tree.find('#cp-app-drive-tree-search-input').focus();
|
||||
}
|
||||
$tree.scrollTop(st);
|
||||
}
|
||||
$tree.find('#cp-app-drive-tree-search-input')[0].selectionStart = getSearchCursor();
|
||||
@@ -3371,12 +3391,7 @@ define([
|
||||
createNewButton(isInRoot, $toolbar.find('.cp-app-drive-toolbar-leftside'));
|
||||
}
|
||||
if (sfId) {
|
||||
var sfData = manager.getSharedFolderData(sfId);
|
||||
var parsed = Hash.parsePadUrl(sfData.href);
|
||||
sframeChan.event('EV_DRIVE_SET_HASH', parsed.hash || '');
|
||||
createShareButton(sfId, $toolbar.find('.cp-app-drive-toolbar-leftside'));
|
||||
} else {
|
||||
sframeChan.event('EV_DRIVE_SET_HASH', '');
|
||||
}
|
||||
|
||||
|
||||
@@ -3482,6 +3497,9 @@ define([
|
||||
} else {
|
||||
$content.scrollTop(s);
|
||||
}
|
||||
|
||||
delete APP.convertedFolder;
|
||||
|
||||
appStatus.ready(true);
|
||||
};
|
||||
var displayDirectory = APP.displayDirectory = function (path, force) {
|
||||
@@ -3944,15 +3962,12 @@ define([
|
||||
// ANON_SHARED_FOLDER
|
||||
el = manager.find(paths[0].path.slice(1), APP.newSharedFolder);
|
||||
}
|
||||
var href;
|
||||
if (manager.isPathIn(p.path, [FILES_DATA])) {
|
||||
href = el.roHref;
|
||||
el = p.path[1];
|
||||
} else {
|
||||
if (!el || manager.isFolder(el)) { return; }
|
||||
var data = manager.getFileData(el);
|
||||
href = data.roHref;
|
||||
}
|
||||
openFile(null, href);
|
||||
openFile(el, true);
|
||||
});
|
||||
}
|
||||
else if ($this.hasClass('cp-app-drive-context-openincode')) {
|
||||
@@ -3972,6 +3987,14 @@ define([
|
||||
common.sessionStorage.put(Constants.newPadTeamKey, APP.team, waitFor());
|
||||
}).nThen(function () {
|
||||
common.openURL('/code/');
|
||||
// We need to restore sessionStorage for the next time we want to create a pad from this tab
|
||||
// NOTE: the 100ms timeout is to fix a race condition in firefox where sessionStorage
|
||||
// would be deleted before the new tab was created
|
||||
setTimeout(function () {
|
||||
common.sessionStorage.put(Constants.newPadFileData, '', function () {});
|
||||
common.sessionStorage.put(Constants.newPadPathKey, '', function () {});
|
||||
common.sessionStorage.put(Constants.newPadTeamKey, '', function () {});
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4051,8 +4074,7 @@ define([
|
||||
|
||||
if (manager.isFolder(el) && !manager.isSharedFolder(el)) { // Folder
|
||||
// if folder is inside SF
|
||||
return UI.warn('ERROR: Temporarily disabled'); // XXX CONVERT
|
||||
/*if (manager.isInSharedFolder(paths[0].path)) {
|
||||
if (manager.isInSharedFolder(paths[0].path)) {
|
||||
return void UI.alert(Messages.convertFolderToSF_SFParent);
|
||||
}
|
||||
// if folder already contains SF
|
||||
@@ -4080,10 +4102,14 @@ define([
|
||||
if (!res) { return; }
|
||||
var password = $(convertContent).find('#cp-upload-password').val() || undefined;
|
||||
var owned = Util.isChecked($(convertContent).find('#cp-upload-owned'));
|
||||
manager.convertFolderToSharedFolder(paths[0].path, owned, password, refresh);
|
||||
manager.convertFolderToSharedFolder(paths[0].path, owned, password, function (err, obj) {
|
||||
if (err || obj && obj.error) { return void console.error(err || obj.error); }
|
||||
if (obj && obj.fId) { APP.convertedFolder = obj.fId; }
|
||||
refresh();
|
||||
});
|
||||
});
|
||||
}*/
|
||||
} else { // File
|
||||
}
|
||||
} else { // File or shared folder
|
||||
var sf = manager.isSharedFolder(el);
|
||||
data = sf ? manager.getSharedFolderData(el) : manager.getFileData(el);
|
||||
parsed = (data.href && data.href.indexOf('#') !== -1) ? Hash.parsePadUrl(data.href) : {};
|
||||
|
||||
@@ -29,7 +29,9 @@ define([
|
||||
handlers['FRIEND_REQUEST'] = function (common, data) {
|
||||
var content = data.content;
|
||||
var msg = content.msg;
|
||||
var name = Util.fixHTML(msg.content.displayName) || Messages.anonymous;
|
||||
var userData = msg.content.user || msg.content;
|
||||
var name = Util.fixHTML(userData.displayName) || Messages.anonymous;
|
||||
msg.content = { user: userData };
|
||||
|
||||
// Display the notification
|
||||
content.getFormatText = function () {
|
||||
@@ -37,7 +39,7 @@ define([
|
||||
};
|
||||
|
||||
// Check authenticity
|
||||
if (msg.author !== msg.content.curvePublic) { return; }
|
||||
if (msg.author !== userData.curvePublic) { return; }
|
||||
|
||||
// if not archived, add handlers
|
||||
if (!content.archived) {
|
||||
@@ -51,7 +53,11 @@ define([
|
||||
handlers['FRIEND_REQUEST_ACCEPTED'] = function (common, data) {
|
||||
var content = data.content;
|
||||
var msg = content.msg;
|
||||
var name = Util.fixHTML(msg.content.name) || Messages.anonymous;
|
||||
var userData = typeof(msg.content.user) === "object" ? msg.content.user : {
|
||||
displayName: msg.content.name,
|
||||
curvePublic: msg.content.user
|
||||
};
|
||||
var name = Util.fixHTML(userData.displayName) || Messages.anonymous;
|
||||
content.getFormatText = function () {
|
||||
return Messages._getKey('friendRequest_accepted', [name]);
|
||||
};
|
||||
@@ -63,7 +69,11 @@ define([
|
||||
handlers['FRIEND_REQUEST_DECLINED'] = function (common, data) {
|
||||
var content = data.content;
|
||||
var msg = content.msg;
|
||||
var name = Util.fixHTML(msg.content.name) || Messages.anonymous;
|
||||
var userData = typeof(msg.content.user) === "object" ? msg.content.user : {
|
||||
displayName: msg.content.name,
|
||||
curvePublic: msg.content.user
|
||||
};
|
||||
var name = Util.fixHTML(userData.displayName) || Messages.anonymous;
|
||||
content.getFormatText = function () {
|
||||
return Messages._getKey('friendRequest_declined', [name]);
|
||||
};
|
||||
|
||||
@@ -62,6 +62,15 @@ body.cp-app-sheet, body.cp-app-oodoc, body.cp-app-ooslide {
|
||||
background-color: lightgrey;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
position: relative;
|
||||
}
|
||||
#cp-app-oo-offline {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
background-color: rgba(255,255,255,0.5);
|
||||
}
|
||||
#ooframe {
|
||||
flex: 1;
|
||||
|
||||
@@ -52,10 +52,10 @@ define([
|
||||
$: $
|
||||
};
|
||||
|
||||
|
||||
var CHECKPOINT_INTERVAL = 50;
|
||||
var DISPLAY_RESTORE_BUTTON = false;
|
||||
var NEW_VERSION = 2;
|
||||
var PENDING_TIMEOUT = 30000;
|
||||
|
||||
var debug = function (x) {
|
||||
if (!window.CP_DEV_MODE) { return; }
|
||||
@@ -76,6 +76,7 @@ define([
|
||||
var privateData = metadataMgr.getPrivateData();
|
||||
var readOnly = false;
|
||||
var offline = false;
|
||||
var pendingChanges = {};
|
||||
var config = {};
|
||||
var content = {
|
||||
hashes: {},
|
||||
@@ -89,6 +90,7 @@ define([
|
||||
var myUniqueOOId;
|
||||
var myOOId;
|
||||
var sessionId = Hash.createChannelId();
|
||||
var cpNfInner;
|
||||
|
||||
// This structure is used for caching media data and blob urls for each media cryptpad url
|
||||
var mediasData = {};
|
||||
@@ -102,6 +104,18 @@ define([
|
||||
return metadataMgr.getNetfluxId() + '-' + privateData.clientId;
|
||||
};
|
||||
|
||||
var setEditable = function (state) {
|
||||
$('#cp-app-oo-editor').find('#cp-app-oo-offline').remove();
|
||||
try {
|
||||
window.frames[0].editor.asc_setViewMode(!state);
|
||||
//window.frames[0].editor.setViewModeDisconnect(true);
|
||||
} catch (e) {}
|
||||
if (!state) {
|
||||
$('#cp-app-oo-editor').append(h('div#cp-app-oo-offline'));
|
||||
}
|
||||
debug(state);
|
||||
};
|
||||
|
||||
var deleteOffline = function () {
|
||||
var ids = content.ids;
|
||||
var users = Object.keys(metadataMgr.getMetadata().users);
|
||||
@@ -573,15 +587,23 @@ define([
|
||||
var myId = getId();
|
||||
content.locks[myId] = msg;
|
||||
oldLocks = JSON.parse(JSON.stringify(content.locks));
|
||||
// Answer to our onlyoffice
|
||||
send({
|
||||
type: "getLock",
|
||||
locks: getLock()
|
||||
});
|
||||
// Remove old locks
|
||||
deleteOfflineLocks();
|
||||
// Prepare callback
|
||||
if (cpNfInner) {
|
||||
var onPatchSent = function () {
|
||||
cpNfInner.offPatchSent(onPatchSent);
|
||||
// Answer to our onlyoffice
|
||||
send({
|
||||
type: "getLock",
|
||||
locks: getLock()
|
||||
});
|
||||
};
|
||||
cpNfInner.onPatchSent(onPatchSent);
|
||||
}
|
||||
// Commit
|
||||
APP.onLocal();
|
||||
APP.realtime.sync();
|
||||
};
|
||||
|
||||
var parseChanges = function (changes) {
|
||||
@@ -600,13 +622,30 @@ define([
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
var handleChanges = function (obj, send) {
|
||||
// Allow the changes
|
||||
send({
|
||||
type: "unSaveLock",
|
||||
index: ooChannel.cpIndex,
|
||||
time: +new Date()
|
||||
});
|
||||
// Add a new entry to the pendingChanges object.
|
||||
// If we can't send the patch within 30s, force a page reload
|
||||
var uid = Util.uid();
|
||||
pendingChanges[uid] = setTimeout(function () {
|
||||
// If we're offline, force a reload on reconnect
|
||||
if (offline) {
|
||||
pendingChanges.force = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// We're online: force a reload now
|
||||
setEditable(false);
|
||||
UI.alert(Messages.realtime_unrecoverableError, function () {
|
||||
common.gotoURL();
|
||||
});
|
||||
|
||||
}, PENDING_TIMEOUT);
|
||||
if (offline) {
|
||||
pendingChanges.force = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the changes
|
||||
rtChannel.sendMsg({
|
||||
type: "saveChanges",
|
||||
@@ -615,7 +654,22 @@ define([
|
||||
locks: [content.locks[getId()]],
|
||||
excelAdditionalInfo: null
|
||||
}, null, function (err, hash) {
|
||||
if (err) { return void console.error(err); }
|
||||
if (err) {
|
||||
return void console.error(err);
|
||||
}
|
||||
if (pendingChanges[uid]) {
|
||||
clearTimeout(pendingChanges[uid]);
|
||||
delete pendingChanges[uid];
|
||||
}
|
||||
// Call unSaveLock to tell onlyoffice that the patch was sent.
|
||||
// It will allow you to make changes to another cell.
|
||||
// If there is an error and unSaveLock is not called, onlyoffice
|
||||
// will try to send the patch again
|
||||
send({
|
||||
type: "unSaveLock",
|
||||
index: ooChannel.cpIndex,
|
||||
time: +new Date()
|
||||
});
|
||||
// Increment index and update latest hash
|
||||
ooChannel.cpIndex++;
|
||||
ooChannel.lastHash = hash;
|
||||
@@ -659,10 +713,12 @@ define([
|
||||
break;
|
||||
case "isSaveLock":
|
||||
// TODO ping the server to check if we're online first?
|
||||
send({
|
||||
type: "saveLock",
|
||||
saveLock: false
|
||||
});
|
||||
if (!offline) {
|
||||
send({
|
||||
type: "saveLock",
|
||||
saveLock: false
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "getLock":
|
||||
handleLock(obj, send);
|
||||
@@ -748,7 +804,9 @@ define([
|
||||
},
|
||||
"events": {
|
||||
"onAppReady": function(/*evt*/) {
|
||||
var $tb = $('iframe[name="frameEditor"]').contents().find('head');
|
||||
var $iframe = $('iframe[name="frameEditor"]').contents();
|
||||
$iframe.prop('tabindex', '-1');
|
||||
var $tb = $iframe.find('head');
|
||||
var css = // Old OO
|
||||
'#id-toolbar-full .toolbar-group:nth-child(2), #id-toolbar-full .separator:nth-child(3) { display: none; }' +
|
||||
'#fm-btn-save { display: none !important; }' +
|
||||
@@ -1283,7 +1341,6 @@ define([
|
||||
|
||||
var initializing = true;
|
||||
var $bar = $('#cp-toolbar');
|
||||
var cpNfInner;
|
||||
|
||||
config = {
|
||||
patchTransformer: ChainPad.SmartJSONTransformer,
|
||||
@@ -1300,15 +1357,6 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
var setEditable = function (state) {
|
||||
if (!state) {
|
||||
try {
|
||||
window.frames[0].editor.setViewModeDisconnect(true);
|
||||
} catch (e) {}
|
||||
}
|
||||
debug(state);
|
||||
};
|
||||
|
||||
var stringifyInner = function () {
|
||||
var obj = {
|
||||
content: content,
|
||||
@@ -1398,11 +1446,20 @@ define([
|
||||
var $exportXLSX = common.createButton('export', true, {}, exportXLSXFile);
|
||||
$exportXLSX.appendTo($rightside);
|
||||
|
||||
var type = common.getMetadataMgr().getPrivateData().ooType;
|
||||
var accept = [".bin", ".ods", ".xlsx"];
|
||||
if (type === "ooslide") {
|
||||
accept = ['.bin', '.odp', '.pptx'];
|
||||
} else if (type === "oodoc") {
|
||||
accept = ['.bin', '.odt', '.docx'];
|
||||
}
|
||||
if (typeof(Atomics) === "undefined") {
|
||||
accept = ['.bin'];
|
||||
}
|
||||
var $importXLSX = common.createButton('import', true, { accept: accept, binary : ["ods", "xlsx"] }, importXLSXFile);
|
||||
var $importXLSX = common.createButton('import', true, {
|
||||
accept: accept,
|
||||
binary : ["ods", "xlsx", "odt", "docx", "odp", "pptx"]
|
||||
}, importXLSXFile);
|
||||
$importXLSX.appendTo($rightside);
|
||||
|
||||
if (common.isLoggedIn()) {
|
||||
@@ -1549,25 +1606,23 @@ define([
|
||||
pinImages();
|
||||
};
|
||||
|
||||
config.onAbort = function () {
|
||||
// inform of network disconnect
|
||||
setEditable(false);
|
||||
toolbar.failed();
|
||||
UI.alert(Messages.common_connectionLost, undefined, true);
|
||||
};
|
||||
|
||||
config.onConnectionChange = function (info) {
|
||||
setEditable(info.state);
|
||||
if (info.state) {
|
||||
UI.findOKButton().click();
|
||||
UI.confirm(Messages.oo_reconnect, function (yes) {
|
||||
if (!yes) { return; }
|
||||
common.gotoURL();
|
||||
});
|
||||
// If we tried to send changes while we were offline, force a page reload
|
||||
UIElements.reconnectAlert();
|
||||
if (Object.keys(pendingChanges).length) {
|
||||
return void UI.confirm(Messages.oo_reconnect, function (yes) {
|
||||
if (!yes) { return; }
|
||||
common.gotoURL();
|
||||
});
|
||||
}
|
||||
setEditable(true);
|
||||
offline = false;
|
||||
} else {
|
||||
setEditable(false);
|
||||
offline = true;
|
||||
UI.findOKButton().click();
|
||||
UI.alert(Messages.common_connectionLost, undefined, true);
|
||||
UIElements.disconnectAlert();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ define([
|
||||
var requireConfig = RequireConfig();
|
||||
|
||||
// Loaded in load #2
|
||||
var hash, href;
|
||||
nThen(function (waitFor) {
|
||||
DomReady.onReady(waitFor());
|
||||
}).nThen(function (waitFor) {
|
||||
@@ -19,6 +20,13 @@ define([
|
||||
};
|
||||
window.rc = requireConfig;
|
||||
window.apiconf = ApiConfig;
|
||||
|
||||
// Hidden hash
|
||||
hash = window.location.hash;
|
||||
href = window.location.href;
|
||||
if (window.history && window.history.replaceState && hash) {
|
||||
window.history.replaceState({}, window.document.title, '#');
|
||||
}
|
||||
document.getElementById('sbox-iframe').setAttribute('src',
|
||||
ApiConfig.httpSafeOrigin + window.location.pathname + 'inner.html?' +
|
||||
requireConfig.urlArgs + '#' + encodeURIComponent(JSON.stringify(req)));
|
||||
@@ -144,6 +152,8 @@ define([
|
||||
});
|
||||
};
|
||||
SFCommonO.start({
|
||||
hash: hash,
|
||||
href: href,
|
||||
type: 'oo',
|
||||
useCreationScreen: true,
|
||||
addData: addData,
|
||||
|
||||
@@ -32,6 +32,16 @@ define([
|
||||
NetConfig, AppConfig,
|
||||
Crypto, ChainPad, CpNetflux, Listmap, nThen, Saferphore) {
|
||||
|
||||
// Default settings for new users
|
||||
var NEW_USER_SETTINGS = {
|
||||
drive: {
|
||||
hideDuplicate: true
|
||||
},
|
||||
general: {
|
||||
allowUserFeedback: true
|
||||
}
|
||||
};
|
||||
|
||||
var create = function () {
|
||||
var Store = window.Cryptpad_Store = {};
|
||||
var postMessage = function () {};
|
||||
@@ -1016,8 +1026,12 @@ define([
|
||||
|
||||
if (title.trim() === "") { title = UserObject.getDefaultName(p); }
|
||||
|
||||
if (AppConfig.disableAnonymousStore && !store.loggedIn) { return void cb(); }
|
||||
if (p.type === "debug") { return void cb(); }
|
||||
if (AppConfig.disableAnonymousStore && !store.loggedIn) {
|
||||
return void cb({ notStored: true });
|
||||
}
|
||||
if (p.type === "debug") {
|
||||
return void cb({ notStored: true });
|
||||
}
|
||||
|
||||
var channelData = Store.channels && Store.channels[channel];
|
||||
|
||||
@@ -1108,7 +1122,7 @@ define([
|
||||
postMessage(clientId, "AUTOSTORE_DISPLAY_POPUP", {
|
||||
autoStore: autoStore
|
||||
});
|
||||
return void cb();
|
||||
return void cb({ notStored: true });
|
||||
} else {
|
||||
var roHref;
|
||||
if (h.mode === "view") {
|
||||
@@ -1187,7 +1201,9 @@ define([
|
||||
});
|
||||
cb(list);
|
||||
};
|
||||
// Get the first pad we can find in any of our managers and return its file data
|
||||
|
||||
// Get the first pad we can find in any of our drives and return its file data
|
||||
// NOTE: This is currently only used for template: this won't search inside shared folders
|
||||
Store.getPadData = function (clientId, id, cb) {
|
||||
var res = {};
|
||||
getAllStores().some(function (s) {
|
||||
@@ -1199,6 +1215,49 @@ define([
|
||||
cb(res);
|
||||
};
|
||||
|
||||
Store.getPadDataFromChannel = function (clientId, obj, cb) {
|
||||
var channel = obj.channel;
|
||||
var edit = obj.edit;
|
||||
var isFile = obj.file;
|
||||
var res;
|
||||
var viewRes;
|
||||
getAllStores().some(function (s) {
|
||||
var chans = s.manager.findChannel(channel);
|
||||
if (!Array.isArray(chans)) { return; }
|
||||
return chans.some(function (pad) {
|
||||
if (!pad || !pad.data) { return; }
|
||||
var data = pad.data;
|
||||
// We've found a match: return the value and stop the loops
|
||||
if ((edit && data.href) || (!edit && data.roHref) || isFile) {
|
||||
res = data;
|
||||
return true;
|
||||
}
|
||||
// We've found a weaker match: store it for now
|
||||
if (edit && !viewRes && data.roHref) {
|
||||
viewRes = data;
|
||||
}
|
||||
});
|
||||
});
|
||||
// Call back with the best value we can get
|
||||
cb(res || viewRes || {});
|
||||
};
|
||||
|
||||
// Hidden hash: if a pad is deleted, we may have to switch back to full hash
|
||||
// in some tabs
|
||||
Store.checkDeletedPad = function (channel) {
|
||||
if (!channel) { return; }
|
||||
|
||||
// Check if the pad is still stored in one of our drives
|
||||
Store.getPadDataFromChannel(null, {
|
||||
channel: channel,
|
||||
isFile: true // we don't care if it's view or edit
|
||||
}, function (res) {
|
||||
// If it is stored, abort
|
||||
if (Object.keys(res).length) { return; }
|
||||
// Otherwise, tell all the tabs that this channel was deleted and give them the hrefs
|
||||
broadcast([], "CHANNEL_DELETED", channel);
|
||||
});
|
||||
};
|
||||
|
||||
// Messaging (manage friends from the userlist)
|
||||
Store.answerFriendRequest = function (clientId, obj, cb) {
|
||||
@@ -1218,15 +1277,15 @@ define([
|
||||
|
||||
// If we accept the request, add the friend to the list
|
||||
if (value) {
|
||||
Messaging.acceptFriendRequest(store, msg.content, function (obj) {
|
||||
Messaging.acceptFriendRequest(store, msg.content.user, function (obj) {
|
||||
if (obj && obj.error) { return void cb(obj); }
|
||||
Messaging.addToFriendList({
|
||||
proxy: store.proxy,
|
||||
realtime: store.realtime,
|
||||
pinPads: function (data, cb) { Store.pinPads(null, data, cb); },
|
||||
}, msg.content, function (err) {
|
||||
}, msg.content.user, function (err) {
|
||||
if (store.messenger) {
|
||||
store.messenger.onFriendAdded(msg.content);
|
||||
store.messenger.onFriendAdded(msg.content.user);
|
||||
}
|
||||
broadcast([], "UPDATE_METADATA");
|
||||
if (err) { return void cb({error: err}); }
|
||||
@@ -1236,12 +1295,7 @@ define([
|
||||
return;
|
||||
}
|
||||
// Otherwise, just remove the notification
|
||||
store.mailbox.sendTo('DECLINE_FRIEND_REQUEST', {
|
||||
displayName: store.proxy['cryptpad.username']
|
||||
}, {
|
||||
channel: msg.content.notifications,
|
||||
curvePublic: msg.content.curvePublic
|
||||
}, function (obj) {
|
||||
Messaging.declineFriendRequest(store, msg.content.user, function (obj) {
|
||||
broadcast([], "UPDATE_METADATA");
|
||||
cb(obj);
|
||||
});
|
||||
@@ -1263,8 +1317,9 @@ define([
|
||||
store.proxy.friends_pending[data.curvePublic] = +new Date();
|
||||
broadcast([], "UPDATE_METADATA");
|
||||
|
||||
var myData = Messaging.createData(store.proxy);
|
||||
store.mailbox.sendTo('FRIEND_REQUEST', myData, {
|
||||
store.mailbox.sendTo('FRIEND_REQUEST', {
|
||||
user: Messaging.createData(store.proxy)
|
||||
}, {
|
||||
channel: data.notifications,
|
||||
curvePublic: data.curvePublic
|
||||
}, function (obj) {
|
||||
@@ -1600,11 +1655,8 @@ define([
|
||||
// If send is true, send the request to the owner.
|
||||
if (owner) {
|
||||
if (data.send) {
|
||||
var myData = Messaging.createData(store.proxy);
|
||||
delete myData.channel;
|
||||
store.mailbox.sendTo('REQUEST_PAD_ACCESS', {
|
||||
channel: data.channel,
|
||||
user: myData
|
||||
channel: data.channel
|
||||
}, {
|
||||
channel: owner.notifications,
|
||||
curvePublic: owner.curvePublic
|
||||
@@ -1638,13 +1690,10 @@ define([
|
||||
}
|
||||
})) { return void cb({error: 'ENOTFOUND'}); }
|
||||
|
||||
var myData = Messaging.createData(store.proxy);
|
||||
delete myData.channel;
|
||||
store.mailbox.sendTo("GIVE_PAD_ACCESS", {
|
||||
channel: channel,
|
||||
href: href,
|
||||
title: title,
|
||||
user: myData
|
||||
title: title
|
||||
}, {
|
||||
channel: data.user.notifications,
|
||||
curvePublic: data.user.curvePublic
|
||||
@@ -1678,13 +1727,11 @@ define([
|
||||
}
|
||||
// Tell all the owners that the pad was deleted from the server
|
||||
var curvePublic = store.proxy.curvePublic;
|
||||
var myData = Messaging.createData(store.proxy, false);
|
||||
m.forEach(function (obj) {
|
||||
var mb = JSON.parse(obj);
|
||||
if (mb.curvePublic === curvePublic) { return; }
|
||||
store.mailbox.sendTo('OWNED_PAD_REMOVED', {
|
||||
channel: channel,
|
||||
user: myData
|
||||
channel: channel
|
||||
}, {
|
||||
channel: mb.notifications,
|
||||
curvePublic: mb.curvePublic
|
||||
@@ -2095,6 +2142,12 @@ define([
|
||||
}
|
||||
}
|
||||
}
|
||||
if (o && !n && Array.isArray(p) && (p[0] === UserObject.FILES_DATA ||
|
||||
(p[0] === 'drive' && p[1] === UserObject.FILES_DATA))) {
|
||||
setTimeout(function () {
|
||||
Store.checkDeletedPad(o && o.channel);
|
||||
});
|
||||
}
|
||||
sendDriveEvent('DRIVE_CHANGE', {
|
||||
id: fId,
|
||||
old: o,
|
||||
@@ -2227,7 +2280,8 @@ define([
|
||||
if (!store.loggedIn) { return void cb(); }
|
||||
Store.pinPads(null, data, cb);
|
||||
};
|
||||
if (!proxy.settings) { proxy.settings = {}; }
|
||||
if (!proxy.settings) { proxy.settings = NEW_USER_SETTINGS; }
|
||||
if (!proxy.friends_pending) { proxy.friends_pending = {}; }
|
||||
var manager = store.manager = ProxyManager.create(proxy.drive, {
|
||||
onSync: function (cb) { onSync(null, cb); },
|
||||
edPublic: proxy.edPublic,
|
||||
@@ -2311,13 +2365,7 @@ define([
|
||||
}
|
||||
}
|
||||
|
||||
if (!proxy.settings || !proxy.settings.general ||
|
||||
typeof(proxy.settings.general.allowUserFeedback) !== 'boolean') {
|
||||
proxy.settings = proxy.settings || {};
|
||||
proxy.settings.general = proxy.settings.general || {};
|
||||
proxy.settings.general.allowUserFeedback = true;
|
||||
}
|
||||
returned.feedback = proxy.settings.general.allowUserFeedback;
|
||||
returned.feedback = Util.find(proxy, ['settings', 'general', 'allowUserFeedback']);
|
||||
Feedback.init(returned.feedback);
|
||||
|
||||
if (typeof(cb) === 'function') { cb(returned); }
|
||||
|
||||
@@ -4,6 +4,7 @@ define([
|
||||
'/common/common-util.js',
|
||||
], function (Messaging, Hash, Util) {
|
||||
|
||||
// Random timeout between 10 and 30 times your sync time (lag + chainpad sync)
|
||||
var getRandomTimeout = function (ctx) {
|
||||
var lag = ctx.store.realtime.getLag().lag || 0;
|
||||
return (Math.max(0, lag) + 300) * 20 * (0.5 + Math.random());
|
||||
@@ -22,9 +23,11 @@ define([
|
||||
// Store the friend request displayed to avoid duplicates
|
||||
var friendRequest = {};
|
||||
handlers['FRIEND_REQUEST'] = function (ctx, box, data, cb) {
|
||||
// Old format: data was stored directly in "content"
|
||||
var userData = data.msg.content.user || data.msg.content;
|
||||
|
||||
// Check if the request is valid (send by the correct user)
|
||||
if (data.msg.author !== data.msg.content.curvePublic) {
|
||||
if (data.msg.author !== userData.curvePublic) {
|
||||
return void cb(true);
|
||||
}
|
||||
|
||||
@@ -40,7 +43,8 @@ define([
|
||||
if (Messaging.getFriend(ctx.store.proxy, data.msg.author) ||
|
||||
ctx.store.proxy.friends_pending[data.msg.author]) {
|
||||
delete ctx.store.proxy.friends_pending[data.msg.author];
|
||||
Messaging.acceptFriendRequest(ctx.store, data.msg.content, function (obj) {
|
||||
|
||||
Messaging.acceptFriendRequest(ctx.store, userData, function (obj) {
|
||||
if (obj && obj.error) {
|
||||
return void cb();
|
||||
}
|
||||
@@ -48,10 +52,10 @@ define([
|
||||
proxy: ctx.store.proxy,
|
||||
realtime: ctx.store.realtime,
|
||||
pinPads: ctx.pinPads
|
||||
}, data.msg.content, function (err) {
|
||||
if (err) { console.error(err); }
|
||||
}, userData, function (err) {
|
||||
if (err) { return void console.error(err); }
|
||||
if (ctx.store.messenger) {
|
||||
ctx.store.messenger.onFriendAdded(data.msg.content);
|
||||
ctx.store.messenger.onFriendAdded(userData);
|
||||
}
|
||||
});
|
||||
ctx.updateMetadata();
|
||||
@@ -63,96 +67,110 @@ define([
|
||||
cb();
|
||||
};
|
||||
removeHandlers['FRIEND_REQUEST'] = function (ctx, box, data) {
|
||||
if (friendRequest[data.content.curvePublic]) {
|
||||
delete friendRequest[data.content.curvePublic];
|
||||
var userData = data.content.user || data.content;
|
||||
if (friendRequest[userData.curvePublic]) {
|
||||
delete friendRequest[userData.curvePublic];
|
||||
}
|
||||
};
|
||||
|
||||
// The DECLINE and ACCEPT messages act on the contacts data
|
||||
// They are processed with a random timeout to avoid having
|
||||
// multiple workers trying to add or remove the contacts at
|
||||
// the same time. Once processed, they are dismissed.
|
||||
// We must dismiss them and send another message to our own
|
||||
// mailbox for the UI part otherwise it would automatically
|
||||
// accept or decline future requests from the same user
|
||||
// until the message is manually dismissed.
|
||||
|
||||
var friendRequestDeclined = {};
|
||||
handlers['DECLINE_FRIEND_REQUEST'] = function (ctx, box, data, cb) {
|
||||
setTimeout(function () {
|
||||
// Our friend request was declined.
|
||||
if (!ctx.store.proxy.friends_pending[data.msg.author]) { return; }
|
||||
// Old format: data was stored directly in "content"
|
||||
var userData = data.msg.content.user || data.msg.content;
|
||||
if (!userData.curvePublic) { userData.curvePublic = data.msg.author; }
|
||||
|
||||
// Our friend request was declined.
|
||||
setTimeout(function () {
|
||||
// Only dismissed once in the timeout to make sure we won't lose
|
||||
// the data if we close the worker before adding the friend
|
||||
cb(true);
|
||||
|
||||
// Make sure we really sent it
|
||||
if (!ctx.store.proxy.friends_pending[data.msg.author]) { return; }
|
||||
// Remove the pending message and display the "declined" state in the UI
|
||||
delete ctx.store.proxy.friends_pending[data.msg.author];
|
||||
|
||||
ctx.updateMetadata();
|
||||
if (friendRequestDeclined[data.msg.author]) { return; }
|
||||
friendRequestDeclined[data.msg.author] = true;
|
||||
box.sendMessage({
|
||||
type: 'FRIEND_REQUEST_DECLINED',
|
||||
content: {
|
||||
user: data.msg.author,
|
||||
name: data.msg.content.displayName
|
||||
}
|
||||
}, function () {
|
||||
if (friendRequestDeclined[data.msg.author]) {
|
||||
// TODO remove our message because another one was sent first?
|
||||
}
|
||||
friendRequestDeclined[data.msg.author] = true;
|
||||
});
|
||||
content: { user: userData }
|
||||
}, function () {});
|
||||
}, getRandomTimeout(ctx));
|
||||
cb(true);
|
||||
};
|
||||
// UI for declined friend request
|
||||
handlers['FRIEND_REQUEST_DECLINED'] = function (ctx, box, data, cb) {
|
||||
ctx.updateMetadata();
|
||||
if (friendRequestDeclined[data.msg.content.user]) { return void cb(true); }
|
||||
friendRequestDeclined[data.msg.content.user] = true;
|
||||
var curve = data.msg.content.user.curvePublic || data.msg.content.user;
|
||||
if (friendRequestDeclined[curve]) { return void cb(true); }
|
||||
friendRequestDeclined[curve] = true;
|
||||
cb();
|
||||
};
|
||||
removeHandlers['FRIEND_REQUEST_DECLINED'] = function (ctx, box, data) {
|
||||
if (friendRequestDeclined[data.content.user]) {
|
||||
delete friendRequestDeclined[data.content.user];
|
||||
}
|
||||
var curve = data.content.user.curvePublic || data.content.user;
|
||||
if (friendRequestDeclined[curve]) { delete friendRequestDeclined[curve]; }
|
||||
};
|
||||
|
||||
var friendRequestAccepted = {};
|
||||
handlers['ACCEPT_FRIEND_REQUEST'] = function (ctx, box, data, cb) {
|
||||
// Old format: data was stored directly in "content"
|
||||
var userData = data.msg.content.user || data.msg.content;
|
||||
|
||||
// Our friend request was accepted.
|
||||
setTimeout(function () {
|
||||
// Only dismissed once in the timeout to make sure we won't lose
|
||||
// the data if we close the worker before adding the friend
|
||||
cb(true);
|
||||
|
||||
// Make sure we really sent it
|
||||
if (!ctx.store.proxy.friends_pending[data.msg.author]) { return; }
|
||||
// Remove the pending state. It will also us to send a new request in case of error
|
||||
delete ctx.store.proxy.friends_pending[data.msg.author];
|
||||
|
||||
// And add the friend
|
||||
Messaging.addToFriendList({
|
||||
proxy: ctx.store.proxy,
|
||||
realtime: ctx.store.realtime,
|
||||
pinPads: ctx.pinPads
|
||||
}, data.msg.content, function (err) {
|
||||
if (err) { console.error(err); }
|
||||
delete ctx.store.proxy.friends_pending[data.msg.author];
|
||||
if (ctx.store.messenger) {
|
||||
ctx.store.messenger.onFriendAdded(data.msg.content);
|
||||
}
|
||||
}, userData, function (err) {
|
||||
if (err) { return void console.error(err); }
|
||||
// Load the chat if contacts app loaded
|
||||
if (ctx.store.messenger) { ctx.store.messenger.onFriendAdded(userData); }
|
||||
// Update the userlist
|
||||
ctx.updateMetadata();
|
||||
// If you have a profile page open, update it
|
||||
if (ctx.store.modules['profile']) { ctx.store.modules['profile'].update(); }
|
||||
if (friendRequestAccepted[data.msg.author]) { return; }
|
||||
// Display the "accepted" state in the UI
|
||||
if (friendRequestAccepted[data.msg.author]) { return; }
|
||||
friendRequestAccepted[data.msg.author] = true;
|
||||
box.sendMessage({
|
||||
type: 'FRIEND_REQUEST_ACCEPTED',
|
||||
content: {
|
||||
user: data.msg.author,
|
||||
name: data.msg.content.displayName
|
||||
}
|
||||
}, function () {
|
||||
if (friendRequestAccepted[data.msg.author]) {
|
||||
// TODO remove our message because another one was sent first?
|
||||
}
|
||||
friendRequestAccepted[data.msg.author] = true;
|
||||
});
|
||||
content: { user: userData }
|
||||
}, function () {});
|
||||
});
|
||||
}, getRandomTimeout(ctx));
|
||||
cb(true);
|
||||
};
|
||||
// UI for accepted friend request
|
||||
handlers['FRIEND_REQUEST_ACCEPTED'] = function (ctx, box, data, cb) {
|
||||
ctx.updateMetadata();
|
||||
if (friendRequestAccepted[data.msg.content.user]) { return void cb(true); }
|
||||
friendRequestAccepted[data.msg.content.user] = true;
|
||||
var curve = data.msg.content.user.curvePublic || data.msg.content.user;
|
||||
if (friendRequestAccepted[curve]) { return void cb(true); }
|
||||
friendRequestAccepted[curve] = true;
|
||||
cb();
|
||||
};
|
||||
removeHandlers['FRIEND_REQUEST_ACCEPTED'] = function (ctx, box, data) {
|
||||
if (friendRequestAccepted[data.content.user]) {
|
||||
delete friendRequestAccepted[data.content.user];
|
||||
}
|
||||
var curve = data.content.user.curvePublic || data.content.user;
|
||||
if (friendRequestAccepted[curve]) { delete friendRequestAccepted[curve]; }
|
||||
};
|
||||
|
||||
handlers['UNFRIEND'] = function (ctx, box, data, cb) {
|
||||
|
||||
@@ -2,11 +2,12 @@ define([
|
||||
'/common/common-util.js',
|
||||
'/common/common-hash.js',
|
||||
'/common/common-realtime.js',
|
||||
'/common/common-messaging.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, Notify, Handlers, CpNetflux, Crypto) {
|
||||
], function (Util, Hash, Realtime, Messaging, Notify, Handlers, CpNetflux, Crypto) {
|
||||
var Mailbox = {};
|
||||
|
||||
var TYPES = [
|
||||
@@ -96,6 +97,12 @@ proxy.mailboxes = {
|
||||
|
||||
var crypto = Crypto.Mailbox.createEncryptor(keys);
|
||||
|
||||
// Always send your data
|
||||
if (typeof(msg) === "object" && !msg.user) {
|
||||
var myData = Messaging.createData(ctx.store.proxy, false);
|
||||
msg.user = myData;
|
||||
}
|
||||
|
||||
var text = JSON.stringify({
|
||||
type: type,
|
||||
content: msg
|
||||
@@ -187,6 +194,11 @@ proxy.mailboxes = {
|
||||
history: [], // All the hashes loaded from the server in corretc order
|
||||
content: {}, // Content of the messages that should be displayed
|
||||
sendMessage: function (msg) { // To send a message to our box
|
||||
// Always send your data
|
||||
if (typeof(msg) === "object" && !msg.user) {
|
||||
var myData = Messaging.createData(ctx.store.proxy, false);
|
||||
msg.user = myData;
|
||||
}
|
||||
try {
|
||||
msg = JSON.stringify(msg);
|
||||
} catch (e) {
|
||||
|
||||
@@ -893,7 +893,7 @@ define([
|
||||
};
|
||||
|
||||
var clearOwnedChannel = function (ctx, id, cb) {
|
||||
var channel = ctx.clients[id];
|
||||
var channel = ctx.channels[id];
|
||||
if (!channel) { return void cb({error: 'NO_CHANNEL'}); }
|
||||
if (!ctx.store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||
ctx.store.rpc.clearOwnedChannel(id, function (err) {
|
||||
|
||||
@@ -53,7 +53,7 @@ define([
|
||||
// all our client IDs.
|
||||
if (chan.clients) {
|
||||
chan.clients.forEach(function (cl) {
|
||||
if (ctx.clients[cl] && !ctx.clients[cl].id) {
|
||||
if (ctx.clients[cl]) {
|
||||
ctx.clients[cl].id = wc.myID + '-' + cl;
|
||||
}
|
||||
});
|
||||
@@ -189,15 +189,22 @@ define([
|
||||
if (!c) { return void cb({ error: 'NOT_IN_CHANNEL' }); }
|
||||
var chan = ctx.channels[c.channel];
|
||||
if (!chan) { return void cb({ error: 'INVALID_CHANNEL' }); }
|
||||
// Prepare the callback: broadcast the message to the other local tabs
|
||||
// if the message is sent
|
||||
var _cb = function (obj) {
|
||||
if (obj && obj.error) { return void cb(obj); }
|
||||
ctx.emit('MESSAGE', {
|
||||
msg: data.msg
|
||||
}, chan.clients.filter(function (cl) {
|
||||
return cl !== clientId;
|
||||
}));
|
||||
cb();
|
||||
};
|
||||
// Send the message
|
||||
if (data.isCp) {
|
||||
return void chan.sendMsg(data.isCp, cb);
|
||||
return void chan.sendMsg(data.isCp, _cb);
|
||||
}
|
||||
chan.sendMsg(data.msg, cb);
|
||||
ctx.emit('MESSAGE', {
|
||||
msg: data.msg
|
||||
}, chan.clients.filter(function (cl) {
|
||||
return cl !== clientId;
|
||||
}));
|
||||
chan.sendMsg(data.msg, _cb);
|
||||
};
|
||||
|
||||
var reencrypt = function (ctx, data, cId, cb) {
|
||||
|
||||
@@ -50,6 +50,7 @@ define([
|
||||
GET_TEMPLATES: Store.getTemplates,
|
||||
GET_SECURE_FILES_LIST: Store.getSecureFilesList,
|
||||
GET_PAD_DATA: Store.getPadData,
|
||||
GET_PAD_DATA_FROM_CHANNEL: Store.getPadDataFromChannel,
|
||||
GET_STRONGER_HASH: Store.getStrongerHash,
|
||||
INCREMENT_TEMPLATE_USE: Store.incrementTemplateUse,
|
||||
GET_SHARED_FOLDER: Store.getSharedFolder,
|
||||
|
||||
@@ -93,6 +93,12 @@ define([
|
||||
}
|
||||
}
|
||||
}
|
||||
if (o && !n && Array.isArray(p) && (p[0] === UserObject.FILES_DATA ||
|
||||
(p[0] === 'drive' && p[1] === UserObject.FILES_DATA))) {
|
||||
setTimeout(function () {
|
||||
ctx.Store.checkDeletedPad(o && o.channel);
|
||||
});
|
||||
}
|
||||
team.sendEvent('DRIVE_CHANGE', {
|
||||
id: fId,
|
||||
old: o,
|
||||
@@ -903,13 +909,11 @@ define([
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
// Send mailbox to offer ownership
|
||||
var myData = Messaging.createData(ctx.store.proxy, false);
|
||||
ctx.store.mailbox.sendTo("ADD_OWNER", {
|
||||
teamChannel: teamData.channel,
|
||||
chatChannel: Util.find(teamData, ['keys', 'chat', 'channel']),
|
||||
rosterChannel: Util.find(teamData, ['keys', 'roster', 'channel']),
|
||||
title: teamData.metadata.name,
|
||||
user: myData
|
||||
title: teamData.metadata.name
|
||||
}, {
|
||||
channel: user.notifications,
|
||||
curvePublic: user.curvePublic
|
||||
@@ -963,12 +967,10 @@ define([
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
// Send mailbox to offer ownership
|
||||
var myData = Messaging.createData(ctx.store.proxy, false);
|
||||
ctx.store.mailbox.sendTo("RM_OWNER", {
|
||||
teamChannel: teamData.channel,
|
||||
title: teamData.metadata.name,
|
||||
pending: isPendingOwner,
|
||||
user: myData
|
||||
pending: isPendingOwner
|
||||
}, {
|
||||
channel: user.notifications,
|
||||
curvePublic: user.curvePublic
|
||||
@@ -1098,11 +1100,9 @@ define([
|
||||
if (!team) { return void cb ({error: 'ENOENT'}); }
|
||||
|
||||
// Send mailbox to offer ownership
|
||||
var myData = Messaging.createData(ctx.store.proxy, false);
|
||||
ctx.store.mailbox.sendTo("TEAM_EDIT_RIGHTS", {
|
||||
state: state,
|
||||
teamData: getInviteData(ctx, teamId, state),
|
||||
user: myData
|
||||
teamData: getInviteData(ctx, teamId, state)
|
||||
}, {
|
||||
channel: user.notifications,
|
||||
curvePublic: user.curvePublic
|
||||
@@ -1169,7 +1169,6 @@ define([
|
||||
team.roster.add(obj, function (err) {
|
||||
if (err && err !== 'NO_CHANGE') { return void cb({error: err}); }
|
||||
ctx.store.mailbox.sendTo('INVITE_TO_TEAM', {
|
||||
user: Messaging.createData(ctx.store.proxy, false),
|
||||
team: getInviteData(ctx, teamId)
|
||||
}, {
|
||||
channel: user.notifications,
|
||||
@@ -1196,7 +1195,6 @@ define([
|
||||
if (!userData || !userData.notifications) { return cb(); }
|
||||
ctx.store.mailbox.sendTo('KICKED_FROM_TEAM', {
|
||||
pending: data.pending,
|
||||
user: Messaging.createData(ctx.store.proxy, false),
|
||||
teamChannel: getInviteData(ctx, teamId).channel,
|
||||
teamName: getInviteData(ctx, teamId).metadata.name
|
||||
}, {
|
||||
|
||||
@@ -438,14 +438,24 @@ define([
|
||||
parentEl.push(id);
|
||||
return;
|
||||
}
|
||||
// Add to root if path is ROOT or if no path
|
||||
// Add to root if no path
|
||||
var filesList = exp.getFiles([ROOT, TRASH, 'hrefArray']);
|
||||
if (path && exp.isPathIn(newPath, [ROOT]) || filesList.indexOf(id) === -1) {
|
||||
parentEl = exp.find(newPath || [ROOT]);
|
||||
if (filesList.indexOf(id) === -1 && !newPath) {
|
||||
newPath = [ROOT];
|
||||
}
|
||||
// Add to root
|
||||
if (path && exp.isPathIn(newPath, [ROOT])) {
|
||||
parentEl = exp.find(newPath);
|
||||
if (parentEl) {
|
||||
var newName = exp.getAvailableName(parentEl, Hash.createChannelId());
|
||||
parentEl[newName] = id;
|
||||
return;
|
||||
} else {
|
||||
parentEl = exp.find([ROOT]);
|
||||
newPath.slice(1).forEach(function (folderName) {
|
||||
parentEl = parentEl[folderName] = parentEl[folderName] || {};
|
||||
});
|
||||
parentEl[Hash.createChannelId()] = id;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -89,18 +89,6 @@ var factory = function (Util, Rpc) {
|
||||
});
|
||||
};
|
||||
|
||||
// get the total stored size of a channel's patches (in bytes)
|
||||
exp.getFileSize = function (file, cb) {
|
||||
rpc.send('GET_FILE_SIZE', file, function (e, response) {
|
||||
if (e) { return void cb(e); }
|
||||
if (response && response.length && typeof(response[0]) === 'number') {
|
||||
return void cb(void 0, response[0]);
|
||||
} else {
|
||||
cb('INVALID_RESPONSE');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// get the combined size of all channels (in bytes) for all the
|
||||
// channels which the server has pinned for your publicKey
|
||||
exp.getFileListSize = function (cb) {
|
||||
|
||||
@@ -587,14 +587,10 @@ define([
|
||||
|
||||
// convert a folder to a Shared Folder
|
||||
var _convertFolderToSharedFolder = function (Env, data, cb) {
|
||||
return void cb({
|
||||
error: 'DISABLED'
|
||||
}); // XXX CONVERT
|
||||
/*var path = data.path;
|
||||
var path = data.path;
|
||||
var folderElement = Env.user.userObject.find(path);
|
||||
// don't try to convert top-level elements (trash, root, etc) to shared-folders
|
||||
// TODO also validate that you're in root (not templates, etc)
|
||||
if (data.path.length <= 1) {
|
||||
if (path.length <= 1 || path[0] !== UserObject.ROOT) {
|
||||
return void cb({
|
||||
error: 'E_INVAL_PATH',
|
||||
});
|
||||
@@ -664,6 +660,21 @@ define([
|
||||
newPath: newPath,
|
||||
copy: false,
|
||||
}, waitFor());
|
||||
}).nThen(function (waitFor) {
|
||||
// Move the owned pads from the old folder to root
|
||||
var paths = [];
|
||||
Object.keys(folderElement).forEach(function (el) {
|
||||
if (!Env.user.userObject.isFile(folderElement[el])) { return; }
|
||||
var data = Env.user.userObject.getFileData(folderElement[el]);
|
||||
if (!data || !_ownedByMe(Env, data.owners)) { return; }
|
||||
// This is an owned pad: move it to ROOT before deleting the initial folder
|
||||
paths.push(path.concat(el));
|
||||
});
|
||||
_move(Env, {
|
||||
paths: paths,
|
||||
newPath: [UserObject.ROOT],
|
||||
copy: false,
|
||||
}, waitFor());
|
||||
}).nThen(function () {
|
||||
// migrate metadata
|
||||
var sharedFolderElement = Env.user.proxy[UserObject.SHARED_FOLDERS][SFId];
|
||||
@@ -678,9 +689,11 @@ define([
|
||||
|
||||
// remove folder
|
||||
Env.user.userObject.delete([path], function () {
|
||||
cb();
|
||||
cb({
|
||||
fId: SFId
|
||||
});
|
||||
});
|
||||
});*/
|
||||
});
|
||||
};
|
||||
|
||||
// Delete permanently some pads or folders
|
||||
@@ -771,6 +784,9 @@ define([
|
||||
toUnpin.forEach(function (chan) {
|
||||
if (toKeep.indexOf(chan) === -1) {
|
||||
unpinList.push(chan);
|
||||
|
||||
// Check if need need to restore a full hash (hidden hash deleted from drive)
|
||||
Env.Store.checkDeletedPad(chan);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -783,7 +799,16 @@ define([
|
||||
};
|
||||
// Empty the trash (main drive only)
|
||||
var _emptyTrash = function (Env, data, cb) {
|
||||
Env.user.userObject.emptyTrash(cb);
|
||||
Env.user.userObject.emptyTrash(function (err, toClean) {
|
||||
cb();
|
||||
|
||||
// Check if need need to restore a full hash (hidden hash deleted from drive)
|
||||
if (!Array.isArray(toClean)) { return; }
|
||||
var toCheck = Util.deduplicateString(toClean);
|
||||
toCheck.forEach(function (chan) {
|
||||
Env.Store.checkDeletedPad(chan);
|
||||
});
|
||||
});
|
||||
};
|
||||
// Rename files or folders
|
||||
var _rename = function (Env, data, cb) {
|
||||
|
||||
@@ -396,9 +396,9 @@ define([
|
||||
if (state === STATE.DELETED) { return; }
|
||||
stateChange(info.state ? STATE.INITIALIZING : STATE.DISCONNECTED, info.permanent);
|
||||
/*if (info.state) {
|
||||
UI.findOKButton().click();
|
||||
UIElements.reconnectAlert();
|
||||
} else {
|
||||
UI.alert(Messages.common_connectionLost, undefined, true);
|
||||
UIElements.disconnectAlert();
|
||||
}*/
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ define([
|
||||
], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) {
|
||||
var requireConfig = RequireConfig();
|
||||
|
||||
var hash, href;
|
||||
nThen(function (waitFor) {
|
||||
DomReady.onReady(waitFor());
|
||||
}).nThen(function (waitFor) {
|
||||
@@ -18,6 +19,14 @@ define([
|
||||
};
|
||||
window.rc = requireConfig;
|
||||
window.apiconf = ApiConfig;
|
||||
|
||||
// Hidden hash
|
||||
hash = window.location.hash;
|
||||
href = window.location.href;
|
||||
if (window.history && window.history.replaceState && hash) {
|
||||
window.history.replaceState({}, window.document.title, '#');
|
||||
}
|
||||
|
||||
document.getElementById('sbox-iframe').setAttribute('src',
|
||||
ApiConfig.httpSafeOrigin + window.location.pathname + 'inner.html?' +
|
||||
requireConfig.urlArgs + '#' + encodeURIComponent(JSON.stringify(req)));
|
||||
@@ -36,6 +45,8 @@ define([
|
||||
window.addEventListener('message', onMsg);
|
||||
}).nThen(function (/*waitFor*/) {
|
||||
SFCommonO.start({
|
||||
hash: hash,
|
||||
href: href,
|
||||
useCreationScreen: true,
|
||||
messaging: true
|
||||
});
|
||||
|
||||
@@ -48,6 +48,8 @@ define([
|
||||
var updateLoadingProgress = config.updateLoadingProgress;
|
||||
config = undefined;
|
||||
|
||||
var evPatchSent = Util.mkEvent();
|
||||
|
||||
var chainpad = ChainPad.create({
|
||||
userName: userName,
|
||||
initialState: initialState,
|
||||
@@ -57,7 +59,10 @@ define([
|
||||
logLevel: logLevel
|
||||
});
|
||||
chainpad.onMessage(function(message, cb) {
|
||||
sframeChan.query('Q_RT_MESSAGE', message, cb);
|
||||
sframeChan.query('Q_RT_MESSAGE', message, function (err) {
|
||||
if (!err) { evPatchSent.fire(); }
|
||||
cb(err);
|
||||
});
|
||||
});
|
||||
chainpad.onPatch(function () {
|
||||
onRemote({ realtime: chainpad });
|
||||
@@ -149,6 +154,8 @@ define([
|
||||
metadataMgr: metadataMgr,
|
||||
whenRealtimeSyncs: whenRealtimeSyncs,
|
||||
onInfiniteSpinner: evInfiniteSpinner.reg,
|
||||
onPatchSent: evPatchSent.reg,
|
||||
offPatchSent: evPatchSent.unreg,
|
||||
chainpad: chainpad,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -62,15 +62,16 @@ define([
|
||||
});
|
||||
|
||||
editor._noCursorUpdate = false;
|
||||
editor.state.focused = true;
|
||||
editor.scrollTo(scroll.left, scroll.top);
|
||||
|
||||
if (!editor.state.focused) { return; }
|
||||
|
||||
if(selects[0] === selects[1]) {
|
||||
editor.setCursor(posToCursor(selects[0], remoteDoc));
|
||||
}
|
||||
else {
|
||||
editor.setSelection(posToCursor(selects[0], remoteDoc), posToCursor(selects[1], remoteDoc));
|
||||
}
|
||||
|
||||
editor.scrollTo(scroll.left, scroll.top);
|
||||
};
|
||||
|
||||
module.getHeadingText = function (editor) {
|
||||
|
||||
@@ -53,11 +53,17 @@ define([
|
||||
|
||||
var $table = File.$table = $('<table>', { id: 'cp-fileupload-table' });
|
||||
|
||||
var hover = false;
|
||||
var createTableContainer = function ($body) {
|
||||
File.$container = $('<div>', { id: 'cp-fileupload' }).append(tableHeader).append($table).appendTo($body);
|
||||
$('.cp-fileupload-header-close').click(function () {
|
||||
File.$container.fadeOut();
|
||||
});
|
||||
File.$container.mouseenter(function () {
|
||||
hover = true;
|
||||
}).mouseleave(function () {
|
||||
hover = false;
|
||||
});
|
||||
return File.$container;
|
||||
};
|
||||
|
||||
@@ -209,6 +215,11 @@ define([
|
||||
window.setTimeout(function () { File.$container.show(); });
|
||||
var file = queue.queue.shift();
|
||||
if (file.dl) { return void file.dl(file); }
|
||||
if (file.$line && file.$line[0] && !hover) {
|
||||
var line = file.$line[0];
|
||||
line.scrollIntoView(false);
|
||||
}
|
||||
delete file.$line;
|
||||
upload(file);
|
||||
};
|
||||
queue.push = function (obj) {
|
||||
@@ -224,10 +235,10 @@ define([
|
||||
$('<div>', {'class':'cp-fileupload-table-progressbar'}).appendTo($progressContainer);
|
||||
$('<span>', {'class':'cp-fileupload-table-progress-value'}).text(Messages.upload_pending).appendTo($progressContainer);
|
||||
|
||||
var $tr = $('<tr>', {id: id}).appendTo($table);
|
||||
var $tr = obj.$line = $('<tr>', {id: id}).appendTo($table);
|
||||
var $lines = $table.find('tr[id]');
|
||||
if ($lines.length > 5) {
|
||||
$lines.slice(0, $lines.length - 5).remove();
|
||||
//$lines.slice(0, $lines.length - 5).remove();
|
||||
}
|
||||
|
||||
var $cancel = $('<span>', {'class': 'cp-fileupload-table-cancel-button fa fa-times'}).click(function () {
|
||||
@@ -257,6 +268,13 @@ define([
|
||||
// cancel
|
||||
$('<td>', {'class': 'cp-fileupload-table-cancel'}).append($cancel).appendTo($tr);
|
||||
|
||||
var tw = $table.width();
|
||||
var cw = File.$container.prop('clientWidth');
|
||||
var diff = tw - cw;
|
||||
if (diff && diff > 0) {
|
||||
$table.css('margin-right', diff+'px');
|
||||
}
|
||||
|
||||
queue.next();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
define([
|
||||
'jquery',
|
||||
'/common/common-util.js',
|
||||
'/common/common-hash.js',
|
||||
'/common/common-interface.js',
|
||||
'/common/common-ui-elements.js',
|
||||
'/common/notifications.js',
|
||||
'/common/hyperscript.js',
|
||||
'/customize/messages.js',
|
||||
], function ($, Util, UI, UIElements, Notifications, h, Messages) {
|
||||
], function ($, Util, Hash, UI, UIElements, Notifications, h, Messages) {
|
||||
var Mailbox = {};
|
||||
|
||||
Mailbox.create = function (Common) {
|
||||
@@ -53,9 +54,23 @@ define([
|
||||
};
|
||||
var createElement = mailbox.createElement = function (data) {
|
||||
var notif;
|
||||
var avatar;
|
||||
var userData = Util.find(data, ['content', 'msg', 'content', 'user']);
|
||||
if (userData && typeof(userData) === "object" && userData.profile) {
|
||||
avatar = h('span.cp-avatar');
|
||||
Common.displayAvatar($(avatar), userData.avatar, userData.displayName || userData.name);
|
||||
$(avatar).click(function (e) {
|
||||
e.stopPropagation();
|
||||
Common.openURL(Hash.hashToHref(userData.profile, 'profile'));
|
||||
});
|
||||
}
|
||||
notif = h('div.cp-notification', {
|
||||
'data-hash': data.content.hash
|
||||
}, [h('div.cp-notification-content', h('p', formatData(data)))]);
|
||||
}, [
|
||||
avatar,
|
||||
h('div.cp-notification-content',
|
||||
h('p', formatData(data)))
|
||||
]);
|
||||
|
||||
if (typeof(data.content.getFormatText) === "function") {
|
||||
$(notif).find('.cp-notification-content p').html(data.content.getFormatText());
|
||||
|
||||
@@ -30,6 +30,12 @@ define([
|
||||
var password;
|
||||
var initialPathInDrive;
|
||||
|
||||
var currentPad = window.CryptPad_location = {
|
||||
app: '',
|
||||
href: cfg.href || window.location.href,
|
||||
hash: cfg.hash || window.location.hash
|
||||
};
|
||||
|
||||
nThen(function (waitFor) {
|
||||
// Load #2, the loading screen is up so grab whatever you need...
|
||||
require([
|
||||
@@ -134,8 +140,15 @@ define([
|
||||
});
|
||||
}
|
||||
}), {
|
||||
driveEvents: cfg.driveEvents
|
||||
driveEvents: cfg.driveEvents,
|
||||
currentPad: currentPad
|
||||
});
|
||||
|
||||
if (window.history && window.history.replaceState && currentPad.hash) {
|
||||
var nHash = currentPad.hash;
|
||||
if (!/^#/.test(nHash)) { nHash = '#' + nHash; }
|
||||
window.history.replaceState({}, window.document.title, nHash);
|
||||
}
|
||||
}));
|
||||
}).nThen(function (waitFor) {
|
||||
if (!Utils.Hash.isValidHref(window.location.href)) {
|
||||
@@ -160,6 +173,8 @@ define([
|
||||
});
|
||||
});
|
||||
|
||||
var parsed = Utils.Hash.parsePadUrl(currentPad.href);
|
||||
currentPad.app = parsed.type;
|
||||
if (cfg.getSecrets) {
|
||||
var w = waitFor();
|
||||
// No password for drive, profile and todo
|
||||
@@ -171,15 +186,25 @@ define([
|
||||
});
|
||||
}));
|
||||
} else {
|
||||
var parsed = Utils.Hash.parsePadUrl(window.location.href);
|
||||
var todo = function () {
|
||||
secret = Utils.secret = Utils.Hash.getSecrets(parsed.type, void 0, password);
|
||||
secret = Utils.secret = Utils.Hash.getSecrets(parsed.type, parsed.hash, password);
|
||||
Cryptpad.getShareHashes(secret, waitFor(function (err, h) {
|
||||
hashes = h;
|
||||
// Update the rendered hash and the full hash with the "password" settings
|
||||
if (password && !parsed.hashData.password) {
|
||||
var opts = parsed.getOptions();
|
||||
opts.password = true;
|
||||
|
||||
// Full hash
|
||||
currentPad.href = parsed.getUrl(opts);
|
||||
if (parsed.hashData) {
|
||||
currentPad.hash = parsed.hashData.getHash(opts);
|
||||
}
|
||||
// Rendered (maybe hidden) hash
|
||||
var renderedParsed = Utils.Hash.parsePadUrl(window.location.href);
|
||||
var ohc = window.onhashchange;
|
||||
window.onhashchange = function () {};
|
||||
window.location.hash = h.fileHash || h.editHash || h.viewHash || window.location.hash;
|
||||
window.location.href = renderedParsed.getUrl(opts);
|
||||
window.onhashchange = ohc;
|
||||
ohc({reset: true});
|
||||
}
|
||||
@@ -241,13 +266,13 @@ define([
|
||||
if (parsed.type === "file") {
|
||||
// `isNewChannel` doesn't work for files (not a channel)
|
||||
// `getFileSize` is not adapted to channels because of metadata
|
||||
Cryptpad.getFileSize(window.location.href, password, function (e, size) {
|
||||
Cryptpad.getFileSize(currentPad.href, password, function (e, size) {
|
||||
next(e, size === 0);
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Not a file, so we can use `isNewChannel`
|
||||
Cryptpad.isNewChannel(window.location.href, password, next);
|
||||
Cryptpad.isNewChannel(currentPad.href, password, next);
|
||||
});
|
||||
sframeChan.event("EV_PAD_PASSWORD", cfg);
|
||||
};
|
||||
@@ -257,7 +282,47 @@ define([
|
||||
var passwordCfg = {
|
||||
value: ''
|
||||
};
|
||||
|
||||
// Hidden hash: can't find the channel in our drives: abort
|
||||
var noPadData = function (err) {
|
||||
sframeChan.event("EV_PAD_NODATA", err);
|
||||
};
|
||||
|
||||
var newHref;
|
||||
nThen(function (w) {
|
||||
if (parsed.hashData.key || !parsed.hashData.channel) { return; }
|
||||
var edit = parsed.hashData.mode === 'edit';
|
||||
Cryptpad.getPadDataFromChannel({
|
||||
channel: parsed.hashData.channel,
|
||||
edit: edit,
|
||||
file: parsed.hashData.type === 'file'
|
||||
}, w(function (err, res) {
|
||||
// Error while getting data? abort
|
||||
if (err || !res || res.error) {
|
||||
w.abort();
|
||||
return void noPadData(err || (!res ? 'EINVAL' : res.error));
|
||||
}
|
||||
// No data found? abort
|
||||
if (!Object.keys(res).length) {
|
||||
w.abort();
|
||||
return void noPadData('NO_RESULT');
|
||||
}
|
||||
// Data found but weaker? warn
|
||||
if (edit && !res.href) {
|
||||
newHref = res.roHref;
|
||||
}
|
||||
// We have good data, keep the hash in memory
|
||||
newHref = edit ? res.href : (res.roHref || res.href);
|
||||
}));
|
||||
}).nThen(function (w) {
|
||||
if (newHref) {
|
||||
// Get the options (embed, present, etc.) of the hidden hash
|
||||
// Use the same options in the full hash
|
||||
var opts = parsed.getOptions();
|
||||
parsed = Utils.Hash.parsePadUrl(newHref);
|
||||
currentPad.href = parsed.getUrl(opts);
|
||||
currentPad.hash = parsed.hashData && parsed.hashData.getHash(opts);
|
||||
}
|
||||
Cryptpad.getPadAttribute('title', w(function (err, data) {
|
||||
stored = (!err && typeof (data) === "string");
|
||||
}));
|
||||
@@ -273,7 +338,7 @@ define([
|
||||
if (parsed.type === "file") {
|
||||
// `isNewChannel` doesn't work for files (not a channel)
|
||||
// `getFileSize` is not adapted to channels because of metadata
|
||||
Cryptpad.getFileSize(window.location.href, password, w(function (e, size) {
|
||||
Cryptpad.getFileSize(currentPad.href, password, w(function (e, size) {
|
||||
if (size !== 0) { return void todo(); }
|
||||
// Wrong password or deleted file?
|
||||
askPassword(true, passwordCfg);
|
||||
@@ -281,7 +346,7 @@ define([
|
||||
return;
|
||||
}
|
||||
// Not a file, so we can use `isNewChannel`
|
||||
Cryptpad.isNewChannel(window.location.href, password, w(function(e, isNew) {
|
||||
Cryptpad.isNewChannel(currentPad.href, password, w(function(e, isNew) {
|
||||
if (!isNew) { return void todo(); }
|
||||
if (parsed.hashData.mode === 'view' && (password || !parsed.hashData.password)) {
|
||||
// Error, wrong password stored, the view seed has changed with the password
|
||||
@@ -305,10 +370,12 @@ define([
|
||||
}
|
||||
}).nThen(function (waitFor) {
|
||||
// Check if the pad exists on server
|
||||
if (!window.location.hash) { isNewFile = true; return; }
|
||||
if (!currentPad.hash) { isNewFile = true; return; }
|
||||
|
||||
if (realtime) {
|
||||
Cryptpad.isNewChannel(window.location.href, password, waitFor(function (e, isNew) {
|
||||
// TODO we probably don't need to check again for password-protected pads
|
||||
// (we use isNewChannel to test the password...)
|
||||
Cryptpad.isNewChannel(currentPad.href, password, waitFor(function (e, isNew) {
|
||||
if (e) { return console.error(e); }
|
||||
isNewFile = Boolean(isNew);
|
||||
}));
|
||||
@@ -322,11 +389,12 @@ define([
|
||||
readOnly = false;
|
||||
}
|
||||
Utils.crypto = Utils.Crypto.createEncryptor(Utils.secret.keys);
|
||||
var parsed = Utils.Hash.parsePadUrl(window.location.href);
|
||||
var parsed = Utils.Hash.parsePadUrl(currentPad.href);
|
||||
var burnAfterReading = parsed && parsed.hashData && parsed.hashData.ownerKey;
|
||||
if (!parsed.type) { throw new Error(); }
|
||||
var defaultTitle = Utils.UserObject.getDefaultName(parsed);
|
||||
var edPublic, curvePublic, notifications, isTemplate;
|
||||
var settings = {};
|
||||
var forceCreationScreen = cfg.useCreationScreen &&
|
||||
sessionStorage[Utils.Constants.displayPadCreationScreen];
|
||||
delete sessionStorage[Utils.Constants.displayPadCreationScreen];
|
||||
@@ -340,9 +408,10 @@ define([
|
||||
edPublic = metaObj.priv.edPublic; // needed to create an owned pad
|
||||
curvePublic = metaObj.user.curvePublic;
|
||||
notifications = metaObj.user.notifications;
|
||||
settings = metaObj.priv.settings;
|
||||
}));
|
||||
if (typeof(isTemplate) === "undefined") {
|
||||
Cryptpad.isTemplate(window.location.href, waitFor(function (err, t) {
|
||||
Cryptpad.isTemplate(currentPad.href, waitFor(function (err, t) {
|
||||
if (err) { console.log(err); }
|
||||
isTemplate = t;
|
||||
}));
|
||||
@@ -368,7 +437,7 @@ define([
|
||||
upgradeURL: Cryptpad.upgradeURL
|
||||
},
|
||||
isNewFile: isNewFile,
|
||||
isDeleted: isNewFile && window.location.hash.length > 0,
|
||||
isDeleted: isNewFile && currentPad.hash.length > 0,
|
||||
forceCreationScreen: forceCreationScreen,
|
||||
password: password,
|
||||
channel: secret.channel,
|
||||
@@ -393,7 +462,7 @@ define([
|
||||
additionalPriv.registeredOnly = true;
|
||||
}
|
||||
|
||||
if (['debug', 'profile'].indexOf(parsed.type) !== -1) {
|
||||
if (['debug', 'profile'].indexOf(currentPad.app) !== -1) {
|
||||
additionalPriv.hashes = hashes;
|
||||
}
|
||||
|
||||
@@ -487,7 +556,7 @@ define([
|
||||
});
|
||||
|
||||
sframeChan.on('Q_SET_LOGIN_REDIRECT', function (data, cb) {
|
||||
sessionStorage.redirectTo = window.location.href;
|
||||
sessionStorage.redirectTo = currentPad.href;
|
||||
cb();
|
||||
});
|
||||
|
||||
@@ -570,7 +639,18 @@ define([
|
||||
channel: secret.channel,
|
||||
path: initialPathInDrive // Where to store the pad if we don't have it in our drive
|
||||
};
|
||||
Cryptpad.setPadTitle(data, function (err) {
|
||||
Cryptpad.setPadTitle(data, function (err, obj) {
|
||||
if (!err && !(obj && obj.notStored)) {
|
||||
// No error and the pad was correctly stored
|
||||
// hide the hash
|
||||
var opts = parsed.getOptions();
|
||||
var hash = Utils.Hash.getHiddenHashFromKeys(parsed.type, secret, opts);
|
||||
var useUnsafe = Utils.Util.find(settings, ['security', 'unsafeLinks']);
|
||||
if (useUnsafe === false && window.history && window.history.replaceState) {
|
||||
if (!/^#/.test(hash)) { hash = '#' + hash; }
|
||||
window.history.replaceState({}, window.document.title, hash);
|
||||
}
|
||||
}
|
||||
cb({error: err});
|
||||
});
|
||||
});
|
||||
@@ -580,6 +660,9 @@ define([
|
||||
});
|
||||
|
||||
sframeChan.on('EV_SET_HASH', function (hash) {
|
||||
// In this case, we want to set the hash for the next page reload
|
||||
// This hash is a category for the sidebar layout apps
|
||||
// No need to store it in memory
|
||||
window.location.hash = hash;
|
||||
});
|
||||
|
||||
@@ -595,6 +678,17 @@ define([
|
||||
forceSave: true
|
||||
};
|
||||
Cryptpad.setPadTitle(data, function (err) {
|
||||
if (!err && !(obj && obj.notStored)) {
|
||||
// No error and the pad was correctly stored
|
||||
// hide the hash
|
||||
var opts = parsed.getOptions();
|
||||
var hash = Utils.Hash.getHiddenHashFromKeys(parsed.type, secret, opts);
|
||||
var useUnsafe = Utils.Util.find(settings, ['security', 'unsafeLinks']);
|
||||
if (useUnsafe === false && window.history && window.history.replaceState) {
|
||||
if (!/^#/.test(hash)) { hash = '#' + hash; }
|
||||
window.history.replaceState({}, window.document.title, hash);
|
||||
}
|
||||
}
|
||||
cb({error: err});
|
||||
});
|
||||
});
|
||||
@@ -801,15 +895,19 @@ define([
|
||||
|
||||
// Present mode URL
|
||||
sframeChan.on('Q_PRESENT_URL_GET_VALUE', function (data, cb) {
|
||||
var parsed = Utils.Hash.parsePadUrl(window.location.href);
|
||||
var parsed = Utils.Hash.parsePadUrl(currentPad.href);
|
||||
cb(parsed.hashData && parsed.hashData.present);
|
||||
});
|
||||
sframeChan.on('EV_PRESENT_URL_SET_VALUE', function (data) {
|
||||
var parsed = Utils.Hash.parsePadUrl(window.location.href);
|
||||
window.location.href = parsed.getUrl({
|
||||
embed: parsed.hashData.embed,
|
||||
present: data
|
||||
});
|
||||
// Update the rendered hash and the full hash with the "present" settings
|
||||
var opts = parsed.getOptions();
|
||||
opts.present = data;
|
||||
// Full hash
|
||||
currentPad.href = parsed.getUrl(opts);
|
||||
if (parsed.hashData) { currentPad.hash = parsed.hashData.getHash(opts); }
|
||||
// Rendered (maybe hidden) hash
|
||||
var hiddenParsed = Utils.Hash.parsePadUrl(window.location.href);
|
||||
window.location.href = hiddenParsed.getUrl(opts);
|
||||
});
|
||||
|
||||
|
||||
@@ -1011,7 +1109,7 @@ define([
|
||||
});
|
||||
|
||||
sframeChan.on('Q_BLOB_PASSWORD_CHANGE', function (data, cb) {
|
||||
data.href = data.href || window.location.href;
|
||||
data.href = data.href || currentPad.href;
|
||||
var onPending = function (cb) {
|
||||
sframeChan.query('Q_BLOB_PASSWORD_CHANGE_PENDING', null, function (err, obj) {
|
||||
if (obj && obj.cancel) { cb(); }
|
||||
@@ -1027,12 +1125,12 @@ define([
|
||||
});
|
||||
|
||||
sframeChan.on('Q_OO_PASSWORD_CHANGE', function (data, cb) {
|
||||
data.href = data.href || window.location.href;
|
||||
data.href = data.href;
|
||||
Cryptpad.changeOOPassword(data, cb);
|
||||
});
|
||||
|
||||
sframeChan.on('Q_PAD_PASSWORD_CHANGE', function (data, cb) {
|
||||
data.href = data.href || window.location.href;
|
||||
data.href = data.href;
|
||||
Cryptpad.changePadPassword(Cryptget, Crypto, data, cb);
|
||||
});
|
||||
|
||||
@@ -1227,14 +1325,26 @@ define([
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
|
||||
// If our channel was deleted from all of our drives, sitch back to full hash
|
||||
// in the address bar
|
||||
Cryptpad.padRpc.onChannelDeleted.reg(function (channel) {
|
||||
if (channel !== secret.channel) { return; }
|
||||
var ohc = window.onhashchange;
|
||||
window.onhashchange = function () {};
|
||||
window.location.href = currentPad.href;
|
||||
window.onhashchange = ohc;
|
||||
ohc({reset: true});
|
||||
});
|
||||
|
||||
// Join the netflux channel
|
||||
var rtStarted = false;
|
||||
var startRealtime = function (rtConfig) {
|
||||
rtConfig = rtConfig || {};
|
||||
rtStarted = true;
|
||||
|
||||
var replaceHash = function (hash) {
|
||||
// The pad has just been created but is not stored yet. We'll switch
|
||||
// to hidden hash once the pad is stored
|
||||
if (window.history && window.history.replaceState) {
|
||||
if (!/^#/.test(hash)) { hash = '#' + hash; }
|
||||
window.history.replaceState({}, window.document.title, hash);
|
||||
@@ -1250,7 +1360,7 @@ define([
|
||||
Cryptpad.padRpc.onReadyEvent.reg(function () {
|
||||
Cryptpad.burnPad({
|
||||
password: password,
|
||||
href: window.location.href,
|
||||
href: currentPad.href,
|
||||
channel: secret.channel,
|
||||
ownerKey: burnAfterReading
|
||||
});
|
||||
@@ -1265,7 +1375,7 @@ define([
|
||||
readOnly: readOnly,
|
||||
crypto: Crypto.createEncryptor(secret.keys),
|
||||
onConnect: function () {
|
||||
if (window.location.hash && window.location.hash !== '#') {
|
||||
if (currentPad.hash && currentPad.hash !== '#') {
|
||||
/*window.location = parsed.getUrl({
|
||||
present: parsed.hashData.present,
|
||||
embed: parsed.hashData.embed
|
||||
@@ -1278,11 +1388,11 @@ define([
|
||||
};
|
||||
|
||||
nThen(function (waitFor) {
|
||||
if (isNewFile && cfg.owned && !window.location.hash) {
|
||||
if (isNewFile && cfg.owned && !currentPad.hash) {
|
||||
Cryptpad.getMetadata(waitFor(function (err, m) {
|
||||
cpNfCfg.owners = [m.priv.edPublic];
|
||||
}));
|
||||
} else if (isNewFile && !cfg.useCreationScreen && window.location.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");
|
||||
sframeChan.onReady(function () {
|
||||
sframeChan.query("EV_LOADING_ERROR", "DELETED");
|
||||
@@ -1309,11 +1419,13 @@ define([
|
||||
var ohc = window.onhashchange;
|
||||
window.onhashchange = function () {};
|
||||
window.location.hash = newHash;
|
||||
currentPad.hash = newHash;
|
||||
currentPad.href = '/' + parsed.type + '/#' + newHash;
|
||||
window.onhashchange = ohc;
|
||||
ohc({reset: true});
|
||||
|
||||
// Update metadata values and send new metadata inside
|
||||
parsed = Utils.Hash.parsePadUrl(window.location.href);
|
||||
parsed = Utils.Hash.parsePadUrl(currentPad.href);
|
||||
defaultTitle = Utils.UserObject.getDefaultName(parsed);
|
||||
hashes = Utils.Hash.getHashes(secret);
|
||||
readOnly = false;
|
||||
|
||||
@@ -83,6 +83,9 @@ define([
|
||||
};
|
||||
|
||||
// UI
|
||||
window.CryptPad_UI = UI;
|
||||
window.CryptPad_UIElements = UIElements;
|
||||
window.CryptPad_common = funcs;
|
||||
funcs.createUserAdminMenu = callWithCommon(UIElements.createUserAdminMenu);
|
||||
funcs.initFilePicker = callWithCommon(UIElements.initFilePicker);
|
||||
funcs.openFilePicker = callWithCommon(UIElements.openFilePicker);
|
||||
@@ -139,7 +142,13 @@ define([
|
||||
if (!$mt || !$mt.is('media-tag')) { return; }
|
||||
var chanStr = $mt.attr('src');
|
||||
var keyStr = $mt.attr('data-crypto-key');
|
||||
var channel = chanStr.replace(/\/blob\/[0-9a-f]{2}\//i, '');
|
||||
// Remove origin
|
||||
var a = document.createElement('a');
|
||||
a.href = chanStr;
|
||||
var src = a.pathname;
|
||||
// Get channel id
|
||||
var channel = src.replace(/\/blob\/[0-9a-f]{2}\//i, '');
|
||||
// Get key
|
||||
var key = keyStr.replace(/cryptpad:/i, '');
|
||||
var metadata = $mt[0]._mediaObject._blob.metadata;
|
||||
ctx.sframeChan.query('Q_IMPORT_MEDIATAG', {
|
||||
@@ -603,6 +612,10 @@ define([
|
||||
|
||||
UI.addTooltips();
|
||||
|
||||
ctx.sframeChan.on("EV_PAD_NODATA", function () {
|
||||
UI.errorLoadingScreen(Messages.safeLinks_error);
|
||||
});
|
||||
|
||||
ctx.sframeChan.on("EV_PAD_PASSWORD", function (cfg) {
|
||||
UIElements.displayPasswordPrompt(funcs, cfg);
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"media": "Multimèdia",
|
||||
"todo": "Tasques",
|
||||
"contacts": "Contactes",
|
||||
"sheet": "Full (Beta)",
|
||||
"sheet": "Full de càlcul",
|
||||
"teams": "Equips"
|
||||
},
|
||||
"button_newpad": "Nou document",
|
||||
@@ -34,7 +34,7 @@
|
||||
"inactiveError": "Donada la seva inactivitat, aquest document s'ha esborrat. Premeu Esc per crear un nou document.",
|
||||
"chainpadError": "Hi ha hagut un error crític mentre s'actualitzava el vostre contingut. Aquesta pàgina es manté en mode només de lectura per assegurar que no perdreu el que ja heu fet.<br>Premeu <em>Esc</em> per continuar veient aquest document o torneu a carregar la pàgina per provar de continuar editant-lo.",
|
||||
"invalidHashError": "El document que heu demanat té una adreça URL no vàlida.",
|
||||
"errorCopy": " Encara podeu copiar el contingut en una altra ubicació prement <em>Esc</em>.<br>Un cop deixeu aquesta pàgina, desapareixerà per sempre!",
|
||||
"errorCopy": " Encara podeu accedir al contingut prement <em>Esc</em>.<br>Un cop tanqueu aquesta finestra no hi podreu tornar a accedir.",
|
||||
"errorRedirectToHome": "Premeu <em>Esc</em> per tornar al vostre CryptDrive.",
|
||||
"newVersionError": "Hi ha una nova versió disponible de CryptPad.<br><a href='#'>Torneu a carregar</a> la pàgina per utilitzar la versió nova o premeu Esc per accedir al vostre contingut en mode <b>fora de línia</b>.",
|
||||
"loading": "Carregant...",
|
||||
@@ -181,7 +181,6 @@
|
||||
"okButton": "D'acord (Enter)",
|
||||
"cancel": "Cancel·la",
|
||||
"cancelButton": "Cancel·la (Esc)",
|
||||
"doNotAskAgain": "No ho preguntis més (Esc)",
|
||||
"show_help_button": "Mostra l'ajuda",
|
||||
"hide_help_button": "Amaga l'ajuda",
|
||||
"help_button": "Ajuda",
|
||||
@@ -299,7 +298,7 @@
|
||||
"contacts_confirmRemoveHistory": "De debò voleu suprimir permanentment el vostre historial de xat? Les dades no es podran restaurar",
|
||||
"contacts_removeHistoryServerError": "Hi ha hagut un error mentre es suprimia el vostre historial del xat. Torneu-ho a provar",
|
||||
"contacts_fetchHistory": "Recupera l'historial antic",
|
||||
"contacts_friends": "Amistats",
|
||||
"contacts_friends": "Contactes",
|
||||
"contacts_rooms": "Sales",
|
||||
"contacts_leaveRoom": "Deixa aquesta sala",
|
||||
"contacts_online": "En aquesta sala hi ha una altra persona en línia",
|
||||
@@ -531,5 +530,91 @@
|
||||
"settings_padSpellcheckTitle": "Correcció ortogràfica",
|
||||
"settings_padSpellcheckHint": "Aquesta opció us permet habilitar la correcció ortogràfica als documents de text. Les errades es subratllaran en vermell i haureu de mantenir apretada la tecla Ctrl o Meta mentre cliqueu el botó dret per veure les opcions correctes.",
|
||||
"settings_padSpellcheckLabel": "Activa la correcció ortogràfica",
|
||||
"settings_creationSkip": "Salta la pantalla de creació de document"
|
||||
"settings_creationSkip": "Salta la pantalla de creació de document",
|
||||
"settings_creationSkipHint": "La pantalla de creació de documents ofereix noves opcions, donant-vos més control sobre les vostres dades. Tot i això, pot alentir una mica la feina afegint un pas addicional i, per això, teniu l'opció de saltar aquesta pantalla i utilitzar les opcions per defecte que hi ha seleccionades.",
|
||||
"settings_creationSkipTrue": "Salta",
|
||||
"settings_creationSkipFalse": "Mostra",
|
||||
"settings_templateSkip": "Salta la finestra de selecció de plantilla",
|
||||
"settings_templateSkipHint": "Quan genereu un document nou buit, si teniu desades plantilles per aquest tipus de document, apareix una finestra preguntant-vos si voleu utilitzar una plantilla. Aquí podeu triar si no voleu veure mai més la finestra i no utilitzar una plantilla.",
|
||||
"settings_ownDriveTitle": "Habilita les darreres funcionalitats del compte",
|
||||
"settings_ownDriveHint": "Per raons tècniques, els comptes antics no tenen accés a totes les funcionalitats noves. Si feu una actualització a un compte nou, preparareu el vostre CryptDrive per les properes funcionalitats sense interrompre la vostra activitat habitual.",
|
||||
"settings_ownDriveButton": "Milloreu el vostre compte",
|
||||
"settings_ownDriveConfirm": "Millorar el vostre compte porta una estona. Necessitareu tornar-vos a connectar en tots els vostres dispositius. Segur que ho voleu fer?",
|
||||
"settings_ownDrivePending": "El vostre compte s'està posant al dia. No tanqueu ni torneu a carregar aquesta pàgina fins que el procés hagi acabat.",
|
||||
"settings_changePasswordTitle": "Canvieu la contrasenya",
|
||||
"settings_changePasswordHint": "Canvieu la contrasenya del vostre compte. Introduïu la contrasenya actual i confirmeu la nova escrivint-la dos cops.<br><b>Si l'oblideu, no podem recuperar la vostra contrasenya, aneu amb molt de compte!</b>",
|
||||
"settings_changePasswordButton": "Canvia la contrasenya",
|
||||
"settings_changePasswordCurrent": "Contrasenya actual",
|
||||
"settings_changePasswordNew": "Nova contrasenya",
|
||||
"settings_changePasswordNewConfirm": "Confirma la nova contrasenya",
|
||||
"settings_changePasswordConfirm": "Segur que voleu canviar la contrasenya? Necessitareu tornar-vos a connectar en tots els dispositius.",
|
||||
"settings_changePasswordError": "Hi ha hagut una errada inesperada. Si no podeu iniciar la sessió o canviar la contrasenya, contacteu l'administració de CryptPad.",
|
||||
"settings_changePasswordPending": "S'està actualitzant la contrasenya. Si us plau, no tanqueu ni carregueu de nou la pàgina fins que el procés s'hagi acabat.",
|
||||
"settings_changePasswordNewPasswordSameAsOld": "La contrasenya nova cal que sigui diferent de l'actual.",
|
||||
"settings_cursorColorTitle": "Color del cursor",
|
||||
"settings_cursorColorHint": "Canvieu el color associat al vostre compte en els documents col·laboratius.",
|
||||
"settings_cursorShareTitle": "Comparteix la posició del meu cursor",
|
||||
"settings_cursorShareHint": "Podeu decidir si, als documents col·laboratius, voleu que la resta de persones vegin el vostre cursor.",
|
||||
"settings_cursorShareLabel": "Comparteix la posició",
|
||||
"settings_cursorShowTitle": "Mostra la posició del cursor de la resta",
|
||||
"settings_cursorShowHint": "Podeu triar si, als documents col·laboratius, voleu veure el cursor de les altres persones.",
|
||||
"settings_cursorShowLabel": "Mostra els cursors",
|
||||
"upload_title": "Carrega fitxer",
|
||||
"upload_type": "Tipus",
|
||||
"upload_modal_title": "Opcions per carregar fitxers",
|
||||
"upload_modal_filename": "Nom del fitxer (extensió <em>{0}</em> afegit automàticament)",
|
||||
"upload_modal_owner": "Fitxer propi",
|
||||
"uploadFolder_modal_title": "Opcions per carregar carpetes",
|
||||
"uploadFolder_modal_filesPassword": "Fitxers de contrasenya",
|
||||
"uploadFolder_modal_owner": "Fitxers propis",
|
||||
"uploadFolder_modal_forceSave": "Deseu fitxers al vostre CryptDrive",
|
||||
"upload_serverError": "Errada interna: ara mateix és impossible carregar el fitxer.",
|
||||
"upload_uploadPending": "Ja teniu una càrrega en marxa. Voleu cancel·lar-la i carregar aquest altre fitxer?",
|
||||
"upload_success": "El fitxer ({0}) ha estat carregat correctament i afegit al vostre CryptDrive.",
|
||||
"upload_notEnoughSpace": "No hi ha prou espai al CryptDrive per aquest fitxer.",
|
||||
"upload_notEnoughSpaceBrief": "No hi ha prou espai",
|
||||
"upload_tooLarge": "Aquest fitxer supera la mida màxima permesa.",
|
||||
"upload_tooLargeBrief": "El fitxer és massa gran",
|
||||
"upload_choose": "Trieu un fitxer",
|
||||
"upload_pending": "Pendent",
|
||||
"upload_cancelled": "Cancel·lat",
|
||||
"upload_name": "Nom del fitxer",
|
||||
"upload_size": "Mida",
|
||||
"upload_progress": "Procés",
|
||||
"upload_mustLogin": "Cal que inicieu la sessió per carregar un fitxer",
|
||||
"upload_up": "Carrega",
|
||||
"download_button": "Desxifra i descarrega",
|
||||
"download_mt_button": "Descarrega",
|
||||
"home_ngi": "Guanyador del premi NGI",
|
||||
"home_host_agpl": "CryptPad es distribueix sota els termes de la llicència de programari AGPL3",
|
||||
"home_host": "Aquesta és una instància comunitària independent de CryptPad. El codi font està disponible<a href=\"https://github.com/xwiki-labs/cryptpad\" target=\"_blank\" rel=\"noreferrer noopener\">a GitHub</a>.",
|
||||
"home_product": "CryptPad és una alternativa, respectuosa amb la privacitat, a les utilitats d'oficina i els serveis al núvol. Tot el contingut desat a CryptPad es xifra abans de ser enviat, això vol dir que ningú pot accedir a les vostres dades sense que li doneu les claus (fins i tot nosaltres).",
|
||||
"mdToolbar_toc": "Taula de continguts",
|
||||
"mdToolbar_code": "Codi",
|
||||
"mdToolbar_check": "Llista de tasques",
|
||||
"mdToolbar_list": "Llista de vinyetes",
|
||||
"mdToolbar_nlist": "Llista ordenada",
|
||||
"mdToolbar_quote": "Cita",
|
||||
"mdToolbar_link": "Enllaç",
|
||||
"mdToolbar_heading": "Capçalera",
|
||||
"mdToolbar_strikethrough": "Tatxat",
|
||||
"mdToolbar_italic": "Cursiva",
|
||||
"mdToolbar_bold": "Negreta",
|
||||
"mdToolbar_tutorial": "https://ca.wikipedia.org/wiki/Markdown",
|
||||
"mdToolbar_help": "Suport",
|
||||
"mdToolbar_defaultText": "El vostre text",
|
||||
"mdToolbar_button": "Mostra o amaga la barra d'eines de Markdown",
|
||||
"pad_base64": "Aquest document conté imatges emmagatzemades de forma ineficient. Aquestes imatges augmenten significativament la mida del document al CryptDrive i fa que la càrrega sigui més lenta. Podeu migrar les imatges a un format diferent perquè es guardin per separat al CryptDrive. Voleu migrar ara aquestes imatges?",
|
||||
"todo_markAsIncompleteTitle": "Marca la tasca com incompleta",
|
||||
"pad_hideToolbar": "Amaga la barra d'eines",
|
||||
"pad_showToolbar": "Mostra la barra d'eines",
|
||||
"todo_removeTaskTitle": "Elimina la tasca del llistat",
|
||||
"todo_markAsCompleteTitle": "Marca la tasca com a completada",
|
||||
"todo_newTodoNameTitle": "Afegiu la tasca al llistat",
|
||||
"todo_newTodoNamePlaceholder": "Descriviu la tasca...",
|
||||
"todo_title": "CryptTasques",
|
||||
"download_step2": "Desxifrant",
|
||||
"download_step1": "Descarregant",
|
||||
"download_dl": "Descarrega",
|
||||
"download_resourceNotAvailable": "El recurs sol·licitat no estava disponible... Premeu Esc per continuar."
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"media": "Medien",
|
||||
"todo": "Aufgaben",
|
||||
"contacts": "Kontakte",
|
||||
"sheet": "Tabelle (Beta)",
|
||||
"sheet": "Tabelle",
|
||||
"teams": "Teams"
|
||||
},
|
||||
"button_newpad": "Neues Rich-Text-Pad",
|
||||
@@ -92,7 +92,7 @@
|
||||
"exportButtonTitle": "Exportiere dieses Pad in eine lokale Datei",
|
||||
"exportPrompt": "Wie möchtest du die Datei nennen?",
|
||||
"changeNamePrompt": "Ändere deinen Namen (oder lasse dieses Feld leer, um anonym zu bleiben): ",
|
||||
"user_rename": "Bearbeite deinen Anzeigename",
|
||||
"user_rename": "Anzeigename ändern",
|
||||
"user_displayName": "Anzeigename",
|
||||
"user_accountName": "Kontoname",
|
||||
"clickToEdit": "Zum Bearbeiten klicken",
|
||||
@@ -179,7 +179,6 @@
|
||||
"okButton": "OK (Enter)",
|
||||
"cancel": "Abbrechen",
|
||||
"cancelButton": "Abbrechen (Esc)",
|
||||
"doNotAskAgain": "Nicht mehr fragen (Esc)",
|
||||
"show_help_button": "Hilfe anzeigen",
|
||||
"hide_help_button": "Hilfe verbergen",
|
||||
"help_button": "Hilfe",
|
||||
@@ -275,11 +274,11 @@
|
||||
"profile_description": "Beschreibung",
|
||||
"profile_fieldSaved": "Neuer Wert gespeichert: {0}",
|
||||
"profile_viewMyProfile": "Mein Profil anzeigen",
|
||||
"userlist_addAsFriendTitle": "Benutzer \"{0}\" eine Freundschaftsanfrage senden",
|
||||
"userlist_addAsFriendTitle": "Benutzer \"{0}\" eine Kontaktanfrage senden",
|
||||
"contacts_title": "Kontakte",
|
||||
"contacts_addError": "Fehler bei dem Hinzufügen des Kontakts zur Liste",
|
||||
"contacts_added": "Verbindungseinladung angenommen.",
|
||||
"contacts_rejected": "Verbindungseinladung abgelehnt",
|
||||
"contacts_added": "Kontaktanfrage akzeptiert.",
|
||||
"contacts_rejected": "Kontaktanfrage abgelehnt",
|
||||
"contacts_request": "Benutzer <em>{0}</em> möchte dich als Kontakt hinzufügen. <b>Annehmen<b>?",
|
||||
"contacts_send": "Senden",
|
||||
"contacts_remove": "Diesen Kontakt entfernen",
|
||||
@@ -515,8 +514,8 @@
|
||||
"settings_creationSkipFalse": "Anzeigen",
|
||||
"settings_templateSkip": "Wahl der Vorlage überspringen",
|
||||
"settings_templateSkipHint": "Wenn du ein neues Pad erstellst und passende Vorlagen vorhanden sind, erscheint ein Dialog zur Auswahl einer Vorlage. Hier kannst du diesen Dialog überspringen und somit keine Vorlage verwenden.",
|
||||
"settings_ownDriveTitle": "Aktiviere die neuesten Funktionen für dein Konto",
|
||||
"settings_ownDriveHint": "Aus technischen Gründen sind nicht alle neue Funktionen für ältere Konten verfügbar. Ein kostenloses Upgrade wird dein CryptDrive für zukünftige Funktionen vorbereiten, ohne deine Arbeit zu stören.",
|
||||
"settings_ownDriveTitle": "Account aktualisieren",
|
||||
"settings_ownDriveHint": "Aus technischen Gründen sind nicht alle neue Funktionen für ältere Konten verfügbar. Eine kostenlose Aktualisierung wird die neuen Funktionen aktivieren und dein CryptDrive für zukünftige Aktualisierungen vorbereiten.",
|
||||
"settings_ownDriveButton": "Upgrade deines Kontos",
|
||||
"settings_ownDriveConfirm": "Das Upgrade deines Kontos kann einige Zeit dauern. Du wirst dich auf allen Geräten neu einloggen müssen. Bist du sicher?",
|
||||
"settings_ownDrivePending": "Das Upgrade deines Kontos läuft. Bitte schließe die Seite nicht und lade sie nicht neu, bis dieser Vorgang abgeschlossen ist.",
|
||||
@@ -664,7 +663,7 @@
|
||||
"features_f_social": "Soziale Anwendungen",
|
||||
"features_f_social_note": "Ein Profil erstellen, ein Profilbild verwenden, mit Kontakten chatten",
|
||||
"features_f_file1": "Dateien hochladen und teilen",
|
||||
"features_f_file1_note": "Dateien mit Freunden teilen oder sie in Dokumenten einbetten",
|
||||
"features_f_file1_note": "Dateien mit Kontakten teilen oder sie in Dokumenten einbetten",
|
||||
"features_f_storage1": "Langfristige Speicherung (50 MB)",
|
||||
"features_f_storage1_note": "Dateien in deinem CryptDrive werden nicht wegen Inaktivität gelöscht",
|
||||
"features_f_register": "Registrieren (kostenlos)",
|
||||
@@ -767,7 +766,7 @@
|
||||
"a": "Registrierte Benutzer können Funktionen verwenden, die anonyme Nutzer nicht verwenden können. Es gibt <a href='/features.html' target='_blank'>hier</a> eine entsprechende Übersicht."
|
||||
},
|
||||
"share": {
|
||||
"q": "Wie kann ich den Zugang zu einem verschlüsselten Pad mit Freunden teilen?",
|
||||
"q": "Wie kann ich den Zugang zu einem verschlüsselten Pad mit Kontakten teilen?",
|
||||
"a": "CryptPad fügt den geheimen Schlüssel deines Pad nach dem Zeichen <em>#</em> zur URL hinzu. Alles, was nach diesem Zeichen kommt, wird nicht zum Server gesendet. Also haben wir nie Zugang zu deinen Schlüsseln. Wenn du den Link zu einem Pad teilst, teilst du auch die Fähigkeit zum Lesen und zum Bearbeiten."
|
||||
},
|
||||
"remove": {
|
||||
@@ -799,7 +798,7 @@
|
||||
"title": "Andere Fragen",
|
||||
"pay": {
|
||||
"q": "Wieso soll ich zahlen, wenn so viele Funktionen sowieso kostenfrei sind?",
|
||||
"a": "Wir geben Unterstützern zusätzlichen Speicherplatz sowie die Möglichkeit, die Speicherplatzbegrenzung ihrer Freunde zu erhöhen (<a href='https://accounts.cryptpad.fr/#/faq' target='_blank'>erfahre mehr</a>).<br><br> Über diese diese kurzfristigen Vorteile hinaus kannst du, wenn du ein Premiumangebot annimmst, die aktive Weiterentwicklung von CryptPad fördern. Das beinhaltet, Fehler zu beseitigen, neue Funktionen zu umzusetzen und Installationen von CryptPad auf eigenen Servern zu erleichtern. Zusätzlich hilfst du, anderen Anbietern zu beweisen, dass Leute datenschutzfreundliche Technologien unterstützen. Wir hoffen, dass Geschäftsmodelle, die auf dem Verkauf von Benutzerdaten basieren, letztendlich der Vergangenheit angehören werden.<br><br>Außerdem glauben wir, dass es gut ist, die Funktionen von CryptPad kostenfrei anzubieten. Denn jeder verdient persönlichen Datenschutz und nicht nur Personen mit hohem Einkommen. Durch deine Unterstützung hilfst du uns, zu ermöglichen, dass auch Menschen mit geringerem Einkommen diese grundlegenden Funktionen genießen können, ohne dass ein Preisetikett daran klebt."
|
||||
"a": "Wir geben Unterstützern zusätzlichen Speicherplatz sowie die Möglichkeit, die Speicherplatzbegrenzung ihrer Kontakte zu erhöhen (<a href='https://accounts.cryptpad.fr/#/faq' target='_blank'>erfahre mehr</a>).<br><br>Über diese diese kurzfristigen Vorteile hinaus kannst du, wenn du ein Premiumangebot annimmst, die aktive Weiterentwicklung von CryptPad fördern. Das beinhaltet, Fehler zu beseitigen, neue Funktionen zu umzusetzen und Installationen von CryptPad auf eigenen Servern zu erleichtern. Zusätzlich hilfst du, anderen Anbietern zu beweisen, dass Leute datenschutzfreundliche Technologien unterstützen. Wir hoffen, dass Geschäftsmodelle, die auf dem Verkauf von Benutzerdaten basieren, letztendlich der Vergangenheit angehören werden.<br><br>Außerdem glauben wir, dass es gut ist, die Funktionen von CryptPad kostenfrei anzubieten. Denn jeder verdient persönlichen Datenschutz und nicht nur Personen mit hohem Einkommen. Durch deine Unterstützung hilfst du uns, zu ermöglichen, dass auch Menschen mit geringerem Einkommen diese grundlegenden Funktionen genießen können, ohne dass ein Preisetikett daran klebt."
|
||||
},
|
||||
"goal": {
|
||||
"q": "Was ist euer Ziel?",
|
||||
@@ -860,7 +859,7 @@
|
||||
"colors": "Ändere Text- und Hintergrundfarbe mit den Schaltflächen <span class=\"fa fa-i-cursor\"></span> und <span class=\"fa fa-square\"></span>"
|
||||
},
|
||||
"poll": {
|
||||
"decisions": "Treffe Entscheidungen gemeinsam mit deinen Bekannten",
|
||||
"decisions": "Treffe Entscheidungen gemeinsam mit deinen Kontakten",
|
||||
"options": "Mache Vorschläge und teile deine Präferenzen mit",
|
||||
"choices": "Klicke in die Zellen in deiner Spalte, um zwischen ja (<strong>✔</strong>), viellecht (<strong>~</strong>), oder nein (<strong>✖</strong>) zu wählen",
|
||||
"submit": "Klicke auf <strong>Senden</strong>, damit deine Auswahl für andere sichtbar wird"
|
||||
@@ -878,7 +877,7 @@
|
||||
},
|
||||
"driveReadmeTitle": "Was ist CryptPad?",
|
||||
"readme_welcome": "Willkommen zu CryptPad!",
|
||||
"readme_p1": "Willkommen zu CryptPad, hier kannst du deine Notizen aufschreiben, allein oder mit Bekannten.",
|
||||
"readme_p1": "Willkommen zu CryptPad, hier kannst du deine Notizen aufschreiben, allein oder mit Kontakten.",
|
||||
"readme_p2": "Dieses Dokument gibt dir einen kurzen Überblick, wie du CryptPad verwenden kannst, um Notizen zu schreiben, sie zu organisieren und mit anderen zusammen zu arbeiten.",
|
||||
"readme_cat1": "Lerne dein CryptDrive kennen",
|
||||
"readme_cat1_l1": "Ein Pad erstellen: Klicke in deinem CryptDrive auf {0} und dann auf {1}.",
|
||||
@@ -911,7 +910,7 @@
|
||||
"feedback_about": "Wenn du das liest, fragst du dich wahrscheinlich, weshalb dein Browser bei der der Ausführung mancher Aktionen Anfragen an Webseiten sendet",
|
||||
"feedback_privacy": "Wir respektieren deine Datenschutz, aber gleichzeitig wollen wir, dass die Benutzung von CryptPad sehr leicht ist. Deshalb wollen wir erfahren, welche Funktion am wichtigsten für unsere Benutzer ist, indem wir diese mit einer genauen Parameterbeschreibung anfordern.",
|
||||
"feedback_optout": "Wenn du das nicht möchtest, kannst du es in <a href='/settings/'>deinen Einstellungen</a> deaktivieren",
|
||||
"creation_404": "Dieses Pad existiert nicht mehr. Benutze das folgende Formular, um ein neues Pad zu gestalten.",
|
||||
"creation_404": "Dieses Pad existiert nicht mehr. Benutze das folgende Formular, um ein neues Pad zu erstellen.",
|
||||
"creation_ownedTitle": "Pad-Typ",
|
||||
"creation_owned": "Eigenes Pad",
|
||||
"creation_ownedTrue": "Eigenes Pad",
|
||||
@@ -931,7 +930,6 @@
|
||||
"creation_noTemplate": "Keine Vorlage",
|
||||
"creation_newTemplate": "Neue Vorlage",
|
||||
"creation_create": "Erstellen",
|
||||
"creation_saveSettings": "Dieses Dialog nicht mehr anzeigen",
|
||||
"creation_settings": "Mehr Einstellungen anzeigen",
|
||||
"creation_rememberHelp": "Gehe zu deinen Einstellungen, um diese Auswahl zurückzusetzen",
|
||||
"creation_owners": "Eigentümer",
|
||||
@@ -998,7 +996,6 @@
|
||||
"crowdfunding_popup_text": "<h3>Wir brauchen deine Hilfe!</h3>Um sicherzustellen, dass CryptPad weiter aktiv entwickelt wird, unterstütze bitte das Projekt über die <a href=\"https://opencollective.com/cryptpad\">OpenCollective Seite</a>, wo du unsere <b>Roadmap</b> und <b>Funding-Ziele</b> lesen kannst.",
|
||||
"crowdfunding_popup_yes": "OpenCollective besuchen",
|
||||
"crowdfunding_popup_no": "Nicht jetzt",
|
||||
"crowdfunding_popup_never": "Nicht mehr darum bitten",
|
||||
"invalidHashError": "Das angeforderte Dokument hat eine ungültige URL.",
|
||||
"oo_cantUpload": "Das Hochladen von Dateien ist nicht erlaubt, während andere Nutzer anwesend sind.",
|
||||
"oo_uploaded": "Das Hochladen wurde abgeschlossen. Klicke auf OK zum Neuladen der Seite oder auf Abbrechen zum Fortfahren im schreibgeschützten Modus.",
|
||||
@@ -1053,18 +1050,17 @@
|
||||
"friendRequest_later": "Später entscheiden",
|
||||
"friendRequest_accept": "Akzeptieren (Enter)",
|
||||
"friendRequest_decline": "Ablehnen",
|
||||
"friendRequest_declined": "<b>{0}</b> hat deine Freundschaftsanfrage abgelehnt",
|
||||
"friendRequest_accepted": "<b>{0}</b> hat deine Freundschaftsanfrage akzeptiert",
|
||||
"friendRequest_received": "<b>{0}</b> möchte mit dir befreundet sein",
|
||||
"friendRequest_notification": "<b>{0}</b> hat dir eine Freundschaftsanfrage geschickt",
|
||||
"friendRequest_declined": "<b>{0}</b> hat deine Kontaktanfrage abgelehnt",
|
||||
"friendRequest_accepted": "<b>{0}</b> hat deine Kontaktanfrage akzeptiert",
|
||||
"friendRequest_received": "<b>{0}</b> möchte dein Kontakt sein",
|
||||
"friendRequest_notification": "<b>{0}</b> hat dir eine Kontaktanfrage geschickt",
|
||||
"notifications_empty": "Keine Benachrichtigungen verfügbar",
|
||||
"notifications_title": "Du hast ungelesene Benachrichtigungen",
|
||||
"profile_addDescription": "Beschreibung hinzufügen",
|
||||
"profile_editDescription": "Deine Beschreibung bearbeiten",
|
||||
"profile_addLink": "Link zu deiner Website hinzufügen",
|
||||
"profile_info": "Andere Nutzer können dein Profil finden, indem sie auf deinen Avatar in der Benutzerliste eines Dokumentes klicken.",
|
||||
"profile_friendRequestSent": "Freundschaftsanfrage gesendet...",
|
||||
"profile_friend": "{0} ist mit dir befreundet",
|
||||
"profile_friendRequestSent": "Kontaktanfrage gesendet...",
|
||||
"notification_padShared": "{0} hat ein Pad mit dir geteilt: <b>{1}</b>",
|
||||
"notification_fileShared": "{0} hat eine Datei mit dir geteilt: <b>{1}</b>",
|
||||
"notification_folderShared": "{0} hat einen Ordner mit dir geteilt: <b>{1}</b>",
|
||||
@@ -1075,7 +1071,7 @@
|
||||
"share_deselectAll": "Alle abwählen",
|
||||
"notifications_dismiss": "Verbergen",
|
||||
"fm_info_sharedFolderHistory": "Dies ist nur der Verlauf deines geteilten Ordners: <b>{0}</b><br/>Dein CryptDrive bleibt beim Navigieren im Nur-Lesen-Modus.",
|
||||
"share_description": "Wähle aus, was du teilen möchtest. Dir wird dann ein entsprechender Link anzeigt. Du kannst es auch direkt an deine Freunde in CryptPad senden.",
|
||||
"share_description": "Wähle aus, was du teilen möchtest. Dir wird dann ein entsprechender Link anzeigt. Du kannst es auch direkt an deine Kontakte in CryptPad senden.",
|
||||
"fc_expandAll": "Alle ausklappen",
|
||||
"fc_collapseAll": "Alle einklappen",
|
||||
"fc_color": "Farbe ändern",
|
||||
@@ -1100,7 +1096,7 @@
|
||||
"support_formMessage": "Gib deine Nachricht ein...",
|
||||
"support_cat_tickets": "Vorhandene Tickets",
|
||||
"support_listTitle": "Support-Tickets",
|
||||
"support_listHint": "Hier ist die Liste der an die Administratoren gesendeten Tickets und der dazugehörigen Antworten. Ein geschlossenes Ticket kann nicht wieder geöffnet werden, du musst ein Ticket eröffnen. Du kannst geschlossene Tickets ausblenden, aber sie werden weiterhin für die Administratoren sichtbar sein.",
|
||||
"support_listHint": "Hier ist die Liste der an die Administratoren gesendeten Tickets und der dazugehörigen Antworten. Ein geschlossenes Ticket kann nicht wieder geöffnet werden, aber du kannst ein neues Ticket eröffnen. Du kannst geschlossene Tickets ausblenden.",
|
||||
"support_answer": "Antworten",
|
||||
"support_close": "Ticket schließen",
|
||||
"support_remove": "Ticket entfernen",
|
||||
@@ -1111,7 +1107,7 @@
|
||||
"notificationsPage": "Benachrichtigungen",
|
||||
"openNotificationsApp": "Benachrichtigungspanel öffnen",
|
||||
"notifications_cat_all": "Alle",
|
||||
"notifications_cat_friends": "Freundschaftsanfragen",
|
||||
"notifications_cat_friends": "Kontaktanfragen",
|
||||
"notifications_cat_pads": "Mit mir geteilt",
|
||||
"notifications_cat_archived": "Verlauf",
|
||||
"notifications_dismissAll": "Alle verbergen",
|
||||
@@ -1119,8 +1115,6 @@
|
||||
"requestEdit_button": "Bearbeitungsrechte anfragen",
|
||||
"requestEdit_dialog": "Bist du sicher, dass du den Eigentümer um Bearbeitungsrechte für das Pad bitten möchtest?",
|
||||
"requestEdit_confirm": "{1} hat Bearbeitungsrechte für das Pad <b>{0}</b> angefragt. Möchtest du die Rechte vergeben?",
|
||||
"requestEdit_fromFriend": "Du bist mit {0} befreundet",
|
||||
"requestEdit_fromStranger": "Du bist <b>nicht</b> mit {0} befreundet",
|
||||
"requestEdit_viewPad": "Pad in neuem Tab öffnen",
|
||||
"later": "Später entscheiden",
|
||||
"requestEdit_request": "{1} möchte das Pad <b>{0}</b> bearbeiten",
|
||||
@@ -1155,7 +1149,7 @@
|
||||
"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_addText": "Einen Kontakt 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?",
|
||||
@@ -1166,8 +1160,7 @@
|
||||
"owner_removedPending": "{0} hat die Einladung zur Eigentümerschaft von <b>{1}</b> zurückgezogen",
|
||||
"share_linkTeam": "Zu Team-Drive hinzufügen",
|
||||
"team_inviteModalButton": "Einladen",
|
||||
"team_pickFriends": "Freunde auswählen, um sie in dieses Team einzuladen",
|
||||
"team_noFriend": "Du bist derzeit mit keinen Freunden auf CryptPad verbunden.",
|
||||
"team_pickFriends": "Kontakte auswählen, um sie in dieses Team einzuladen",
|
||||
"team_pcsSelectLabel": "Speichern in",
|
||||
"team_pcsSelectHelp": "Die Erstellung eines eigenen Pads im Drive deines Teams gibt die Eigentümerschaft an das Team.",
|
||||
"team_invitedToTeam": "{0} hat dich zum Team eingeladen: <b>{1}</b>",
|
||||
@@ -1189,7 +1182,7 @@
|
||||
"team_rosterPromote": "Befördern",
|
||||
"team_rosterDemote": "Degradieren",
|
||||
"team_rosterKick": "Aus dem Team entfernen",
|
||||
"team_inviteButton": "Freunde einladen",
|
||||
"team_inviteButton": "Kontakte einladen",
|
||||
"team_leaveButton": "Dieses Team verlassen",
|
||||
"team_leaveConfirm": "Wenn du dieses Team verlässt, verlierst du den Zugriff auf das dazugehörige CryptDrive, den Chatverlauf und andere Inhalte. Bist du sicher?",
|
||||
"team_owner": "Eigentümer",
|
||||
@@ -1294,5 +1287,15 @@
|
||||
"oo_exportInProgress": "Export wird durchgeführt",
|
||||
"oo_sheetMigration_loading": "Deine Tabelle wird auf die neueste Version aktualisiert",
|
||||
"oo_sheetMigration_complete": "Eine aktualisierte Version ist verfügbar. Klicke auf OK, um neu zu laden.",
|
||||
"oo_sheetMigration_anonymousEditor": "Die Bearbeitung dieser Tabelle ist für anonyme Benutzer deaktiviert, bis sie von einem registrierten Benutzer auf die neueste Version aktualisiert wird."
|
||||
"oo_sheetMigration_anonymousEditor": "Die Bearbeitung dieser Tabelle ist für anonyme Benutzer deaktiviert, bis sie von einem registrierten Benutzer auf die neueste Version aktualisiert wird.",
|
||||
"imprint": "Impressum",
|
||||
"isContact": "{0} ist einer deiner Kontakte",
|
||||
"isNotContact": "{0} ist <b>nicht</b> einer deiner Kontakte",
|
||||
"settings_cat_security": "Vertraulichkeit",
|
||||
"settings_safeLinksHint": "CryptPad fügt den Pad-Links die Schlüssel zum Entschlüsseln der Inhalte hinzu. Jeder, der Zugriff auf den Browserverlauf hat, kann möglicherweise die Daten lesen. Dazu gehören Browsererweiterungen und Browser, die den Verlauf geräteübergreifend synchronisieren. Die Aktivierung von \"sicheren Links\" verhindert, dass die Schlüssel in den Browserverlauf gelangen oder in der Adressleiste angezeigt werden, wann immer dies möglich ist. Wir empfehlen dringend, diese Funktion zu aktivieren und das Menü {0} Teilen zu verwenden.",
|
||||
"dontShowAgain": "Nicht mehr anzeigen",
|
||||
"profile_login": "Du musst dich einloggen, um diesen Benutzer zu deinen Kontakten hinzuzufügen",
|
||||
"safeLinks_error": "Dieser Link gibt dir keinen Zugriff auf das Dokument",
|
||||
"settings_safeLinksCheckbox": "Sichere Links aktivieren",
|
||||
"settings_safeLinksTitle": "Sichere Links"
|
||||
}
|
||||
|
||||
@@ -151,7 +151,6 @@
|
||||
"okButton": "OK (enter)",
|
||||
"cancel": "Ακύρωση",
|
||||
"cancelButton": "Ακύρωση (esc)",
|
||||
"doNotAskAgain": "Να μην ρωτηθώ ξανά (Esc)",
|
||||
"historyText": "Ιστορικό",
|
||||
"historyButton": "Εμφάνιση ιστορικού του εγγράφου",
|
||||
"history_next": "Μετάβαση στην επόμενη έκδοση",
|
||||
|
||||
@@ -479,7 +479,6 @@
|
||||
"slide_invalidLess": "Estilo personalizado no válido",
|
||||
"fileShare": "Copiar link",
|
||||
"ok": "OK",
|
||||
"doNotAskAgain": "No preguntar nuevamente (Esc)",
|
||||
"show_help_button": "Mostrar ayuda",
|
||||
"hide_help_button": "Esconder ayuda",
|
||||
"help_button": "Ayuda",
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"media": "Media",
|
||||
"todo": "Tehtävälista",
|
||||
"contacts": "Yhteystiedot",
|
||||
"sheet": "Taulukko (Beta)",
|
||||
"sheet": "Taulukko",
|
||||
"teams": "Teams"
|
||||
},
|
||||
"button_newpad": "Uusi Teksti-padi",
|
||||
@@ -184,7 +184,6 @@
|
||||
"okButton": "OK (Enter)",
|
||||
"cancel": "Keskeytä",
|
||||
"cancelButton": "Keskeytä (Esc)",
|
||||
"doNotAskAgain": "Älä kysy uudestaan (Esc)",
|
||||
"show_help_button": "Näytä ohje",
|
||||
"hide_help_button": "Piilota ohje",
|
||||
"help_button": "Ohje",
|
||||
@@ -619,7 +618,7 @@
|
||||
"about_intro": "CryptPadia kehittää Pariisissa, Ranskassa ja Iasissa, Romaniassa toimiva<a href=\"http://xwiki.com\">XWiki SAS</a>-pienyrityksen tutkimusryhmä. CryptPadin parissa työskentelee kolme ryhmän ydinjäsentä ja lisäksi joitakin avustajia XWiki SAS:n sisältä ja ulkopuolelta.",
|
||||
"about_core": "Ydinkehittäjät",
|
||||
"about_contributors": "Tärkeät avustajat",
|
||||
"main_info": "<h2>Luottamuksellista yhteistyötä</h2>Jaetut dokumentit mahdollistavat ideoiden jakamisen samalla kun <strong>nollatietoperiaate</strong>-teknologia suojaa yksityisyytesi - <strong>jopa meiltä</strong>.",
|
||||
"main_info": "<h2>Luottamuksellista yhteistyötä</h2> Jaa ideoita yhdessä jaettujen dokumenttien avulla.<strong>Nollatieto</strong>-teknologia turvaa yksityisyytesi - <strong>jopa meiltä</strong>.",
|
||||
"main_catch_phrase": "Pilvipalvelu nollatietoperiaatteella",
|
||||
"main_footerText": "CryptPadin avulla voit nopeasti luoda kollaboratiivisia dokumentteja muistiinpanoja ja yhteistä ideointia varten.",
|
||||
"footer_applications": "Sovellukset",
|
||||
@@ -665,8 +664,6 @@
|
||||
"requestEdit_button": "Pyydä muokkausoikeutta",
|
||||
"requestEdit_dialog": "Haluatko varmasti pyytää padin omistajalta muokkausoikeutta?",
|
||||
"requestEdit_confirm": "{1} on pyytänyt oikeutta muokata padia <b>{0}</b>. Haluatko myöntää muokkausoikeuden?",
|
||||
"requestEdit_fromFriend": "Olet kaveri käyttäjän {0} kanssa",
|
||||
"requestEdit_fromStranger": "<b>Et ole</b> käyttäjän {0} kaveri",
|
||||
"requestEdit_viewPad": "Avaa padi uudessa välilehdessä",
|
||||
"later": "Päätä myöhemmin",
|
||||
"requestEdit_request": "{1} haluaa muokata padia <b>{0}</b>",
|
||||
@@ -695,7 +692,6 @@
|
||||
"share_linkTeam": "Lisää tiimin CryptDriveen",
|
||||
"team_pickFriends": "Valitse tiimiin kutsuttavat kaverit",
|
||||
"team_inviteModalButton": "Kutsu",
|
||||
"team_noFriend": "Sinulla ei ole vielä kavereita CryptPadissa.",
|
||||
"drive_sfPassword": "Jaettu kansiosi {0} ei ole enää saatavilla. Se on joko poistettu omistajansa toimesta tai sille on asetettu uusi salasana. Voit poistaa tämän kansion CryptDrivestasi tai palauttaa käyttöoikeuden käyttämällä uutta salasanaa.",
|
||||
"drive_sfPasswordError": "Väärä salasana",
|
||||
"password_error_seed": "Padia ei löytynyt!<br>Tämä virhe voi johtua kahdesta syystä: joko padiin on lisätty tai vaihdettu salasana, tai padi on poistettu palvelimelta.",
|
||||
@@ -876,8 +872,198 @@
|
||||
"keywords": {
|
||||
"title": "Avainsanat",
|
||||
"pad": {
|
||||
"q": "Mikä on padi?"
|
||||
"q": "Mikä on padi?",
|
||||
"a": "<em>Padi</em> on <a href='http://etherpad.org/' target='_blank'>Etherpad-projektin</a> popularisoima termi reaaliaikaiselle kollaboratiiviselle editorille.\nSe tarkoittaa selaimessa muokattavaa dokumenttia, jossa muiden käyttäjien tekemät muutokset näkyvät lähes välittömästi."
|
||||
},
|
||||
"owned": {
|
||||
"q": "Mikä on omistettu padi?",
|
||||
"a": "<em>Omistettu padi</em> on padi, jolla on erityisesti määritelty <em>omistaja</em>, jonka palvelin tunnistaa <em>julkisen salausavaimen</em> perusteella. Padin omistaja voi poistaa omistamansa padit palvelimelta, jolloin muut yhteiskäyttäjät eivät voi enää käyttää niitä riippumatta siitä, olivatko ne tallennettuna heidän henkilökohtaisiin CryptDriveihinsa."
|
||||
},
|
||||
"expiring": {
|
||||
"q": "Mikä on vanheneva padi?",
|
||||
"a": "<em>Vanheneva padi</em> on padi, jolle on määritelty vanhenemisajankohta, jolloin padi poistetaan automaattisesti palvelimelta. Vanhenevat padit voidaan määritellä säilymään minkä tahansa ajan yhdestä tunnista 100 kuukauteen. Vanheneva padi ja sen historia muuttuvat vanhenemishetkellä pysyvästi käyttökelvottomiksi, vaikka padia muokattaisiinkin silloin.<br><br>Jos padi on määritelty vanhenevaksi, voit tarkastaa sen vanhenemisajan padin <em>ominaisuuksista</em> joko CryptDrivessa padin kohdalla hiiren oikealla painikkeella aukeavasta valikosta tai käyttämällä <em>Ominaisuudet-valikkoa</em> sovelluksen työkalupalkista."
|
||||
},
|
||||
"tag": {
|
||||
"q": "Miten voin käyttää tunnisteita?",
|
||||
"a": "Voit lisätä padeihin ja ladattuihin tiedostoihin tunnisteita CryptDrivessa tai käyttää <em>Tunniste</em>-painiketta (<span class='fa fa-hashtag'></span>) minkä tahansa editorin työkalupalkista. Hae padeja ja tiedostoja CryptDriven hakupalkista käyttämällä ristikkomerkillä alkavaa hakusanaa (esimerkiksi <em>#crypto</em>)."
|
||||
},
|
||||
"template": {
|
||||
"q": "Mikä on mallipohja?",
|
||||
"a": "Mallipohja on padi, jolla voit määritellä luotavan padin oletussisällön luodessasi toista samantyyppistä padia. Voit muuttaa minkä tahansa olemassaolevan padin mallipohjaksi siirtämällä sen <em>Mallipohjat</em>-osastoon CryptDrivessasi. Voit myös tehdä padista mallipohjana käytettävän kopion klikkaamalla Mallipohja-painiketta (<span class='fa fa-bookmark'></span>) editorin työkalupalkista."
|
||||
},
|
||||
"abandoned": {
|
||||
"q": "Mikä on hylätty padi?",
|
||||
"a": "<em>Hylätty padi</em> on padi, jota ei ole kiinnitetty yhdenkään rekisteröityneen käyttäjän CryptDriveen ja jota ei ole muokattu kuuteen kuukauteen. Hylätyt dokumentit poistetaan palvelimelta automaattisesti."
|
||||
}
|
||||
},
|
||||
"privacy": {
|
||||
"title": "Yksityisyys",
|
||||
"different": {
|
||||
"q": "Miten CryptPad eroaa muista padeja tarjoavista palveluista?",
|
||||
"a": "CryptPad salaa padeihin tekemäsi muutokset ennen niiden lähettämistä palvelimelle, joten emme voi lukea, mitä kirjoitat."
|
||||
},
|
||||
"me": {
|
||||
"q": "Mitä palvelin tietää minusta?",
|
||||
"a": "Palvelimen ylläpitäjät näkevät CryptPadia käyttävien ihmisten IP-osoitteet. Emme pidä kirjaa siitä, mitkä osoitteet vierailevat missäkin padeissa. Tämä olisi kuitenkin teknisesti mahdollista, vaikka emme pääsekään tarkastelemaan padien salaamatonta sisältöä. Jos pelkäät meidän analysoivan näitä tietoja, on parasta olettaa meidän keräävän niitä, sillä emme voi todistaa, ettemme tee niin.<br><br>Keräämme käyttäjiltämme joitakin perustason telemetriatietoja, kuten käytetyn laitteen näytön koon ja tietoja useimmin käytetyistä painikkeista. Nämä auttavat meitä parantamaan CryptPadia, mutta jos et halua lähettää telemetriatietoja CryptPadille, voit <strong>jättäytyä pois tietojen keräämisestä ottamalla rastin pois <em>Salli käyttäjäpalaute</em>-ruudusta</strong>.<br><br>Pidämme kirjaa siitä, mitä padeja käyttäjät säilyttävät CryptDriveissaan pystyäksemme asettamaan tallennustilarajoituksia. Emme kuitenkaan tiedä näiden padien tyyppiä tai sisältöä. Tallennustilakiintiöt määritellään käyttäjien julkisten salausavainten perusteella, mutta emme yhdistä käyttäjien nimiä tai sähköpostiosoitteita näihin avaimiin.<br><br>Saadaksesi lisätietoja aiheesta voit tutustua kirjoittamaamme <a href='https://blog.cryptpad.fr/2017/07/07/cryptpad-analytics-what-we-cant-know-what-we-must-know-what-we-want-to-know/' target='_blank'>blogikirjoitukseen</a>."
|
||||
},
|
||||
"register": {
|
||||
"q": "Jos rekisteröidyn, tietääkö palvelin minusta enemmän?",
|
||||
"a": "Emme vaadi käyttäjiltä sähköpostiosoitteen vahvistusta, eikä palvelin saa tietää rekisteröitymisen yhteydessä edes käyttäjänimeäsi tai salasanaasi. Sen sijaan rekisteröitymis- ja sisäänkirjautumislomakkeet luovat antamastasi syötteestä uniikin avainrenkaan, ja palvelin saa tietoonsa ainoastaan kryptografisen allekirjoituksesi. Käytämme tätä tietoa yksityiskohtien, kuten tallennustilan käytön valvomiseen ja siten tallennustilakiintiöiden ylläpitämiseen.<br><br>Käytämme <em>palaute</em>-toimintoa kertoaksemme palvelimelle, että IP-osoitteestasi on luotu käyttäjätili. Tämä auttaa meitä pitämään kirjaa CryptPadiin rekisteröityneiden käyttäjien määrästä ja maantieteellisestä sijainnista, jotta voimme paremmin arvioida, mitä kieliä palvelumme kannattaisi tukea.<br><br>Rekisteröityneet käyttäjät kertovat palvelimelle, mitä padeja he säilyttävät CryptDriveissaan. Tämä on tarpeen, että kyseisiä padeja ei todeta hylätyiksi ja siten poisteta käyttämättömyyden takia."
|
||||
},
|
||||
"other": {
|
||||
"q": "Mitä yhteistyökumppanit saavat tietää minusta?",
|
||||
"a": "Muokatessasi padia jonkun toisen kanssa kaikki yhteydet kulkevat palvelimen kautta, joten vain me saamme tietää IP-osoitteesi. Muut käyttäjät näkevät näyttönimesi, avatar-kuvasi, linkin profiiliisi (jos olet luonut sellaisen) ja <em>julkisen salausavaimesi</em> (jota käytetään yhteyksien salaamiseen)."
|
||||
},
|
||||
"anonymous": {
|
||||
"q": "Tekeekö CryptPad minusta anonyymin?",
|
||||
"a": "Vaikka CryptPad on suunniteltu tietämään sinusta niin vähän kuin mahdollista, se ei tarjoa vahvaa anonymiteettisuojaa. Palvelimemme tietävät IP-osoitteesi, mutta voit halutessasi piilottaa sen käyttämällä CryptPadia Tor-verkosta. Pelkkä Tor-verkon käyttäminen ilman muutoksia verkkokäyttäytymiseesi ei takaa anonymiteettiä, sillä palvelin tunnistaa käyttäjät uniikkien salaustunnisteiden perusteella. Jos käytät samaa käyttäjätunnusta Tor-verkosta ja sen ulkopuolelta, istuntosi voidaan yhdistää sinuun.<br><br>Käyttäjille, joiden yksityisyysvaatimukset ovat matalammat - toisin kuin monet muut palvelut, CryptPad ei vaadi käyttäjiä tunnistautumaan nimellä, puhelinnumerolla tai sähköpostiosoitteella."
|
||||
},
|
||||
"policy": {
|
||||
"q": "Onko teillä tietosuojakäytäntö?",
|
||||
"a": "Kyllä! Se löytyy <a href='/privacy.html' target='_blank'>täältä</a>."
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
"pad_password": {
|
||||
"q": "Mitä tapahtuu, kun suojaan padin tai kansion salasanalla?",
|
||||
"a": "Voit suojata minkä tahansa padin tai jaetun kansion salasanalla luodessasi sen. Voit myös käyttää Ominaisuudet-valikkoa asettaaksesi, vaihtaaksesi tai poistaaksesi salasanan milloin tahansa.<br><br>Padien ja jaettujen kansioiden salasanat on tarkoitettu suojaamaan linkkiä jakaessasi sitä mahdollisesti turvattomien kanavien, kuten sähköpostin tai tekstiviestin kautta. Jos joku onnistuu kaappaamaan linkkisi, mutta ei tiedä sen salasanaa, ei hän pääse lukemaan dokumenttiasi.<br><br>Kun jaat sisältöä CryptPadin sisällä yhteystietojesi tai tiimiesi kanssa, tiedonsiirto on salattua ja oletamme, että haluat heidän pääsevän käyttämään dokumenttiasi. Siksi salasana tallennetaan ja lähetetään padin mukana jakaessasi sitä CryptPadin sisällä. Vastaanottajalta tai sinulta itseltäsi <b>ei</b> pyydetä salasanaa dokumenttia avatessa."
|
||||
},
|
||||
"title": "Turvallisuus",
|
||||
"proof": {
|
||||
"q": "Miten käytätte nollatietotodistuksia (Zero Knowledge Proofs)?",
|
||||
"a": "Käyttäessämme termiä \"nollatieto\" (Zero Knowledge) emme viittaa <em>nollatietotodistuksiin</em> (Zero Knowledge Proofs) vaan <em>nollatieto-verkkopalveluihin</em> (Zero Knowledge Web Services). Nollatieto-verkkopalvelut salaavat käyttäjän datan tämän selaimessa niin, ettei palvelin pääse missään vaiheessa käsittelemään salaamatonta dataa tai salausavaimia.<br><br>Olemme keränneet listan muista nollatietopalveluista <a href='https://blog.cryptpad.fr/2017/02/20/Time-to-Encrypt-the-Cloud/#Other-Zero-Knowledge-Services'>tänne</a>."
|
||||
},
|
||||
"why": {
|
||||
"q": "Miksi minun kannattaisi käyttää CryptPadia?",
|
||||
"a": "Mielestämme pilvipalveluiden ei tarvitse päästä lukemaan dataasi, jotta voit jakaa sen ystäviesi ja kollegoidesi kanssa. Jos käytät yhteistyöhön jotakin muuta palvelua, eikä palvelu erikseen ilmoita, ettei se pääse käsiksi tietoihisi, on hyvin todennäköistä, että tietojasi käytetään kaupallisiin tarkoituksiin."
|
||||
},
|
||||
"compromised": {
|
||||
"q": "Suojaako CryptPad minua, jos laitteeni tietoturva on vaarantunut?",
|
||||
"a": "Jos laitteesi varastetaan, CryptPad voi kirjata sinut ulos kaikista muista laitteista, paitsi nykyisestä laitteestasi. Tehdäksesi niin mene <strong>Asetukset</strong>-sivulle ja valitse <strong>Kirjaudu ulos kaikkialta</strong>. Kaikki muut tilillesi kirjautuneet aktiiviset laitteet kirjautuvat välittömästi ulos. Ne laitteet, joilla CryptPadia on käytetty aiemmin kirjautuvat ulos seuraavan sivunlatauksen yhteydessä.<br><br>Tällä hetkellä <em>etäuloskirjautuminen</em> on toteutettu selainpohjaisesti palvelimen sijaan. Näin ollen se ei suojaa sinua valtiollisilta toimijoilta, mutta on riittävä, jos unohdit kirjautua ulos CryptPadista käytettyäsi jaettua tietokonetta."
|
||||
},
|
||||
"crypto": {
|
||||
"q": "Mitä kryptografisia menetelmiä käytätte?",
|
||||
"a": "CryptPad perustuu kahteen avoimen lähdekoodin kryptografiakirjastoon: <a href='https://github.com/dchest/tweetnacl-js' target='_blank'>tweetnacl.js:n</a> ja <a href='https://github.com/dchest/scrypt-async-js' target='_blank'>scrypt-async.js:n</a>. <br><br>Scrypt on <em>salasanapohjainen avaimenmuodostusalgoritmi</em>. Käytämme sitä muuntaaksemme käyttäjätunnuksesi ja salasanasi uniikiksi avainrenkaaksi, joka turvaa pääsyn CryptDriveesi niin, että ainoastaan sinä pääset käsiksi padilistaasi. <br><br>Käytämme vastaavasti tweetnacl:n tarjoamia <em>xsalsa20-poly1305</em>- ja <em>x25519-xsalsa20-poly1305</em>-salakirjoitusjärjestelmiä salaamaan padeja ja keskusteluhistoriaa."
|
||||
}
|
||||
},
|
||||
"usability": {
|
||||
"title": "Käytettävyys",
|
||||
"register": {
|
||||
"q": "Mitä hyötyä rekisteröitymisestä on minulle?",
|
||||
"a": "Rekisteröityneille käyttäjille on tarjolla joitakin toimintoja, jotka eivät ole saatavilla rekisteröitymättömille käyttäjille. Löydät nämä toiminnot <a href='/features.html' target='_blank'>luomastamme kaaviosta</a>."
|
||||
},
|
||||
"share": {
|
||||
"q": "Miten jaan salattuja padeja kavereideni kanssa?",
|
||||
"a": "CryptPad laittaa URL-osoitteessa padisi salaisen salausavaimen <em>#</em>-merkin jälkeen. Tämän merkin jälkeen laitettuja tietoja ei lähetetä palvelimelle, joten emme pääse koskaan käyttämään salausavaimiasi. Jakaessasi linkin padiin jaat oikeuden lukea ja käyttää sitä."
|
||||
},
|
||||
"remove": {
|
||||
"q": "Poistin padin tai tiedoston CryptDrivestani, mutta sen sisältö on yhä käytettävissä. Miten voin poistaa sen?",
|
||||
"a": "Ainoastaan <em>omistettuja padeja</em> (otettu käyttöön helmikuussa 2018) voi poistaa. Lisäksi nämä padit voi poistaa ainoastaan niiden <em>omistaja</em> eli henkilö, joka alun perin loi kyseisen padin. Jos et ole luonut kyseistä padia, joudut pyytämään sen omistajaa poistamaan sen puolestasi. Omistamiesi padien poistaminen onnistuu CryptDrivessa <strong>klikkaamalla padia hiiren oikealla painikkeella</strong> ja valitsemalla <strong>Poista palvelimelta</strong>."
|
||||
},
|
||||
"forget": {
|
||||
"q": "Mitä tapahtuu, jos unohdan salasanani?",
|
||||
"a": "Valitettavasti se, että pystyisimme palauttamaan käyttöoikeuden salattuihin padeihisi tarkoittaisi myös sitä, että pääsisimme itse käsiksi niiden sisältöön. Jos et kirjoittanut käyttäjätunnustasi ja salasanaasi ylös etkä muista kumpaakaan, voit mahdollisesti palauttaa padisi selaimesi historiaa suodattamalla."
|
||||
},
|
||||
"change": {
|
||||
"q": "Entä jos haluan vaihtaa salasanani?",
|
||||
"a": "Voit vaihtaa CryptPad-salasanasi Tilin asetukset-sivulta."
|
||||
},
|
||||
"devices": {
|
||||
"q": "Olen kirjautunut sisään kahdella laitteella, ja näen kaksi eri CryptDrivea. Miten tämä on mahdollista?",
|
||||
"a": "On todennäköistä, että olet rekisteröitynyt samalla käyttäjänimellä kahdesti eri salasanoja käyttäen. CryptPad-palvelin tunnistaa sinut kryptografisen allekirjoituksesi perusteella käyttäjänimen sijaan, joten se ei voi estää muita rekisteröitymästä samalla käyttäjänimellä. Tästä johtuen jokaisella käyttäjätilillä on ainutlaatuinen käyttäjänimen ja salasanan yhdistelmä. Sisäänkirjautuneet käyttäjät voivat nähdä käyttäjänimensä Asetukset-sivun ylälaidassa."
|
||||
},
|
||||
"folder": {
|
||||
"q": "Voinko jakaa kokonaisia kansioita CryptDrivestani?",
|
||||
"a": "Kyllä, voit luoda <em>jaetun kansion</em> CryptDrivestasi ja jakaa kerralla kaikki sen sisältämät padit."
|
||||
},
|
||||
"feature": {
|
||||
"q": "Voitteko lisätä CryptPadiin tarvitsemani ominaisuuden?",
|
||||
"a": "Monet CryptPadin ominaisuuksista ovat olemassa, koska käyttäjämme ovat toivoneet niitä. <a href='https://cryptpad.fr/contact.html' target='_blank'>Yhteystiedot-sivumme</a> kertoo, millä tavoin meihin saa yhteyden.<br><br>Valitettavasti emme voi taata, että pystymme toteuttamaan kaikki käyttäjiemme ehdotukset. Jos jokin tietty ominaisuus on kriittinen organisaatiosi kannalta, voit sponsoroida kehitystä varmistaaksesi sen toteutumisen. Ota yhteyttä osoitteeseen <a href='mailto:sales@cryptpad.fr' target='_blank'>sales@cryptpad.fr</a> saadaksesi lisätietoja.<br><br>Vaikka kehitystyön sponsorointi ei olisikaan mahdollista, olemme silti kiinnostuneita palautteesta, joka auttaa meitä parantamaan CryptPadia. Ota meihin milloin tahansa yhteyttä yllä luetelluilla tavoilla."
|
||||
}
|
||||
},
|
||||
"other": {
|
||||
"title": "Muita kysymyksiä",
|
||||
"pay": {
|
||||
"q": "Miksi minun täytyisi maksaa, kun niin monet toiminnot ovat ilmaisia?",
|
||||
"a": "Annamme tukijoillemme lisätallennustilaa ja mahdollisuuden kasvattaa kavereiden tallennustilakiintiöitä (<a href='https://accounts.cryptpad.fr/#/faq' target='_blank'>lue lisää</a>).<br><br>Näiden lyhytaikaisten etujen lisäksi premium-tilaus auttaa rahoittamaan CryptPadin jatkuvaa, aktiivista kehitystyötä. Tähän kuuluu bugien korjaamista, uusien ominaisuuksien lisäämistä ja CryptPad-instanssien pystyttämisen ja ylläpidon helpottamista. Lisäksi autat näyttämään muille palveluntarjoajille, että ihmiset ovat valmiita tukemaan yksityisyyttä parantavia teknologioita. Toivomme, että käyttäjätietojen myymiseen perustuvat liiketoimintamallit jäävät lopulta menneeseen.<br><br>Lopuksi, tarjoamme suurimman osan CryptPadin toiminnallisuudesta ilmaiseksi, koska uskomme yksityisyyden kuuluvan kaikille - ei vain niille, joilla on varaa maksaa siitä. Tukemalla meitä autat tarjoamaan heikommassa asemassa oleville väestöille pääsyn näihin peruspalveluihin."
|
||||
},
|
||||
"goal": {
|
||||
"q": "Mitkä ovat tavoitteenne?",
|
||||
"a": "Kehittämällä yksityisyyttä kunnioittavaa kollaboraatioteknologiaa toivomme nostavamme käyttäjien odotuksia pilvipalveluiden yksityisyyden suhteen. Toivomme, että työmme rohkaisee muita palveluntarjoajia pyrkimään samaan tai parempaan lopputulokseen. Optimismistamme huolimatta tiedämme, että suuri osa webistä rahoitetaan kohdistetulla mainonnalla. Tehtävää on paljon enemmän, kuin mihin pystymme yksin - arvostamme yhteisömme tarjoamaa mainostusta, tukea ja panosta tavoitteidemme saavuttamisessa."
|
||||
},
|
||||
"jobs": {
|
||||
"q": "Etsittekö työntekijöitä?",
|
||||
"a": "Kyllä! Esittäydy meille sähköpostilla osoitteeseen <a href='mailto:jobs@xwiki.com' target='_blank'>jobs@xwiki.com</a>."
|
||||
},
|
||||
"host": {
|
||||
"q": "Voitteko auttaa minua perustamaan oman CryptPad-instanssini?",
|
||||
"a": "Tarjoamme mielellämme tukea organisaatiosi sisäiselle CryptPad-instanssille. Ota yhteyttä osoitteeseen <a href='mailto:sales@cryptpad.fr' target='_blank'>sales@cryptpad.fr</a> saadaksesi lisätietoja."
|
||||
},
|
||||
"revenue": {
|
||||
"q": "Kuinka voin osallistua tulojen jakamiseen?",
|
||||
"a": "Jos ylläpidät omaa CryptPad-instanssiasi, haluaisit ottaa käyttöön maksulliset käyttäjätilit ja jakaa tulot CryptPadin kehittäjien kanssa, palvelimesi täytyy määritellä kumppanipalveluksi.<br><br>CryptPad-asennushakemistosi <em>config.example.js</em>-tiedostosta pitäisi löytyä ohjeet tämän palvelun käyttöönottoon. Sinun tulee myös ottaa yhteyttä osoitteeseen <a href='mailto:sales@cryptpad.fr'>sales@cryptpad.fr</a> varmistaaksesi, että palvelimesi HTTPS-määritykset ovat kunnossa ja sopiaksesi käytettävistä maksutavoista."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"policy_howweuse_p1": "Käytämme näitä tietoja suunnitellaksemme CryptPadin mainostusta ja arvioidaksemme aiempien kampanjoiden onnistumista. Sijaintitietosi puolestaan kertovat meille, mitä kieliä CryptPadin tulisi mahdollisesti tukea englannin lisäksi.",
|
||||
"tos_title": "CryptPad-käyttöehdot",
|
||||
"tos_legal": "Älä ole pahantahtoinen, väärinkäyttäjä tai tee mitään laitonta.",
|
||||
"tos_availability": "Toivomme sinun pitävän tätä palvelua hyödyllisenä, mutta emme voi taata sen saatavuutta tai suorituskykyä. Viethän tietosi säännöllisesti muualle talteen.",
|
||||
"tos_e2ee": "CryptPad-sisältöä voi lukea tai muokata kuka tahansa, joka pystyy arvaamaan tai muuten saamaan käsiinsä padin katkelmatunnisteen. Suosittelemme käyttämään päästä päähän salattuja viestintämenetelmiä linkkien jakamiseen, emmekä ota vastuuta tilanteissa, joissa sellainen linkki pääsee vuotamaan.",
|
||||
"tos_logs": "Selaimesi palvelimelle tarjoama metadata voidaan kerätä palvelun ylläpitämistä varten.",
|
||||
"tos_3rdparties": "Emme luovuta yksilöityä dataa kolmansille osapuolille, ellei meillä ole lakisääteistä velvollisuutta tehdä niin.",
|
||||
"four04_pageNotFound": "Etsimääsi sivua ei löytynyt.",
|
||||
"updated_0_header_logoTitle": "Siirry CryptDriveesi",
|
||||
"header_logoTitle": "Siirry CryptDriveesi",
|
||||
"header_homeTitle": "Siirry CryptPad-kotisivulle",
|
||||
"help": {
|
||||
"title": "Näin pääset alkuun",
|
||||
"generic": {
|
||||
"more": "Tutustu <a href=\"/faq.html\" target=\"_blank\">usein kysyttyihin kysymyksiin</a> saadaksesi lisätietoja CryptPadin toiminnallisuudesta",
|
||||
"share": "Käytä Jaa-valikkoa (<span class=\"fa fa-shhare-alt\"></span>) luodaksesi linkin, jonka kautta yhteistyökumppanit pääsevät katselemaan tai muokkaamaan padia",
|
||||
"save": "Kaikki tekemäsi muutokset synkronoidaan automaattisesti, joten sinun ei tarvitse koskaan tallentaa"
|
||||
},
|
||||
"text": {
|
||||
"formatting": "Voit näyttää tai piilottaa Tekstin muotoilu-työkalupalkin klikkaamalla <span class=\"fa fa-caret-down\"></span> tai <span class=\"fa fa-caret-up\"></span>-painikkeita",
|
||||
"embed": "Rekisteröityneet käyttäjät voivat upottaa kuvan tai CryptDriveen tallennetun tiedoston <span class=\"fa fa-image\"></span> avulla",
|
||||
"history": "Voit käyttää <em>historiaa</em> <span class=\"fa fa-history\"></span> katsellaksesi tai palauttaaksesi aiempia versioita"
|
||||
},
|
||||
"pad": {
|
||||
"export": "Voit viedä sisältösi PDF-tiedostoon Tekstin muotoilu-työkalupalkin <span class=\"fa fa-print\"></span> -painikkeella"
|
||||
},
|
||||
"code": {
|
||||
"modes": "Käytä <span class=\"fa fa-ellipsis-h\"></span> -alavalikon pudotusvalikoita vaihtaaksesi syntaksin korostustilaa tai väriteemoja"
|
||||
},
|
||||
"beta": {
|
||||
"warning": "Tämä editori on edelleen <strong>koekäytössä</strong>, voit ilmoittaa löytämäsi bugit <a href=\"https://github.com/xwiki-labs/cryptpad/issues/\" target=\"_blank\">asianhallintajärjestelmäämme</a>"
|
||||
},
|
||||
"oo": {
|
||||
"access": "Käyttö on rajattu ainoastaan rekisteröityneille käyttäjille, yhteistyökumppanien tulee kirjautua sisään"
|
||||
},
|
||||
"slide": {
|
||||
"markdown": "Kirjoita diat <a href=\"http://www.markdowntutorial.com/\">Markdown-kielellä</a> ja erota ne toisistaan <code>---</code> -rivillä",
|
||||
"present": "Aloita esitys <span class=\"fa fa-play-circle\"></span> -painikkeella",
|
||||
"settings": "Muuta dian asetuksia (taustakuvaa, siirtymiä, sivunumeroita jne.) <span class=\"fa fa-ellipsis-h\"></span> -alavalikon <span class=\"fa fa-cog\"></span> -painikkeella",
|
||||
"colors": "Vaihda tekstin ja taustan väriä <span class=\"fa fa-i-cursor\"></span> ja <span class=\"fa fa-square\"></span> -painikkeilla"
|
||||
},
|
||||
"poll": {
|
||||
"decisions": "Tee päätöksiä luotettujen ystävien kesken",
|
||||
"options": "Ehdota vaihtoehtoja ja tuo ilmi mielipiteesi",
|
||||
"choices": "Napsauta sarakkeesi soluja valitaksesi kyllä- (<strong>✔</strong>), ehkä- (<strong>~</strong>), tai ei (<strong>✖</strong>) -vaihtoehdon",
|
||||
"submit": "Napsauta <strong>Lähetä</strong> tehdäksesi valintasi näkyviksi muille"
|
||||
},
|
||||
"whiteboard": {
|
||||
"colors": "Kaksoisnapsauta värejä muokataksesi väripalettiasi",
|
||||
"mode": "Ota piirtotila pois käytöstä vetääksesi ja venyttääksesi viivoja",
|
||||
"embed": "Upota kuvia kovalevyltäsi <span class=\"fa fa-file-image-o\"></span> tai CryptDrivestasi <span class=\"fa fa-image\"></span> ja vie ne PNG-tiedostomuodossa kovalevyllesi <span class=\"fa fa-download\"></span> tai CryptDriveesi <span class=\"fa fa-cloud-upload\"></span>"
|
||||
},
|
||||
"kanban": {
|
||||
"add": "Lisää uusia tauluja oikeassa yläkulmassa olevalla <span class=\"fa fa-plus\"></span> -painikkeella",
|
||||
"task": "Siirrä kohtia raahaamalla ja pudottamalla ne yhdestä taulusta toiseen",
|
||||
"color": "Vaihda värejä napsauttamalla taulun otsikon vieressä olevaa värillistä osaa"
|
||||
}
|
||||
},
|
||||
"driveReadmeTitle": "Mikä on CryptPad?",
|
||||
"readme_welcome": "Tervetuloa CryptPadiin!",
|
||||
"readme_p1": "Tervetuloa CryptPadiin, täällä voit tehdä muistiinpanoja yksin tai ystäviesi kanssa."
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"media": "Média",
|
||||
"todo": "Todo",
|
||||
"contacts": "Contacts",
|
||||
"sheet": "Tableur (Beta)",
|
||||
"sheet": "Tableur",
|
||||
"teams": "Équipes"
|
||||
},
|
||||
"button_newpad": "Nouveau document texte",
|
||||
@@ -181,7 +181,6 @@
|
||||
"okButton": "OK (Entrée)",
|
||||
"cancel": "Annuler",
|
||||
"cancelButton": "Annuler (Échap)",
|
||||
"doNotAskAgain": "Ne plus demander (Échap)",
|
||||
"show_help_button": "Afficher l'aide",
|
||||
"hide_help_button": "Cacher l'aide",
|
||||
"help_button": "Aide",
|
||||
@@ -279,7 +278,7 @@
|
||||
"profile_description": "Description",
|
||||
"profile_fieldSaved": "Nouvelle valeur enregistrée : {0}",
|
||||
"profile_viewMyProfile": "Voir mon profil",
|
||||
"userlist_addAsFriendTitle": "Envoyer une demande d'ami à « {0} »",
|
||||
"userlist_addAsFriendTitle": "Envoyer une demande de contact à « {0} »",
|
||||
"contacts_title": "Contacts",
|
||||
"contacts_addError": "Erreur lors de l'ajout de ce contact dans votre liste",
|
||||
"contacts_added": "Invitation de contact acceptée.",
|
||||
@@ -299,7 +298,7 @@
|
||||
"contacts_confirmRemoveHistory": "Êtes-vous sûr de vouloir supprimer définitivement l'historique de votre chat ? Les messages ne pourront pas être restaurés.",
|
||||
"contacts_removeHistoryServerError": "Une erreur est survenue lors de la supprimer de l'historique du chat. Veuillez réessayer plus tard.",
|
||||
"contacts_fetchHistory": "Récupérer l'historique plus ancien",
|
||||
"contacts_friends": "Amis",
|
||||
"contacts_friends": "Contacts",
|
||||
"contacts_rooms": "Salons",
|
||||
"contacts_leaveRoom": "Quitter ce salon",
|
||||
"contacts_online": "Un autre utilisateur est en ligne dans ce salon",
|
||||
@@ -522,8 +521,8 @@
|
||||
"settings_creationSkipFalse": "Afficher",
|
||||
"settings_templateSkip": "Passer la fenêtre de choix d'un modèle",
|
||||
"settings_templateSkipHint": "Quand vous créez un nouveau pad, et si vous possédez des modèles pour ce type de pad, une fenêtre peut apparaître pour demander si vous souhaitez importer un modèle. Ici vous pouvez choisir de ne jamais montrer cette fenêtre et donc de ne jamais utiliser de modèle.",
|
||||
"settings_ownDriveTitle": "Activer les dernières fonctionnalités du compte",
|
||||
"settings_ownDriveHint": "Pour des raisons techniques, les comptes utilisateurs les plus anciens n'ont pas accès à toutes les fonctionnalités. Une mise à niveau gratuite permet de préparer votre CryptDrive pour les nouveautés à venir sans perturber vos activités habituelles.",
|
||||
"settings_ownDriveTitle": "Mise à jour du compte",
|
||||
"settings_ownDriveHint": "Les comptes plus anciens n'ont pas accès aux dernières fonctionnalités, pour des raisons techniques. Une mise à niveau gratuite permet d'activer les fonctionnalités actuelles et de préparer votre CryptDrive pour les futures mises à jour.",
|
||||
"settings_ownDriveButton": "Mettre à niveau votre compte",
|
||||
"settings_ownDriveConfirm": "La mise à niveau peut prendre du temps. Vous devrez vous reconnecter sur tous vos appareils. Voulez-vous continuer ?",
|
||||
"settings_ownDrivePending": "Votre compte est en train d'être mis à jour. Veuillez ne pas fermer ou recharger cette page avant que le traitement soit terminé.",
|
||||
@@ -671,7 +670,7 @@
|
||||
"features_f_social": "Applications sociales",
|
||||
"features_f_social_note": "Créer un profil, utiliser un avatar, chat avec les contacts",
|
||||
"features_f_file1": "Importer et partager des fichiers",
|
||||
"features_f_file1_note": "Partager des fichiers avec vos amis ou les intégrer dans vos pads",
|
||||
"features_f_file1_note": "Partager des fichiers avec vos contacts ou les intégrer dans vos pads",
|
||||
"features_f_storage1": "Stockage permanent (50Mo)",
|
||||
"features_f_storage1_note": "Les pads stockés dans votre CryptDrive ne seront jamais supprimés pour cause d'inactivité",
|
||||
"features_f_register": "S'enregistrer gratuitement",
|
||||
@@ -774,7 +773,7 @@
|
||||
"a": "Les utilisateurs enregistrés ont accès à un certain nombre de nouvelles fonctionnalités inaccessibles aux utilisateurs non connectés. Un tableau récapitulatif est disponible <a href=\"/features.html\">ici</a>."
|
||||
},
|
||||
"share": {
|
||||
"q": "Comment partager des pads chiffrés avec mes amis ?",
|
||||
"q": "Comment partager des pads chiffrés avec mes contacts ?",
|
||||
"a": "CryptPad stocke la clé secrète de chiffrement des pads après le symbole <em>#</em> dans l'URL. Tout ce qui se trouve après ce symbole n'est jamais envoyé au serveur, ainsi nous n'avons pas accès à vos clés de chiffrement. Partager le lien d'un pad revient donc à permettre la lecture ou la modification du contenu."
|
||||
},
|
||||
"remove": {
|
||||
@@ -806,7 +805,7 @@
|
||||
"title": "Autres questions",
|
||||
"pay": {
|
||||
"q": "Pourquoi payer alors que toutes les fonctionnalités sont gratuites ?",
|
||||
"a": "Un compte premium permet d'<b>augmenter la limite de stockage</b> dans le CryptDrive, ainsi que celle de ses amis (<a href=\"https://accounts.cryptpad.fr/#/faq\" target=\"_blank\">en savoir plus</a>).<br>En plus de ces avantages directs, l'abonnement premium permet aussi de <b>financer le développement</b> actif et de manière continue de CryptPad. Cela comprend la correction de bugs, l'ajout de nouvelles fonctionnalités et rendre plus facile l'hébergement de CryptPad par d'autres personnes.<br>Avec un abonnement, vous aidez aussi à prouver aux autres fournisseurs de services que les gens sont prêts à supporter les technologies améliorant le respect de leur vie privée. Nous espérons qu'un jour, les entreprises ayant pour revenu principal la revente de données des utilisateurs soient de l'histoire ancienne.<br>Enfin, nous offrons la plupart des fonctionnalités gratuitement parce que nous croyons que tout le monde mérite le respect de la vie privée. En souscrivant à un compte premium, vous nous aider à maintenir ces fonctionnalités basiques accessibles aux populations défavorisées."
|
||||
"a": "Un compte premium permet d'<b>augmenter la limite de stockage</b> dans le CryptDrive, ainsi que celle de ses contacts (<a href=\"https://accounts.cryptpad.fr/#/faq\" target=\"_blank\">en savoir plus</a>).<br>En plus de ces avantages directs, l'abonnement premium permet aussi de <b>financer le développement</b> actif et de manière continue de CryptPad. Cela comprend la correction de bugs, l'ajout de nouvelles fonctionnalités et rendre plus facile l'hébergement de CryptPad par d'autres personnes.<br>Avec un abonnement, vous aidez aussi à prouver aux autres fournisseurs de services que les gens sont prêts à supporter les technologies améliorant le respect de leur vie privée. Nous espérons qu'un jour, les entreprises ayant pour revenu principal la revente de données des utilisateurs soient de l'histoire ancienne.<br>Enfin, nous offrons la plupart des fonctionnalités gratuitement parce que nous croyons que tout le monde mérite le respect de la vie privée. En souscrivant à un compte premium, vous nous aider à maintenir ces fonctionnalités basiques accessibles aux populations défavorisées."
|
||||
},
|
||||
"goal": {
|
||||
"q": "Quel est votre objectif ?",
|
||||
@@ -885,7 +884,7 @@
|
||||
},
|
||||
"driveReadmeTitle": "Qu'est-ce que CryptPad ?",
|
||||
"readme_welcome": "Bienvenue dans CryptPad !",
|
||||
"readme_p1": "Bienvenue dans CryptPad, le lieu où vous pouvez prendre des notes seul ou avec des amis.",
|
||||
"readme_p1": "Bienvenue dans CryptPad, le lieu où vous pouvez prendre des notes seul ou avec des contacts.",
|
||||
"readme_p2": "Ce pad va vous donner un aperçu de la manière dont vous pouvez utiliser CryptPad pour prendre des notes, les organiser et travailler en groupe sur celles-ci.",
|
||||
"readme_cat1": "Découvrez votre CryptDrive",
|
||||
"readme_cat1_l1": "Créer un pad : Dans votre CryptDrive, cliquez sur {0} puis {1} et vous obtenez un nouveau pad.",
|
||||
@@ -938,7 +937,6 @@
|
||||
"creation_noTemplate": "Pas de modèle",
|
||||
"creation_newTemplate": "Nouveau modèle",
|
||||
"creation_create": "Créer",
|
||||
"creation_saveSettings": "Ne plus me demander",
|
||||
"creation_settings": "Voir davantage de préférences",
|
||||
"creation_rememberHelp": "Ouvrez votre page de Préférences pour voir ce formulaire à nouveau",
|
||||
"creation_owners": "Propriétaires",
|
||||
@@ -1006,7 +1004,6 @@
|
||||
"crowdfunding_popup_text": "<h3>Aider CryptPad</h3>Pour vous assurer que CryptPad soit activement développé, nous vous invitons à supporter le projet via la <a href=\"https://opencollective.com/cryptpad\">page OpenCollective</a>, où vous pouvez trouver notre <b>Roadmap</b> et nos <b>objectifs de financement</b>.",
|
||||
"crowdfunding_popup_yes": "Voir la page",
|
||||
"crowdfunding_popup_no": "Pas maintenant",
|
||||
"crowdfunding_popup_never": "Ne plus demander",
|
||||
"survey": "Enquête CryptPad",
|
||||
"markdown_toc": "Sommaire",
|
||||
"debug_getGraph": "Obtenir le code permettant de générer un graphe de ce document",
|
||||
@@ -1058,18 +1055,17 @@
|
||||
"friendRequest_later": "Décider plus tard",
|
||||
"friendRequest_accept": "Accepter (Entrée)",
|
||||
"friendRequest_decline": "Décliner",
|
||||
"friendRequest_declined": "<b>{0}</b> a décliné votre demande d'ami",
|
||||
"friendRequest_accepted": "<b>{0}</b> a accepté votre demande d'ami",
|
||||
"friendRequest_received": "<b>{0}</b> souhaite être votre ami",
|
||||
"friendRequest_notification": "<b>{0}</b> vous a envoyé une demande d'ami",
|
||||
"friendRequest_declined": "<b>{0}</b> a décliné votre demande de contact",
|
||||
"friendRequest_accepted": "<b>{0}</b> a accepté votre demande de contact",
|
||||
"friendRequest_received": "<b>{0}</b> souhaite être votre contact",
|
||||
"friendRequest_notification": "<b>{0}</b> vous a envoyé une demande de contact",
|
||||
"notifications_empty": "Pas de nouvelle notification",
|
||||
"notifications_title": "Vous avez des notifications non lues",
|
||||
"profile_addDescription": "Ajouter une description",
|
||||
"profile_editDescription": "Modifier votre description",
|
||||
"profile_addLink": "Ajouter un lien vers votre site web",
|
||||
"profile_info": "Les autres utilisateurs peuvent trouver votre profil en cliquant sur votre nom dans la liste d'utilisateurs des documents.",
|
||||
"profile_friendRequestSent": "Demande d'ami en attente...",
|
||||
"profile_friend": "{0} est votre ami(e)",
|
||||
"profile_friendRequestSent": "Demande de contact en attente...",
|
||||
"notification_padShared": "{0} a partagé un pad avec vous : <b>{1}</b>",
|
||||
"notification_fileShared": "{0} a partagé un fichier avec vous : <b>{1}</b>",
|
||||
"notification_folderShared": "{0} a partagé un dossier avec vous : <b>{1}</b>",
|
||||
@@ -1080,7 +1076,7 @@
|
||||
"share_withFriends": "Partager",
|
||||
"notifications_dismiss": "Cacher",
|
||||
"fm_info_sharedFolderHistory": "Vous regardez l'historique de votre dossier partagé <b>{0}</b><br/>Votre CryptDrive restera en lecture seule pendant la navigation.",
|
||||
"share_description": "Choisissez ce que vous souhaitez partager puis obtenez le lien ou envoyez-le directement à vos amis CryptPad.",
|
||||
"share_description": "Choisissez ce que vous souhaitez partager puis obtenez le lien ou envoyez-le directement à vos contacts CryptPad.",
|
||||
"fc_color": "Changer la couleur",
|
||||
"supportPage": "Support",
|
||||
"admin_cat_support": "Support",
|
||||
@@ -1108,7 +1104,7 @@
|
||||
"notificationsPage": "Notifications",
|
||||
"openNotificationsApp": "Ouvrir le panneau de notifications",
|
||||
"notifications_cat_all": "Toutes",
|
||||
"notifications_cat_friends": "Demandes d'ami",
|
||||
"notifications_cat_friends": "Demandes de contact",
|
||||
"notifications_cat_pads": "Partagé avec moi",
|
||||
"notifications_cat_archived": "Historique",
|
||||
"notifications_dismissAll": "Tout cacher",
|
||||
@@ -1122,8 +1118,6 @@
|
||||
"requestEdit_button": "Demander les droits d'édition",
|
||||
"requestEdit_dialog": "Êtes-vous sûr de vouloir demander les droits d'édition de ce pad au propriétaire ?",
|
||||
"requestEdit_confirm": "{1} a demandé les droits d'édition pour le pad <b>{0}</b>. Souhaitez-vous leur accorder les droits ?",
|
||||
"requestEdit_fromFriend": "Vous êtes amis avec {0}",
|
||||
"requestEdit_fromStranger": "Vous n'êtes <b>pas</b> amis avec {0}",
|
||||
"requestEdit_viewPad": "Ouvrir le pad dans un nouvel onglet",
|
||||
"later": "Décider plus tard",
|
||||
"requestEdit_request": "{1} souhaite éditer le pad <b>{0}</b>",
|
||||
@@ -1150,7 +1144,7 @@
|
||||
"register_emailWarning3": "Si vous souhaitez tout de même utiliser votre adresse email comme nom d'utilisateur, appuyez sur OK.",
|
||||
"owner_removeText": "Supprimer un propriétaire existant",
|
||||
"owner_removePendingText": "Annuler une offre en attente",
|
||||
"owner_addText": "Proposer à un ami d'être co-propriétaire de ce document",
|
||||
"owner_addText": "Proposer à un contact d'être co-propriétaire de ce document",
|
||||
"owner_unknownUser": "Utilisateur inconnu",
|
||||
"owner_removeButton": "Supprimer les propriétaires sélectionnés",
|
||||
"owner_removePendingButton": "Annuler les offres sélectionnées",
|
||||
@@ -1167,9 +1161,8 @@
|
||||
"owner_removedPending": "{0} a annulé l'offre de co-propriété reçue pour <b>{1}</b>",
|
||||
"padNotPinnedVariable": "Ce pad va expirer après {4} jours d'inactivité, {0}connectez-vous{1} ou {2}enregistrez-vous{3} pour le préserver.",
|
||||
"share_linkTeam": "Ajouter au CryptDrive d'une équipe",
|
||||
"team_pickFriends": "Choisissez les amis à inviter dans cette équipe",
|
||||
"team_pickFriends": "Choisissez les contacts à inviter dans cette équipe",
|
||||
"team_inviteModalButton": "Inviter",
|
||||
"team_noFriend": "Vous n'avez pas encore ajouté d'ami sur CryptPad.",
|
||||
"team_pcsSelectLabel": "Sauver dans",
|
||||
"team_pcsSelectHelp": "Créer un pad dans le drive d'une équipe rend cette équipe propriétaire du pad si l'option est cochée.",
|
||||
"team_invitedToTeam": "{0} vous à inviter à rejoindre l'équipe : <b>{1}</b>",
|
||||
@@ -1191,7 +1184,7 @@
|
||||
"team_rosterPromote": "Promouvoir",
|
||||
"team_rosterDemote": "Rétrograder",
|
||||
"team_rosterKick": "Expulser de l'équipe",
|
||||
"team_inviteButton": "Inviter des amis",
|
||||
"team_inviteButton": "Inviter des contacts",
|
||||
"team_leaveButton": "Quitter cette équipe",
|
||||
"team_leaveConfirm": "Si vous quittez cette équipe, vous perdrez l'accès à son CryptDrive, son chat et les autres contenus. Êtes-vous sûr ?",
|
||||
"team_owner": "Propriétaires",
|
||||
@@ -1294,5 +1287,15 @@
|
||||
"oo_exportInProgress": "Exportation en cours",
|
||||
"oo_sheetMigration_loading": "Mise à jour de la feuille de calcul",
|
||||
"oo_sheetMigration_complete": "Version mise à jour disponible, appuyez sur OK pour recharger.",
|
||||
"oo_sheetMigration_anonymousEditor": "L'édition de cette feuille de calcul est désactivée pour les utilisateurs anonymes jusqu'à ce qu'elle soit mise à jour par un utilisateur enregistré."
|
||||
"oo_sheetMigration_anonymousEditor": "L'édition de cette feuille de calcul est désactivée pour les utilisateurs anonymes jusqu'à ce qu'elle soit mise à jour par un utilisateur enregistré.",
|
||||
"imprint": "Mentions légales",
|
||||
"isContact": "{0} est dans vos contacts",
|
||||
"isNotContact": "{0} n'est <b>pas</b> dans vos contacts",
|
||||
"settings_safeLinksHint": "CryptPad inclut dans ses liens les clés permettant de déchiffrer vos pads. Toute personne ayant accès à votre historique de navigation peut potentiellement lire vos données. Cela inclut les extensions de navigateur intrusives et les navigateurs qui synchronisent votre historique entre les appareils. L'activation des \"liens sécurisés\" empêche les clés d'entrer dans votre historique de navigation ou d'être affichées dans votre barre d'adresse quand cela est possible. Nous vous recommandons vivement d'activer cette fonction et d'utiliser le menu {0} Partager.",
|
||||
"profile_login": "Vous devez vous connecter pour ajouter cet utilisateur à vos contacts",
|
||||
"dontShowAgain": "Ne plus demander",
|
||||
"safeLinks_error": "Le lien utilisé ne permet pas d'ouvrir ce document",
|
||||
"settings_safeLinksCheckbox": "Activer les liens sécurisés",
|
||||
"settings_safeLinksTitle": "Liens Sécurisés",
|
||||
"settings_cat_security": "Confidentialité"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"main_title": "CryptPad: Editor collaborativo in tempo reale, zero knowledge",
|
||||
"main_title": "CryptPad: Editor zero knowledge collaborativo in tempo reale",
|
||||
"type": {
|
||||
"pad": "Testo",
|
||||
"code": "Code",
|
||||
"code": "Codice",
|
||||
"poll": "Sondaggio",
|
||||
"kanban": "Kanban",
|
||||
"slide": "Presentazione",
|
||||
@@ -10,13 +10,13 @@
|
||||
"whiteboard": "Lavagna",
|
||||
"file": "File",
|
||||
"media": "Media",
|
||||
"todo": "Todo",
|
||||
"todo": "Promemoria",
|
||||
"contacts": "Contatti",
|
||||
"sheet": "Fogli (Beta)",
|
||||
"sheet": "Fogli",
|
||||
"teams": "Team"
|
||||
},
|
||||
"button_newpad": "Nuovo pad di Testo",
|
||||
"button_newcode": "Nuovo pad di Code",
|
||||
"button_newcode": "Nuovo pad di Codice",
|
||||
"button_newpoll": "Nuovo Sondaggio",
|
||||
"button_newslide": "Nuova Presentazione",
|
||||
"button_newwhiteboard": "Nuova Lavagna",
|
||||
@@ -34,7 +34,7 @@
|
||||
"inactiveError": "Questo pad è stato cancellato per inattività. Premi Esc per creare un nuovo pad.",
|
||||
"chainpadError": "Si è verificato un errore critico nell'aggiornamento del tuo contenuto. Questa pagina è in modalità solo lettura per assicurarci che non perderai il tuo lavoro..<br>Premi <em>Esc</em> per continuare a visualizzare questo pad, o ricarica la pagina per provare a modificarlo di nuovo.",
|
||||
"invalidHashError": "Il documento richiesto ha un URL non valido.",
|
||||
"errorCopy": " Puoi ancora copiare il contenuto altrove premendo <em>Esc</em>.<br>Una volta abbandonata questa pagina, non sarà possibile recuperarlo!",
|
||||
"errorCopy": " Puoi ancora accedere al contenuto premendo <em>Esc</em>.<br>Una volta chiusa questa finestra, non sarà possibile accedere di nuovo.",
|
||||
"errorRedirectToHome": "Premi <em>Esc</em> per essere reindirizzato al tuo CryptDrive.",
|
||||
"newVersionError": "Una nuova versione di CryptPad è disponibile. <br><a href='#'>Ricarica</a> per usare la nuova versione, o premi Esc per accedere al contenuto in <b>modalità offline</b>.",
|
||||
"loading": "Caricamento...",
|
||||
@@ -181,7 +181,6 @@
|
||||
"okButton": "OK (Enter)",
|
||||
"cancel": "Cancella",
|
||||
"cancelButton": "Cancella (Esc)",
|
||||
"doNotAskAgain": "Non chiedere più (Esc)",
|
||||
"show_help_button": "Mostra l'aiuto",
|
||||
"hide_help_button": "Nascondi l'aiuto",
|
||||
"help_button": "Aiuto",
|
||||
@@ -447,19 +446,19 @@
|
||||
"settings_exportTitle": "Esporta il tuo CryptDrive",
|
||||
"settings_exportDescription": "Per favore attendi mentre scarichiamo e decriptiamo i tuoi documenti. Potrebbe richiedere qualche minuto. Chiudere la finestra interromperà il processo.",
|
||||
"settings_exportFailed": "Se il pad richiede più di un minuto per essere scaricato, non sarà incluso nell'export. Un link a qualsiasi pad non esportato sarà mostrato.",
|
||||
"settings_exportWarning": "",
|
||||
"settings_exportCancel": "",
|
||||
"settings_export_reading": "",
|
||||
"settings_export_download": "",
|
||||
"settings_export_compressing": "",
|
||||
"settings_export_done": "",
|
||||
"settings_exportError": "",
|
||||
"settings_exportErrorDescription": "",
|
||||
"settings_exportErrorEmpty": "",
|
||||
"settings_exportErrorMissing": "",
|
||||
"settings_exportErrorOther": "",
|
||||
"settings_exportWarning": "Nota bene: questo strumento è ancora in versione beta e può presentare problemi di scalabilità. Per migliorare le prestazioni, è consigliabile lasciare attiva questa tab.",
|
||||
"settings_exportCancel": "Sei sicuro di voler cancellare l'export? Dovrai iniziare da capo la prossima volta.",
|
||||
"settings_export_reading": "Lettura del tuo CryptDrive in corso...",
|
||||
"settings_export_download": "Scaricamento e decriptazione dei tuoi documenti in corso...",
|
||||
"settings_export_compressing": "Compressione dei dati in corso...",
|
||||
"settings_export_done": "Il tuo download è pronto!",
|
||||
"settings_exportError": "Visualizza errori",
|
||||
"settings_exportErrorDescription": "Non siamo riusciti ad aggiungere i seguenti documenti all'export:",
|
||||
"settings_exportErrorEmpty": "Questo documento non può essere esportato (contenuto vuoto o invalido).",
|
||||
"settings_exportErrorMissing": "Questo documento non è stato trovato nei nostri server (scaduto o rimosso dal suo proprietario)",
|
||||
"settings_exportErrorOther": "È accaduto un errore durante l'esportazione di questo documento: {0}",
|
||||
"settings_resetNewTitle": "Pulisci CryptDrive",
|
||||
"settings_resetButton": "",
|
||||
"settings_resetButton": "Rimuovi",
|
||||
"settings_reset": "Rimuovi tutti i file e le cartelle dal tuo CryptDrive",
|
||||
"settings_resetPrompt": "",
|
||||
"settings_resetDone": "",
|
||||
@@ -513,5 +512,9 @@
|
||||
},
|
||||
"readme_cat3_l1": "Con l'editor di codice di CryptPad, puoi collaborare su linguaggi di programmazione come Javascript e linguaggi di markup come HTML o Markdown",
|
||||
"settings_codeSpellcheckLabel": "Abilita la revisione ortografica nell'editor di codice",
|
||||
"team_inviteLinkError": "Si è verificato un errore durante la creazione del link."
|
||||
"team_inviteLinkError": "Si è verificato un errore durante la creazione del link.",
|
||||
"register_emailWarning1": "Puoi farlo se vuoi, ma non verrà inviato ai nostri server.",
|
||||
"register_emailWarning2": "Non sarai in grado di resettare la tua password usando la tua email, a differenza di come puoi fare con molti altri servizi.",
|
||||
"register_emailWarning3": "Se hai capito, ma intendi comunque usare la tua email come nome utente, clicca OK.",
|
||||
"oo_sheetMigration_anonymousEditor": "Le modifiche da parte di utenti anonimi a questo foglio di calcolo sono disabilitate finchè un utente registrato non lo aggiorna all'ultima versione."
|
||||
}
|
||||
|
||||
@@ -184,7 +184,6 @@
|
||||
"okButton": "OK (enter)",
|
||||
"cancel": "Cancel",
|
||||
"cancelButton": "Cancel (esc)",
|
||||
"doNotAskAgain": "Don't ask me again (Esc)",
|
||||
"show_help_button": "Show help",
|
||||
"hide_help_button": "Hide help",
|
||||
"help_button": "Help",
|
||||
@@ -282,7 +281,7 @@
|
||||
"profile_description": "Description",
|
||||
"profile_fieldSaved": "New value saved: {0}",
|
||||
"profile_viewMyProfile": "View my profile",
|
||||
"userlist_addAsFriendTitle": "Send \"{0}\" a friend request",
|
||||
"userlist_addAsFriendTitle": "Send \"{0}\" a contact request",
|
||||
"contacts_title": "Contacts",
|
||||
"contacts_addError": "Error while adding that contact to the list",
|
||||
"contacts_added": "Contact invite accepted.",
|
||||
@@ -302,7 +301,7 @@
|
||||
"contacts_confirmRemoveHistory": "Are you sure you want to permanently remove your chat history? Data cannot be restored",
|
||||
"contacts_removeHistoryServerError": "There was an error while removing your chat history. Try again later",
|
||||
"contacts_fetchHistory": "Retrieve older history",
|
||||
"contacts_friends": "Friends",
|
||||
"contacts_friends": "Contacts",
|
||||
"contacts_rooms": "Rooms",
|
||||
"contacts_leaveRoom": "Leave this room",
|
||||
"contacts_online": "Another user from this room is online",
|
||||
@@ -535,8 +534,8 @@
|
||||
"settings_creationSkipFalse": "Display",
|
||||
"settings_templateSkip": "Skip the template selection modal",
|
||||
"settings_templateSkipHint": "When you create a new empty pad, if you have stored templates for this type of pad, a modal appears to ask if you want to use a template. Here you can choose to never show this modal and so to never use a template.",
|
||||
"settings_ownDriveTitle": "Enable latest account features",
|
||||
"settings_ownDriveHint": "For technical reasons, older accounts do not have access to all of our latest features. A free upgrade to a new account will prepare your CryptDrive for upcoming features without disrupting your usual activities.",
|
||||
"settings_ownDriveTitle": "Update Account",
|
||||
"settings_ownDriveHint": "Older accounts do not have access to the latest features, due to technical reasons. A free update will enable current features, and prepare your CryptDrive for future updates.",
|
||||
"settings_ownDriveButton": "Upgrade your account",
|
||||
"settings_ownDriveConfirm": "Upgrading your account may take some time. You will need to log back in on all your devices. Are you sure?",
|
||||
"settings_ownDrivePending": "Your account is being upgraded. Please do not close or reload this page until the process has completed.",
|
||||
@@ -689,7 +688,7 @@
|
||||
"features_f_social": "Social applications",
|
||||
"features_f_social_note": "Create a profile, use an avatar, chat with contacts",
|
||||
"features_f_file1": "Upload and share files",
|
||||
"features_f_file1_note": "Share files with your friends or embed them in your pads",
|
||||
"features_f_file1_note": "Share files with your contacts or embed them in your pads",
|
||||
"features_f_storage1": "Permanent storage (50MB)",
|
||||
"features_f_storage1_note": "Pads stored in your CryptDrive are never deleted for inactivity",
|
||||
"features_f_register": "Register for free",
|
||||
@@ -792,7 +791,7 @@
|
||||
"a": "Registered users have access to a number of features unavailable to unregistered users. There's a chart <a href='/features.html' target='_blank'>here</a>."
|
||||
},
|
||||
"share": {
|
||||
"q": "How can I share encrypted pads with my friends?",
|
||||
"q": "How can I share encrypted pads with my contacts?",
|
||||
"a": "CryptPad puts the secret encryption key to your pad after the <em>#</em> character in the URL. Anything after this character is not sent to the server, so we never have access to your encryption keys. By sharing the link to a pad, you share the ability to read and access it."
|
||||
},
|
||||
"remove": {
|
||||
@@ -824,7 +823,7 @@
|
||||
"title": "Other questions",
|
||||
"pay": {
|
||||
"q": "Why should I pay when so many features are free?",
|
||||
"a": "We give supporters additional storage and the ability to increase their friends' quotas (<a href='https://accounts.cryptpad.fr/#/faq' target='_blank'>learn more</a>).<br><br>Beyond these short term benefits, by subscribing with a premium account you help to fund continued, active development of CryptPad. That includes fixing bugs, adding new features, and making it easier for others to help host CryptPad themselves. Additionally, you help to prove to other service providers that people are willing to support privacy enhancing technologies. It is our hope that eventually business models based on selling user data will become a thing of the past.<br><br>Finally, we offer most of CryptPad's functionality for free because we believe everyone deserves personal privacy, not just those with disposable income. By supporting us, you help us continue to make it possible for underprivileged populations to access these basic features without a price tag attached."
|
||||
"a": "We give supporters additional storage and the ability to increase their contacts' quotas (<a href='https://accounts.cryptpad.fr/#/faq' target='_blank'>learn more</a>).<br><br>Beyond these short term benefits, by subscribing with a premium account you help to fund continued, active development of CryptPad. That includes fixing bugs, adding new features, and making it easier for others to help host CryptPad themselves. Additionally, you help to prove to other service providers that people are willing to support privacy enhancing technologies. It is our hope that eventually business models based on selling user data will become a thing of the past.<br><br>Finally, we offer most of CryptPad's functionality for free because we believe everyone deserves personal privacy, not just those with disposable income. By supporting us, you help us continue to make it possible for underprivileged populations to access these basic features without a price tag attached."
|
||||
},
|
||||
"goal": {
|
||||
"q": "What is your goal?",
|
||||
@@ -885,7 +884,7 @@
|
||||
"colors": "Change the text and background colors using the <span class=\"fa fa-i-cursor\"></span> and <span class=\"fa fa-square\"></span> buttons"
|
||||
},
|
||||
"poll": {
|
||||
"decisions": "Make decisions in private among trusted friends",
|
||||
"decisions": "Make decisions in private among trusted contacts",
|
||||
"options": "Propose options, and express your preferences",
|
||||
"choices": "Click cells in your column to cycle through yes (<strong>✔</strong>), maybe (<strong>~</strong>), or no (<strong>✖</strong>)",
|
||||
"submit": "Click <strong>submit</strong> to make your choices visible to others"
|
||||
@@ -903,7 +902,7 @@
|
||||
},
|
||||
"driveReadmeTitle": "What is CryptPad?",
|
||||
"readme_welcome": "Welcome to CryptPad !",
|
||||
"readme_p1": "Welcome to CryptPad, this is where you can take note of things alone and with friends.",
|
||||
"readme_p1": "Welcome to CryptPad, this is where you can take note of things alone and with contacts.",
|
||||
"readme_p2": "This pad will give you a quick walk through of how you can use CryptPad to take notes, keep them organized and work together on them.",
|
||||
"readme_cat1": "Get to know your CryptDrive",
|
||||
"readme_cat1_l1": "Make a pad: In your CryptDrive, click {0} then {1} and you can make a pad.",
|
||||
@@ -936,7 +935,7 @@
|
||||
"feedback_about": "If you're reading this, you were probably curious why CryptPad is requesting web pages when you perform certain actions",
|
||||
"feedback_privacy": "We care about your privacy, and at the same time we want CryptPad to be very easy to use. We use this file to figure out which UI features matter to our users, by requesting it along with a parameter specifying which action was taken.",
|
||||
"feedback_optout": "If you would like to opt out, visit <a href='/settings/'>your user settings page</a>, where you'll find a checkbox to enable or disable user feedback",
|
||||
"creation_404": "This pad not longer exists. Use the following form to create a new pad.",
|
||||
"creation_404": "This pad no longer exists. Use the following form to create a new pad.",
|
||||
"creation_ownedTitle": "Type of pad",
|
||||
"creation_owned": "Owned pad",
|
||||
"creation_ownedTrue": "Owned pad",
|
||||
@@ -956,7 +955,6 @@
|
||||
"creation_noTemplate": "No template",
|
||||
"creation_newTemplate": "New template",
|
||||
"creation_create": "Create",
|
||||
"creation_saveSettings": "Don't show this again",
|
||||
"creation_settings": "View more settings",
|
||||
"creation_rememberHelp": "Visit your Settings page to reset this preference",
|
||||
"creation_owners": "Owners",
|
||||
@@ -1028,7 +1026,6 @@
|
||||
"crowdfunding_popup_text": "<h3>We need your help!</h3>To ensure that CryptPad is actively developed, consider supporting the project via the <a href=\"https://opencollective.com/cryptpad\">OpenCollective page</a>, where you can see our <b>Roadmap</b> and <b>Funding goals</b>.",
|
||||
"crowdfunding_popup_yes": "Go to OpenCollective",
|
||||
"crowdfunding_popup_no": "Not now",
|
||||
"crowdfunding_popup_never": "Don't ask me again",
|
||||
"survey": "CryptPad survey",
|
||||
"markdown_toc": "Contents",
|
||||
"fm_expirablePad": "Expires: {0}",
|
||||
@@ -1075,18 +1072,19 @@
|
||||
"friendRequest_later": "Decide later",
|
||||
"friendRequest_accept": "Accept (Enter)",
|
||||
"friendRequest_decline": "Decline",
|
||||
"friendRequest_declined": "<b>{0}</b> declined your friend request",
|
||||
"friendRequest_accepted": "<b>{0}</b> accepted your friend request",
|
||||
"friendRequest_received": "<b>{0}</b> would like to be your friend",
|
||||
"friendRequest_notification": "<b>{0}</b> sent you a friend request",
|
||||
"friendRequest_declined": "<b>{0}</b> declined your contact request",
|
||||
"friendRequest_accepted": "<b>{0}</b> accepted your contact request",
|
||||
"friendRequest_received": "<b>{0}</b> would like to be your contact",
|
||||
"friendRequest_notification": "<b>{0}</b> sent you a contact request",
|
||||
"notifications_empty": "No notifications available",
|
||||
"notifications_title": "You have unread notifications",
|
||||
"profile_addDescription": "Add a description",
|
||||
"profile_editDescription": "Edit your description",
|
||||
"profile_addLink": "Add a link to your website",
|
||||
"profile_info": "Other users can find your profile through your avatar in document user lists.",
|
||||
"profile_friendRequestSent": "Friend request pending...",
|
||||
"profile_friend": "{0} is your friend",
|
||||
"profile_friendRequestSent": "Contact request pending...",
|
||||
"isContact": "{0} is one of your contacts",
|
||||
"isNotContact": "{0} is <b>not</b> one of your contacts",
|
||||
"notification_padShared": "{0} has shared a pad with you: <b>{1}</b>",
|
||||
"notification_fileShared": "{0} has shared a file with you: <b>{1}</b>",
|
||||
"notification_folderShared": "{0} has shared a folder with you: <b>{1}</b>",
|
||||
@@ -1097,7 +1095,7 @@
|
||||
"share_withFriends": "Share",
|
||||
"notifications_dismiss": "Dismiss",
|
||||
"fm_info_sharedFolderHistory": "This is only the history of your shared folder: <b>{0}</b><br/>Your CryptDrive will stay in read-only mode while you navigate.",
|
||||
"share_description": "Choose what you'd like to share and either get the link or send it directly to your CryptPad friends.",
|
||||
"share_description": "Choose what you'd like to share and either get the link or send it directly to your CryptPad contacts.",
|
||||
"supportPage": "Support",
|
||||
"admin_cat_support": "Support",
|
||||
"admin_supportInitHelp": "Your server is not yet configured to have a support mailbox. If you want a support mailbox to receive messages from your users, you should ask your server administrator to run the script located in \"./scripts/generate-admin-keys.js\", then store the public key in the \"config.js\" file and send you the private key.",
|
||||
@@ -1130,7 +1128,7 @@
|
||||
"notificationsPage": "Notifications",
|
||||
"openNotificationsApp": "Open notifications panel",
|
||||
"notifications_cat_all": "All",
|
||||
"notifications_cat_friends": "Friend requests",
|
||||
"notifications_cat_friends": "Contact requests",
|
||||
"notifications_cat_pads": "Shared with me",
|
||||
"notifications_cat_archived": "History",
|
||||
"notifications_dismissAll": "Dismiss all",
|
||||
@@ -1138,8 +1136,6 @@
|
||||
"requestEdit_button": "Request edit rights",
|
||||
"requestEdit_dialog": "Are you sure you'd like to ask the owner of this pad for the ability to edit?",
|
||||
"requestEdit_confirm": "{1} has asked for the ability to edit the pad <b>{0}</b>. Would you like to grant them access?",
|
||||
"requestEdit_fromFriend": "You are friends with {0}",
|
||||
"requestEdit_fromStranger": "You are <b>not</b> friends with {0}",
|
||||
"requestEdit_viewPad": "Open the pad in a new tab",
|
||||
"later": "Decide later",
|
||||
"requestEdit_request": "{1} wants to edit the pad <b>{0}</b>",
|
||||
@@ -1153,7 +1149,7 @@
|
||||
"features_emailRequired": "Email address required",
|
||||
"owner_removeText": "Remove an existing owner",
|
||||
"owner_removePendingText": "Cancel a pending offer",
|
||||
"owner_addText": "Offer co-ownership to a friend",
|
||||
"owner_addText": "Offer co-ownership to a contact",
|
||||
"owner_unknownUser": "Unknown user",
|
||||
"owner_removeButton": "Remove selected owners",
|
||||
"owner_removePendingButton": "Cancel selected offers",
|
||||
@@ -1169,9 +1165,8 @@
|
||||
"owner_removed": "{0} has removed your ownership of <b>{1}</b>",
|
||||
"owner_removedPending": "{0} has canceled your ownership offer for <b>{1}</b>",
|
||||
"share_linkTeam": "Add to team drive",
|
||||
"team_pickFriends": "Choose which friends to invite to this team",
|
||||
"team_pickFriends": "Choose which contacts to invite to this team",
|
||||
"team_inviteModalButton": "Invite",
|
||||
"team_noFriend": "You haven't connected with any friends on CryptPad yet.",
|
||||
"team_pcsSelectLabel": "Store in",
|
||||
"team_pcsSelectHelp": "Creating an owned pad in your team's drive will give ownership to the team.",
|
||||
"team_invitedToTeam": "{0} has invited you to join their team: <b>{1}</b>",
|
||||
@@ -1193,7 +1188,7 @@
|
||||
"team_rosterPromote": "Promote",
|
||||
"team_rosterDemote": "Demote",
|
||||
"team_rosterKick": "Kick from the team",
|
||||
"team_inviteButton": "Invite friends",
|
||||
"team_inviteButton": "Invite contacts",
|
||||
"team_leaveButton": "Leave this team",
|
||||
"team_leaveConfirm": "If you leave this team you will lose access to its CryptDrive, chat history, and other contents. Are you sure?",
|
||||
"team_owner": "Owners",
|
||||
@@ -1294,5 +1289,13 @@
|
||||
"oo_exportInProgress": "Export in progress",
|
||||
"oo_sheetMigration_loading": "Upgrading your spreadsheet to the latest version",
|
||||
"oo_sheetMigration_complete": "Updated version available, press OK to reload.",
|
||||
"oo_sheetMigration_anonymousEditor": "Editing this spreadsheet is disabled for anonymous users until it is upgraded to the latest version by a registered user."
|
||||
"oo_sheetMigration_anonymousEditor": "Editing this spreadsheet is disabled for anonymous users until it is upgraded to the latest version by a registered user.",
|
||||
"imprint": "Legal notice",
|
||||
"settings_cat_security": "Confidentiality",
|
||||
"settings_safeLinksTitle": "Safe Links",
|
||||
"settings_safeLinksCheckbox": "Enable safe links",
|
||||
"safeLinks_error": "This link does not give you access to the document",
|
||||
"dontShowAgain": "Don't show again",
|
||||
"profile_login": "You need to log in to add this user to your contacts",
|
||||
"settings_safeLinksHint": "CryptPad includes the keys to decrypt your pads in their links. Anyone with access to your browsing history can potentially read your data. This includes intrusive browser extensions and browsers that sync your history across devices. Enabling \"safe links\" prevents the keys from entering your browsing history or being displayed in your address bar whenever possible. We strongly recommend that you enable this feature and use the {0} Share menu."
|
||||
}
|
||||
|
||||
@@ -193,7 +193,6 @@
|
||||
"crowdfunding_button": "Støtt CryptPad",
|
||||
"crowdfunding_popup_yes": "Gå til OpenCollective",
|
||||
"crowdfunding_popup_no": "Ikke nå",
|
||||
"crowdfunding_popup_never": "Ikke spør igjen takk",
|
||||
"markdown_toc": "Innhold",
|
||||
"fm_expirablePad": "Denne paden vill utgå på dato den {0}",
|
||||
"admin_authError": "Kun admin-tilgang",
|
||||
|
||||
@@ -409,7 +409,6 @@
|
||||
"fileEmbedScript": "",
|
||||
"fileEmbedTag": "",
|
||||
"ok": "",
|
||||
"doNotAskAgain": "",
|
||||
"show_help_button": "",
|
||||
"hide_help_button": "",
|
||||
"help_button": "",
|
||||
|
||||
@@ -395,7 +395,6 @@
|
||||
"fileEmbedTitle": "Include fișierul într-o pagină externă",
|
||||
"fileEmbedTag": "După care plasează această etichetă Media oriunde pe pagina unde vrei sa o plasezi",
|
||||
"ok": "Ok",
|
||||
"doNotAskAgain": "Nu mă întreba din nou (Esc)",
|
||||
"show_help_button": "Arată ajutorul",
|
||||
"hide_help_button": "Maschează ajutorul",
|
||||
"help_button": "Ajutor",
|
||||
|
||||
@@ -175,7 +175,6 @@
|
||||
"okButton": "OK (Enter)",
|
||||
"cancel": "Отмена",
|
||||
"cancelButton": "Отмена (Esc)",
|
||||
"doNotAskAgain": "Не спрашивать снова (Esc)",
|
||||
"show_help_button": "Показать справку",
|
||||
"hide_help_button": "Скрыть справку",
|
||||
"help_button": "Справка",
|
||||
@@ -299,7 +298,6 @@
|
||||
"fm_removeSeveralPermanentlyDialog": "Вы уверены, что хотите навсегда удалить {0} элементов из вашего Хранилища?",
|
||||
"crowdfunding_button": "Поддержите CryptPad",
|
||||
"crowdfunding_popup_no": "Не сейчас",
|
||||
"crowdfunding_popup_never": "Не спрашивать меня снова",
|
||||
"markdown_toc": "Содержимое",
|
||||
"fm_expirablePad": "Этот блокнот истечет {0}",
|
||||
"fileEmbedTitle": "Встроить файл во внешнюю страницу",
|
||||
|
||||
Reference in New Issue
Block a user