mirror of
https://github.com/cryptpad/cryptpad.git
synced 2026-09-12 19:49:59 +05:00
Merge branch 'staging' into workerapimerge
This commit is contained in:
commit
173d6941ea
@ -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/
|
||||
58
.eslintrc.js
58
.eslintrc.js
@ -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'],
|
||||
}
|
||||
};
|
||||
12
.github/ISSUE_TEMPLATE/bug_resolution.yml
vendored
12
.github/ISSUE_TEMPLATE/bug_resolution.yml
vendored
@ -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
|
||||
|
||||
@ -135,7 +135,6 @@ define(req, function(AppConfig, Default, Language) {
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
return Messages;
|
||||
|
||||
});
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
---
|
||||
services:
|
||||
cryptpad:
|
||||
image: "cryptpad/cryptpad:version-2024.9.0"
|
||||
image: "cryptpad/cryptpad:version-2024.12.0"
|
||||
hostname: cryptpad
|
||||
|
||||
environment:
|
||||
|
||||
@ -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
96
eslint.config.js
Normal 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",
|
||||
},
|
||||
}];
|
||||
@ -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
794
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -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",
|
||||
|
||||
@ -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 don’t 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.
|
||||
|
||||

|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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] || {};
|
||||
|
||||
@ -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});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -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; }
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -293,7 +293,7 @@ define([
|
||||
|
||||
let closeAll = () => {
|
||||
History.state = false;
|
||||
$hist.hide()
|
||||
$hist.hide();
|
||||
$bottom.show();
|
||||
$cke.show();
|
||||
$(window).trigger('resize');
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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": "Тази папка не може да бъде преобразувана в споделена папка в текущото си местоположение. Преместете го извън споделената папка, за да продължите."
|
||||
}
|
||||
|
||||
@ -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"
|
||||
}
|
||||
|
||||
@ -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": "activé",
|
||||
"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."
|
||||
}
|
||||
|
||||
@ -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"
|
||||
}
|
||||
|
||||
@ -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 dell’istanza",
|
||||
"admin_onboardingOptionsHint": "Scegli l’opzione appropriata per la tua istanza.<br> Queste configurazioni possono essere cambiate successivamente nel pannello di amministrazione."
|
||||
"admin_onboardingOptionsHint": "Scegli l’opzione 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?"
|
||||
}
|
||||
|
||||
@ -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"
|
||||
}
|
||||
|
||||
@ -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
@ -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ı"
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
"diagram": "图表"
|
||||
},
|
||||
"common_connectionLost": "<b>伺服器連線中斷</b><br>現在是唯讀狀態,直到連線恢復正常。",
|
||||
"typeError": "此文档与所选应用进程不兼容",
|
||||
"typeError": "此文档与所选应用不兼容",
|
||||
"onLogout": "您已退出登录,{0}点击此处{1}登录<br>或按 Esc 以只读模式访问您的文档。",
|
||||
"loading": "載入中...",
|
||||
"error": "錯誤",
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -44,7 +44,10 @@ define([
|
||||
}
|
||||
}
|
||||
},
|
||||
order: ["1", "2"]
|
||||
order: ["1", "2"],
|
||||
metadata: {
|
||||
title: Messages.form_template_poll
|
||||
}
|
||||
}
|
||||
}];
|
||||
});
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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'
|
||||
});
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user