mirror of https://github.com/xwiki-labs/cryptpad
Merge branch '2024.9-rc' into monitoring
This commit is contained in:
commit
9454df6862
|
@ -35,7 +35,7 @@ module.exports = {
|
|||
4
|
||||
],
|
||||
'linebreak-style': [
|
||||
'error',
|
||||
'off', // git handles linebreak conversion for us
|
||||
'unix'
|
||||
],
|
||||
'quotes': [
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/*
|
||||
|
|
50
CHANGELOG.md
50
CHANGELOG.md
|
@ -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 don’t 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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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': '中文(簡體)',
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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%));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,3 +8,8 @@
|
|||
margin: 3cm;
|
||||
size: A4 portrait;
|
||||
}
|
||||
@media print {
|
||||
body {
|
||||
background: white !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
@ -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;
|
||||
});
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
---
|
||||
services:
|
||||
cryptpad:
|
||||
image: "cryptpad/cryptpad:version-2024.6.0"
|
||||
image: "cryptpad/cryptpad:version-2024.6.1"
|
||||
hostname: cryptpad
|
||||
|
||||
environment:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
|
|
|
@ -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});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "cryptpad",
|
||||
"description": "a collaborative office suite that is end-to-end encrypted and open-source",
|
||||
"version": "2024.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",
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
|
@ -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]
|
|
@ -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]
|
|
@ -0,0 +1 @@
|
|||
{"owners":["TestOwner"],"validateKey":"TestKey","channel":"0","created":1721035117462}
|
|
@ -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]
|
|
@ -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]
|
|
@ -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]
|
|
@ -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]
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 |
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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. ",
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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}) бе успешно качен и е добавен към устройството ви."
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue