Merge branch 'staging' into workerapimerge

This commit is contained in:
yflory 2025-01-28 11:41:25 +01:00
commit 173d6941ea
49 changed files with 1780 additions and 995 deletions

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
node_modules/
www/components/
www/bower_components/
www/common/onlyoffice/dist
www/common/onlyoffice/x2t
onlyoffice-dist/
www/scratch
www/accounts
www/lib
www/accounts
www/worker
www/todo
#lib/plugins/
www/common/hyperscript.js
www/pad/wysiwygarea-plugin.js
www/pad/mediatag-plugin.js
www/pad/mediatag-plugin-dialog.js
www/pad/disable-base64.js
www/pad/wordcount/
www/kanban/jkanban.js
www/common/jscolor.js
www/common/media-tag-nacl.min.js
customize/
www/debug/chainpad.dist.js
www/pad/mathjax/
www/code/mermaid*.js
www/code/orgmode.js
www/common/worker.bundle.js
src/tweetnacl/
_build/

View File

@ -1,58 +0,0 @@
// SPDX-FileCopyrightText: 2024 XWiki CryptPad Team <contact@cryptpad.org> and contributors
//
// SPDX-License-Identifier: AGPL-3.0-or-later
module.exports = {
'env': {
'browser': true,
'es2021': true,
'node': true
},
'plugins': ['compat'],
'extends': ['eslint:recommended', 'plugin:compat/recommended'],
"globals": {
"define": "readonly",
},
'overrides': [
{
'env': {
'node': true
},
'files': [
'.eslintrc.{js,cjs}'
],
'parserOptions': {
'sourceType': 'script'
}
}
],
'parserOptions': {
'ecmaVersion': 'latest'
},
'rules': {
'indent': [
'off', // TODO enable this check
4
],
'linebreak-style': [
'off', // git handles linebreak conversion for us
'unix'
],
'quotes': [
'off', // TODO enable this check
'single'
],
'semi': [
'error',
'always'
],
// TODO remove these exceptions from the eslint defaults
'no-irregular-whitespace': ['off'],
'no-self-assign': ['off'],
'no-empty': ['off'],
'no-useless-escape': ['off'],
'no-extra-boolean-cast': ['off'],
'no-prototype-builtins': ['off'],
}
};

View File

@ -89,21 +89,13 @@ body:
label: Version
description: What version of CryptPad are you running?
options:
- 2024.12.0
- 2024.9.1
- 2024.9.0
- 2024.6.1
- 2024.6.0
- 2024.3.1
- 2024.3.0
- 5.7.0
- 5.6.0
- 5.5.0
- 5.4.1
- 5.4.0
- 5.3.0
- 5.2.1
- 5.2.0
- 5.1.0
- 5.0.0
- Other
validations:
required: true

View File

@ -135,7 +135,6 @@ define(req, function(AppConfig, Default, Language) {
return text;
}
};
return Messages;
});

View File

@ -69,7 +69,7 @@
outline: none;
width: 700px;
max-width: 90vw;
height: 500px;
//height: 500px;
border-radius: @variables_radius_L;
max-height: ~"calc(100vh - 20px)";
margin: 0px;
@ -85,6 +85,7 @@
}
.cp-creation-checkboxes {
min-width: 300px;
flex-flow: column;
align-items: baseline !important;
max-height: 150px;
@ -156,11 +157,10 @@
flex: 1 0 auto;
justify-content: space-around;
& > div {
width: 300px;
//width: 300px;
max-width: 100%;
display: flex;
align-items: center;
flex-wrap: wrap;
font-size: 16px;
//margin: 10px 0;
min-height: 28px;
@ -293,6 +293,11 @@
}
}
}
.cp-creation-password-warning {
font-size: 0.75em;
line-height: 120%;
margin: 0.4rem 1rem calc(0.4rem + 6px);
}
.cp-creation-settings {
button {
margin: 0;
@ -421,7 +426,6 @@
#cp-creation-form {
& > div {
width: 95%;
margin: 0 auto;
}
.cp-creation-expire {
&.active {
@ -431,7 +435,6 @@
.cp-creation-slider {
flex: none;
order: 10;
width: 100%;
}
}
}
@ -440,7 +443,7 @@
}
@media screen and (max-width: 800px) {
#cp-creation {
height: 550px;
//height: 550px;
#cp-creation-form {
div.cp-creation-template {
flex-flow: column;

View File

@ -160,12 +160,12 @@
li[role="menuitem"] {
border-radius: @variables_radius;
white-space: nowrap;
&:hover {
background-color: @cp_dropdown-bg-hover !important;
}
&:focus-visible {
outline-color: @cp_dropdown-fg;
}
&:hover {
background-color: @cp_dropdown-bg-hover;
}
}
&> span {
box-sizing: border-box;

View File

@ -97,6 +97,26 @@
margin: 0;
}
}
.cp-dropdown-container .cp-dropdown-content {
li[role="menuitem"]{
.cp-notification-dismiss:hover {
border-radius: @variables_radius;
background-color: @cp_dropdown-bg-hover;
}
}
li[role="menuitem"].cp-notification-avatar {
&:hover {
background-color: transparent;
}
.cp-avatar:hover {
background-color: @cp_dropdown-bg-hover;
}
.cp-notification-content:hover {
border-radius: @variables_radius;
background-color: @cp_dropdown-bg-hover;
}
}
}
}

View File

@ -734,10 +734,6 @@
color: inherit;
}
}
.cp-notifications-empty {
color: @cp_dropdown-fg;
padding: 5px;
}
button {
position: relative;
.cp-dropdown-button-title {
@ -765,6 +761,10 @@
margin: 0 -5px;
padding: 0;
}
.cp-notifications-empty {
color: @cp_dropdown-fg;
padding: 0.3em;
}
}
}
.cp-toolbar-link {

View File

@ -5,7 +5,7 @@
---
services:
cryptpad:
image: "cryptpad/cryptpad:version-2024.9.0"
image: "cryptpad/cryptpad:version-2024.12.0"
hostname: cryptpad
environment:

View File

@ -100,7 +100,7 @@ server {
resolver 8.8.8.8 8.8.4.4 1.1.1.1 1.0.0.1 9.9.9.9 149.112.112.112 208.67.222.222 208.67.220.220;
# OnlyOffice fonts may be loaded from both domains
if ($uri ~ ^\/common\/onlyoffice\/.*\/fonts\/.*$) { set $allowed_origins "*"; }
if ($uri ~ ^/common/onlyoffice/.*/fonts/) { set $allowed_origins "*"; }
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options nosniff;
@ -128,7 +128,7 @@ server {
# We had inverted them as an optimization, but Safari 16 introduced a bug that interpreted
# some important headers incorrectly when loading these files from cache.
# This is why we can't have nice things :(
if ($uri ~ ^(\/|.*\/|.*\.html)$) {
if ($uri ~ ^(?:/|.*/|.*\.html)$) {
set $cacheControl no-cache;
}
@ -177,8 +177,8 @@ server {
set $unsafe 0;
# the following assets are loaded via the sandbox domain
# they unfortunately still require exceptions to the sandboxing to work correctly.
if ($uri ~ ^\/(sheet|doc|presentation)\/inner.html.*$) { set $unsafe 1; }
if ($uri ~ ^\/common\/onlyoffice\/.*\/.*\.html.*$) { set $unsafe 1; }
if ($uri ~ ^/(?:sheet|doc|presentation)/inner.html) { set $unsafe 1; }
if ($uri ~ ^/common/onlyoffice/.*/.*\.html) { set $unsafe 1; }
# everything except the sandbox domain is a privileged scope, as they might be used to handle keys
if ($host != $sandbox_domain) { set $unsafe 0; }
@ -186,7 +186,7 @@ server {
# because of bugs in Chromium-based browsers that incorrectly ignore headers that are supposed to enable
# the use of some modern APIs that we require when javascript is run in a cross-origin context.
# We've applied other sandboxing techniques to mitigate the risk of running WebAssembly in this privileged scope
if ($uri ~ ^\/unsafeiframe\/inner\.html.*$) { set $unsafe 1; }
if ($uri ~ ^/unsafeiframe/inner\.html) { set $unsafe 1; }
# privileged contexts allow a few more rights than unprivileged contexts, though limits are still applied
if ($unsafe) {
@ -235,7 +235,7 @@ server {
# /api/config is loaded once per page load and is used to retrieve
# the caching variable which is applied to every other resource
# which is loaded during that session.
location ~ ^/api/.*$ {
location ^~ /api/ {
proxy_pass http://localhost:3000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
@ -249,7 +249,7 @@ server {
add_header Cross-Origin-Embedder-Policy require-corp;
}
location ~ ^/extensions.js {
location = /extensions.js/ {
proxy_pass http://localhost:3000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
@ -268,7 +268,7 @@ server {
# or with odd unexpected permissions. Serving blobs in this manner also means that it will be possible to
# enforce access control for them, though this is not yet implemented.
# Access control (via TOTP 2FA) has been added to blocks, so they can be handled with the same directives.
location ~ ^/(blob|block)/.*$ {
location ~ ^/(?:blob|block)/ {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' "${allowed_origins}";
add_header 'Access-Control-Allow-Credentials' true;
@ -293,8 +293,8 @@ server {
# The nodejs server has some built-in forwarding rules to prevent
# URLs like /pad from resulting in a 404. This simply adds a trailing slash
# to a variety of applications.
location ~ ^/(register|login|recovery|settings|user|pad|drive|poll|slide|code|whiteboard|file|media|profile|contacts|todo|filepicker|debug|kanban|sheet|support|admin|notifications|teams|calendar|presentation|doc|form|report|convert|checkup|diagram)$ {
rewrite ^(.*)$ $1/ redirect;
location ~ ^/(?:register|login|recovery|settings|user|pad|drive|poll|slide|code|whiteboard|file|media|profile|contacts|todo|filepicker|debug|kanban|sheet|support|admin|notifications|teams|calendar|presentation|doc|form|report|convert|checkup|diagram)$ {
return 301 https://$host$uri/;
}
# Finally, serve anything the above exceptions don't govern.

96
eslint.config.js Normal file
View File

@ -0,0 +1,96 @@
// SPDX-FileCopyrightText: 2024 XWiki CryptPad Team <contact@cryptpad.org> and contributors
//
// SPDX-License-Identifier: AGPL-3.0-or-later
const compatPlugin = require("eslint-plugin-compat");
const globals = require("globals");
const js = require("@eslint/js");
const FlatCompat = require("@eslint/eslintrc").FlatCompat;
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all
});
module.exports = [{
ignores: [
"**/node_modules/",
"www/components/",
"www/bower_components/",
"www/common/onlyoffice/dist",
"www/common/onlyoffice/x2t",
"**/onlyoffice-dist/",
"www/scratch",
"www/accounts",
"www/lib",
"www/accounts",
"www/worker",
"www/todo",
"**/*worker.bundle.js",
"**/_build",
"www/common/hyperscript.js",
"www/pad/wysiwygarea-plugin.js",
"www/pad/mediatag-plugin.js",
"www/pad/mediatag-plugin-dialog.js",
"www/pad/disable-base64.js",
"www/pad/wordcount/",
"www/kanban/jkanban.js",
"www/common/jscolor.js",
"www/common/media-tag-nacl.min.js",
"**/customize/",
"www/debug/chainpad.dist.js",
"www/pad/mathjax/",
"www/code/mermaid*.js",
"www/code/orgmode.js",
"www/common/worker.bundle.js",
"src/tweetnacl",
"_build",
],
}, ...compat.extends("eslint:recommended", "plugin:compat/recommended"), {
plugins: {
compatPlugin,
},
languageOptions: {
globals: {
...globals.browser,
...globals.node,
define: "readonly",
},
ecmaVersion: "latest",
sourceType: "commonjs",
},
rules: {
indent: ["off", 4],
"linebreak-style": ["off", "unix"],
quotes: ["off", "single"],
semi: ["error", "always"],
"no-irregular-whitespace": ["off"],
"no-self-assign": ["off"],
"no-empty": ["off"],
"no-useless-escape": ["off"],
"no-extra-boolean-cast": ["off"],
"no-prototype-builtins": ["off"],
"no-use-before-define": ["error"],
"no-unused-vars": [
"error",
{
caughtErrors: "none"
}
]
},
}, {
files: ["**/.eslintrc.{js,cjs}"],
languageOptions: {
globals: {
...globals.node,
},
ecmaVersion: 5,
sourceType: "commonjs",
},
}];

View File

@ -669,6 +669,7 @@ module.exports = function (Env, cb) {
if (item.mtime > inactiveTime) { return void next(); }
nThen(function (w) {
blobs.size(item.blobId, w(function (err, size) {
if (err && err === 'ENOENT') { return; } // XXX delete the proof
if (err) {
w.abort();
return void Log.error("EVICT_BLOB_LIST_PROOFS_ERROR", err, next);

794
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"name": "cryptpad",
"description": "a collaborative office suite that is end-to-end encrypted and open-source",
"version": "2024.9.1",
"version": "2024.12.0",
"license": "AGPL-3.0+",
"repository": {
"type": "git",
@ -30,7 +30,7 @@
"croppie": "^2.5.0",
"dragula": "3.7.2",
"drawio": "github:cryptpad/drawio-npm#npm-21.8.2+5",
"express": "~4.21.1",
"express": "~4.21.2",
"file-saver": "1.3.1",
"fs-extra": "^7.0.0",
"get-folder-size": "^2.0.1",
@ -38,7 +38,7 @@
"http-proxy-middleware": "^3.0.3",
"hyper-json": "~1.4.0",
"jquery": "3.6.0",
"json.sortify": "~2.1.0",
"json.sortify": "github:cryptpad/JSON.sortify",
"jsonwebtoken": "^9.0.0",
"jszip": "3.10.1",
"localforage": "^1.5.2",
@ -72,8 +72,8 @@
"@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.0",
"eslint": "^8.57.0",
"eslint-plugin-compat": "^4.2.0",
"eslint": "^9.16.0",
"eslint-plugin-compat": "^6.0.1",
"rollup": "^4.24.0",
"stylelint": "^16.9.0",
"stylelint-config-standard-less": "^3.0.1",

View File

@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
# CryptPad
CryptPad is a collaboration suite that is end-to-end-encrypted and open-source. It is built to enable collaboration, synchronizing changes to documents in real time. Because all data are encrypted, in the eventuality of a breach, attackers have no way of seeing the stored content. Moreover, if the administrators dont alter the code, they and the service also cannot infer any piece of information about the users' content.
CryptPad is a collaboration suite that is end-to-end encrypted and open-source. It is designed to facilitate collaboration by synchronizing changes to documents in real time. Since all the user data is encrypted, in the event of a breach, attackers have no way of accessing the stored content. Furthermore, if the administrators do not modify the code, they and the service also cannot access any information about the users' content.
![Drive screenshot](screenshot.png "preview of the CryptDrive")
@ -20,7 +20,7 @@ Our [developer guide](https://docs.cryptpad.org/en/dev_guide/setup.html) provide
## For production
Configuring CryptPad for production requires a little more work, but the process is described in our [admin installation guide](https://docs.cryptpad.org/en/admin_guide/installation.html). From there you can find more information about customization and maintenance.
Configuring CryptPad for production requires additional steps. Refer to our [admin installation guide](https://docs.cryptpad.org/en/admin_guide/installation.html) for production-related instructions, customization, and maintenance details.
## Current version

View File

@ -592,7 +592,7 @@ const factory = (Util, Hash, Constants, Realtime, Cache, Rec,
ctx.clients.push(cId);
}
cb({
empty: !Object.keys(ctx.calendars).length
length: Object.keys(ctx.calendars).length
});
Object.keys(ctx.calendars).forEach(function (channel) {
var c = ctx.calendars[channel] || {};

View File

@ -98,6 +98,7 @@ const factory = (ApiConfig = {}, Util, Hash, Realtime, Pinpad, Crypt,
msg = JSON.parse(msg);
} catch (e) {
console.error(e);
return; // Don't show "undefined" messages
}
msg.time = time;
if (author) { msg.author = author; }
@ -226,6 +227,7 @@ const factory = (ApiConfig = {}, Util, Hash, Realtime, Pinpad, Crypt,
var anonRpc = Util.find(ctx, [ 'store', 'anon_rpc' ]);
if (!mailbox) { return void cb('E_NOT_READY'); }
if (!anonRpc) { return void cb("anonymous rpc session not ready"); }
if (!data?.ticket) { return void cb('E_NO_DATA'); }
var theirPublic, myCurve, notifKey;
var time;
nThen((waitFor) => {
@ -525,6 +527,11 @@ const factory = (ApiConfig = {}, Util, Hash, Realtime, Pinpad, Crypt,
if (last.sender) {
entry.lastAdmin = !last.sender.blockLocation;
}
if (last.close) {
doc.tickets.closed[data.channel] = entry;
delete doc.tickets.active[data.channel];
notifyClient(ctx, true, 'UPDATE_TICKET', data.channel);
}
/*
let senderKey = last.sender && last.sender.edPublic;
if (senderKey) {
@ -583,7 +590,9 @@ const factory = (ApiConfig = {}, Util, Hash, Realtime, Pinpad, Crypt,
doc.tickets.closed[data.channel] = entry;
delete doc.tickets.active[data.channel];
delete doc.tickets.pending[data.channel];
cb({closed: true});
Realtime.whenRealtimeSyncs(ctx.adminDoc.realtime, function () {
cb({closed: true});
});
});
});
};

View File

@ -968,12 +968,7 @@ define([
});
};
let btn = blocks.activeButton('primary', '',
Messages.admin_colorChange, (done) => {
let color = $input.val();
setColor(color, done);
});
let $input = $();
let onColorPicked = () => {
require(['/lib/less.min.js'], (Less) => {
let color = $input.val();
@ -995,7 +990,14 @@ define([
$preview.find('.cp-admin-color-preview-light a').attr('style', `color: ${color} !important`);
});
};
let $input = $(input).on('change', onColorPicked).addClass('cp-admin-color-picker');
$input = $(input).on('change', onColorPicked).addClass('cp-admin-color-picker');
let btn = blocks.activeButton('primary', '',
Messages.admin_colorChange, (done) => {
let color = $input.val();
setColor(color, done);
});
UI.confirmButton($remove, {
classes: 'btn-danger',
@ -3185,6 +3187,7 @@ define([
labelEnd,
], blocks.nav([button]));
let send = function () {};
var refresh = getApi(function (Broadcast) {
$active.empty();
var removeButton = blocks.button('danger', '', Messages.admin_maintenanceCancel);
@ -3246,7 +3249,7 @@ define([
};
};
var send = function (data) {
send = function (data) {
disable($button);
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE',

View File

@ -599,6 +599,18 @@
margin-top: 30px;
}
}
@media screen and (max-width: @browser_media-medium-screen) {
.cp-calendar-entries {
display: none;
}
.cp-calendar-entries.visible {
display: block;
}
}
.cp-calendar-entries {
margin-bottom: 10px;
}
.cp-calendar-entry {
display: flex;
align-items: center;
@ -628,6 +640,7 @@
}
&.cp-ghost {
padding: 0;
margin-top: 1rem;
button {
.tools_unselectable();
cursor: pointer;

View File

@ -775,7 +775,7 @@ define([
var data = APP.calendars[id];
var edit;
if (data.loading) {
edit = h('i.fa.fa-spinner.fa-spin');
edit = h('i.fa.fa-spinner.fa-spin', {'aria-hidden': 'true'});
} else {
edit = makeEditDropdown(id, teamId);
}
@ -796,8 +796,8 @@ define([
h('i.cp-calendar-inactive.fa.fa-calendar-o')
]),
h('span.cp-calendar-title', md.title),
data.restricted ? h('i.fa.fa-ban', {title: Messages.fm_restricted}) :
(isReadOnly(id, teamId) ? h('i.fa.fa-eye', {title: Messages.readonly}) : undefined),
data.restricted ? h('i.fa.fa-ban', {title: Messages.fm_restricted, 'aria-hidden': 'true'}) :
(isReadOnly(id, teamId) ? h('i.fa.fa-eye', {title: Messages.readonly, 'aria-hidden': 'true'}) : undefined),
edit
]);
var $calendar = $(calendar).click(function () {
@ -820,10 +820,14 @@ define([
if (APP.$calendars) { APP.$calendars.append(calendar); }
return calendar;
};
var makeLeftside = function (calendar, $container) {
// Show calendars
var calendars = h('div.cp-calendar-list');
var $calendars = APP.$calendars = $(calendars).appendTo($container);
var isMobileView = window.innerWidth <= 600;
var visible = !isMobileView; // Initialize 'visible' state: true for large screens, false for mobile
onCalendarsUpdate.reg(function () {
$calendars.empty();
var privateData = metadataMgr.getPrivateData();
@ -831,7 +835,7 @@ define([
var LOOKUP = {};
return Object.keys(APP.calendars || {}).filter(function (id) {
var cal = APP.calendars[id] || {};
var teams = (cal.teams || []).map(function (tId) { return Number(tId); });
var teams = (cal.teams || []).map(function (tId) { return Number(tId); });
return teams.indexOf(typeof(teamId) !== "undefined" ? Number(teamId) : 1) !== -1;
}).map(function (k) {
// nearly constant-time pre-sort
@ -872,26 +876,33 @@ define([
}
return;
}
var myCalendars = filter(1);
var totalCalendars = myCalendars.length + Object.keys(privateData.teams).reduce((sum, teamId) => {
return sum + filter(teamId).length;
}, 0);
var $contentContainer = $(h('div.cp-calendar-content')).appendTo($calendars);
if (myCalendars.length) {
var user = metadataMgr.getUserData();
var avatar = h('span.cp-avatar');
var uid = user.uid;
var name = user.name || Messages.anonymous;
common.displayAvatar($(avatar), user.avatar, name, function(){}, uid);
APP.$calendars.append(h('div.cp-calendar-team', [
$contentContainer.append(h('div.cp-calendar-team', [
avatar,
h('span.cp-name', {title: name}, name)
]));
myCalendars.forEach((id) => {
var calendarEntry = makeCalendarEntry(id, 1);
$contentContainer.append(calendarEntry);
});
}
myCalendars.forEach(function (id) {
makeCalendarEntry(id, 1);
});
// Add new button
var $newContainer = $(h('div.cp-calendar-entry.cp-ghost')).appendTo($calendars);
// Add the new calendar button
var $newContainer = $(h('div.cp-calendar-entry.cp-ghost')).appendTo($contentContainer);
var newButton = h('button', [
h('i.fa.fa-calendar-plus-o'),
h('i.fa.fa-calendar-plus-o', {'aria-hidden': 'true'}),
h('span', Messages.calendar_new),
h('span')
]);
@ -905,19 +916,61 @@ define([
var team = privateData.teams[teamId];
var avatar = h('span.cp-avatar');
common.displayAvatar($(avatar), team.avatar, team.displayName || team.name);
APP.$calendars.append(h('div.cp-calendar-team', [
var $teamContainer = h('div.cp-calendar-team', [
avatar,
h('span.cp-name', {title: team.name}, team.name)
]));
calendars.forEach(function (id) {
makeCalendarEntry(id, teamId);
h('span.cp-name', {title: team.name}, team.name),
h('span')
]);
$contentContainer.append($teamContainer);
calendars.forEach((id) => {
var calendarEntry = makeCalendarEntry(id, teamId);
$contentContainer.append(calendarEntry);
});
});
if(isMobileView) {
// If initial number of calendars or current number > 2,
// hide the calendars list and display a "show" button
if (APP.numberCalendars > 2 || totalCalendars > 2) {
var $showContainer = $(h('div.cp-calendar-entry.cp-ghost')).appendTo($calendars);
var iconClass = visible ? 'fa-eye-slash' : 'fa-eye';
var buttonText = visible ? Messages.calendar_hide : Messages.calendar_show;
var showCalendarsBtn = h('button', [
h('i.fa.' + iconClass, {'aria-hidden': "true"}),
h('span.cp-calendar-title', buttonText),
h('span')
]);
$(showCalendarsBtn).click(() => {
visible = !visible;
$contentContainer.toggle(visible);
iconClass = visible ? 'fa-eye-slash' : 'fa-eye';
buttonText = visible ? Messages.calendar_hide : Messages.calendar_show;
$(showCalendarsBtn).find('i').attr('class', 'fa ' + iconClass).attr('aria-hidden', "true");
$(showCalendarsBtn).find('span').first().text(visible ? Messages.calendar_hide : Messages.calendar_show);
}).appendTo($showContainer);
}
else {visible = true;}
}
$contentContainer.toggle(visible);
$(window).resize(function () {
var newIsMobileView = window.innerWidth <= 600;
if (!newIsMobileView) {
visible = true;
$contentContainer.show();
}
if (newIsMobileView !== isMobileView) {
isMobileView = newIsMobileView;
if (isMobileView) {
visible = false;
$contentContainer.hide();
}
onCalendarsUpdate.fire();
}
});
});
onCalendarsUpdate.fire();
};
var _updateRecurring = function () {
var cal = APP.calendar;
if (!cal) { return; }
@ -1275,17 +1328,15 @@ ICS ==> create a new event with the same UID and a RECURRENCE-ID field (with a v
store.put('calendarView', mode, function () {});
});
APP.toolbar.$bottomR.append($block);
// New event button
var newEventBtn = h('button.cp-calendar-newevent', [
h('i.fa.fa-plus'),
h('i.fa.fa-plus', {'aria-hidden': 'true'}),
h('span', Messages.calendar_newEvent)
]);
$(newEventBtn).click(function (e) {
e.preventDefault();
cal.openCreationPopup({isAllDay:false});
}).appendTo(APP.toolbar.$bottomL);
// Change page
var goLeft = h('button.fa.fa-chevron-left',{'aria-label': Messages.goLeft});
var goRight = h('button.fa.fa-chevron-right', {'aria-label': Messages.goRight});
@ -2152,7 +2203,7 @@ APP.recurrenceRule = {
// Customize creation/update popup
var onCalendarPopup = function (el) {
var $el = $(el);
$el.find('.tui-full-calendar-confirm').addClass('btn btn-primary').prepend(h('i.fa.fa-floppy-o'));
$el.find('.tui-full-calendar-confirm').addClass('btn btn-primary').prepend(h('i.fa.fa-floppy-o', {'aria-hidden': 'true'}));
$el.find('input').attr('autocomplete', 'off');
$el.find('.tui-full-calendar-dropdown-button').addClass('btn btn-secondary');
$el.find('.tui-full-calendar-popup-close').addClass('btn btn-cancel fa fa-times cp-calendar-close').empty();
@ -2234,7 +2285,7 @@ APP.recurrenceRule = {
$el.find('.tui-full-calendar-content').removeClass('tui-full-calendar-content');
var delButton = h('button.btn.btn-danger', [
h('i.fa.fa-trash'),
h('i.fa.fa-trash', {'aria-hidden': 'true'}),
h('span', Messages.kanban_delete)
]);
var $del = $el.find('.tui-full-calendar-popup-delete').hide();
@ -2267,7 +2318,7 @@ APP.recurrenceRule = {
// This is a recurring event, add button to stop recurrence now
var $b = $(h('button.btn.btn-default', [
h('i.fa.fa-times'),
h('i.fa.fa-times', {'aria-hidden': 'true'}),
h('span', Messages.calendar_rec_stop)
])).insertBefore($section);
UI.confirmButton($b[0], { classes: 'btn-default' }, function () {
@ -2340,7 +2391,9 @@ APP.recurrenceRule = {
});
var store = window.cryptpadStore;
APP.module.execCommand('SUBSCRIBE', null, function (obj) {
if (obj.empty && !privateData.calendarHash) {
let empty = !obj.length;
APP.numberCalendars = obj.length;
if (empty && !privateData.calendarHash) {
if (!privateData.loggedIn) {
return void UI.errorLoadingScreen(Messages.mustLogin, false, function () {
common.setLoginRedirect('login');

View File

@ -133,7 +133,7 @@ const factory = () => {
// en: "Hello world",
// fr: "Bonjour le monde",
// de: "Hallo Welt",
// "pt-br": "Olá Mundo"<
// "pt-br": "Olá Mundo"
};
/* Cryptpad apps use a common API to display notifications to users

View File

@ -2525,6 +2525,7 @@ define([
window.setTimeout(function () {
modal.show();
$modal.focus();
next();
});
};
@ -2770,7 +2771,11 @@ define([
]);
// Password
var password = h('div.cp-creation-password', [
let text;
if (type === 'form') {
text = h('div.cp-creation-password-warning.alert.alert-info.dismissable', h('span.cp-inline-alert-text', Messages.form_passwordWarning));
}
var password = h('div.cp-creation-password', [
UI.createCheckbox('cp-creation-password', Messages.properties_addPassword, false),
h('span.cp-creation-password-picker.cp-creation-slider', [
UI.passwordInput({id: 'cp-creation-password-val'})
@ -2808,6 +2813,7 @@ define([
expire,
password,
]),
text,
templates,
createDiv
])).appendTo($creation);

View File

@ -930,13 +930,15 @@ define([
delete meta.cursor;
if (meta.type === "form") {
// Keep anonymous and makeAnonymous values from templates
// Keep anonymous, makeAnonymous and submit message values from templates
var anonymous = parsed.answers.anonymous || false;
var makeAnonymous = parsed.answers.makeAnonymous || false;
var msg = parsed.answers.msg || undefined;
delete parsed.answers;
parsed.answers = {
anonymous: anonymous,
makeAnonymous: makeAnonymous
makeAnonymous: makeAnonymous,
msg: msg
};
}
}

View File

@ -314,20 +314,29 @@ define([
};
APP.selectedFiles = [];
var findElementId = function ($element) {
var isTrashed = $element.data("path")[0] === TRASH;
let elementId;
if (isTrashed) {
elementId = $element.data("path").join(',');
} else {
elementId = $element.data("path").slice(-1)[0];
}
return elementId;
};
var isElementSelected = function ($element) {
var elementId = $element.data("path").slice(-1)[0];
var elementId = findElementId($element);
return APP.selectedFiles.indexOf(elementId) !== -1;
};
var selectElement = function ($element) {
var elementId = $element.data("path").slice(-1)[0];
var elementId = findElementId($element);
if (APP.selectedFiles.indexOf(elementId) === -1) {
APP.selectedFiles.push(elementId);
}
$element.addClass("cp-app-drive-element-selected");
};
var unselectElement = function ($element) {
var elementId = $element.data("path").slice(-1)[0];
var elementId = findElementId($element);
var index = APP.selectedFiles.indexOf(elementId);
if (index !== -1) {
APP.selectedFiles.splice(index, 1);
@ -990,6 +999,9 @@ define([
// Arrow keys to modify the selection
var onWindowKeydown = function (e) {
if (!$content.is(':visible')) { return; }
if ($('.cp-modal').is(':visible')) {
return;
}
var $searchBar = $tree.find('#cp-app-drive-tree-search-input');
if (document.activeElement && document.activeElement.nodeName === 'INPUT') { return; }
if ($searchBar.is(':focus') && $searchBar.val()) { return; }
@ -1459,7 +1471,6 @@ define([
}
});
if (paths.length > 1) {
hide.push('restore');
hide.push('properties', 'access');
hide.push('rename');
hide.push('openparent');
@ -2968,6 +2979,16 @@ define([
});
UI.openCustomModal(m);
};
var triggerEnter = function (selector, $context) {
$context.find(selector).on('keypress', function (event) {
if (event.which === 13) { // enter
event.preventDefault();
$(this).trigger('click'); // the keypress event triggers the click event
}
});
};
var addNewPadHandlers = function ($block, isInRoot) {
// Handlers
if (isInRoot) {
@ -2983,56 +3004,51 @@ define([
refresh();
};
$block.find('a.cp-app-drive-new-folder, li.cp-app-drive-new-folder')
.on('click keypress', function (event) {
if (event.type === 'click' || (event.type === 'keypress' && event.which === 13)) {
event.preventDefault();
manager.addFolder(currentPath, null, onCreated);
}
.on('click', function (event) {
event.preventDefault();
manager.addFolder(currentPath, null, onCreated);
});
triggerEnter('a.cp-app-drive-new-folder, li.cp-app-drive-new-folder', $block);
if (!APP.disableSF && !manager.isInSharedFolder(currentPath)) {
$block.find('a.cp-app-drive-new-shared-folder, li.cp-app-drive-new-shared-folder')
.on('click keypress', function (event) {
if (event.type === 'click' || (event.type === 'keypress' && event.which === 13)) {
event.preventDefault();
addSharedFolderModal(function (obj) {
if (!obj) { return; }
manager.addSharedFolder(currentPath, obj, refresh);
});
}
.on('click', function (event) {
event.preventDefault();
addSharedFolderModal(function (obj) {
if (!obj) { return; }
manager.addSharedFolder(currentPath, obj, refresh);
});
});
triggerEnter('a.cp-app-drive-new-shared-folder, li.cp-app-drive-new-shared-folder', $block);
}
$block.find('a.cp-app-drive-new-fileupload, li.cp-app-drive-new-fileupload')
.on('click keypress', function (event) {
if (event.type === 'click' || (event.type === 'keypress' && event.which === 13)) {
event.preventDefault();
showUploadFilesModal();
}
.on('click', function (event) {
event.preventDefault();
showUploadFilesModal();
});
triggerEnter('a.cp-app-drive-new-fileupload, li.cp-app-drive-new-fileupload', $block);
$block.find('a.cp-app-drive-new-folderupload, li.cp-app-drive-new-folderupload')
.on('click keypress', function (event) {
if (event.type === 'click' || (event.type === 'keypress' && event.which === 13)) {
event.preventDefault();
showUploadFolderModal();
}
.on('click', function (event) {
event.preventDefault();
showUploadFolderModal();
});
triggerEnter('a.cp-app-drive-new-folderupload, li.cp-app-drive-new-folderupload', $block);
$block.find('a.cp-app-drive-new-link, li.cp-app-drive-new-link')
.on('click keypress', function (event) {
if (event.type === 'click' || (event.type === 'keypress' && event.which === 13)) {
event.preventDefault();
showLinkModal();
}
.on('click', function (event) {
event.preventDefault();
showLinkModal();
});
triggerEnter('a.cp-app-drive-new-link, li.cp-app-drive-new-link', $block);
}
$block.find('a.cp-app-drive-new-doc, li.cp-app-drive-new-doc')
.on('click auxclick keypress', function (event) {
if (event.type === 'click' || event.type === 'auxclick' || (event.type === 'keypress' && event.which === 13))
{
.on('click auxclick', function (event) {
if (event.type === 'click' || event.type === 'auxclick') {
event.preventDefault();
var type = $(this).attr('data-type') || 'pad';
var path = manager.isPathIn(currentPath, [TRASH]) ? '' : currentPath;
openIn(type, path, APP.team);
}
});
triggerEnter('a.cp-app-drive-new-doc, li.cp-app-drive-new-doc', $block);
};
var getNewPadOptions = function (isInRoot) {
var options = [];
@ -5181,24 +5197,45 @@ define([
return void deletePaths(paths);
}
else if ($this.hasClass("cp-app-drive-context-restore")) {
if (paths.length !== 1) { return; }
var restorePath = paths[0].path;
var restoreName = paths[0].path[paths[0].path.length - 1];
if (restorePath.length === 4) {
var rEl = manager.find(restorePath);
if (manager.isFile(rEl)) {
restoreName = manager.getTitle(rEl);
} else if (manager.isSharedFolder(rEl)) {
var sfData = manager.getSharedFolderData(rEl);
restoreName = sfData.title || sfData.lastTitle || Messages.fm_deletedFolder;
} else {
restoreName = restorePath[1];
let getRestoreProperties = (path) => {
let restorePath = path;
let restoreName = path.at(-1);
if (restorePath.length === 4) {
let rEl = manager.find(restorePath);
if (manager.isFile(rEl)) {
restoreName = manager.getTitle(rEl);
} else if (manager.isSharedFolder(rEl)) {
let sfData = manager.getSharedFolderData(rEl);
restoreName = sfData.title || sfData.lastTitle || Messages.fm_deletedFolder;
} else {
restoreName = restorePath[1];
}
}
return [restorePath, restoreName];
};
let restoreNumber = paths.length;
if (restoreNumber === 0) { return; }
if (restoreNumber === 1) { // single file restoration
let [restorePath, restoreName] = getRestoreProperties(paths[0].path);
UI.confirm(Messages._getKey("fm_restoreDialog", [restoreName]), res => {
if (!res) { return; }
manager.restore(restorePath, refresh);
});
} else { // multiple files restoration
UI.confirm(Messages._getKey("fm_restoreMultipleDialog", [restoreNumber]), res => {
if (!res) { return; }
nThen(waitFor => {
paths.forEach(path => {
if (!path) { // We met an error
console.error("Error while restoring files: no path");
return;
}
let restorePath = getRestoreProperties(path.path)[0];
setTimeout(manager.restore(restorePath, waitFor()), 10);
});
}).nThen(refresh);
});
}
UI.confirm(Messages._getKey("fm_restoreDialog", [restoreName]), function(res) {
if (!res) { return; }
manager.restore(restorePath, refresh);
});
}
else if ($this.hasClass("cp-app-drive-context-openparent")) {
if (paths.length !== 1) { return; }

View File

@ -1108,7 +1108,7 @@ define([
var owned = Modal.isOwned(Env, data);
// Request edit access
if (common.isLoggedIn() && data.roHref && !owned && !opts.calendar && priv.app !== 'form') {
if (common.isLoggedIn() && data.roHref && !owned && !opts.calendar && priv.app !== 'form' && !data.href) {
var requestButton = h('button.btn.btn-secondary.no-margin.cp-access-margin-right',
Messages.requestEdit_button);
var requestBlock = h('p', requestButton);

View File

@ -49,7 +49,13 @@ define([
// Access modal and the pad is not stored: get the hashes from outer
var hashes = priv.hashes || {};
// For calendars, individual href is passed via opts
data.href = ((priv.app === 'calendar') && opts.href) || Hash.hashToHref(hashes.editHash || hashes.fileHash, priv.app);
if (priv.app === 'calendar') {
data.href = opts.href;
} else if (hashes.editHash || hashes.fileHash) {
data.href = Hash.hashToHref(hashes.editHash || hashes.fileHash, priv.app);
} else {
data.href = undefined;
}
if (hashes.viewHash) {
data.roHref = Hash.hashToHref(hashes.viewHash, priv.app);
}

View File

@ -169,10 +169,17 @@ define([
data: fData
});
}
var href = (fData.href && fData.href.indexOf('#') !== -1) ? fData.href : fData.roHref;
var parsed = Hash.parsePadUrl(href);
if (['pad', 'file'].indexOf(parsed.hashData.type) === -1) { return; }
var href;
var parsed;
if (!fData.channel) {
href = fData.href;
parsed = {};
parsed['hashData'] = {type: 'link'};
} else {
href = (fData.href && fData.href.indexOf('#') !== -1) ? fData.href : fData.roHref;
parsed = Hash.parsePadUrl(href);
}
if (['pad', 'file', 'link'].indexOf(parsed.hashData.type) === -1) { return; }
// waitFor is used to make sure all the pads and files are process before downloading the zip.
var w = ctx.waitFor();
@ -220,7 +227,7 @@ define([
var opts = {
password: fData.password
};
var rawName = fData.filename || fData.title || 'File';
var rawName = fData.filename || fData.title || fData.name || 'File';
console.log(rawName);
// Pads (pad,code,slide,kanban,poll,...)
@ -276,8 +283,21 @@ define([
}
}, 50);
};
var todoLink = function () {
var opts = {
binary: true,
};
var fileName = getUnique(sanitize(rawName), '.txt', existingNames);
existingNames.push(fileName.toLowerCase());
var content = new Blob([fData.href, '\n'], { type: "text/plain;charset=utf-8" });
zip.file(fileName, content, opts);
console.log('DONE ---- ' + fileName);
setTimeout(done, 1000);
};
if (parsed.hashData.type === 'file') {
return void todoFile();
} else if (parsed.hashData.type === 'link') {
return void todoLink();
}
todoPad();
});
@ -286,7 +306,7 @@ define([
};
// Add folders and their content recursively in the zip
var makeFolder = function (ctx, root, zip, fd) {
var makeFolder = function (ctx, root, zip, fd, sd) {
if (typeof (root) !== "object") { return; }
var existingNames = [];
Object.keys(root).forEach(function (k) {
@ -294,19 +314,20 @@ define([
if (typeof el === "object" && el.metadata !== true) { // if folder
var fName = getUnique(sanitize(k), '', existingNames);
existingNames.push(fName.toLowerCase());
return void makeFolder(ctx, el, zip.folder(fName), fd);
return void makeFolder(ctx, el, zip.folder(fName), fd, sd);
}
if (ctx.data.sharedFolders[el]) { // if shared folder
let staticData = ctx.sf[el].static;
var sfData = ctx.sf[el].metadata;
var sfName = getUnique(sanitize((sfData && sfData.title) || 'Folder'), '', existingNames);
existingNames.push(sfName.toLowerCase());
return void makeFolder(ctx, ctx.sf[el].root, zip.folder(sfName), ctx.sf[el].filesData);
return void makeFolder(ctx, ctx.sf[el].root, zip.folder(sfName), ctx.sf[el].filesData, staticData);
}
var fData = fd[el];
var fData = fd[el] || (sd && sd[el]);
if (fData) {
addFile(ctx, zip, fData, existingNames);
return;
}
}
});
};
@ -330,11 +351,13 @@ define([
sframeChan: sframeChan
};
var filesData = data.sharedFolderId && ctx.sf[data.sharedFolderId] ? ctx.sf[data.sharedFolderId].filesData : ctx.data.filesData;
var links = ctx.sf[data.sharedFolderId] && ctx.sf[data.sharedFolderId].static ? ctx.data.static && ctx.sf[data.sharedFolderId].static : ctx.data.static;
progress('reading', -1); // Msg.settings_export_reading
nThen(function (waitFor) {
ctx.waitFor = waitFor;
var zipRoot = ctx.zip.folder(data.name || Messages.fm_rootName);
makeFolder(ctx, ctx.folder || ctx.data.root, zipRoot, filesData);
makeFolder(ctx, ctx.folder || ctx.data.root, zipRoot, filesData, links);
progress('download', {}); // Msg.settings_export_download
}).nThen(function () {
console.log(ctx.zip);

View File

@ -68,6 +68,8 @@ define([
var NEW_VERSION = 7; // version of the .bin, patches and ChainPad formats
var PENDING_TIMEOUT = 30000;
var CURRENT_VERSION = X2T.CURRENT_VERSION;
const HISTORY_KEEPER_INDEX_USER = 1;
const READ_ONLY_INDEX_USER = 2;
//var READONLY_REFRESH_TO = 15000;
@ -181,17 +183,17 @@ define([
});
};
const getNewUserIndex = function () {
const ids = content.ids || {};
const indexes = Object.values(ids).map((user) => user.index);
const maxIndex = Math.max(...indexes);
return maxIndex === -Infinity ? 1 : maxIndex+1;
const getNextUserIndex = function () {
let nextUserIndex;
do {
nextUserIndex = Util.createRandomInteger();
} while (nextUserIndex === HISTORY_KEEPER_INDEX_USER || nextUserIndex === READ_ONLY_INDEX_USER);
return nextUserIndex;
};
var setMyId = function () {
// Remove ids for users that have left the channel
deleteOffline();
var ids = content.ids;
deleteOffline(); // Remove ids for users that have left the channel
const ids = content.ids;
if (!myOOId) {
myOOId = Util.createRandomInteger();
// f: function used in .some(f) but defined outside of the while
@ -202,13 +204,21 @@ define([
myOOId = Util.createRandomInteger();
}
}
var myId = getId();
const myId = getId();
const myIndex = getNextUserIndex();
ids[myId] = {
ooid: myOOId,
index: getNewUserIndex(),
index: myIndex,
netflux: metadataMgr.getNetfluxId()
};
oldIds = JSON.parse(JSON.stringify(ids));
if (!myUniqueOOId) {
myUniqueOOId = String(myOOId) + myIndex;
}
oldIds = structuredClone(ids);
APP.onLocal();
};
@ -920,56 +930,42 @@ define([
const getMyOOIndex = function() {
const user = findUserByOOId(myOOId);
return user
? user.index
: content.ids.length; // Assign an unused id to read-only users
return user ? user.index : READ_ONLY_INDEX_USER;
};
var getParticipants = function () {
var users = metadataMgr.getMetadata().users;
var i = 1;
var p = Object.keys(content.ids || {}).map(function (id) {
var nId = id.slice(0,32);
if (!users[nId]) { return; }
var ooId = content.ids[id].ooid;
var idx = content.ids[id].index;
if (!ooId || ooId === myOOId) { return; }
if (idx >= i) { i = idx + 1; }
return {
id: String(ooId) + idx,
idOriginal: String(ooId),
username: (users[nId] || {}).name || Messages.anonymous,
indexUser: idx,
connectionId: content.ids[id].netflux || Hash.createChannelId(),
isCloseCoAuthoring:false,
view: false
};
});
const getParticipants = function () {
const users = metadataMgr.getMetadata().users;
// Add an history keeper user to show that we're never alone
var hkId = Util.createRandomInteger();
p.push({
id: hkId,
const historyKeeper = [{
id: String(hkId),
idOriginal: String(hkId),
username: "History",
indexUser: i,
indexUser: HISTORY_KEEPER_INDEX_USER,
connectionId: Hash.createChannelId(),
isCloseCoAuthoring:false,
view: false
}];
const realParticipants = Object.entries(content.ids).map(([id, user]) => {
const nId = id.slice(0,32);
const username = (users[nId] || {}).name || Messages.anonymous;
return {
id: String(user.ooid) + user.index,
idOriginal: String(user.ooid),
username,
indexUser: user.index,
connectionId: user.netflux || Hash.createChannelId(),
isCloseCoAuthoring: false,
view: false
};
});
const myOOIndex = getMyOOIndex();
if (!myUniqueOOId) { myUniqueOOId = String(myOOId) + myOOIndex; }
p.push({
id: String(myOOId),
idOriginal: String(myOOId),
username: metadataMgr.getUserData().name || Messages.anonymous,
indexUser: myOOIndex,
connectionId: metadataMgr.getNetfluxId() || Hash.createChannelId(),
isCloseCoAuthoring:false,
view: false
});
const participants = historyKeeper.concat(realParticipants);
return {
index: myOOIndex,
list: p.filter(Boolean)
index: getMyOOIndex(),
list: participants,
};
};
@ -3409,7 +3405,7 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
if (content.ids) {
handleNewIds(oldIds, content.ids);
oldIds = JSON.parse(JSON.stringify(content.ids));
oldIds = structuredClone(content.ids);
}
if (content.locks) {
handleNewLocks(oldLocks, content.locks);

View File

@ -293,7 +293,7 @@ define([
let closeAll = () => {
History.state = false;
$hist.hide()
$hist.hide();
$bottom.show();
$cke.show();
$(window).trigger('resize');

View File

@ -105,7 +105,9 @@ define([
h('p', data.content.msg.type + ' - ' +formatData(data))
])
]);
if ($(notif).find('.cp-avatar').length) {
$(notif).addClass('cp-notification-avatar');
}
if (typeof(data.content.getFormatText) === "function") {
$(notif).find('.cp-notification-content p').html(data.content.getFormatText());
if (data.content.autorefresh) {

View File

@ -78,7 +78,6 @@ define([
requireConfig.urlArgs + '#' + encodeURIComponent(JSON.stringify(req)));
$i.attr('allowfullscreen', 'true');
$i.attr('allow', 'clipboard-write');
$i.attr('title', 'iframe');
$('iframe-placeholder').after($i).remove();
// This is a cheap trick to avoid loading sframe-channel in parallel with the

View File

@ -1198,7 +1198,9 @@ MessengerUI, Messages, Pages, PadTypes) {
$('body').find('.cp-dropdown-content li').first().focus();
return $(el).find('.cp-notification-dismiss').click();
}
$(el).find('.cp-notification-content').click();
setTimeout(function () {
$(el).find('.cp-notification-content').click();
}, 0);
});
refresh();
},
@ -1416,7 +1418,7 @@ MessengerUI, Messages, Pages, PadTypes) {
tb['pad'] = function () {
toolbar.$file.show();
addElement([
toolbar.addElement([
'chat',
'collapse',
'userlist', 'title', 'useradmin', 'spinner',

View File

@ -467,5 +467,137 @@
"upload_modal_title": "Опции за качване на файлове",
"upload_tooLarge": "Този файл надвишава максималния разрешен размер на качване за вашия акаунт.",
"upload_serverError": "Грешка в сървъра: файлът ви не може да бъде качен в момента.",
"upload_success": "Вашият файл ({0}) бе успешно качен и е добавен към устройството ви."
"upload_success": "Вашият файл ({0}) бе успешно качен и е добавен към устройството ви.",
"todo_markAsCompleteTitle": "Маркирайте тази задача като завършена",
"todo_markAsIncompleteTitle": "Маркирайте тази задача като незавършена",
"upload_tooLargeBrief": "Файлът надвишава ограничението от {0}MB за това устройство",
"upload_choose": "Избиране на файл",
"upload_pending": "Изчакване",
"upload_cancelled": "Отменено",
"upload_size": "Размер",
"upload_mustLogin": "Трябва да сте влезли, за да качвате файлове",
"upload_up": "Качване",
"download_mt_button": "Изтегляне",
"download_dl": "Изтегляне",
"download_step1": "Изтегля се",
"download_step2": "Декриптиране",
"todo_title": "Crypt Todo",
"todo_removeTaskTitle": "Премахнете тази задача от вашия списък със задачи",
"pad_base64": "Този документ съдържа изображения, съхранени по неефективен начин. Тези изображения значително ще увеличат размера на документа във вашия CryptDrive и ще направят зареждането му по-бавно. Можете да промените тези файлове в нов формат, който ще се съхранява отделно във вашия CryptDrive. Искате ли да промените тези изображения сега?",
"mdToolbar_button": "Показване или скриване на лентата с инструменти Markdown",
"mdToolbar_defaultText": "Вашият текст тук",
"mdToolbar_help": "Помощ",
"mdToolbar_tutorial": "https://www.markdowntutorial.com/",
"mdToolbar_bold": "Удебелен",
"mdToolbar_italic": "Курсив",
"mdToolbar_strikethrough": "Зачертано",
"mdToolbar_heading": "Заглавие",
"mdToolbar_nlist": "Подреден списък",
"mdToolbar_list": "Неподреден списък",
"mdToolbar_check": "Списък със задачи",
"mdToolbar_code": "Код",
"home_host": "Това е отделен екземпляр от общността на CryptPad.",
"about": "Относно",
"privacy": "Политика за поверителност",
"contact": "Контакт",
"terms": "Условия за ползване",
"features": "Характеристики",
"features_title": "Характеристики",
"features_anon": "Гост",
"features_registered": "Регистриран",
"features_premium": "Премиум",
"features_f_core": "Общи характеристики",
"features_f_file0": "Отваряне на документи",
"mdToolbar_quote": "Цитат",
"features_f_apps": "Достъп до всички приложения",
"features_f_core_note": "Редактиране, импортиране и експортиране, история, потребителски списък, чат",
"mdToolbar_toc": "Съдържание",
"mdToolbar_link": "Връзка",
"main_catch_phrase": "Пакет за сътрудничество<br>криптиран от край до край и с отворен код",
"features_f_file0_note": "Преглеждане и изтегляне на документи, споделени от другите потребители",
"features_f_cryptdrive0": "Ограничен достъп до CryptDrive",
"features_f_cryptdrive0_note": "Възможност за съхраняване на последно използваните документи във вашия браузър, за да можете да ги отворите по-късно",
"features_f_storage0": "Ограничено време за съхранение",
"features_f_storage0_note": "Документите се изтриват след {0} дни неактивност",
"features_f_anon": "Всички потребителски функции за гости",
"features_f_anon_note": "С допълнителна функционалност",
"features_f_cryptdrive1": "Пълна функционалност на CryptDrive",
"features_f_cryptdrive1_note": "Папки, споделени папки, шаблони, тагове",
"features_f_devices": "Вашите документи във всичките ви устройства",
"features_f_devices_note": "Достъп до вашия CryptDrive отвсякъде чрез акаунта ви",
"features_f_social": "Социални характеристики",
"features_f_social_note": "Добавете контакти за сигурно сътрудничество, създайте профил, прецизни контроли за достъп",
"features_f_file1": "Качване и споделяне на файлове",
"features_f_file1_note": "Съхранявайте файлове във вашия CryptDrive: изображения, PDF файлове, видеоклипове и др. Споделете ги с вашите контакти или ги вградете във вашите документи. (до {0}MB)",
"features_f_storage1": "Лично хранилище ({0})",
"features_f_storage1_note": "Документите, съхранявани във вашия CryptDrive, не се изтриват при неактивност",
"features_f_register": "Безплатно регистриране",
"features_f_reg": "Всички регистрирани потребителски функции",
"features_f_reg_note": "С допълнителни предимства",
"features_f_storage2": "Допълнително място за съхранение",
"features_f_storage2_note": "От 5 GB на 50 GB в зависимост от плана, увеличен лимит от {0} MB за качване на файлове",
"features_f_support": "По-бърза поддръжка",
"features_f_support_note": "Приоритетен отговор от административния екип чрез имейл и вградена билетна система",
"features_f_supporter": "Поверителност при поддръжка",
"features_f_supporter_note": "Помогнете на CryptPad да стане финансово устойчив и покажете, че софтуерът за подобряване на поверителността, доброволно финансиран от потребителите, трябва да бъде норма",
"four04_pageNotFound": "Не успяхме да намерим страницата, която търсите.",
"help_genericMore": "Научете повече за това как CryptPad може да работи за вас, като прочетете нашата <a>Документация</a>",
"feedback_about": "Ако четете това, вероятно сте били любопитни защо CryptPad търси уеб страници, когато извършвате определени действия.",
"creation_owned1": "<b>Притежаван</b> документ може да бъде унищожен, когато собственикът поиска. Унищожаването на притежаван документ го прави недостъпен чрез CryptDrives за другите потребители.",
"feedback_privacy": "Грижим се за вашата поверителност и в същото време искаме CryptPad да бъде много лесен за използване. Използваме този файл, за да разберем кои функции на потребителския интерфейс имат значение за нашите потребители, като го изискваме заедно с параметър, указващ кое действие е предприето.",
"creation_newPadModalDescription": "Кликнете върху приложението, за да създадете нов документ. Можете също да натиснете <b>Tab</b>, за да изберете приложението, и да натиснете <b>Enter</b>, за да потвърдите.",
"features_f_subscribe": "Абониране",
"features_f_subscribe_note": "За абониране е необходим акаунт",
"header_logoTitle": "Към вашия CryptDrive",
"header_homeTitle": "Към началната страница на CryptPad",
"edit": "редактиране",
"view": "преглед",
"feedback_optout": "Ако искате да се откажете, посетете <a>страницата си с потребителски настройки</a>, където ще намерите отметка, за да активирате или деактивирате обратната връзка с потребителя.",
"creation_404": "Този документ вече не съществува. Използвайте следната форма, за да създадете нов документ.",
"creation_owned": "Собственост на документа",
"creation_expire": "Изтичащ документ",
"creation_expireFalse": "Неограничен",
"creation_expireHours": "Час(ове)",
"creation_expireDays": "Ден(дни)",
"creation_expireMonths": "Месец(и)",
"creation_password": "Парола\n",
"creation_noTemplate": "Празен документ",
"creation_newTemplate": "Нов шаблон",
"creation_create": "Създаване",
"creation_owners": "Собственици",
"creation_noOwner": "Без собственик",
"creation_expiration": "Дата на унищожаване",
"creation_passwordValue": "Парола",
"password_info": "Документът, който се опитвате да отворите, вече не съществува или е защитен с нова парола. Въведете правилната парола за достъп до съдържанието.",
"properties_changePasswordButton": "Изпращане",
"sharedFolders_forget": "Този документ се съхранява само в споделена папка, не можете да го преместите в кошчето. Можете да използвате своя CryptDrive, ако искате да го изтриете.",
"share_linkEmbed": "Режим на вграждане (скриване на лентата с инструментите и потребителския списък)",
"share_mediatagCopy": "Копиране на медийния маркер в клипборда",
"sharedFolders_share": "Споделете тази връзка с други регистрирани потребители, за да им дадете достъп до споделената папка. След като отворят тази връзка, споделената папка ще бъде добавена към техния CryptDrive.",
"convertFolderToSF_SFChildren": "Тази папка не може да бъде преобразувана в споделена папка, защото вече съдържа споделени папки. Преместете тези споделени папки другаде, за да продължите.",
"password_error": "Документът не е намерен<br>Тази грешка може да бъде причинена от две причини: или паролата е невалидна, или документът е унищожен.",
"password_placeholder": "Въведете паролата тук...",
"password_submit": "Изпращане",
"properties_addPassword": "Добавяне на парола",
"properties_changePassword": "Промяна на паролата",
"properties_confirmNew": "Сигурен ли си? Добавянето на парола ще промени адреса на този документ и ще премахне неговата история. Потребителите без паролата ще загубят достъпа до този документ",
"properties_confirmChange": "Сигурен ли си? Промяната на паролата ще премахне нейната история. Потребителите без новата парола ще загубят достъпа до този документ",
"properties_passwordSame": "Новите пароли трябва да се различават от текущата.",
"properties_passwordError": "Възникна грешка при опит за промяна на паролата. Моля, опитайте отново.",
"properties_passwordWarning": "Паролата беше променена успешно, но не успяхме да актуализираме вашия CryptDrive с новите данни. Може да се наложи да премахнете старата версия на документа ръчно.<br>Натиснете OK, за да презаредите и актуализирате правата си за достъп.",
"properties_passwordSuccess": "Паролата бе променена успешно.<br>Натиснете OK, за да презаредите и актуализирате правата си за достъп.",
"share_linkCategory": "Връзка",
"share_linkAccess": "Права за достъп",
"share_linkEdit": "Редактиране",
"share_linkView": "Преглед",
"share_linkPresent": "Текущ",
"share_linkOpen": "Отваряне на връзка",
"share_linkCopy": "Копиране на връзка",
"share_contactCategory": "Контакти",
"share_embedCategory": "Вграждане",
"sharedFolders_duplicate": "Някои от документите, които се опитвахте да преместите, вече бяха споделени в целевата папка.",
"sharedFolders_create": "Създаване на споделена папка",
"sharedFolders_create_name": "Име на папка",
"sharedFolders_create_owned": "Собствена папка",
"convertFolderToSF_SFParent": "Тази папка не може да бъде преобразувана в споделена папка в текущото си местоположение. Преместете го извън споделената папка, за да продължите."
}

View File

@ -1783,5 +1783,9 @@
"admin_onboardingNameHint": "Bitte wähle einen Namen, eine Beschreibung, eine Akzentfarbe und ein Logo (alle Angaben sind optional)",
"team_autoTrim": "Verlauf des Team-Drives wird gelöscht... Bitte warten.",
"admin_mfa_confirm_enable": "Bist du sicher, dass du die Multi-Faktor-Authentifizierung aktivieren möchtest?",
"admin_mfa_confirm_disable": "Bist du sicher, dass du die Multi-Faktor-Authentifizierung deaktivieren möchtest?"
"admin_mfa_confirm_disable": "Bist du sicher, dass du die Multi-Faktor-Authentifizierung deaktivieren möchtest?",
"fm_restoreMultipleDialog": "Bist du sicher, dass du {0} Dateien/Ordner zurück in den ursprünglichen Ordner verschieben möchtest?",
"calendar_show": "Kalender anzeigen",
"form_passwordWarning": "Bitte beachte, dass ein Formularpasswort nur zum Zeitpunkt der Erstellung festgelegt und später nicht mehr geändert werden kann.",
"calendar_hide": "Kalender verbergen"
}

View File

@ -1442,8 +1442,8 @@
"admin_uptimeHint": "Date et heure auxquelles le serveur a été démarré",
"admin_cat_database": "Base de données",
"admin_generatedAt": "Horodatage du rapport",
"ui_true": "acti",
"ui_false": "désactivé",
"ui_true": "vrai",
"ui_false": "faux",
"ui_none": "aucun",
"ui_generateReport": "Générer un rapport",
"ui_success": "Succès",
@ -1783,5 +1783,9 @@
"install_notes": "<ul class=\"cp-notes-list\"><li>Créez votre premier compte administrateur·ice sur cette page. Les administrateur·ices peuvent paramétrer l'instance, ceci incluant les quotas de stockage, et ont accès aux outils de modération.</li><li>Votre mot de passe est la clé secrète qui chiffre l'ensemble de vos documents et vos privilèges d'administration sur l'instance. <span class=\"red\">Si vous le perdez il n'est pas possible de récupérer vos données.</span></li><li>Si vous utilisez un ordinateur partagé, <span class=\"red\">n'oubliez pas de vous déconnecter</span> quand vous aurez terminé. Simplement fermer la fenêtre du navigateur web laisse votre compte exposé à des risques de sécurité. </li></ul>",
"team_autoTrim": "Suppression de l'historique du drive d'équipe... Veuillez patienter.",
"admin_mfa_confirm_disable": "Êtes-vous sûr de vouloir désactiver l'authentification multi-facteur ?",
"admin_mfa_confirm_enable": "Êtes-vous sûr de vouloir activer l'authentification multi-facteur ?"
"admin_mfa_confirm_enable": "Êtes-vous sûr de vouloir activer l'authentification multi-facteur ?",
"fm_restoreMultipleDialog": "Êtes-vous sûr·e de vouloir restaurer {0} fichiers et/ou dossiers à leurs emplacements précédents?",
"calendar_hide": "Cacher les calendriers",
"calendar_show": "Afficher les calendriers",
"form_passwordWarning": "Veuillez noter qu'un mot de passe pour Formulaire peut uniquement être spécifié lors de la création du document et ne peut pas être changé plus tard."
}

View File

@ -421,5 +421,35 @@
"settings_import": "Importálás",
"upload_pending": "Függöben lévő",
"upload_up": "Feltöltés",
"features_f_subscribe": "Feliratkozás"
"features_f_subscribe": "Feliratkozás",
"toolbar_savetodrive": "Mentés képként",
"comments_comment": "Hozzászólás",
"drive_treeButton": "Fájlok",
"fm_noResult": "Keresés eredménytelen",
"settings_cat_style": "Megjelenés",
"logoutButton": "Kijelentkezés",
"login_noSuchUser": "Hibás felhasználónév vagy jelszó",
"login_invalUser": "Felhasználónév szükséges",
"login_unhandledError": "Váratlan hiba történt :(",
"register_passwordsDontMatch": "A jelszavak nem egyeznek!",
"register_alreadyRegistered": "Ez a felhasználónév már létezik! Szeretnél bejelentkezni?",
"settings_changePasswordNew": "Új jelszó",
"settings_changePasswordCurrent": "Aktuális jelszó",
"settings_codeSpellcheckTitle": "Helyesírás-ellenőrzés",
"comments_submit": "Beküldés",
"toolbar_insert": "Beszúrás",
"toolbar_tools": "Eszközök",
"slide_backCol": "Háttérszín",
"slide_textCol": "Szövegszín",
"toolbar_file": "Fájl",
"support_cat_bug": "Hibajelentés",
"support_attachments": "Csatolmányok",
"settings_kanbanTagsOr": "VAGY",
"history_close": "Bezár",
"history_restore": "Visszaállít",
"fm_restricted": "Nincs hozzáférése",
"support_cat_all": "Összes",
"support_addAttachment": "Csatolmány hozzáadása",
"oo_refresh": "Frissítés",
"support_formCategoryError": "Hiba: üres kategória"
}

View File

@ -648,7 +648,7 @@
"contact_dev": "Contatta la quadra di sviluppo",
"contact_admin": "Contatta gli amministratori e le amministratrici per: {0}",
"footer_donate": "Dona",
"admin_registeredTitle": "Utenti registrati",
"admin_registeredTitle": "Drive di utenti e gruppi",
"admin_activePadsTitle": "Documenti attivi",
"admin_activeSessionsTitle": "Connessioni attive",
"adminPage": "Amministrazione",
@ -843,7 +843,7 @@
"support_disabledHint": "Questa istanza di CryptPad non è ancora configurata per utilizzare un modulo di assistenza.",
"sharedFolders_share": "Condividi questo link con altri utenti registrati o altre utenti registrate per dare loro accesso alla cartella condivisa. Una volta che aprono questo link, la cartella condivisa sarà aggiunta al loro CryptDrive.",
"autostore_notAvailable": "Devi salvare questo documento nel tuo CryptDrive prima di poter utilizzare questa funzionalità.",
"admin_registeredHint": "Numero di utenti registrati/e nella tua istanza",
"admin_registeredHint": "Numero di drive attivi sulla tua istanza",
"admin_updateLimitDone": "Aggiornamento completato con successo",
"requestEdit_button": "Richiedi i diritti di modifica",
"requestEdit_accepted": "{1} ti ha permesso di modificare il documento <b>{0}</b>",
@ -1768,5 +1768,8 @@
"admin_appsHint": "Scegli le app da abilitare su questa istanza.",
"admin_cat_apps": "ApplicazIoni",
"admin_onboardingOptionsTitle": "Opzioni dellistanza",
"admin_onboardingOptionsHint": "Scegli lopzione appropriata per la tua istanza.<br> Queste configurazioni possono essere cambiate successivamente nel pannello di amministrazione."
"admin_onboardingOptionsHint": "Scegli lopzione appropriata per la tua istanza.<br> Queste configurazioni possono essere cambiate successivamente nel pannello di amministrazione.",
"team_autoTrim": "Eliminazione della cronologia del drive del gruppo... Si prega di attendere.",
"admin_mfa_confirm_enable": "Sei sicuro/a di voler attivare l'autenticazione a più fattori?",
"admin_mfa_confirm_disable": "Sei sicuro/a di voler disattivare l'autenticazione a più fattori?"
}

View File

@ -1783,5 +1783,9 @@
"admin_onboardingDescPlaceholder": "Instance description text",
"team_autoTrim": "Trimming team drive history... Please wait.",
"admin_mfa_confirm_enable": "Are you sure you want to enable Multi-Factor Authentication?",
"admin_mfa_confirm_disable": "Are you sure you want to disable Multi-Factor Authentication?"
"admin_mfa_confirm_disable": "Are you sure you want to disable Multi-Factor Authentication?",
"form_passwordWarning": "Please note that a Form password can only be set now at creation time and cannot be changed later.",
"fm_restoreMultipleDialog": "Are you sure you want to restore {0} files and/or folders to their previous locations?",
"calendar_show": "Show calendars",
"calendar_hide": "Hide calendars"
}

View File

@ -663,5 +663,9 @@
"admin_broadcastTitle": "Broadcast bericht",
"pad_settings_hide": "Verbergen",
"importError": "Importeren mislukt (verkeerd formaat)",
"terms": "Servicevoorwaarden"
"terms": "Servicevoorwaarden",
"creation_404": "Dit document bestaat niet meer. Gebruik het volgende formulier om een nieuw document te maken.",
"feedback_about": "Als je dit leest was je waarschijnlijk nieuwsgierig waarom CryptPad om webpagina's vraagt wanneer je bepaalde acties uitvoert.",
"features_f_social_note": "Voeg contacten toe voor veilige samenwerking, creëer een profiel, fijnmazige toegangscontroles",
"feedback_optout": "Als u zich wilt afmelden, ga dan naar <a>uw gebruikersinstellingenpagina</a>, waar u een selectievakje vindt om gebruikersfeedback in of uit te schakelen."
}

File diff suppressed because it is too large Load Diff

View File

@ -11,8 +11,8 @@
"slide": "Markdown Sunumları",
"poll": "Anket",
"code": "Kod",
"pad": "Zengin metin biçimi",
"doc": "Doküman",
"pad": "Zengin Metin",
"doc": "Belge",
"presentation": "Sunum",
"diagram": "Diyagram",
"sheet": "Sheet",
@ -45,12 +45,12 @@
"MB": "MB",
"GB": "GB",
"formattedGB": "{0} GB",
"formattedKB": "{0} KB",
"formattedKB": "{0} kB",
"importButton": "İçe aktar",
"clickToEdit": "Düzenlemek için tıkla",
"forgetButton": "Sil",
"shareButton": "Paylaş",
"uploadFolderButton": "Klasör yükle",
"uploadFolderButton": "Dizini karşıya yükle",
"saveTemplatePrompt": "Şablon için bir başlık seçin",
"templateSaved": "Şablon kaydedildi!",
"selectTemplate": "Bir şablon seçin veya escape tuşuna basın",
@ -66,15 +66,15 @@
"mustLogin": "Bu sayfaya erişmek için giriş yapmalısınız",
"forgotten": "Çöp kutusuna taşındı",
"errorState": "Kritik hata: {0}",
"KB": "KB",
"KB": "kB",
"formattedMB": "{0} MB",
"typeError": "Bu doküman seçilmiş uygulama ile uyumlu değil",
"typeError": "Bu belge seçilen uygulama ile uyumlu değil",
"disconnected": "Bağlantı kesildi",
"forgetPrompt": "Tamam'ı tıkladığınızda bu belge çöp kutunuza taşınacaktır. Emin misin?",
"uploadButtonTitle": "CryptDrive'ınıza yeni bir dosya yükleyin",
"userAccountButton": "Kullanıcı menüsü",
"deletedError": "Bu belge silindi ve artık mevcut değil.",
"inactiveError": "Bu belge, işlem yapılmaması nedeniyle silinmiştir. Yeni bir belge oluşturmak için Esc tuşuna basın.",
"inactiveError": "Bu belge, işlem yapılmaması nedeniyle silindi. Yeni bir belge oluşturmak için Esc tuşuna basın.",
"chainpadError": "İçeriğiniz güncellenirken kritik bir hata oluştu. Çalışmanızı kaybetmemeniz için bu sayfa salt okunur modundadır.<br>Bu belgeyi görüntülemeye devam etmek için Esc tuşuna basın veya yeniden düzenlemeyi denemek için yeniden yükleyin.",
"errorRedirectToHome": "CryptDrive'ınıza yönlendirilmek için Esc tuşuna basın.",
"newVersionError": "CryptPad'in yeni bir sürümü mevcut. Yeni sürümü kullanmak için <br><a href='#'>yeniden yükleyin</a> veya <b>çevrimdışı modda</b> içeriğinize erişmek için escape tuşuna basın.",
@ -84,5 +84,453 @@
"pinLimitReachedAlert": "Depolama sınırınıza ulaştınız. Yeni belgeler CryptDrive'ınızda saklanmaz.<br>Sınırınızı artırmak için belgeleri CryptDrive'ınızdan kaldırabilir veya <a>premium bir teklife abone olabilirsiniz</a>.",
"pinLimitNotPinned": "Depolama sınırınıza ulaştınız.<br>Bu belge CryptDrive'ınızda saklanmıyor.",
"movedToTrash": "Bu doküman çöp kutusuna taşındı.<br><a>Drive'ıma erişin</a>",
"saveTemplateButton": "Şablon olarak kaydet"
"saveTemplateButton": "Şablon olarak kaydet",
"propertiesButton": "Özellikler",
"filePicker_close": "Kapat",
"poll_unlocked": "Kilidi açık",
"pad_mediatagPreview": "Ön izleme",
"printText": "Yazdır",
"slideOptionsText": "Seçenekler",
"languageButton": "Dil",
"themeButton": "Tema",
"ok": "Tamam",
"cancel": "İptal",
"help_button": "Yardım",
"historyText": "Geçmiş",
"kanban_done": "Tamamlandı",
"poll_publish_button": "Yayınla",
"poll_commit": "Gönder",
"poll_optionPlaceholder": "Seçenek",
"poll_remove": "Kaldır",
"poll_edit": "Düzenle",
"poll_locked": "Kilitli",
"exportButton": "Dışarı Aktar",
"canvas_clear": "Temizle",
"settings_import": "İçeri Aktar",
"upload_pending": "Bekleniyor",
"fm_templateName": "Şablonlar",
"settingsButton": "Ayarlar",
"fm_searchPlaceholder": "Ara...",
"fm_prop_tagsList": "Etiketler",
"fc_rename": "Yeniden Adlandır",
"settings_resetButton": "Kaldır",
"settings_cat_account": "Hesap",
"upload_size": "Size",
"notificationsPage": "Bildirimler",
"settings_thumbnails": "Küçük Resimler",
"settings_autostoreYes": "Otomatik",
"upload_cancelled": "İptal Edildi",
"upload_up": "Karşıya Yükle",
"properties_changePasswordButton": "Gönder",
"share_withFriends": "Paylaş",
"mdToolbar_tutorial": "https://www.markdowntutorial.com/",
"edit": "düzenle",
"features": "Özellikler",
"share_linkEdit": "Düzenle",
"share_linkPresent": "Present",
"contact_chat": "Sohbet",
"settings_codeSpellcheckTitle": "Yazım denetimi",
"support_formButton": "Gönder",
"team_cat_list": "Takımlar",
"team_cat_chat": "Sohbet",
"poll_total": "TOPLAM",
"poll_comment_list": "Yorumlar",
"poll_comment_submit": "Gönder",
"canvas_width": "Genişlik",
"canvas_opacity": "Opaklık",
"profileButton": "Profil",
"contacts_title": "Kişiler",
"contacts_send": "Gönder",
"contacts_padTitle": "Sohbet",
"contacts_rooms": "Odalar",
"fm_rootName": "Drive",
"fm_trashName": "Çöp",
"fm_searchName": "Ara",
"fm_recentPadsName": "Son",
"fm_ownedPadsName": "Sahiplenmiş",
"fm_tagsName": "Etiketler",
"fm_newButton": "Yeni",
"fm_folder": "Dizin",
"fm_type": "Tür",
"fm_creation": "Oluşturma",
"fc_open": "Aç",
"fc_delete_owned": "Yok Et",
"fc_restore": "Geri Getir",
"fc_remove": "Kaldır",
"fc_remove_sharedfolder": "Kaldır",
"fc_prop": "Özellikler",
"fc_hashtag": "Etiketler",
"login_username": "Kullanıcı Adı",
"login_password": "Parola",
"register_header": "Kayıt Ol",
"register_cancel": "İptal",
"register_warning": "Uyarı",
"settings_cat_drive": "CryptDrive",
"settings_cat_cursor": "İmleç",
"settings_cat_code": "Kod",
"settings_cat_subscription": "Abonelik",
"settings_title": "Ayarlar",
"settings_save": "Kaydet",
"settings_backupCategory": "Yedekle",
"settings_backup": "Yedekle",
"settings_restore": "Geri Getir",
"settings_resetTipsAction": "Sıfırla",
"settings_resetTips": "İpuçları",
"settings_resetThumbnailsAction": "Temizle",
"settings_userFeedbackTitle": "Geri Bildirim",
"settings_padSpellcheckTitle": "Yazım Denetimi",
"download_mt_button": "İndir",
"download_step1": "İndiriliyor",
"download_dl": "İndir",
"download_step2": "Şifre çözülüyor",
"todo_title": "CryptTodo",
"mdToolbar_help": "Yardım",
"mdToolbar_bold": "Kalın",
"mdToolbar_italic": "İtalik",
"mdToolbar_strikethrough": "Üstü çizili",
"mdToolbar_heading": "Başlık",
"mdToolbar_link": "Bağlantı",
"mdToolbar_quote": "Quote",
"mdToolbar_code": "Kod",
"about": "Hakkında",
"contact": "İletişim",
"features_title": "Özellikler",
"features_anon": "Misafir",
"features_registered": "Kayıt olundu",
"features_premium": "Premium",
"features_f_subscribe": "Abone ol",
"view": "görüntüle",
"creation_expireFalse": "Sınırsız",
"creation_expireHours": "Saat",
"creation_expireDays": "Gün",
"creation_expireMonths": "Ay",
"creation_password": "Parola\n",
"creation_create": "Oluştur",
"creation_owners": "Sahipler",
"creation_passwordValue": "Parola",
"password_submit": "Gönder",
"share_linkCategory": "Bağlantı",
"share_linkView": "Görüntüle",
"share_contactCategory": "Contacts",
"share_embedCategory": "Gömülü",
"autostore_file": "dosya",
"autostore_sf": "dizin",
"autostore_pad": "pad",
"autostore_store": "Depola",
"crowdfunding_button2": "Bağış Yap",
"markdown_toc": "İçerikler",
"admin_cat_general": "Genel",
"admin_cat_stats": "İstatistikler",
"adminPage": "Yönetim",
"footer_donate": "Bağış",
"contact_email": "E-posta",
"friendRequest_decline": "Reddet",
"notifications_dismiss": "Anımsatma",
"supportPage": "Destek",
"admin_cat_support": "Destek",
"support_answer": "Yanıtla",
"notifications_cat_all": "Tüm",
"notifications_cat_archived": "Geçmiş",
"pricing": "Fiyatlandırma",
"owner_removeText": "Sahipler",
"owner_removePendingText": "Bekleniyor",
"owner_unknownUser": "bilinmiyor",
"team_inviteModalButton": "Davet et",
"team_cat_general": "Hakkında",
"team_cat_create": "Yeni",
"team_cat_members": "Üyeler",
"team_cat_drive": "Drive",
"team_cat_admin": "Yönetim",
"team_rosterPromote": "Promote",
"team_rosterDemote": "Demote",
"team_owner": "Sahipler",
"team_admins": "Yöneticiler",
"team_members": "Üyeler",
"broadcast_start": "Başlat",
"broadcast_end": "Bitir",
"kanban_delete": "Sil",
"toolbar_tools": "Araçlar",
"drive_treeButton": "Dosyalar",
"support_cat_other": "Diğer",
"admin_cat_performance": "Performans",
"settings_colortheme_dark": "Koyu",
"ui_ms": "milisaniye",
"form_editor": "Düzenleyici",
"team_inviteFrom": "From:",
"snapshots_delete": "Sil",
"Offline": "Çevrim dışı",
"admin_performanceKeyHeading": "Komut",
"expiredError": "Bu belgenin geçerlilik süresi doldu ve artık mevcut değil.",
"team_viewers": "İzleyiciler",
"oo_refresh": "Yenile",
"access_main": "Erişim",
"calendar_notifications": "Hatırlatıcılar",
"form_poll_day": "Gün",
"form_pollTotal": "Toplam",
"form_text_number": "Sayı",
"form_input_ph_email": "eposta@ornek.com",
"calendar_rec_freq_daily": "gün",
"admin_limitSetNote": "Not",
"tag_edit": "Düzenle",
"form_preview_button": "Ön izleme",
"download_step3": "Dönüştürülüyor...",
"initializing": "Initializing...",
"admin_documentConflict": "Arşiv/geri getir",
"upgradeAccount": "Hesabı yükselt",
"error_incorrectAccess": "Bu sayfaya yalnızca {0} üzerinden erişilebilir.",
"toolbar_preview": "Ön izleme",
"calendar_rec_daily": "Günlük",
"calendar_str_filter": "Filtreler:",
"form_editable_str": "Başvuru",
"admin_cat_network": "Ağ bağlantısı",
"fm_link_type": "Bağlantı",
"ui_archive": "Arşivle",
"admin_totpDisableButton": "Devre dışı bırak",
"printButton": "Yazdır (enter)",
"invalidHashError": "İstediğiniz belgenin bağlantısı geçersiz.",
"deletedFromServer": "Belge yok edildi",
"user_displayName": "Görünen ad",
"printOptions": "Tasarım seçenekleri",
"editShare": "Düzenleme bağlantısı",
"viewShare": "salt-okunur bağlantı",
"okButton": "Tamam (enter)",
"cancelButton": "İptal (esc)",
"show_help_button": "Yardımı göster",
"history_next": "Sonraki sürüm",
"history_prev": "Önceki sürüm",
"history_restoreDone": "Belge geri getirildi",
"pad_mediatagTitle": "Medya-Etiket ayarları",
"pad_mediatagWidth": "Genişlik (px)",
"pad_mediatagHeight": "Yükseklik (px)",
"pad_mediatagRatio": "Oranı koru",
"pad_mediatagOptions": "Resim özellikleri",
"kanban_newBoard": "Yeni Tahta",
"kanban_item": "Öge {0}",
"kanban_todo": "Yapılacaklar",
"kanban_working": "Üzerinden çalışılıyor",
"poll_userPlaceholder": "İsminiz",
"poll_comment_placeholder": "Yorumunuz",
"canvas_delete": "Seçili kısmı sil",
"canvas_opacityLabel": "Opaklık: {0}",
"canvas_widthLabel": "Genişlik: {0}",
"canvas_currentBrush": "Geçerli fırça",
"fm_filesDataName": "Tüm dosyalar",
"ui_more": "Daha fazla",
"fm_newFile": "Yeni belge",
"fm_sharedFolder": "Paylaşılmış dizinler",
"fm_folderName": "Dizin ismi",
"fm_fileName": "Dosya ismi",
"fm_lastAccess": "Son erişim",
"fm_forbidden": "Yasaklı eylem",
"fm_newFolder": "Yeni dizin",
"fm_sharedFolderName": "Paylaşılmış dizinle",
"team_pending": "Davet Edildi",
"team_deleteButton": "Sil",
"team_pendingOwner": "(bekleniyor)",
"teams_table": "Roller",
"teams_table_specific": "İstisnalar",
"teams_table_role": "Rol",
"contacts_mute": "Sessize Al",
"contacts_unmute": "Sesi Aç",
"allow_disabled": "devre dışı",
"access_allow": "Liste",
"accessButton": "Erişim",
"contacts": "Contacts",
"allow_enabled": "etkinleştirildi",
"teams": "Takımlar",
"kanban_title": "Başlık",
"kanban_body": "İçerik",
"kanban_color": "Renk",
"canvas_brush": "Fırça",
"canvas_select": "Seç",
"cba_enable": "Etkinleştir",
"comments_edited": "Düzenlendi",
"comments_submit": "Gönder",
"comments_reply": "Yanıtla",
"comments_resolve": "Çözümle",
"comments_comment": "Yorum",
"fm_sort": "Sırala",
"toolbar_theme": "Tema",
"toolbar_insert": "Insert",
"toolbar_file": "Dosya",
"support_cat_all": "Tüm",
"support_attachments": "Ekler",
"pad_tocHide": "Taslak",
"settings_kanbanTagsAnd": "VE",
"settings_kanbanTagsOr": "VEYA",
"settings_cat_kanban": "Kanban",
"history_restore": "Geri Getir",
"history_close": "Kapat",
"snaphot_title": "Anlık Görüntü Al",
"snapshots_button": "Anlık Görüntüler",
"snapshots_open": "Aç",
"snapshots_restore": "Geri Getir",
"snapshots_close": "Kapat",
"oo_version_latest": "En son",
"oo_version": "Versiyon: ",
"team_exportButton": "İndir",
"tag_add": "Ekle",
"admin_archiveButton": "Arşivle",
"admin_unarchiveButton": "Geri Getir",
"mediatag_saveButton": "Kaydet",
"admin_support_open": "Göster",
"admin_support_collapse": "Daralt",
"docs_link": "Belgeleme (Dokümantasyon)",
"settings_cacheTitle": "Önbellek",
"undo": "Geri Al",
"redo": "Yeniden Yap",
"admin_performanceProfilingTitle": "Performans",
"admin_performancePercentHeading": "Yüzde",
"settings_cat_style": "Görünüm",
"settings_colortheme_light": "Açık",
"pad_settings_hide": "Gizle",
"pad_settings_show": "Göster",
"settings_colortheme_custom": "Özel",
"admin_cat_broadcast": "Broadcast",
"admin_maintenanceTitle": "Bakım",
"admin_surveyTitle": "Anket",
"admin_surveyCancel": "Kaldır",
"admin_broadcastButton": "Gönder",
"broadcast_translations": "Çeviriler",
"footer_roadmap": "Roadmap",
"calendar_before": "önceki",
"calendar": "Takvim",
"calendar_day": "Gün",
"calendar_week": "Hafta",
"calendar_month": "Ay",
"calendar_today": "Bugün",
"calendar_update": "Güncelle",
"calendar_title": "Başlık",
"calendar_loc": "Konum",
"form_required_answer": "Cevap: ",
"form_required_on": "Gerekli",
"form_required_off": "Opsiyonel",
"admin_usersRemove": "Kaldır",
"calendar_minutes": "Dakika",
"calendar_hours": "Saat",
"calendar_days": "Gün",
"calendar_noNotification": "None",
"mediatag_defaultImageName": "resim",
"share_formEdit": "Yazar",
"share_formAuditor": "Denetçi",
"share_formView": "Katılımcı",
"form_editBlock": "Düzenle",
"form_poll_time": "Zaman",
"form_text_url": "Bağlantı",
"form_text_text": "Metin",
"form_poll_text": "Metin",
"form_text_email": "E-posta",
"form_type_input": "Metin",
"form_type_textarea": "Paragraf",
"form_type_radio": "Seçim",
"form_type_checkbox": "Checkbox",
"form_type_poll": "Anket",
"form_type_md": "Açıklama",
"form_submit": "Gönder",
"form_update": "Güncelle",
"form_reset": "Sıfırla",
"form_delete": "Sil",
"form_viewButton": "Görüntüle",
"form_backButton": "Geri",
"form_input_ph_url": "https://ornek.com",
"form_open": "Aç",
"form_anonymous_on": "İzin verildi",
"form_anonymous_off": "Blocked",
"form_clear": "Temizle",
"fm_link_url": "Bağlantı",
"ui_collapse": "Daralt",
"ui_expand": "Genişlet",
"form_condition_is": "dır",
"form_condition_has": "sahiptir",
"admin_archiveNote": "Not",
"support_cat_document": "Belge",
"admin_noticeTitle": "Ana sayfa bildirimi",
"home_morestorage": "Daha fazla depolama alanı için:",
"admin_cat_database": "Veri tabanı",
"ui_true": "doğru",
"ui_false": "yanlış",
"ui_undefined": "bilinmiyor",
"ui_none": "none",
"ui_success": "Başarılı",
"ui_restore": "Geri Getir",
"ui_fetch": "Fetch",
"ui_confirm": "Onayla",
"admin_documentCreationTime": "Oluşturuldu",
"admin_channelAvailable": "Uygun",
"admin_channelArchived": "Arşivlendi",
"fm_filterBy": "Filtre",
"calendar_rec": "Tekrarla",
"calendar_rec_custom": "Özel",
"calendar_rec_freq_weekly": "hafta",
"calendar_rec_freq_monthly": "ay",
"calendar_rec_freq_yearly": "yıl",
"calendar_rec_until_no": "Asla",
"calendar_rec_until_count": "Sonra",
"calendar_rec_until_count2": "kere",
"calendar_nth_1": "ilk",
"calendar_nth_2": "ikinci",
"calendar_nth_3": "üçüncü",
"calendar_nth_4": "dördüncü",
"calendar_nth_last": "en son",
"calendar_nth_5": "beşinci",
"form_condorcetSchulze": "Schulze",
"form_showCondorcetWinner": "kazanan: ",
"form_showDetails": "Detaylar",
"form_type_date": "Tarih",
"done": "Tamamlandı",
"continue": "Devam et",
"goLeft": "Sol",
"goRight": "Sağ",
"date": "Tarih",
"calendar_desc": "Açıklama",
"calendar_description": "Açıklama:{0}{1}",
"duplicate": "Kopya",
"admin_cat_security": "Güvenlik",
"admin_cat_customize": "Özelleştir",
"support_cat_open": "Gelen kutusu",
"support_cat_search": "Ara",
"support_cat_closed": "Kapatıldı",
"support_cat_settings": "Ayarlar",
"support_cat_legacy": "Eskiden kalma",
"support_pending_tag": "Arşivlendi",
"support_active_tag": "Gelen Kutusu",
"support_closed_tag": "Kapatıldı",
"support_recordedTitle": "Kod parçacıkları",
"support_recordedContent": "İçerik",
"moderationPage": "Yardım-masası",
"install_header": "Yükleme",
"admin_cat_apps": "Uygulamalar",
"fm_noname": "Başlıksız Belge",
"fm_originalPath": "Orijinal yol",
"fm_viewListButton": "Liste görünümü",
"fm_viewGridButton": "Tablo görünümü",
"fm_tags_name": "Etiket ismi",
"fm_passwordProtected": "Parola korundu",
"fc_newfolder": "Yeni dizin",
"fc_color": "Rengi değiştir",
"fc_open_ro": "Aç (salt-okunur)",
"fc_expandAll": "Hepsini genişlet",
"fc_collapseAll": "Hepsini daralt",
"login_login": "Giriş",
"login_register": "Kayıt ol",
"logoutButton": ıkış",
"login_invalUser": "Kullanıcı adı gerekli",
"login_invalPass": "Parola gerekli",
"settings_cat_pad": "Zengin metin",
"settings_export_compressing": "Veri sıkıştırılıyor...",
"settings_exportError": "Hataları görüntüle",
"settings_resetNewTitle": "CryptDrive'ı Temizle",
"settings_importDone": "İçeri aktarma tamamlandı",
"settings_deleteTitle": "Hesap silme",
"settings_logoutEverywhereButton": ıkış",
"settings_driveDuplicateLabel": "Kopyaları gizle",
"settings_ownDriveTitle": "Hesabı Güncelle",
"settings_changePasswordButton": "Parola değiştir",
"settings_changePasswordCurrent": "Şuanki parola",
"settings_changePasswordNew": "Yeni parola",
"settings_cursorColorTitle": "İmleç rengi",
"settings_cursorShowLabel": "İmleçleri göster",
"upload_title": "Dosya yükleme",
"upload_modal_owner": "Sahip olunan dosya",
"uploadFolder_modal_filesPassword": "Dosya parolası"
}

View File

@ -20,7 +20,7 @@
"diagram": "图表"
},
"common_connectionLost": "<b>伺服器連線中斷</b><br>現在是唯讀狀態,直到連線恢復正常。",
"typeError": "此文档与所选应用进程不兼容",
"typeError": "此文档与所选应用不兼容",
"onLogout": "您已退出登录,{0}点击此处{1}登录<br>或按 Esc 以只读模式访问您的文档。",
"loading": "載入中...",
"error": "錯誤",

View File

@ -701,7 +701,7 @@ const factory = (AppConfig = {}, Util, Hash,
}
if (exp.isFolder(obj.element)) { fixRoot(obj.element); }
if (typeof obj.element === "number") {
var data = files[FILES_DATA][obj.element];
var data = files[FILES_DATA][obj.element] || files[STATIC_DATA][obj.element];
if (!data) {
debug("An element in TRASH doesn't have associated data", obj.element, el);
toClean.push(idx);

View File

@ -980,6 +980,7 @@
.cp-radio, .cp-checkmark {
display: inline-flex;
max-width: 100%;
padding: 0.313rem 0;
}
.cp-checkmark-label {
word-break: break-word;
@ -1043,7 +1044,7 @@
}
.cp-form-sort-order {
border: 1px solid @cryptpad_text_col;
padding: 0 5px;
padding: 0 0.313rem;
margin-right: 5px;
}
&:hover {
@ -1318,6 +1319,22 @@
}
}
}
.cp-radio {
margin: 0.2rem 0;
}
.cp-form-status {
margin-bottom: 0.2rem;
}
.cp-form-mute-radio {
margin-top: 0.2rem;
}
.cp-form-privacy-container {
margin-top: 0.8rem
}
.cp-form-results-type {
display: inline-block;
margin: 0.2rem 0 0.2rem 0;
}
}
}
& > .flatpickr-calendar {

View File

@ -1097,6 +1097,11 @@ define([
block = h('div.cp-form-edit-options-block', [t]);
cm = SFCodeMirror.create("gfm", CMeditor, t);
editor = cm.editor;
editor.setOption("extraKeys", {
"Esc": function () {
editor.display.input.blur();
},
});
editor.setOption('lineNumbers', true);
editor.setOption('lineWrapping', true);
editor.setOption('styleActiveLine', true);
@ -4928,7 +4933,7 @@ define([
// End date / Closed state
var endDateContainer = h('div.cp-form-status-container');
var endDateStr = h('div');
var endDateStr = h('div.cp-form-status');
var $endDate = $(endDateContainer);
var $endDateStr = $(endDateStr);

View File

@ -44,7 +44,10 @@ define([
}
}
},
order: ["1", "2"]
order: ["1", "2"],
metadata: {
title: Messages.form_template_poll
}
}
}];
});

View File

@ -110,6 +110,7 @@ define([
var boardContainerOuter = document.createElement('div');
boardContainerOuter.classList.add('kanban-container-outer');
var boardContainer = document.createElement('div');
boardContainer.setAttribute('id', 'kanban-container');
boardContainer.classList.add('kanban-container');
boardContainerOuter.appendChild(boardContainer);
self.container = boardContainer;
@ -737,6 +738,15 @@ define([
return boardNode;
};
let reorder = () => {
// Push "add" button to the end of the list
let add = document.getElementById('kanban-addboard');
let list = document.getElementById('kanban-container');
if (!add || !list) { return; }
list.appendChild(add);
};
this.addBoard = function (board) {
if (!board || !board.id) { return; }
// We need to store all the columns in _boards too because it's used to
@ -757,6 +767,7 @@ define([
_boards.list.push(board.id);
var boardNode = getBoardNode(board);
self.container.appendChild(boardNode);
reorder();
};
this.addBoards = function() {
@ -824,6 +835,7 @@ define([
$('.kanban-board[data-id="'+id+'"] .kanban-drag').scrollTop(scroll[id]);
});
$el.scrollLeft(scrollLeft);
reorder();
};
// If the tab is not focused, redraw on focus

View File

@ -94,9 +94,59 @@ define([
return void cb(blob);
}
if (ext === ".md") {
let strikethrough = {
filter: ['s', 'del', 'strike'],
replacement: function (content) {
return '~' + content + '~';
}
};
let underline = {
filter: ['u'],
replacement: function (content) {
return '<u>' + content + '</u>';
}
};
var md = Turndown({
headingStyle: 'atx'
}).turndown(toExport);
}).addRule('table', {
filter: ['table'],
replacement: function (content, node) {
var childNodeArr = Array.from(node.childNodes);
var table = '';
childNodeArr.forEach(function(rowNode) {
rowNode.childNodes.forEach(function(childNode) {
var rowContent = Array.from(childNode.childNodes);
var indexOf = Array.prototype.indexOf;
var index = childNodeArr.length > 1 ? indexOf.call(node.childNodes, rowNode) : indexOf.call(rowNode.childNodes, childNode);
var row = '|';
var rowLength = rowContent.filter(Boolean).length;
for (var i =0; i < rowLength; i++) {
var cell = rowContent[i];
var cellContent = Array.from(cell.childNodes);
if ((cellContent.length === 1 && cellContent[0].nodeName === "BR") || !cellContent.length) {
row += '|';
} else if (cellContent.length >= 1) {
row += Turndown({
headingStyle: 'atx'
}).addRule('strikethrough', strikethrough)
.addRule('underline', underline)
.turndown(cell.innerHTML).replaceAll('\n', '<br>');
row += '|';
}
}
var newRow = row.concat('\n');
if (index === 0) {
var separator = '|-';
newRow += `${separator.repeat(rowLength)}|\n`;
}
table += newRow;
return newRow;
});
});
return table;
}}).addRule('strikethrough', strikethrough)
.addRule('underline', underline)
.turndown(toExport);
var mdBlob = new Blob([md], {
type: 'text/markdown;charset=utf-8'
});

View File

@ -162,6 +162,7 @@ define([
var makeForm = function (ctx, opts, cb) {
let { oldData, recorded, title, hideNotice } = opts || {};
var button;
cb = Util.once(cb);
if (typeof(cb) === "function") {
button = h('button.btn.btn-primary.cp-support-list-send', Messages.contacts_send);