Compare commits

..

41 commits
load ... main

Author SHA1 Message Date
mathilde-cryptpad
a46efaafa8 upgrade docker hub image version to 2024.6.1 2024-08-02 15:13:48 +02:00
David Benque
31a30e2bf9
Bump more version numbers 2024-07-25 14:46:06 +01:00
David Benque
e4529d41b4
Add changelog for 2024.6.1 2024-07-25 14:09:21 +01:00
David Benque
f46d76decb Update version strings 2024-07-25 13:54:11 +01:00
yflory
eec885f365 Fix cancel button in UI.confirm #1576 2024-07-24 14:14:33 +02:00
yflory
bb73e171ab Enable Arabic, Swedish and Cuban Spanish languages 2024-07-24 12:08:52 +02:00
David Benque
92674084cf
Merge pull request #1544 from cryptpad/weblate
2024.6.1 translations
2024-07-24 10:53:30 +01:00
David Benque
b6f9cda20d
Merge pull request #1575 from cryptpad-team/weblate-cryptpad-app
Translations update from CryptPad Translations
2024-07-24 10:48:21 +01:00
Weblate
e37ae3b8d3 Translated using Weblate (Indonesian)
Currently translated at 100.0% (1780 of 1780 strings)

Co-authored-by: Linerly <linerly@proton.me>
Translate-URL: https://weblate.cryptpad.org/projects/cryptpad/app/id/
Translation: CryptPad/App
2024-07-24 11:35:25 +02:00
Weblate
3bcc16220b Translated using Weblate (French)
Currently translated at 100.0% (1780 of 1780 strings)

Co-authored-by: David Benqué <david.benque@xwiki.com>
Translate-URL: https://weblate.cryptpad.org/projects/cryptpad/app/fr/
Translation: CryptPad/App
2024-07-24 11:35:25 +02:00
Weblate
4583351a95 Translated using Weblate (Spanish (Cuba))
Currently translated at 98.6% (1756 of 1780 strings)

Translated using Weblate (Spanish (Cuba))

Currently translated at 98.7% (1758 of 1780 strings)

Translated using Weblate (Spanish (Cuba))

Currently translated at 98.7% (1758 of 1780 strings)

Co-authored-by: Desire Hernandez <dhern031@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://weblate.cryptpad.org/projects/cryptpad/app/es_CU/
Translation: CryptPad/App
2024-07-24 11:35:25 +02:00
Weblate
8c39136fae Translated using Weblate (Swedish)
Currently translated at 94.3% (1680 of 1780 strings)

Translated using Weblate (Swedish)

Currently translated at 93.5% (1665 of 1780 strings)

Co-authored-by: Helle <helle.berglund@protonmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://weblate.cryptpad.org/projects/cryptpad/app/sv/
Translation: CryptPad/App
2024-07-24 11:35:25 +02:00
Weblate
5905f37fc6 Translated using Weblate (German)
Currently translated at 100.0% (1780 of 1780 strings)

Co-authored-by: Jan <janrei.g@googlemail.com>
Translate-URL: https://weblate.cryptpad.org/projects/cryptpad/app/de/
Translation: CryptPad/App
2024-07-24 11:35:25 +02:00
Weblate
fa097a4377 Translated using Weblate (English)
Currently translated at 100.0% (1780 of 1780 strings)

Co-authored-by: David Benqué <david.benque@xwiki.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://weblate.cryptpad.org/projects/cryptpad/app/en/
Translation: CryptPad/App
2024-07-24 11:35:25 +02:00
yflory
b2c37beae8
Merge pull request #1573 from cryptpad/fix-oo-npe
Fix broken OnlyOffice Document
2024-07-24 10:01:40 +02:00
Wolfgang Ginolas
9823fd564e Fix broken OnlyOffice Document
https://github.com/cryptpad/cryptpad/issues/1572
2024-07-23 16:31:49 +02:00
yflory
2963ca3002 lint compliance 2024-07-23 13:06:36 +02:00
yflory
edef920363 Merge branch '1487-print-gives-an-empty-sheet' into 2024.6.1 2024-07-23 10:37:34 +02:00
yflory
4544be6b4d Partial revert of fd77428 when counting registered users 2024-07-23 10:36:46 +02:00
mathilde-cryptpad
ce3db73219
Merge pull request #1562 from cryptpad-team/weblate-cryptpad-app
Translations update from CryptPad Translations
2024-07-15 08:41:37 +02:00
Weblate
f3c7b7f0bc Translated using Weblate (Spanish (Cuba))
Currently translated at 98.7% (1758 of 1780 strings)

Translated using Weblate (Spanish (Cuba))

Currently translated at 87.5% (1558 of 1780 strings)

Translated using Weblate (Spanish (Cuba))

Currently translated at 84.2% (1500 of 1780 strings)

Translated using Weblate (Spanish (Cuba))

Currently translated at 84.2% (1499 of 1780 strings)

Translated using Weblate (Spanish (Cuba))

Currently translated at 79.6% (1417 of 1780 strings)

Translated using Weblate (Spanish (Cuba))

Currently translated at 67.3% (1199 of 1780 strings)

Translated using Weblate (Spanish (Cuba))

Currently translated at 67.3% (1199 of 1780 strings)

Co-authored-by: Desire Hernandez <dhern031@gmail.com>
Co-authored-by: Raul Gonzalez <Raulitopatry@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://weblate.cryptpad.org/projects/cryptpad/app/es_CU/
Translation: CryptPad/App
2024-07-15 07:43:38 +02:00
yflory
13a82303e0 Make background color white while printing 2024-07-12 11:29:13 +02:00
DianaXWiki
848387edcc Fix overlapping variable names 2024-07-11 14:49:15 +03:00
mathilde-cryptpad
5e83049c8e
Merge pull request #1545 from cryptpad-team/weblate-cryptpad-app
Translations update from CryptPad Translations
2024-07-11 07:55:18 +02:00
Weblate
bc77d140ec Translated using Weblate (French)
Currently translated at 100.0% (1780 of 1780 strings)

Co-authored-by: Mathilde <mathilde@users.noreply.weblate.cryptpad.org>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://weblate.cryptpad.org/projects/cryptpad/app/fr/
Translation: CryptPad/App
2024-07-11 07:53:41 +02:00
Weblate
c60b14b466 Translated using Weblate (Bulgarian)
Currently translated at 25.0% (446 of 1780 strings)

Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Мария Рангелова <rangelova_1186@abv.bg>
Translate-URL: https://weblate.cryptpad.org/projects/cryptpad/app/bg/
Translation: CryptPad/App
2024-07-11 07:53:41 +02:00
Weblate
f363b5be07 Translated using Weblate (Italian)
Currently translated at 97.8% (1741 of 1780 strings)

Co-authored-by: Viki Halwick <vikihalwick@users.noreply.weblate.cryptpad.org>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://weblate.cryptpad.org/projects/cryptpad/app/it/
Translation: CryptPad/App
2024-07-11 07:53:41 +02:00
Weblate
b0db6ce939 Translated using Weblate (Spanish (Cuba))
Currently translated at 63.3% (1128 of 1780 strings)

Translated using Weblate (Spanish (Cuba))

Currently translated at 52.7% (939 of 1780 strings)

Translated using Weblate (Spanish (Cuba))

Currently translated at 37.7% (672 of 1780 strings)

Translated using Weblate (Spanish (Cuba))

Currently translated at 27.0% (481 of 1780 strings)

Translated using Weblate (Spanish (Cuba))

Currently translated at 20.8% (372 of 1780 strings)

Co-authored-by: Raul Gonzalez <Raulitopatry@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://weblate.cryptpad.org/projects/cryptpad/app/es_CU/
Translation: CryptPad/App
2024-07-11 07:53:41 +02:00
Weblate
174b7aff5d Translated using Weblate (Spanish)
Currently translated at 86.7% (1545 of 1780 strings)

Co-authored-by: Mirel Gonzalez <arong7@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://weblate.cryptpad.org/projects/cryptpad/app/es/
Translation: CryptPad/App
2024-07-11 07:53:41 +02:00
yflory
072dba254e Fix calendar refresh issue #1551 2024-07-10 14:52:00 +02:00
Wolfgang Ginolas
6638c4cc49 Handle ids of OO read only users correctly 2024-07-08 14:30:52 +00:00
Wolfgang Ginolas
8d0c411a38 Do not send OO changes when in read only mode
https://github.com/cryptpad/cryptpad/issues/1364
2024-07-08 14:30:52 +00:00
mathilde-cryptpad
3863888fd3 add a forgotten entry in the changelog 2024-07-08 08:50:41 +02:00
Wolfgang Ginolas
fd98c2d143 Merge branch 'jdownie/main' into staging 2024-07-05 13:01:44 +02:00
mathilde-cryptpad
867295b07d
Merge pull request #1542 from cryptpad-team/weblate-cryptpad-app
Translations update from CryptPad Translations
2024-07-04 11:39:52 +02:00
Weblate
34eee624ed Translated using Weblate (Spanish (Cuba))
Currently translated at 4.4% (79 of 1780 strings)

Translated using Weblate (Spanish (Cuba))

Currently translated at 4.4% (79 of 1780 strings)

Co-authored-by: Mirel Gonzalez <arong7@gmail.com>
Co-authored-by: Raul Gonzalez <Raulitopatry@gmail.com>
Translate-URL: https://weblate.cryptpad.org/projects/cryptpad/app/es_CU/
Translation: CryptPad/App
2024-07-04 10:34:48 +02:00
James Downie
b1e30221ce Removed a superfluous apt install statement. 2024-07-02 19:13:30 +10:00
James Downie
2d2855679d Added new install-onlyoffice.sh switch to docker-entrypoint.sh 2024-07-02 09:48:39 +10:00
James Downie
2ec3dd909a Parameterising the git config safe.directory feature. 2024-07-02 09:38:51 +10:00
James Downie
e2c9b66fe9 Added a few packages to Dockerfile that are handy for troubleshooting. 2024-06-29 20:36:35 +10:00
James Downie
9b3cfce766 Setting the cloned bare repository to safe.directory. 2024-06-29 20:15:47 +10:00
41 changed files with 2124 additions and 1057 deletions

View file

@ -89,6 +89,7 @@ body:
label: Version
description: What version of CryptPad are you running?
options:
- 2024.6.1
- 2024.6.0
- 2024.3.1
- 2024.3.0

View file

