Merge branch '2024.9-rc' into monitoring

This commit is contained in:
yflory 2024-09-11 12:15:27 +02:00
commit 9454df6862
184 changed files with 6163 additions and 1272 deletions

View File

@ -35,7 +35,7 @@ module.exports = {
4
],
'linebreak-style': [
'error',
'off', // git handles linebreak conversion for us
'unix'
],
'quotes': [

View File

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

11
.gitignore vendored
View File

@ -17,12 +17,11 @@ customize
messages.log
www/scratch
data
pins/
blob/
block/
blobstage/
block/
logs/
pins
blob
block
blobstage
logs
privileged.conf
config/config.js
config/sso.js

View File

@ -29,6 +29,10 @@ Files: .stylelintrc.js
Copyright: 2023 XWiki CryptPad Team <contact@cryptpad.org> and contributors
License: AGPL-3.0-or-later
Files: scripts/tests/test-data/*
Copyright: 2024 XWiki CryptPad Team <contact@cryptpad.org> and contributors
License: AGPL-3.0-or-later
## Dependencies
Files: www/common/theme/*

View File

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

View File

@ -14,7 +14,7 @@ COPY . /cryptpad
RUN sed -i "s@//httpAddress: 'localhost'@httpAddress: '0.0.0.0'@" /cryptpad/config/config.example.js
RUN sed -i "s@installMethod: 'unspecified'@installMethod: 'docker'@" /cryptpad/config/config.example.js
# Install dependencies
RUN npm install --production \
&& npm run install:components

View File

@ -1,4 +1,4 @@
Copyright (c) <year> <owner>.
Copyright (c) <year> <owner>.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

View File

@ -35,7 +35,7 @@ Mozilla Public License Version 2.0
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"

View File

@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" type="image/png" href="/customize/favicon/main-favicon.png" id="favicon"/>
<script async data-bootload="/customize/four-oh-four.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.5"></script>
<script async data-bootload="/customize/four-oh-four.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.7"></script>
</head>
<body class="html">
<noscript>

View File

@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" type="image/png" href="/customize/favicon/main-favicon.png" id="favicon"/>
<script async data-bootload="/customize/four-oh-four.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.5"></script>
<script async data-bootload="/customize/four-oh-four.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.7"></script>
</head>
<body class="html">
<noscript>

View File

@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<link rel="icon" type="image/png" href="/customize/favicon/main-favicon.png" id="favicon"/>
<script src="/customize/pre-loading.js?ver=1.1"></script>
<link href="/customize/src/pre-loading.css?ver=1.0" rel="stylesheet" type="text/css">
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.5"></script>
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.7"></script>
</head>
<body class="html">
<noscript></noscript>

View File

@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<link rel="icon" type="image/png" href="/customize/favicon/main-favicon.png" id="favicon"/>
<script src="/customize/pre-loading.js?ver=1.1"></script>
<link href="/customize/src/pre-loading.css?ver=1.0" rel="stylesheet" type="text/css">
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.5"></script>
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.7"></script>
</head>
<body class="html">
<noscript></noscript>

View File

@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<link rel="icon" type="image/png" href="/customize/favicon/main-favicon.png" id="favicon"/>
<script src="/customize/pre-loading.js?ver=1.1"></script>
<link href="/customize/src/pre-loading.css?ver=1.0" rel="stylesheet" type="text/css">
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.5"></script>
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.7"></script>
</head>
<body class="html">
<noscript></noscript>

View File

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

View File

@ -28,8 +28,9 @@ define([
var premiumButton = h('a', {
href: accounts.upgradeURL,
target: '_blank',
rel: 'noopener noreferrer'
}, h('button.cp-features-register-button', Msg.features_f_subscribe));
rel: 'noopener noreferrer',
class: 'cp-features-register-button',
}, Msg.features_f_subscribe);
var groupItemTemplate = function (title, content) {
return h('li.list-group-item', [
@ -119,14 +120,15 @@ define([
h('div.card-body',[
h('div.cp-features-register#cp-features-register', [
h('a', {
href: '/register/'
}, h('button.cp-features-register-button', Msg.features_f_register))
href: '/register/',
class: 'cp-features-register-button',
}, Msg.features_f_register)
]),
]),
]),
]);
var premiumFeatures =
h('div.col-12.col-sm-4.cp-anon-user',[
h('div.col-12.col-sm-4.cp-premium-user',[
h('div.card',[
h('div.title-card',[
h('h3.text-center',Msg.features_premium)

View File

@ -153,10 +153,10 @@ define([
if (Pages.areSubscriptionsAllowed() && !LocalStore.getPremium()) {
var sub = h('div.cp-sub-prompt', [
h('span', Msg.home_morestorage),
h('a', {href:"/accounts/"}, h('button', [
h('a', {href:"/accounts/", class:'subscribe-btn'}, [
h('i.fa.fa-ticket'),
Msg.features_f_subscribe
]))
])
]);
return sub;
} else {

View File

@ -200,8 +200,8 @@
}
.cp-usergrid-user, textarea, a, .fa-times {
outline: none;
&:focus {
outline: @cryptpad_color_brand solid 2px;
&:focus-visible {
outline: @variables_focus_style;
}
}
}
@ -244,8 +244,8 @@
}
}
outline: none;
&:focus {
outline: @cryptpad_color_brand solid 2px;
&:focus-visible {
outline: @variables_focus_style;
}
}
span.alertify-tabs-active {
@ -275,7 +275,7 @@
.tools_placeholder-color();
outline: none;
&:focus-visible {
outline: @cryptpad_color_brand solid 2px;
outline: @variables_focus_style;
}
}

View File

@ -129,8 +129,8 @@
box-sizing: border-box;
}
outline: none;
&:focus {
outline: @cryptpad_color_brand solid 2px;
&:focus-visible {
outline: @variables_focus_style;
}
}
@ -217,8 +217,8 @@
height: var(--checkmark-dim1);
}
outline: none;
&:focus {
outline: @cryptpad_color_brand solid 2px;
&:focus-visible {
outline: @variables_focus_style;
}
}

View File

@ -30,6 +30,11 @@
.cp-unselectable {
.tools_unselectable();
}
.btn-primary{
&:focus-visible {
outline: @variables_focus_style;
}
}
/* local mixins */
@drive_icon-margin: 10px;
@ -329,11 +334,10 @@
overflow: hidden;
text-overflow: ellipsis;
.leftside-menu-category_main();
margin: 0;
display: flex;
align-items: center;
cursor: pointer;
margin-left: -5px;
margin: 0 0 2px -5px;
padding-left: 5px;
.fa, .cptools {
display: inline-block;
@ -348,6 +352,9 @@
overflow: hidden;
text-overflow: ellipsis;
}
&:focus-visible {
outline: @variables_focus_style;
}
}
}
}
@ -373,6 +380,9 @@
padding-left: 20px;
.leftside-menu-category_main();
margin: 0;
&:focus-visible {
outline: @variables_focus_style;
}
}
}
}
@ -399,6 +409,10 @@
position: relative;
top: -1px;
}
&:focus-visible {
outline: @variables_focus_style;
border-radius: @variables_radius_S;
}
}
.cp-app-drive-tree-docs {
box-shadow: @cryptpad_ui_shadow;
@ -953,6 +967,9 @@
li, li .fa, li .cptools {
cursor: pointer;
border-radius: @variables_radius;
&:focus {
outline: @cryptpad_color_brand solid 2px;
}
}
&> p {
display: flex;

View File

@ -28,6 +28,9 @@
}
&.tui-full-calendar-content {
font-size: @colortheme_app-font-size;
&:focus-visible {
outline: @variables_focus_style;
}
}
&[readonly] {
//margin-top:1rem;
@ -138,8 +141,8 @@
color: @cryptpad_text_col;
}
outline: none;
&:focus {
outline: @cryptpad_color_brand solid 2px;
&:focus-visible {
outline: @variables_focus_style;
}
}
}
@ -148,8 +151,8 @@
background-color: @cp_buttons-cancel;
box-sizing: border-box;
align-items: center;
padding: 0 6px;
line-height: 36px;
line-height: 20px;
padding: 8px 6px;
white-space: nowrap;
text-align: center;
text-transform: uppercase;
@ -257,8 +260,8 @@
outline: none;
&:focus {
outline: @cryptpad_color_brand solid 2px;
&:focus-visible {
outline: @variables_focus_style;
}
&::-moz-focus-inner {
border: 0;

View File

@ -33,8 +33,8 @@
color: @cp_drive-fg;
}
&.cp-icons-element-selected {
background: @cp_drive-icon-hover;
color: @cp_drive-fg;
outline: @cryptpad_color_brand solid 2px;
}
.fa, .cptools {
display: block;

View File

@ -6,6 +6,7 @@
@import (reference) "./colortheme-all.less";
@import (reference) "./font.less";
@import (reference) "./variables.less";
@infopages-radius: 5px;
@infopages-radius-L: 10px;
@ -46,6 +47,10 @@ body.html {
a:hover {
opacity: 1;
}
a:focus-visible {
outline: @variables_focus_style;
border-radius: @variables_radius;
}
border: 0;
padding: 0;
margin: 0;
@ -231,6 +236,9 @@ body.html {
border: 0px;
border-radius: @infopages-radius;
padding: 0.5em 0.7em;
&:focus-visible {
outline: @variables_focus_style;
}
}
}
.cp-footer-version {
@ -293,6 +301,9 @@ body.html {
&:hover {
color: @cryptpad_text_col;
}
&:focus-visible {
outline: @variables_focus_style;
}
i {
margin-right: 5px;
}

View File

@ -47,6 +47,9 @@
align-items: center;
.leftside-menu-category_main();
box-shadow: @cryptpad_ui_shadow;
&:focus-visible {
outline: @variables_focus_style;
}
}
}
&.cp-leftside-narrow {

View File

@ -81,8 +81,8 @@
padding-left: 4px;
vertical-align: middle;
outline: none;
&:focus {
outline: @cryptpad_color_brand solid 2px;
&:focus-visible {
outline: @variables_focus_style;
}
}
.close {

View File

@ -29,35 +29,6 @@
font: @colortheme_app-font;
}
@media screen and (max-width: 870px) {
flex-flow: column;
.cp-toolbar-history-actions {
width: 100%;
.cp-history-actions-first {
margin-right: 0 !important;
}
}
.cp-toolbar-history-timeline {
width: ~"calc(100% - 20px)";
margin-right: 10px !important;
}
}
@media screen and (max-height: 500px) {
padding-top: 0px;
.cp-history-timeline-line {
display: none !important;
}
.cp-toolbar-history-timeline {
width: 100% !important;
margin: 0 !important;
}
.cp-history-timeline-actions {
margin-left: 0 !important;
}
}
&.cp-history-init {
padding: 0;
height: 32px;
@ -102,8 +73,8 @@
text-transform: uppercase;
display: inline-flex;
align-items: center;
.fa:not(:last-child) {
margin-right: 5px;
i:not(:last-child) {
margin-right: 0.5rem;
}
&:hover {
background-color: fade(@cp_toolbar-fg, 30%);
@ -293,6 +264,39 @@
left: ~"calc(50% - 6px)";
}
}
@media screen and (max-width: 870px) {
flex-flow: column;
.cp-toolbar-history-actions {
width: 100%;
margin: 0.6rem 0;
height: auto;
.cp-history-actions-first {
margin-right: 0 !important;
}
button {
margin: 0.1rem;
}
}
.cp-toolbar-history-timeline {
width: ~"calc(100% - 20px)";
margin-right: 10px !important;
}
}
@media screen and (max-height: 500px) {
padding-top: 0px;
.cp-history-timeline-line {
display: none !important;
}
.cp-toolbar-history-timeline {
width: 100% !important;
margin: 0 !important;
}
.cp-history-timeline-actions {
margin-left: 0 !important;
}
}
}
}

View File

@ -77,8 +77,8 @@
&:hover {
background-color: contrast(@cp_toolbar-bg, darken(@cp_toolbar-bg, 5%), lighten(@cp_toolbar-bg, 5%));
}
&:focus {
outline: @cryptpad_color_brand solid 2px;
&:focus-visible {
outline: @variables_focus_style;
}
}
button:nth-of-type(1) {
@ -373,7 +373,7 @@
* {
outline-style: none;
&:focus-visible {
outline: @cryptpad_color_brand solid 2px;
outline: @variables_focus_style;
}
}
@ -676,6 +676,10 @@
overflow: hidden;
text-overflow: ellipsis;
border-collapse: collapse;
&:focus {
margin: 2px;
border-radius: @variables_radius;
}
}
input {
color: @cryptpad_text_col;
@ -983,11 +987,8 @@
height: @toolbar_line-height;
display: inline-flex;
align-items: center;
.fa, .cptools {
margin-right: 5px;
}
.cp-dropdown-button-title .cp-icon {
margin-left: 5px;
i ~ span {
margin-left: 0.5rem;
}
&:hover {
background-color: fade(@cp_toolbar-bottom-bg, 70%);
@ -1109,15 +1110,6 @@
}
}
.cp-toolbar-bottom-left {
.cp-toolbar-appmenu, .cp-toolbar-file {
button {
&::before, .fa {
min-width: 20px;
text-align: center;
margin-right: 0;
}
}
}
.cp-toolbar-file {
order: 1;
}

View File

@ -18,6 +18,8 @@
@variables_shadow: 0 8px 32px 0 @cp_shadow-color;
// Rounded corners
@variables_radius_S: 3px;
@variables_radius: 5px;
@variables_radius_L: 10px;
@variables_focus_style: @cryptpad_color_brand solid 2px;

View File

@ -15,12 +15,19 @@
padding: 20px;
}
.cp-features-register-button {
text-decoration: none;
text-align: center;
background-color: @cp_buttons-primary;
color: @cryptpad_text_col;
font-size: 20px;
color: @cryptpad_color_white;
background: @cryptpad_color_brand;
border-radius: 0;
&:hover {
cursor: pointer;
margin: 10px 0;
border-radius: @infopages-radius;
padding: 10px 20px;
&:hover, &:focus {
background-color: contrast(@cp_buttons-primary-text, darken(@cp_buttons-primary, 10%), lighten(@cp_buttons-primary, 10%));
}
&:visited {
color: @cryptpad_text_col;
}
}
.cp-features-web {
@ -114,5 +121,10 @@
margin-top: 3em;
}
}
.cp-premium-user {
@media (max-width:575px) {
margin-top: 3em;
}
}
}

View File

@ -255,9 +255,10 @@
* {
display: block;
}
button {
background-color: @cryptpad_color_link;
color: @cryptpad_text_col_inv;
.subscribe-btn {
text-align: center;
background-color: @cp_buttons-primary;
color: @cryptpad_text_col;
font-size: 1.4rem;
margin: 10px 0;
border-radius: @infopages-radius-L;
@ -267,8 +268,7 @@
margin-right: 10px;
}
&:hover, &:focus {
background-color: @cp_static-card-bg;
color: @cryptpad_text_col;
background-color: contrast(@cp_buttons-primary-text, darken(@cp_buttons-primary, 10%), lighten(@cp_buttons-primary, 10%));
}
}
}

View File

@ -43,13 +43,10 @@
nav {
display: flex;
align-items: center;
justify-content: flex-end;
}
@media screen and (max-width: 600px) {
nav .btn-danger {
line-height: inherit;
align-items: flex-end;
.btn-confirm {
white-space: normal;
}
}

View File

@ -68,13 +68,10 @@
nav {
display: flex;
align-items: center;
justify-content: flex-end;
}
@media screen and (max-width: 600px) {
nav .btn-danger {
line-height: inherit;
align-items: flex-end;
.btn-confirm {
white-space: normal;
}
}

View File

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

View File

@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<link rel="icon" type="image/png" href="/customize/favicon/main-favicon.png" id="favicon"/>
<script src="/customize/pre-loading.js?ver=1.1"></script>
<link href="/customize/src/pre-loading.css?ver=1.0" rel="stylesheet" type="text/css">
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.5"></script>
<script async data-bootload="/customize/template.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.7"></script>
</head>
<body class="html">
<noscript></noscript>

View File

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

View File

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

View File

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

View File

@ -22,16 +22,16 @@ if [ ! -f "$CPAD_CONF" ]; then
eg: docker run -v /path/to/config.js:/cryptpad/config/config.js \n\
#################################################################### \n"
cp "$CPAD_HOME"/config/config.example.js "$CPAD_CONF"
cp "$CPAD_HOME"/config/config.example.js "$CPAD_CONF"
sed -i -e "s@\(httpUnsafeOrigin:\).*[^,]@\1 '$CPAD_MAIN_DOMAIN'@" \
sed -i -e "s@\(httpUnsafeOrigin:\).*[^,]@\1 '$CPAD_MAIN_DOMAIN'@" \
-e "s@\(^ *\).*\(httpSafeOrigin:\).*[^,]@\1\2 '$CPAD_SANDBOX_DOMAIN'@" "$CPAD_CONF"
fi
cd $CPAD_HOME
if [ "$CPAD_INSTALL_ONLYOFFICE" == "yes" ]; then
./install-onlyoffice.sh --accept-license
./install-onlyoffice.sh --accept-license --trust-repository
fi
npm run build

10
docs/community/README.md Normal file
View File

@ -0,0 +1,10 @@
<!--
SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-->
# Community configuration files
This folder is listing community-contributed configuration files. They **aren't** supported and are provided as-is, without any warranty.
If you are using CryptPad in production and require professional support please contact sales@cryptpad.org

View File

@ -0,0 +1,245 @@
# SPDX-FileCopyrightText: 2024 XWiki CryptPad Team <contact@cryptpad.org> and contributors
#
# SPDX-License-Identifier: AGPL-3.0-or-later
# This file is included strictly as an example of how Caddy can be configured
# to work with CryptPad. This example WILL NOT WORK AS IS. For best results,
# compare the sections of this configuration file against a working CryptPad
# installation (http server by the Nodejs process). If you are using CryptPad
# in production and require professional support please contact sales@cryptpad.org
(trustedProxies) {
# Force Caddy to accept `X-Forwarded-For` and other origin headers.
# Modify the line below if you want to restrict the scope of direct downstream sending these headers.
trusted_proxies 0.0.0.0/0 ::/0
}
# Caddy does not have variables for server names, so domains need to be hardcoded.
# You can bulk replace "your-main-domain.com" and "your-sandbox-domain.com" safely.
your-main-domain.com:443,
your-sandbox-domain.com:443 {
# Define your certificates below.
# No need to adjust TLS configurations, as the defaults in Caddy are already secure.
tls /path/to/fullchain/publicKey.pem /path/to/certificate/privateKey.pem
# Enable HSTS.
# Do not enable this line when configuring over mixnet, e.g. Tor.
header Strict-Transport-Security "max-age=63072000; includeSubDomains"
# Security headers
header X-XSS-Protection "1; mode=block"
header X-Content-Type-Options "nosniff"
header Access-Control-Allow-Credentials "true"
#header X-Frame-Options "SAMEORIGIN"
# OnlyOffice fonts may be loaded from both domains.
@onlyOfficeFonts {
path_regexp "^\\/common\\/onlyoffice\\/.*\\/fonts\\/.*$"
}
header Access-Control-Allow-Origin "*"
# By default CryptPad forbids remote domains from embedding CryptPad documents in iframes.
# The sandbox domain must always be permitted in order for the platform to function.
# If you wish to enable remote embedding you may change the value below to "*"
# as per the commented value.
header ?Access-Control-Allow-Origin "https://your-sandbox-domain.com"
#header ?Access-Control-Allow-Origin "*"
# Opt out of Google's FLoC Network
header Permissions-Policy "interest-cohort=()"
# Enable SharedArrayBuffer in Firefox (for .xlsx export)
header ?Cross-Origin-Resource-Policy "cross-origin"
header ?Cross-Origin-Embedder-Policy "require-corp"
# Specify the relative path to root of your custom error page.
# This error page won't only be served for 404 errors.
handle_errors {
rewrite * /error.htm
header Cache-Control "no-cache, no-store"
file_server
templates
}
# Insert the path to your CryptPad repository root here
root /home/cryptpad/cryptpad
# Any static assets loaded with "vers=" in their URL will be cached for a year
@staticAssets {
query "ver=*"
}
header @staticAssets Cache-Control "max-age=31536000"
vars {
# CSS can be dynamically set inline, loaded from the same domain, or from your main domain.
styleSrc "'unsafe-inline' 'self' https://your-main-domain.com"
# connect-src restricts URLs which can be loaded using script interfaces.
# If you have configured your instance to use a dedicated file delivery domain or API domain,
# you will need to add them below.
connectSrc "'self' https://your-main-domain.com blob: wss://api.your-main-domain.com https://your-sandbox-domain.com"
# Fonts can be loaded from data-URLs or the main domain.
fontSrc "'self' data: https://your-main-domain.com"
# Images can be loaded from anywhere, though we'd like to deprecate this as it allows
# the use of images for tracking.
imgSrc "'self' data: blob: https://your-main-domain.com"
# frame-src specifies valid sources for nested browsing contexts.
# This prevents loading any iframes from anywhere other than the sandbox domain.
frameSrc "'self' https://your-sandbox-domain.com blob:"
# media-src specifies valid sources for loading media using video or audio.
mediaSrc "blob:"
# child-src defines valid sources for webworkers and nested browser contexts.
# It is deprecated in favour of worker-src and frame-src.
childSrc "https://your-main-domain.com"
# worker-src valid sources for Worker, Shared Worker, or Service Worker scripts.
# Supercedes child-src, but is unfortunately not yet universally supported.
workerSrc "'self'"
# script-src specifies valid sources for JavaScript, including inline handlers.
scriptSrc "'self' resource: https://your-main-domain.com"
# frame-ancestors specifies which origins can embed your CryptPad instance.
# This must include 'self' and your main domain (over HTTPS) in order for CryptPad to work,
# if you have enabled remote embedding via the admin panel, then this must be more permissive.
# Note: cryptpad.fr permits web pages served via https: and vector: (element desktop app)
frameAncestors "'self' https://your-main-domain.com"
#frameAncestors "'self' https: vector:"
# A few assets are loaded via the sandbox domain.
# They unfortunately still require exceptions to the sandboxing to work correctly.
# Everything except the sandbox domain is a privileged scope, as they might be used to handle keys.
# Unsafe iframes are exceptions. Office file formats are converted outside of the sandboxed scope,
# because of bugs in Chromium-based browsers that incorrectly ignore headers supposed to enable
# the use of some modern APIs, that are required 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.
# Privileged contexts allow a few more rights than unprivileged contexts, though limits are still applied.
scriptSrcUnsafe "'self' 'unsafe-eval' 'unsafe-inline' resource: https://your-main-domain.com"
}
# Finally, set all the security rules you have composed above.
@privilegedScope1 {
host "your-sandbox-domain.com"
path_regexp "^\\/(sheet|doc|presentation)\\/inner.html.*$"
}
@privilegedScope2 {
host "your-sandbox-domain.com"
path_regexp "^\\/common\\/onlyoffice\\/.*\\/.*\\.html.*$"
}
@privilegedScope3 {
host "your-sandbox-domain.com"
path_regexp "^\\/unsafeiframe\\/inner\\.html.*$"
}
header @privilegedScope1 Content-Security-Policy "default-src 'none'; child-src {vars.childSrc}; worker-src {vars.workerSrc}; media-src {vars.mediaSrc}; style-src {vars.styleSrc}; script-src {vars.scriptSrcUnsafe}; connect-src {vars.connectSrc}; font-src {vars.fontSrc}; img-src {vars.imgSrc}; frame-src {vars.frameSrc}; frame-ancestors {vars.frameAncestors}"
header @privilegedScope2 Content-Security-Policy "default-src 'none'; child-src {vars.childSrc}; worker-src {vars.workerSrc}; media-src {vars.mediaSrc}; style-src {vars.styleSrc}; script-src {vars.scriptSrcUnsafe}; connect-src {vars.connectSrc}; font-src {vars.fontSrc}; img-src {vars.imgSrc}; frame-src {vars.frameSrc}; frame-ancestors {vars.frameAncestors}"
header @privilegedScope3 Content-Security-Policy "default-src 'none'; child-src {vars.childSrc}; worker-src {vars.workerSrc}; media-src {vars.mediaSrc}; style-src {vars.styleSrc}; script-src {vars.scriptSrcUnsafe}; connect-src {vars.connectSrc}; font-src {vars.fontSrc}; img-src {vars.imgSrc}; frame-src {vars.frameSrc}; frame-ancestors {vars.frameAncestors}"
header ?Content-Security-Policy "default-src 'none'; child-src {vars.childSrc}; worker-src {vars.workerSrc}; media-src {vars.mediaSrc}; style-src {vars.styleSrc}; script-src {vars.scriptSrc}; connect-src {vars.connectSrc}; font-src {vars.fontSrc}; img-src {vars.imgSrc}; frame-src {vars.frameSrc}; frame-ancestors {vars.frameAncestors}"
# Add support for .mjs files used by pdfjs
@fileModuleJS {
path "*.mjs"
}
header @fileModuleJS Content-Type "application/javascript"
# The Node.js process can handle all traffic, whether accessed over websocket or as static assets.
# We prefer to serve static content from Caddy directly, and to leave the API server to handle the
# the dynamic content that only it can manage. This is primarily for optimization.
handle /cryptpad_websocket/* {
reverse_proxy * {
to 127.0.0.1:3003
header_up Host "{host}"
header_up X-Real-IP "{remote_host}"
# Caddy supports WebSockets directly. No additional headers are needed.
import trustedProxies
}
}
handle_path /customize.dist/* {
# This is needed in order to prevent infinite recursion between /customize/ and the root.
}
# Try to load customizeable content via /customize/ and fall back to the default content located
# at /customize.dist/ .
# This is what allows you to override behaviour.
handle_path /customize/* {
try_files /customize/{path} /customize.dist/{path}
file_server {
index index.html index.htm default.html default.htm
}
}
# /api/config is loaded once per page load, and is used to retrieve the caching variable,
# which is applied to every other resource loaded during that session.
@sharedReverseProxy {
path /api/*
path /extensions.js
}
handle @sharedReverseProxy {
reverse_proxy * {
to 127.0.0.1:3000
header_up Host "{host}"
header_up X-Real-IP "{remote_host}"
# These settings prevent both Caddy and the API server from setting duplicate headers.
header_down Cross-Origin-Resource-Policy cross-origin
header_down Cross-Origin-Embedder-Policy require-corp
import trustedProxies
}
}
# Requests for blobs and blocks are now proxied to the API server.
# This simplifies Caddy path configuration, in the event they are being hosted in a non-standard location
# 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.
@blobsAndBlocks {
path /blob/*
path /block/*
}
handle @blobsAndBlocks {
@corsPreflight {
method OPTIONS
}
handle @corsPreflight {
header Access-Control-Allow-Origin "https://your-sandbox-domain.com"
header Access-Control-Allow-Credentials "true"
header Access-Control-Allow-Methods "GET, POST, OPTIONS"
header Access-Control-Allow-Headers "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range"
header Access-Control-Max-Age "1728000"
header Content-Type "application/octet-stream; charset=utf-8"
header Content-Length "0"
respond 204
}
reverse_proxy * {
to 127.0.0.1:3000
# Preventing these headers from getting duplicated, since we are proxying to the API server.
header_down -X-Content-Type-Options
header_down -Access-Control-Allow-Origin
header_down -Permissions-Policy
header_down -X-XSS-Protection
header_down -Cross-Origin-Resource-Policy
header_down -Cross-Origin-Embedder-Policy
}
}
# The Node.JS server has some built-in forwarding rulesets to prevent URLs not suffixed with a slash
# from resulting in a 404 error. This simply adds a trailing slash to a variety of applications.
@preventNotFound {
path_regexp "^/(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)$"
}
redir @preventNotFound "{path}/"
# Enable file serving
file_server {
index index.html index.htm default.html default.htm
}
}

View File

@ -0,0 +1,64 @@
# SPDX-FileCopyrightText: 2024 XWiki CryptPad Team <contact@cryptpad.org> and contributors
#
# SPDX-License-Identifier: AGPL-3.0-or-later
# This file is included strictly as an example of how Caddy can be configured
# to work with CryptPad. This example WILL NOT WORK AS IS. For best results,
# compare the sections of this configuration file against a working CryptPad
# installation (http server by the Nodejs process). If you are using CryptPad
# in production and require professional support please contact sales@cryptpad.org
(trustedProxies) {
# Force Caddy to accept `X-Forwarded-For` and other origin headers.
# Modify the line below if you want to restrict the scope of direct downstream sending these headers.
trusted_proxies 0.0.0.0/0 ::/0
}
# Caddy does not have variables for server names, so domains need to be hardcoded.
# You can bulk replace "your-main-domain.com" and "your-sandbox-domain.com" safely.
your-main-domain.com:443,
your-sandbox-domain.com:443 {
# Define your certificates below.
# No need to adjust TLS configurations, as the defaults in Caddy are already secure.
tls /path/to/fullchain/publicKey.pem /path/to/certificate/privateKey.pem
# Enable HSTS.
# Do not enable this line when configuring over mixnet, e.g. Tor.
header Strict-Transport-Security "max-age=63072000; includeSubDomains"
# Specify the relative path to root of your custom error page.
# This error page won't only be served for 404 errors.
handle_errors {
rewrite * /error.htm
header Cache-Control "no-cache, no-store"
file_server
templates
}
# The Node.js process can handle all traffic, whether accessed over websocket or as static assets.
reverse_proxy /cryptpad_websocket/* {
to 127.0.0.1:3003
header_up Host "{host}"
header_up X-Real-IP "{remote_host}"
# Caddy supports WebSockets directly. No additional headers are needed.
import trustedProxies
}
reverse_proxy * {
to 127.0.0.1:3000
header_up Host "{host}"
header_up X-Real-IP "{remote_host}"
# These settings prevent both Caddy and the API server from setting duplicate headers.
header_down Cross-Origin-Resource-Policy cross-origin
header_down Cross-Origin-Embedder-Policy require-corp
import trustedProxies
}
# Enable file serving
file_server {
index index.html index.htm default.html default.htm
}
}

View File

@ -4,7 +4,7 @@
# This file is included strictly as an example of how Apache httpd can be
# configured to work with CryptPad. If you are using CryptPad in production
# and require professional support please contact sales@cryptpad.fr
# and require professional support please contact sales@cryptpad.org
# This configuration requires mod_ssl, mod_socache_shmcb, mod_proxy,
# mod_proxy_http and mod_headers

View File

@ -8,6 +8,21 @@
# installation (http server by the Nodejs process). If you are using CryptPad
# in production and require professional support please contact sales@cryptpad.fr
server {
listen 80;
listen [::]:80;
server_name your-main-domain.com your-sandbox-domain.com;
access_log /dev/null;
error_log /dev/null emerg;
# Let's Encrypt webroot
include letsencrypt-webroot;
# Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;

View File

@ -8,6 +8,21 @@
# installation (http server by the Nodejs process). If you are using CryptPad
# in production and require professional support please contact sales@cryptpad.fr
server {
listen 80;
listen [::]:80;
server_name your-main-domain.com your-sandbox-domain.com;
access_log /dev/null;
error_log /dev/null emerg;
# Let's Encrypt webroot
include letsencrypt-webroot;
# Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;

View File

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

View File

@ -190,6 +190,9 @@ nThen(function (w) {
removed++;
}
});
if (Env.store) {
Env.store.closeInactiveChannels(active);
}
if (removed) {
Env.Log.info("CLEANED_ACTIVE_CHANNELS_MAP", {removed});
}

View File

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

View File

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

View File

@ -147,6 +147,7 @@ const dropChannel = HK.dropChannel = function (Env, chanName) {
expireChannel(Env, chanName);
}, TEMPORARY_CHANNEL_LIFETIME);
}
if (Env.store) { Env.store.closeChannel(chanName, function () {}); }
};
/* checkExpired

View File

@ -286,6 +286,16 @@ var closeChannel = function (env, channelName, cb) {
}
};
var closeInactiveChannels = function (env, schedule, active) {
Object.keys(env.channels).forEach(channelName => {
if (!active.includes(channelName)) {
schedule.ordered(channelName, function (next) {
closeChannel(env, channelName, next);
});
}
});
};
var clearOffset = function (env, channelId, cb) {
var path = mkOffsetPath(env, channelId);
// we should always be able to recover from invalid offsets, so failure to delete them
@ -429,7 +439,27 @@ How to proceed
*/
let requiresChannel = true;
let all = [];
nThen(function (w) {
let first = true;
getDedicatedMetadata(env, channelId, (err, line) => {
if (first && !err) {
if (!Array.isArray(line)) {
requiresChannel = false;
}
first = false;
}
all.push({err, line});
}, w(function (err) {
if (err) {
// stream errors?
w.abort();
return void cb(err);
}
}));
}).nThen(function (w) {
if (!requiresChannel) { return; }
// returns the first line of a channel, parsed...
getChannelMetadata(env, channelId, w(function (err, data) {
if (err) {
@ -445,13 +475,10 @@ How to proceed
handler(null, data);
}));
}).nThen(function () {
getDedicatedMetadata(env, channelId, handler, function (err) {
if (err) {
// stream errors?
return void cb(err);
}
cb();
all.forEach(({err, line}) => {
handler(err, line);
});
cb();
});
};
@ -1445,6 +1472,9 @@ module.exports.create = function (conf, _cb) {
closeChannel(env, channelName, Util.both(cb, next));
});
},
closeInactiveChannels: function (active) {
closeInactiveChannels(env, schedule, active);
},
// write to a log file
log: function (channelName, content, cb) {
// you probably want the events in your log to be in the correct order.

1913
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"name": "cryptpad",
"description": "a collaborative office suite that is end-to-end encrypted and open-source",
"version": "2024.6.0",
"version": "2024.9.0",
"license": "AGPL-3.0+",
"repository": {
"type": "git",
@ -22,7 +22,7 @@
"chainpad-crypto": "^0.2.5",
"chainpad-listmap": "^1.1.0",
"chainpad-netflux": "^1.2.0",
"chainpad-server": "^5.2.1",
"chainpad-server": "^5.2.2",
"ckeditor": "npm:ckeditor4@~4.22.1",
"codemirror": "^5.19.0",
"components-font-awesome": "^4.6.3",
@ -30,7 +30,7 @@
"croppie": "^2.5.0",
"dragula": "3.7.2",
"drawio": "github:cryptpad/drawio-npm#npm-21.8.2+5",
"express": "~4.19.2",
"express": "~4.20.0",
"file-saver": "1.3.1",
"fs-extra": "^7.0.0",
"get-folder-size": "^2.0.1",
@ -44,7 +44,7 @@
"localforage": "^1.5.2",
"marked": "^4.3.0",
"mathjax": "3.0.5",
"netflux-websocket": "^1.2.0",
"netflux-websocket": "^1.2.1",
"notp": "^2.0.3",
"nthen": "0.1.8",
"open-sans-fontface": "^1.4.0",
@ -54,7 +54,7 @@
"prompt-confirm": "^2.0.4",
"pull-stream": "^3.6.1",
"require-css": "0.1.10",
"requirejs": "2.3.5",
"requirejs": "2.3.7",
"requirejs-plugins": "^1.0.2",
"saferphore": "0.0.1",
"scrypt-async": "1.2.0",

View File

@ -28,7 +28,7 @@ The most recent version and all past release notes can be found on the [releases
## Setup using Docker
You can find `Dockerfile`, `docker-compose.yml` and `docker-entrypoint.sh` files at the root of this repository. We also publish every release on [Docker Hub](https://hub.docker.com/r/cryptpad/cryptpad) as AMD64 & ARM64 official images.
You can find `Dockerfile`, `docker-compose.yml` and `docker-entrypoint.sh` files at the root of this repository. We also publish every release on [Docker Hub](https://hub.docker.com/r/cryptpad/cryptpad) as AMD64 & ARM64 official images.
Previously, Docker images were community maintained, had their own repository and weren't official supported. We changed that with v5.4.0 during July 2023. Thanks to @promasu for all the work on the community images.

View File

@ -5,6 +5,6 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
for f in ../customize.dist/favicon/*.png; do
base="$(basename $f ".png")"
magick convert $f -define icon:auto-resize=16,24,32,48,64,72,96,128,256 "$base.ico"
base="$(basename $f ".png")"
magick convert $f -define icon:auto-resize=16,24,32,48,64,72,96,128,256 "$base.ico"
done

View File

@ -0,0 +1,7 @@
{"owners":["TestOwner"],"validateKey":"TestKey","channel":"0","created":1721035117462}
[0,"test","MSG","test",1721035117969]
[0,"test","MSG","test2",1721035117969]
[0,"test","MSG","test3",1721035117969]
[0,"test","MSG","test4",1721035117969]
[0,"test","MSG","test5",1721035117969]
[0,"test","MSG","test6",1721035117969]

View File

@ -0,0 +1,5 @@
["RESTRICT_ACCESS",[true],1721035145090]
["ADD_ALLOWED",["NewAllowedKeyNewAllowedKeyNewAllowedKeyNewAl"],1721035148115]
["ADD_PENDING_OWNERS",["PendingOwner"],1721035151295]
["RESTRICT_ACCESS",[false],1721035155836]
["RESTRICT_ACCESS",[true],1721035156728]

View File

@ -0,0 +1,7 @@
{"owners":["TestOwner"],"validateKey":"TestKey","channel":"0","created":1721035117462}
[0,"test","MSG","test",1721035117969]
[0,"test","MSG","test2",1721035117969]
[0,"test","MSG","test3",1721035117969]
[0,"test","MSG","test4",1721035117969]
[0,"test","MSG","test5",1721035117969]
[0,"test","MSG","test6",1721035117969]

View File

@ -0,0 +1 @@
{"owners":["TestOwner"],"validateKey":"TestKey","channel":"0","created":1721035117462}

View File

@ -0,0 +1,6 @@
[0,"test","MSG","test",1721035117969]
[0,"test","MSG","test2",1721035117969]
[0,"test","MSG","test3",1721035117969]
[0,"test","MSG","test4",1721035117969]
[0,"test","MSG","test5",1721035117969]
[0,"test","MSG","test6",1721035117969]

View File

@ -0,0 +1,6 @@
{"owners":["TestOwner"],"validateKey":"TestKey","channel":"0","created":1721035117462}
["RESTRICT_ACCESS",[true],1721035145090]
["ADD_ALLOWED",["NewAllowedKeyNewAllowedKeyNewAllowedKeyNewAl"],1721035148115]
["ADD_PENDING_OWNERS",["PendingOwner"],1721035151295]
["RESTRICT_ACCESS",[false],1721035155836]
["RESTRICT_ACCESS",[true],1721035156728]

View File

@ -0,0 +1,6 @@
[0,"test","MSG","test",1721035117969]
[0,"test","MSG","test2",1721035117969]
[0,"test","MSG","test3",1721035117969]
[0,"test","MSG","test4",1721035117969]
[0,"test","MSG","test5",1721035117969]
[0,"test","MSG","test6",1721035117969]

View File

@ -0,0 +1,6 @@
[0,"test","MSG","test",1721035117969]
[0,"test","MSG","test2",1721035117969]
[0,"test","MSG","test3",1721035117969]
[0,"test","MSG","test4",1721035117969]
[0,"test","MSG","test5",1721035117969]
[0,"test","MSG","test6",1721035117969]

View File

@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and contributors
//
// SPDX-License-Identifier: AGPL-3.0-or-later
const Store = require("../../lib/storage/file");
const Meta = require("../../lib/metadata");
const nThen = require('nthen');
let chanOld = '00000000000000000000000000000000';
let chanOldUpdated = '00000000000000000000000000000001';
let chanNew = '00000000000000000000000000000002';
let chanNewUpdated = '00000000000000000000000000000003';
let chanNoMeta = '00000000000000000000000000000004';
Store.create({
filePath: './test-data/'
}, (err, store) => {
if (err) { return void console.error(err); }
const readMetadata = (channel, cb) => {
const ref = {};
const h = Meta.createLineHandler(ref, console.error);
store.readChannelMetadata(channel, h, () => {
cb(ref && ref.meta);
});
};
nThen(w => {
readMetadata(chanOld, w(meta => {
if (!meta || meta.validateKey !== "TestKey") {
console.log('OldChanNoUpdate', meta);
throw new Error("Error with old channel without metadata update");
}
}));
readMetadata(chanOldUpdated, w(meta => {
if (!meta || meta.validateKey !== "TestKey" || !meta.restricted || !meta.allowed.includes('NewAllowedKeyNewAllowedKeyNewAllowedKeyNewAl')) {
console.log('OldChanUpdate', meta);
throw new Error("Error with old channel with metadata updates");
}
}));
readMetadata(chanNew, w(meta => {
if (!meta || meta.validateKey !== "TestKey") {
console.log('NewChanNoUpdate', meta);
throw new Error("Error with new channel without metadata update");
}
}));
readMetadata(chanNewUpdated, w(meta => {
if (!meta || meta.validateKey !== "TestKey" || !meta.restricted || !meta.allowed.includes('NewAllowedKeyNewAllowedKeyNewAllowedKeyNewAl')) {
console.log('NewChanUpdate', meta);
throw new Error("Error with new channel with metadata updates");
}
}));
readMetadata(chanNoMeta, w(meta => {
if (meta && Object.keys(meta).length) {
console.log('NoMetadataChan', meta);
throw new Error("Error with channel without metadata");
}
}));
}).nThen(() => {
console.log('Success');
process.exit(1);
});
});

View File

@ -22,12 +22,48 @@
flex-flow: column;
font: @colortheme_app-font;
input, textarea, .cp-appblock {
&:focus-visible {
outline: @variables_focus_style;
}
}
.cp-admin-customize-logo {
padding: 1em;
img {
max-height: 250px;
}
}
#cp-sidebarlayout-container {
#cp-sidebarlayout-rightside {
.cp-sidebarlayout-element[data-item] {
div#cp-admin-table-container {
overflow-x: auto;
.cp-sidebar-table#cp-admin-table {
tr {
display: flex;
}
th, td {
flex: 1;
word-wrap: break-word;
white-space: normal;
min-width: 13rem;
margin-right: 0px;
}
@media (max-width: @browser_media-not-small) {
width: 100%;
tr {
width: 100%;
}
th, td {
margin-right: 0;
min-width: 7rem;
}
}
}
}
}
}
}
.cp-admin-color-current {
width: 20px;
height: 20px;

View File

@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<meta name="referrer" content="no-referrer" />
<script src="/customize/pre-loading.js?ver=1.1"></script>
<link href="/customize/src/pre-loading.css?ver=1.0" rel="stylesheet" type="text/css">
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.5"></script>
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.7"></script>
<link href="/customize/src/outer.css?ver=1.3.2" rel="stylesheet" type="text/css">
</head>
<body>

View File

@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script src="/customize/pre-loading.js?ver=1.1"></script>
<link href="/customize/src/pre-loading.css?ver=1.0" rel="stylesheet" type="text/css">
<script async data-bootload="/admin/inner.js" data-main="/common/sframe-boot.js?ver=1.11" src="/components/requirejs/require.js?ver=2.3.5"></script>
<script async data-bootload="/admin/inner.js" data-main="/common/sframe-boot.js?ver=1.11" src="/components/requirejs/require.js?ver=2.3.7"></script>
<style>
.loading-hidden { display: none; }
</style>

View File

@ -578,23 +578,38 @@ define([
return APP.instanceStatus.enforceMFA;
},
query: function (val, setState) {
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE',
data: ['ENFORCE_MFA', [val]]
}, function (e, response) {
if (e || response.error) {
UI.warn(Messages.error);
console.error(e, response);
}
APP.updateStatus(function () {
setState(APP.instanceStatus.enforceMFA);
flushCache();
var isChecked = APP.instanceStatus.enforceMFA;
function showConfirmation(isChecked, setState) {
const confirmationContent = isChecked ? Messages.admin_mfa_confirm_disable : Messages.admin_mfa_confirm_enable;
UI.confirm(confirmationContent, function (confirmed) {
if (!confirmed) {
// User canceled their changes, restore the checkbox value
setState(isChecked);
return;
}
// User confirmed their changes, call the command and update the state
sFrameChan.query('Q_ADMIN_RPC', {
cmd: 'ADMIN_DECREE',
data: ['ENFORCE_MFA', [val]]
}, function (e, response) {
if (e || response.error) {
UI.warn(Messages.error);
console.error(e, response);
} else {
APP.updateStatus(function () {
setState(APP.instanceStatus.enforceMFA);
flushCache();
});
}
});
});
});
},
}
showConfirmation(isChecked, setState);
}
});
var getInstanceString = function (attr) {
var val = APP.instanceStatus[attr];
var type = typeof(val);
@ -1101,6 +1116,9 @@ define([
""
];
var list = blocks.table(header, []);
list.setAttribute('id', 'cp-admin-table');
let div = blocks.block([list]);
div.setAttribute('id', 'cp-admin-table-container');
var nav = blocks.nav([button, refreshButton]);
var form = blocks.form([
@ -1201,7 +1219,7 @@ define([
});
});
cb([form, list]);
cb([form, div]);
});
var getBlockId = (val) => {
@ -1413,6 +1431,9 @@ define([
""
];
var list = blocks.table(header, []);
list.setAttribute('id', 'cp-admin-table');
let div = blocks.block([list]);
div.setAttribute('id', 'cp-admin-table-container');
var nav = blocks.nav([button, refreshButton]);
@ -1581,7 +1602,7 @@ define([
});
});
cb([form, list]);
cb([form, div]);
});
// Msg.admin_defaultlimitHint, .admin_defaultlimitTitle
@ -1741,6 +1762,9 @@ define([
Messages.admin_note
];
var table = blocks.table(header, []);
table.setAttribute('id', 'cp-admin-table');
let div = blocks.block([table]);
div.setAttribute('id', 'cp-admin-table-container');
let $table = $(table).hide();
APP.refreshLimits = function () {
@ -1798,7 +1822,7 @@ define([
});
};
APP.refreshLimits();
cb(table);
cb(div);
});
// Msg.admin_accountMetadataHint.admin_accountMetadataTitle
@ -2713,8 +2737,7 @@ define([
}, function (e, arr) {
pre.innerText = '';
let data = arr[0];
pre.append(String(data.blocks));
pre.append(' (old value including teams: ' + String(data.users) + ')'); // XXX
pre.append(String(data.users));
});
};
onRefresh();
@ -3595,6 +3618,9 @@ define([
];
var table = blocks.table(header, []);
table.setAttribute('id', 'cp-admin-table');
let div = blocks.block([table]);
div.setAttribute('id', 'cp-admin-table-container');
const onRefresh = function () {
sFrameChan.query('Q_ADMIN_RPC', {
@ -3627,7 +3653,7 @@ define([
onRefresh();
onRefreshPerformance.reg(onRefresh);
cb(table);
cb(div);
});

View File

@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<script data-bootload="main.js" data-main="/common/boot.js" src="/components/requirejs/require.js"></script>
<script data-bootload="main.js" data-main="/common/boot.js" src="/components/requirejs/require.js?ver=2.3.7"></script>
<style>
.report {

View File

@ -9,6 +9,6 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<script data-bootload="main.js" data-main="/common/boot.js" src="/components/requirejs/require.js"></script>
<script data-bootload="main.js" data-main="/common/boot.js" src="/components/requirejs/require.js?ver=2.3.7"></script>
</head>
<body>

View File

@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" type="image/png" href="/customize/favicon/main-favicon.png" id="favicon"/>
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.5"></script>
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.7"></script>
</head>
<body class="html">
<noscript></noscript>

View File

@ -39,7 +39,7 @@
.tui-full-calendar-confirm {
span, i {
padding: 0.5rem;
padding: 0 0.5rem;
}
span {
padding-left: 0;
@ -133,6 +133,13 @@
width: 540px;
max-width: 100%;
max-height: 100%;
border-radius: @variables_radius_L;
@media screen and (max-width : @browser_media-medium-screen){
position: fixed;
top: 5.5rem;
overflow-y: auto;
max-height: calc(100% - 6rem);
}
}
}
#tui-full-calendar-popup-arrow {
@ -153,10 +160,12 @@
border-color: @cp_calendar-border !important;
}
.tui-full-calendar-popup {
border-radius: @variables_radius_L;
}
.tui-full-calendar-popup-container {
.tui-full-calendar-section-allday{
&:focus {
outline: @cryptpad_color_brand solid 2px;
}
}
min-width: 100%;
background: @cp_flatpickr-bg;
color: @cryptpad_text_col;
@ -208,6 +217,9 @@
text-overflow: ellipsis;
font: @colortheme_app-font;
padding: 0 10px;
&:focus{
outline: @cryptpad_color_brand solid 2px;
}
}
input { flex: 1; }
}
@ -403,14 +415,16 @@
& > input {
height: 40px;
}
.tui-full-calendar-content{
&:focus{
outline: @cryptpad_color_brand solid 2px;
}
}
}
// margin-bottom: 20px;
input {
width: 80px;
margin-right: 0.3rem;
}
.fa-plus{
margin-left:0.3rem;
margin-right: 0.5rem;
}
}
}
@ -633,6 +647,9 @@
color: @cp_sidebar-left-active-fg;
border: 0px;
}
&:focus-visible {
outline: @variables_focus_style;
}
i {
margin-left: 5px;
}

View File

@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<meta name="referrer" content="no-referrer" />
<script src="/customize/pre-loading.js?ver=1.1"></script>
<link href="/customize/src/pre-loading.css?ver=1.0" rel="stylesheet" type="text/css">
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.5"></script>
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.7"></script>
<link href="/customize/src/outer.css?ver=1.3.2" rel="stylesheet" type="text/css">
</head>
<body>

View File

@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script src="/customize/pre-loading.js?ver=1.1"></script>
<link href="/customize/src/pre-loading.css?ver=1.0" rel="stylesheet" type="text/css">
<script async data-bootload="/calendar/inner.js" data-main="/common/sframe-boot.js?ver=1.11" src="/components/requirejs/require.js?ver=2.3.5"></script>
<script async data-bootload="/calendar/inner.js" data-main="/common/sframe-boot.js?ver=1.11" src="/components/requirejs/require.js?ver=2.3.7"></script>
<style>
.loading-hidden { display: none; }
</style>

View File

@ -2063,6 +2063,15 @@ APP.recurrenceRule = {
editor.setOption('readOnly', false);
editor.setOption('autoRefresh', true);
editor.setOption('gutters', []);
editor.on('keydown', function (editor, e) {
if (e.which === 27) {
let $next = $(e.target).closest('.tui-full-calendar-popup-section').next();
if ($next.length) {
$next.find('#tui-full-calendar-schedule-start-date').focus();
}
e.stopPropagation();
}
});
cm.configureTheme(common, function () {});
editor.setValue(oldEventBody);
@ -2147,6 +2156,13 @@ APP.recurrenceRule = {
$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();
$el.find('.tui-full-calendar-section-allday').attr('tabindex', 0);
$el.find('.cp-calendar-close').attr('tabindex',-1);
$el.find('.tui-full-calendar-section-allday').keydown(function (e) {
if (e.which === 13) {
$(this).click();
}
});
var $container = $el.closest('.tui-full-calendar-floating-layer');
$container.addClass('cp-calendar-popup-flex');
@ -2209,6 +2225,7 @@ APP.recurrenceRule = {
setFormat(allDay);
});
});
UI.addTabListener(el);
};
var onCalendarEditPopup = function (el) {
var $el = $(el);
@ -2244,6 +2261,7 @@ APP.recurrenceRule = {
var data = ev.schedule || {};
var id = data.id;
UI.addTabListener(el);
if (!id) { return; }
if (id.indexOf('|') === -1) { return; } // Original event ID doesn't contain |

View File

@ -15,5 +15,5 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<div id="cp-progress"></div>
<iframe-placeholder>
<script type="text/javascript" src="/checkup/dependency-warning.js?ver=1.0.1"></script>
<script data-bootload="main.js" data-main="/common/boot.js" src="/components/requirejs/require.js"></script>
<script data-bootload="main.js" data-main="/common/boot.js" src="/components/requirejs/require.js?ver=2.3.7"></script>

View File

@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<html class="cp-app-noscroll cp-app-print">
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script async data-bootload="/checkup/inner.js" data-main="/common/sframe-boot.js?ver=1.11" src="/components/requirejs/require.js?ver=2.3.5"></script>
<script async data-bootload="/checkup/inner.js" data-main="/common/sframe-boot.js?ver=1.11" src="/components/requirejs/require.js?ver=2.3.7"></script>
</head>
<body class="cp-app-checkup">
</body>

View File

@ -574,7 +574,7 @@ define([
});
assert(function (cb, msg) {
var support = ApiConfig.supportMailbox;
var support = ApiConfig.supportMailboxKey;
setWarningClass(msg);
msg.appendChild(h('span', [
"This instance's encrypted support ticket functionality has not been enabled. This can make it difficult for its users to safely report issues that concern sensitive information. ",

View File

@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<script data-bootload="main.js" data-main="/common/boot.js" src="/components/requirejs/require.js"></script>
<script data-bootload="main.js" data-main="/common/boot.js" src="/components/requirejs/require.js?ver=2.3.7"></script>
</head>
<body>
<div id="cp-progress"></div>

View File

@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<meta name="referrer" content="no-referrer" />
<script src="/customize/pre-loading.js?ver=1.1"></script>
<link href="/customize/src/pre-loading.css?ver=1.0" rel="stylesheet" type="text/css">
<script async data-bootload="/common/sframe-app-outer.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.5"></script>
<script async data-bootload="/common/sframe-app-outer.js" data-main="/common/boot.js?ver=1.0" src="/components/requirejs/require.js?ver=2.3.7"></script>
<link href="/customize/src/outer.css?ver=1.3.2" rel="stylesheet" type="text/css">
<link id="favicon-ico" type="image/x-icon" rel="icon"
data-main-favicon="/customize/favicon/main-favicon-code.ico"

View File

@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script src="/customize/pre-loading.js?ver=1.1"></script>
<link href="/customize/src/pre-loading.css?ver=1.0" rel="stylesheet" type="text/css">
<script async data-bootload="/code/inner.js" data-main="/common/sframe-boot.js?ver=1.11" src="/components/requirejs/require.js?ver=2.3.5"></script>
<script async data-bootload="/code/inner.js" data-main="/common/sframe-boot.js?ver=1.11" src="/components/requirejs/require.js?ver=2.3.7"></script>
<style>
.loading-hidden { display: none; }
#editor1 { display: none; }

View File

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

View File

@ -164,12 +164,12 @@ define([
dialog.okButton = function (content, classString) {
var sel = typeof(classString) === 'string'? 'button.ok.' + classString:'button.btn.ok.primary';
return h(sel, { tabindex: '2', }, content || Messages.okButton);
return h(sel, content || Messages.okButton);
};
dialog.cancelButton = function (content, classString) {
var sel = typeof(classString) === 'string'? 'button.' + classString:'button.btn.cancel';
return h(sel, { tabindex: '1'}, content || Messages.cancelButton);
return h(sel, content || Messages.cancelButton);
};
dialog.message = function (text) {
@ -577,7 +577,7 @@ define([
return frame;
};
let addTabListener = frame => {
let addTabListener = UI.addTabListener = frame => {
// find focusable elements
let modalElements = $(frame).find('a, button, input, [tabindex]:not([tabindex="-1"]), textarea').filter(':visible').filter(':not(:disabled)');
@ -670,6 +670,7 @@ define([
$modal: $blockContainer,
show: function () {
$blockContainer.css('display', 'flex');
addTabListener($blockContainer);
},
hide: hide
};
@ -719,6 +720,7 @@ define([
Notifier.notify();
});
addTabListener(frame);
return {
element: frame,
delete: close
@ -770,7 +772,7 @@ define([
document.body.appendChild(frame);
setTimeout(function () {
$(input).select().focus();
addTabListener(frame);
Notifier.notify();
});
};
@ -778,7 +780,6 @@ define([
UI.confirm = function (msg, cb, opt, force) {
cb = cb || function () {};
opt = opt || {};
var message;
if (typeof(msg) === 'string') {
if (!force) { msg = Util.fixHTML(msg); }
@ -811,19 +812,11 @@ define([
addTabListener(frame);
frame.addEventListener('keydown', function(e) {
if (e.keyCode === 13) {
if (document.activeElement === $ok[0]) {
$ok.click();
} else if (document.activeElement === $cancel[0]) {
$cancel.click();
}
} else if (e.keyCode === 27) {
$cancel.click();
}
});
listener = listenForKeys(function () {
// Only trigger OK if cancel is not focused
if (document.activeElement === $cancel[0]) {
return void $cancel.click();
}
$ok.click();
}, function () {
$cancel.click();

View File

@ -623,13 +623,22 @@ define([
var appType = (common.getMetadataMgr().getMetadata().type || 'pad').toUpperCase();
data = data || {};
if (!callback && data.callback) { callback = data.callback; }
let makeButton = function(iconClasses, buttonClasses, title, text) {
const ariaLabel = title || text || '';
return $(h('button', {
class: buttonClasses,
title: title,
'aria-label': ariaLabel
}, [
iconClasses ? h('i', { class: iconClasses }) : null,
text ? h('span', { class: 'cp-toolbar-drawer-element' }, text) : null
]));
};
switch (type) {
case 'export':
button = $('<button>', {
'class': 'fa fa-download cp-toolbar-icon-export',
title: Messages.exportButtonTitle,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.exportButton));
button = makeButton('fa fa-download', 'cp-toolbar-icon-export', Messages.exportButtonTitle, Messages.exportButton);
button
.click(common.prepareFeedback(type))
.click(UI.clearTooltipsDelay);
@ -638,34 +647,28 @@ define([
}
break;
case 'import':
button = $('<button>', {
'class': 'fa fa-upload cp-toolbar-icon-import',
title: Messages.importButtonTitle,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.importButton));
var importer = importContent((data && data.binary) ? 'application/octet-stream' : 'text/plain', callback, {
accept: data ? data.accept : undefined,
binary: data ? data.binary : undefined
});
button = makeButton('fa fa-upload', 'cp-toolbar-icon-import', Messages.importButtonTitle, Messages.importButton);
var importer = importContent((data && data.binary) ? 'application/octet-stream' : 'text/plain', callback, {
accept: data ? data.accept : undefined,
binary: data ? data.binary : undefined
});
var handler = data.first? function () {
data.first(function () {
importer(); // Make sure we don't pass arguments to importer
});
}: importer; //importContent;
button
.click(common.prepareFeedback(type))
.click(function () {
handler();
UI.clearTooltipsDelay();
var handler = data.first? function () {
data.first(function () {
importer(); // Make sure we don't pass arguments to importer
});
}: importer; //importContent;
button
.click(common.prepareFeedback(type))
.click(function () {
handler();
UI.clearTooltipsDelay();
});
//}
break;
case 'upload':
button = $('<button>', {
'class': 'btn btn-primary new',
title: Messages.uploadButtonTitle,
}).append($('<span>', {'class':'fa fa-upload'})).append(' '+Messages.uploadButton);
button = makeButton('fa fa-upload', 'btn btn-primary new', Messages.uploadButtonTitle, Messages.uploadButton);
if (!data.FM) { return; }
var $input = $('<input>', {
'type': 'file',
@ -697,9 +700,7 @@ define([
});
break;
case 'copy':
button = $('<button>', {
'class': 'fa fa-files-o cp-toolbar-icon-import',
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.makeACopy));
button = makeButton('fa fa-files-o', 'cp-toolbar-icon-import', '', Messages.makeACopy);
button
.click(common.prepareFeedback(type))
.click(function () {
@ -710,9 +711,7 @@ define([
case 'importtemplate':
if (!AppConfig.enableTemplates) { return; }
if (!common.isLoggedIn()) { return; }
button = $('<button>', {
'class': 'fa fa-upload cp-toolbar-icon-import',
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.template_import));
button = makeButton('fa fa-upload', 'cp-toolbar-icon-import', '', Messages.template_import);
button
.click(common.prepareFeedback(type))
.click(function () {
@ -724,9 +723,7 @@ define([
case 'template':
if (!AppConfig.enableTemplates) { return; }
if (!common.isLoggedIn()) { return; }
button = $('<button>', {
'class': 'cptools cptools-new-template cp-toolbar-icon-template',
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.saveTemplateButton));
button = makeButton('cptools cptools-new-template', 'cp-toolbar-icon-template', '', Messages.saveTemplateButton);
if (data.rt || data.callback) {
button
.click(function () {
@ -774,9 +771,7 @@ define([
}
break;
case 'forget':
button = $('<button>', {
'class': "fa fa-trash cp-toolbar-icon-forget"
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.fc_delete));
button = makeButton('fa fa-trash', 'cp-toolbar-icon-forget', '', Messages.fc_delete);
callback = typeof callback === "function" ? callback : function () {};
button
.click(common.prepareFeedback(type))
@ -837,25 +832,16 @@ define([
])).click(common.prepareFeedback(type));
break;
case 'print':
button = $('<button>', {
title: Messages.printButtonTitle2,
'class': "fa fa-print cp-toolbar-icon-print",
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.printText));
button = makeButton('fa fa-print', 'cp-toolbar-icon-print', Messages.printButtonTitle2, Messages.printText);
break;
case 'history':
if (!AppConfig.enableHistory) {
button = $('<span>');
break;
}
button = $('<button>', {
title: Messages.historyButton,
'aria-label': Messages.historyButton,
'class': "fa fa-history cp-toolbar-icon-history",
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.historyText));
button = makeButton('fa fa-history', 'cp-toolbar-icon-history', Messages.historyButton, Messages.historyText, Messages.historyButton);
if (data.histConfig) {
button
.click(common.prepareFeedback(type))
.on('click', function () {
button.click(common.prepareFeedback(type)).on('click', function () {
common.getHistory(data.histConfig);
UI.clearTooltipsDelay();
});
@ -879,9 +865,10 @@ define([
if (callback) { button.click(callback); }
break;
case 'storeindrive':
button = $(h('button.cp-toolbar-storeindrive.fa.fa-hdd-o', {
button = $(h('button.cp-toolbar-storeindrive', {
style: 'display:none;'
}, [
h('i.fa.fa-hdd-o'),
h('span.cp-toolbar-name.cp-toolbar-drawer-element', Messages.toolbar_storeInDrive)
])).click(common.prepareFeedback(type)).click(function () {
$(button).hide();
@ -902,10 +889,7 @@ define([
});
break;
case 'hashtag':
button = $('<button>', {
'class': 'fa fa-hashtag cp-toolbar-icon-hashtag',
title: Messages.tags_title,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.fc_hashtag));
button = makeButton('fa fa-hashtag', 'cp-toolbar-icon-hashtag', Messages.tags_title, Messages.fc_hashtag);
button.click(common.prepareFeedback(type))
.click(function () {
common.isPadStored(function (err, data) {
@ -950,11 +934,8 @@ define([
//updateIcon(data.element.is(':visible'));
break;
case 'properties':
button = $('<button>', {
'class': 'fa fa-info-circle cp-toolbar-icon-properties',
title: Messages.propertiesButtonTitle,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'})
.text(Messages.propertiesButton))
button = makeButton('fa fa-info-circle', 'cp-toolbar-icon-properties', Messages.propertiesButtonTitle, Messages.propertiesButton);
button
.click(common.prepareFeedback(type))
.click(function () {
var isTop;
@ -970,11 +951,8 @@ define([
});
break;
case 'save': // OnlyOffice save
button = $('<button>', {
'class': 'fa fa-save',
title: Messages.settings_save,
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'})
.text(Messages.settings_save))
button = makeButton('fa fa-save', '', Messages.settings_save, Messages.settings_save);
button
.click(function() {
common.prepareFeedback(type);
UI.clearTooltipsDelay();
@ -982,10 +960,7 @@ define([
if (callback) { button.click(callback); }
break;
case 'newpad':
button = $('<button>', {
title: Messages.newButtonTitle,
'class': 'fa fa-plus cp-toolbar-icon-newpad',
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.newButton));
button = makeButton('fa fa-plus', 'cp-toolbar-icon-newpad', Messages.newButtonTitle, Messages.newButton);
button
.click(common.prepareFeedback(type))
.click(function () {
@ -994,10 +969,7 @@ define([
});
break;
case 'snapshots':
button = $('<button>', {
title: Messages.snapshots_button,
'class': 'fa fa-camera cp-toolbar-icon-snapshots',
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'}).text(Messages.snapshots_button));
button = makeButton('fa fa-camera', 'cp-toolbar-icon-snapshots', Messages.snapshots_button,Messages.snapshots_button);
button
.click(common.prepareFeedback(type))
.click(function () {
@ -1280,6 +1252,7 @@ define([
kanban: 'kanban',
form: 'form',
whiteboard: 'whiteboard',
diagram: 'diagram',
};
var href = "https://docs.cryptpad.org/en/user_guide/applications.html";
@ -1607,10 +1580,10 @@ define([
]));
if (config.caretDown) {
$button.append(h('i.fa.fa-caret-down'));
$button.prepend(h('i.fa.fa-caret-down'));
}
if (config.angleDown) {
$button.append(h('i.fa.fa-angle-down'));
$button.prepend(h('i.fa.fa-angle-down'));
}
// Menu
@ -2510,10 +2483,21 @@ define([
});
var selected = -1;
var previous = function () {
selected = (selected === 0 ? types.length : selected) - 1;
$container.find('.cp-icons-element-selected').removeClass('cp-icons-element-selected');
let element = $container.find('#cp-newpad-icons-'+selected).addClass('cp-icons-element-selected');
if (element.hasClass('cp-app-disabled')) {
previous();
}
};
var next = function () {
selected = ++selected % types.length;
$container.find('.cp-icons-element-selected').removeClass('cp-icons-element-selected');
$container.find('#cp-newpad-icons-'+selected).addClass('cp-icons-element-selected');
let element = $container.find('#cp-newpad-icons-'+selected).addClass('cp-icons-element-selected');
if (element.hasClass('cp-app-disabled')) {
next();
}
};
$modal.off('keydown');
@ -2521,7 +2505,11 @@ define([
if (e.which === 9) {
e.preventDefault();
e.stopPropagation();
next();
if (e.shiftKey) {
previous();
} else {
next();
}
return;
}
if (e.which === 13) {
@ -3559,7 +3547,7 @@ define([
$(link).click(function (e) {
e.preventDefault();
e.stopPropagation();
var obj = { pw: msg.content.password || '' };
var obj = { pw: msg.content.password || '', f: 1 };
common.openURL(Hash.getNewPadURL(msg.content.href, obj));
});
@ -3615,7 +3603,7 @@ define([
// Add the pad to your drive
// This command will also add your mailbox to the metadata log
// The callback is called when the pad is stored, independantly of the metadata command
// The callback is called when the pad is stored, independently of the metadata command
if (data.calendar) {
var calendarModule = common.makeUniversal('calendar');
var calendarData = data.calendar;

View File

@ -2619,7 +2619,8 @@ define([
disableCache: localStorage['CRYPTPAD_STORE|disableCache'],
driveEvents: !rdyCfg.noDrive, //rdyCfg.driveEvents // Boolean
lastVisit: Number(localStorage.lastVisit) || undefined,
blockId: blockId
blockId: blockId,
blockHash: blockHash
};
common.userHash = userHash || LocalStore.getUserHash();

View File

@ -3568,7 +3568,8 @@ define([
if (APP.$content.data('readOnlyFolder') || !APP.editable) { return; }
var isInRoot = currentPath[0] === ROOT;
var $element = $('<li>', {
'class': 'cp-app-drive-element-row cp-app-drive-new-ghost'
'class': 'cp-app-drive-element-row cp-app-drive-new-ghost',
'tabindex': 0
}).prepend($addIcon.clone()).appendTo($list);
$element.append($('<span>', {'class': 'cp-app-drive-element-name'})
.text(Messages.fm_newButton));
@ -3587,6 +3588,12 @@ define([
window.setTimeout(function () { modal.show(); });
addNewPadHandlers($modal, isInRoot);
});
$element.keydown(function(){
if (event.which === 13) {
event.stopPropagation();
$element.click();
}
});
};
// Drive content toolbar

View File

@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
<!DOCTYPE html>
<head>
<title>About our feedback api</title>
<script data-bootload="feedback-main.js" data-main="/common/boot.js" src="/components/requirejs/require.js"></script>
<script data-bootload="feedback-main.js" data-main="/common/boot.js" src="/components/requirejs/require.js?ver=2.3.7"></script>
<style>
body {
max-width: 60vw;

View File

@ -123,8 +123,19 @@ define([
if (!seed) { return; }
return ANIMALS[seed % ANIMALS.length] || '';
};
//this regex identifies both discord and unicode emojis (with optional skin tone modifiers) and complex zwj emoji sequences
const emojiWithZWJRegex = /(?:\p{Extended_Pictographic}(?:\p{Emoji_Modifier}|\uFE0F)?(?:\u200D\p{Extended_Pictographic}(?:\p{Emoji_Modifier}|\uFE0F)?)*|\p{Extended_Pictographic})/gu;
var getPrettyInitials = MT.getPrettyInitials = function (name) {
let matches = name.match(emojiWithZWJRegex);
if (matches && name.startsWith(matches[0])) {
return matches[0];
}
else {
//this is for removing all trailing white characters and unnecessary/redundant emojis
name = name.replace(emojiWithZWJRegex, '');
name = name.replace(/\uFE0F/g, '').replace(/\u200D/g, '').replace(/\u2060/g, '');
name = name.trim();
}
var parts = name.split(/\s+/);
var text;
if (parts.length > 1) {

View File

@ -129,6 +129,7 @@ define([
var obj = {
p: msg.content.isTemplate ? ['template'] : undefined,
t: teamNotification || undefined,
f: 1,
pw: msg.content.password || ''
};
common.openURL(Hash.getNewPadURL(msg.content.href, obj));

View File

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

View File

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

View File

@ -62,6 +62,19 @@ define([
return channels;
};
let getTeamChannels = function (ctx, teamId) {
let team = Util.find(ctx.store, ['proxy', 'teams', teamId]);
if (!team) { return []; }
let channels = [team.channel];
let roster = team.keys.roster;
channels.push({
channel: roster.channel,
lastKnownHash: roster.lastKnownHash
});
return channels;
};
var getEdPublic = function (ctx, teamId) {
if (!teamId) { return Util.find(ctx.store, ['proxy', 'edPublic']); }
@ -155,6 +168,8 @@ define([
// If account trim history, get the correct channels here
if (data.account) {
channels = getAccountChannels(ctx);
} else if (data.team) {
channels = getTeamChannels(ctx, data.team);
}
var size = 0;

View File

@ -8,7 +8,8 @@ define([
'/common/common-hash.js',
'/common/common-util.js',
'/components/chainpad-crypto/crypto.js',
], function (ApiConfig, Messaging, Hash, Util, Crypto) {
'/common/outer/login-block.js',
], function (ApiConfig, Messaging, Hash, Util, Crypto, Block) {
// Random timeout between 10 and 30 times your sync time (lag + chainpad sync)
var getRandomTimeout = function (ctx) {
@ -237,6 +238,14 @@ define([
cb(true);
};
// Encrypt the password under the right key before sending it via URL hash
var encryptPassword = function(ctx, password) {
let uHash = ctx.store.data.blockHash;
let uSecret = Block.parseBlockHash(uHash);
let key = uSecret.keys.symmetric;
return Crypto.encrypt(password, key);
};
// Hide duplicates when receiving a SHARE_PAD notification:
// Keep only one notification per channel: the stronger and more recent one
var channels = {};
@ -265,8 +274,7 @@ define([
}
if (content.password) {
var key = ctx.store.driveSecret.keys.cryptKey;
content.password = Crypto.encrypt(content.password, key);
content.password = encryptPassword(ctx, content.password);
}
// Update the data
@ -384,8 +392,8 @@ define([
var channel = content.channel || content.teamChannel;
if (content.password) {
var key = ctx.store.driveSecret.keys.cryptKey;
content.password = Crypto.encrypt(content.password, key);
content.pw = content.password;
content.password = encryptPassword(ctx, content.password);
}
if (addOwners[channel]) { return void cb(true); }

View File

@ -111,7 +111,7 @@ define([
var removeClient = function (ctx, cId) {
var idx = ctx.clients.indexOf(cId);
ctx.clients.splice(idx, 1);
if (idx !== -1) { ctx.clients.splice(idx, 1); }
};
Profile.init = function (cfg, waitFor, emit) {

View File

@ -694,7 +694,7 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback)
// but since multiple users who can and should might be online at once
// and since they'll all trigger this process at the same time...
// we want to stagger attempts at random intervals
setTimeout(function () {
ref.internal.checkpointTimeout = setTimeout(function () {
ref.internal.pendingCheckpointId = roster.checkpoint(function (err) {
if (err) { console.error(err); }
});
@ -730,7 +730,10 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback)
//console.log("Sending with id [%s]", id, msg);
//console.log();
response.expect(id, cb, TIMEOUT_INTERVAL);
response.expect(id, function (err, state) {
if (err) { return void cb(err); }
cb(void 0, state, id);
}, TIMEOUT_INTERVAL);
anon_rpc.send('WRITE_PRIVATE_MESSAGE', [
channel,
ciphertext

View File

@ -4,7 +4,7 @@
/* global importScripts, tabs */
importScripts('/components/requirejs/require.js');
importScripts('/components/requirejs/require.js?ver=2.3.7');
window = self; // eslint-disable-line no-global-assign
localStorage = { // eslint-disable-line no-global-assign

View File

@ -4,7 +4,7 @@
/* global importScripts */
importScripts('/components/requirejs/require.js');
importScripts('/components/requirejs/require.js?ver=2.3.7');
window = self; // eslint-disable-line no-global-assign
localStorage = { // eslint-disable-line no-global-assign

View File

@ -4,7 +4,7 @@
/* global importScripts */
importScripts('/components/requirejs/require.js');
importScripts('/components/requirejs/require.js?ver=2.3.7');
window = self; // eslint-disable-line no-global-assign
localStorage = { // eslint-disable-line no-global-assign

View File

@ -1083,7 +1083,7 @@ define([
Cryptpad.addSharedFolder(null, secret, cb);
} else {
var _data = {
password: data.password,
password: data.pw || data.password,
href: data.href,
channel: data.channel,
title: data.title,
@ -1359,7 +1359,7 @@ define([
var viewH = Utils.Hash.getViewHashFromKeys(_secret);
var href = Utils.Hash.hashToHref(editH, parsed.type);
var roHref = Utils.Hash.hashToHref(viewH, parsed.type);
Cryptpad.setPadAttribute('password', password, w(), parsed.getUrl());
Cryptpad.setPadAttribute('password', pw, w(), parsed.getUrl());
Cryptpad.setPadAttribute('channel', chan, w(), parsed.getUrl());
Cryptpad.setPadAttribute('href', href, w(), parsed.getUrl());
Cryptpad.setPadAttribute('roHref', roHref, w(), parsed.getUrl());

View File

@ -426,5 +426,46 @@
"settings_logoutEverywhereConfirm": "Сигурен ли си? Ще трябва да влезеш на всичките си устройства.",
"settings_driveDuplicateHint": "Когато преместите вашите документи в споделена папка, копие от тях се запазва във вашия CryptDrive, за да се гарантира, контрола ви върху него. Можете да скриете дублираните файлове. Само споделената версия ще бъде видима, освен ако не бъде изтрита, в този случай оригиналът ще се покаже на предишното си място.",
"settings_padWidthHint": "Превключване между режим на страница (по подразбиране), който ограничава ширината на текстовия редактор, и използване на цялата ширина на екрана.",
"settings_padSpellcheckHint": "Тази опция ви позволява да активирате проверка на правописа в документи с форматиран текст. Правописните грешки ще бъдат подчертани в червено и ще трябва да задържите клавиша Ctrl или Meta, докато щраквате с десния бутон, за да видите опциите."
"settings_padSpellcheckHint": "Тази опция ви позволява да активирате проверка на правописа в документи с форматиран текст. Правописните грешки ще бъдат подчертани в червено и ще трябва да задържите клавиша Ctrl или Meta, докато щраквате с десния бутон, за да видите опциите.",
"settings_padSpellcheckLabel": "Активиране на проверката на правописа в документи с форматиран текст",
"settings_padOpenLinkTitle": "Отваряне на връзки при първо щракване",
"settings_padOpenLinkHint": "С тази възможност можете да отваряте вградени връзки при щракване без изскачащ прозорец за показване",
"settings_padOpenLinkLabel": "Разрешаване на отварянето на директната връзка",
"settings_ownDriveTitle": "Актуализиране на профила",
"settings_ownDriveHint": "По-старите акаунти нямат достъп до най-новите функции поради технически причини. Безплатното обновяване ще активира текущите функции и ще подготви вашия CryptDrive за бъдещи обновявания.",
"settings_ownDriveButton": "Надграждане на профила ви",
"settings_ownDriveConfirm": "Надграждането на вашия акаунт може да отнеме известно време. Ще трябва да влезете отново през всичките си устройства. Сигурен ли сте?",
"settings_ownDrivePending": "Вашият профил се надгражда. Моля, не затваряйте и не презареждайте тази страница, докато процесът не приключи.",
"settings_changePasswordTitle": "Промяна на паролата",
"settings_changePasswordHint": "Променете паролата на профила си. Въведете текущата си парола и потвърдете новата парола, като я въведете два пъти.<br><b>Не можем да възстановим паролата ви, ако я забравите, така че бъдете много внимателни!</b>",
"settings_changePasswordButton": "Промяна на парола",
"settings_changePasswordCurrent": "Текуща парола",
"settings_changePasswordNew": "Нова парола",
"settings_changePasswordNewConfirm": "Потвърждаване на новата парола",
"settings_changePasswordConfirm": "Сигурни ли сте, че искате да промените паролата си? Ще трябва да влезете отново през всичките си устройства.",
"settings_changePasswordPending": "Вашата парола се обновява. Моля, не затваряйте и не презареждайте тази страница, докато процесът не приключи.",
"settings_cursorColorTitle": "Цвят на курсора",
"settings_changePasswordError": "Възникна неочаквана грешка. Ако не можете да влезете или да промените паролата си, свържете се с администраторите на CryptPad.",
"settings_changePasswordNewPasswordSameAsOld": "Вашата нова парола трябва да е различна от настоящата.",
"settings_cursorShareTitle": "Споделяне на позицията на курсора ми",
"settings_cursorShareLabel": "Споделяне на позицията",
"settings_cursorShowTitle": "Показване на позицията на курсора на другите потребители",
"settings_cursorShowHint": "Можете да изберете дали искате да виждате курсора на другите потребители в документите за съвместната работа.",
"settings_cursorShowLabel": "Показване на курсорите",
"upload_title": "Качване на файла",
"upload_modal_filename": "Име на файла (разширението <em>{0}</em> се добавя автоматично)",
"upload_modal_owner": "Личен файл",
"uploadFolder_modal_filesPassword": "Парола на файловете",
"uploadFolder_modal_owner": "Лични файлове",
"uploadFolder_modal_forceSave": "Съхраняване на файлове във вашия CryptDrive",
"upload_uploadPending": "В момента качвате. Искате ли да го отмените и да качите новия си файл?",
"upload_notEnoughSpace": "Няма достатъчно място за този файл във вашия CryptDrive.",
"upload_notEnoughSpaceBrief": "Няма достатъчно място",
"settings_cursorColorHint": "Променете цвета, свързан с вашия потребител в документите за съвместната работа.",
"settings_cursorShareHint": "Можете да решите дали искате други потребители да виждат позицията на курсора ви в документите за съвместната работа.",
"uploadFolder_modal_title": "Опции за качване на папка",
"upload_modal_title": "Опции за качване на файлове",
"upload_tooLarge": "Този файл надвишава максималния разрешен размер на качване за вашия акаунт.",
"upload_serverError": "Грешка в сървъра: файлът ви не може да бъде качен в момента.",
"upload_success": "Вашият файл ({0}) бе успешно качен и е добавен към устройството ви."
}

View File

@ -1129,7 +1129,7 @@
"admin_updateLimitTitle": "Actualitzar les quotes d'usuari",
"admin_updateLimitButton": "Actualitzar les quotes",
"admin_flushCacheTitle": "Neteja la memòria cau HTTP",
"admin_flushCacheHint": "Força als usuaris a descarregar els últims actius del client (només si el servidor està en mode nou)",
"admin_flushCacheHint": "Força tots els usuaris a baixar els últims actius després d'un canvi en la configuració o la personalització. Evita reiniciar el servidor, però fa que cada usuari actiu restableixi la connexió, utilitzeu-ho amb moderació.",
"admin_flushCacheButton": "Neteja la memòria cau",
"footer_donate": "Donació",
"contact_admin": "Contacteu amb l'administració de: {0}",
@ -1340,7 +1340,7 @@
"snapshots_ooPickVersion": "Heu de seleccionar una versió abans de crear una instantània",
"snapshot_error_exists": "Ja hi ha una instantània d'aquesta versió",
"snapshots_notFound": "Aquesta instantània ja no existeix perquè s'ha suprimit l'historial del document.",
"admin_registrationHint": "No permetis que cap usuari nou es registri",
"admin_registrationHint": "Els visitants de la instància no poden crear comptes. Els administradors poden crear invitacions.",
"admin_registrationTitle": "Tanca el registre",
"admin_defaultlimitHint": "Límit màxim d'emmagatzematge per a CryptDrives (usuaris i equips) quan no s'apliqui cap regla personalitzada",
"admin_setlimitButton": "Estableix el límit",
@ -1717,5 +1717,50 @@
"admin_usersHint": "Llista dels comptes coneguts d'aquesta instància. Seleccioneu a continuació per a afegir comptes automàticament, o introduïu la informació manualment amb el formulari.",
"admin_usersBlock": "URL de blocatge d'inici de sessió de l'usuari (opcional)",
"ssoauth_form_hint_register": "Afegiu una contrasenya CryptPad per a més seguretat, o deixeu-la buida i continueu. Si no afegiu una contrasenya, els administradors de la instància tindran a l'abast les claus que protegeixen les vostres dades.",
"admin_storeInvitedLabel": "Desa automàticament els usuaris convidats"
"admin_storeInvitedLabel": "Desa automàticament els usuaris convidats",
"admin_supportAdd": "Afegiu un contacte a l'equip d'assistència",
"admin_supportConfirm": "N'esteu segur? Això eliminarà tots els tiquets existents i blocarà l'accés a tots els moderadors.",
"support_cat_settings": "Configuració",
"admin_supportDelete": "Desactiva l'assistència",
"admin_supportMembers": "Equip d'assistència",
"admin_supportRotateNotify": "Atenció: les claus noves s'han generat, però un error inesperat ha evitat que el sistema les enviés als moderadors. Elimineu i reafegiu a tots els moderadors de l'equip d'administració",
"support_cat_closed": "Tancats",
"support_cat_search": "Cerca",
"support_cat_open": "Safata d'entrada",
"support_openTicketTitle": "Obre un tiquet amb un usuari",
"admin_supportTeamTitle": "Gestiona l'equip d'assistència",
"support_cat_legacy": "Compatibilitat",
"support_pending": "Tiquets arxivats:",
"support_privacyHint": "Marqueu aquesta opció per a respondre com a «Equip d'assistència» en compte d'amb el vostre nom d'usuari",
"support_pending_tag": "Arxivat",
"support_active_tag": "Safata d'entrada",
"support_closed_tag": "Tancat",
"support_privacyTitle": "Resposta anònima",
"support_notificationsHint": "Marqueu aquesta opció per a desactivar les notificacions de tiquets i respostes nous",
"support_notificationsTitle": "Desactiva les notificacions",
"support_userChannel": "ID del canal de notificacions de l'usuari",
"support_openTicketHint": "Copia les dades de l'usuari destinatari des de la seva pàgina de perfil o des d'un tiquet existent. Rebran una notificació del CryptPad sobre aquest missatge.",
"support_userKey": "Clau pública de l'usuari",
"support_invalChan": "Canal de notificacions no vàlid",
"admin_supportTeamHint": "Afegiu i elimina gent de la instància de l'equip d'assistència",
"support_pasteUserData": "Enganxeu les dades de l'usuari aquí",
"support_legacyButton": "Recupera els tiquets actius",
"support_recordedHint": "Emmagatzema el text comú com a dreceres d'un clic per a inserir-los a missatges d'assistència.",
"support_recordedContent": "Contingut",
"support_legacyTitle": "Mostra les dades d'assistència antigues",
"support_searchLabel": "Cerca (títol o ID del tiquet)",
"support_team": "L'Equip d'assistència",
"support_legacyHint": "Mostra els tiquets del sistema d'assistència antic i els recrea en el sistema nou.",
"support_legacyDump": "Exporta-ho tot",
"support_legacyClear": "Elimina d'aquest compte",
"support_moveActive": "Mou a l'arxiu",
"support_answerAs": "Es respon com a <b>{0}</b>",
"support_movePending": "Mou a l'arxiu",
"admin_supportOpen": "Obre el servei d'assistència",
"moderationPage": "Servei d'assistència",
"support_copyUserData": "Copia les dades d'usuari",
"support_userNotification": "Tiquet d'assistència o resposta nou: {0}",
"install_instance": "Creeu el primer compte d'administrador, després personalitzeu la instància",
"install_header": "Instal·lació",
"install_launch": "Configuració de la instància"
}

View File

@ -1 +1,25 @@
{}
{
"type": {
"slide": "Markdown slides",
"media": "Media",
"pad": "Rich tekst",
"poll": "Afstemning",
"file": "Fil",
"todo": "Todo",
"contacts": "Kontakter",
"code": "Kode",
"drive": "CryptDrive",
"form": "Form",
"teams": "Hold",
"doc": "Dokument",
"presentation": "Præsentation",
"diagram": "Diagram",
"kanban": "Kanban",
"sheet": "Ark",
"whiteboard": "Whiteboard"
},
"typeError": "Dette dokument er ikke kompatibelt med det valgte program",
"main_title": "CryptPad: Zero Knowledge, Collaborative Real Time Editing",
"common_connectionLost": "<b>Serverforbindelse afbrudt</b><br>Du er nu i skrivebeskyttet tilstand, indtil forbindelsen er tilbage.",
"onLogout": "Du er logget ud, {0}klik her{1} for at logge ind<br>eller tryk på Esc for at få adgang til dit dokument i skrivebeskyttet tilstand."
}

Some files were not shown because too many files have changed in this diff Show More