Merge branch 'staging' into accounts

This commit is contained in:
yflory 2020-09-10 14:53:24 +02:00
commit cbd36787b3
26 changed files with 1665 additions and 1072 deletions

View File

@ -17,6 +17,7 @@ www/common/old-media-tag.js
www/scratch
www/accounts
www/lib
www/accounts
www/common/toolbar.js
www/common/hyperscript.js

View File

@ -123,7 +123,10 @@ define([
};
});
var init = false;
CodeMirror.registerHelper("orgmode", "init", function (editor) {
if (init) { return; }
editor.setOption("extraKeys", {
"Tab": function(cm) { org_cycle(cm); },
"Shift-Tab": function(cm){ org_shifttab(cm); },
@ -139,6 +142,7 @@ define([
"Shift-Right": function(cm){ org_shiftright(cm); }
});
init = true;
editor.on('mousedown', toggleHandler);
editor.on('touchstart', toggleHandler);
editor.on('gutterClick', foldLine);
@ -155,6 +159,9 @@ define([
});
CodeMirror.registerHelper("orgmode", "destroy", function (editor) {
if (!init) { return; }
init = false;
editor.off('mousedown', toggleHandler);
editor.off('touchstart', toggleHandler);
editor.off('gutterClick', foldLine);

View File

@ -229,181 +229,7 @@ define([
};
};
var createShareWithFriends = function (config, onShare, linkGetter) {
var common = config.common;
var sframeChan = common.getSframeChannel();
var title = config.title;
var friends = config.friends || {};
var teams = config.teams || {};
var myName = common.getMetadataMgr().getUserData().name;
var order = [];
var smallCurves = Object.keys(friends).map(function (c) {
return friends[c].curvePublic.slice(0,8);
});
var div = h('div.contains-nav');
var $div = $(div);
// Replace "copy link" by "share with friends" if at least one friend is selected
// Also create the "share with friends" button if it doesn't exist
var refreshButtons = function () {
var $nav = $div.closest('.alertify').find('nav');
var friendMode = $div.find('.cp-usergrid-user.cp-selected').length;
if (friendMode) {
$nav.find('button.cp-share-with-friends').prop('disabled', '');
} else {
$nav.find('button.cp-share-with-friends').prop('disabled', 'disabled');
}
};
config.noInclude = true;
Object.keys(friends).forEach(function (curve) {
var data = friends[curve];
if (curve.length > 40 && data.notifications) { return; }
delete friends[curve];
});
var others = [];
if (Object.keys(friends).length) {
var friendsList = UIElements.getUserGrid(Messages.share_linkFriends, {
common: common,
data: friends,
noFilter: false,
large: true
}, refreshButtons);
var friendDiv = friendsList.div;
$div.append(friendDiv);
others = friendsList.icons;
}
if (Object.keys(teams).length) {
var teamsList = UIElements.getUserGrid(Messages.share_linkTeam, {
common: common,
noFilter: true,
large: true,
data: teams
}, refreshButtons);
$div.append(teamsList.div);
}
var shareButton = {
className: 'primary cp-share-with-friends',
name: Messages.share_withFriends,
onClick: function () {
var href;
NThen(function (waitFor) {
var w = waitFor();
// linkGetter can be async if this is a burn after reading URL
var res = linkGetter({}, function (url) {
if (!url) {
waitFor.abort();
return;
}
href = url;
setTimeout(w);
});
if (res && /^http/.test(res)) {
href = Hash.getRelativeHref(res);
setTimeout(w);
return;
}
}).nThen(function () {
var $friends = $div.find('.cp-usergrid-user.cp-selected');
$friends.each(function (i, el) {
var curve = $(el).attr('data-curve');
var ed = $(el).attr('data-ed');
var friend = curve && friends[curve];
var team = teams[ed];
// If the selected element is a friend or a team without edit right,
// send a notification
var mailbox = friend || ((team && team.viewer) ? team : undefined);
if (mailbox) { // Friend
if (friends[curve] && !mailbox.notifications) { return; }
if (mailbox.notifications && mailbox.curvePublic) {
common.mailbox.sendTo("SHARE_PAD", {
href: href,
password: config.password,
isTemplate: config.isTemplate,
name: myName,
title: title
}, {
viewed: team && team.id,
channel: mailbox.notifications,
curvePublic: mailbox.curvePublic
});
return;
}
}
// If it's a team with edit right, add the pad directly
if (!team) { return; }
sframeChan.query('Q_STORE_IN_TEAM', {
href: href,
password: config.password,
path: config.isTemplate ? ['template'] : undefined,
title: title,
teamId: team.id
}, function (err) {
if (err) { return void console.error(err); }
});
});
UI.findCancelButton().click();
// Update the "recently shared with" array:
// Get the selected curves
var curves = $friends.toArray().map(function (el) {
return ($(el).attr('data-curve') || '').slice(0,8);
}).filter(function (x) { return x; });
// Prepend them to the "order" array
Array.prototype.unshift.apply(order, curves);
order = Util.deduplicateString(order);
// Make sure we don't have "old" friends and save
order = order.filter(function (curve) {
return smallCurves.indexOf(curve) !== -1;
});
common.setAttribute(['general', 'share-friends'], order);
if (onShare) {
onShare.fire();
}
});
},
keys: [13]
};
common.getAttribute(['general', 'share-friends'], function (err, val) {
order = val || [];
// Sort friends by "recently shared with"
others.sort(function (a, b) {
var ca = ($(a).attr('data-curve') || '').slice(0,8);
var cb = ($(b).attr('data-curve') || '').slice(0,8);
if (!ca && !cb) { return 0; }
if (!ca) { return 1; }
if (!cb) { return -1; }
var ia = order.indexOf(ca);
var ib = order.indexOf(cb);
if (ia === -1 && ib === -1) { return 0; }
if (ia === -1) { return 1; }
if (ib === -1) { return -1; }
return ia - ib;
});
// Reorder the friend icons
others.forEach(function (el, i) {
$(el).attr('data-order', i).css('order', i);
});
// Display them
$(friendDiv).find('.cp-usergrid-grid').detach();
$(friendDiv).append(h('div.cp-usergrid-grid', others));
refreshButtons();
});
return {
content: div,
buttons: [shareButton]
};
};
var noContactsMessage = function(common){
UIElements.noContactsMessage = function (common) {
var metadataMgr = common.getMetadataMgr();
var data = metadataMgr.getUserData();
var origin = metadataMgr.getPrivateData().origin;
@ -446,655 +272,6 @@ define([
}
};
var getEditableTeams = function (common, config) {
var privateData = common.getMetadataMgr().getPrivateData();
var teamsData = Util.tryParse(JSON.stringify(privateData.teams)) || {};
var teams = {};
Object.keys(teamsData).forEach(function (id) {
// config.teamId only exists when we're trying to share a pad from a team drive
// In this case, we don't want to share the pad with the current team
if (config.teamId && config.teamId === id) { return; }
var t = teamsData[id];
teams[t.edPublic] = {
viewer: !teamsData[id].hasSecondaryKey,
notifications: t.notifications,
curvePublic: t.curvePublic,
displayName: t.name,
edPublic: t.edPublic,
avatar: t.avatar,
id: id
};
});
return teams;
};
var makeBurnAfterReadingUrl = function (common, href, channel, cb) {
var keyPair = Hash.generateSignPair();
var parsed = Hash.parsePadUrl(href);
var newHref = parsed.getUrl({
ownerKey: keyPair.safeSignKey
});
var sframeChan = common.getSframeChannel();
var rtChannel;
NThen(function (waitFor) {
if (parsed.type !== "sheet") { return; }
common.getPadAttribute('rtChannel', waitFor(function (err, chan) {
rtChannel = chan;
}));
}).nThen(function (waitFor) {
sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel,
command: 'ADD_OWNERS',
value: [keyPair.validateKey]
}, waitFor(function (err) {
if (err) {
waitFor.abort();
UI.warn(Messages.error);
}
}));
if (rtChannel) {
sframeChan.query('Q_SET_PAD_METADATA', {
channel: rtChannel,
command: 'ADD_OWNERS',
value: [keyPair.validateKey]
}, waitFor(function (err) {
if (err) {
console.error(err);
}
}));
}
}).nThen(function () {
cb(newHref);
});
};
UIElements.createShareModal = function (config) {
var origin = config.origin;
var pathname = config.pathname;
var hashes = config.hashes;
var common = config.common;
if (!hashes || (!hashes.editHash && !hashes.viewHash)) { return; }
// check if the pad is password protected
var hash = hashes.editHash || hashes.viewHash;
var href = origin + pathname + '#' + hash;
var parsedHref = Hash.parsePadUrl(href);
var hasPassword = parsedHref.hashData.password;
var makeFaqLink = function () {
var link = h('span', [
h('i.fa.fa-question-circle'),
h('a', {href: '#'}, Messages.passwordFaqLink)
]);
$(link).click(function () {
common.openURL(config.origin + "/faq.html#security-pad_password");
});
return link;
};
var parsed = Hash.parsePadUrl(pathname);
var canPresent = ['code', 'slide'].indexOf(parsed.type) !== -1;
var canBAR = parsed.type !== 'drive';
var burnAfterReading = (hashes.viewHash && canBAR) ?
UI.createRadio('accessRights', 'cp-share-bar', Messages.burnAfterReading_linkBurnAfterReading, false, {
mark: {tabindex:1},
label: {style: "display: none;"}
}) : undefined;
var rights = h('div.msg.cp-inline-radio-group', [
h('label', Messages.share_linkAccess),
h('div.radio-group',[
UI.createRadio('accessRights', 'cp-share-editable-false',
Messages.share_linkView, true, { mark: {tabindex:1} }),
canPresent ? UI.createRadio('accessRights', 'cp-share-present',
Messages.share_linkPresent, false, { mark: {tabindex:1} }) : undefined,
UI.createRadio('accessRights', 'cp-share-editable-true',
Messages.share_linkEdit, false, { mark: {tabindex:1} })]),
burnAfterReading
]);
// Burn after reading
// Check if we are an owner of this pad. If we are, we can show the burn after reading option.
// When BAR is selected, display a red message indicating the consequence and add
// the options to generate the BAR url
var barAlert = h('div.alert.alert-danger.cp-alertify-bar-selected', {
style: 'display: none;'
}, Messages.burnAfterReading_warningLink);
var channel = Hash.getSecrets('pad', hash, config.password).channel;
common.getPadMetadata({
channel: channel
}, function (obj) {
if (!obj || obj.error) { return; }
var priv = common.getMetadataMgr().getPrivateData();
// Not an owner: don't display the burn after reading option
if (!Array.isArray(obj.owners) || obj.owners.indexOf(priv.edPublic) === -1) {
$(burnAfterReading).remove();
return;
}
// When the burn after reading option is selected, transform the modal buttons
$(burnAfterReading).css({
display: 'flex'
});
});
var $rights = $(rights);
var saveValue = function () {
var edit = Util.isChecked($rights.find('#cp-share-editable-true'));
var present = Util.isChecked($rights.find('#cp-share-present'));
common.setAttribute(['general', 'share'], {
edit: edit,
present: present
});
};
var burnAfterReadingUrl;
var getLinkValue = function (initValue, cb) {
var val = initValue || {};
var edit = val.edit !== undefined ? val.edit : Util.isChecked($rights.find('#cp-share-editable-true'));
var embed = val.embed;
var present = val.present !== undefined ? val.present : Util.isChecked($rights.find('#cp-share-present'));
var burnAfterReading = Util.isChecked($rights.find('#cp-share-bar'));
if (burnAfterReading && !burnAfterReadingUrl) {
if (cb) { // Called from the contacts tab, "share" button
var barHref = origin + pathname + '#' + (hashes.viewHash || hashes.editHash);
return makeBurnAfterReadingUrl(common, barHref, channel, function (url) {
cb(url);
});
}
return Messages.burnAfterReading_generateLink;
}
var hash = (!hashes.viewHash || (edit && hashes.editHash)) ? hashes.editHash : hashes.viewHash;
var href = burnAfterReading ? burnAfterReadingUrl : (origin + pathname + '#' + hash);
var parsed = Hash.parsePadUrl(href);
return origin + parsed.getUrl({embed: embed, present: present});
};
var makeCancelButton = function() {
return {
className: 'cancel',
name: Messages.cancel,
onClick: function () {},
keys: [27]
};
};
// Share link tab
var linkContent = config.sharedFolder ? [
h('label', Messages.sharedFolders_share),
h('br'),
] : [
UI.createCheckbox('cp-share-embed', Messages.share_linkEmbed, false, { mark: {tabindex:1} }),
];
linkContent.push(h('div.cp-spacer'));
linkContent.push(UI.dialog.selectableArea('', { id: 'cp-share-link-preview', tabindex: 1, rows:3}));
// Show alert if the pad is password protected
if (hasPassword) {
linkContent.push(h('div.alert.alert-primary', [
h('i.fa.fa-lock'),
Messages.share_linkPasswordAlert, h('br'),
makeFaqLink()
]));
}
// warning about sharing links
var localStore = window.cryptpadStore;
var dismissButton = h('span.fa.fa-times');
var shareLinkWarning = h('div.alert.alert-warning.dismissable',
{ style: 'display: none;' },
[
h('span.cp-inline-alert-text', Messages.share_linkWarning),
dismissButton
]);
linkContent.push(shareLinkWarning);
localStore.get('hide-alert-shareLinkWarning', function (val) {
if (val === '1') { return; }
$(shareLinkWarning).show();
$(dismissButton).on('click', function () {
localStore.put('hide-alert-shareLinkWarning', '1');
$(shareLinkWarning).remove();
});
});
linkContent.push($(barAlert).clone()[0]); // Burn after reading
var link = h('div.cp-share-modal', linkContent);
var $link = $(link);
var linkButtons = [
makeCancelButton(),
!config.sharedFolder && {
className: 'secondary cp-nobar',
name: Messages.share_linkOpen,
onClick: function () {
saveValue();
var v = getLinkValue({
embed: Util.isChecked($link.find('#cp-share-embed'))
});
window.open(v);
return true;
},
keys: [[13, 'ctrl']]
}, {
className: 'primary cp-nobar',
name: Messages.share_linkCopy,
onClick: function () {
saveValue();
var v = getLinkValue({
embed: Util.isChecked($link.find('#cp-share-embed'))
});
var success = Clipboard.copy(v);
if (success) { UI.log(Messages.shareSuccess); }
},
keys: [13]
}, {
className: 'primary cp-bar',
name: 'GENERATE LINK',
onClick: function () {
var barHref = origin + pathname + '#' + (hashes.viewHash || hashes.editHash);
makeBurnAfterReadingUrl(common, barHref, channel, function (url) {
burnAfterReadingUrl = url;
$rights.find('input[type="radio"]').trigger('change');
});
return true;
},
keys: []
}
];
var frameLink = UI.dialog.customModal(link, {
buttons: linkButtons,
onClose: config.onClose,
});
$(frameLink).find('.cp-bar').hide();
// Share with contacts tab
var teams = getEditableTeams(common, config);
config.teams = teams;
var hasFriends = Object.keys(config.friends || {}).length ||
Object.keys(teams).length;
var onFriendShare = Util.mkEvent();
var friendsObject = hasFriends ? createShareWithFriends(config, onFriendShare, getLinkValue) : noContactsMessage(common);
var friendsList = friendsObject.content;
onFriendShare.reg(saveValue);
var contactsContent = h('div.cp-share-modal');
var $contactsContent = $(contactsContent);
$contactsContent.append(friendsList);
// Show alert if the pad is password protected
if (hasPassword) {
$contactsContent.append(h('div.alert.alert-primary', [
h('i.fa.fa-unlock'),
Messages.share_contactPasswordAlert, h('br'),
makeFaqLink()
]));
}
$(contactsContent).append($(barAlert).clone()); // Burn after reading
var contactButtons = friendsObject.buttons;
contactButtons.unshift(makeCancelButton());
var onShowContacts = function () {
if (!hasFriends) {
$rights.hide();
}
};
var frameContacts = UI.dialog.customModal(contactsContent, {
buttons: contactButtons,
onClose: config.onClose,
});
// Embed tab
var getEmbedValue = function () {
var url = getLinkValue({
embed: true
});
return '<iframe src="' + url + '"></iframe>';
};
var embedContent = [
h('p', Messages.viewEmbedTag),
UI.dialog.selectableArea(getEmbedValue(), { id: 'cp-embed-link-preview', tabindex: 1, rows: 3})
];
// Show alert if the pad is password protected
if (hasPassword) {
embedContent.push(h('div.alert.alert-primary', [
h('i.fa.fa-lock'), ' ',
Messages.share_embedPasswordAlert, h('br'),
makeFaqLink()
]));
}
var embedButtons = [
makeCancelButton(), {
className: 'primary',
name: Messages.share_linkCopy,
onClick: function () {
var v = getEmbedValue();
var success = Clipboard.copy(v);
if (success) { UI.log(Messages.shareSuccess); }
},
keys: [13]
}];
var onShowEmbed = function () {
$rights.find('#cp-share-bar').closest('label').hide();
$rights.find('input[type="radio"]:enabled').first().prop('checked', 'checked');
$rights.find('input[type="radio"]').trigger('change');
};
var embed = h('div.cp-share-modal', embedContent);
var $embed = $(embed);
var frameEmbed = UI.dialog.customModal(embed, {
buttons: embedButtons,
onClose: config.onClose,
});
// update values for link and embed preview when radio btns change
$embed.find('#cp-embed-link-preview').val(getEmbedValue());
$link.find('#cp-share-link-preview').val(getLinkValue());
$rights.find('input[type="radio"]').on('change', function () {
$link.find('#cp-share-link-preview').val(getLinkValue({
embed: Util.isChecked($link.find('#cp-share-embed'))
}));
// Hide or show the burn after reading alert
if (Util.isChecked($rights.find('#cp-share-bar')) && !burnAfterReadingUrl) {
$('.cp-alertify-bar-selected').show();
// Show burn after reading button
$('.alertify').find('.cp-bar').show();
$('.alertify').find('.cp-nobar').hide();
return;
}
$embed.find('#cp-embed-link-preview').val(getEmbedValue());
// Hide burn after reading button
$('.alertify').find('.cp-nobar').show();
$('.alertify').find('.cp-bar').hide();
$('.cp-alertify-bar-selected').hide();
});
$link.find('input[type="checkbox"]').on('change', function () {
$link.find('#cp-share-link-preview').val(getLinkValue({
embed: Util.isChecked($link.find('#cp-share-embed'))
}));
});
// Create modal
var resetTab = function () {
$rights.show();
$rights.find('label.cp-radio').show();
};
var tabs = [{
title: Messages.share_contactCategory,
icon: "fa fa-address-book",
content: frameContacts,
active: hasFriends,
onShow: onShowContacts,
onHide: resetTab
}, {
title: Messages.share_linkCategory,
icon: "fa fa-link",
content: frameLink,
active: !hasFriends
}, {
title: Messages.share_embedCategory,
icon: "fa fa-code",
content: frameEmbed,
onShow: onShowEmbed,
onHide: resetTab
}];
if (typeof(AppConfig.customizeShareOptions) === 'function') {
AppConfig.customizeShareOptions(hashes, tabs, {
type: 'DEFAULT',
origin: origin,
pathname: pathname
});
}
var modal = UI.dialog.tabs(tabs);
$(modal).find('.alertify-tabs-titles').after(rights);
// disable edit share options if you don't have edit rights
if (!hashes.editHash) {
$rights.find('#cp-share-editable-false').attr('checked', true);
$rights.find('#cp-share-editable-true').removeAttr('checked').attr('disabled', true);
} else if (!hashes.viewHash) {
$rights.find('#cp-share-editable-false').removeAttr('checked').attr('disabled', true);
$rights.find('#cp-share-present').removeAttr('checked').attr('disabled', true);
$rights.find('#cp-share-editable-true').attr('checked', true);
}
common.getAttribute(['general', 'share'], function (err, val) {
val = val || {};
if (val.present && canPresent) {
$rights.find('#cp-share-editable-false').prop('checked', false);
$rights.find('#cp-share-editable-true').prop('checked', false);
$rights.find('#cp-share-present').prop('checked', true);
} else if ((val.edit === false && hashes.viewHash) || !hashes.editHash) {
$rights.find('#cp-share-editable-false').prop('checked', true);
$rights.find('#cp-share-editable-true').prop('checked', false);
$rights.find('#cp-share-present').prop('checked', false);
} else {
$rights.find('#cp-share-editable-true').prop('checked', true);
$rights.find('#cp-share-editable-false').prop('checked', false);
$rights.find('#cp-share-present').prop('checked', false);
}
delete val.embed;
if (!canPresent) {
delete val.present;
}
$link.find('#cp-share-link-preview').val(getLinkValue(val));
});
common.getMetadataMgr().onChange(function () {
// "hashes" is only available is the secure "share" app
var _hashes = common.getMetadataMgr().getPrivateData().hashes;
if (!_hashes) { return; }
hashes = _hashes;
$link.find('#cp-share-link-preview').val(getLinkValue());
});
return modal;
};
UIElements.createFileShareModal = function (config) {
var origin = config.origin;
var pathname = config.pathname;
var hashes = config.hashes;
var common = config.common;
var fileData = config.fileData;
if (!hashes.fileHash) { throw new Error("You must provide a file hash"); }
var url = origin + pathname + '#' + hashes.fileHash;
// check if the file is password protected
var parsedHref = Hash.parsePadUrl(url);
var hasPassword = parsedHref.hashData.password;
var makeFaqLink = function () {
var link = h('span', [
h('i.fa.fa-question-circle'),
h('a', {href: '#'}, Messages.passwordFaqLink)
]);
$(link).click(function () {
common.openURL(config.origin + "/faq.html#security-pad_password");
});
return link;
};
var getLinkValue = function () { return url; };
var makeCancelButton = function() {
return {className: 'cancel',
name: Messages.cancel,
onClick: function () {},
keys: [27]};
};
// Share link tab
var linkContent = [
UI.dialog.selectableArea(getLinkValue(), { id: 'cp-share-link-preview', tabindex: 1, rows:2 })
];
// Show alert if the pad is password protected
if (hasPassword) {
linkContent.push(h('div.alert.alert-primary', [
h('i.fa.fa-lock'),
Messages.share_linkPasswordAlert, h('br'),
makeFaqLink()
]));
}
// warning about sharing links
var localStore = window.cryptpadStore;
var dismissButton = h('span.fa.fa-times');
var shareLinkWarning = h('div.alert.alert-warning.dismissable',
{ style: 'display: none;' },
[
h('span.cp-inline-alert-text', Messages.share_linkWarning),
dismissButton
]);
linkContent.push(shareLinkWarning);
localStore.get('hide-alert-shareLinkWarning', function (val) {
if (val === '1') { return; }
$(shareLinkWarning).show();
$(dismissButton).on('click', function () {
localStore.put('hide-alert-shareLinkWarning', '1');
$(shareLinkWarning).remove();
});
});
var link = h('div.cp-share-modal', linkContent);
var linkButtons = [
makeCancelButton(),
{
className: 'primary',
name: Messages.share_linkCopy,
onClick: function () {
var v = getLinkValue();
var success = Clipboard.copy(v);
if (success) { UI.log(Messages.shareSuccess);
}
},
keys: [13]
}
];
var frameLink = UI.dialog.customModal(link, {
buttons: linkButtons,
onClose: config.onClose,
});
// share with contacts tab
var teams = getEditableTeams(common, config);
config.teams = teams;
var hasFriends = Object.keys(config.friends || {}).length ||
Object.keys(teams).length;
var friendsObject = hasFriends ? createShareWithFriends(config, null, getLinkValue) : noContactsMessage(common);
var friendsList = friendsObject.content;
var contactsContent = h('div.cp-share-modal');
var $contactsContent = $(contactsContent);
$contactsContent.append(friendsList);
// Show alert if the pad is password protected
if (hasPassword) {
$contactsContent.append(h('div.alert.alert-primary', [
h('i.fa.fa-unlock'),
Messages.share_contactPasswordAlert, h('br'),
makeFaqLink()
]));
}
var contactButtons = friendsObject.buttons;
contactButtons.unshift(makeCancelButton());
var frameContacts = UI.dialog.customModal(contactsContent, {
buttons: contactButtons,
onClose: config.onClose,
});
// Embed tab
var embed = h('div.cp-share-modal', [
h('p', Messages.fileEmbedScript),
UI.dialog.selectable(common.getMediatagScript()),
h('p', Messages.fileEmbedTag),
UI.dialog.selectable(common.getMediatagFromHref(fileData)),
]);
// Show alert if the pad is password protected
if (hasPassword) {
embed.append(h('div.alert.alert-primary', [
h('i.fa.fa-lock'), ' ',
Messages.share_embedPasswordAlert, h('br'),
makeFaqLink()
]));
}
var embedButtons = [{
className: 'cancel',
name: Messages.cancel,
onClick: function () {},
keys: [27]
}, {
className: 'primary',
name: Messages.share_mediatagCopy,
onClick: function () {
var v = common.getMediatagFromHref(fileData);
var success = Clipboard.copy(v);
if (success) { UI.log(Messages.shareSuccess); }
},
keys: [13]
}];
var frameEmbed = UI.dialog.customModal(embed, {
buttons: embedButtons,
onClose: config.onClose,
});
// Create modal
var tabs = [{
title: Messages.share_contactCategory,
icon: "fa fa-address-book",
content: frameContacts,
active: hasFriends,
}, {
title: Messages.share_linkCategory,
icon: "fa fa-link",
content: frameLink,
active: !hasFriends
}, {
title: Messages.share_embedCategory,
icon: "fa fa-code",
content: frameEmbed
}];
if (typeof(AppConfig.customizeShareOptions) === 'function') {
AppConfig.customizeShareOptions(hashes, tabs, {
type: 'FILE',
origin: origin,
pathname: pathname
});
}
var modal = UI.dialog.tabs(tabs);
return modal;
};
UIElements.createInviteTeamModal = function (config) {
var common = config.common;
var hasFriends = Object.keys(config.friends || {}).length !== 0;
@ -1159,7 +336,7 @@ define([
buttons: contactsButtons
};
};
var friendsObject = hasFriends ? getContacts() : noContactsMessage(common);
var friendsObject = hasFriends ? getContacts() : UIElements.noContactsMessage(common);
var friendsList = friendsObject.content;
var contactsButtons = friendsObject.buttons;
contactsButtons.unshift({
@ -2257,7 +1434,12 @@ define([
});
$container.keydown(function (e) {
var $value = $innerblock.find('[data-value].cp-dropdown-element-active:visible');
if (!$value.length) {
$value = $innerblock.find('[data-value]').first();
}
if (e.which === 38) { // Up
e.preventDefault();
e.stopPropagation();
if ($value.length) {
$value.mouseleave();
var $prev = $value.prev();
@ -2266,6 +1448,8 @@ define([
}
}
if (e.which === 40) { // Down
e.preventDefault();
e.stopPropagation();
if ($value.length) {
$value.mouseleave();
var $next = $value.next();
@ -2274,12 +1458,16 @@ define([
}
}
if (e.which === 13) { //Enter
e.preventDefault();
e.stopPropagation();
if ($value.length) {
$value.click();
hide();
}
}
if (e.which === 27) { // Esc
e.preventDefault();
e.stopPropagation();
$value.mouseleave();
hide();
}

View File

@ -1,5 +1,6 @@
define([
'jquery',
'/api/config/',
'/common/toolbar.js',
'json.sortify',
'/common/common-util.js',
@ -9,6 +10,7 @@ define([
'/common/common-constants.js',
'/common/common-feedback.js',
'/common/inner/share.js',
'/common/inner/access.js',
'/common/inner/properties.js',
@ -19,6 +21,7 @@ define([
'/customize/messages.js',
], function (
$,
ApiConfig,
Toolbar,
JSONSortify,
Util,
@ -27,6 +30,7 @@ define([
UI,
Constants,
Feedback,
Share,
Access,
Properties,
nThen,
@ -2007,26 +2011,25 @@ define([
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; }
if (!data.href && !ro) { return; }
$shareBlock.click(function () {
UI.openCustomModal(modal);
Share.getShareModal(common, {
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,
}
});
});
$container.append($shareBlock);
return $shareBlock;
@ -2340,7 +2343,7 @@ define([
return $(common.fixLinks($box.html(msg)));
}
if (!APP.loggedIn) {
msg = APP.newSharedFolder ? Messages.fm_info_sharedFolder : Messages.fm_info_anonymous;
msg = APP.newSharedFolder ? Messages.fm_info_sharedFolder : Messages._getKey('fm_info_anonymous', [ApiConfig.inactiveTime || 90]);
return $(common.fixLinks($box.html(msg)));
}
if (!msg || APP.store['hide-info-' + path[0]] === '1') {
@ -3660,14 +3663,6 @@ define([
if (!readOnlyFolder) {
createNewButton(isInRoot, APP.toolbar.$bottomL);
}
/*
// The share button is not displayed anymore in the toolbar: users can't know
// if they're going to share the current shared folder or the selected pad
if (sfId) {
createShareButton(sfId, APP.toolbar.$bottomL);
}
*/
if (APP.mobile()) {
var $context = $('<button>', {
@ -4334,7 +4329,7 @@ define([
else if ($this.hasClass('cp-app-drive-context-share')) {
if (paths.length !== 1) { return; }
el = manager.find(paths[0].path);
var parsed, modal;
var parsed;
var friends = common.getFriends();
var anonDrive = manager.isPathIn(currentPath, [FILES_DATA]) && !APP.loggedIn;
@ -4406,9 +4401,10 @@ define([
sharedFolder: sf,
common: common
};
modal = padType === 'file' ? UIElements.createFileShareModal(padData)
: UIElements.createShareModal(padData);
UI.openCustomModal(modal);
if (padType === 'file') {
return void Share.getFileShareModal(common, padData);
}
Share.getShareModal(common, padData);
}
}
else if ($this.hasClass('cp-app-drive-context-savelocal')) {
@ -4780,7 +4776,7 @@ define([
placeholder: Messages.settings_changePasswordNew,
style: 'flex: 1;'
});
var passwordOk = h('button.btn', Messages.properties_changePasswordButton);
var passwordOk = h('button.btn.btn-secondary', Messages.properties_changePasswordButton);
var changePass = h('span.cp-password-container', [
newPassword,
passwordOk

View File

@ -110,6 +110,10 @@ define([
waitFor.abort();
return void cb(e);
}
if (c && c.content && c.buttons) {
obj.buttons = c.buttons;
c = c.content;
}
var node = (c instanceof $) ? c[0] : c;
tabs[i] = {
content: c && UI.dialog.customModal(node, {
@ -120,6 +124,9 @@ define([
}
}),
disabled: !c,
active: obj.active,
onShow: obj.onShow,
onHide: obj.onHide,
title: obj.title,
icon: obj.icon
};

853
www/common/inner/share.js Normal file
View File

@ -0,0 +1,853 @@
define([
'jquery',
'/common/common-util.js',
'/common/common-hash.js',
'/common/common-interface.js',
'/common/common-ui-elements.js',
'/common/inner/common-modal.js',
'/common/hyperscript.js',
'/common/clipboard.js',
'/customize/messages.js',
'/bower_components/nthen/index.js',
], function ($, Util, Hash, UI, UIElements, Modal, h, Clipboard,
Messages, nThen) {
var Share = {};
var createShareWithFriends = function (config, onShare, linkGetter) {
var common = config.common;
var sframeChan = common.getSframeChannel();
var title = config.title;
var friends = config.friends || {};
var teams = config.teams || {};
var myName = common.getMetadataMgr().getUserData().name;
var order = [];
var smallCurves = Object.keys(friends).map(function (c) {
return friends[c].curvePublic.slice(0,8);
});
var div = h('div.contains-nav');
var $div = $(div);
// Replace "copy link" by "share with friends" if at least one friend is selected
// Also create the "share with friends" button if it doesn't exist
var refreshButtons = function () {
var $nav = $div.closest('.alertify').find('nav');
var friendMode = $div.find('.cp-usergrid-user.cp-selected').length;
if (friendMode) {
$nav.find('button.cp-share-with-friends').prop('disabled', '');
} else {
$nav.find('button.cp-share-with-friends').prop('disabled', 'disabled');
}
};
config.noInclude = true;
Object.keys(friends).forEach(function (curve) {
var data = friends[curve];
if (curve.length > 40 && data.notifications) { return; }
delete friends[curve];
});
var others = [];
if (Object.keys(friends).length) {
var friendsList = UIElements.getUserGrid(Messages.share_linkFriends, {
common: common,
data: friends,
noFilter: false,
large: true
}, refreshButtons);
var friendDiv = friendsList.div;
$div.append(friendDiv);
others = friendsList.icons;
}
if (Object.keys(teams).length) {
var teamsList = UIElements.getUserGrid(Messages.share_linkTeam, {
common: common,
noFilter: true,
large: true,
data: teams
}, refreshButtons);
$div.append(teamsList.div);
}
var shareButton = {
className: 'primary cp-share-with-friends',
name: Messages.share_withFriends,
onClick: function () {
var href;
nThen(function (waitFor) {
var w = waitFor();
// linkGetter can be async if this is a burn after reading URL
var res = linkGetter({}, function (url) {
if (!url) {
waitFor.abort();
return;
}
href = url;
setTimeout(w);
});
if (res && /^http/.test(res)) {
href = Hash.getRelativeHref(res);
setTimeout(w);
return;
}
}).nThen(function () {
var $friends = $div.find('.cp-usergrid-user.cp-selected');
$friends.each(function (i, el) {
var curve = $(el).attr('data-curve');
var ed = $(el).attr('data-ed');
var friend = curve && friends[curve];
var team = teams[ed];
// If the selected element is a friend or a team without edit right,
// send a notification
var mailbox = friend || ((team && team.viewer) ? team : undefined);
if (mailbox) { // Friend
if (friends[curve] && !mailbox.notifications) { return; }
if (mailbox.notifications && mailbox.curvePublic) {
common.mailbox.sendTo("SHARE_PAD", {
href: href,
password: config.password,
isTemplate: config.isTemplate,
name: myName,
title: title
}, {
viewed: team && team.id,
channel: mailbox.notifications,
curvePublic: mailbox.curvePublic
});
return;
}
}
// If it's a team with edit right, add the pad directly
if (!team) { return; }
sframeChan.query('Q_STORE_IN_TEAM', {
href: href,
password: config.password,
path: config.isTemplate ? ['template'] : undefined,
title: title,
teamId: team.id
}, function (err) {
if (err) { return void console.error(err); }
});
});
UI.findCancelButton().click();
// Update the "recently shared with" array:
// Get the selected curves
var curves = $friends.toArray().map(function (el) {
return ($(el).attr('data-curve') || '').slice(0,8);
}).filter(function (x) { return x; });
// Prepend them to the "order" array
Array.prototype.unshift.apply(order, curves);
order = Util.deduplicateString(order);
// Make sure we don't have "old" friends and save
order = order.filter(function (curve) {
return smallCurves.indexOf(curve) !== -1;
});
common.setAttribute(['general', 'share-friends'], order);
if (onShare) {
onShare.fire();
}
});
},
keys: [13]
};
common.getAttribute(['general', 'share-friends'], function (err, val) {
order = val || [];
// Sort friends by "recently shared with"
others.sort(function (a, b) {
var ca = ($(a).attr('data-curve') || '').slice(0,8);
var cb = ($(b).attr('data-curve') || '').slice(0,8);
if (!ca && !cb) { return 0; }
if (!ca) { return 1; }
if (!cb) { return -1; }
var ia = order.indexOf(ca);
var ib = order.indexOf(cb);
if (ia === -1 && ib === -1) { return 0; }
if (ia === -1) { return 1; }
if (ib === -1) { return -1; }
return ia - ib;
});
// Reorder the friend icons
others.forEach(function (el, i) {
$(el).attr('data-order', i).css('order', i);
});
// Display them
$(friendDiv).find('.cp-usergrid-grid').detach();
$(friendDiv).append(h('div.cp-usergrid-grid', others));
refreshButtons();
});
return {
content: div,
buttons: [shareButton]
};
};
var getEditableTeams = function (common, config) {
var privateData = common.getMetadataMgr().getPrivateData();
var teamsData = Util.tryParse(JSON.stringify(privateData.teams)) || {};
var teams = {};
Object.keys(teamsData).forEach(function (id) {
// config.teamId only exists when we're trying to share a pad from a team drive
// In this case, we don't want to share the pad with the current team
if (config.teamId && config.teamId === id) { return; }
var t = teamsData[id];
teams[t.edPublic] = {
viewer: !teamsData[id].hasSecondaryKey,
notifications: t.notifications,
curvePublic: t.curvePublic,
displayName: t.name,
edPublic: t.edPublic,
avatar: t.avatar,
id: id
};
});
return teams;
};
var makeBurnAfterReadingUrl = function (common, href, channel, cb) {
var keyPair = Hash.generateSignPair();
var parsed = Hash.parsePadUrl(href);
var newHref = parsed.getUrl({
ownerKey: keyPair.safeSignKey
});
var sframeChan = common.getSframeChannel();
var rtChannel;
nThen(function (waitFor) {
if (parsed.type !== "sheet") { return; }
common.getPadAttribute('rtChannel', waitFor(function (err, chan) {
rtChannel = chan;
}));
}).nThen(function (waitFor) {
sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel,
command: 'ADD_OWNERS',
value: [keyPair.validateKey]
}, waitFor(function (err) {
if (err) {
waitFor.abort();
UI.warn(Messages.error);
}
}));
if (rtChannel) {
sframeChan.query('Q_SET_PAD_METADATA', {
channel: rtChannel,
command: 'ADD_OWNERS',
value: [keyPair.validateKey]
}, waitFor(function (err) {
if (err) { console.error(err); }
}));
}
}).nThen(function () {
cb(newHref);
});
};
var makeFaqLink = function (opts) {
var link = h('span', [
h('i.fa.fa-question-circle'),
h('a', {href: '#'}, Messages.passwordFaqLink)
]);
$(link).click(function () {
opts.common.openURL(opts.origin + "/faq.html#security-pad_password");
});
return link;
};
var makeCancelButton = function() {
return {
className: 'cancel',
name: Messages.cancel,
onClick: function () {},
keys: [27]
};
};
var getContactsTab = function (Env, data, opts, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var common = Env.common;
var hasFriends = opts.hasFriends;
var onFriendShare = Util.mkEvent();
var friendsObject = hasFriends ? createShareWithFriends(opts, onFriendShare, opts.getLinkValue) : UIElements.noContactsMessage(common);
var friendsList = friendsObject.content;
onFriendShare.reg(opts.saveValue);
var contactsContent = h('div.cp-share-modal');
var $contactsContent = $(contactsContent);
$contactsContent.append(friendsList);
// Show alert if the pad is password protected
if (opts.hasPassword) {
$contactsContent.append(h('div.alert.alert-primary', [
h('i.fa.fa-unlock'),
Messages.share_contactPasswordAlert, h('br'),
makeFaqLink(opts)
]));
}
// Burn after reading warning
if (opts.barAlert) { $contactsContent.append(opts.barAlert.cloneNode(true)); }
var contactButtons = friendsObject.buttons;
contactButtons.unshift(makeCancelButton());
cb(void 0, {
content: contactsContent,
buttons: contactButtons
});
};
var getLinkTab = function (Env, data, opts, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var common = Env.common;
var origin = opts.origin;
var pathname = opts.pathname;
var hashes = opts.hashes;
// Create modal
var linkContent = opts.sharedFolder ? [
h('label', Messages.sharedFolders_share),
h('br'),
] : [
UI.createCheckbox('cp-share-embed', Messages.share_linkEmbed, false, { mark: {tabindex:1} }),
];
linkContent.push(h('div.cp-spacer'));
linkContent.push(UI.dialog.selectableArea('', { id: 'cp-share-link-preview', tabindex: 1, rows:3}));
// Show alert if the pad is password protected
if (opts.hasPassword) {
linkContent.push(h('div.alert.alert-primary', [
h('i.fa.fa-lock'),
Messages.share_linkPasswordAlert, h('br'),
makeFaqLink(opts)
]));
}
// warning about sharing links
var localStore = window.cryptpadStore;
var dismissButton = h('span.fa.fa-times');
var shareLinkWarning = h('div.alert.alert-warning.dismissable',
{ style: 'display: none;' },
[
h('span.cp-inline-alert-text', Messages.share_linkWarning),
dismissButton
]);
linkContent.push(shareLinkWarning);
localStore.get('hide-alert-shareLinkWarning', function (val) {
if (val === '1') { return; }
$(shareLinkWarning).show();
$(dismissButton).on('click', function () {
localStore.put('hide-alert-shareLinkWarning', '1');
$(shareLinkWarning).remove();
});
});
// Burn after reading
if (opts.barAlert) { linkContent.push(opts.barAlert.cloneNode(true)); }
var link = h('div.cp-share-modal', linkContent);
var $link = $(link);
$link.find('#cp-share-link-preview').val(opts.getLinkValue());
$link.find('input[type="checkbox"]').on('change', function () {
$link.find('#cp-share-link-preview').val(opts.getLinkValue({
embed: Util.isChecked($link.find('#cp-share-embed'))
}));
});
Messages.share_bar = "Generate link"; // XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
var linkButtons = [
makeCancelButton(),
!opts.sharedFolder && {
className: 'secondary cp-nobar',
name: Messages.share_linkOpen,
onClick: function () {
opts.saveValue();
var v = opts.getLinkValue({
embed: Util.isChecked($link.find('#cp-share-embed'))
});
window.open(v);
return true;
},
keys: [[13, 'ctrl']]
}, {
className: 'primary cp-nobar',
name: Messages.share_linkCopy,
onClick: function () {
opts.saveValue();
var v = opts.getLinkValue({
embed: Util.isChecked($link.find('#cp-share-embed'))
});
var success = Clipboard.copy(v);
if (success) { UI.log(Messages.shareSuccess); }
},
keys: [13]
}, {
className: 'primary cp-bar',
name: Messages.share_bar,
onClick: function () {
var barHref = origin + pathname + '#' + (hashes.viewHash || hashes.editHash);
makeBurnAfterReadingUrl(common, barHref, opts.channel, function (url) {
opts.burnAfterReadingUrl = url;
opts.$rights.find('input[type="radio"]').trigger('change');
});
return true;
},
keys: []
}
];
$link.find('.cp-bar').hide();
cb(void 0, {
content: link,
buttons: linkButtons
});
};
var getEmbedTab = function (Env, data, opts, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var embedContent = [
h('p', Messages.viewEmbedTag),
UI.dialog.selectableArea(opts.getEmbedValue(), { id: 'cp-embed-link-preview', tabindex: 1, rows: 3})
];
// Show alert if the pad is password protected
if (opts.hasPassword) {
embedContent.push(h('div.alert.alert-primary', [
h('i.fa.fa-lock'), ' ',
Messages.share_embedPasswordAlert, h('br'),
makeFaqLink(opts)
]));
}
var embedButtons = [
makeCancelButton(),
{
className: 'primary',
name: Messages.share_linkCopy,
onClick: function () {
var v = opts.getEmbedValue();
var success = Clipboard.copy(v);
if (success) { UI.log(Messages.shareSuccess); }
},
keys: [13]
}];
var embed = h('div.cp-share-modal', embedContent);
var $embed = $(embed);
$embed.find('#cp-embed-link-preview').val(opts.getEmbedValue());
cb(void 0, {
content: embed,
buttons: embedButtons
});
};
var getRightsHeader = function (common, opts) {
var hashes = opts.hashes;
var hash = hashes.editHash || hashes.viewHash;
var origin = opts.origin;
var pathname = opts.pathname;
var parsed = Hash.parsePadUrl(pathname);
var canPresent = ['code', 'slide'].indexOf(parsed.type) !== -1;
var canBAR = parsed.type !== 'drive';
var burnAfterReading = (hashes.viewHash && canBAR) ?
UI.createRadio('accessRights', 'cp-share-bar', Messages.burnAfterReading_linkBurnAfterReading, false, {
mark: {tabindex:1},
label: {style: "display: none;"}
}) : undefined;
var rights = h('div.msg.cp-inline-radio-group', [
h('label', Messages.share_linkAccess),
h('div.radio-group',[
UI.createRadio('accessRights', 'cp-share-editable-false',
Messages.share_linkView, true, { mark: {tabindex:1} }),
canPresent ? UI.createRadio('accessRights', 'cp-share-present',
Messages.share_linkPresent, false, { mark: {tabindex:1} }) : undefined,
UI.createRadio('accessRights', 'cp-share-editable-true',
Messages.share_linkEdit, false, { mark: {tabindex:1} })]),
burnAfterReading
]);
// Burn after reading
// Check if we are an owner of this pad. If we are, we can show the burn after reading option.
// When BAR is selected, display a red message indicating the consequence and add
// the options to generate the BAR url
opts.barAlert = h('div.alert.alert-danger.cp-alertify-bar-selected', {
style: 'display: none;'
}, Messages.burnAfterReading_warningLink);
var channel = opts.channel = Hash.getSecrets('pad', hash, opts.password).channel;
common.getPadMetadata({
channel: channel
}, function (obj) {
if (!obj || obj.error) { return; }
var priv = common.getMetadataMgr().getPrivateData();
// Not an owner: don't display the burn after reading option
if (!Array.isArray(obj.owners) || obj.owners.indexOf(priv.edPublic) === -1) {
$(burnAfterReading).remove();
return;
}
// When the burn after reading option is selected, transform the modal buttons
$(burnAfterReading).css({
display: 'flex'
});
});
var $rights = $(rights);
opts.saveValue = function () {
var edit = Util.isChecked($rights.find('#cp-share-editable-true'));
var present = Util.isChecked($rights.find('#cp-share-present'));
common.setAttribute(['general', 'share'], {
edit: edit,
present: present
});
};
opts.getLinkValue = function (initValue, cb) {
var val = initValue || {};
var edit = val.edit !== undefined ? val.edit : Util.isChecked($rights.find('#cp-share-editable-true'));
var embed = val.embed;
var present = val.present !== undefined ? val.present : Util.isChecked($rights.find('#cp-share-present'));
var burnAfterReading = Util.isChecked($rights.find('#cp-share-bar'));
if (burnAfterReading && !opts.burnAfterReadingUrl) {
if (cb) { // Called from the contacts tab, "share" button
var barHref = origin + pathname + '#' + (hashes.viewHash || hashes.editHash);
return makeBurnAfterReadingUrl(common, barHref, channel, function (url) {
cb(url);
});
}
return Messages.burnAfterReading_generateLink;
}
var hash = (!hashes.viewHash || (edit && hashes.editHash)) ? hashes.editHash
: hashes.viewHash;
var href = burnAfterReading ? opts.burnAfterReadingUrl
: (origin + pathname + '#' + hash);
var parsed = Hash.parsePadUrl(href);
return origin + parsed.getUrl({embed: embed, present: present});
};
opts.getEmbedValue = function () {
var url = opts.getLinkValue({
embed: true
});
return '<iframe src="' + url + '"></iframe>';
};
// disable edit share options if you don't have edit rights
if (!hashes.editHash) {
$rights.find('#cp-share-editable-false').attr('checked', true);
$rights.find('#cp-share-editable-true').removeAttr('checked').attr('disabled', true);
} else if (!hashes.viewHash) {
$rights.find('#cp-share-editable-false').removeAttr('checked').attr('disabled', true);
$rights.find('#cp-share-present').removeAttr('checked').attr('disabled', true);
$rights.find('#cp-share-editable-true').attr('checked', true);
}
var getLink = function () {
return $rights.parent().find('#cp-share-link-preview');
};
var getEmbed = function () {
return $rights.parent().find('#cp-embed-link-preview');
};
// update values for link and embed preview when radio btns change
$rights.find('input[type="radio"]').on('change', function () {
getLink().val(opts.getLinkValue({
embed: Util.isChecked($('.alertify').find('#cp-share-embed'))
}));
// Hide or show the burn after reading alert
if (Util.isChecked($rights.find('#cp-share-bar')) && !opts.burnAfterReadingUrl) {
$('.cp-alertify-bar-selected').show();
// Show burn after reading button
$('.alertify').find('.cp-bar').show();
$('.alertify').find('.cp-nobar').hide();
return;
}
getEmbed().val(opts.getEmbedValue());
// Hide burn after reading button
$('.alertify').find('.cp-nobar').show();
$('.alertify').find('.cp-bar').hide();
$('.cp-alertify-bar-selected').hide();
});
// Set default values
common.getAttribute(['general', 'share'], function (err, val) {
val = val || {};
if (val.present && canPresent) {
$rights.find('#cp-share-editable-false').prop('checked', false);
$rights.find('#cp-share-editable-true').prop('checked', false);
$rights.find('#cp-share-present').prop('checked', true);
} else if ((val.edit === false && hashes.viewHash) || !hashes.editHash) {
$rights.find('#cp-share-editable-false').prop('checked', true);
$rights.find('#cp-share-editable-true').prop('checked', false);
$rights.find('#cp-share-present').prop('checked', false);
} else {
$rights.find('#cp-share-editable-true').prop('checked', true);
$rights.find('#cp-share-editable-false').prop('checked', false);
$rights.find('#cp-share-present').prop('checked', false);
}
delete val.embed;
if (!canPresent) {
delete val.present;
}
getLink().val(opts.getLinkValue(val));
});
common.getMetadataMgr().onChange(function () {
// "hashes" is only available is the secure "share" app
var _hashes = common.getMetadataMgr().getPrivateData().hashes;
if (!_hashes) { return; }
hashes = _hashes;
getLink().val(opts.getLinkValue());
});
return $rights;
};
// In the share modal, tabs need to share data between themselves.
// To do so we're using "opts" to store data and functions
Share.getShareModal = function (common, opts, cb) {
cb = cb || function () {};
opts = opts || {};
opts.access = true; // Allow the use of the modal even if the pad is not stored
var hashes = opts.hashes;
if (!hashes || (!hashes.editHash && !hashes.viewHash)) { return; }
var teams = getEditableTeams(common, opts);
opts.teams = teams;
var hasFriends = opts.hasFriends = Object.keys(opts.friends || {}).length ||
Object.keys(teams).length;
// check if the pad is password protected
var pathname = opts.pathname;
var hash = hashes.editHash || hashes.viewHash;
var href = pathname + '#' + hash;
var parsedHref = Hash.parsePadUrl(href);
opts.hasPassword = parsedHref.hashData.password;
var $rights = opts.$rights = getRightsHeader(common, opts);
var resetTab = function () {
$rights.show();
$rights.find('label.cp-radio').show();
};
var onShowEmbed = function () {
$rights.find('#cp-share-bar').closest('label').hide();
$rights.find('input[type="radio"]:enabled').first().prop('checked', 'checked');
$rights.find('input[type="radio"]').trigger('change');
};
var onShowContacts = function () {
if (!hasFriends) {
$rights.hide();
}
};
var tabs = [{
getTab: getContactsTab,
title: Messages.share_contactCategory,
icon: "fa fa-addessèbook",
active: hasFriends,
onShow: onShowContacts,
onHide: resetTab
}, {
getTab: getLinkTab,
title: Messages.share_linkCategory,
icon: "fa fa-link",
active: !hasFriends,
}, {
getTab: getEmbedTab,
title: Messages.share_embedCategory,
icon: "fa fa-code",
onShow: onShowEmbed,
onHide: resetTab
}];
Modal.getModal(common, opts, tabs, function (err, modal) {
$(modal).find('.cp-bar').hide();
// Prepend the "rights" radio selection
$(modal).find('.alertify-tabs-titles').after($rights);
// callback
cb(err, modal);
});
};
var getFileContactsTab = function (Env, data, opts, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var common = Env.common;
var friendsObject = opts.hasFriends ? createShareWithFriends(opts, null, opts.getLinkValue) : UIElements.noContactsMessage(common);
var friendsList = friendsObject.content;
var contactsContent = h('div.cp-share-modal');
var $contactsContent = $(contactsContent);
$contactsContent.append(friendsList);
// Show alert if the pad is password protected
if (opts.hasPassword) {
$contactsContent.append(h('div.alert.alert-primary', [
h('i.fa.fa-lock'),
Messages.share_linkPasswordAlert, h('br'),
makeFaqLink(opts)
]));
}
var contactButtons = friendsObject.buttons;
contactButtons.unshift(makeCancelButton());
cb(void 0, {
content: contactsContent,
buttons: contactButtons
});
};
var getFileLinkTab = function (Env, data, opts, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var linkContent = [
UI.dialog.selectableArea(opts.getLinkValue(), {
id: 'cp-share-link-preview', tabindex: 1, rows:2
})
];
// Show alert if the pad is password protected
if (opts.hasPassword) {
linkContent.push(h('div.alert.alert-primary', [
h('i.fa.fa-lock'),
Messages.share_linkPasswordAlert, h('br'),
makeFaqLink(opts)
]));
}
// warning about sharing links
var localStore = window.cryptpadStore;
var dismissButton = h('span.fa.fa-times');
var shareLinkWarning = h('div.alert.alert-warning.dismissable',
{ style: 'display: none;' },
[
h('span.cp-inline-alert-text', Messages.share_linkWarning),
dismissButton
]);
linkContent.push(shareLinkWarning);
localStore.get('hide-alert-shareLinkWarning', function (val) {
if (val === '1') { return; }
$(shareLinkWarning).show();
$(dismissButton).on('click', function () {
localStore.put('hide-alert-shareLinkWarning', '1');
$(shareLinkWarning).remove();
});
});
var link = h('div.cp-share-modal', linkContent);
var linkButtons = [
makeCancelButton(),
{
className: 'primary',
name: Messages.share_linkCopy,
onClick: function () {
var v = opts.getLinkValue();
var success = Clipboard.copy(v);
if (success) { UI.log(Messages.shareSuccess);
}
},
keys: [13]
}
];
cb(void 0, {
content: link,
buttons: linkButtons
});
};
var getFileEmbedTab = function (Env, data, opts, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var common = Env.common;
var fileData = opts.fileData;
var embed = h('div.cp-share-modal', [
h('p', Messages.fileEmbedScript),
UI.dialog.selectable(common.getMediatagScript()),
h('p', Messages.fileEmbedTag),
UI.dialog.selectable(common.getMediatagFromHref(fileData)),
]);
// Show alert if the pad is password protected
if (opts.hasPassword) {
$(embed).append(h('div.alert.alert-primary', [
h('i.fa.fa-lock'),
Messages.share_linkPasswordAlert, h('br'),
makeFaqLink(opts)
]));
}
var embedButtons = [{
className: 'cancel',
name: Messages.cancel,
onClick: function () {},
keys: [27]
}, {
className: 'primary',
name: Messages.share_mediatagCopy,
onClick: function () {
var v = common.getMediatagFromHref(opts.fileData);
var success = Clipboard.copy(v);
if (success) { UI.log(Messages.shareSuccess); }
},
keys: [13]
}];
cb(void 0, {
content: embed,
buttons: embedButtons
});
};
Share.getFileShareModal = function (common, opts, cb) {
cb = cb || function () {};
opts = opts || {};
opts.access = true; // Allow the use of the modal even if the pad is not stored
var hashes = opts.hashes;
if (!hashes || !hashes.fileHash) { return; }
var teams = getEditableTeams(common, opts);
opts.teams = teams;
var hasFriends = opts.hasFriends = Object.keys(opts.friends || {}).length ||
Object.keys(teams).length;
// check if the pad is password protected
var origin = opts.origin;
var pathname = opts.pathname;
var url = opts.url = origin + pathname + '#' + hashes.fileHash;
var parsedHref = Hash.parsePadUrl(url);
opts.hasPassword = parsedHref.hashData.password;
opts.getLinkValue = function () { return url; };
var tabs = [{
getTab: getFileContactsTab,
title: Messages.share_contactCategory,
icon: "fa fa-addessèbook",
active: hasFriends,
}, {
getTab: getFileLinkTab,
title: Messages.share_linkCategory,
icon: "fa fa-link",
active: !hasFriends,
}, {
getTab: getFileEmbedTab,
title: Messages.share_embedCategory,
icon: "fa fa-code",
}];
Modal.getModal(common, opts, tabs, cb);
};
return Share;
});

View File

@ -312,7 +312,7 @@ define([
}
};
handlers['INVITE_TO_TEAM_ANSWER'] = function(common, data) {
handlers['INVITE_TO_TEAM_ANSWERED'] = function(common, data) {
var content = data.content;
var msg = content.msg;

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 121 KiB

View File

@ -109,6 +109,13 @@ define([
delete localStorage[k];
delete sessionStorage[k];
});
try {
Object.keys(localStorage || {}).forEach(function (k) {
// Remvoe everything in localStorage except CACHE and FS_hash
if (/^CRYPTPAD_CACHE/.test(k) || /^LESS_CACHE/.test(k) || k === Constants.fileHashKey) { return; }
delete localStorage[k];
});
} catch (e) { console.error(e); }
LocalStore.clearThumbnail();
// Make sure we have an FS_hash in localStorage before reloading all the tabs
// so that we don't end up with tabs using different anon hashes

View File

@ -444,11 +444,21 @@ define([
// If they declined the invitation, remove them from the roster (as a pending member)
try {
var module = ctx.store.modules['team'];
module.removeFromTeam(teamId, msg.author);
module.removeFromTeam(teamId, msg.author, true);
} catch (e) { console.error(e); }
}
cb(false);
var userData = content.user || content;
box.sendMessage({
type: 'INVITE_TO_TEAM_ANSWERED',
content: {
user: userData,
team: team,
answer: content.answer
}
}, function () {});
cb(true);
};
handlers['TEAM_EDIT_RIGHTS'] = function (ctx, box, data, cb) {
@ -648,9 +658,15 @@ define([
if (!data.msg) { return void cb(true); }
// Check if the request is valid (sent by the correct user)
var myCurve = Util.find(ctx, ['store', 'proxy', 'curvePublic']);
var curve = Util.find(data, ['msg', 'content', 'user', 'curvePublic']) ||
Util.find(data, ['msg', 'content', 'curvePublic']);
if (curve && data.msg.author !== curve) { console.error('blocked'); return void cb(true); }
// Block messages that are not coming from the user described in the message
// except if the author is ourselves.
if (curve && data.msg.author !== curve && data.msg.author !== myCurve) {
console.error('blocked');
return void cb(true);
}
var type = data.msg.type;

View File

@ -206,6 +206,10 @@ proxy.mailboxes = {
if (!box) { return void cb(); }
if (!box.cpNf || typeof(box.cpNf.stop) !== "function") { return void cb('EINVAL'); }
box.cpNf.stop();
Object.keys(box.content).forEach(function (h) {
Handlers.remove(ctx, box, box.content[h], h);
hideMessage(ctx, type, h, ctx.clients);
});
delete ctx.boxes[type];
};
var openChannel = function (ctx, type, m, onReady, opts) {

View File

@ -578,6 +578,12 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto) {
console.error("CHANNEL_ERROR", info);
};
var onConnectionChange = function (info) {
if (info.state) { return; }
// Disconnect: don't send event anymore until ready
ready = false;
};
var onConnect = function (/* wc, sendMessage */) {
console.log("ROSTER CONNECTED");
};
@ -621,12 +627,12 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto) {
// if a checkpoint was successfully applied, emit an event
if (parsed[0] === 'CHECKPOINT' && changed) {
events.checkpoint.fire(hash);
if (isReady()) { events.checkpoint.fire(hash); }
// reset the counter for messages since the last checkpoint
ref.internal.sinceLastCheckpoint = 0;
ref.internal.lastCheckpointHash = hash;
} else if (changed) {
events.change.fire();
if (isReady()) { events.change.fire(); }
}
// CHECKPOINT logic...
@ -833,7 +839,7 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto) {
onChannelError: onChannelError,
onReady: onReady,
onConnect: onConnect,
onConnectionChange: function () {},
onConnectionChange: onConnectionChange,
onMessage: onMessage,
noChainPad: true,

View File

@ -221,6 +221,11 @@ define([
roster.on('change', function () {
var state = roster.getState();
var me = Util.find(ctx, ['store', 'proxy', 'curvePublic']);
if (!state.members || !Object.keys(state.members).length) {
// invalid roster, don't leave the team
console.error(JSON.stringify(state));
return;
}
if (!state.members[me]) {
return void closeTeam(ctx, id);
}
@ -1133,10 +1138,15 @@ define([
teamData.hash = data.hash;
teamData.keys.drive.edPrivate = data.keys.drive.edPrivate;
teamData.keys.chat.edit = data.keys.chat.edit;
initRpc(ctx, team, teamData.keys.drive, function () {});
initRpc(ctx, team, teamData.keys.drive, function () {
team.manager.addPin(team.pin, team.unpin);
});
var secret = Hash.getSecrets('team', data.hash, teamData.password);
team.secondaryKey = secret && secret.keys.secondaryKey;
var crypto = Crypto.createEncryptor(secret.keys);
team.listmap.setReadOnly(false, crypto);
} else {
delete teamData.hash;
delete teamData.keys.drive.edPrivate;
@ -1145,6 +1155,8 @@ define([
if (team.rpc && team.rpc.destroy) {
team.rpc.destroy();
}
team.manager.removePin();
team.listmap.setReadOnly(true);
}
updateMyRights(ctx, teamId, data.hash);
@ -1698,8 +1710,22 @@ define([
team.getTeams = function () {
return Object.keys(ctx.teams);
};
team.removeFromTeam = function (teamId, curve) {
var isPending = function (teamId, curve) {
var team = ctx.teams[teamId];
if (!team) { return; }
var state = team.roster && team.roster.getState();
if (!state.members) { return; }
var m = state.members[curve] || {};
return m.pending;
};
team.removeFromTeam = function (teamId, curve, pendingOnly) {
if (!teams[teamId]) { return; }
// When receiving a negative answer to a team invitation, remove
// the pending user from the roster.
if (pendingOnly && !isPending(teamId, curve)) { return; }
if (ctx.onReadyHandlers[teamId]) {
ctx.onReadyHandlers[teamId].push({cb : function () {
ctx.teams[teamId].roster.remove([curve], function (err) {

View File

@ -1243,6 +1243,15 @@ define([
};
};
var addPin = function (pin, unpin) {
Env.pinPads = pin;
Env.unpinPads = unpin;
};
var removePin = function () {
delete Env.pinPads;
delete Env.unpinPads;
};
return {
// Manager
addProxy: callWithEnv(addProxy),
@ -1250,6 +1259,8 @@ define([
deprecateProxy: callWithEnv(deprecateProxy),
restrictedProxy: callWithEnv(restrictedProxy),
addSharedFolder: callWithEnv(_addSharedFolder),
addPin: addPin,
removePin: removePin,
// Drive
command: callWithEnv(onCommand),
getPadAttribute: callWithEnv(getPadAttribute),

View File

@ -48,12 +48,19 @@ define([
titleUpdated = cb;
};
var ret = {
updateTitle: exp.updateTitle,
suggestName: suggestTitle,
defaultName: exp.defaultTitle,
getTitle: exp.getTitle
};
metadataMgr.onChange(function () {
var md = metadataMgr.getMetadata();
if ($title) {
$title.find('input').prop('placeholder', md.defaultTitle);
}
exp.defaultTitle = md.defaultTitle;
ret.defaultName = exp.defaultTitle = md.defaultTitle;
});
metadataMgr.onTitleChange(function (title, defaultTitle) {
if ($title) {
@ -79,12 +86,7 @@ define([
});
exp.getTitleConfig = function () {
return {
updateTitle: exp.updateTitle,
suggestName: suggestTitle,
defaultName: exp.defaultTitle,
getTitle: exp.getTitle
};
return ret;
};
exp.onTitleChange = evTitleChange.reg;

View File

@ -0,0 +1 @@
{}

View File

@ -29,7 +29,7 @@
"padNotPinned": "Dieses Pad wird nach 3 Monaten ohne Aktivität auslaufen, {0}logge dich ein{1} oder {2}registriere dich{3}, um das Auslaufen zu verhindern.",
"anonymousStoreDisabled": "Der Webmaster dieses CryptPad-Servers hat die anonyme Verwendung des Speichers deaktiviert. Du musst dich einloggen, um CryptDrive zu verwenden.",
"expiredError": "Dieses Pad ist abgelaufen und ist nicht mehr verfügbar.",
"deletedError": "Dieses Pad wurde von seinem Besitzer gelöscht und ist nicht mehr verfügbar.",
"deletedError": "Dieses Pad wurde von seinem Eigentümer gelöscht und ist nicht mehr verfügbar.",
"inactiveError": "Dieses Pad ist wegen Inaktivität gelöscht worden. Drücke Esc, um ein neues Pad zu erstellen.",
"chainpadError": "Ein kritischer Fehler ist beim Aktualisieren deines Inhalts aufgetreten. Diese Seite ist schreibgeschützt, damit du sicherstellen kannst, dass kein Inhalt verloren geht.<br>Drücke <em>Esc</em>, um das Pad schreibgeschützt zu lesen oder lade es neu, um die Bearbeitung fortzusetzen.",
"errorCopy": " Du kannst noch auf den Inhalt zugreifen, indem du <em>Esc</em> drückst.<br>Wenn du das Fenster schließt, kannst du darauf nicht mehr zugreifen.",
@ -68,8 +68,8 @@
"comingSoon": "Kommt bald...",
"newVersion": "<b>CryptPad wurde aktualisiert!</b><br>Entdecke, was neu in dieser Version ist:<br><a href=\"https://github.com/xwiki-labs/cryptpad/releases/tag/{0}\" target=\"_blank\">Versionshinweise für CryptPad {0}</a>",
"upgrade": "Upgrade",
"upgradeTitle": "Dein Konto upgraden, um mehr Speicherplatz zu haben",
"upgradeAccount": "Konto upgraden",
"upgradeTitle": "Dein Account upgraden, um mehr Speicherplatz zu haben",
"upgradeAccount": "Account upgraden",
"MB": "MB",
"GB": "GB",
"KB": "KB",
@ -105,7 +105,7 @@
"shareSuccess": "Die URL wurde in die Zwischenablage kopiert",
"userListButton": "Benutzerliste",
"chatButton": "Chat",
"userAccountButton": "Dein Konto",
"userAccountButton": "Dein Account",
"newButton": "Neu",
"newButtonTitle": "Neues Pad erstellen",
"uploadButton": "Hochladen",
@ -182,7 +182,7 @@
"hide_help_button": "Hilfe verbergen",
"help_button": "Hilfe",
"historyText": "Verlauf",
"historyButton": "Den Dokumentverlauf anzeigen",
"historyButton": "Dokumentverlauf anzeigen",
"history_next": "Neuere Version",
"history_prev": "Ältere Version",
"history_loadMore": "Weiteren Verlauf laden",
@ -264,7 +264,7 @@
"profile_urlPlaceholder": "URL",
"profile_namePlaceholder": "Angezeigter Name",
"profile_avatar": "Avatar",
"profile_upload": " Einen neuen Avatar hochladen",
"profile_upload": " Neuen Avatar hochladen",
"profile_uploadSizeError": "Fehler: Dein Avatar muss kleiner als {0} sein",
"profile_uploadTypeError": "Fehler: Der Typ dieses Bildes wird nicht unterstützt. Unterstütze Typen sind: {0}",
"profile_error": "Fehler bei der Erstellung deines Profils: {0}",
@ -303,7 +303,7 @@
"fm_filesDataName": "Alle Dateien",
"fm_templateName": "Vorlagen",
"fm_searchName": "Suchen",
"fm_recentPadsName": "Zuletzt geöffnete Pads",
"fm_recentPadsName": "Zuletzt geöffnet",
"fm_ownedPadsName": "Eigene",
"fm_tagsName": "Tags",
"fm_sharedFolderName": "Geteilter Ordner",
@ -377,7 +377,7 @@
"fc_restore": "Wiederherstellen",
"fc_remove": "Entfernen",
"fc_remove_sharedfolder": "Entfernen",
"fc_empty": "Den Papierkorb leeren",
"fc_empty": "Papierkorb leeren",
"fc_prop": "Eigenschaften",
"fc_hashtag": "Tags",
"fc_sizeInKilobytes": "Größe in Kilobyte",
@ -419,7 +419,7 @@
"register_cancel": "Zurück",
"register_warning": "\"Ohne Preisgabe von Daten\" bedeutet, dass niemand deine Daten wiederherstellen kann, wenn du dein Passwort verlierst.",
"register_alreadyRegistered": "Dieser Benutzer existiert bereits, möchtest du dich einloggen?",
"settings_cat_account": "Konto",
"settings_cat_account": "Account",
"settings_cat_drive": "CryptDrive",
"settings_cat_cursor": "Cursor",
"settings_cat_code": "Code",
@ -467,7 +467,7 @@
"settings_resetThumbnailsDone": "Alle Vorschaubilder wurden entfernt.",
"settings_importTitle": "Importiere die kürzlich besuchten Dokumente in dein CryptDrive",
"settings_import": "Importieren",
"settings_importConfirm": "Bist du sicher, dass du die kürzlich besuchten Pads in das CryptDrive deines Kontos importieren möchtest??",
"settings_importConfirm": "Bist du sicher, dass du die kürzlich besuchten Pads in das CryptDrive deines Accounts importieren möchtest??",
"settings_importDone": "Import abgeschlossen",
"settings_autostoreTitle": "Speichern von Pads im CryptDrive",
"settings_autostoreHint": "<b>Automatisch:</b> Alle Pads werden in deinem CryptDrive gespeichert.<br><b>Manuell (immer nachfragen):</b> Wenn du ein Pad noch nicht gespeichert hast, wirst du gefragt, ob du es im CryptDrive speichern willst.<br><b>Manuell (nie nachfragen):</b> Pads werden nicht automatisch im CryptDrive gespeichert. Die Option zum Speichern wird versteckt.",
@ -478,12 +478,12 @@
"settings_userFeedbackHint1": "CryptPad sendet grundlegende Rückmeldungen zum Server, um die Benutzererfahrung verbessern zu können. ",
"settings_userFeedbackHint2": "Der Inhalt deiner Pads wird nie mit dem Server geteilt.",
"settings_userFeedback": "Rückmeldungen aktivieren",
"settings_deleteTitle": "Löschung des Kontos",
"settings_deleteHint": "Die Löschung eines Kontos ist endgültig. Dein CryptDrive und die Liste deiner Pads werden vom Server gelöscht. Deine restlichen Pads werden nach 90 Tage gelöscht, wenn niemand anderes sie in seinem CryptDrive gespeichert hat.",
"settings_deleteButton": "Dein Konto löschen",
"settings_deleteTitle": "Löschung des Accounts",
"settings_deleteHint": "Die Löschung eines Accounts ist endgültig. Dein CryptDrive und die Liste deiner Pads werden vom Server gelöscht. Deine restlichen Pads werden nach 90 Tage gelöscht, wenn niemand anderes sie in seinem CryptDrive gespeichert hat.",
"settings_deleteButton": "Deinen Account löschen",
"settings_deleteModal": "Gib die folgenden Informationen an deinen CryptPad-Administrator weiter, damit er die Daten vom Server löschen kann.",
"settings_deleteConfirm": "Wenn du auf OK klickst, wird dein Konto dauerhaft gelöscht. Bist du sicher?",
"settings_deleted": "Dein Konto ist jetzt gelöscht. Klicke auf OK, um zur Hauptseite zu gelangen.",
"settings_deleteConfirm": "Wenn du auf OK klickst, wird dein Account dauerhaft gelöscht. Bist du sicher?",
"settings_deleted": "Dein Account ist jetzt gelöscht. Klicke auf OK, um zur Hauptseite zu gelangen.",
"settings_anonymous": "Du bist nicht eingeloggt. Die Einstellungen hier gelten nur für diesen Browser.",
"settings_publicSigningKey": "Öffentlicher Schlüssel zum Unterschreiben",
"settings_usage": "Verbrauch",
@ -515,11 +515,11 @@
"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": "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.",
"settings_ownDriveButton": "Upgrade deines Accounts",
"settings_ownDriveConfirm": "Das Upgrade deines Accounts kann einige Zeit dauern. Du wirst dich auf allen Geräten neu einloggen müssen. Bist du sicher?",
"settings_ownDrivePending": "Das Upgrade deines Accounts läuft. Bitte schließe die Seite nicht und lade sie nicht neu, bis dieser Vorgang abgeschlossen ist.",
"settings_changePasswordTitle": "Dein Passwort ändern",
"settings_changePasswordHint": "Ändere das Passwort deines Kontos, ohne deine Daten zu verlieren. Du musst einmal das aktuelle Passwort eingeben und dann das gewünschte neue Passwort zweimal.<br><b>Wir können das Passwort nicht zurücksetzen, wenn du es vergisst. Sei also besonders sorgfältig!</b>",
"settings_changePasswordHint": "Ändere das Passwort deines Accounts, ohne deine Daten zu verlieren. Du musst einmal das aktuelle Passwort eingeben und dann das gewünschte neue Passwort zweimal.<br><b>Wir können das Passwort nicht zurücksetzen, wenn du es vergisst. Sei also besonders sorgfältig!</b>",
"settings_changePasswordButton": "Passwort ändern",
"settings_changePasswordCurrent": "Aktuelles Passwort",
"settings_changePasswordNew": "Neues Passwort",
@ -658,7 +658,7 @@
"features_f_cryptdrive1": "Alle Funktionen des CryptDrives",
"features_f_cryptdrive1_note": "Ordner, geteilte Ordner, Vorlagen, Tags",
"features_f_devices": "Deine Dokumente auf allen deinen Geräten",
"features_f_devices_note": "Überall Zugang zu deinem CryptDrive mit deinem Benutzerkonto",
"features_f_devices_note": "Überall Zugang zu deinem CryptDrive mit deinem Benutzer-Account",
"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",
@ -835,8 +835,8 @@
"save": "Alle Änderungen werden automatisch synchronisiert. Du musst sie also nicht selbst speichern"
},
"text": {
"formatting": "Blende die Werkzeugleiste ein/aus mit der Schaltfläche <i class=\"fa fa-wrench\"></i><b>Werkzeuge</b>.",
"embed": "Füge mit der Schaltfläche <i class=\"fa fa-image\"></i> <b>Insert</b> ein Bild aus deinem CryptDrive oder deinem Computer ein (eingeloggte Benutzer).",
"formatting": "Blende die Werkzeugleiste ein/aus mit der Schaltfläche <i class=\"fa fa-wrench\"></i> <b>Werkzeuge</b>.",
"embed": "Füge mit der Schaltfläche <i class=\"fa fa-image\"></i> <b>Einfügen</b> ein Bild aus deinem CryptDrive oder deinem Computer ein (eingeloggte Benutzer).",
"history": "Du kannst das Menü <em>Verlauf</em> <span class=\"fa fa-history\"></span> benutzen, um frühere Versionen anzusehen oder wiederherzustellen"
},
"pad": {
@ -1384,7 +1384,7 @@
"user_about": "Über CryptPad",
"support_cat_all": "Alle",
"support_cat_other": "Anderes",
"support_cat_account": "Benutzerkonto",
"support_cat_account": "Benutzer-Account",
"support_cat_data": "Datenverlust",
"notification_folderSharedTeam": "{0} hat einen Ordner mit dem Team {2} geteilt: <b>{1}</b>",
"notification_fileSharedTeam": "{0} hat eine Datei mit dem Team {2} geteilt: <b>{1}</b>",

View File

@ -24,7 +24,7 @@
"common_connectionLost": "<b>Yhteys palvelimelle katkennut</b><br>Sovellus on vain luku-tilassa, kunnes yhteys palaa.",
"websocketError": "Yhdistäminen websocket-palvelimelle epäonnistui...",
"typeError": "Tämä padi ei ole yhteensopiva valitun sovelluksen kanssa",
"onLogout": "Olet kirjautunut ulos, {0}klikkaa tästä{1} kirjautuaksesi sisään tai paina <em>Esc-näppäintä</em> käyttääksesi padia vain luku-tilassa.",
"onLogout": "Olet kirjautunut ulos, {0}napsauta tästä{1} kirjautuaksesi sisään tai paina <em>Esc-näppäintä</em> käyttääksesi padia vain luku-tilassa.",
"wrongApp": "Reaaliaikaisen sisällön näyttäminen selaimessa epäonnistui. Ole hyvä ja yritä sivun lataamista uudelleen.",
"padNotPinned": "Tämä padi vanhenee kolmen kuukauden käyttämättömyyden jälkeen, {0}kirjaudu sisään{1} tai [2}rekisteröidy{3} säilyttääksesi sen.",
"padNotPinnedVariable": "Tämä padi vanhenee {4} päivän käyttämättömyyden jälkeen, {0}kirjaudu sisään{1} tai {2}rekisteröidy{3} säilyttääksesi sen.",
@ -33,9 +33,9 @@
"deletedError": "Tämä padi on poistettu omistajansa toimesta, eikä se ole enää saatavilla.",
"inactiveError": "Tämä padi on poistettu käyttämättömyyden vuoksi. Paina Esc-näppäintä luodaksesi uuden padin.",
"chainpadError": "Sisältöä päivitettäessä tapahtui vakava virhe. Tämä sivu on vain luku-tilassa, jotta tekemäsi muutokset eivät katoaisi.<br>Paina <em>Esc-näppäintä</em> jatkaaksesi padin katselua vain luku-tilassa, tai lataa sivu uudelleen yrittääksesi muokkaamista.",
"invalidHashError": "Pyytämäsi dokumentin URL-osoite on virheellinen.",
"invalidHashError": "Pyytämäsi asiakirjan URL-osoite on virheellinen.",
"main_title": "CryptPad: Reaaliaikaista, kollaboratiivista editointia nollatietoperiaatteella",
"errorCopy": " Pääset yhä käyttämään dokumentin sisältöä painamalla <em>Esc</em>-näppäintä.<br>Suljettuasi tämän ikkunan sisältö katoaa, etkä voi enää käyttää sitä.",
"errorCopy": " Pääset yhä käyttämään asiakirjan sisältöä painamalla <em>Esc</em>-näppäintä.<br>Suljettuasi tämän ikkunan sisältö katoaa, etkä voi enää käyttää sitä.",
"errorRedirectToHome": "Paina<em>Esc</em>-näppäintä ohjautuaksesi CryptDriveen.",
"newVersionError": "Uusi versio CryptPadista on saatavilla.<br><a href='#'>Lataa sivu uudelleen</a> siirtyäksesi uuteen versioon, tai paina <em>Esc</em>-näppäintä käyttääksesi sisältöäsi <b>offline-tilassa</b>.",
"loading": "Ladataan...",
@ -48,7 +48,7 @@
"disabledApp": "Tämä sovellus on poistettu käytöstä. Saadaksesi lisätietoja ota yhteyttä tämän CryptPad-instanssin ylläpitäjään.",
"realtime_unrecoverableError": "Tapahtui peruuttamaton virhe. Paina OK ladataksesi uudelleen.",
"disconnected": "Yhteys katkaistu",
"synchronizing": "Synkronisoidaan",
"synchronizing": "Synkronoidaan",
"reconnecting": "Yhdistetään uudelleen",
"typing": "Muokataan",
"initializing": "Valmistellaan...",
@ -99,11 +99,11 @@
"user_rename": "Vaihda näyttönimeä",
"user_displayName": "Näyttönimi",
"user_accountName": "Käyttäjätilin nimi",
"clickToEdit": "Klikkaa muokataksesi",
"clickToEdit": "Napsauta muokataksesi",
"saveTitle": "Tallenna otsikko (Enter)",
"forgetButton": "Poista",
"forgetButtonTitle": "Siirrä padi roskakoriin",
"forgetPrompt": "OK:n klikkaaminen siirtää padin roskakoriin. Oletko varma?",
"forgetPrompt": "OK:n napsauttaminen siirtää padin roskakoriin. Oletko varma?",
"movedToTrash": "Padi on siirretty roskakoriin.<br><a href=\"/drive/\">Siirry Driveen</a>",
"shareButton": "Jaa",
"shareSuccess": "Linkki kopioitu leikepöydälle",
@ -114,7 +114,7 @@
"newButtonTitle": "Luo uusi padi",
"uploadButton": "Lataa tiedostoja palvelimelle",
"uploadFolderButton": "Latauskansio",
"uploadButtonTitle": "Lataa uusi tiedosto nykyiseen kansioon",
"uploadButtonTitle": "Lataa uusi tiedosto CryptDriveesi",
"saveTemplateButton": "Tallenna mallipohjaksi",
"saveTemplatePrompt": "Valitse otsikko mallipohjalle",
"templateSaved": "Mallipohja tallennettu!",
@ -132,7 +132,7 @@
"propertiesButtonTitle": "Hae padin ominaisuudet",
"printText": "Tulosta",
"printButton": "Tulosta (Enter)",
"printButtonTitle2": "Tulosta dokumentti tai vie se PDF-tiedostoon",
"printButtonTitle2": "Tulosta asiakirja tai vie se PDF-tiedostoon",
"printOptions": "Layout-asetukset",
"printSlideNumber": "Näytä dian numero",
"printDate": "Näytä päivämäärä",
@ -150,7 +150,7 @@
"filePicker_filter": "Suodata tiedostot nimen perusteella",
"or": "tai",
"tags_title": "Tunnisteet (vain sinulle)",
"tags_add": "Päivitä sivun tunnisteet",
"tags_add": "Päivitä valittujen padien tunnisteet",
"tags_notShared": "Tunnisteitasi ei jaeta muiden käyttäjien kanssa",
"tags_duplicate": "Kaksinkertainen tunniste: {0}",
"tags_noentry": "Et voi lisätä tunnistetta poistettuun padiin!",
@ -187,14 +187,14 @@
"hide_help_button": "Piilota ohje",
"help_button": "Ohje",
"historyText": "Historia",
"historyButton": "Näytä dokumentin historia",
"historyButton": "Näytä asiakirjan historia",
"history_next": "Uudempi versio",
"history_prev": "Vanhempi versio",
"history_loadMore": "Lataa lisää historiatietoja",
"history_closeTitle": "Sulje historia",
"history_restoreTitle": "Palauta dokumentin valittu versio",
"history_restorePrompt": "Oletko varma, että haluat korvata dokumentin nykyisen version esitetyllä versiolla?",
"history_restoreDone": "Dokumentti palautettu",
"history_restoreTitle": "Palauta asiakirjan valittu versio",
"history_restorePrompt": "Oletko varma, että haluat korvata asiakirjan nykyisen version esitetyllä versiolla?",
"history_restoreDone": "Asiakirja palautettu",
"history_version": "Versio:",
"openLinkInNewTab": "Avaa linkki uuteen välilehteen",
"pad_mediatagTitle": "Media-tunnisteen asetukset",
@ -218,7 +218,7 @@
"poll_subtitle": "Nollatietoperiaatteen mukaista aikataulutusta <em>reaaliajassa</em>",
"poll_p_save": "Asetuksesi otetaan käyttöön välittömästi, joten niitä ei tarvitse tallentaa.",
"poll_p_encryption": "Kaikki tekemäsi syötteet ovat salattuja, joten vain linkin saaneet henkilöt voivat käyttää sitä. Edes itse palvelin ei näe tekemiäsi muutoksia.",
"wizardLog": "Klikkaa vasemmassa yläkulmassa olevaa painiketta palataksesi kyselyyn",
"wizardLog": "Napsauta vasemmassa yläkulmassa olevaa painiketta palataksesi kyselyyn",
"wizardTitle": "Käytä avustajaa kyselyn luomiseen",
"wizardConfirm": "Oletko todella valmis lisäämään nämä vaihtoehdot kyselyysi?",
"poll_publish_button": "Julkaise",
@ -238,7 +238,7 @@
"poll_removeOption": "Oletko varma, että haluat poistaa tämän vaihtoehdon?",
"poll_removeUser": "Oletko varma, että haluat poistaa tämän käyttäjän?",
"poll_titleHint": "Otsikko",
"poll_descriptionHint": "Kuvaile kyselyäsi, ja klikkaa ✓ (Julkaise)-painiketta, kun olet valmis.\nKuvauksen kirjoittamiseen voi käyttää Markdown-syntaksia, ja voit halutessasi upottaa mediaelementtejä CryptDrivestäsi.\nKuka tahansa linkin tietävä voi muuttaa kuvausta, mutta tätä ei suositella.",
"poll_descriptionHint": "Kuvaile kyselyäsi, ja napsauta ✓ (Julkaise)-painiketta, kun olet valmis.\nKuvauksen kirjoittamiseen voi käyttää Markdown-syntaksia, ja voit halutessasi upottaa mediaelementtejä CryptDrivestäsi.\nKuka tahansa linkin tietävä voi muuttaa kuvausta, mutta tätä ei suositella.",
"poll_remove": "Poista",
"poll_edit": "Muokkaa",
"poll_locked": "Lukittu",
@ -251,10 +251,10 @@
"poll_comment_submit": "Lähetä",
"poll_comment_remove": "Poista kommentti",
"poll_comment_placeholder": "Kommenttisi",
"poll_comment_disabled": "Julkaise kysely klikkaamalla ✓ -painiketta ottaaksesi kommentit käyttöön.",
"oo_reconnect": "Yhteys palvelimeen on palannut. Klikkaa OK ladataksesi sivu uudelleen ja jatkaaksesi muokkaamista.",
"poll_comment_disabled": "Julkaise kysely napsauttamalla ✓ -painiketta ottaaksesi kommentit käyttöön.",
"oo_reconnect": "Yhteys palvelimeen on palannut. Napsauta OK ladataksesi sivu uudelleen ja jatkaaksesi muokkaamista.",
"oo_cantUpload": "Tiedostojen lataaminen palvelimelle ei ole sallittua muiden käyttäjien läsnäollessa.",
"oo_uploaded": "Tiedosto on latautunut palvelimelle. Klikkaa OK ladataksesi sivun uudelleen tai Keskeytä jatkaaksesi vain luku-tilassa.",
"oo_uploaded": "Tiedosto on latautunut palvelimelle. Napsauta OK ladataksesi sivun uudelleen tai Keskeytä jatkaaksesi vain luku-tilassa.",
"canvas_clear": "Tyhjennä",
"canvas_delete": "Poista valinta",
"canvas_disable": "Ota piirtäminen pois käytöstä",
@ -293,8 +293,8 @@
"contacts_warning": "Kaikki tähän kirjoittamasi on pysyvää ja saatavilla kaikille tämän padin nykyisille ja tuleville käyttäjille. Olethan varovainen arkaluontoisten tietojen suhteen!",
"contacts_padTitle": "Keskustelu",
"contacts_info1": "Tässä ovat yhteystietosi. Täältä käsin voit:",
"contacts_info2": "Klikkaa yhteystietosi kuvaketta keskustellaksesi hänen kanssaan",
"contacts_info3": "Kaksoisklikkaa yhteystietosi kuvaketta katsellaksesi hänen profiiliaan",
"contacts_info2": "Napsauttaa yhteystietosi kuvaketta keskustellaksesi hänen kanssaan",
"contacts_info3": "Kaksoisnapsauttaa yhteystietosi kuvaketta katsellaksesi hänen profiiliaan",
"contacts_info4": "Kumpi tahansa keskustelun osapuoli voi tyhjentää keskusteluhistorian pysyvästi",
"contacts_removeHistoryTitle": "Tyhjennä keskusteluhistoria",
"contacts_confirmRemoveHistory": "Oletko varma, että haluat tyhjentää keskusteluhistorian pysyvästi? Tietoja ei voida palauttaa",
@ -304,10 +304,10 @@
"contacts_rooms": "Keskusteluhuoneet",
"contacts_leaveRoom": "Lähde keskusteluhuoneesta",
"contacts_online": "Toinen käyttäjä tästä keskusteluhuoneesta on online-tilassa",
"debug_getGraph": "Hae koodi kaavion luomiseen dokumentin pohjalta",
"debug_getGraph": "Hae koodi kaavion luomiseen asiakirjan pohjalta",
"debug_getGraphWait": "Luodaan kaaviota... Odota hetki.",
"debug_getGraphText": "Tämä on DOT-koodi dokumentin historiakaavion luomiseen:",
"fm_rootName": "Dokumentit",
"debug_getGraphText": "Tämä on DOT-koodi asiakirjan historiakaavion luomiseen:",
"fm_rootName": "Asiakirjat",
"fm_trashName": "Roskakori",
"fm_unsortedName": "Järjestelemättömät tiedostot",
"fm_filesDataName": "Kaikki tiedostot",
@ -336,11 +336,11 @@
"fm_forbidden": "Kielletty toiminto",
"fm_originalPath": "Alkuperäinen polku",
"fm_openParent": "Näytä kansiossa",
"fm_noname": "Nimetön dokumentti",
"fm_noname": "Nimetön asiakirja",
"fm_emptyTrashDialog": "Oletko varma, että haluat tyhjentää roskakorin?",
"fm_removeSeveralPermanentlyDialog": "Oletko varma, että haluat poistaa pysyvästi {0} elementtiä CryptDrivestasi?",
"fm_removeSeveralPermanentlyDialog": "Oletko varma, että haluat poistaa {0} kohdetta CryptDrivestasi? Ne säilyvät edelleen muiden käyttäjien CryptDriveissa.",
"fm_removePermanentlyNote": "Jos jatkat, omistamasi padit poistetaan palvelimelta.",
"fm_removePermanentlyDialog": "Oletko varma, että haluat poistaa tämän elementin CryptDrivestasi?",
"fm_removePermanentlyDialog": "Oletko varma, että haluat poistaa tämän kohteen CryptDrivestasi? Se säilyy edelleen muiden käyttäjien CryptDriveissa.",
"fm_removeSeveralDialog": "Oletko varma, että haluat siirtää {0} elementtiä roskakoriin?",
"fm_removeDialog": "Oletko varma, että haluat siirtää {0} roskakoriin?",
"fm_deleteOwnedPad": "Haluatko varmasti poistaa tämän padin palvelimelta pysyvästi?",
@ -355,8 +355,8 @@
"fm_info_template": "Sisältää kaikki mallipohjiksi tallennetut padit, joita voit käyttää uudelleen luodessasi uuden padin.",
"fm_info_recent": "Tässä näytetään sinun tai yhteistyökumppaniesi äskettäin avaamat tai muokkaamat padit.",
"fm_info_trash": "Tyhjennä roskakorisi vapauttaaksesi CryptDrive-tallennustilaa.",
"fm_info_allFiles": "Sisältää kaikki tiedostot \"Dokumentit\"- \"Lajittelemattomat\"- ja \"Roskakori\"-näkymistä. Et voi siirtää tai poistaa tiedostoja täältä.",
"fm_info_anonymous": "Et ole kirjautunut sisään. Padisi vanhenevat kolmen kuukauden kuluttua (<a href=\"https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/\" target=\"_blank\">(lue lisää)</a>). Padit säilytetään paikallisesti selaimessasi, joten selaimen historiatietojen tyhjentäminen saattaa hävittää ne.<br><a href=\"/register/\">Rekisteröidy</a> tai <a href=\"/login/\">kirjaudu sisään</a> säilyttääksesi padisi palvelimella.<br>",
"fm_info_allFiles": "Sisältää kaikki tiedostot \"Asiakirjat\"- \"Lajittelemattomat\"- ja \"Roskakori\"-näkymistä. Et voi siirtää tai poistaa tiedostoja täältä.",
"fm_info_anonymous": "Et ole kirjautunut sisään. Padisi vanhenevat kolmen kuukauden kuluttua (<a href=\"https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/\" target=\"_blank\">(lue lisää)</a>). Padit säilytetään paikallisesti selaimessasi, joten selaimen historiatietojen tyhjentäminen saattaa hävittää ne.<br><a href=\"/register/\">Rekisteröidy</a> tai <a href=\"/login/\">kirjaudu sisään</a> säilyttääksesi padisi palvelimella.<br>",
"fm_info_sharedFolder": "Tämä on jaettu kansio. Et ole kirjautunut sisään, joten voit käyttää sitä ainoastaan vain luku-tilassa.<br><a href=\"/register/\">Rekisteröidy</a> tai <a href=\"/login/\">kirjaudu sisään</a> tuodaksesi kansion omaan CryptDriveesi ja muokataksesi sen sisältöä.",
"fm_info_owned": "Omistat tässä näytetyt padit. Se tarkoittaa, että voit halutessasi poistaa ne palvelimelta. Jos teet niin, muut käyttäjät eivät voi enää käyttää niitä.",
"fm_alert_backupUrl": "Driven varmuuskopiointilinkki.<br>On <strong>erittäin suositeltavaa</strong> pitää se salassa.<br>Voit käyttää varmuuskopiointilinkkiä noutaaksesi kaikki Drivellesi tallennetut tiedot, jos selaimesi välimuisti on tyhjentynyt.<br>Kuka tahansa linkin tietävä voi muokata tiedostojasi tai poistaa ne kaikki.<br>",
@ -389,9 +389,9 @@
"fc_expandAll": "Laajenna kaikki",
"fc_collapseAll": "Tiivistä kaikki",
"fc_delete": "Siirrä roskakoriin",
"fc_delete_owned": "Poista palvelimelta",
"fc_delete_owned": "Tuhoa",
"fc_restore": "Palauta",
"fc_remove": "Poista CryptDrivesta",
"fc_remove": "Poista",
"fc_remove_sharedfolder": "Poista",
"fc_empty": "Tyhjennä roskakori",
"fc_prop": "Ominaisuudet",
@ -438,7 +438,7 @@
"register_emailWarning0": "Näyttää siltä, että lähetit sähköpostiosoitteesi käyttäjätunnuksenasi.",
"register_emailWarning1": "Voit halutessasi tehdä niin, mutta sitä ei lähetetä palvelimellemme.",
"register_emailWarning2": "Toisin kuin monissa muissa palveluissa, emme voi palauttaa salasanaasi sähköpostin avulla.",
"register_emailWarning3": "Jos ymmärrät tämän ja haluat silti käyttää sähköpostiosoitettasi käyttäjätunnuksenasi, klikkaa OK.",
"register_emailWarning3": "Jos ymmärrät tämän ja haluat silti käyttää sähköpostiosoitettasi käyttäjätunnuksenasi, napsauta OK.",
"settings_cat_account": "Käyttäjätili",
"settings_cat_drive": "CryptDrive",
"settings_cat_cursor": "Kursori",
@ -456,19 +456,19 @@
"settings_backup2": "Lataa oma CryptDrive tietokoneellesi",
"settings_backup2Confirm": "Tämä lataa kaikki CryptDrivesi padit ja tiedostot tietokoneellesi. Jos haluat jatkaa, valitse nimi ja paina OK",
"settings_exportTitle": "Vie oma CryptDrive",
"settings_exportDescription": "Odota hetki, dokumenttejasi puretaan ja ladataan. Tämä voi viedä muutaman minuutin. Välilehden sulkeminen keskeyttää toimenpiteen.",
"settings_exportDescription": "Odota hetki, asiakirjojasi puretaan ja ladataan. Tämä voi viedä muutaman minuutin. Välilehden sulkeminen keskeyttää toimenpiteen.",
"settings_exportFailed": "Jos padin lataamiseen menee enemmän kuin yksi (1) minuutti, sitä ei oteta mukaan vientiin. Viennistä pois jääneiden padien linkit näytetään lopuksi.",
"settings_exportWarning": "Huomautus: tämä työkalu on beta-versiossa, ja siinä saattaa olla skaalautuvuusongelmia. Suosittelemme välilehden jättämistä aktiiviseksi paremman suorituskyvyn takaamiseksi.",
"settings_exportCancel": "Haluatko varmasti keskeyttää viennin? Seuraavalla kerralla toimenpide täytyy aloittaa alusta.",
"settings_export_reading": "Luetaan CryptDrivea...",
"settings_export_download": "Ladataan ja puretaan dokumentteja...",
"settings_export_download": "Ladataan ja puretaan asiakirjoja...",
"settings_export_compressing": "Pakataan tiedostoja...",
"settings_export_done": "Latauksesi on valmis!",
"settings_exportError": "Tarkastele virheitä",
"settings_exportErrorDescription": "Emme onnistuneet lisäämään seuraavia dokumentteja vientipakettiin:",
"settings_exportErrorEmpty": "Tätä dokumenttia ei voi viedä (tyhjä tai virheellinen sisältö).",
"settings_exportErrorMissing": "Tätä dokumenttia ei löydy palvelimeltamme (vanhentunut tai omistajansa poistama)",
"settings_exportErrorOther": "Dokumenttia viedessä tapahtui virhe: {0}",
"settings_exportErrorDescription": "Emme onnistuneet lisäämään seuraavia asiakirjoja vientipakettiin:",
"settings_exportErrorEmpty": "Tätä asiakirjaa ei voi viedä (tyhjä tai virheellinen sisältö).",
"settings_exportErrorMissing": "Tätä asiakirjaa ei löydy palvelimeltamme (vanhentunut tai omistajansa poistama)",
"settings_exportErrorOther": "Asiakirjaa viedessä tapahtui virhe: {0}",
"settings_resetNewTitle": "Tyhjennä CryptDrive",
"settings_resetButton": "Poista",
"settings_reset": "Poista kaikki tiedostot ja kansiot CryptDrivestasi",
@ -490,7 +490,7 @@
"settings_importConfirm": "Haluatko varmasti tuoda tässä selaimessa viimeksi käytetyt padit käyttäjätilisi CryptDriveen?",
"settings_importDone": "Tuonti valmis",
"settings_autostoreTitle": "Padien tallennus CryptDrivessa",
"settings_autostoreHint": "<b>Automaattinen</b>Kaikki käyttämäsi padit tallennetaan CryptDriveesi.<br><b>Manuaalinen (kysy aina)</b> Jos et ole vielä tallentanut padia, kysytään sinulta, haluatko tallentaa sen CryptDriveesi.<br><b>Manuaalinen (älä kysy)</b>Padeja ei tallenneta automaattisesti CryptDriveesi. Padien tallennusmahdollisuus piilotetaan.",
"settings_autostoreHint": "<b>Automaattinen</b> Kaikki käyttämäsi padit tallennetaan CryptDriveesi.<br><b>Manuaalinen (kysy aina)</b> Jos et ole vielä tallentanut padia, kysytään sinulta, haluatko tallentaa sen CryptDriveesi.<br><b>Manuaalinen (älä kysy)</b> Padeja ei tallenneta automaattisesti CryptDriveesi. Padien tallennusmahdollisuus piilotetaan.",
"settings_autostoreYes": "Automaattinen",
"settings_autostoreNo": "Manuaalinen (älä kysy)",
"settings_autostoreMaybe": "Manuaalinen (kysy aina)",
@ -502,8 +502,8 @@
"settings_deleteHint": "Käyttäjätilin poisto on pysyvä toimenpide. CryptDrivesi ja lista padeistasi poistetaan palvelimelta. Loput padeistasi poistetaan 90 päivän kuluttua, jos kukaan muu ei ole tallentanut niitä omaan CryptDriveensa.",
"settings_deleteButton": "Poista käyttäjätilisi",
"settings_deleteModal": "Jaa seuraavat tiedot CryptPad-instanssisi ylläpitäjän kanssa poistaaksesi tietosi palvelimelta.",
"settings_deleteConfirm": "Klikkaamalla OK käyttäjätilisi poistetaan pysyvästi. Oletko varma?",
"settings_deleted": "Käyttäjätilisi on nyt poistettu. Klikkaa OK siirtyäksesi kotisivulle.",
"settings_deleteConfirm": "Napsauttamalla OK käyttäjätilisi poistetaan pysyvästi. Oletko varma?",
"settings_deleted": "Käyttäjätilisi on nyt poistettu. Napsauta OK siirtyäksesi kotisivulle.",
"settings_anonymous": "Et ole kirjautunut sisään. Nämä asetukset koskevat vain tätä selainta.",
"settings_publicSigningKey": "Julkinen salausavain",
"settings_usage": "Käyttö",
@ -522,10 +522,10 @@
"settings_codeUseTabs": "Sisennä sarkainmerkkejä käyttäen (välilyöntien sijaan)",
"settings_codeFontSize": "Koodieditorin fonttikoko",
"settings_padWidth": "Editorin maksimileveys",
"settings_padWidthHint": "Teksti-tyyppiset padit käyttävät oletusleveytenä näyttölaitteesi maksimileveyttä, mikä voi tehdä lukemisesta vaikeaa. Tästä voit pienentää editorin leveyttä.",
"settings_padWidthHint": "Valitse joko sivutila (oletus), joka rajoittaa tekstieditorin leveyttä tai näyttöruudun täyttä leveyttä käyttävä tila.",
"settings_padWidthLabel": "Pienennä editorin leveyttä",
"settings_padSpellcheckTitle": "Oikeinkirjoituksen tarkistus",
"settings_padSpellcheckHint": "Tämä vaihtoehto ottaa käyttöön oikeinkirjoituksen tarkastuksen Teksti-tyyppisissä padeissa. Oikeinkirjoitusvirheet alleviivataan punaisella. Klikkaa virheellistä sanaa hiiren oikealla painikkeella painaessasi Ctrl- tai Meta-näppäintä pohjaan nähdäksesi korjausehdotukset.",
"settings_padSpellcheckHint": "Tämä vaihtoehto ottaa käyttöön oikeinkirjoituksen tarkastuksen Teksti-tyyppisissä padeissa. Oikeinkirjoitusvirheet alleviivataan punaisella. Napsauta virheellistä sanaa hiiren oikealla painikkeella painaessasi Ctrl- tai Meta-näppäintä pohjaan nähdäksesi korjausehdotukset.",
"settings_padSpellcheckLabel": "Ota oikeinkirjoituksen tarkastus käyttöön Teksti-muotoisissa padeissa",
"settings_creationSkip": "Ohita padin luontiruutu",
"settings_creationSkipHint": "Padien luontisivu esittää vaihtoehtoja padin luomiseen auttaakseen sinua hallitsemaan ja suojaamaan tietojasi. Jos koet sen hidastavan työskentelyäsi, voit tällä asetuksella ohittaa luontisivun ja käyttää sen sijaan yläpuolella määrittelemiäsi oletusasetuksia.",
@ -549,12 +549,12 @@
"settings_changePasswordPending": "Salasanaasi päivitetään. Ole hyvä äläkä sulje tai lataa tätä sivua uudelleen, ennen kuin toimenpide on valmis.",
"settings_changePasswordNewPasswordSameAsOld": "Uuden salasanasi on oltava erilainen kuin nykyinen salasana.",
"settings_cursorColorTitle": "Kursorin väri",
"settings_cursorColorHint": "Vaihda kollaboratiivisissa dokumenteissa käytettävää käyttäjääsi yhdistettyä väriä.",
"settings_cursorColorHint": "Vaihda kollaboratiivisissa asiakirjoissa käytettävää käyttäjääsi yhdistettyä väriä.",
"settings_cursorShareTitle": "Jaa oma kursorisijainti",
"settings_cursorShareHint": "Voit päättää, haluatko kursorisijaintisi näkyvän muille kollaboratiivisissa dokumenteissa.",
"settings_cursorShareHint": "Voit päättää, haluatko kursorisijaintisi näkyvän muille kollaboratiivisissa asiakirjoissa.",
"settings_cursorShareLabel": "Jaa sijainti",
"settings_cursorShowTitle": "Näytä muiden käyttäjien kursorisijainti",
"settings_cursorShowHint": "Voit valita, haluatko nähdä muiden käyttäjien kursorit kollaboratiivisissa dokumenteissa.",
"settings_cursorShowHint": "Voit valita, haluatko nähdä muiden käyttäjien kursorit kollaboratiivisissa asiakirjoissa.",
"settings_cursorShowLabel": "Näytä kursorit",
"upload_title": "Tiedostojen lataus",
"upload_type": "Tyyppi",
@ -570,7 +570,7 @@
"upload_success": "Tiedosto ({0}) on ladattu onnistuneesti palvelimelle ja lisätty CryptDriveesi.",
"upload_notEnoughSpace": "CryptDrivessasi ei ole tarpeeksi vapaata tallennustilaa tälle tiedostolle.",
"upload_notEnoughSpaceBrief": "Tallennustila ei riitä",
"upload_tooLarge": "Tiedoston koko ylittää suurimman sallitun latauskoon.",
"upload_tooLarge": "Tiedoston koko ylittää suurimman käyttäjätilillesi sallitun latauskoon.",
"upload_tooLargeBrief": "Liian suuri tiedosto",
"upload_choose": "Valitse tiedosto",
"upload_pending": "Odottaa",
@ -617,9 +617,9 @@
"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> Jaa ideoita yhdessä jaettujen dokumenttien avulla.<strong>Nollatieto</strong>-teknologia turvaa yksityisyytesi - <strong>jopa meiltä</strong>.",
"main_info": "<h2>Luottamuksellista yhteistyötä</h2> Jaa ideoita yhdessä jaettujen asiakirjojen 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.",
"main_footerText": "CryptPadin avulla voit nopeasti luoda kollaboratiivisia asiakirjoja muistiinpanoja ja yhteistä ideointia varten.",
"footer_applications": "Sovellukset",
"footer_contact": "Ota yhteyttä",
"footer_aboutUs": "Tietoa meistä",
@ -631,14 +631,14 @@
"topbar_whatIsCryptpad": "Mikä on CryptPad",
"whatis_title": "Mikä on CryptPad",
"whatis_collaboration": "Nopeaa ja helppoa yhteistyötä",
"whatis_collaboration_p1": "CryptPadin avulla voit nopeasti luoda kollaboratiivisia dokumentteja muistiinpanoja ja yhteistä ideointia varten. Rekisteröitymällä ja kirjautumalla sisään saat mahdollisuuden ladata tiedostoja palvelimelle ja oman CryptDriven, jossa voit säilyttää kaikki padisi. Rekisteröityneet käyttäjät saavat ilmaiseksi 50 Mt tallennustilaa.",
"whatis_collaboration_p2": "Voit helposti antaa käyttöoikeuden CryptPad-dokumenttiin jakamalla sen linkin. Voit myös jakaa dokumentin linkin <em>vain luku</em>-tilassa, jolloin voit julkistaa yhteistyön tulokset ja muokata niitä edelleen.",
"whatis_collaboration_p1": "CryptPadin avulla voit nopeasti luoda kollaboratiivisia asiakirjoja muistiinpanoja ja yhteistä ideointia varten. Rekisteröitymällä ja kirjautumalla sisään saat mahdollisuuden ladata tiedostoja palvelimelle ja oman CryptDriven, jossa voit säilyttää kaikki padisi. Rekisteröityneet käyttäjät saavat ilmaiseksi 50 Mt tallennustilaa.",
"whatis_collaboration_p2": "Voit helposti antaa käyttöoikeuden CryptPad-dokumenttiin jakamalla sen linkin. Voit myös jakaa asiakirjan linkin <em>vain luku</em>-tilassa, jolloin voit julkistaa yhteistyön tulokset ja muokata niitä edelleen.",
"team_inviteLinkError": "Linkin luomisessa tapahtui virhe.",
"whatis_collaboration_p3": "<a href=\"http://ckeditor.com/\">CKEditor</a>:illa voit luoda yksinkertaisia muotoiltavia tekstitiedostoja sekä Markdown-tiedostoja, jotka muunnetaan tekstiä muokatessasi reaaliaikaisesti esitysmuotoon. Voit myös käyttää Kysely-sovellusta tapahtumien ajoittamiseen useiden osallistujien kanssa.",
"whatis_zeroknowledge": "Nollatietoperiaate",
"whatis_zeroknowledge_p1": "Emme halua tietää, mitä kirjoitat. Modernin kryptografian avulla voit olla varma, ettemme todellakaan tiedä siitä mitään. CryptPad käyttää <strong>100-prosenttisesti asiakasohjelmassa tapahtuvaa salausta</strong> suojatakseen tuottamaasi sisältöä meiltä palvelimen ylläpitäjiltä.",
"whatis_zeroknowledge_p2": "Rekisteröityessäsi ja kirjautuessasi sisään käyttäjätunnuksesi ja salasanasi lasketaan salaiseksi avaimeksi <a href=\"https://en.wikipedia.org/wiki/Scrypt\">scrypt-avaintenmuodostusfunktiolla</a>. Tätä avainta, käyttäjätunnustasi ja salasanaasi ei koskaan lähetetä palvelimelle. Sen sijaan niitä käytetään asiakasohjelmassa CryptDrivesi sisällön purkamiseen. CryptDrivesi puolestaan sisältää avaimet kaikkiin padeihin, joihin sinulla on käyttöoikeus.",
"whatis_zeroknowledge_p3": "Kun jaat linkin dokumenttiin, jaat itse asiassa dokumentin käyttöön tarvittavan salausavaimen, mutta koska salausavain sisällytetään <a href=\"https://en.wikipedia.org/wiki/Fragment_identifier\">katkelmatunnisteeseen (fragment identifier)</a>, sitä ei koskaan lähetetä palvelimelle suoraan. Tutustu <a href=\"https://blog.cryptpad.fr/2017/07/07/cryptpad-analytics-what-we-cant-know-what-we-must-know-what-we-want-to-know/\">yksityisyydestä kertovaan blogikirjoitukseemme</a> saadaksesi selville, mihin metadataan meillä on pääsy ja mihin taas ei.",
"whatis_zeroknowledge_p3": "Kun jaat linkin asiakirjaan, jaat itse asiassa asiakirjan käyttöön tarvittavan salausavaimen, mutta koska salausavain sisällytetään <a href=\"https://en.wikipedia.org/wiki/Fragment_identifier\">katkelmatunnisteeseen (fragment identifier)</a>, sitä ei koskaan lähetetä palvelimelle suoraan. Tutustu <a href=\"https://blog.cryptpad.fr/2017/07/07/cryptpad-analytics-what-we-cant-know-what-we-must-know-what-we-want-to-know/\">yksityisyydestä kertovaan blogikirjoitukseemme</a> saadaksesi selville, mihin metadataan meillä on pääsy ja mihin taas ei.",
"whatis_drive": "CryptDriven järjestely",
"whatis_drive_p1": "Kun käytät padia CryptPadissa, lisätään se automaattisesti CryptDrivesi pääkansioon. Voit halutessasi myöhemmin järjestellä padit kansioihin tai viedä ne roskakoriin. CryptDrive antaa sinun hakea padejasi ja järjestellä niitä milloin ja miten haluat.",
"whatis_drive_p2": "Intuitiivinen raahaa ja pudota-käyttöliittymä mahdollistaa padien siirtelemisen CryptDrivessa niin, etteivät niiden linkit muutu, eivätkä padien osallistujat siten koskaan menetä käyttöoikeuttaan niihin.",
@ -647,7 +647,7 @@
"admin_activeSessionsTitle": "Aktiiviset yhteydet",
"admin_activeSessionsHint": "Aktiivisten WebSocket-yhteyksien määrä (ja yhdistetyt uniikit IP-osoitteet)",
"admin_activePadsTitle": "Aktiiviset padit",
"admin_activePadsHint": "Tällä hetkellä katseltavien tai muokattavien uniikkien dokumenttien määrä",
"admin_activePadsHint": "Tällä hetkellä katseltavien tai muokattavien uniikkien asiakirjojen määrä",
"admin_registeredTitle": "Rekisteröityneet käyttäjät",
"admin_registeredHint": "CryptPad-instanssiisi rekisteröityneiden käyttäjien määrä",
"admin_updateLimitTitle": "Päivitä käyttäjien tallennuskiintiöt",
@ -655,7 +655,7 @@
"admin_updateLimitButton": "Päivitä tallennuskiintiöt",
"admin_updateLimitDone": "Päivitys onnistui",
"admin_flushCacheTitle": "Tyhjennä HTTP-välimuisti",
"notifications_cat_friends": "Kaveripyynnöt",
"notifications_cat_friends": "Yhteyspyynnöt",
"notifications_cat_pads": "Kanssani jaetut",
"notifications_cat_archived": "Historia",
"notifications_dismissAll": "Hylkää kaikki",
@ -674,10 +674,10 @@
"features_noData": "Henkilötietoja ei tarvita",
"features_pricing": "{0}-{2}€/kk",
"features_emailRequired": "Sähköpostiosoite vaaditaan",
"owner_removeText": "Poista olemassaoleva omistaja",
"owner_removePendingText": "Peru odottava tarjous",
"owner_addText": "Tarjoa yhteisomistajuutta kaverille",
"owner_unknownUser": "Tuntematon käyttäjä",
"owner_removeText": "Omistajat",
"owner_removePendingText": "Odottavat",
"owner_addText": "Tarjoa yhteisomistajuutta yhteystiedolle",
"owner_unknownUser": "tuntematon",
"owner_removeButton": "Poista valitut omistajat",
"owner_removePendingButton": "Peru valitut tarjoukset",
"owner_addButton": "Tarjoa omistajuutta",
@ -689,7 +689,7 @@
"owner_request": "{0} haluaa sinut <b>{1}</b> omistajaksi",
"owner_request_accepted": "{0} on hyväksynyt tarjouksesi <b>{1}</b> omistajuudesta",
"share_linkTeam": "Lisää tiimin CryptDriveen",
"team_pickFriends": "Valitse tiimiin kutsuttavat kaverit",
"team_pickFriends": "Valitse tiimiin kutsuttavat yhteystiedot",
"team_inviteModalButton": "Kutsu",
"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",
@ -711,7 +711,7 @@
"teams_table_owners": "Hallitse tiimiä",
"teams_table_role": "Rooli",
"pad_wordCount": "Sanamäärä: {0}",
"share_linkWarning": "Tämä linkki sisältää dokumenttisi avaimet. Linkin vastaanottajat saavat dokumenttiisi käyttöoikeudet, joita ei voi poistaa jälkikäteen.",
"share_linkWarning": "Tämä linkki sisältää asiakirjasi avaimet. Linkin vastaanottajat saavat asiakirjaasi käyttöoikeudet, joita ei voi poistaa jälkikäteen.",
"share_linkPasswordAlert": "Tämä elementti on salasanasuojattu. Kun lähetät linkin, täytyy vastaanottajan syöttää salasana.",
"share_contactPasswordAlert": "Tämä elementti on salasanasuojattu. Koska jaat sen CryptPad-yhteyshenkilön kanssa, ei vastaanottajan tarvitse syöttää salasanaa.",
"share_embedPasswordAlert": "Tämä elementti on salasanasuojattu. Kun upotat tämän padin, katselijoita pyydetään syöttämään salasana.",
@ -745,67 +745,67 @@
"team_cat_link": "Kutsulinkki",
"team_links": "Kutsulinkit",
"team_inviteInvalidLinkError": "Tämä kutsulinkki ei ole kelvollinen.",
"notificationsPage": "",
"openNotificationsApp": "",
"notifications_cat_all": "",
"owner_request_declined": "",
"owner_removed": "",
"owner_removedPending": "",
"team_pcsSelectLabel": "",
"team_pcsSelectHelp": "",
"team_invitedToTeam": "",
"team_kickedFromTeam": "",
"team_acceptInvitation": "",
"team_declineInvitation": "",
"team_cat_general": "",
"team_cat_list": "",
"team_cat_create": "",
"team_cat_back": "",
"team_cat_members": "",
"team_cat_chat": "",
"team_cat_drive": "",
"team_cat_admin": "",
"team_infoLabel": "",
"team_listLoad": "",
"team_createLabel": "",
"team_createName": "",
"team_rosterPromote": "",
"team_rosterDemote": "",
"team_rosterKick": "",
"team_inviteButton": "",
"team_leaveButton": "",
"team_leaveConfirm": "",
"team_owner": "",
"team_admins": "",
"team_members": "",
"team_nameTitle": "",
"team_nameHint": "",
"team_avatarTitle": "",
"team_avatarHint": "",
"team_infoContent": "",
"team_maxOwner": "",
"team_maxTeams": "",
"team_listTitle": "",
"team_listSlot": "",
"owner_addTeamText": "",
"owner_team_add": "",
"team_rosterPromoteOwner": "",
"team_ownerConfirm": "",
"team_kickConfirm": "",
"sent": "",
"team_pending": "",
"team_deleteTitle": "",
"team_deleteHint": "",
"team_deleteButton": "",
"team_deleteConfirm": "",
"team_pendingOwner": "",
"team_pendingOwnerTitle": "",
"team_demoteMeConfirm": "",
"team_title": "",
"team_quota": "",
"drive_quota": "",
"settings_codeBrackets": "",
"team_viewers": "",
"notificationsPage": "Ilmoitukset",
"openNotificationsApp": "Avaa ilmoituspaneeli",
"notifications_cat_all": "Kaikki",
"owner_request_declined": "{0} on kieltäytynyt tarjoamastasi <b>{1}</b> omistajuudesta",
"owner_removed": "{0} on poistanut omistajuutesi <b>{1}</b>",
"owner_removedPending": "{0} on perunut omistajuustarjouksesi <b>{1}</b>",
"team_pcsSelectLabel": "Tallenna sijaintiin",
"team_pcsSelectHelp": "Omistetun padin luominen tiimin CryptDriveen antaa padin omistajuuden tiimille.",
"team_invitedToTeam": "{0} on kutsunut sinut tiimiin: <b>{1}</b>",
"team_kickedFromTeam": "{0} on potkinut sinut tiimistä: <b>{1}</b>",
"team_acceptInvitation": "{0} on hyväksynyt tarjouksesi liittyä tiimiin: <b>{1}</b>",
"team_declineInvitation": "{0} on kieltäytynyt tarjouksesta liittyä tiimiin: <b>{1}</b>",
"team_cat_general": "Tietoja",
"team_cat_list": "Tiimit",
"team_cat_create": "Uusi",
"team_cat_back": "Takaisin tiimeihin",
"team_cat_members": "Jäsenet",
"team_cat_chat": "Keskustelu",
"team_cat_drive": "CryptDrive",
"team_cat_admin": "Ylläpito",
"team_infoLabel": "Tietoa tiimeistä",
"team_listLoad": "Avaa",
"team_createLabel": "Luo uusi tiimi",
"team_createName": "Tiimin nimi",
"team_rosterPromote": "Ylennä",
"team_rosterDemote": "Alenna",
"team_rosterKick": "Potki tiimistä",
"team_inviteButton": "Kutsu jäseniä",
"team_leaveButton": "Lähde tiimistä",
"team_leaveConfirm": "Jos poistut tiimistä, menetät käyttöoikeutesi sen CryptDriveen, keskusteluhistoriaan ja muuhun sisältöön. Oletko varma?",
"team_owner": "Omistajat",
"team_admins": "Ylläpitäjät",
"team_members": "Jäsenet",
"team_nameTitle": "Tiimin nimi",
"team_nameHint": "Määritä tiimin nimi",
"team_avatarTitle": "Tiimin avatar-kuva",
"team_avatarHint": "Kuvan maksimikoko 500Kt (png, jpg, jpeg, gif)",
"team_infoContent": "Jokaisella tiimillä on oma CryptDrive, tallennustilakiintiö, keskustelualue ja jäsenlista. Tiimien omistajat voivat poistaa tiimin. Ylläpitäjät voivat kutsua tai poistaa jäseniä. Jäsenet voivat lähteä tiimistä.",
"team_maxOwner": "Jokainen käyttäjätili voi omistaa ainoastaan yhden tiimin.",
"team_maxTeams": "Käyttäjätili voi olla jäsenenä vain {0} tiimissä.",
"team_listTitle": "Omat tiimit",
"team_listSlot": "Avoin tiimipaikka",
"owner_addTeamText": "...tai tiimi",
"owner_team_add": "{0} haluaa sinut omistajaksi tiimiin <b>{1}</b>. Hyväksytkö tarjouksen?",
"team_rosterPromoteOwner": "Tarjoa omistajuutta",
"team_ownerConfirm": "Yhteisomistajat voivat muokata tiimiä, poistaa sen kokonaan tai poistaa omistajuutesi. Oletko varma?",
"team_kickConfirm": "{0} saa tietää, että olet poistanut tämän tiimistä. Oletko varma?",
"sent": "Viesti lähetetty",
"team_pending": "Kutsuttu",
"team_deleteTitle": "Tiimin poisto",
"team_deleteHint": "Poista tiimi ja kaikki yksinomaan tiimin omistamat asiakirjat.",
"team_deleteButton": "Poista",
"team_deleteConfirm": "Olet poistamassa kaikki tiimin tiedot. Tämä voi vaikuttaa muiden tiimin jäsenien omien tietojen käyttöön. Toimintoa ei voi perua. Oletko varma, että haluat jatkaa?",
"team_pendingOwner": "(odottaa)",
"team_pendingOwnerTitle": "Tämä ylläpitäjä ei ole vielä hyväksynyt omistajuustarjousta.",
"team_demoteMeConfirm": "Olet luopumassa oikeuksistasi. Tätä toimintoa ei voi perua. Oletko varma?",
"team_title": "Tiimi: {0}",
"team_quota": "Tiimisi tallennustilakiintiö",
"drive_quota": "Tallennustilakiintiösi",
"settings_codeBrackets": "Sulje sulkeet automaattisesti",
"team_viewers": "Katselijat",
"whatis_business_p1": "CryptPadin nollatietoperiaate-salaus moninkertaistaa olemassaolevien tietoturvaprotokollien tehokkuuden peilaamalla organisaation pääsynvalvontaa kryptografiassa. Arkaluonteisten tietojen purkamiseen tarvitaan aina käyttäjän kirjautumistiedot, joten CryptPad on potentiaaliselle hyökkääjälle perinteisiä pilvipalveluita vähempiarvoisempi kohde. Lue lisää hyödyistä yrityksille <a href='https://blog.cryptpad.fr/images/CryptPad-Whitepaper-v1.0.pdf'>CryptPad Whitepaper-julkaisusta</a>.",
"whatis_business_p2": "Cryptpad on saatavilla on-premises-ratkaisuna, ja XWiki SAS:n<a href=\"https://cryptpad.fr/about.html\">CryptPad-kehittäjät</a> tarjoavat kaupallista tukea sekä räätälöinti- ja kehityspalveluita. Ota yhteyttä osoitteeseen <a href=\"mailto:sales@cryptpad.fr\">sales@cryptpad.fr</a> saadaksesi lisätietoja.",
"policy_title": "CryptPad-tietosuojakäytäntö",
@ -823,7 +823,7 @@
"policy_choices": "Vaihtoehtosi",
"policy_choices_open": "Lähdekoodimme on avointa, joten voit halutessasi perustaa oman CryptPad-instanssisi.",
"policy_choices_vpn": "Jos haluat käyttää ylläpitämäämme CryptPad-instanssia, mutta et paljastaa IP-osoitettasi, voit suojata sen käyttämällä <a href=\"https://www.torproject.org/projects/torbrowser.html.en\" title=\"downloads from the Tor project\" target=\"_blank\" rel=\"noopener noreferrer\">Tor-selainpakettia</a> tai <a href=\"https://riseup.net/en/vpn\" title=\"VPNs provided by Riseup\" target=\"_blank\" rel=\"noopener noreferrer\">VPN-palvelua</a>.",
"policy_choices_ads": "Jos haluat ainoastaan estää analytiikkapalvelumme, voit käyttää mainosten estämiseen tarkoitettuja työkaluja, kuten <a hre=\"https://www.eff.org/privacybadger\" title=\"download privacy badger\" target=\"_blank\" rel=\"noopener noreferrer\">Privacy Badgeria</a>.",
"policy_choices_ads": "Jos haluat ainoastaan estää analytiikkapalvelumme, voit käyttää mainosten estämiseen tarkoitettuja työkaluja, kuten <a href=\"https://www.eff.org/privacybadger\" title=\"download privacy badger\" target=\"_blank\" rel=\"noopener noreferrer\">Privacy Badgeria</a>.",
"features": "Ominaisuudet",
"features_title": "Ominaisuuksien vertailu",
"features_feature": "Ominaisuus",
@ -872,7 +872,7 @@
"title": "Avainsanat",
"pad": {
"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."
"a": "<em>Padi</em> on <a href='http://etherpad.org/' target='_blank'>Etherpad-projektin</a> popularisoima termi reaaliaikaiselle kollaboratiiviselle editorille.\nSe tarkoittaa selaimessa muokattavaa asiakirjaa, jossa muiden käyttäjien tekemät muutokset näkyvät lähes välittömästi."
},
"owned": {
"q": "Mikä on omistettu padi?",
@ -884,15 +884,15 @@
},
"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>)."
"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."
"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 napsauttamalla 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."
"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 asiakirjat poistetaan palvelimelta automaattisesti."
}
},
"privacy": {
@ -925,7 +925,7 @@
"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."
"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 asiakirjaasi.<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 asiakirjaasi. Siksi salasana tallennetaan ja lähetetään padin mukana jakaessasi sitä CryptPadin sisällä. Vastaanottajalta tai sinulta itseltäsi <b>ei</b> pyydetä salasanaa asiakirjaa avatessa."
},
"title": "Turvallisuus",
"proof": {
@ -957,7 +957,7 @@
},
"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>."
"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>napsauttamalla padia hiiren oikealla painikkeella</strong> ja valitsemalla <strong>Poista palvelimelta</strong>."
},
"forget": {
"q": "Mitä tapahtuu, jos unohdan salasanani?",
@ -977,7 +977,7 @@
},
"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."
"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": {
@ -1018,20 +1018,20 @@
"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",
"more": "Tutustu <a href=\"/faq.html\" target=\"_blank\">usein kysyttyihin kysymyksiin</a> saadaksesi lisätietoja CryptPadin toiminnallisuudesta.",
"share": "Jaa tämä asiakirja <i class=\"fa fa-shhare-alt\"></i> <b>Jaa</b>-painikkeella ja hallitse käyttöoikeuksia <i class=\"fa fa-unlock-alt\"></i> <b>Käyttöoikeudet</b>-painikkeella.",
"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",
"formatting": "Näytä tai piilota tekstityökalupalkki <i class=\"fa fa-wrench\"></i> <b>Työkalut</b>-painikkeella.",
"embed": "Upota kuvatiedosto CryptDrivestasi tai tietokoneeltasi <i class=\"fa fa-image\"></i> <b>Upota</b>-painikkeella (vain rekisteröityneille käyttäjille).",
"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"
"modes": "Käytä <span class=\"cptools cptools-palette\"></span> <b>Teema</b> -valikkoa valitaksesi editorin teeman ja syntaksin korostustilan."
},
"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>"
@ -1042,7 +1042,7 @@
"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",
"settings": "Käytä <span class=\"cptools cptools-palette\"></span> <b>Teema</b>-valikkoa vaihtaaksesi diavärejä ja <span class=\"fa fa-cog\"></span> <b>Asetukset</b> vaihtaaksesi taustakuvaa, siirtymiä, sivunumeroita jne.",
"colors": "Vaihda tekstin ja taustan väriä <span class=\"fa fa-i-cursor\"></span> ja <span class=\"fa fa-square\"></span> -painikkeilla"
},
"poll": {
@ -1053,16 +1053,356 @@
},
"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>"
"mode": "Siirry <span class=\"fa fa-arrows\"></span> valintatilaan siirtääksesi ja muuttaaksesi viivoja.",
"embed": "Upota kuvia CryptDrivestasi tai tietokoneeltasi <span class=\"fa fa-file-image-o\"></span> <b>Lisää</b>-toiminnolla. Vie kangas PNG-tiedostomuodossa CryptDriveesi <b><span class=\"fa fa-file-o\"></span> Tiedosto > <span class=\"fa fa-file-image-o\"></span> Tallenna kuvatiedostona</b> tai omalle tiedokoneellesi <b><span class=\"fa fa-file-o\"></span> Tiedosto > <span class=\"fa fa-download\"></span> Vie</b>."
},
"kanban": {
"add": "Lisää uusia kortteja ja tauluja <span class=\"fa fa-plus\"></span> -painikkeella",
"add": "Lisää uusia kortteja ja tauluja <span class=\"fa fa-plus\"></span> -painikkeella",
"task": "Siirrä kohtia raahaamalla ja pudottamalla, raahaa <span class=\"fa fa-trash\"></span> roskakoriin poistaaksesi",
"color": "Muokkaa otsikoita, sisältöä, tunnisteita ja värejä korttien ja taulujen otsikoiden vieressä olevalla span class=\"fa fa-pencil\"></span> -painikkeella"
"color": "Muokkaa otsikoita, sisältöä, tunnisteita ja värejä korttien ja taulujen otsikoiden vieressä olevalla <span class=\"fa fa-pencil\"></span> -painikkeella"
}
},
"driveReadmeTitle": "Mikä on CryptPad?",
"readme_welcome": "Tervetuloa CryptPadiin!",
"readme_p1": "Tervetuloa CryptPadiin, täällä voit tehdä muistiinpanoja yksin tai kontaktiesi kanssa."
"readme_p1": "Tervetuloa CryptPadiin, täällä voit tehdä muistiinpanoja yksin tai kontaktiesi kanssa.",
"settings_kanbanTagsOr": "TAI",
"settings_kanbanTagsAnd": "JA",
"settings_kanbanTagsHint": "Valitse, miten haluat tunnistesuodattimen toimivan useita tunnisteita käytettäessä: näytä kortit, jotka sisältävät kaikki valitut tunnisteet (JA) tai näytä kortit, jotka sisältävät jonkun tai jotkin valituista tunnisteista (TAI)",
"settings_kanbanTagsTitle": "Tunnistesuodatin",
"pad_tocHide": "Hahmotelma",
"fm_noResult": "Ei tuloksia",
"fm_restricted": "Sinulla ei ole käyttöoikeutta",
"fm_emptyTrashOwned": "Roskakori sisältää omistamiasi asiakirjoja. Voit <b>poistaa</b> ne omasta CryptDrivestasi tai <b>tuhota</b> ne kaikilta käyttäjiltä.",
"support_formCategoryError": "Virhe: kategoria on tyhjä",
"support_category": "Valitse kategoria",
"oo_refreshText": "Tämä asiakirja on päivitetty",
"oo_refresh": "Päivitä",
"notification_folderSharedTeam": "{0} on jakanut tiimin {2} kanssa kansion: <b>{1}</b>",
"notification_fileSharedTeam": "{0} on jakanut tiimin {2} kanssa tiedoston: <b>{1}</b>",
"notification_padSharedTeam": "{0} on jakanut tiimin {2} kanssa padin: <b>{1}</b>",
"support_addAttachment": "Lisää liite",
"support_attachments": "Liitteet",
"support_cat_all": "Kaikki",
"support_cat_other": "Muu",
"support_cat_bug": "Virheraportti",
"support_cat_data": "Sisällön katoaminen",
"support_cat_account": "Käyttäjätili",
"info_privacyFlavour": "<a>Tietosuojakäytäntömme</a> kertoo, miten kohtelemme tietojasi.",
"user_about": "Tietoja CryptPadista",
"info_imprintFlavour": "<a>Oikeudellisia tietoja tämän instanssin ylläpitäjistä</a>.",
"settings_safeLinkDefault": "Turvalliset linkit on nyt otettu käyttöön oletusarvoisesti. Käytä <i class=\"fa fa-shhare-alt\"></i> <b>Jaa</b>-valikkoa linkkien kopiointiin selaimesi osoiterivin sijaan.",
"support_languagesPreamble": "Tukitiimi puhuu seuraavia kieliä:",
"slide_textCol": "Tekstin väri",
"slide_backCol": "Taustaväri",
"toolbar_file": "Tiedosto",
"code_editorTheme": "Editoriteema",
"drive_treeButton": "Tiedostot",
"toolbar_tools": "Työkalut",
"toolbar_savetodrive": "Tallenna kuvatiedostona",
"toolbar_insert": "Lisää",
"toolbar_theme": "Teema",
"todo_move": "Tehtäväluettelosi on nyt kanbanissa <b>{0}</b> CryptDrivessasi.",
"fm_sort": "Lajittele",
"comments_error": "Kommentin lisääminen ei onnistu tähän",
"settings_padNotifCheckbox": "Ota kommentti-ilmoitukset pois käytöstä",
"settings_padNotifHint": "Jätä huomioitta ilmoitukset, kun joku vastaa kommentteihisi",
"settings_padNotifTitle": "Kommentti-ilmoitukset",
"comments_comment": "Kommentoi",
"comments_resolve": "Ratkaise",
"comments_reply": "Vastaa",
"comments_submit": "Lähetä",
"comments_edited": "Muokattu",
"comments_deleted": "Tekijän poistama kommentti",
"mentions_notification": "{0} on maininnut sinut <b>{1}</b>",
"unknownPad": "Tuntematon padi",
"comments_notification": "Vastaukset kommenttiisi \"{0}\" <b>{1}</b>",
"cba_title": "Tekijäkohtaiset värit",
"pad_usePageWidth": "Sivutila",
"pad_useFullWidth": "Täyden leveyden tila",
"oo_login": "Kirjaudu sisään tai rekisteröidy parantaaksesi taulukoiden suorituskykyä.",
"cba_hide": "Piilota tekijäkohtaiset värit",
"cba_show": "Näytä tekijäkohtaiset värit",
"cba_disable": "Tyhjennä ja poista käytöstä",
"cba_enable": "Ota käyttöön",
"cba_hint": "Tämä asetus muistetaan seuraavan padin luonnin yhteydessä.",
"cba_properties": "Tekijäkohtaiset värit (kokeellinen)",
"cba_writtenBy": "Kirjoittaja: {0}",
"canvas_select": "Valinta",
"canvas_brush": "Sivellin",
"admin_openFilesHint": "Palvelimella olevien avoimien tiedostokahvojen määrä.",
"admin_openFilesTitle": "Avoimet tiedostot",
"profile_copyKey": "Kopioi julkinen avain",
"oo_isLocked": "synkronoidaan muutoksia, odota",
"kanban_editBoard": "Muokkaa tätä taulua",
"kanban_editCard": "Muokkaa tätä korttia",
"kanban_clearFilter": "Tyhjennä suodatin",
"kanban_conflicts": "Muokkaamassa:",
"kanban_noTags": "Ei tunnisteita",
"kanban_tags": "Suodata tunnisteella",
"kanban_delete": "Poista",
"kanban_color": "Väri",
"kanban_title": "Otsikko",
"teams": "Tiimit",
"allow_text": "Käyttölista tarkoittaa, että vain valitut käyttäjät ja omistajat pääsevät käyttämään tätä asiakirjaa.",
"owner_text": "Padin omistajat ovat ainoita käyttäjiä, jotka saavat lisätä/poistaa omistajia, rajoittaa padin käyttöä käyttölistalla tai poistaa padin.",
"access_muteRequests": "Vaimenna tämän padin käyttöpyynnöt",
"allow_label": "Käyttölista: {0}",
"allow_disabled": "pois käytöstä",
"allow_enabled": "käytössä",
"allow_checkbox": "Ota käyttöön käyttölista",
"access_noContact": "Ei lisättäviä yhteystietoja",
"contacts": "Yhteystiedot",
"accessButton": "Käyttöoikeudet",
"access_allow": "Lista",
"access_main": "Käyttöoikeudet",
"copy_title": "{0} (kopio)",
"makeACopy": "Tee kopio",
"settings_trimHistoryHint": "Säästä tallennustilaa poistamalla CryptDrivesi historia ja ilmoitukset. Tämä ei vaikuta padiesi historiaan. Voit poistaa padien historian niiden Ominaisuudet-ikkunasta.",
"settings_trimHistoryTitle": "Poista historiatiedot",
"trimHistory_noHistory": "Ei poistettavia historiatietoja",
"trimHistory_currentSize": "Nykyinen historiakoko: <b>{0}</b>",
"trimHistory_needMigration": "Ole hyvä ja <a>päivitä CryptDrivesi</a> käyttääksesi tätä toimintoa.",
"trimHistory_success": "Historiatiedot on poistettu",
"trimHistory_error": "Virhe historiatietoja poistettaessa",
"trimHistory_getSizeError": "Virhe CryptDriven historian kokoa laskettaessa",
"trimHistory_button": "Poista historiatiedot",
"historyTrim_contentsSize": "SIsältö: {0}",
"historyTrim_historySize": "Historia: {0}",
"areYouSure": "Oletko varma?",
"settings_safeLinksHint": "CryptPad sisällyttää padien avaamiseen tarvittavat avaimet niiden linkkeihin. Kuka tahansa selaushistoriaasi katseleva voi mahdollisesti päästä käsiksi tietoihisi. Tämä sisältää urkkivat selainlaajennukset ja selaushistorian usealle laitteelle synkronoivat selaimet. Turvallisten linkkien käyttöönotto estää linkkien tallentumisen selaushistoriaan tai niiden näyttämisen selaimen osoitepalkissa silloin, kun se on mahdollista. Suosittelemme tämän toiminnon käyttöönottoa ja {0} Jaa-valikon käyttöä.",
"profile_login": "Kirjaudu sisään lisätäksesi tämän käyttäjän yhteystietoihisi",
"dontShowAgain": "Älä näytä uudestaan",
"settings_safeLinksCheckbox": "Ota käyttöön turvalliset linkit",
"settings_safeLinksTitle": "Turvalliset linkit",
"settings_cat_security": "Luottamuksellisuus",
"imprint": "Oikeudellinen huomautus",
"oo_sheetMigration_anonymousEditor": "Taulukon muokkaaminen on poistettu käytöstä anonyymeille käyttäjille, kunnes rekisteröitynyt käyttäjä päivittää sen viimeisimpään versioon.",
"oo_sheetMigration_complete": "Päivitetty versio saatavilla, paina OK ladataksesi uudelleen.",
"oo_sheetMigration_loading": "Päivitetään taulukkoasi viimeisimpään versioon",
"oo_exportInProgress": "Vienti menossa",
"oo_importInProgress": "Tuonti menossa",
"oo_invalidFormat": "Tätä tiedostoa ei voida tuoda",
"oo_exportChrome": "Microsoft Office-tiedostomuotoihin muuntaminen on tällä hetkellä saatavilla ainoastaan Google Chrome-selaimessa.",
"burnAfterReading_warningDeleted": "Tämä padi on poistettu pysyvästi. Kun suljet tämän ikkunan, et pääse enää käyttämään sitä.",
"burnAfterReading_proceed": "katsele ja poista",
"burnAfterReading_generateLink": "Napsauta allaolevaa painiketta luodaksesi linkin.",
"burnAfterReading_warningLink": "Olet laittanut tämän padin itsetuhotilaan. Kun vastaanottaja avaa tämän linkin, hän voi katsella padia kerran, jonka jälkeen se poistetaan pysyvästi.",
"burnAfterReading_linkBurnAfterReading": "Katso kerran ja tuhoudu",
"fc_noAction": "Ei saatavilla olevia toimintoja",
"support_closed": "Tämä palvelupyyntö on suljettu",
"support_from": "<b>Lähettäjä:</b> {0}",
"support_showData": "Näytä/piilota käyttäjätiedot",
"support_remove": "Poista palvelupyyntö",
"support_close": "Sulje palvelupyyntö",
"support_answer": "Vastaa",
"support_listHint": "Tässä on lista ylläpitäjille lähetetyistä palvelupyynnöistä ja niiden vastauksista. Suljettua palvelupyyntöä ei voida avata uudelleen, mutta voit luoda uuden. Voit piilottaa suljetut palvelupyynnöt.",
"support_listTitle": "Tukipalvelupyynnöt",
"support_cat_tickets": "Olemassaolevat palvelupyynnöt",
"support_formMessage": "Kirjoita viestisi...",
"support_formContentError": "Virhe: sisältö on tyhjä",
"support_formTitleError": "Virhe: otsikko on tyhjä",
"support_formButton": "Lähetä",
"support_formHint": "Tällä lomakkeella voit luoda uuden palvelupyynnön. Käytä sitä ottaaksesi ylläpitäjiin yhteyttä ongelmatilanteissa tai kysyäksesi kysymyksiä turvallisella tavalla. Älä luo uutta palvelupyyntöä, jos olet jo luonut palvelupyynnön samasta ongelmasta, vaan käytä vastauspainiketta antaaksesi lisätietoja.",
"support_formTitle": "Uusi palvelupyyntö",
"support_cat_new": "Uusi palvelupyyntö",
"support_disabledHint": "Tätä CryptPad-instanssia ei ole konfiguroitu tukilomaketta varten.",
"support_disabledTitle": "Tuki ei ole käytössä",
"admin_supportListHint": "Tässä on lista käyttäjien tukipostilaatikkoon lähettämistä palvelupyynnöistä. Kaikki ylläpitäjät voivat nähdä viestit ja niiden vastaukset. Suljettua palvelupyyntöä ei voida avata uudelleen. Voit ainoastaan poistaa (piilottaa) suljettuja palvelupyyntöjä. Muut ylläpitäjät näkevät edelleen poistetut palvelupyynnöt.",
"admin_supportListTitle": "Tukipostilaatikko",
"admin_supportInitHint": "Voit konfiguroida tukipostilaatikon CryptPad-instanssisi käyttäjiltä ongelmatilanteissa tulevia tukipyyntöjä varten.",
"admin_supportInitTitle": "Tukipostilaatikon alustaminen",
"admin_supportAddError": "Virheellinen yksityinen avain",
"admin_supportAddKey": "Lisää yksityinen avain",
"admin_supportInitPrivate": "CryptPad-instanssisi on konfiguroitu käyttämään ylläpitopostilaatikkoa, mutta käyttäjätililläsi ei ole sen käyttöön tarvittavaa yksityistä avainta. Käytä seuraavaa lomaketta lisätäksesi tai päivittääksesi yksityinen avain.",
"admin_supportInitHelp": "Palvelimellesi ei ole asetettu ylläpitopostilaatikkoa. Jos haluat ottaa käyttöön ylläpitopostilaatikon käyttäjiltä tulevia viestejä varten, pyydä palvelimen ylläpitäjää ajamaan skripti \"./scripts/generate-admin-keys.js\", laittamaan skriptin generoima julkinen avain \"config.js\"-tiedostoon ja lähettämään sinulle yksityinen avain.",
"admin_cat_support": "Tuki",
"supportPage": "Tuki",
"share_description": "Valitse, mitä haluat jakaa ja luo jakolinkki tai jaa suoraan CryptPad-yhteystiedoillesi.",
"fm_info_sharedFolderHistory": "Tämä on jaetun kansiosi historia: <b>{0}</b><br/>CryptDrivesi pysyy vain luku-tilassa selatessasi sitä.",
"notifications_dismiss": "Hylkää",
"share_withFriends": "Jaa",
"share_linkFriends": "Jaa yhteystietojen kanssa",
"share_filterFriend": "Hae nimen perusteella",
"share_deselectAll": "Poista valinta",
"share_selectAll": "Valitse kaikki",
"notification_folderShared": "{0} on jakanut kanssasi kansion: <b>{1}</b>",
"notification_fileShared": "{0} on jakanut kanssasi tiedoston: <b>{1}</b>",
"notification_padShared": "{0} on jakanut kanssasi padin: <b>{1}</b>",
"isNotContact": "{0} <b>ei</b> ole yhteystiedoissasi",
"isContact": "{0} on yhteystiedoissasi",
"profile_friendRequestSent": "Yhteyspyyntö odottaa...",
"profile_addLink": "Lisää linkki kotisivullesi",
"profile_editDescription": "Muokkaa kuvaustasi",
"profile_addDescription": "Lisää kuvaus",
"notifications_title": "Sinulla on lukemattomia ilmoituksia",
"notifications_empty": "Ei ilmoituksia saatavilla",
"friendRequest_notification": "<b>{0}</b> lähetti sinulle yhteyspyynnön",
"friendRequest_received": "<b>{0}</b> haluaisi olla yhteystietosi",
"friendRequest_accepted": "<b>{0}</b> hyväksyi yhteyspyyntösi",
"friendRequest_declined": "<b>{0}</b> kieltäytyi yhteyspyynnöstäsi",
"friendRequest_decline": "Kieltäydy",
"friendRequest_accept": "Hyväksy (Enter)",
"friendRequest_later": "Päätä myöhemmin",
"drive_activeOld": "Vanhemmat padit",
"drive_active28Days": "Viimeiset 4 viikkoa",
"drive_active7Days": "Viimeiset 7 päivää",
"drive_active1Day": "Viimeiset 24 tuntia",
"settings_codeSpellcheckLabel": "Ota oikeinkirjoituksen tarkistus käyttöön koodieditorissa",
"settings_codeSpellcheckTitle": "Oikeinkirjoituksen tarkistus",
"admin_diskUsageButton": "Luo raportti",
"admin_diskUsageHint": "CryptPad-resurssien käyttämä tallennustila",
"admin_diskUsageTitle": "Tallennustilan käyttö",
"timeoutError": "Virhe on katkaissut yhteytesi palvelimelle. <br> Paina <em>Esc</em> ladataksesi sivun uudelleen.",
"contact_email": "Sähköposti",
"contact_chat": "Keskustelu",
"contact_bug": "Virheraportti",
"contact_devHint": "Ominaisuuspyyntöjä, käytettävyysparannuksia tai kiitoksia varten.",
"contact_dev": "Ota yhteyttä kehittäjiin",
"contact_adminHint": "Käyttäjätiliin, tallennustilaan tai palvelun saatavuuteen liittyviä ongelmia varten.",
"contact_admin": "Ota yhteyttä ylläpitäjiin",
"footer_tos": "Käyttöehdot",
"footer_legal": "Oikeusasiat",
"footer_donate": "Lahjoita",
"footer_team": "Tiimimme",
"footer_product": "Tuote",
"admin_flushCacheDone": "Välimuisti tyhjennetty onnistuneesti",
"admin_flushCacheButton": "Tyhjennä välimuisti",
"admin_flushCacheHint": "Pakota käyttäjät lataamaan uusimmat asiakassovelluksen osat (ainoastaan jos palvelimesi on fresh modessa)",
"adminPage": "Ylläpito",
"admin_cat_stats": "Tilastotiedot",
"admin_cat_general": "Yleiset asetukset",
"admin_authError": "Vain ylläpitäjät pääsevät tälle sivulle",
"fm_expirablePad": "Vanhenee: {0}",
"markdown_toc": "Sisältö",
"survey": "CryptPad-kysely",
"crowdfunding_popup_no": "Ei nyt",
"crowdfunding_popup_yes": "Mene OpenCollectiveen",
"crowdfunding_popup_text": "<h3>Tarvitsemme apuasi!</h3>Tukeaksesi CryptPadin aktiivista kehitystä, harkitse projektin tukemista <a href=\"https://opencollective.com/cryptpad\">OpenCollective-sivullamme</a>, jossa voit nähdä <b>tiekarttamme</b> ja <b>rahoitustavoitteemme</b>.",
"crowdfunding_button2": "Auta CryptPadia",
"crowdfunding_button": "Tue CryptPadia",
"crowdfunding_home2": "Napsauta painiketta saadaksesi lisätietoja joukkorahoituskampanjastamme.",
"crowdfunding_home1": "CryptPad tarvitsee apuasi!",
"autostore_notAvailable": "Sinun täytyy tallentaa tämä padi CryptDriveesi käyttääksesi tätä toimintoa.",
"autostore_forceSave": "Tallenna tiedosto CryptDriveesi",
"autostore_saved": "Padi tallennettiin onnistuneesti CryptDriveesi!",
"autostore_error": "Odottamaton virhe: emme voineet tallentaa padia, ole hyvä ja yritä uudestaan.",
"autostore_hide": "Älä tallenna",
"autostore_store": "Tallenna",
"autostore_settings": "Voit ottaa automaattisen padien tallennuksen käyttöön <a href=\"/settings/\">Asetukset</a>-sivulta!",
"autostore_notstored": "Tämä {0} ei ole tallennettuna CryptDrivessasi. Haluatko tallentaa sen nyt?",
"autostore_pad": "padi",
"autostore_sf": "kansio",
"autostore_file": "tiedosto",
"chrome68": "Näyttää, että käytät Chrome- tai Chromium-selaimen versiota 68. Siinä on bugi, joka saattaa muuttaa sivun valkoiseksi tai estää sivua reagoimasta napsautuksiin. Korjataksesi ongelman voit siirtyä toiseen välilehteen ja siirtyä takaisin tai vaihtoehtoisesti vierittää sivua. Bugin tulisi olla korjattu selaimesi seuraavassa versiossa.",
"convertFolderToSF_confirm": "Tämä kansio täytyy muuttaa jaetuksi kansioksi, jotta muut voivat katsella sitä. Jatketaanko?",
"convertFolderToSF_SFChildren": "Tätä kansiota ei voida muuttaa jaetuksi kansioksi, sillä se sisältää ennestään jaettuja kansioita. Siirrä jaetut kansiot muualle jatkaaksesi.",
"convertFolderToSF_SFParent": "Tätä kansiota ei voida muuttaa jaetuksi kansioksi sen nykyisessä sijainnissa. Siirrä se nykyisen jaetun kansion ulkopuolelle jatkaaksesi.",
"sharedFolders_share": "Jaa tämä linkki muiden rekisteröityneiden käyttäjien kanssa antaaksesi heille pääsyn jaettuun kansioon. Kun linkki avataan, lisätään jaettu kansio heidän CryptDriveihinsa.",
"sharedFolders_create_password": "Kansion salasana",
"sharedFolders_create_owned": "Omistettu kansio",
"sharedFolders_create_name": "Kansion nimi",
"sharedFolders_create": "Luo jaettu kansio",
"sharedFolders_duplicate": "Jotkin padeista, joita yritit siirtää olivat jo jaettuja kohdekansiossa.",
"sharedFolders_forget": "Tämä padi on tallennettu ainoastaan jaettuun kansioon, et voi siirtää sitä roskakoriin. Voit käyttää CryptDriveasi sen poistamiseen.",
"loading_drive_3": "Tarkistetaan tietojen eheyttä",
"loading_drive_2": "Päivitetään tietomuotoa",
"loading_drive_1": "Ladataan tietoja",
"loading_pad_2": "Ladataan padin sisältöä",
"loading_pad_1": "Alustetaan padia",
"share_mediatagCopy": "Kopioi mediatunniste leikepöydälle",
"share_embedCategory": "Upota",
"share_contactCategory": "Yhteystiedot",
"share_linkCopy": "Kopioi",
"share_linkOpen": "Esikatsele",
"share_linkPresent": "Nykyinen",
"share_linkEmbed": "Upotettu tila (piilota työkalupalkki ja käyttäjälista)",
"share_linkView": "Katsele",
"share_linkEdit": "Muokkaa",
"share_linkAccess": "Käyttöoikeudet",
"share_linkCategory": "Linkki",
"properties_changePasswordButton": "Lähetä",
"properties_passwordSuccess": "Salasana vaihdettiin onnistuneesti.<br>Napsauta OK ladataksesi sivu uudelleen ja päivittääksesi käyttöoikeudet.",
"properties_passwordWarning": "Salasana vaihdettiin onnistuneesti, mutta emme voineet päivittää CryptDriveasi uusilla tiedoilla. Joudut ehkä poistamaan padin vanhan version manuaalisesti.<br>Napsauta OK ladataksesi sivu uudelleen ja päivittääksesi käyttöoikeudet.",
"properties_passwordError": "Salasanan vaihtamisessa tapahtui virhe. Ole hyvä ja yritä uudelleen.",
"properties_passwordSame": "Uuden salasanan täytyy erota nykyisestä salasanasta.",
"properties_confirmNew": "Oletko varma? Salasanan lisääminen muuttaa padin URL-osoitteen ja poistaa sen historiatiedot. Käyttäjät, joilla ei ole salasanaa eivät enää pääse käyttämään padia",
"properties_confirmChange": "Oletko varma? Salasanan vaihtaminen poistaa sen historian. Käyttäjät, joilla ei ole salasanaa eivät enää pääse käyttämään padia",
"properties_changePassword": "Vaihda salasanaa",
"properties_addPassword": "Lisää salasana",
"password_show": "Näytä",
"password_submit": "Lähetä",
"password_placeholder": "Kirjoita salasana tähän...",
"password_error": "Padia ei löytynyt!<br>Tämä virhe voi johtua kahdesta syystä: joko salasana on virheellinen tai padi on poistettu palvelimelta.",
"password_info": "Padia, jota yrität avata ei ole enää olemassa tai se on suojattu salasanalla. Syötä oikea salasana käyttääksesi padin sisältöä.",
"creation_newPadModalAdvanced": "Näytä padien luontiruutu",
"creation_newPadModalDescriptionAdvanced": "Voit rastittaa valintaruudun (tai painaa <b>Välilyönti</b>-painiketta vaihtaaksesi sen arvoa), jos haluat näyttää padien luontiruudun (omistetut ja vanhenevat padit jne.).",
"creation_newPadModalDescription": "Napsauta padityyppiä luodaksesi sellainen. Voit myös painaa <b>Tab</b>-näppäintä valitaksesi tyypin ja <b>Enter</b> vahvistaaksesi valinnan.",
"creation_appMenuName": "Uusi padi (Ctrl + E)",
"creation_propertiesTitle": "Saatavuus",
"creation_passwordValue": "Salasana",
"creation_expiration": "Vanhenemisaika",
"creation_noOwner": "Ei omistajaa",
"creation_ownedByOther": "Toisen käyttäjän omistama",
"creation_owners": "Omistajat",
"creation_rememberHelp": "Käy Asetukset-sivulla nollataksesi tämän asetuksen",
"creation_settings": "Katso lisää asetuksia",
"creation_create": "Luo",
"creation_newTemplate": "Uusi mallipohja",
"creation_noTemplate": "Ei mallipohjaa",
"creation_password": "Lisää salasana",
"creation_expire2": "<b>Vanhenevalla</b> padilla on asetettu elinikä, jonka täyttyessä se poistetaan automaattisesti palvelimelta ja muiden käyttäjien CryptDriveista.",
"creation_expire1": "<b>Rajoittamatonta</b> padia ei poisteta palvelimelta, ellei sen omistaja poista sitä.",
"creation_expireMonths": "Kuukausi(a)",
"creation_expireDays": "Päiv(i)ä",
"creation_expireHours": "Tunti(a)",
"creation_expireFalse": "Rajoittamaton",
"creation_expireTrue": "Lisää elinaika",
"creation_expire": "Vanheneva padi",
"creation_expireTitle": "Elinikä",
"creation_owned2": "<b>Avoimella</b> padilla ei ole omistajaa, ja sitä ei voida poistaa palvelimelta, ellei sen vanhenemisaika ole tullut täyteen.",
"creation_owned1": "<b>Omistettu</b> padi voidaan poistaa palvelimelta omistajan niin halutessa. Omistetun padin poistaminen poistaa sen muiden käyttäjien CryptDriveista.",
"creation_ownedFalse": "Avoin padi",
"creation_ownedTrue": "Omistettu padi",
"creation_owned": "Omistettu padi",
"creation_ownedTitle": "Padin tyyppi",
"creation_404": "Tätä padia ei ole enää olemassa. Käytä seuraavaa lomaketta uuden padin luontiin.",
"feedback_optout": "Jos haluat jättäytyä pois tästä toiminnallisuudesta, voit tehdä sen <a href='/settings/'>Käyttäjäasetukset</a>-sivulta löytyvän käyttäjäpalautevalintaruudun avulla",
"feedback_privacy": "Välitämme yksityisyydestäsi, ja haluamme samalla tehdä CryptPadista mahdollisimman helppokäyttöisen. Käytämme tätä tiedostoa selvittääksemme, mitkä käyttöliittymätoiminnot ovat tärkeitä käyttäjillemme, pyytämällä sitä tehdyn toiminnon kertovan parametrin yhteydessä.",
"feedback_about": "Jos luet tätä, haluat todennäköisesti tietää, miksi CryptPad lähettää pyyntöjä web-sivuille tiettyjen toimintojen yhteydessä",
"tips": {
"tags": "Lisää padeihisi tunnisteita ja aloita haku #-merkillä CryptDrivessasi löytääksesi ne tunnisteen perusteella",
"avatars": "Voit ladata käyttäjäprofiiliisi avatar-kuvan. Muut padia kanssasi käyttävät henkilöt näkevät sen.",
"profile": "Rekisteröityneet käyttäjät voivat luoda käyttäjäprofiilin oikeasta yläkulmasta löytyvästä käyttäjävalikosta.",
"drive": "Sisäänkirjautuneet käyttäjät voivat järjestellä CryptDriveen tallennettuja tiedostoja napsauttamalla kaikkien padien vasemmassa yläkulmassa olevaa CryptPad-kuvaketta.",
"filenames": "Voit nimetä tiedostoja uudelleen CryptDrivessasi, tämä nimi on vain sinua varten.",
"driveUpload": "Rekisteröityneet käyttäjät voivat ladata salattuja tiedostoja napsauttamalla ja raahaamalla ne omiin CryptDriveihinsa.",
"marker": "Voit korostaa padissa olevaa tekstiä käyttämällä tyylialavalikosta löytyvää huopakynää.",
"store": "Joka kerta kun käytät padia sisäänkirjautuneena, se tallennetaan CryptDriveesi.",
"indent": "Numeroiduissa luetteloissa ja luettelomerkkiluetteloissa voit käyttää Tab- tai Shift+Tab-näppäinyhdistelmiä kasvattaaksesi tai pienentääksesi sisennystä.",
"shortcuts": "`ctrl+b`, `ctrl+i` ja `ctrl+u`toimivat oikoteinä lihavoidulle, kursiiville ja alleviivatulle tekstille."
},
"readme_cat3_l3": "CryptPad-kyselyilä voit pitää nopeita äänestyksiä esimerkiksi kokouksien aikataulujen yhteensovittamiseksi",
"readme_cat3_l2": "CryptPad-diaeditorilla voit luoda nopeasti diaesityksiä Markdown-kielellä",
"readme_cat3_l1": "CryptPad-koodieditorilla voit työskennellä yhdessä muiden kanssa koodin, kuten Javascriptin ja Markdownin parissa",
"settings_cat_kanban": "Kanban",
"kanban_body": "Sisältö",
"logoutEverywhere": "Kirjaudu ulos kaikkialta",
"restrictedError": "Sinulla ei ole oikeutta käyttää tätä asiakirjaa",
"safeLinks_error": "Tämä linkki ei anna sinulle käyttöoikeutta asiakirjaan",
"burnAfterReading_warningAccess": "Tämä asiakirja tuhoaa itsensä. Kun napsautat allaolevaa painiketta, näet sisällön kerran, jonka jälkeen se poistetaan pysyvästi. Kun suljet tämän ikkunan, et pääse enää käyttämään asiakirjan sisältöä. Jos et ole valmis jatkamaan, voit sulkea tämän ikkunan ja tulla myöhemmin takaisin.",
"profile_info": "Muut käyttäjät voivat löytää käyttäjäprofiilisi asiakirjojen käyttäjälistoissa näkyvän avatarin avulla.",
"readme_cat3": "Löydä CryptPad-sovellukset",
"readme_cat2_l2": "Vaihda padin otsikkoa napsauttamalla lyijykynää",
"readme_cat2_l1": "Padistasi löytyvä {0}-painike antaa sinun jakaa yhteistyökumppaneillesi oikeuden [1} tai {2} padia.",
"edit": "muokata",
"view": "katsella",
"readme_cat2": "Luo padeja kuin ammattilainen",
"readme_cat1_l4": "Laita vanhat padit roskakoriin: Voit napsauttaa ja raahata padeja {0}in samalla tavalla kuin raahaisit niitä kansioihin.",
"readme_cat1_l3_l2": "Muista kokeilla hiiren oikeaa painiketta kuvakkeiden päällä, sillä niistä aukeaa usein lisävalikoita.",
"readme_cat1_l3_l1": "Voit napsauttaa ja raahata tiedostoja kansioihin CryptDrivesi {0}-osiossa ja luoda uusia kansioita.",
"readme_cat1_l3": "Järjestele padejasi: Kun olet kirjautuneena sisään, kaikki käyttämäsi padit näytetään CryptDrivesi {0}-osiossa.",
"readme_cat1_l2": "Avaa padeja CryptDrivestasi: kaksoisnapsauta padin kuvaketta avataksesi sen.",
"readme_cat1_l1": "Luo padi: Siirry CryptDriveesi, napsauta {0} ja sitten {1}, ja voit luoda padin.",
"readme_cat1": "Tutustu CryptDriveesi",
"readme_p2": "Tämä padi toimii pikaisena perehdytyksenä CryptPadin ominaisuuksiin - kuinka tehdä muistiinpanoja, järjestellä niitä ja tehdä yhteistyötä niiden parissa."
}

View File

@ -349,7 +349,7 @@
"fm_info_recent": "Ces pads ont été récemment ouverts ou modifiés par vous ou vos collaborateurs.",
"fm_info_trash": "Vider la corbeille permet de libérer de l'espace dans votre CryptDrive.",
"fm_info_allFiles": "Contient tous les fichiers de \"Documents\", \"Fichiers non triés\" et \"Corbeille\". Vous ne pouvez pas supprimer ou déplacer des fichiers depuis cet endroit.",
"fm_info_anonymous": "Vous n'êtes pas connecté, ces pads seront donc supprimés après 3 mois d'inactivité (<a href=\"https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/\" target=\"_blank\">découvrez pourquoi</a>). Ils sont stockés dans votre navigateur donc nettoyer votre historique peut les faire disparaître.<br><a href=\"/register/\">Inscrivez-vous</a> ou <a href=\"/login/\">connectez-vous</a> pour les maintenir en vie.<br>",
"fm_info_anonymous": "Vous n'êtes pas connecté, ces documents seront donc supprimés après {0} jours. Nettoyer l'historique de votre navigateur peut les faire disparaître.<br><a href=\"/register/\">Enregistrez vous</a> (aucune information personelle requise) ou <a href=\"/login/\">Connectez vous</a> pour les stocker de manière permanente dans votre drive. <a href=\"https://docs.cryptpad.fr/fr/user_guide/user_account.html#account-types\" target=\"_blank\">En lire plus sur les comptes utilisateurs</a>.",
"fm_info_sharedFolder": "Voici le contenu d'un dossier partagé. Il n'est accessible qu'en lecture seule car vous n'êtes pas connecté.<br><a href=\"/register/\">Inscrivez-vous</a> ou <a href=\"/login/\">connectez-vous</a> pour pouvoir l'importer dans votre CryptDrive et le modifier.",
"fm_info_owned": "Vous êtes propriétaire des pads affichés dans cette catégorie. Cela signifie que vous pouvez choisir de les supprimer définitivement du serveur à n'importe quel moment. Ils seront alors inaccessibles pour tous les autres utilisateurs.",
"fm_alert_backupUrl": "Lien de secours pour ce CryptDrive.<br>Il est <strong>fortement recommandé</strong> de garder ce lien pour vous-même.<br>Il vous servira en cas de perte des données de votre navigateur afin de retrouver vos fichiers.<br>Quiconque se trouve en possession de celui-ci peut modifier ou supprimer tous les fichiers de ce gestionnaire.<br>",
@ -1293,7 +1293,7 @@
"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",
"safeLinks_error": "Ce lien a été copié depuis la barre d'addresse du navigateur et ne donne pas accès au document. Veuillez utiliser le menu <i class=\"fa fa-shhare-alt\"></i> <b>Partager</b> pour partager avec vos contacts ou copier un lien. <a href=\"https://docs.cryptpad.fr/fr/user_guide/user_account.html#confidentiality\"> En savoir plus sur les Liens Sécurisés</a>.\n",
"settings_safeLinksCheckbox": "Activer les liens sécurisés",
"settings_safeLinksTitle": "Liens Sécurisés",
"settings_cat_security": "Confidentialité",

View File

@ -356,7 +356,7 @@
"fm_info_recent": "These pads have recently been opened or modified by you or people you collaborate with.",
"fm_info_trash": "Empty your trash to free space in your CryptDrive.",
"fm_info_allFiles": "Contains all the files from \"Documents\", \"Unsorted\" and \"Trash\". You can't move or remove files from here.",
"fm_info_anonymous": "You are not logged in so your pads will expire after 3 months (<a href=\"https://blog.cryptpad.fr/2017/05/17/You-gotta-log-in/\" target=\"_blank\">find out more</a>). They are stored in your browser so clearing history may make them disappear.<br><a href=\"/register/\">Sign up</a> or <a href=\"/login/\">Log in</a> to keep them alive.<br>",
"fm_info_anonymous": "You are not logged in so your documents will expire after {0} days. Clearing your browser's history may make them disappear.<br><a href=\"/register/\">Sign up</a> (no personal information required) or <a href=\"/login/\">Log in</a> to store them in your drive indefinitely. <a href=\"https://docs.cryptpad.fr/en/user_guide/user_account.html#account-types\" target=\"_blank\">Read more about registered accounts</a>.",
"fm_info_sharedFolder": "This is a shared folder. You're not logged in so you can only access it in read-only mode.<br><a href=\"/register/\">Sign up</a> or <a href=\"/login/\">Log in</a> to be able to import it to your CryptDrive and to modify it.",
"fm_info_owned": "You are the owner of the pads displayed here. This means you can remove them permanently from the server whenever you want. If you do so, other users won't be able to access them anymore.",
"fm_alert_backupUrl": "Backup link for this drive.<br>It is <strong>highly recommended</strong> that you keep it secret.<br>You can use it to retrieve all your files in case your browser memory got erased.<br>Anybody with that link can edit or remove all the files in your file manager.<br>",
@ -1293,7 +1293,7 @@
"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",
"safeLinks_error": "This link was copied from the browser's address bar and does not provide access to the document. Please use the <i class=\"fa fa-shhare-alt\"></i> <b>Share</b> menu to share directly with contacts or copy the link. <a href=\"https://docs.cryptpad.fr/en/user_guide/user_account.html#confidentiality\"> Read more about the Safe Links feature</a>.\n",
"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.",

View File

@ -373,14 +373,14 @@
"fm_burnThisDriveButton": "Удалить всю информацию, хранящуюся от CryptPad в браузере",
"fm_tags_used": "Количество использований",
"fm_restoreDrive": "Восстановление прежнего состояния диска. Для достижения наилучших результатов не вносите изменения в диск, пока этот процесс не будет завершен.",
"fm_passwordProtected": "Этот документ защищен паролем",
"fm_passwordProtected": "Защищено паролем",
"fc_newfolder": "Новая папка",
"fc_newsharedfolder": "Новая общая папка",
"fc_rename": "Переименовать",
"fc_open": "Открыть",
"fc_open_ro": "Отркыть (режим чтения)",
"fc_delete": "Переместить в корзину",
"fc_delete_owned": "Удалить с сервера",
"fc_delete_owned": "Уничтожить",
"fc_restore": "Восстановить",
"fc_remove_sharedfolder": "Удалить",
"fc_empty": "Удалить корзину",
@ -392,7 +392,7 @@
"fc_color": "Изменить цвет",
"fc_expandAll": "Расширить все",
"fc_collapseAll": "Скрыть все",
"fc_remove": "Удалить из вашего CryptDrive",
"fc_remove": "Удалить",
"fo_moveUnsortedError": "Вы не можете переместить папку в список черновиков",
"fo_existingNameError": "Это имя уже используется в данной директории. Пожалуйста выберите другое.",
"fo_unableToRestore": "Невозможно восстановить этот файл в исходное местоположение. Вы можете попытаться переместить его в другое место.",
@ -540,5 +540,99 @@
"fm_morePads": "Ещё",
"uploadFolderButton": "Загрузить папку",
"storageStatus": "Хранилище:<br /><b>{0}</b> использовано из <b>{1}</b>",
"padNotPinnedVariable": "Этот документ исчезнет через {4} дней неактивности, {0}войдите{1} или {2}зарегистируйтесь{3} чтобы сохранить его."
"padNotPinnedVariable": "Этот документ исчезнет через {4} дней неактивности, {0}войдите{1} или {2}зарегистируйтесь{3} чтобы сохранить его.",
"creation_propertiesTitle": "Доступность",
"creation_passwordValue": "Пароль",
"creation_owners": "Владельцы",
"creation_create": "Создать",
"creation_expireMonths": "Месяцев",
"creation_expireDays": "Дней",
"creation_expireHours": "Часов",
"creation_expireFalse": "Неограниченно",
"view": "показать",
"edit": "редактировать",
"faq": {
"usability": {
"title": "Удобство использования"
},
"security": {
"title": "Безопасность"
},
"privacy": {
"title": "Конфиденциальность"
}
},
"settings_ownDriveTitle": "Обновить аккаунт",
"settings_templateSkipHint": "При создании нового документа, если для этого типа документов были сохранены шаблоны, вам будет предложено их использовать. Вы можете выбрать никогда не показывать окно с этим предложением, а значит никогда не использовать шаблоны.",
"settings_templateSkip": "Пропускать экран выбора шаблонов",
"settings_creationSkipFalse": "Отображать",
"settings_creationSkipHint": "Экран создания документов предлагает новые опции, давая вам больше контроля над вашими данными. Однако, он может замедлить вашу работу, добавив дополнительный шаг, так что вы можете пропустить этот экран, согласившись на выбранные выше настройки по умолчанию.",
"settings_padSpellcheckLabel": "Включить проверку написания",
"settings_padSpellcheckHint": "Эта опция включает проверку написания. Ошибки будут подчёркнуты красным, нажмите на них правой кнопкой мыши, удерживая клавишу Ctrl или Meta для просмотра исправленных вариантов.",
"settings_padWidthHint": "Переключайтесь между режимом страницы (по умолчанию) с ограниченной шириной редактора и использованием полной ширины экрана.",
"settings_codeIndentation": "Отступы редактора кода (пробелы)",
"settings_driveDuplicateHint": "Когда вы помещаете присвоенные документы в общую папку, копия сохраняется в вашем хранилище, чтобы удостовериться, что вы все еще имеете над ними контроль. Вы можете скрыть продублированные файлы. Будет отображена только общая версия документа, если она не удалена, в этом случае будет отображена оригинальная версия в своей последней локации.",
"settings_driveDuplicateTitle": "Продублированы присвоенные документы",
"settings_logoutEverywhereConfirm": "Вы уверены? Вам придётся войти в аккаунт на всех устройствах.",
"settings_autostoreHint": "<b>Автоматически</b> Все открытые вами документы будут сохранены в вашем хранилище.<br><b>Вручную (всегда спрашивать)</b> Если вы еще не сохраняли документов, вам будет предложено сохранить документ в хранилище.<br><b>Вручную (никогда не спрашивать)</b> Документы не будут сохраняться в вашем хранилище. Кнопка для их сохранения будет скрыта.",
"register_emailWarning3": "Нажмите OK, если осознанно хотите использовать свою почту в качестве имени пользователя.",
"register_emailWarning2": "Восстановить ваш пароль с помощью почты, как на других сайтах, не получится.",
"register_emailWarning1": "Вы можете сделать это, если хочется, но на наш сервер это отправлено не будет.",
"register_emailWarning0": "Похоже, что ваша почта и имя пользователя совпадают.",
"register_explanation": "<h3>Проясним несколько вещей:</h3><ul class='list-unstyled'><li><i class='fa fa-info-circle'> </i>Ваш пароль - это ваш секретный ключ, шифрующий всеваши документы. Если вы его забудете, восстановить ваши данные будет невозможно.</li><li><i class='fa fa-info-circle'> </i> Вы можете импортировать недавно просмотренные в браузере документы в ваш аккаунт.</li><li><i class='fa fa-info-circle'> </i> Если вы пользуетесь общим компьютером: по завершении работы необходимо выйти из системы, закрытия вкладки в браузере недостаточно.</li></ul>",
"password_submit": "Отправить",
"team_pendingOwner": "(ожидание)",
"team_deleteButton": "Удалить",
"team_pending": "Приглашённые",
"team_members": "Участники",
"team_admins": "Администраторы",
"team_owner": "Владельцы",
"team_rosterDemote": "Понизить",
"team_rosterPromote": "Повысить",
"team_listLoad": "Открыть",
"team_cat_admin": "Администрация",
"team_cat_drive": "Хранилище",
"team_cat_chat": "Чат",
"team_cat_members": "Участники",
"team_cat_create": "Создать",
"team_cat_list": "Команды",
"team_cat_general": "Сведения",
"team_inviteModalButton": "Пригласить",
"owner_unknownUser": "неизвестный",
"owner_removePendingText": "Ожидает",
"owner_removeText": "Владельцы",
"pricing": "Цены",
"notifications_cat_archived": "История",
"notifications_cat_all": "Все",
"notificationsPage": "Уведомления",
"support_answer": "Ответить",
"support_formButton": "Отправить",
"admin_cat_support": "Поддержка",
"supportPage": "Поддержка",
"notifications_dismiss": "Скрыть",
"share_withFriends": "Поделиться",
"friendRequest_decline": "Отклонить",
"settings_codeSpellcheckTitle": "Проверка правописания",
"contact_email": "Эл. почта",
"contact_chat": "Чат",
"footer_tos": "Условия использования",
"footer_legal": "Юридическая информация",
"footer_donate": "Пожертвовать",
"footer_product": "Продукт",
"adminPage": "Администрация",
"admin_cat_stats": "Статистика",
"admin_cat_general": "Общее",
"autostore_store": "Хранить",
"autostore_pad": "документ",
"autostore_sf": "папка",
"autostore_file": "файл",
"share_embedCategory": "Вставить",
"share_linkCopy": "Копировать",
"share_linkOpen": "Предпросмотр",
"share_linkPresent": "Текущий",
"share_linkView": "Посмотреть",
"share_linkEdit": "Изменить",
"share_linkCategory": "Ссылка",
"properties_changePasswordButton": "Отправить",
"password_show": "Показать"
}

View File

@ -9,7 +9,11 @@
"whiteboard": "白板",
"file": "檔案",
"media": "多媒體",
"kanban": "任务看板"
"kanban": "任务看板",
"todo": "待办事项",
"teams": "团队",
"sheet": "工作表",
"contacts": "联系我们"
},
"button_newpad": "富文件檔案",
"button_newcode": "新代碼檔案",
@ -25,7 +29,7 @@
"error": "錯誤",
"saved": "儲存",
"synced": "所有資料已儲存好了",
"deleted": "自 CryptDrive 刪除案",
"deleted": "自 CryptDrive 刪除案",
"disconnected": "已斷線",
"synchronizing": "同步中",
"reconnecting": "重新連結...",
@ -335,5 +339,32 @@
},
"feedback_about": "如果你讀了這裏,也許會好奇為何當你執行某些動作時 CryptPad 會請求網頁資訊。",
"feedback_privacy": "我們注重你的隱私,同時也要讓 CryptPad 容易使用。我們利用這個檔案來了解哪一種介面設計為用戶所重視,透過它來請求特別的功能參數。",
"feedback_optout": "如果欲退出客戶資料收集, 請到 <a href='/settings/'>用戶設定頁</a>, 可以找到勾選項目來啟用或關閉用戶回饋功能。"
"feedback_optout": "如果欲退出客戶資料收集, 請到 <a href='/settings/'>用戶設定頁</a>, 可以找到勾選項目來啟用或關閉用戶回饋功能。",
"settings_cat_kanban": "看板",
"themeButton": "主题",
"languageButton": "语言",
"slideOptionsText": "选项",
"or": "或者",
"filePicker_close": "关闭",
"printText": "打印",
"propertiesButton": "属性",
"chatButton": "聊天",
"forgetButton": "删除",
"exportButton": "导出",
"importButton": "导入",
"forgotten": "移到垃圾箱",
"initializing": "初始化中",
"typing": "编辑中",
"profile_editDescription": "编辑描述",
"profile_addDescription": "添加描述",
"poll_comment_submit": "发送",
"poll_comment_list": "评论",
"poll_edit": "编辑",
"poll_remove": "删除",
"kanban_done": "完成",
"pad_mediatagPreview": "预览",
"historyText": "历史",
"help_button": "帮助",
"button_newsheet": "新工作表",
"button_newkanban": "新看板"
}

View File

@ -45,32 +45,33 @@ define([
// Share modal
create['share'] = function (data) {
var priv = metadataMgr.getPrivateData();
var f = (data && data.file) ? UIElements.createFileShareModal
: UIElements.createShareModal;
var friends = common.getFriends();
var _modal;
var modal = f({
origin: priv.origin,
pathname: priv.pathname,
password: priv.password,
isTemplate: priv.isTemplate,
hashes: priv.hashes,
common: common,
title: data.title,
friends: friends,
onClose: function () {
if (_modal && _modal.close) { _modal.close(); }
hideIframe();
},
fileData: {
hash: priv.hashes.fileHash,
password: priv.password
}
require(['/common/inner/share.js'], function (Share) {
var f = (data && data.file) ? Share.getFileShareModal
: Share.getShareModal;
f(common, {
origin: priv.origin,
pathname: priv.pathname,
password: priv.password,
isTemplate: priv.isTemplate,
hashes: priv.hashes,
common: common,
title: data.title,
versionHash: data.versionHash,
friends: friends,
onClose: function () {
hideIframe();
},
fileData: {
hash: priv.hashes.fileHash,
password: priv.password
}
}, function (e, modal) {
if (e) { console.error(e); }
displayed = modal;
});
});
_modal = UI.openCustomModal(modal);
displayed = modal;
};
// Properties modal