@ -4,6 +4,51 @@ SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and cont
SPDX-License-Identifier: AGPL-3.0-or-later
-->
# 2024.6.1
## Goals
This is a bugfix release to address issues that were reported by Cryptpad.fr users. We took the opportunity to update the translations with some new languages contributed by the community.
## Improvements
- Translations update from CryptPad Translations [#1575](https://github.com/cryptpad/cryptpad/pull/1575)
- Added: Español cubano, اَلْعَرَبِيَّةُ Arabic, Svenska
- Removed some languages without enough coverage
- Greek (16%)
- Romanian (36%)
## Fixes
- Calendar events sometimes dont appear when created [#1551](https://github.com/cryptpad/cryptpad/issues/1551) fixed by [072dba2](https://github.com/cryptpad/cryptpad/commit/072dba254e3c2be32cd6b261d84510909deb713f)
- Revert the new method of counting registered users in the admin panel [4544be6](https://github.com/cryptpad/cryptpad/commit/4544be6b4d9fa7291b19cb366f7dd492dfe07340)
- Fix broken OnlyOffice Document [#1572](https://github.com/cryptpad/cryptpad/issues/1572)
- Fix printing in Code documents [#1557](https://github.com/cryptpad/cryptpad/pull/1557) [#1478](https://github.com/cryptpad/cryptpad/pull/1478)
- Fix OnlyOffice undefined functions [#1550](https://github.com/cryptpad/cryptpad/pull/1550)
- Fix keyboard operation of confirm modals [#1576](https://github.com/cryptpad/cryptpad/issues/1576)
- Pressing Enter on the "Cancel" button triggered the "OK" button instead
## Upgrade notes
If you are upgrading from a version older than `2024.6.0` please read the upgrade notes of all versions between yours and `2024.6.1` to avoid configuration issues.
To upgrade:
1. Stop your server
2. Get the latest code with git
```bash
git fetch origin --tags
git checkout 2024.6.1
npm ci
npm run install:components
./install-onlyoffice.sh
```
3. Restart your server
4. Review your instance's checkup page to ensure that you are passing all tests
# 2024.6.0
## Goals
@ -35,6 +80,7 @@ This release introduces a new onboarding flow to guide administrators through th
- Remove x2t from the CryptPad repo [#1454](https://github.com/cryptpad/cryptpad/issues/1454)
- Other OnlyOffice users are shown as "Guest" [#1446](https://github.com/cryptpad/cryptpad/issues/1446)
- Document PDF exports are empty when remote embedding is disabled [#1472](https://github.com/cryptpad/cryptpad/issues/1472)
- Sometimes images of a presentation are not exported to PDF [#1500](https://github.com/cryptpad/cryptpad/issues/1500)
- Automatic upgrade of an OnlyOffice document fails sometimes [#1534](https://github.com/cryptpad/cryptpad/issues/1534)
- Import/Export is broken [#1532](https://github.com/cryptpad/cryptpad/issues/1532)
- Print is broken [#1533](https://github.com/cryptpad/cryptpad/issues/1533)

View file

@ -5,11 +5,13 @@
(function () {
// add your module to this map so it gets used
var map = {
'ar': 'اَلْعَرَبِيَّةُ',
'ca': 'Català',
'cs': 'Čeština',
'de': 'Deutsch',
'el': 'Ελληνικά',
//'el': 'Ελληνικά',
'es': 'Español',
'es_CU': 'Español cubano',
'eu': 'Euskara',
'fi': 'Suomi',
'fr': 'Français',
@ -22,9 +24,9 @@ var map = {
'pl': 'Polski',
'pt-br': 'Português do Brasil',
'pt-pt': 'Português do Portugal',
'ro': 'Română',
//'ro': 'Română',
'ru': 'Русский',
//'sv': 'Svenska',
'sv': 'Svenska',
//'te': 'తెలుగు',
'uk': 'Українська',
'zh': '中文(簡體)',

View file

@ -1,44 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and contributors
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
@import (reference) "../include/infopages.less";
@import (reference) "../include/colortheme-all.less";
@import (reference) "../include/alertify.less";
@import (reference) "../include/checkmark.less";
@import (reference) "../include/forms.less";
&.cp-page-load {
.infopages_main();
.forms_main();
.alertify_main();
.checkmark_main(20px);
.form {
max-width: 400px;
padding: 20px;
}
.alertify {
// workaround for alertify making empty p
p:empty {
display: none;
}
nav {
display: flex;
align-items: center;
justify-content: flex-end;
}
@media screen and (max-width: 600px) {
nav .btn-danger {
line-height: inherit;
}
}
}
}

View file

@ -8,3 +8,8 @@
margin: 3cm;
size: A4 portrait;
}
@media print {
body {
background: white !important;
}
}

View file

@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and contributors
//
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
* You can override the translation text using this file.
* The recommended method is to make a copy of this file (/customize.dist/translations/messages.{LANG}.js)
in a 'customize' directory (/customize/translations/messages.{LANG}.js).
* If you want to check all the existing translation keys, you can open the internal language file
but you should not change it directly (/common/translations/messages.{LANG}.js)
*/
define(['/common/translations/messages.ar.js'], function (Messages) {
// Replace the existing keys in your copied file here:
// Messages.button_newpad = "New Rich Text Document";
return Messages;
});

View file

@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and contributors
//
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
* You can override the translation text using this file.
* The recommended method is to make a copy of this file (/customize.dist/translations/messages.{LANG}.js)
in a 'customize' directory (/customize/translations/messages.{LANG}.js).
* If you want to check all the existing translation keys, you can open the internal language file
but you should not change it directly (/common/translations/messages.{LANG}.js)
*/
define(['/common/translations/messages.es_CU.js'], function (Messages) {
// Replace the existing keys in your copied file here:
// Messages.button_newpad = "New Rich Text Document";
return Messages;
});

View file

@ -5,7 +5,7 @@
---
services:
cryptpad:
image: "cryptpad/cryptpad:version-2024.6.0"
image: "cryptpad/cryptpad:version-2024.6.1"
hostname: cryptpad
environment:

View file

@ -31,7 +31,7 @@ fi
cd $CPAD_HOME
if [ "$CPAD_INSTALL_ONLYOFFICE" == "yes" ]; then
./install-onlyoffice.sh --accept-license
./install-onlyoffice.sh --accept-license --trust-repository
fi
npm run build

View file

@ -33,7 +33,7 @@ main() {
install_version v4 6ebc6938
install_version v5 88a356f0
install_version v6 abd8a309
install_version v7 ba82142f
install_version v7 e1267803
install_x2t v7.3+1 ab0c05b0e4c81071acea83f0c6a8e75f5870c360ec4abc4af09105dd9b52264af9711ec0b7020e87095193ac9b6e20305e446f2321a541f743626a598e5318c1
rm -rf "$BUILDS_DIR"
@ -69,6 +69,10 @@ parse_arguments() {
ACCEPT_LICENSE="1"
shift
;;
-t | --trust-repository)
TRUST_REPOSITORY="1"
shift
;;
*)
show_help
shift
@ -110,17 +114,26 @@ OPTIONS:
Accept the license of OnlyOffice and do not ask when running this
script. Read and accept this before using this option:
https://github.com/ONLYOFFICE/web-apps/blob/master/LICENSE.txt
-t, --trust-repository
Automatically configure the cloned onlyoffice-builds repository
as a safe.directory.
https://git-scm.com/docs/git-config/#Documentation/git-config.txt-safedirectory
EOF
exit 1
}
ensure_oo_is_downloaded() {
ensure_command_available git
ensure_command_available git
if ! [ -d "$BUILDS_DIR" ]; then
echo "Downloading OnlyOffice..."
git clone --bare https://github.com/cryptpad/onlyoffice-builds.git "$BUILDS_DIR"
fi
if ! [ -d "$BUILDS_DIR" ]; then
echo "Downloading OnlyOffice..."
git clone --bare https://github.com/cryptpad/onlyoffice-builds.git "$BUILDS_DIR"
fi
if [ ${TRUST_REPOSITORY+x} ] || [ "${PROPS[trust_repository]:-no}" == yes ]; then
git config --global --add safe.directory /cryptpad/onlyoffice-conf/onlyoffice-builds.git
fi
}
install_version() {

View file

@ -108,7 +108,7 @@ nThen(function (w) {
};
// spawn ws server and attach netflux event handlers
let Server = Env.Server = NetfluxSrv.create(new WebSocketServer({ server: Env.httpServer}))
let Server = NetfluxSrv.create(new WebSocketServer({ server: Env.httpServer}))
.on('channelClose', historyKeeper.channelClose)
.on('channelMessage', historyKeeper.channelMessage)
.on('channelOpen', historyKeeper.channelOpen)

View file

@ -104,10 +104,8 @@ var shutdown = function (Env, Server, cb) {
var getRegisteredUsers = Admin.getRegisteredUsers = function (Env, Server, cb) {
Env.batchRegisteredUsers('', cb, function (done) {
var dir = Env.paths.pin;
var dirB = Env.paths.block;
var folders, foldersB;
var folders;
var users = 0;
var blocks = 0;
nThen(function (waitFor) {
Fs.readdir(dir, waitFor(function (err, list) {
if (err) {
@ -116,13 +114,6 @@ var getRegisteredUsers = Admin.getRegisteredUsers = function (Env, Server, cb) {
}
folders = list;
}));
Fs.readdir(dirB, waitFor(function (err, list) {
if (err) {
waitFor.abort();
return void done(err);
}
foldersB = list;
}));
}).nThen(function (waitFor) {
folders.forEach(function (f) {
var dir = Env.paths.pin + '/' + f;
@ -135,20 +126,8 @@ var getRegisteredUsers = Admin.getRegisteredUsers = function (Env, Server, cb) {
users += list.length;
}));
});
}).nThen(function (waitFor) {
foldersB.forEach(function (f) {
var dir = Env.paths.block + '/' + f;
Fs.readdir(dir, waitFor(function (err, list) {
if (err) { return; }
// Don't count placeholders
list = list.filter(name => {
return !/\.placeholder$/.test(name);
});
blocks += list.length;
}));
});
}).nThen(function () {
done(void 0, {users, blocks});
done(void 0, {users});
});
});
};

View file

@ -161,13 +161,11 @@ var queryAccountServer = function (Env, cb) {
nThen(waitFor => {
Admin.getRegisteredUsers(Env, null, waitFor((err, data) => {
if (err) { return; }
stats.registered = data.blocks;
stats.registered = data.users;
if (Env.lastPingRegisteredUsers) {
stats.usersDiff = stats.registered - Env.lastPingRegisteredUsers;
}
Env.lastPingRegisteredUsers = stats.registered;
let teams = (data.users - data.blocks);
if (teams > 0) { stats.teams = teams; }
}));
}).nThen(() => {
if (Env.maxConcurrentWs) {

View file

@ -1,24 +0,0 @@
const Nacl = require('tweetnacl/nacl-fast');
const CPCrypto = module.exports;
const plugins = require('./plugin-manager');
CPCrypto.init = (cb) => {
const crypto = {};
crypto.open = (signedMsg, validateKey) => {
return Nacl.sign.open(signedMsg, validateKey);
};
crypto.detachedVerify = (signedBuffer, signatureBuffer, validateKey) => {
return Nacl.sign.detached.verify(signedBuffer, signatureBuffer, pubBuffer);
};
if (plugins.SODIUM && plugins.SODIUM.crypto) {
let c = plugins.SODIUM.crypto;
if (c.open) { crypto.open = c.open; }
if (c.detachedVerify) { crypto.detachedVerify = c.detachedVerify; }
}
// Make async because we might need it later with libsodium's promise
// libsodium.ready.then(() => {});
setTimeout(() => {
cb(void 0, crypto);
});
};

View file

@ -252,7 +252,6 @@ module.exports.create = function (config) {
curvePublic: Nacl.util.encodeBase64(curve.publicKey),
selfDestructTo: {},
monitoring: {}
};
(function () {
@ -416,7 +415,6 @@ const BAD = [
'limits',
'customLimits',
'scheduleDecree',
'monitoring',
'httpServer',

View file

@ -19,9 +19,6 @@ const BlobStore = require("./storage/blob");
const BlockStore = require("./storage/block");
const plugins = require("./plugin-manager");
const Prometheus = require('prom-client');
const Monitoring = require('./monitoring');
const DEFAULT_QUERY_TIMEOUT = 5000;
const PID = process.pid;
@ -69,102 +66,6 @@ Env.incrementBytesWritten = function () {};
const EVENTS = {};
// XXX Store in monitoring.js
const rssMetric = new Prometheus.Gauge({
name: `memory_rss`,
help: 'The amount of space occupied in the main memory device for the process.',
labelNames: ['pid', 'type']
});
const heapTotalMetric = new Prometheus.Gauge({
name: `memory_heap_total`,
help: "Total heap memory.",
labelNames: ['pid', 'type']
});
const heapUsedMetric = new Prometheus.Gauge({
name: `memory_heap_used`,
help: 'Used heap memory.',
labelNames: ['pid', 'type']
});
const externalMetric = new Prometheus.Gauge({
name: `memory_external`,
help: 'Memory usage of C++ objects bound to JavaScript objects managed by V8.',
labelNames: ['pid', 'type']
});
const arrayBufferMetric = new Prometheus.Gauge({
name: `memory_array_buffers`,
help: 'Memory allocated for ArrayBuffers and SharedArrayBuffers.',
labelNames: ['pid', 'type']
});
const cpuUserMetric = new Prometheus.Gauge({
name: `process_cpu_user_seconds_total`,
help: 'Total user CPU time spent in seconds during the configured interval.',
labelNames: ['pid', 'type']
});
const cpuSystemMetric = new Prometheus.Gauge({
name: `process_cpu_system_seconds_total`,
help: 'Total system CPU time spent in seconds during the configured interval.',
labelNames: ['pid', 'type']
});
const cpuTotalMetric = new Prometheus.Gauge({
name: `process_cpu_seconds_total`,
help: 'Total user and system CPU time spent in seconds during the configured interval',
labelNames: ['pid', 'type']
});
const cpuPercentMetric = new Prometheus.Gauge({
name: `process_cpu_percent`,
help: 'Total user and system CPU time spent divided by the interval duration',
labelNames: ['pid', 'type']
});
const wsMetric = new Prometheus.Gauge({
name: `active_websockets`,
help: 'Number of active websocket connections',
});
const regMetric = new Prometheus.Gauge({
name: `active_registered_users`,
help: 'Number of registered users online',
});
const chanMetric = new Prometheus.Gauge({
name: `active_channels`,
help: 'Number of active pads',
});
EVENTS.MONITORING = function (data) {
/*
{
main: {
rss: 1234
...
},
pid1: {
rss: 234
...
}
}
*/
Object.keys(data).forEach(pid => {
let val = data[pid];
let type = val.type;
rssMetric.set({pid, type}, val.mem?.rss || 0);
heapTotalMetric.set({pid, type}, val.mem?.heapTotal || 0);
heapUsedMetric.set({pid, type}, val.mem?.heapUsed || 0);
externalMetric.set({pid, type}, val.mem?.external || 0);
arrayBufferMetric.set({pid, type}, val.mem?.arrayBuffers || 0);
let userSeconds = (val.cpu?.user || 0) / 1000000;
let systemSeconds = (val.cpu?.system || 0) / 1000000;
cpuUserMetric.set({pid, type}, userSeconds);
cpuSystemMetric.set({pid, type}, systemSeconds);
let sum = userSeconds + systemSeconds;
let percent = sum / (Monitoring.interval/1000);
cpuTotalMetric.set({pid, type}, sum);
cpuPercentMetric.set({pid, type}, percent);
if (type === 'main') {
wsMetric.set(val.ws || 0);
regMetric.set(val.registered || 0);
chanMetric.set(val.channels || 0);
}
});
};
EVENTS.ENV_UPDATE = function (data /*, cb */) {
try {
Env = JSON.parse(data);
@ -318,13 +219,6 @@ const wsProxy = createProxyMiddleware({
app.use('/cryptpad_websocket', wsProxy);
app.get('/metrics', (req, res) => {
Prometheus.register.metrics().then((data) => {
res.set('Content-Type', Prometheus.register.contentType);
res.send(data);
});
});
app.use('/ssoauth', (req, res, next) => {
if (SSOUtils && req && req.body && req.body.SAMLResponse) {
req.method = 'GET';
@ -905,14 +799,7 @@ nThen(function (w) {
}));
}).nThen(function () {
// TODO inform the parent process that this worker is ready
setInterval(() => {
sendMessage({
command: 'MONITORING',
data: Monitoring.getData('http-worker')
}, () => {
// Done
});
}, Monitoring.interval);
});
process.on('uncaughtException', function (err) {

View file

@ -1,45 +0,0 @@
/*
globals process
*/
const VALUES = {};
VALUES.mem = () => {
return process.memoryUsage();
};
let oldCpu;
VALUES.cpu = () => {
if (!oldCpu) {
oldCpu = process.cpuUsage();
return {user:0,system:0};
}
let val = process.cpuUsage(oldCpu);
oldCpu = process.cpuUsage();
return val;
};
const applyToEnv = (Env, data) => {
if (!Env) { return; }
Env.monitoring[data.pid] = data;
};
const getData = (type) => {
const value = {
pid: process.pid,
type: type
};
Object.keys(VALUES).forEach(key => {
value[key] = VALUES[key]();
});
return value;
};
const remove = (Env, pid) => {
if (Env && Env.monitoring && pid && Env.monitoring[pid]) {
delete Env.monitoring[pid];
}
};
module.exports = {
interval: 5000,
applyToEnv,
getData,
remove
};

View file

@ -170,15 +170,6 @@ var rpc = function (Env, Server, userId, data, respond) {
var command = msg[1];
/*
// TODO get data from lib/upload.js to be able to get the size of the uploaded file
if (command === 'UPLOAD_COMPLETE' || command === 'OWNED_UPLOAD_COMPLETE') {
let m = Env.monitoring = Env.monitoring || {};
let b = m.upload = m.upload || {};
let id = msg[2];
if (id) { b[id] = +new Date(); }
}
*/
if (command === 'UPLOAD') {
// UPLOAD is a special case that skips signature validation
// intentional fallthrough behaviour

View file

@ -16,8 +16,6 @@ const Logger = require("../log");
const Tasks = require("../storage/tasks");
const Nacl = require('tweetnacl/nacl-fast');
const Eviction = require("../eviction");
const Monitoring = require('../monitoring');
const CPCrypto = require('../crypto');
const Env = {
Log: {},
@ -58,13 +56,6 @@ const init = function (config, _cb) {
Env.archiveRetentionTime = config.archiveRetentionTime;
Env.accountRetentionTime = config.accountRetentionTime;
setInterval(() => {
process.send({
monitoring: true,
data: Monitoring.getData('db-worker')
});
}, Monitoring.interval);
nThen(function (w) {
Store.create(config, w(function (err, _store) {
if (err) {
@ -110,10 +101,6 @@ const init = function (config, _cb) {
}
Env.tasks = tasks;
}));
}).nThen(function (w) {
CPCrypto.init(w(function (err, crypto) {
Env.crypto = crypto;
}));
}).nThen(function () {
cb();
});
@ -693,8 +680,7 @@ COMMANDS.INLINE = function (data, cb) {
return void cb("E_BADKEY");
}
// validate the message
//const validated = Nacl.sign.open(signedMsg, validateKey);
const validated = Env.crypto.open(signedMsg, validateKey);
const validated = Nacl.sign.open(signedMsg, validateKey);
if (!validated) {
return void cb("FAILED");
}
@ -734,8 +720,7 @@ const checkDetachedSignature = function (signedMsg, signature, publicKey) {
throw new Error("INVALID_SIGNATURE_LENGTH");
}
//if (Nacl.sign.detached.verify(signedBuffer, signatureBuffer, pubBuffer) !== true) {
if (Env.crypto.detachedVerify(signedBuffer, signatureBuffer, pubBuffer) !== true) {
if (Nacl.sign.detached.verify(signedBuffer, signatureBuffer, pubBuffer) !== true) {
throw new Error("FAILED");
}
};

View file

@ -9,7 +9,6 @@ const { fork } = require('child_process');
const Workers = module.exports;
const PID = process.pid;
const Block = require("../storage/block");
const Monitoring = require('../monitoring');
const DB_PATH = 'lib/workers/db-worker';
const MAX_JOBS = 16;
@ -163,13 +162,6 @@ Workers.initialize = function (Env, config, _cb) {
if (res.log) {
return void handleLog(res.log, res.label, res.info);
}
// handle monitoring data
if (res.monitoring) {
Monitoring.applyToEnv(Env, res.data);
return;
}
// but don't bother handling things addressed to other processes
// since it's basically guaranteed not to work
if (res.pid !== PID) {
@ -234,9 +226,7 @@ Workers.initialize = function (Env, config, _cb) {
handleResponse(state, res);
});
let pid = worker.pid;
var substituteWorker = Util.once(function () {
Monitoring.remove(Env, pid);
Env.Log.info("SUBSTITUTE_DB_WORKER", '');
var idx = workers.indexOf(state);
if (idx !== -1) {
@ -273,10 +263,9 @@ Workers.initialize = function (Env, config, _cb) {
};
nThen(function (w) {
var limit = Env.maxWorkers || OS.cpus().length;
var limit = Env.maxWorkers;
var logged;
/*
OS.cpus().forEach(function (cpu, index) {
if (limit && index >= limit) {
if (!logged) {
@ -292,14 +281,6 @@ Workers.initialize = function (Env, config, _cb) {
return void cb(err);
}));
});
*/
for (let i = 0; i<limit; i++) {
initWorker(fork(DB_PATH), w(function (err) {
if (!err) { return; }
w.abort();
return void cb(err);
}));
}
}).nThen(function () {
Env.computeIndex = function (Env, channel, cb) {
Env.store.getWeakLock(channel, function (next) {

29
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "cryptpad",
"version": "2024.6.0",
"version": "2024.6.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cryptpad",
"version": "2024.6.0",
"version": "2024.6.1",
"license": "AGPL-3.0+",
"dependencies": {
"@mcrowe/minibloom": "^0.2.0",
@ -47,7 +47,6 @@
"open-sans-fontface": "^1.4.0",
"openid-client": "^5.4.2",
"pako": "^2.1.0",
"prom-client": "^14.2.0",
"prompt-confirm": "^2.0.4",
"pull-stream": "^3.6.1",
"require-css": "0.1.10",
@ -1203,11 +1202,6 @@
"node": ">= 0.6.0"
}
},
"node_modules/bintrees": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz",
"integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw=="
},
"node_modules/body-parser": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
@ -3937,17 +3931,6 @@
"node": ">=0.4.0"
}
},
"node_modules/prom-client": {
"version": "14.2.0",
"resolved": "https://registry.npmjs.org/prom-client/-/prom-client-14.2.0.tgz",
"integrity": "sha512-sF308EhTenb/pDRPakm+WgiN+VdM/T1RaHj1x+MvAuT8UiQP8JmOEbxVqtkbfR4LrvOg5n7ic01kRBDGXjYikA==",
"dependencies": {
"tdigest": "^0.1.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/prompt-actions": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/prompt-actions/-/prompt-actions-3.0.2.tgz",
@ -5341,14 +5324,6 @@
"node": ">=8"
}
},
"node_modules/tdigest": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz",
"integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==",
"dependencies": {
"bintrees": "1.0.2"
}
},
"node_modules/terminal-paginator": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/terminal-paginator/-/terminal-paginator-2.0.2.tgz",

View file

@ -1,7 +1,7 @@
{
"name": "cryptpad",
"description": "a collaborative office suite that is end-to-end encrypted and open-source",
"version": "2024.6.0",
"version": "2024.6.1",
"license": "AGPL-3.0+",
"repository": {
"type": "git",
@ -50,7 +50,6 @@
"open-sans-fontface": "^1.4.0",
"openid-client": "^5.4.2",
"pako": "^2.1.0",
"prom-client": "^14.2.0",
"prompt-confirm": "^2.0.4",
"pull-stream": "^3.6.1",
"require-css": "0.1.10",

View file

@ -1,61 +0,0 @@
let SodiumNative = require('sodium-native');
let Nacl = require('tweetnacl/nacl-fast');
let LibSodium = require('libsodium-wrappers');
let msgStr = "This is a test";
let keys = Nacl.sign.keyPair();
let pub = keys.publicKey;
let msg = Nacl.util.decodeUTF8(msgStr);
let signedMsg = Nacl.sign(msg, keys.secretKey);
let sig = signedMsg.subarray(0, 64);
LibSodium.ready.then(() => {
/*
console.log('tweetnacl open');
console.log(!!Nacl.sign.open(signedMsg, pub));
console.log('tweetnacl detached');
console.log(Nacl.sign.detached.verify(msg, sig, pub));
console.log('sodium-native open');
console.log(SodiumNative.crypto_sign_open(msg, signedMsg, pub));
console.log('sodium-native detached');
console.log(SodiumNative.crypto_sign_verify_detached(sig, msg, pub));
LibSodium.ready.then(() => {
console.log('libsodium open');
console.log(!!LibSodium.crypto_sign_open(signedMsg, pub));
console.log('libsodium detached');
console.log(LibSodium.crypto_sign_verify_detached(sig, msg, pub));
});
*/
const n = 10000;
let a;
console.log('start sodium-native');
a = +new Date();
for (var i = 0; i < n; i++) {
SodiumNative.crypto_sign_open(msg, signedMsg, pub);
SodiumNative.crypto_sign_verify_detached(sig, msg, pub);
}
console.log('end sodium-native ', (+new Date() - a), ' ms');
console.log('start libsodium');
a = +new Date();
for (var i = 0; i < n; i++) {
LibSodium.crypto_sign_open(signedMsg, pub);
LibSodium.crypto_sign_verify_detached(sig, msg, pub);
}
console.log('end libsodium ', (+new Date() - a), ' ms');
console.log('start tweetnacl');
a = +new Date();
for (var i = 0; i < n; i++) {
Nacl.sign.open(signedMsg, pub);
Nacl.sign.detached.verify(msg, sig, pub);
}
console.log('end tweetnacl ', (+new Date() - a), ' ms');
});

View file

@ -15,7 +15,6 @@ var config = require("./lib/load-config");
var Environment = require("./lib/env");
var Env = Environment.create(config);
var Default = require("./lib/defaults");
var Monitoring = require('./lib/monitoring');
var app = Express();
@ -50,11 +49,6 @@ COMMANDS.GET_PROFILING_DATA = function (msg, cb) {
cb(void 0, Env.bytesWritten);
};
COMMANDS.MONITORING = function (msg, cb) {
Monitoring.applyToEnv(Env, msg.data);
cb();
};
nThen(function (w) {
require("./lib/log").create(config, w(function (_log) {
Env.Log = _log;
@ -99,7 +93,6 @@ nThen(function (w) {
var launchWorker = (online) => {
var worker = Cluster.fork(workerState);
var pid = worker.process.pid;
worker.on('online', () => {
online();
});
@ -129,7 +122,6 @@ nThen(function (w) {
});
worker.on('exit', (code, signal) => {
Monitoring.remove(Env, pid);
if (!signal && code === 0) { return; }
// relaunch http workers if they crash
Env.Log.error('HTTP_WORKER_EXIT', {
@ -171,19 +163,6 @@ nThen(function (w) {
broadcast('FLUSH_CACHE', Env.FRESH_KEY);
}, 250);
setInterval(() => {
// Add main process data to monitoring
let monitoring = Monitoring.getData('main');
let Server = Env.Server;
let stats = Server.getSessionStats();
monitoring.ws = stats.total;
monitoring.channels = Server.getActiveChannelCount();
monitoring.registered = Object.keys(Env.netfluxUsers).length;
// Send updated values
Monitoring.applyToEnv(Env, monitoring);
broadcast('MONITORING', Env.monitoring);
}, Monitoring.interval);
Env.envUpdated.reg(throttledEnvChange);
Env.cacheFlushed.reg(throttledCacheFlush);

View file

@ -2713,8 +2713,7 @@ define([
}, function (e, arr) {
pre.innerText = '';
let data = arr[0];
pre.append(String(data.blocks));
pre.append(' (old value including teams: ' + String(data.users) + ')'); // XXX
pre.append(String(data.users));
});
};
onRefresh();

View file

@ -106,7 +106,7 @@ define([
framework._.toolbar.$theme.append($showAuthorColors);
markers.setButton($showAuthorColors);
};
var mkPrintButton = function (framework, $content) {
var mkPrintButton = function (framework, $content, $print) {
var $printButton = framework._.sfCommon.createButton('print', true);
$printButton.click(function () {
$print.html($content.html());
@ -115,8 +115,8 @@ define([
framework.feedback('PRINT_CODE');
UI.clearTooltipsDelay();
});
var $print = UIElements.getEntryFromButton($printButton);
framework._.toolbar.$drawer.append($print);
var $dropdownEntry = UIElements.getEntryFromButton($printButton);
framework._.toolbar.$drawer.append($dropdownEntry);
};
var mkMarkdownTb = function (editor, framework) {
var $codeMirrorContainer = $('#cp-app-code-container');

View file

@ -811,19 +811,11 @@ define([
addTabListener(frame);
frame.addEventListener('keydown', function(e) {
if (e.keyCode === 13) {
if (document.activeElement === $ok[0]) {
$ok.click();
} else if (document.activeElement === $cancel[0]) {
$cancel.click();
}
} else if (e.keyCode === 27) {
$cancel.click();
}
});
listener = listenForKeys(function () {
// Only trigger OK if cancel is not focused
if (document.activeElement === $cancel[0]) {
return void $cancel.click();
}
$ok.click();
}, function () {
$cancel.click();

View file

@ -919,7 +919,10 @@ define([
};
const getMyOOIndex = function() {
return findUserByOOId(myOOId).index;
const user = findUserByOOId(myOOId);
return user
? user.index
: content.ids.length; // Assign an unused id to read-only users
};
var getParticipants = function () {
@ -1478,6 +1481,10 @@ define([
send({ type: "message" });
break;
case "saveChanges":
if (readOnly) {
return;
}
// If we have unsaved data before reloading for a checkpoint...
if (APP.onStrictSaveChanges) {
delete APP.unsavedLocks;

View file

@ -789,8 +789,7 @@ define([
ownedPads.forEach(function (c) {
var w = waitFor();
sem.take(function (_give) {
var give = _give();
sem.take(function (give) {
var otherOwners = false;
nThen(function (_w) {
// Don't check server metadata for blobs

View file

@ -1055,7 +1055,7 @@ define([
var removeClient = function (ctx, cId) {
var idx = ctx.clients.indexOf(cId);
ctx.clients.splice(idx, 1);
if (idx !== -1) { ctx.clients.splice(idx, 1); }
Object.keys(ctx.calendars).forEach(function (id) {
var cal = ctx.calendars[id];

View file

@ -426,5 +426,25 @@
"settings_logoutEverywhereConfirm": "Сигурен ли си? Ще трябва да влезеш на всичките си устройства.",
"settings_driveDuplicateHint": "Когато преместите вашите документи в споделена папка, копие от тях се запазва във вашия CryptDrive, за да се гарантира, контрола ви върху него. Можете да скриете дублираните файлове. Само споделената версия ще бъде видима, освен ако не бъде изтрита, в този случай оригиналът ще се покаже на предишното си място.",
"settings_padWidthHint": "Превключване между режим на страница (по подразбиране), който ограничава ширината на текстовия редактор, и използване на цялата ширина на екрана.",
"settings_padSpellcheckHint": "Тази опция ви позволява да активирате проверка на правописа в документи с форматиран текст. Правописните грешки ще бъдат подчертани в червено и ще трябва да задържите клавиша Ctrl или Meta, докато щраквате с десния бутон, за да видите опциите."
"settings_padSpellcheckHint": "Тази опция ви позволява да активирате проверка на правописа в документи с форматиран текст. Правописните грешки ще бъдат подчертани в червено и ще трябва да задържите клавиша Ctrl или Meta, докато щраквате с десния бутон, за да видите опциите.",
"settings_padSpellcheckLabel": "Активиране на проверката на правописа в документи с форматиран текст",
"settings_padOpenLinkTitle": "Отваряне на връзки при първо щракване",
"settings_padOpenLinkHint": "С тази възможност можете да отваряте вградени връзки при щракване без изскачащ прозорец за показване",
"settings_padOpenLinkLabel": "Разрешаване на отварянето на директната връзка",
"settings_ownDriveTitle": "Актуализиране на профила",
"settings_ownDriveHint": "По-старите акаунти нямат достъп до най-новите функции поради технически причини. Безплатното обновяване ще активира текущите функции и ще подготви вашия CryptDrive за бъдещи обновявания.",
"settings_ownDriveButton": "Надграждане на профила ви",
"settings_ownDriveConfirm": "Надграждането на вашия акаунт може да отнеме известно време. Ще трябва да влезете отново през всичките си устройства. Сигурен ли сте?",
"settings_ownDrivePending": "Вашият профил се надгражда. Моля, не затваряйте и не презареждайте тази страница, докато процесът не приключи.",
"settings_changePasswordTitle": "Промяна на паролата",
"settings_changePasswordHint": "Променете паролата на профила си. Въведете текущата си парола и потвърдете новата парола, като я въведете два пъти.<br><b>Не можем да възстановим паролата ви, ако я забравите, така че бъдете много внимателни!</b>",
"settings_changePasswordButton": "Промяна на парола",
"settings_changePasswordCurrent": "Текуща парола",
"settings_changePasswordNew": "Нова парола",
"settings_changePasswordNewConfirm": "Потвърждаване на новата парола",
"settings_changePasswordConfirm": "Сигурни ли сте, че искате да промените паролата си? Ще трябва да влезете отново през всичките си устройства.",
"settings_changePasswordPending": "Вашата парола се обновява. Моля, не затваряйте и не презареждайте тази страница, докато процесът не приключи.",
"settings_cursorColorTitle": "Цвят на курсора",
"settings_changePasswordError": "Възникна неочаквана грешка. Ако не можете да влезете или да промените паролата си, свържете се с администраторите на CryptPad.",
"settings_changePasswordNewPasswordSameAsOld": "Вашата нова парола трябва да е различна от настоящата."
}

View file

@ -607,8 +607,8 @@
"admin_activeSessionsHint": "Anzahl aktiver Websocket-Verbindungen (und verbundener IP-Adressen)",
"admin_activePadsTitle": "Aktive Dokumente",
"admin_activePadsHint": "Anzahl der Dokumente, die gerade angesehen oder bearbeitet werden",
"admin_registeredTitle": "Registrierte Nutzer",
"admin_registeredHint": "Anzahl der auf deiner Instanz registrierten Nutzer",
"admin_registeredTitle": "Nutzer und Team-Drives",
"admin_registeredHint": "Anzahl aktiver Drives auf deiner Instanz",
"admin_updateLimitTitle": "Nutzer-Quotas aktualisieren",
"admin_updateLimitHint": "Das Erzwingen einer Aktualisierung der Speicherbegrenzungen für Nutzer ist jederzeit möglich, aber nur im Fehlerfall notwendig",
"admin_updateLimitButton": "Quotas aktualisieren",

View file

@ -553,7 +553,7 @@
"password_info": "El documento que intenta abrir no existe o está protegido con una contraseña. Ingrese la contraseña correcta para acceder a su contenido.",
"creation_newPadModalDescription": "Haz clic en una aplicación para crear un nuevo documento. Tú también puedes presionar <b>Tab</b> para seleccionar la aplicación y presiona <b>Enter</b> para confirmar.",
"toolbar_degraded": "Actualmente hay más de {0} editores en este documento. La lista de usuarios y el chat están desactivados para mejorar el rendimiento.",
"oo_lostEdits": "Lamentablemente, las ediciones recientes no guardadas no se pueden recuperar después de sincronizar el nuevo contenido.",
"oo_lostEdits": "Lamentablemente, las ediciones recientes no guardadas no se pudieron recuperar después de sincronizar contenido nuevo.",
"properties_passwordError": "Se ha producido un error al intentar cambiar la contraseña. Por favor, inténtelo de nuevo.",
"properties_passwordSame": "Las nuevas contraseñas deben diferir de la actual.",
"properties_confirmChange": "¿Está seguro? Al cambiar la contraseña se eliminará su historial. Los usuarios que no tengan la nueva contraseña perderán el acceso a este documento",
@ -863,7 +863,7 @@
"form_open": "Abrir",
"form_isPrivate": "Las respuestas son privadas",
"form_isPublic": "Las respuestas son públicas",
"form_makePublicWarning": "¿Estás seguro/a que quieres hacer las respuestas a este formulario públicas? Las respuestas pasadas y futuras serán visibles por los participantes. Esto no se puede deshacer.",
"form_makePublicWarning": "¿Estás seguro de querer hacer públicas las respuestas a este formulario? Las respuestas pasadas y futuras serán visibles para los participantes. Esta acción no se puede deshacer.",
"form_makePublic": "Publicar respuestas",
"form_invalidQuestion": "Pregunta {0}",
"form_invalidWarning": "Hay errores en algunas respuestas:",
@ -875,7 +875,7 @@
"form_backButton": "Atrás",
"form_viewButton": "Ver",
"form_answerAnonymous": "Respuesta anónima en {0}",
"form_showSummary": "Mostrar sumario",
"form_showSummary": "Mostrar resumen",
"form_showIndividual": "Mostrar respuestas individuales",
"form_editor": "Editor",
"form_results_empty": "No hay respuestas",
@ -887,49 +887,49 @@
"form_delete": "Eliminar",
"form_reset": "Reiniciar",
"form_submit": "Enviar",
"form_maxOptions": "respuesta(s) máximas {0}",
"form_maxOptions": "Máximo {0} respuesta(s)",
"form_description_default": "Tu texto aquí",
"form_type_page": "Salto de página",
"form_type_md": "Descripción",
"form_sort_hint": "Por favor arrastre estos ítems desde el más (1) al menos ({0}) preferido.",
"form_type_sort": "Lista ordenada",
"form_type_poll": "Encuesta",
"form_type_multicheck": "Cuadrícula de casillas de verificación",
"form_type_multicheck": "Grilla de casillas de verificación",
"form_type_checkbox": "Casilla de verificación",
"form_type_multiradio": "Escoge la red",
"form_type_multiradio": "Grilla de opciones",
"form_type_radio": "Opción",
"form_type_textarea": "Párrafo",
"form_type_input": "Texto",
"form_default": "¿Tu pregunta aquí?",
"form_text_number": "Número",
"form_text_email": "Correo",
"form_text_url": "Link",
"form_text_url": "Enlace",
"form_text_text": "Texto",
"form_textType": "Tipo de texto",
"form_pollYourAnswers": "Tus respuestas",
"form_pollTotal": "Total",
"form_poll_switch": "Intercambio de ejes",
"form_poll_time": "Tiempo",
"form_poll_switch": "Invertir ejes",
"form_poll_time": "Hora",
"form_poll_day": "Día",
"form_poll_text": "Texto",
"form_editType": "Tipo de opción",
"form_editMaxLength": "Máximos carácteres",
"form_editMax": "Máximas opciones seleccionables",
"form_editMaxLength": "Máximo de caracteres",
"form_editMax": "Máximo de opciones seleccionables",
"form_editBlock": "Editar",
"form_invalid": "Formulario inválido",
"share_formView": "Participante",
"share_formAuditor": "Auditor/a",
"share_formEdit": "Autor/a",
"share_formAuditor": "Auditor",
"share_formEdit": "Autor",
"admin_supportPrivButton": "Mostrar llave",
"admin_supportPrivHint": "Muestra la llave privada que otros/as administradores/as necesitarán para ver tickets de soporte. Una forma de ingresar esta llave será mostrada en su panel de administrador/a.",
"admin_supportPrivHint": "Muestra la llave privada que otros administradores necesitarán para ver tickets de soporte. Se mostrará un formulario para ingresar esta clave en su panel de administración.",
"admin_supportInitGenerate": "Generar llaves de soporte",
"admin_supportPrivTitle": "Llave privada del buzón de soporte",
"admin_emailHint": "Establece el correo de contacto para tu instancia aquí",
"admin_emailTitle": "Correo de contacto del administrador/a",
"admin_emailTitle": "Correo de contacto del administrador",
"oo_importBin": "Presiona OK para importar el formato .bin interno de CryptPad.",
"oo_conversionSupport": "Tu navegador no puede manejar la conversión hacia y desde formatos de office. Sugerimos usar una versión reciente de Firefox o Chrome.",
"register_registrationIsClosed": "La registración está cerrada.",
"mediatag_defaultImageName": "imágen",
"mediatag_defaultImageName": "imagen",
"genericCopySuccess": "Copiado al portapapeles",
"toolbar_storeInDrive": "Guardar en CryptDrive",
"calendar_noNotification": "Ninguno",
@ -957,9 +957,9 @@
"calendar_newEvent": "Nuevo evento",
"calendar_import": "Añadir a mis calendarios",
"calendar_errorNoCalendar": "Ningún calendario editable seleccionado",
"calendar_deleteOwned": "Se mantendrá visible para otros/as usuarios/as con los que se haya compartido.",
"calendar_deleteTeamConfirm": "¿Estás seguro/a que quieres eliminar este calendario del equipo?",
"calendar_deleteConfirm": "¿Estás seguro/a que quieres eliminar este calendario de tu cuenta?",
"calendar_deleteOwned": "Se mantendrá visible para otros usuarios con los que se haya compartido.",
"calendar_deleteTeamConfirm": "¿Estás seguro que quieres eliminar este calendario del equipo?",
"calendar_deleteConfirm": "¿Estás seguro que quieres eliminar este calendario de tu cuenta?",
"calendar_today": "Hoy",
"calendar_month": "Mes",
"calendar_week": "Semana",
@ -972,73 +972,73 @@
"calendar_import_temp": "Importar este calendario",
"pad_goToAnchor": "Ir al anclaje",
"oo_cantMigrate": "Esta hoja excede el tamaño máximo de subida y es muy grande para ser migrada.",
"footer_roadmap": "Hoja de ruta",
"settings_deleteSubscription": "Manejar mi suscripción",
"footer_roadmap": "Plan de acción",
"settings_deleteSubscription": "Administrar mi suscripción",
"settings_deleteContinue": "Eliminar mi cuenta",
"settings_deleteWarning": "Advertencia: actualmente estás suscrito/a a un plan premium (pagado o dado por otro/a usuario/a). Por favor cancela tu plan antes de eliminar tu cuenta ya que no será posible sin contactar a soporte una vez tu cuenta esté eliminada.",
"broadcast_newCustom": "Mensaje de los/as administradores/as",
"settings_deleteWarning": "Advertencia: actualmente estás suscrito a un plan premium (pagado o dado por otro usuario). Por favor cancela tu plan antes de eliminar tu cuenta, de otra forma tendrás que contactar al soporte para poder cancelar tu plan.",
"broadcast_newCustom": "Mensaje de los administradores",
"broadcast_preview": "Previsualizar notificación",
"broadcast_defaultLanguage": "Volver a este idioma",
"broadcast_translations": "Traducciones",
"admin_broadcastCancel": "Eliminar mensaje",
"admin_broadcastActive": "Mensaje activo",
"admin_broadcastButton": "Envíar",
"admin_broadcastHint": "Envía un mensaje a todos/as los/as usuarios/as en esta instancia. Todos/as los/as usuarios/as nuevos/as y existentes lo recibirán como una notificación. Previsualiza los mensajes antes de enviarlos con \"Previsualizar notificación\". Las notificaciones de previsualización tienen un ícono rojo y son visibles sólo para ti.",
"admin_broadcastButton": "Enviar",
"admin_broadcastHint": "Envía un mensaje a todos los usuarios en esta instancia. Todos los usuarios nuevos y existentes lo recibirán como una notificación. Previsualiza los mensajes antes de enviarlos con \"Previsualizar notificación\". Las notificaciones de previsualización tienen un ícono rojo y son visibles sólo para ti.",
"admin_broadcastTitle": "Mensaje de difusión",
"broadcast_surveyURL": "Link del cuestionario",
"broadcast_surveyURL": "Enlace del cuestionario",
"admin_surveyActive": "Abrir cuestionario",
"admin_surveyCancel": "Eliminar",
"admin_surveyButton": "Guardar cuestionario",
"broadcast_newSurvey": "Un nuevo cuestionario está disponible. Presiona para abrir.",
"admin_surveyHint": "Añade, actualiza o borra un link a un cuestionario externo. Los/as usuarios/as recibirán una notificación y el cuestionario se mantendrá disponible desde el menú de usuario/a.",
"admin_surveyHint": "Añade, actualiza o borra un enlace a un cuestionario externo. Los usuarios recibirán una notificación y el cuestionario se mantendrá disponible desde el menú del usuario.",
"admin_surveyTitle": "Cuestionario",
"broadcast_maintenance": "Un mantenimiento está planeado entre las <b>{0}</b> y <b>{1}</b>. CryptPad puede no estar disponible durante ese tiempo.",
"broadcast_maintenance": "Un mantenimiento está planeado entre las <b>{0}</b> y <b>{1}</b>. CryptPad puede que no esté disponible durante ese tiempo.",
"broadcast_end": "Terminar",
"broadcast_start": "Empezar",
"admin_maintenanceCancel": "Cancelar mantenimiento",
"admin_maintenanceButton": "Planear mantenimiento",
"admin_maintenanceHint": "Planear un mantenimiento en esta instancia y notificar a todos/as los/as usuarios/as. Limitado a un mantenimiento activo en un momento dado.",
"admin_maintenanceHint": "Planear un mantenimiento en esta instancia y notificar a todos los usuarios. Limitado a un mantenimiento activo en un momento dado.",
"admin_maintenanceTitle": "Mantenimiento",
"admin_cat_broadcast": "Difusión",
"fm_cantUploadHere": "No se puede subir un archivo aquí",
"importError": "Fallo al importar (formato incorrecto)",
"importError": "Error al importar (formato incorrecto)",
"addOptionalPassword": "Añadir una contraseña (opcional)",
"settings_colortheme_custom": "Personalizado",
"settings_colortheme_custom": "Personalizar",
"pad_settings_show": "Mostrar",
"pad_settings_hide": "Esconder",
"pad_settings_comments": "Escoge si los comentarios deberían ser visibles o escondidos por defecto.",
"pad_settings_outline": "Escoge si la tabla de contemidos debería ser visible o escondida por defecto.",
"pad_settings_hide": "Ocultar",
"pad_settings_comments": "Escoge si los comentarios deberían ser visibles u oculta por defecto.",
"pad_settings_outline": "Escoge si la tabla de contenidos debería ser visible u oculta por defecto.",
"pad_settings_width_large": "Ancho total",
"pad_settings_width_small": "Modo de página",
"pad_settings_info": "Configuraciones por defecto para este documento. Estos serán aplicados cuando usuarios/as nuevos/as visiten este documento.",
"pad_settings_info": "Configuraciones por defecto para este documento. Estos serán aplicados cuando usuarios nuevos visiten este documento.",
"pad_settings_title": "Configuración del documento",
"settings_colorthemeTitle": "Tema del color",
"settings_colorthemeHint": "Cambiar los colores en general de CryptPad en este dispositivo.",
"settings_colortheme_default": "Tema del sistema por defecto ({0})",
"settings_colortheme_light": "Blanco",
"settings_colortheme_dark": "Negro",
"settings_colorthemeTitle": "Tema de color",
"settings_colorthemeHint": "Cambiar los colores de CryptPad en este dispositivo.",
"settings_colortheme_default": "Configuración predeterminada del sistema ({0})",
"settings_colortheme_light": "Claro",
"settings_colortheme_dark": "Oscuro",
"settings_cat_style": "Apariencia",
"admin_performancePercentHeading": "Porcentaje",
"admin_performanceTimeHeading": "Tiempo (segundos)",
"admin_performanceKeyHeading": "Comando",
"admin_performanceProfilingTitle": "Rendimiento",
"admin_performanceProfilingHint": "Una visión general del tiempo total gastado ejecutando varios comandos en el lado del servidor",
"admin_performanceProfilingHint": "Una visión general del tiempo total dedicado a ejecutar varios comandos del lado del servidor",
"admin_cat_performance": "Rendimiento",
"redo": "Rehacer",
"undo": "Deshacer",
"settings_cacheButton": "Limpiar caché existente",
"settings_cacheButton": "Borrar caché existente",
"settings_cacheCheckbox": "Activar caché en este dispositivo",
"settings_cacheHint": "CryptPad guarda partes de tus documentos en la memoria de tu navegador para poder ahorrar en uso de la red y mejorar los tiempos de carga. Puedes desactivar el caché si tu dispositivo no tiene mucho espacio libre. Por razones de seguridad, el caché siempre es limpiado cuando cierras sesión, pero puedes limpiarlo manualmente si quieres recuperar espacio de almacenamiento en tu máquina.",
"settings_cacheHint": "CryptPad guarda partes de tus documentos en la memoria de tu navegador para poder ahorrar conexión y mejorar los tiempos de carga. Puedes desactivar el caché si tu dispositivo no tiene mucho espacio libre. Por razones de seguridad, el caché siempre es borrado cuando cierras sesión, pero puedes borrarlo manualmente si quieres recuperar espacio de almacenamiento en tu dispositivo.",
"settings_cacheTitle": "Caché",
"docs_link": "Documentación",
"creation_helperText": "Abrir en documentación",
"creation_expiresIn": "Expira en",
"register_warning_note": "Debido a la naturaleza encriptada de CryptPad, los/as administradores/as de servicio no podrán recuperar los datos en caso de que olvides tu nombre de usuario/a y/o contraseña. Por favor guardalos en un lugar seguro.",
"register_notes": "<ul class=\"cp-notes-list\"><li>Tu contraseña es la llave secreta que encripta todos tus documentos. <span class=\"red\">Si la pierdes no hay forma que podamos recuperar tus datos.</span></li><li>Si estás usando una computadora compartida, <span class=\"red\">recuerda cerrar la sesión</span> cuando estés listo/a. Tan solo cerrar la ventana del navegador dejará a tu cuenta expuesta. </li><li>Para mantener los documentos que creaste y/o guardaste sin acceder, marca \"Importar documentos de tu sesión de invitado/a\".</li></ul>",
"creation_expiresIn": "Destruir en",
"register_warning_note": "Debido a la naturaleza encriptada de CryptPad, los administradores no podrán recuperar los datos en caso de que olvides tu nombre de usuario y/o contraseña. Por favor, guárdalos en un lugar seguro.",
"register_notes": "<ul class=\"cp-notes-list\"><li>Tu contraseña es la llave secreta que encripta todos tus documentos. <span class=\"red\">Si la pierdes no hay forma que podamos recuperar tus datos.</span></li><li>Si estás usando una computadora compartida, <span class=\"red\">recuerda cerrar la sesión</span> cuando termines. Cerrar solo la ventana del navegador dejará tu cuenta expuesta. </li><li>Para quedarte con los documentos que creaste y/o guardaste sin iniciar sesión, marca \"Importar documentos de tu sesión de invitado\".</li></ul>",
"register_notes_title": "Notas importantes",
"offlineError": "No se han podido sincronizar los datos más recientes, esta página no puede ser mostrada ahora. La carga continuará cuando tu conexión al servicio sea restaurada.",
"share_noContactsOffline": "Actualmente estás fuera de línea. Los contactos no están disponibles.",
"access_offline": "Actualmente estás fuera de línea. Manejo de acceso no está disponible.",
"access_offline": "Actualmente estás fuera de línea. La administración de acceso no está disponible.",
"admin_support_first": "Creado en: ",
"admin_support_collapse": "Colapsar",
"admin_support_open": "Mostrar",
@ -1046,10 +1046,10 @@
"admin_support_answered": "Tickets respondidos:",
"admin_support_normal": "Tickets sin responder:",
"admin_support_premium": "Tickets premium:",
"contacts_confirmCancel": "¿Estás seguro/a que quieres cancelar tu solicitud de contacto con <b>{0}</b>?",
"history_trimPrompt": "Este documento ha acumulado {0} de historial que puede ralentizar el tiempo de carga. Considere borrar el historial si no es necesario.",
"contacts_confirmCancel": "¿Estás seguro que quieres cancelar tu solicitud de contacto con <b>{0}</b>?",
"history_trimPrompt": "Este documento ha acumulado {0} de historial que puede ralentizar el tiempo de carga. Considera borrar el historial si no lo necesita.",
"mediatag_loadButton": "Cargar adjunto",
"settings_mediatagSizeHint": "Tamaño máximo en megabytes (MB) para automáticamente cargar elementos multimedia (imágenes, vídeos, pdf) incrustados en documentos. Elementos más grandes que el tamaño especificado pueden ser cargados manualmente. Use \"-1\" para siempre cargar los elementos multimedia automáticamente.",
"settings_mediatagSizeHint": "Tamaño máximo en megabytes (MB) para cargar automáticamente elementos multimedia (imágenes, vídeos, pdf) dentro de los documentos. Elementos más grandes que el tamaño especificado pueden ser cargados manualmente. Usa \"-1\" para cargar siempre los elementos multimedia automáticamente.",
"settings_mediatagSizeTitle": "Límite automático de descarga",
"mediatag_notReady": "Por favor complete la descarga",
"pad_mediatagOpen": "Abrir archivo",
@ -1067,33 +1067,33 @@
"admin_unarchiveButton": "Restaurar",
"admin_unarchiveHint": "Restaurar un documento que fue previamente archivado",
"admin_archiveButton": "Archivo",
"admin_archiveHint": "Hacer un documento no disponible sin eliminarlo permanentemente. Será puesto en un directorio 'archivo' y eliminado luego de unos días (configurable en el archivo de configuración del servidor).",
"errorPopupBlocked": "CryptPad necesita poder abrir nuevas pestañas para operar. Por favor permita las ventanas emergentes en la barra de direcciones de su navegador. Estas ventanas nunca van a ser usadas para mostrarle anuncios.",
"unableToDisplay": "No se puede mostrar este documento. Por favor presione Esc para recargar la página. Si el problema persiste, por favor contacte a soporte.",
"admin_archiveHint": "Poner un documento no disponible sin eliminarlo permanentemente. Será puesto en un directorio 'archivo' y eliminado luego de unos días (se puede configurar en el archivo de configuración del servidor).",
"errorPopupBlocked": "CryptPad necesita poder abrir nuevas pestañas para operar. Por favor permita las ventanas emergentes en la barra de direcciones de su navegador. Estas ventanas nunca van a ser usadas para mostrar anuncios.",
"unableToDisplay": "No se puede mostrar el documento. Por favor presione Esc para recargar la página. Si el problema persiste, por favor contacte al soporte.",
"documentID": "Identificador del documento",
"error_unhelpfulScriptError": "Error de script: Vea la consola del navegador para los detalles",
"error_unhelpfulScriptError": "Error de script: Vea la consola del navegador para detalles",
"tag_edit": "Editar",
"tag_add": "Añadir",
"loading_state_5": "Reconstruir documento",
"loading_state_4": "Cargar equipos",
"loading_state_3": "Cargar carpetas compartidas",
"loading_state_2": "Actualizar el contenido",
"loading_state_2": "Actualizar contenido",
"loading_state_1": "Cargar el disco",
"loading_state_0": "Construir interfaz",
"fm_shareFolderPassword": "Proteger esta carpeta con una contraseña (opcional)",
"access_destroyPad": "Destruir este documento o carpeta permanentemente",
"fm_deletedFolder": "Carpeta eliminada",
"admin_limitUser": "Llave pública del usuario/a",
"admin_limitUser": "Llave pública del usuario",
"team_exportButton": "Descargar",
"team_exportHint": "Descargar todos los documentos en el disco de este equipo. Los documentos serán descargados en formatos que sean legibles para otras aplicaciones cuando cierto formato esté disponible. Cuando cierto formato no esté disponible, los documentos serán descargados en un formato legible por CryptPad.",
"team_exportHint": "Descargar todos los documentos en el disco de este equipo. Los documentos serán descargados en formatos que sean legibles por otras aplicaciones cuando el formato esté disponible. Cuando el formato no esté disponible, los documentos serán descargados en un formato legible por CryptPad.",
"team_exportTitle": "Descargar disco del equipo",
"admin_cat_quota": "Almacenamiento del usuario/a",
"admin_invalLimit": "Valor de límite inválido",
"admin_cat_quota": "Almacenamiento del usuario",
"admin_invalLimit": "Valor de límite no válido",
"admin_invalKey": "Llave pública inválida",
"admin_limitSetNote": "Nota",
"admin_limitMB": "Límite (en MB)",
"admin_setlimitTitle": "Aplicar un límite personalizado",
"admin_setlimitHint": "Establecer límites personalizados para usuarios/as usando su llave pública. Puedes actualizar o borrar un límite existente.",
"admin_setlimitHint": "Establecer límites personalizados para usuarios usando su llave pública. Puedes actualizar o borrar un límite existente.",
"admin_limitNote": "Nota: {0}",
"admin_limitPlan": "Plan: {0}",
"admin_getlimitsTitle": "Límites personalizados",
@ -1101,13 +1101,13 @@
"admin_limit": "Límite actual: {0}",
"admin_setlimitButton": "Establecer límite",
"admin_defaultlimitTitle": "Límite de almacenamiento (MB)",
"admin_defaultlimitHint": "Límite de almacenamiento máximo para los CryptDrives (usuarios/as y equipos) cuando no hay una regla personalizada aplicada",
"admin_defaultlimitHint": "Límite de almacenamiento máximo para los CryptDrives (usuarios y equipos) cuando no se aplica ninguna regla",
"admin_registrationTitle": "Cerrar registración",
"admin_registrationHint": "No permitir a ningún usuario/a nuevo/a registrarse",
"snapshots_cantMake": "La instantánea no pudo ser creada. Estás desconectado/a.",
"snapshots_notFound": "Esta instantánea ya no existe debido a que el historial del documento ha sido borrado.",
"snapshot_error_exists": "Ya hay una instantánea de esta versión",
"snapshots_ooPickVersion": "Debes seleccionar una versión antes de crear una instantánea",
"admin_registrationHint": "Los visitantes de la instancia no pueden crear cuentas. Las invitaciones pueden ser creadas por los administradores.",
"snapshots_cantMake": "La captura no pudo ser creada. Has sido desconectado.",
"snapshots_notFound": "Esta captura ya no existe debido a que el historial del documento ha sido borrado.",
"snapshot_error_exists": "Ya hay una captura de esta versión",
"snapshots_ooPickVersion": "Debes seleccionar una versión antes de crear una captura",
"oo_version": "Versión: ",
"oo_version_latest": "Último",
"snapshots_delete": "Eliminar",
@ -1115,50 +1115,50 @@
"snapshots_close": "Cerrar",
"snapshots_restore": "Restaurar",
"snapshots_open": "Abrir",
"snapshots_placeholder": "Título de la instantánea",
"snapshots_new": "Nueva Instantánea",
"snapshots_button": "Instantáneas",
"snaphot_title": "Instantánea",
"infobar_versionHash": "Actualmente estás viendo una versión pasada de este documento ({0}).",
"snapshots_placeholder": "Título de la captura",
"snapshots_new": "Nueva captura",
"snapshots_button": "Capturas",
"snaphot_title": "Captura",
"infobar_versionHash": "Actualmente estás viendo una versión antigua de este documento ({0}).",
"history_restoreDriveDone": "CryptDrive restaurado",
"history_restoreDrivePrompt": "¿Estás seguro/a de que quieres reemplazar la versión actual del CryptDrive con la versión mostrada?",
"history_restoreDrivePrompt": "¿Estás seguro de que quieres reemplazar la versión actual del CryptDrive con la versión mostrada?",
"history_restoreDriveTitle": "Restaurar la versión seleccionada del CryptDrive",
"history_userNext": "Siguiente autor/a",
"history_userNext": "Siguiente autor",
"history_fastNext": "Siguiente sesión de edición",
"history_userPrev": "Autor/a previo/a",
"history_userPrev": "Autor previo",
"history_fastPrev": "Sesión de edición previa",
"share_versionHash": "Estás a punto de compartir la versión del historial seleccionado del documento en el modo de solo lectura. Esto también <b>dará acceso de visión</b> para todas las versiones del documento.",
"history_shareTitle": "Compartir un link a esta versión",
"history_cantRestore": "Restauración fallida. Estás desconectado/a.",
"share_versionHash": "Estás a punto de compartir la versión del historial seleccionada del documento en el modo de solo lectura. Esto también <b>dará acceso de visión</b> para todas las versiones del documento.",
"history_shareTitle": "Compartir un enlace a esta versión",
"history_cantRestore": "Restauración fallida. Desconectado.",
"history_close": "Cerrar",
"history_restore": "Restaurar",
"share_bar": "Crear link",
"share_bar": "Crear enlace",
"settings_cat_kanban": "Kanban",
"settings_kanbanTagsOr": "O",
"settings_kanbanTagsAnd": "Y",
"settings_kanbanTagsHint": "Selecciona como quieres que actúe el filtro de etiquetas cuando se seleccionen múltiples etiquetas: solo mostrar tarjetas conteniendo todas las etiquetas seleccionadas (Y) o mostrar tarjetas conteniendo cualquiera de las etiquetas seleccionadas (O)",
"settings_kanbanTagsHint": "Selecciona como quieres que actúe el filtro de etiquetas cuando se seleccionen múltiples etiquetas: mostrar solo tarjetas que contienen todas las etiquetas seleccionadas (Y) o mostrar tarjetas que contienen cualquiera de las etiquetas seleccionadas (O)",
"settings_kanbanTagsTitle": "Filtro de etiquetas",
"pad_tocHide": "Esquema",
"pad_tocHide": "Resumen",
"fm_noResult": "No se encontraron resultados",
"fm_restricted": "No tienes acceso",
"fm_emptyTrashOwned": "Tu basurero contiene documentos que te pertenecen. Puedes <b>borrarlos</b> solamente de tu disco, o <b>detruirlos</b> para todos/as los/as usuarios/as.",
"fm_emptyTrashOwned": "Tu basurero contiene documentos que te pertenecen. Puedes <b>borrarlos</b> solamente de tu disco, o <b>destruirlos</b> para todos los usuarios.",
"support_formCategoryError": "Error: la categoría está vacía",
"support_category": "Escoge una categoría",
"oo_refresh": "Refrescar",
"notification_folderSharedTeam": "{0} ha compartido una carpet con el equipo {2}: <b>{1}</b>",
"notification_folderSharedTeam": "{0} ha compartido una carpeta con el equipo {2}: <b>{1}</b>",
"notification_fileSharedTeam": "{0} ha compartido un archivo con el equipo {2}: <b>{1}</b>",
"notification_padSharedTeam": "{0} ha compartido un documento con el equipo {2}: <b>{1}</b>",
"support_addAttachment": "Añadir adjunto",
"support_attachments": "Adjuntos",
"support_cat_all": "Todo",
"support_cat_other": "Otros",
"support_cat_bug": "Reporte de fallos",
"support_cat_bug": "Reporte de errores",
"support_cat_data": "Pérdida de contenido",
"support_cat_account": "Cuenta de usuario/a",
"support_cat_account": "Cuenta de usuario",
"info_privacyFlavour": "<a>Política de privacidad</a> para esta instancia",
"user_about": "Acerca de CryptPad",
"info_imprintFlavour": "<a>Información legal</a> acerca de los/as administradores/as de esta instancia",
"settings_safeLinkDefault": "Los links seguros ahora están activados por defecto. Por favor usa el menú <i></i><b>Compartir</b> para compartir links en lugar de la barra de direcciones de tu navegador.",
"info_imprintFlavour": "<a>Información legal</a> acerca de los administradores de esta instancia",
"settings_safeLinkDefault": "Los links seguros ahora están activados por defecto. Por favor usa <i></i><b>Compartir</b> menu para compartir links en lugar de la barra de direcciones de tu navegador.",
"support_languagesPreamble": "El equipo de soporte habla los siguientes idiomas:",
"slide_textCol": "Color del texto",
"slide_backCol": "Color de fondo",
@ -1169,20 +1169,20 @@
"toolbar_savetodrive": "Guardar como imagen",
"toolbar_insert": "Insertar",
"toolbar_theme": "Tema",
"todo_move": "Tu lista de cosas por hacer ahora está en el kanban <b>{0}</b> en tu Disco.",
"todo_move": "Tu lista de cosas por hacer ahora está en el kanban <b>{0}</b> en tu disco.",
"fm_sort": "Ordenar",
"comments_error": "No se puede comentar aquí",
"comments_error": "No se puede añadir comentario aquí",
"settings_padNotifCheckbox": "Desactivar las notificaciones de comentarios",
"settings_padNotifHint": "Ignorar las notificaciones cuando alguien responda a uno de tus comentarios",
"settings_padNotifHint": "Ignorar notificaciones cuando alguien responda a uno de tus comentarios",
"comments_comment": "Comentar",
"comments_resolve": "Resolver",
"comments_reply": "Responder",
"comments_submit": "Envíar",
"comments_submit": "Enviar",
"comments_edited": "Editado",
"comments_deleted": "Comentario eliminado por su autor/a",
"comments_deleted": "Comentario eliminado por su autor",
"mentions_notification": "{0} te ha mencionado en <b>{1}</b>",
"unknownPad": "Documento desconocido",
"comments_notification": "Responde a tu comentario \"{0}\" en <b>{1}</b>",
"comments_notification": "Respuestas a tu comentario \"{0}\" en <b>{1}</b>",
"cba_title": "Colores del autor",
"oo_login": "Por favor accede o regístrate para mejorar el rendimiento de las hojas de cálculo.",
"cba_hide": "Esconder los colores del autor",
@ -1209,9 +1209,9 @@
"kanban_body": "Contenido",
"kanban_title": "Título",
"teams": "Equipos",
"allow_text": "Usar una lista de acceso significa que solo los/as usuarios/as seleccionados/as y propietarios/as podrán acceder a este documento.",
"allow_text": "Usar una lista de acceso significa que solo los usuarios seleccionados y propietarios podrán acceder a este documento.",
"logoutEverywhere": "Cerrar sesión en todos lados",
"owner_text": "El/los/as propietario/a/s de un documento son los/as únicos/as usuarios/as autorizados/as para: añadir/expulsar propietarios/as, restringir acceso al documento con una lista de acceso, o eliminar el documento.",
"owner_text": "El o los propietarios de un documento son los únicos usuarios autorizados a: añadir o quitar propietarios, restringir acceso al documento con una lista de acceso, o eliminar el documento.",
"access_muteRequests": "Silenciar solicitudes de acceso para este documento",
"allow_label": "Lista de acceso: {0}",
"allow_disabled": "desactivado",
@ -1249,32 +1249,32 @@
"oo_sheetMigration_complete": "Versión actualizada disponible, presione OK para recargar.",
"admin_consentToContactTitle": "Consentir a contacto",
"admin_checkupButton": "Ejecutar diagnóstico",
"admin_checkupHint": "CryptoPad incluye una página que automáticamente diagnostica errores comunes y sugiere cómo corregirlos si fuera necesario.",
"admin_checkupTitle": "Valide la configuración de la instancia",
"admin_updateAvailableButton": "Ver las notas de lanzamiento",
"admin_updateAvailableHint": "Una versión nueva de CryptPad está disponible",
"admin_checkupHint": "CryptPad incluye una página que diagnostica automáticamente problemas comunes de configuración y sugiere cómo corregirlos si es necesario.",
"admin_checkupTitle": "Validar la configuración de la instancia",
"admin_updateAvailableButton": "Ver notas de la versión",
"admin_updateAvailableHint": "Una nueva versión de CryptPad está disponible",
"admin_updateAvailableTitle": "Nuevas versiones",
"admin_cat_network": "Red",
"mdToolbar_embed": "Incrustar archivo",
"restrictedLoginPrompt": "No estás autorizado/a a acceder a este documento. <a>Accede</a> si crees que tu cuenta debería poder accederlo.",
"restrictedLoginPrompt": "No estás autorizado a acceder a este documento. <a>Accede</a> si crees que tu cuenta debería tener acceso.",
"copyToClipboard": "Copiar al portapapeles",
"settings_driveRedirect": "Redirigirme automáticamente",
"settings_driveRedirectHint": "La redirección automática desde la página de inicio al disco cuando ya accedió ya no está activada por defecto. El comportamiento de legado puede ser activado abajo.",
"settings_driveRedirect": "Redirígeme automáticamente",
"settings_driveRedirectHint": "La redirección automática desde la página de inicio al drive cuando se inicia sesión ya no está habilitada por defecto. El comportamiento anterior puede habilitarse a continuación.",
"settings_driveRedirectTitle": "Redirección a la página de inicio",
"form_page": "Página {0}/{1}",
"form_clear": "Limpiar",
"form_addMultipleHint": "Añadir múltiples fechas y tiempos",
"form_addMultipleHint": "Añadir múltiples fechas y horas",
"form_addMultiple": "Añadir todo",
"form_anonymous_blocked": "Las respuestas de invitados/a están bloqueadas para este formulario. Debes <a href=\"/login/\">acceder</a> o <a href=\"/register/\">registrarte</a> para enviar respuestas.",
"form_add_item": "Añadir ítem",
"form_anonymous_blocked": "Las respuestas de invitados están bloqueadas para este formulario. Debes <a href=\"/login/\">iniciar sesión</a> o <a href=\"/register/\">registrarte</a> para enviar respuestas.",
"form_add_item": "Añadir elemento",
"form_add_option": "Añadir opción",
"form_newItem": "Nuevo ítem",
"form_newItem": "Nuevo elemento",
"form_newOption": "Nueva opción",
"form_defaultItem": "Ítem {0}",
"form_defaultItem": "Elemento {0}",
"form_defaultOption": "Opción {0}",
"form_anonymous_off": "Bloqueado",
"form_anonymous_on": "Permitido",
"form_anonymous": "Acceso de invitado/a (sin acceder)",
"form_anonymous": "Acceso de invitado (sin sesión iniciada)",
"oo_sheetMigration_loading": "Actualizando tu documento a la última versión. Porfavor espera aproximadamente 1 minuto.",
"oo_exportInProgress": "Exportación en progreso",
"oo_importInProgress": "Importación en progreso",
@ -1453,50 +1453,50 @@
"toolbar_expand": "Expandir barra de herramientas",
"toolbar_collapse": "Colapsar barra de herramientas",
"support_premiumLink": "Ver opciones de suscripción",
"support_premiumPriority": "Los/as usuarios/as premium ayudan a financiar mejoras a la usabilidad de CryptPad y se benefician de respuestas priorizadas a sus tickets de soporte.",
"form_totalResponses": "Respuestas totales: {0}",
"support_premiumPriority": "Los usuarios premium ayudan a respaldar las mejoras en la usabilidad de CryptPad y se benefician de respuestas prioritarias a sus solicitudes de soporte.",
"form_totalResponses": "Total de respuestas: {0}",
"ui_expand": "Expandir",
"ui_collapse": "Colapsar",
"fm_link_invalid": "URL inválida",
"fm_link_warning": "Advertencia: la URL excede los 200 carácteres",
"fm_link_invalid": "URL no válida",
"fm_link_warning": "Advertencia: la URL excede los 200 caracteres",
"form_anonName": "Tu nombre",
"notification_linkShared": "{0} ha compartido un link contigo: <b>{1}</b>",
"fm_link_name_placeholder": "Mí link",
"notification_linkShared": "{0} ha compartido un enlace contigo: <b>{1}</b>",
"fm_link_name_placeholder": "Mi enlace",
"fm_link_url": "URL",
"fm_link_name": "Nombre del link",
"fm_link_type": "Link",
"fm_link_new": "Nuevo link",
"notification_openLink": "Has recibido un link <b>{0}</b> de {1}:",
"fm_link_name": "Nombre del Enlace",
"fm_link_type": "Enlace",
"fm_link_new": "Nuevo enlace",
"notification_openLink": "Has recibido un enlace <b>{0}</b> de {1}:",
"form_exportCSV": "Exportar a CSV",
"team_leaveOwner": "Por favor degradate del rol de propietario/a antes de dejar el equipo. Ten en cuenta que los equipos deben tener al menos un/a propietario/a, por favor añade uno/a antes de proceder si actualmente eres el único/a propietario/a.",
"admin_instancePurposeHint": "¿Porqué diriges esta instancia? Tu respuesta será usada para informar al plan de desarrollo si su telemetría esta activada.",
"team_leaveOwner": "Por favor, reduce privilegios de propietario antes de salir del equipo. Ten en cuenta que los equipos deben tener al menos un propietario; añade uno antes de proceder si actualmente eres el único.",
"admin_instancePurposeHint": "¿Porqué usas esta instancia? Tu respuesta será usada para ayudar al plan de desarrollo si la telemetría está activada.",
"admin_purpose_business": "Para una empresa u organización comercial",
"admin_purpose_public": "Para proveer un servicio gratuito al público",
"admin_purpose_education": "Para una escuela, colegio o universidad",
"admin_purpose_org": "Para una organización sin ánimos de lucro o un grupo de defensa",
"admin_purpose_personal": "Para mí, familia, o amigos/as",
"admin_purpose_education": "Para una escuela o universidad",
"admin_purpose_org": "Para una organización sin fines de lucro o grupo de defensa",
"admin_purpose_personal": "Para mí, familia, o amigos",
"admin_purpose_experiment": "Para probar la plataforma o desarrollar nuevas funciones",
"admin_purpose_noanswer": "Prefiero no decirlo",
"admin_instancePurposeTitle": "Propósito de la instancia",
"resources_learnWhy": "Conoce por qué fue bloqueado",
"resources_openInNewTab": "Abrirlo en una nueva pestaña",
"resources_imageBlocked": "CryptPad bloqueó una imágen remota",
"resources_learnWhy": "Entérate por qué fue bloqueada",
"resources_openInNewTab": "Abrir en una nueva pestaña",
"resources_imageBlocked": "CryptPad bloqueó una imagen remota",
"fc_open_formro": "Abrir (como participante)",
"form_poll_hint": "<i></i>: Si, <i></i>: No, <i></i>: Aceptable",
"form_poll_hint": "<i></i>: Si, <i></i>: No, <i></i>: Aceptar",
"admin_provideAggregateStatisticsLabel": "Proveer estadísticas adicionales",
"admin_provideAggregateStatisticsHint": "Puedes optar por incluir mediciones de uso adicionales para los/as desarrolladores/as, tal como el número aproximado de usuarios/as diarios y registrados/as para tu instancia.",
"admin_provideAggregateStatisticsTitle": "Agregación estadística",
"admin_provideAggregateStatisticsHint": "Puedes optar por incluir mediciones de uso adicionales para los desarrolladores, tal como el número aproximado de usuarios diarios y registrados en tu instancia.",
"admin_provideAggregateStatisticsTitle": "Estadísticas adicionales",
"admin_blockDailyCheckLabel": "Desactivar telemetría del servidor",
"admin_blockDailyCheckHint": "Las instancias de CryptPad envian un mensaje al servidor de los/as desarrolladores/as cuando son lanzadas y una vez por día a partir de entonces. Esto les permite mantenerse al tanto de cuantos servidores están funcionando con que versiones del software. Puedes salirte de esta medición abajo. Los contenidos de este mensaje pueden ser encontrados en el registro de la aplicación del servidor para tu revisión.",
"admin_blockDailyCheckHint": "Las instancias de CryptPad envían un mensaje al servidor de los desarrolladores una vez al día al iniciarse. Esto les permite llevar un registro de cuántos servidores se están ejecutando y qué versiones del software. Puedes optar por no participar en esta medición a continuación. El contenido de este mensaje se puede encontrar en el registro del servidor de la aplicación para tu revisión.",
"admin_blockDailyCheckTitle": "Telemetría del servidor",
"admin_removeDonateButtonLabel": "No publicar las campañas de financiación colectiva",
"admin_removeDonateButtonHint": "El desarrollo de CryptPad es financiado parcialmente por subvenciones públicas y donaciones. Publicar nuestros esfuerzos de financiación colectiva en tu instancia ayuda a los/as desarrolladores/as a continuar mejorando la plataforma para todos/as, pero puedes desactivar estos abisos si los encuentras inapropiados.",
"admin_removeDonateButtonTitle": "Participación en la financiación colectiva",
"admin_removeDonateButtonLabel": "No promocionar campañas de crowdfunding",
"admin_removeDonateButtonHint": "El desarrollo de CryptPad está parcialmente financiado por subvenciones públicas y donaciones. Publicitar nuestros esfuerzos de crowdfunding en tu instancia ayuda a los desarrolladores a continuar mejorando la plataforma para todos, pero puedes desactivar estos avisos si los consideras inapropiados.",
"admin_removeDonateButtonTitle": "Participación Crowdfunding",
"admin_listMyInstanceLabel": "Listar esta instancia",
"admin_listMyInstanceHint": "Si tu instancia es adecuada para el uso público puedes consentir a que sea enlistada en los directorios de la red. La telemetría del servidor debe estar activada para que esto tenga algún efecto.",
"admin_listMyInstanceTitle": "Listar mi instancia en los directorios públicos",
"admin_consentToContactLabel": "Consiento",
"admin_consentToContactHint": "La telemetría del servidor incluye el correo de contacto del administrador/a para que así los/as desarrolladores/as puedan notificarte de problemas serios con el software o tu configuración. Nunca será compartido, vendido, o usado por razones de marketing. Consiente al contacto si te gustaría estar informado/a de problemas críticos en tu servidor.",
"admin_listMyInstanceHint": "Si tu instancia es adecuada para el uso público puedes aceptar que sea enlistada en los directorios de la red. La telemetría del servidor debe estar activada para que esto tenga efecto.",
"admin_listMyInstanceTitle": "Listar mi instancia en directorios públicos",
"admin_consentToContactLabel": "Acepto",
"admin_consentToContactHint": "La telemetría del servidor incluye el correo electrónico de contacto del administrador para que los desarrolladores puedan notificarte sobre problemas graves con el software o la configuración. Nunca será compartido, vendido ni utilizado con fines de marketing. Acepta para ser contactado si deseas ser informado de problemas críticos en tu servidor.",
"calendar_nth_5": "quinto",
"calendar_nth_last": "último",
"calendar_rec_monthly_nth": "Cada {0} {1} del mes",

File diff suppressed because it is too large Load diff

View file

@ -609,8 +609,8 @@
"admin_activeSessionsHint": "Nombre de connexions websocket actives (et adresses IP uniques connectées)",
"admin_activePadsTitle": "Documents actifs",
"admin_activePadsHint": "Nombre de documents uniques actuellement ouverts (lus ou modifiés)",
"admin_registeredTitle": "Utilisateur·ices enregistré·es",
"admin_registeredHint": "Nombre d'utilisateur·ices enregistré·es sur votre instance",
"admin_registeredTitle": "Drives utilisateur·ices et équipes",
"admin_registeredHint": "Nombre de drives actifs sur votre instance",
"admin_updateLimitTitle": "Mettre à jour les quotas",
"admin_updateLimitHint": "Forcer la mise à jour des limites de stockage des utilisateur·ices peut être effectué à tout moment, mais cela n'est utile que lorsqu'une erreur survient",
"admin_updateLimitButton": "Mettre à jour les quotas",
@ -1249,7 +1249,7 @@
"form_makePublic": "Publier les réponses",
"form_invalidQuestion": "Questions {0}",
"form_invalidWarning": "Certaines réponses contiennent des erreurs :",
"form_input_ph_url": "https://example.net/exemple",
"form_input_ph_url": "https://example.net",
"form_input_ph_email": "courriel_exemple@example.net",
"form_notAnswered": "<b>{0}</b> réponses vides",
"form_answerWarning": "Identité non confirmée",

View file

@ -647,8 +647,8 @@
"fm_expirablePad": "Kedaluwarsa: {0}",
"admin_activePadsTitle": "Dokumen aktif",
"admin_activePadsHint": "Jumlah dokumen unik yang saat ini ditampilkan atau disunting",
"admin_registeredTitle": "Pengguna terdaftar",
"admin_registeredHint": "Jumlah pengguna terdaftar di server Anda",
"admin_registeredTitle": "Pengguna dan drive tim",
"admin_registeredHint": "Jumlah drive aktif di server Anda",
"admin_updateLimitButton": "Perbarui kuota",
"admin_updateLimitDone": "Pembaruan berhasil",
"admin_flushCacheDone": "Tembolok berhasil dibersihkan",

View file

@ -1026,12 +1026,12 @@
"unableToDisplay": "Impossibile visualizzare il documento. Premi Esc per ricaricare la pagina. Se il problema persiste, contatta l'assistenza.",
"documentID": "Codice identificativo del documento",
"error_unhelpfulScriptError": "Errore di script: vedi la console del browser per i dettagli",
"loading_state_5": "Ricostruzione del documento",
"loading_state_4": "Caricamento dei gruppi",
"loading_state_3": "Caricamento delle cartelle condivise",
"loading_state_2": "Aggiornamento del contenuto",
"loading_state_1": "Caricamento del drive",
"loading_state_0": "Creazione dell'interfaccia",
"loading_state_5": "Ricostruzione documento",
"loading_state_4": "Caricamento gruppi",
"loading_state_3": "Caricamento cartelle condivise",
"loading_state_2": "Aggiornamento contenuto",
"loading_state_1": "Caricamento drive",
"loading_state_0": "Creazione interfaccia",
"fm_shareFolderPassword": "Proteggi questa cartella con una password (facoltativo)",
"access_destroyPad": "Distruggi questo documento o questa cartella in modo permanente",
"fm_deletedFolder": "Cartella eliminata",

View file

@ -628,8 +628,8 @@
"admin_activeSessionsHint": "Number of active websocket connections (and unique IP addresses connected)",
"admin_activePadsTitle": "Active documents",
"admin_activePadsHint": "Number of unique documents currently being viewed or edited",
"admin_registeredTitle": "Registered users",
"admin_registeredHint": "Number of users registered on your instance",
"admin_registeredTitle": "User and team drives",
"admin_registeredHint": "Number of active drives on your instance",
"admin_updateLimitTitle": "Update user quotas",
"admin_updateLimitHint": "Forcing an update of user storage limits can be done any time, but is only necessary in the event of an error",
"admin_updateLimitButton": "Update quotas",

View file

@ -6,12 +6,12 @@
"whiteboard": "Whiteboard",
"drive": "CryptDrive",
"slide": "Markdown bilder",
"poll": "Röstning",
"poll": "Omröstning",
"code": "Kod",
"todo": "Att-göra",
"teams": "Teams",
"sheet": "Kalkylark",
"pad": "Rik text",
"pad": "Rich text",
"kanban": "Kanban",
"presentation": "Presentation",
"doc": "Dokument",
@ -137,11 +137,11 @@
"chainpadError": "Ett kritiskt fel har uppstått när ditt innehåll uppdaterades. Denna sida är i skrivskyddat läge för att säkerställa att du inte förlorar ditt arbete.<br>Tryck Esc för att fortsätta visa detta dokument, eller ladda om och försök redigera igen.",
"inactiveError": "Det här dokumentet har tagits bort på grund av inaktivitet. Vänligen tryck Esc för att skapa ett nytt dokument.",
"deletedError": "Det här dokumentet har tagits bort och är inte längre tillgängligt.",
"expiredError": "Det här dokument har nått sitt makuleringsdatum och är inte längre tillgängligt.",
"expiredError": "Det här dokument har nått sitt utgångsdatum och är inte längre tillgängligt.",
"padNotPinnedVariable": "Detta dokument kommer gå ut efter {4} dagars inaktivitet, {0}logga in{1} eller {2}registrera dig{3} för att bevara det.",
"padNotPinned": "Det här dokument kommer automatiskt gå ut efter 3 månader av inaktivitet, {0}logga in{1} eller {2}registrera dig{3} för att bevara det.",
"onLogout": "Du är utloggad, {0}klicka här{1} för att logga in<br>eller tryck Esc för att öppna ditt dokument i skrivskyddat läge.",
"typeError": "Det här dokument är inte kompatibelt med vald applikationen",
"typeError": "Det här dokumentet är inte kompatibelt med den valda applikationen",
"kanban_working": "Pågående",
"kanban_done": "Färdigt",
"kanban_todo": "Att göra",
@ -480,7 +480,7 @@
"settings_changePasswordCurrent": "Nuvarande lösenord",
"settings_changePasswordNew": "Nytt lösenord",
"features_f_core": "Vanliga funktioner",
"creation_expiration": "Makuleringsdatum",
"creation_expiration": "Förfallodatum",
"share_linkAccess": "Behörighetsinställningar",
"share_linkOpen": "Öppna länk",
"share_linkCopy": "Kopiera länk",
@ -755,7 +755,7 @@
"admin_support_closed": "Avslutade ärenden:",
"form_makeAnon": "Anonymisera svaren",
"team_exportButton": "Ladda ned",
"creation_expiresIn": "Makulera om",
"creation_expiresIn": "Förfaller om",
"footer_website": "Projektets webbplats",
"form_condition_isnot": "är inte",
"creation_expire": "Utgående dokument",
@ -986,7 +986,7 @@
"history_restoreDrivePrompt": "Är du säker på att du vill ersätta den nuvarande versionen av CryptDrive med den här versionen?",
"infobar_versionHash": "Du visar för närvarande en tidigare version av detta dokument ({0}).",
"oo_deletedVersion": "Denna version finns inte längre i historien.",
"admin_registrationHint": "Tillåt inga nya användare att registrera sig",
"admin_registrationHint": "Besökare till instansen har inte möjlighet att skapa konton. Inbjudningar kan skapas av administratörer.",
"admin_defaultlimitTitle": "Lagringsgräns (MB)",
"error_unhelpfulScriptError": "Skriptfel: Se webbläsarkonsolen för mer information",
"pad_settings_comments": "Välj om kommentarerna ska vara synliga eller dolda som standard.",
@ -1122,7 +1122,7 @@
"fm_info_root": "Skapa så många mappar här som du vill för att sortera dina filer.",
"fm_info_template": "Dessa dokument lagras som mallar. De kan återanvändas när du skapar nya dokument.",
"fm_info_trash": "Töm papperskorgen för att frigöra utrymme i din CryptDrive.",
"admin_flushCacheHint": "Tvinga användare att ladda ner de senaste tillgångarna på klientsidan (endast om din server är i uppdaterat läge)",
"admin_flushCacheHint": "Tvinga alla användare att ladda ner de senaste tillgångarna efter ändring i anpassningar eller inställningar. Detta undviker omstart av servern men tvingar varje aktiv användare att återansluta, använd sparsamt.",
"contact_admin": "Kontakta administratörerna för: {0}",
"contact_adminHint": "För eventuella problem relaterade till ditt konto, lagringsgräns eller tillgänglighet av tjänsten.\n",
"owner_addConfirm": "Delägare kommer att kunna ändra innehållet och ta bort dig som ägare. Är du säker?",
@ -1334,7 +1334,7 @@
"settings_importConfirm": "Är du säker på att du vill importera de senaste dokumenten från den här webbläsaren till ditt användarkontos CryptDrive?",
"settings_changePasswordHint": "Ändra ditt kontos lösenord. Ange ditt nuvarande lösenord och bekräfta det nya lösenordet genom att skriva det två gånger.<br><b>Vi kan inte återställa ditt lösenord om du glömmer det, så var mycket försiktig!</b>",
"creation_newPadModalDescription": "Klicka på en app för att skapa ett nytt dokument. Du kan också trycka på <b>Tabb</b> för att välja appen och trycka på <b>Retur</b> för att bekräfta.",
"settings_autostoreTitle": "Pad lagring i CryptDrive",
"settings_autostoreTitle": "Dokument lagring i CryptDrive",
"settings_changePasswordNewConfirm": "Bekräfta nytt lösenord",
"properties_addPassword": "Lägg till ett lösenord",
"admin_supportInitHint": "Du kan konfigurera en supportbrevlåda för att ge användare av din CryptPad-instans ett sätt att kontakta dig säkert om de har problem med sitt konto.",
@ -1665,5 +1665,20 @@
"access_passwordUsed": "Lösenordet har använts tidigare för det här dokumentet. Det kan inte återanvändas.",
"status": "Status-sida",
"admin_diskUsageWarning": "Använd med försiktighet! Beroende på storleken på de data som lagras på instansen kan generering av denna rapport använda allt tillgängligt minne på servern och leda till en krasch.",
"dph_pad_pw": "Det här dokumentet skyddas med ett nytt lösenord"
"dph_pad_pw": "Det här dokumentet skyddas med ett nytt lösenord",
"calendar_rec_change_first": "Flyttar den första återkommande händelsen till en annan kalender. Samtliga återkommande händelser kommer också att flyttas.",
"admin_forcemfaTitle": "Obligatorisk Två-Faktor Autentisering",
"calendar_desc": "Beskrivning",
"calendar_description": "Beskrivning:{0}{1}",
"sso_login_description": "Logga in med",
"sso_register_description": "Registrera med",
"ssoauth_form_hint_login": "Var vänlig ange ditt CryptPad lösenord",
"duplicate": "Duplicera",
"kanban_showTags": "Se alla taggar",
"kanban_hideTags": "Se färre taggar",
"ssoauth_header": "CryptPad Lösenord",
"admin_forcemfaHint": "Alla användare av den här instansen kommer att uppmanas att ställa in tvåfaktorsautentisering för att logga in på sitt konto. Notera att existerande användare inte kommer att kunna behålla sina konton utan att ställa in en TOTP-lösning.",
"ssoauth_form_hint_register": "Lägg till ett CryptPad-lösenord för extra säkerhet eller lämna tomt och fortsätt. Om du inte lägger till ett lösenord kommer nycklarna som skyddar din data att vara tillgänglig för instansadministratörerna.",
"calendar_rec_change": "Flyttar en återkommande händelse till en annan kalender. Du kan bara applicera förändringen till denna eller alla återkommande händelser.",
"admin_channelPlaceholder": "Förfallna dokument platshållare"
}

View file

@ -1,18 +0,0 @@
<!--
SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-->
<!DOCTYPE html>
<html>
<head>
<title data-localization="main_title">CryptPad: Collaboration suite, encrypted and open-source</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" href="/customize/favicon/main-favicon.png" id="favicon"/>
<script async data-bootload="/loadtest/main.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.5"></script>
</head>
<body class="html cp-page-load">
<noscript></noscript>

View file

@ -1,431 +0,0 @@
// SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and contributors
//
// SPDX-License-Identifier: AGPL-3.0-or-later
define([
'/api/config',
'jquery',
'netflux-client',
'/common/hyperscript.js',
'/common/common-hash.js',
'/common/common-util.js',
'/common/common-interface.js',
'/common/outer/network-config.js',
'/components/nthen/index.js',
'/components/saferphore/index.js',
'/components/tweetnacl/nacl-fast.min.js',
'less!/customize/src/less2/pages/page-load.less',
'css!/components/components-font-awesome/css/font-awesome.min.css',
], function (Config, $, Netflux, h, Hash, Util, UI, NetConfig, nThen, Saferphore) {
const wsUrl = NetConfig.getWebsocketURL();
const nacl = window.nacl;
let makeNetwork = function (cb) {
Netflux.connect(wsUrl).then(function (network) {
cb(null, network);
}, function (err) {
cb(err);
});
};
let Env = {
users: {},
channels: {},
queries: 0,
lag: [],
errors: 0
};
let startSendDataEvt = Util.mkEvent(true);
let hk;
let edPublic = "gH12mjdXc1hGsVMtCJeoGTkBQRA21V0VOEGphoddmPM=";
let edPrivate = "5V0tO8q1wKr62KIJadYdXaXvgG8f6FQtS6XHYrHYLzGAfXaaN1dzWEaxUy0Il6gZOQFBEDbVXRU4QamGh12Y8w==";
let hash = "/2/undefined/edit/UCrOzk5XEOP7qi"; // missing 10 characters
let makeHash = (id) => {
let l = String(id).length;
let add = 10 - l;
let str = String(id);
for(let i=0; i<add; i++) {
str = 'x' + str;
}
let _hash = hash + str + '/';
return _hash;
};
let getMsg = isCp => {
let base = nacl.util.encodeBase64(nacl.randomBytes(30));
let repeat = isCp ? 300 : 5;
let str = base;
for (let i = 0; i < repeat; i++) {
str += base;
}
return str;
};
let signMsg = (isCp, secret) => {
let msg = getMsg(isCp);
let signKey = nacl.util.decodeBase64(secret.keys.signKey);
let signed = nacl.util.encodeBase64(nacl.sign(nacl.util.decodeUTF8(msg), signKey));
if (!isCp) { return signed; }
let id = msg.slice(0,8);
return `cp|${id}|${signed}`;
};
let makeData = function (id, cb) {
let user = Env.users[id];
if (!user || !user.wc || !user.secret || !user.isEmpty) {
return void setTimeout(cb);
}
let n = nThen;
for (let i = 1; i<=130; i++) {
n = n(w => {
let m = signMsg(!(i%50), user.secret);
user.wc.bcast(m).then(w());
}).nThen;
}
n(() => {
cb();
});
};
let clearDataCmd = max => {
let cmd = 'Run the following commands to clear all the data\n';
cmd += 'rm ';
for (let i=0; i<max; i++) {
let hash = makeHash(i);
let secret = Hash.getSecrets('pad', hash);
let chan = secret.channel;
cmd += `${chan.slice(0,2)}/${chan}* `;
}
console.error(cmd);
};
let joinChan = (user, id, cb) => {
if (!user || !user.network) { return; }
let network = user.network;
let hash = makeHash(id);
let secret = Hash.getSecrets('pad', hash);
if (!user.hash && !user.secret) {
user.hash = hash;
user.secret = secret;
}
user.isEmpty = true; // Only used with the makeData button
let chan = Env.channels[secret.channel] = Env.channels[secret.channel] || {
secret: secret,
};
let n = 0;
network.on('message', (msg, sender) => {
if (sender !== hk) { return; }
let parsed = JSON.parse(msg);
if (parsed.state === 1 && parsed.channel === secret.channel) {
chan.total = n; // %50 to know if we should make a cp
return void cb();
}
let m = parsed[4];
if (parsed[3] !== secret.channel) { return; }
if (!m) { return; }
n++;
user.isEmpty = false;
});
network.join(secret.channel).then(wc => {
user.wc = wc;
if (!hk) {
wc.members.forEach(function (p) {
if (p.length === 16) { hk = p; }
});
}
let cfg = {
metadata: {
validateKey: secret.keys.validateKey,
owners: [edPublic]
}
};
let msg = ['GET_HISTORY', wc.id, cfg];
network.sendto(hk, JSON.stringify(msg));
});
};
// TODO
// Connect many websockets and have them run tasks
// * [x] JOIN with 10 users per pad
// * [x] GET_HISTORY
// * [x] SEND content at random intervals
// * UPLOAD random blobs
// * RPC commands?
let setRandomInterval = f => {
let delay = (Env.delay - 300)*2;
let rdm = 300 + Math.floor(delay * Math.random());
if (Env.stopPatch) { return; }
setTimeout(function () {
f();
setRandomInterval(f);
}, rdm);
};
let startOneUser = function (i, init, cb) {
let network;
let myPads = [i];
let me;
nThen(w => {
makeNetwork(w((err, _network) => {
if (err) {
w.abort();
return void console.error(err);
}
network = _network;
me = Env.users[i] = {
network: network,
myPads
};
}));
}).nThen(w => {
joinChan(me, i, w());
}).nThen(w => {
if (!init) { return; }
makeData(i, w());
}).nThen(w => {
if (init) { return; }
console.warn(i, me.secret.channel);
let min = Math.max(Env.offset, i-5); // XXX 5 users per pad
for (let j = min; j<i; j++) {
myPads.push(j);
joinChan(me, j, w());
}
}).nThen(w => {
if (init) { return; }
myPads.forEach(function (id) {
let channel = (Env.users[id] && Env.users[id].secret) ? Env.users[id].secret.channel : null;
if (channel==null) {
console.log("Channel " + id + " is null");
} else {
let wc = me.network.webChannels.find(obj => {
return obj.id === channel;
});
let chanObj = Env.channels[channel] || {};
// Only fill the chan if it is not originally empty
if (Env.users[id].isEmpty) { return; }
startSendDataEvt.reg(function () {
setRandomInterval(function () {
let i = chanObj.total || 0;
let m = signMsg(!(i%50), chanObj.secret);
console.log('Send patch', channel, i%50);
chanObj.total = i+1;
Env.incQueries();
let t = +new Date();
wc.bcast(m).then(() => {
let now = +new Date();
Env.lag.push((now - t));
}, err => {
Env.errors++;
console.error(err);
});
});
});
}
});
}).nThen(w => {
// TODO
// RPC commands? Upload blob?
}).nThen(w => {
cb();
});
};
let start = function (cb) {
clearDataCmd(Env.numberUsers);
var sem = Saferphore.create(20);
let max = Env.numberUsers + Env.offset;
nThen(w => {
for (let i=Env.offset; i<max; i++) {
let done = w();
sem.take(function(give) {
console.log('loading user ', i);
startOneUser(i, false, () => {
setTimeout(give(() => {
done();
}));
console.log('loaded user ', i);
});
});
}
}).nThen(() => {
cb();
});
};
let makeAllData = function (cb) {
var sem = Saferphore.create(10);
let max = Env.numberUsers + Env.offset;
nThen(w => {
for (let i=Env.offset; i<max; i++) {
let done = w();
sem.take(function(give) {
console.log('loading user ', i);
startOneUser(i, true, () => {
setTimeout(give(() => {
done();
}));
console.log('loaded user ', i);
});
});
}
}).nThen(() => {
cb();
});
};
$(function () {
let input = h('input', {type:'number',value:100,min:1, step:1});
let label = h('label', [
h('span', 'Number of users'),
input
]);
let inputOff = h('input', {type:'number',value:0,min:0, step:1});
let labelOff = h('label', [
h('span', 'User offset'),
inputOff
]);
let inputFreq = h('input', {type:'number',value:800,min:300, step:1});
let labelFreq = h('label', [
h('span', 'Average time between patches (ms) per user per channel'),
inputFreq
]);
let inputMax = h('input', {type:'number',value:0,min:0, step:1});
let labelMax = h('label', [
h('span', 'Max queries (0 for infinite)'),
inputMax
]);
let queries = h('span');
let freq = h('span');
let freqr = h('span');
let time = h('span');
let lag = h('span');
let errors = h('span');
let res = h('div', [
queries,
h('br'),
time,
h('br'),
freq,
h('br'),
freqr,
h('br'),
lag,
h('br'),
errors
]);
let button = h('button.btn.btn-primary', 'Start load testing');
let buttonPatch = h('button.btn.btn-primary', {style:'display:none;'}, 'Start sending patches');
let buttonStopPatch = h('button.btn.btn-danger-alt', {style:'display:none;'}, 'STOP sending patches');
let buttonData = h('button.btn', 'Create data');
var spinner = UI.makeSpinner();
let content = h('div', [
h('div.form', [
label,
labelOff,
labelFreq,
labelMax,
h('nav', [button, buttonPatch, buttonStopPatch, buttonData, spinner.spinner]),
res
])
]);
Env.incQueries = () => {
Env.queries++;
if (Env.maxQ && Env.queries >= Env.maxQ) {
Env.stopPatch = true;
$(buttonStopPatch).click();
}
};
let started = false;
$(button).click(() => {
if (started) { return; }
spinner.spin();
started = true;
//$(button).remove();
let users = Env.numberUsers = Number($(input).val());
Env.offset = Number($(inputOff).val()) || 0;
Env.delay = Number($(inputFreq).val()) || 800;
Env.maxQ = Number($(inputMax).val()) || 0;
if (typeof(users) !== "number" || !users) {
return void console.error('Not a valid number');
}
$(buttonData).remove();
start(() => {
spinner.done();
started = false;
UI.log('READY: you can now start sending patches');
$(buttonPatch).show();
});
});
let qIt, fIt;
let last = {};
$(buttonPatch).click(() => {
startSendDataEvt.fire();
$(buttonPatch).remove();
$(buttonStopPatch).show();
Env.start = +new Date();
last.t = +new Date();
last.q = 0;
qIt = setInterval(() => {
$(queries).text('Queries: '+Env.queries);
let q = Env.queries;
let now = +new Date();
let diffTime = (now-Env.start)/1000;
let f = Math.floor(q/diffTime);
const average = Math.round((Env.lag.length && Env.lag.reduce((a, b) => a + b, 0) / Env.lag.length)) || 0;
$(freq).text('Queries/s (all): '+f);
$(time).text('Time: '+Math.floor(diffTime)+'s');
$(lag).text('Avg response time: '+average+'ms');
$(errors).text('Errors: '+Env.errors);
Env.lag = [];
}, 200);
fIt = setInterval(() => {
let q = Env.queries;
let now = +new Date();
let fr = Math.floor(1000*(Env.queries-last.q)/(now-last.t));
last.t = +new Date();
last.q = q;
$(freqr).text('Queries/s (recent): '+fr);
}, 1000);
});
$(buttonStopPatch).click(() => {
Env.stopPatch = true;
clearInterval(qIt);
clearInterval(fIt);
$(buttonStopPatch).remove();
});
let startedData = false;
$(buttonData).click(() => {
if (startedData) { return; }
startedData = true;
spinner.spin();
let users = Env.numberUsers = Number($(input).val());
Env.offset = Number($(inputOff).val()) || 0;
Env.delay = Number($(inputFreq).val()) || 800;
Env.maxQ = Number($(inputMax).val()) || 0;
if (typeof(users) !== "number" || !users) {
return void console.error('Not a valid number');
}
$(button).remove();
$(buttonData).remove();
makeAllData(() => {
spinner.done();
UI.log('DONE');
});
});
$('body').append(content);
});
});