mirror of
https://github.com/cryptpad/cryptpad.git
synced 2026-09-14 11:05:41 +05:00
Merge branch 'staging' into multiple-calendars-inout-of-view
This commit is contained in:
commit
b658c2fe5e
@ -59,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
|
||||
|
||||
@ -92,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"
|
||||
|
||||
@ -135,7 +135,6 @@ define(req, function(AppConfig, Default, Language) {
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
Messages.calendar_show = 'Show calendars'; // XXX
|
||||
Messages.calendar_hide = 'Hide calendars'; // XXX
|
||||
Messages.admin_mfa_confirm_enable = "Are you sure you want to enable Multi-Factor Authentication?"; // XXX
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -22,9 +22,9 @@ 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
|
||||
|
||||
|
||||
10
docs/community/README.md
Normal file
10
docs/community/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and contributors
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
# Community configuration files
|
||||
This folder is listing community-contributed configuration files. They **aren't** supported and are provided as-is, without any warranty.
|
||||
|
||||
If you are using CryptPad in production and require professional support please contact sales@cryptpad.org
|
||||
245
docs/community/example-advanced.caddy.conf
Normal file
245
docs/community/example-advanced.caddy.conf
Normal file
@ -0,0 +1,245 @@
|
||||
# SPDX-FileCopyrightText: 2024 XWiki CryptPad Team <contact@cryptpad.org> and contributors
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
# This file is included strictly as an example of how Caddy can be configured
|
||||
# to work with CryptPad. This example WILL NOT WORK AS IS. For best results,
|
||||
# compare the sections of this configuration file against a working CryptPad
|
||||
# installation (http server by the Nodejs process). If you are using CryptPad
|
||||
# in production and require professional support please contact sales@cryptpad.org
|
||||
|
||||
(trustedProxies) {
|
||||
# Force Caddy to accept `X-Forwarded-For` and other origin headers.
|
||||
# Modify the line below if you want to restrict the scope of direct downstream sending these headers.
|
||||
trusted_proxies 0.0.0.0/0 ::/0
|
||||
}
|
||||
|
||||
# Caddy does not have variables for server names, so domains need to be hardcoded.
|
||||
# You can bulk replace "your-main-domain.com" and "your-sandbox-domain.com" safely.
|
||||
your-main-domain.com:443,
|
||||
your-sandbox-domain.com:443 {
|
||||
# Define your certificates below.
|
||||
# No need to adjust TLS configurations, as the defaults in Caddy are already secure.
|
||||
tls /path/to/fullchain/publicKey.pem /path/to/certificate/privateKey.pem
|
||||
|
||||
# Enable HSTS.
|
||||
# Do not enable this line when configuring over mixnet, e.g. Tor.
|
||||
header Strict-Transport-Security "max-age=63072000; includeSubDomains"
|
||||
|
||||
# Security headers
|
||||
header X-XSS-Protection "1; mode=block"
|
||||
header X-Content-Type-Options "nosniff"
|
||||
header Access-Control-Allow-Credentials "true"
|
||||
#header X-Frame-Options "SAMEORIGIN"
|
||||
|
||||
# OnlyOffice fonts may be loaded from both domains.
|
||||
@onlyOfficeFonts {
|
||||
path_regexp "^\\/common\\/onlyoffice\\/.*\\/fonts\\/.*$"
|
||||
}
|
||||
header Access-Control-Allow-Origin "*"
|
||||
|
||||
# By default CryptPad forbids remote domains from embedding CryptPad documents in iframes.
|
||||
# The sandbox domain must always be permitted in order for the platform to function.
|
||||
# If you wish to enable remote embedding you may change the value below to "*"
|
||||
# as per the commented value.
|
||||
header ?Access-Control-Allow-Origin "https://your-sandbox-domain.com"
|
||||
#header ?Access-Control-Allow-Origin "*"
|
||||
|
||||
# Opt out of Google's FLoC Network
|
||||
header Permissions-Policy "interest-cohort=()"
|
||||
|
||||
# Enable SharedArrayBuffer in Firefox (for .xlsx export)
|
||||
header ?Cross-Origin-Resource-Policy "cross-origin"
|
||||
header ?Cross-Origin-Embedder-Policy "require-corp"
|
||||
|
||||
# Specify the relative path to root of your custom error page.
|
||||
# This error page won't only be served for 404 errors.
|
||||
handle_errors {
|
||||
rewrite * /error.htm
|
||||
header Cache-Control "no-cache, no-store"
|
||||
file_server
|
||||
templates
|
||||
}
|
||||
|
||||
# Insert the path to your CryptPad repository root here
|
||||
root /home/cryptpad/cryptpad
|
||||
|
||||
# Any static assets loaded with "vers=" in their URL will be cached for a year
|
||||
@staticAssets {
|
||||
query "ver=*"
|
||||
}
|
||||
header @staticAssets Cache-Control "max-age=31536000"
|
||||
|
||||
vars {
|
||||
# CSS can be dynamically set inline, loaded from the same domain, or from your main domain.
|
||||
styleSrc "'unsafe-inline' 'self' https://your-main-domain.com"
|
||||
|
||||
# connect-src restricts URLs which can be loaded using script interfaces.
|
||||
# If you have configured your instance to use a dedicated file delivery domain or API domain,
|
||||
# you will need to add them below.
|
||||
connectSrc "'self' https://your-main-domain.com blob: wss://api.your-main-domain.com https://your-sandbox-domain.com"
|
||||
|
||||
# Fonts can be loaded from data-URLs or the main domain.
|
||||
fontSrc "'self' data: https://your-main-domain.com"
|
||||
|
||||
# Images can be loaded from anywhere, though we'd like to deprecate this as it allows
|
||||
# the use of images for tracking.
|
||||
imgSrc "'self' data: blob: https://your-main-domain.com"
|
||||
|
||||
# frame-src specifies valid sources for nested browsing contexts.
|
||||
# This prevents loading any iframes from anywhere other than the sandbox domain.
|
||||
frameSrc "'self' https://your-sandbox-domain.com blob:"
|
||||
|
||||
# media-src specifies valid sources for loading media using video or audio.
|
||||
mediaSrc "blob:"
|
||||
|
||||
# child-src defines valid sources for webworkers and nested browser contexts.
|
||||
# It is deprecated in favour of worker-src and frame-src.
|
||||
childSrc "https://your-main-domain.com"
|
||||
|
||||
# worker-src valid sources for Worker, Shared Worker, or Service Worker scripts.
|
||||
# Supercedes child-src, but is unfortunately not yet universally supported.
|
||||
workerSrc "'self'"
|
||||
|
||||
# script-src specifies valid sources for JavaScript, including inline handlers.
|
||||
scriptSrc "'self' resource: https://your-main-domain.com"
|
||||
|
||||
# frame-ancestors specifies which origins can embed your CryptPad instance.
|
||||
# This must include 'self' and your main domain (over HTTPS) in order for CryptPad to work,
|
||||
# if you have enabled remote embedding via the admin panel, then this must be more permissive.
|
||||
# Note: cryptpad.fr permits web pages served via https: and vector: (element desktop app)
|
||||
frameAncestors "'self' https://your-main-domain.com"
|
||||
#frameAncestors "'self' https: vector:"
|
||||
|
||||
# A few assets are loaded via the sandbox domain.
|
||||
# They unfortunately still require exceptions to the sandboxing to work correctly.
|
||||
# Everything except the sandbox domain is a privileged scope, as they might be used to handle keys.
|
||||
# Unsafe iframes are exceptions. Office file formats are converted outside of the sandboxed scope,
|
||||
# because of bugs in Chromium-based browsers that incorrectly ignore headers supposed to enable
|
||||
# the use of some modern APIs, that are required when JavaScript is run in a cross-origin context.
|
||||
# We've applied other sandboxing techniques to mitigate the risk of running WebAssembly
|
||||
# in this privileged scope.
|
||||
# Privileged contexts allow a few more rights than unprivileged contexts, though limits are still applied.
|
||||
scriptSrcUnsafe "'self' 'unsafe-eval' 'unsafe-inline' resource: https://your-main-domain.com"
|
||||
}
|
||||
|
||||
# Finally, set all the security rules you have composed above.
|
||||
@privilegedScope1 {
|
||||
host "your-sandbox-domain.com"
|
||||
path_regexp "^\\/(sheet|doc|presentation)\\/inner.html.*$"
|
||||
}
|
||||
@privilegedScope2 {
|
||||
host "your-sandbox-domain.com"
|
||||
path_regexp "^\\/common\\/onlyoffice\\/.*\\/.*\\.html.*$"
|
||||
}
|
||||
@privilegedScope3 {
|
||||
host "your-sandbox-domain.com"
|
||||
path_regexp "^\\/unsafeiframe\\/inner\\.html.*$"
|
||||
}
|
||||
header @privilegedScope1 Content-Security-Policy "default-src 'none'; child-src {vars.childSrc}; worker-src {vars.workerSrc}; media-src {vars.mediaSrc}; style-src {vars.styleSrc}; script-src {vars.scriptSrcUnsafe}; connect-src {vars.connectSrc}; font-src {vars.fontSrc}; img-src {vars.imgSrc}; frame-src {vars.frameSrc}; frame-ancestors {vars.frameAncestors}"
|
||||
header @privilegedScope2 Content-Security-Policy "default-src 'none'; child-src {vars.childSrc}; worker-src {vars.workerSrc}; media-src {vars.mediaSrc}; style-src {vars.styleSrc}; script-src {vars.scriptSrcUnsafe}; connect-src {vars.connectSrc}; font-src {vars.fontSrc}; img-src {vars.imgSrc}; frame-src {vars.frameSrc}; frame-ancestors {vars.frameAncestors}"
|
||||
header @privilegedScope3 Content-Security-Policy "default-src 'none'; child-src {vars.childSrc}; worker-src {vars.workerSrc}; media-src {vars.mediaSrc}; style-src {vars.styleSrc}; script-src {vars.scriptSrcUnsafe}; connect-src {vars.connectSrc}; font-src {vars.fontSrc}; img-src {vars.imgSrc}; frame-src {vars.frameSrc}; frame-ancestors {vars.frameAncestors}"
|
||||
header ?Content-Security-Policy "default-src 'none'; child-src {vars.childSrc}; worker-src {vars.workerSrc}; media-src {vars.mediaSrc}; style-src {vars.styleSrc}; script-src {vars.scriptSrc}; connect-src {vars.connectSrc}; font-src {vars.fontSrc}; img-src {vars.imgSrc}; frame-src {vars.frameSrc}; frame-ancestors {vars.frameAncestors}"
|
||||
|
||||
# Add support for .mjs files used by pdfjs
|
||||
@fileModuleJS {
|
||||
path "*.mjs"
|
||||
}
|
||||
header @fileModuleJS Content-Type "application/javascript"
|
||||
|
||||
# The Node.js process can handle all traffic, whether accessed over websocket or as static assets.
|
||||
# We prefer to serve static content from Caddy directly, and to leave the API server to handle the
|
||||
# the dynamic content that only it can manage. This is primarily for optimization.
|
||||
handle /cryptpad_websocket/* {
|
||||
reverse_proxy * {
|
||||
to 127.0.0.1:3003
|
||||
header_up Host "{host}"
|
||||
header_up X-Real-IP "{remote_host}"
|
||||
|
||||
# Caddy supports WebSockets directly. No additional headers are needed.
|
||||
|
||||
import trustedProxies
|
||||
}
|
||||
}
|
||||
|
||||
handle_path /customize.dist/* {
|
||||
# This is needed in order to prevent infinite recursion between /customize/ and the root.
|
||||
}
|
||||
|
||||
# Try to load customizeable content via /customize/ and fall back to the default content located
|
||||
# at /customize.dist/ .
|
||||
# This is what allows you to override behaviour.
|
||||
handle_path /customize/* {
|
||||
try_files /customize/{path} /customize.dist/{path}
|
||||
file_server {
|
||||
index index.html index.htm default.html default.htm
|
||||
}
|
||||
}
|
||||
|
||||
# /api/config is loaded once per page load, and is used to retrieve the caching variable,
|
||||
# which is applied to every other resource loaded during that session.
|
||||
@sharedReverseProxy {
|
||||
path /api/*
|
||||
path /extensions.js
|
||||
}
|
||||
handle @sharedReverseProxy {
|
||||
reverse_proxy * {
|
||||
to 127.0.0.1:3000
|
||||
header_up Host "{host}"
|
||||
header_up X-Real-IP "{remote_host}"
|
||||
|
||||
# These settings prevent both Caddy and the API server from setting duplicate headers.
|
||||
header_down Cross-Origin-Resource-Policy cross-origin
|
||||
header_down Cross-Origin-Embedder-Policy require-corp
|
||||
|
||||
import trustedProxies
|
||||
}
|
||||
}
|
||||
|
||||
# Requests for blobs and blocks are now proxied to the API server.
|
||||
# This simplifies Caddy path configuration, in the event they are being hosted in a non-standard location
|
||||
# or with odd unexpected permissions. Serving blobs in this manner also means that it will be possible to
|
||||
# enforce access control for them, though this is not yet implemented.
|
||||
# Access control (via TOTP 2FA) has been added to blocks, so they can be handled with the same directives.
|
||||
@blobsAndBlocks {
|
||||
path /blob/*
|
||||
path /block/*
|
||||
}
|
||||
handle @blobsAndBlocks {
|
||||
@corsPreflight {
|
||||
method OPTIONS
|
||||
}
|
||||
handle @corsPreflight {
|
||||
header Access-Control-Allow-Origin "https://your-sandbox-domain.com"
|
||||
header Access-Control-Allow-Credentials "true"
|
||||
header Access-Control-Allow-Methods "GET, POST, OPTIONS"
|
||||
header Access-Control-Allow-Headers "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range"
|
||||
header Access-Control-Max-Age "1728000"
|
||||
header Content-Type "application/octet-stream; charset=utf-8"
|
||||
header Content-Length "0"
|
||||
respond 204
|
||||
}
|
||||
reverse_proxy * {
|
||||
to 127.0.0.1:3000
|
||||
# Preventing these headers from getting duplicated, since we are proxying to the API server.
|
||||
header_down -X-Content-Type-Options
|
||||
header_down -Access-Control-Allow-Origin
|
||||
header_down -Permissions-Policy
|
||||
header_down -X-XSS-Protection
|
||||
header_down -Cross-Origin-Resource-Policy
|
||||
header_down -Cross-Origin-Embedder-Policy
|
||||
}
|
||||
}
|
||||
|
||||
# The Node.JS server has some built-in forwarding rulesets to prevent URLs not suffixed with a slash
|
||||
# from resulting in a 404 error. This simply adds a trailing slash to a variety of applications.
|
||||
@preventNotFound {
|
||||
path_regexp "^/(register|login|recovery|settings|user|pad|drive|poll|slide|code|whiteboard|file|media|profile|contacts|todo|filepicker|debug|kanban|sheet|support|admin|notifications|teams|calendar|presentation|doc|form|report|convert|checkup|diagram)$"
|
||||
}
|
||||
redir @preventNotFound "{path}/"
|
||||
|
||||
# Enable file serving
|
||||
file_server {
|
||||
index index.html index.htm default.html default.htm
|
||||
}
|
||||
}
|
||||
64
docs/community/example.caddy.conf
Normal file
64
docs/community/example.caddy.conf
Normal file
@ -0,0 +1,64 @@
|
||||
# SPDX-FileCopyrightText: 2024 XWiki CryptPad Team <contact@cryptpad.org> and contributors
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
# This file is included strictly as an example of how Caddy can be configured
|
||||
# to work with CryptPad. This example WILL NOT WORK AS IS. For best results,
|
||||
# compare the sections of this configuration file against a working CryptPad
|
||||
# installation (http server by the Nodejs process). If you are using CryptPad
|
||||
# in production and require professional support please contact sales@cryptpad.org
|
||||
|
||||
(trustedProxies) {
|
||||
# Force Caddy to accept `X-Forwarded-For` and other origin headers.
|
||||
# Modify the line below if you want to restrict the scope of direct downstream sending these headers.
|
||||
trusted_proxies 0.0.0.0/0 ::/0
|
||||
}
|
||||
|
||||
# Caddy does not have variables for server names, so domains need to be hardcoded.
|
||||
# You can bulk replace "your-main-domain.com" and "your-sandbox-domain.com" safely.
|
||||
your-main-domain.com:443,
|
||||
your-sandbox-domain.com:443 {
|
||||
# Define your certificates below.
|
||||
# No need to adjust TLS configurations, as the defaults in Caddy are already secure.
|
||||
tls /path/to/fullchain/publicKey.pem /path/to/certificate/privateKey.pem
|
||||
|
||||
# Enable HSTS.
|
||||
# Do not enable this line when configuring over mixnet, e.g. Tor.
|
||||
header Strict-Transport-Security "max-age=63072000; includeSubDomains"
|
||||
|
||||
# Specify the relative path to root of your custom error page.
|
||||
# This error page won't only be served for 404 errors.
|
||||
handle_errors {
|
||||
rewrite * /error.htm
|
||||
header Cache-Control "no-cache, no-store"
|
||||
file_server
|
||||
templates
|
||||
}
|
||||
|
||||
# The Node.js process can handle all traffic, whether accessed over websocket or as static assets.
|
||||
reverse_proxy /cryptpad_websocket/* {
|
||||
to 127.0.0.1:3003
|
||||
header_up Host "{host}"
|
||||
header_up X-Real-IP "{remote_host}"
|
||||
|
||||
# Caddy supports WebSockets directly. No additional headers are needed.
|
||||
|
||||
import trustedProxies
|
||||
}
|
||||
reverse_proxy * {
|
||||
to 127.0.0.1:3000
|
||||
header_up Host "{host}"
|
||||
header_up X-Real-IP "{remote_host}"
|
||||
|
||||
# These settings prevent both Caddy and the API server from setting duplicate headers.
|
||||
header_down Cross-Origin-Resource-Policy cross-origin
|
||||
header_down Cross-Origin-Embedder-Policy require-corp
|
||||
|
||||
import trustedProxies
|
||||
}
|
||||
|
||||
# Enable file serving
|
||||
file_server {
|
||||
index index.html index.htm default.html default.htm
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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});
|
||||
}
|
||||
|
||||
@ -146,6 +146,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.
|
||||
|
||||
20
package-lock.json
generated
20
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "cryptpad",
|
||||
"version": "2024.6.1",
|
||||
"version": "2024.9.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cryptpad",
|
||||
"version": "2024.6.1",
|
||||
"version": "2024.9.0",
|
||||
"license": "AGPL-3.0+",
|
||||
"dependencies": {
|
||||
"@mcrowe/minibloom": "^0.2.0",
|
||||
@ -19,7 +19,7 @@
|
||||
"chainpad-crypto": "^0.2.5",
|
||||
"chainpad-listmap": "^1.1.0",
|
||||
"chainpad-netflux": "^1.2.0",
|
||||
"chainpad-server": "^5.2.0",
|
||||
"chainpad-server": "^5.2.2",
|
||||
"ckeditor": "npm:ckeditor4@~4.22.1",
|
||||
"codemirror": "^5.19.0",
|
||||
"components-font-awesome": "^4.6.3",
|
||||
@ -41,7 +41,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",
|
||||
@ -1396,9 +1396,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/chainpad-server": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/chainpad-server/-/chainpad-server-5.2.1.tgz",
|
||||
"integrity": "sha512-r6kNMCbMuwUMxsFIZJgyXtdGxwoXm+gFVIUBxKLOzhvbeMm0RSBYa6BJ5qgzYIwUhYgqyUxmm9tXzYecmPUHIA==",
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/chainpad-server/-/chainpad-server-5.2.2.tgz",
|
||||
"integrity": "sha512-fUCffhnd8PlO92A6cYH5lSIiiI6bpJymP0yC3O2KI+4NPyxFK7I2iuadMrC7RzK3eAI1w72Y1nbMN8rQImppoQ==",
|
||||
"dependencies": {
|
||||
"nthen": "0.1.8",
|
||||
"pull-stream": "^3.6.9",
|
||||
@ -3520,9 +3520,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/netflux-websocket": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/netflux-websocket/-/netflux-websocket-1.2.0.tgz",
|
||||
"integrity": "sha512-CNQF1wd/UOCmnfaWDATi5vTMfIpMm0Zghy1Fkvr5p7Q70wJdT85RdtnQvsqfLU5AXMUKwjeP1GgxMyAAdRUeJA=="
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/netflux-websocket/-/netflux-websocket-1.2.1.tgz",
|
||||
"integrity": "sha512-8oQOyEyh0MCnifNIfyvkNXpEELlVRzDleRivRgsDGh+5ZDG109axweVZ0RIOIpkQmTZk4/xVsBk/oLXpMSWEOw=="
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.14",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cryptpad",
|
||||
"description": "a collaborative office suite that is end-to-end encrypted and open-source",
|
||||
"version": "2024.6.1",
|
||||
"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.0",
|
||||
"chainpad-server": "^5.2.2",
|
||||
"ckeditor": "npm:ckeditor4@~4.22.1",
|
||||
"codemirror": "^5.19.0",
|
||||
"components-font-awesome": "^4.6.3",
|
||||
@ -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",
|
||||
|
||||
@ -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]
|
||||
60
scripts/tests/test-metadata.js
Normal file
60
scripts/tests/test-metadata.js
Normal file
@ -0,0 +1,60 @@
|
||||
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,6 +22,11 @@
|
||||
flex-flow: column;
|
||||
font: @colortheme_app-font;
|
||||
|
||||
input, textarea, .cp-appblock {
|
||||
&:focus-visible {
|
||||
outline: @variables_focus_style;
|
||||
}
|
||||
}
|
||||
.cp-admin-customize-logo {
|
||||
padding: 1em;
|
||||
img {
|
||||
|
||||
@ -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,9 +160,6 @@
|
||||
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 {
|
||||
@ -420,10 +424,7 @@
|
||||
// margin-bottom: 20px;
|
||||
input {
|
||||
width: 80px;
|
||||
margin-right: 0.3rem;
|
||||
}
|
||||
.fa-plus{
|
||||
margin-left:0.3rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -658,6 +659,9 @@
|
||||
color: @cp_sidebar-left-active-fg;
|
||||
border: 0px;
|
||||
}
|
||||
&:focus-visible {
|
||||
outline: @variables_focus_style;
|
||||
}
|
||||
i {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
@ -780,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); }
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -446,5 +446,26 @@
|
||||
"settings_changePasswordPending": "Вашата парола се обновява. Моля, не затваряйте и не презареждайте тази страница, докато процесът не приключи.",
|
||||
"settings_cursorColorTitle": "Цвят на курсора",
|
||||
"settings_changePasswordError": "Възникна неочаквана грешка. Ако не можете да влезете или да промените паролата си, свържете се с администраторите на CryptPad.",
|
||||
"settings_changePasswordNewPasswordSameAsOld": "Вашата нова парола трябва да е различна от настоящата."
|
||||
"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."
|
||||
}
|
||||
|
||||
@ -589,8 +589,8 @@
|
||||
"properties_passwordWarning": "La contraseña ha sido cambiada con éxito pero fue incapaz de actualizar tu CryptDrive con los nuevos datos. Es posible que tenga que eliminar la versión antigua del documento manualmente.<br>Pulse OK para recargar y actualizar sus derechos de acceso.",
|
||||
"properties_passwordSuccess": "La contraseña ha sido cambiada con éxito.<br>Pulsa OK para recargar y actualizar tus derechos de acceso.",
|
||||
"admin_updateLimitTitle": "Actualizar cuotas de usuarios",
|
||||
"admin_registeredHint": "Número de usuarios registrados en tu instancia",
|
||||
"admin_registeredTitle": "Usuarios registrados",
|
||||
"admin_registeredHint": "Número de unidades activas en tu instancia",
|
||||
"admin_registeredTitle": "Unidades de usuario y de equipo",
|
||||
"admin_activePadsHint": "Número de documentos únicos que se están viendo o editando ahora",
|
||||
"admin_activePadsTitle": "Documentos activos",
|
||||
"admin_activeSessionsHint": "Número de conexiones websocket activas (y direcciones IP únicas conectadas)",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -594,8 +594,8 @@
|
||||
"features_f_storage1": "Biltegiratze pertsonala ({0})",
|
||||
"features_f_file1_note": "Gorde fitxategiak zure CryptDrive-an: irudiak, PDFak, bideoak eta abar. Partekatu itzazu zure kontaktuekin edo txertatu dokumentuetan. (gehienez {0}MB)",
|
||||
"admin_updateLimitTitle": "Eguneratu erabiltzaileen kuotak",
|
||||
"admin_registeredHint": "Zure instantzian erregistratutako erabiltzaile kopurua",
|
||||
"admin_registeredTitle": "Erabiltzaile erregistratuak",
|
||||
"admin_registeredHint": "Zure instantziako unitate aktibo kopurua",
|
||||
"admin_registeredTitle": "Erabiltzaile eta taldeko gidak",
|
||||
"admin_activePadsHint": "Une honetan ikusten edo editatzen ari diren dokumentu bakarrak",
|
||||
"admin_activePadsTitle": "Dokumentu aktiboak",
|
||||
"admin_activeSessionsHint": "Websocket konexio aktiboen kopurua (eta konektatutako IP helbide bakarrak)",
|
||||
@ -1205,7 +1205,7 @@
|
||||
"admin_defaultlimitTitle": "Biltegiratze muga (MB)",
|
||||
"admin_defaultlimitHint": "CryptDrive edo unitatearen (erabiltzaileren zein talderen) biltegiratzeko gehienezko muga, arau pertsonalizatua aplikatzen ez denean",
|
||||
"admin_registrationTitle": "Itxi izena ematea",
|
||||
"admin_registrationHint": "Ez utzi erabiltzaile berririk izena ematen",
|
||||
"admin_registrationHint": "Instantziaren bisitariek ezin dute konturik sortu. Gonbidapenak administratzaileek sor ditzakete.",
|
||||
"snapshots_notFound": "Babeskopia hau jada ez dago dokumentuaren historia ezabatu delako.",
|
||||
"snapshot_error_exists": "Dagoeneko badago bertsio honen babeskopia bat",
|
||||
"snapshots_ooPickVersion": "Bertsio bat hautatu behar duzu babeskopia bat sortu aurretik",
|
||||
@ -1668,7 +1668,7 @@
|
||||
"calendar_rec_change": "Errepikatzen den gertaera bat beste egutegi batera eramatea.. Aldaketa hau gertaera honetan edo errepikatzen diren gertaera guztietan soilik aplika dezakezu.",
|
||||
"access_passwordUsed": "Pasahitz hau dagoeneko erabili da dokumentu honetarako. Ezin da berriro erabili.",
|
||||
"calendar_rec_change_first": "Lehenengo errepikapena beste egutegi batera mugitzea. Errepikatzen diren gertaera guztiak ere mugituko dira.",
|
||||
"admin_forcemfaHint": "Instantzia honetako erabiltzaile guztiei bi faktoreko autentifikazioa konfiguratzeko eskatuko zaie beren kontuan saioa hasteko.",
|
||||
"admin_forcemfaHint": "Instantzia honetako erabiltzaile guztiei bi faktoreko autentifikazioa konfiguratzeko eskatuko zaie beren kontuan saioa hasteko. Kontuan izan lehendik dauden erabiltzaileek ezin izango dutela beren kontua erabiltzen jarraitu TOTP aplikazioa konfiguratu gabe.",
|
||||
"ssoauth_form_hint_login": "Sartu zure CryptPad pasahitza",
|
||||
"status": "Egoera orria",
|
||||
"calendar_desc": "Deskribapena",
|
||||
@ -1763,5 +1763,22 @@
|
||||
"admin_supportOpen": "Ireki laguntza tresnak",
|
||||
"support_copyUserData": "Kopiatu erabiltzailearen datuak",
|
||||
"support_userNotification": "Laguntza-txartel edo erantzun berria: {0}",
|
||||
"admin_supportTeamHint": "Gehitu eta kendu jendea instantziako laguntza-taldetik"
|
||||
"admin_supportTeamHint": "Gehitu eta kendu jendea instantziako laguntza-taldetik",
|
||||
"install_token": "Instalazioaren tokena",
|
||||
"install_header": "Instalazioa",
|
||||
"admin_appSelection": "Aplikazioaren konfigurazioa",
|
||||
"install_instance": "Sortu lehen administratzailearen kontua, eta jarraitu instantzia hau pertsonalizatzen",
|
||||
"install_launch": "Instantziaren konfigurazioa",
|
||||
"install_notes": "<ul class=\"cp-notes-list\"><li>Sortu zure lehen administratzaile kontua orrialde honetan. Administratzaileek instantzia-ezarpenak kudeatzen dituzte biltegiratze-kuotak barne, eta moderazio-tresnetarako sarbidea dute.</li><li>Zure pasahitza instantzia honetako dokumentu eta administratzaile-pribilegio guztiak enkriptatzen dituen gako sekretua da. <span class=\"red\">Galtzen baduzu, ezin dugu zure datuak berreskuratu.</span></li><li>Partekatutako ordenagailu bat erabiltzen ari bazara, <span class=\"red\">gogoratu amaitutakoan saioa ixtea</span>. Nabigatzailearen leihoa ixteak bakarrik utziko du zure kontua agerian. </li></ul>",
|
||||
"admin_appsTitle": "Instantziaren aplikazioak",
|
||||
"admin_appsHint": "Aukeratu zein aplikazio aktibatu instantzia honetan.",
|
||||
"admin_cat_apps": "Aplikazioak",
|
||||
"onboarding_upload": "Hautatu logoa",
|
||||
"onboarding_save_error": "Aukera batzuk ezin izan dira behar bezala gorde. Mesedez, bisitatu administrazio panelera balioak egiaztatzeko.",
|
||||
"admin_onboardingNameHint": "Mesedez, aukeratu izenburua, deskribapena, nabarmentzeko kolorea eta logotipoa (guztiak aukerakoak dira)",
|
||||
"admin_onboardingNameTitle": "Ongi etorri zure CryptPad instantziara",
|
||||
"admin_onboardingOptionsTitle": "Instantziaren aukerak",
|
||||
"admin_onboardingNamePlaceholder": "Instantziaren izenburua",
|
||||
"admin_onboardingDescPlaceholder": "Instantziaren deskribapen testua",
|
||||
"admin_onboardingOptionsHint": "Mesedez, hautatu aukera egokia zure instantziarako.<br>Ezarpen hauek geroago alda daitezke administrazio panelean."
|
||||
}
|
||||
|
||||
@ -1780,5 +1780,8 @@
|
||||
"admin_onboardingNamePlaceholder": "Nom de l'instance",
|
||||
"admin_onboardingOptionsHint": "Veuillez sélectionner les options appropriées pour votre instance.<br>Ces paramètres peuvent être changés plus tard depuis l'espace d'administration.",
|
||||
"admin_onboardingDescPlaceholder": "Texte de description de l'instance",
|
||||
"install_notes": "<ul class=\"cp-notes-list\"><li>Créez votre premier compte administrateur·ice sur cette page. Les administrateur·ices peuvent paramétrer l'instance, ceci incluant les quotas de stockage, et ont accès aux outils de modération.</li><li>Votre mot de passe est la clé secrète qui chiffre l'ensemble de vos documents et vos privilèges d'administration sur l'instance. <span class=\"red\">Si vous le perdez il n'est pas possible de récupérer vos données.</span></li><li>Si vous utilisez un ordinateur partagé, <span class=\"red\">n'oubliez pas de vous déconnecter</span> quand vous aurez terminé. Simplement fermer la fenêtre du navigateur web laisse votre compte exposé à des risques de sécurité. </li></ul>"
|
||||
"install_notes": "<ul class=\"cp-notes-list\"><li>Créez votre premier compte administrateur·ice sur cette page. Les administrateur·ices peuvent paramétrer l'instance, ceci incluant les quotas de stockage, et ont accès aux outils de modération.</li><li>Votre mot de passe est la clé secrète qui chiffre l'ensemble de vos documents et vos privilèges d'administration sur l'instance. <span class=\"red\">Si vous le perdez il n'est pas possible de récupérer vos données.</span></li><li>Si vous utilisez un ordinateur partagé, <span class=\"red\">n'oubliez pas de vous déconnecter</span> quand vous aurez terminé. Simplement fermer la fenêtre du navigateur web laisse votre compte exposé à des risques de sécurité. </li></ul>",
|
||||
"team_autoTrim": "Suppression de l'historique du drive d'équipe... Veuillez patienter.",
|
||||
"admin_mfa_confirm_disable": "Êtes-vous sûr de vouloir désactiver l'authentification multi-facteur ?",
|
||||
"admin_mfa_confirm_enable": "Êtes-vous sûr de vouloir activer l'authentification multi-facteur ?"
|
||||
}
|
||||
|
||||
@ -1741,5 +1741,32 @@
|
||||
"admin_onboardingNameHint": "Scegli un titolo, una descrizione, un colore principale ed un logo (sono tutti facoltativi)",
|
||||
"admin_onboardingNameTitle": "Benvenut* nella tua istanza di CryptPad",
|
||||
"admin_onboardingNamePlaceholder": "Titolo dell'istanza",
|
||||
"admin_onboardingDescPlaceholder": "Descrizione dell'istanza"
|
||||
"admin_onboardingDescPlaceholder": "Descrizione dell'istanza",
|
||||
"admin_planName": "Nome della pianificazione",
|
||||
"admin_enableembedsTitle": "Abilita l’ incorporamento remoto",
|
||||
"error_evalPermitted": "Sto abortendo: eval non dovrebbe essere permesso.\n\nQuesto errore è collegato al Content-Security-Policy header, può essere causato: da un browser obsoleto che non lo supporta, da un estensione che interferisce con il comportamento atteso o da una configurazione errata di questa istanza di CryptPad.",
|
||||
"admin_blockMetadataTitle": "Informazioni sulla Sezione Login",
|
||||
"admin_blockMetadataHint": "La sezione di login abilita un account ad identificarsi in CryptPad con la propria combinazione di nome utente + password",
|
||||
"admin_blockMetadataPlaceholder": "URL assoluto o relativo della sezione",
|
||||
"admin_restoreBlock": "Ripristina la sezione archiviata",
|
||||
"admin_blockAvailable": "La sezione è disponibile",
|
||||
"admin_blockArchived": "La sezione é archiviata",
|
||||
"admin_archiveBlock": "Archivia la sezione",
|
||||
"admin_note": "Note della pianificazione",
|
||||
"support_cat_closed": "Chiuso",
|
||||
"support_cat_open": "Casella in arrivo",
|
||||
"support_pending_tag": "Archiviato",
|
||||
"support_active_tag": "Casella in ingresso",
|
||||
"support_closed_tag": "Chiuso",
|
||||
"support_moveActive": "Sposta negli attivi",
|
||||
"install_header": "Installazione",
|
||||
"install_token": "Token di installazione",
|
||||
"admin_appSelection": "Configurazione app",
|
||||
"install_instance": "Crea il primo profilo da amministratore quindi procedi a configurare questa istanza",
|
||||
"install_launch": "Configura l’istanza",
|
||||
"admin_appsTitle": "Applicazioni dell’istanza",
|
||||
"admin_appsHint": "Scegli le app da abilitare su questa istanza.",
|
||||
"admin_cat_apps": "ApplicazIoni",
|
||||
"admin_onboardingOptionsTitle": "Opzioni dell’istanza",
|
||||
"admin_onboardingOptionsHint": "Scegli l’opzione appropriata per la tua istanza.<br> Queste configurazioni possono essere cambiate successivamente nel pannello di amministrazione."
|
||||
}
|
||||
|
||||
@ -490,7 +490,7 @@
|
||||
"admin_supportListTitle": "メールボックスのサポート",
|
||||
"friendRequest_later": "あとで決める",
|
||||
"admin_flushCacheButton": "キャッシュを消去",
|
||||
"admin_registeredTitle": "登録ユーザー",
|
||||
"admin_registeredTitle": "ユーザーとチームのドライブ",
|
||||
"crowdfunding_popup_no": "あとで",
|
||||
"sharedFolders_create_name": "フォルダー名",
|
||||
"creation_newTemplate": "新しいテンプレート",
|
||||
@ -1075,7 +1075,7 @@
|
||||
"admin_flushCacheTitle": "HTTPキャッシュを消去",
|
||||
"admin_updateLimitDone": "アップデートが完了しました",
|
||||
"admin_updateLimitButton": "クォータを更新",
|
||||
"admin_registeredHint": "あなたのインスタンスの登録ユーザー数",
|
||||
"admin_registeredHint": "あなたのインスタンスのアクティブなドライブ数",
|
||||
"admin_updateLimitTitle": "ユーザーのクォータを更新",
|
||||
"mdToolbar_defaultText": "ここにテキストを入力",
|
||||
"upload_modal_filename": "ファイル名(拡張子 <em>{0}</em> を自動で追加)",
|
||||
@ -1769,7 +1769,17 @@
|
||||
"admin_onboardingNameTitle": "あなたのCryptPadのインスタンスにようこそ",
|
||||
"support_legacyHint": "旧式のサポートシステムのチケットを表示した後、新しいサポートシステムで改めてチケットを作成します。",
|
||||
"admin_onboardingOptionsTitle": "インスタンスのオプション",
|
||||
"admin_onboardingOptionsHint": "インスタンスに適切な設定を選択してください。</br>これらの設定は、後から管理者パネルで変更できます。",
|
||||
"admin_onboardingOptionsHint": "インスタンスに適切な設定を選択してください。<br>これらの設定は、後から管理者パネルで変更できます。",
|
||||
"admin_onboardingNamePlaceholder": "インスタンスのタイトル",
|
||||
"admin_onboardingDescPlaceholder": "インスタントの説明文"
|
||||
"admin_onboardingDescPlaceholder": "インスタントの説明文",
|
||||
"form_condorcetRanked": "ランク付けペア",
|
||||
"admin_mfa_confirm_enable": "多要素認証を有効にしてよろしいですか?",
|
||||
"admin_mfa_confirm_disable": "多要素認証を無効にしてよろしいですか?",
|
||||
"install_instance": "初めに管理者アカウントを作成して、その後にインスタンスのカスタマイズに移ります",
|
||||
"support_legacyButton": "アクティブなチケットを入手",
|
||||
"support_searchLabel": "検索(タイトルまたはチケットのID)",
|
||||
"support_legacyClear": "このアカウントで削除",
|
||||
"support_moveActive": "アクティブに移動",
|
||||
"support_userNotification": "新しいサポートチケットまたは回答:{0}",
|
||||
"install_notes": "<ul class=\"cp-notes-list\"><li>このページで最初の管理者アカウントを作成してください。管理者は、ストレージの割合などを含むインスタンスの設定を管理したり、モデレーション用のツールにアクセスしたりできます。</li><li>あなたのパスワードは、あなたの全ドキュメントと、このインスタンスの管理者特権を暗号化する秘密鍵となります。 <span class=\"red\">パスワードを紛失した場合、データを復元することはできません。</span></li><li>コンピューターを共用している場合、作業を完了したら<span class=\"red\">必ずログアウトしてください</span>。ブラウザーのウインドウを閉じるだけでは、第三者によりあなたのアカウントにアクセスされるおそれがあります。 </li></ul>"
|
||||
}
|
||||
|
||||
@ -1780,5 +1780,8 @@
|
||||
"admin_onboardingOptionsTitle": "Instance options",
|
||||
"admin_onboardingOptionsHint": "Please select the appropriate option for your instance.<br>These settings can be changed later in the admin panel.",
|
||||
"admin_onboardingNamePlaceholder": "Instance title",
|
||||
"admin_onboardingDescPlaceholder": "Instance description text"
|
||||
"admin_onboardingDescPlaceholder": "Instance description text",
|
||||
"team_autoTrim": "Trimming team drive history... Please wait.",
|
||||
"admin_mfa_confirm_enable": "Are you sure you want to enable Multi-Factor Authentication?",
|
||||
"admin_mfa_confirm_disable": "Are you sure you want to disable Multi-Factor Authentication?"
|
||||
}
|
||||
|
||||
@ -509,8 +509,8 @@
|
||||
"admin_updateLimitButton": "Aktualizuj limity",
|
||||
"admin_updateLimitHint": "Wymuszenie aktualizacji limitu przestrzeni dostępnej dla użytkownika może być wykonane w dowolnym momencie, ale jest konieczne tylko w przypadku wystąpienia błędu",
|
||||
"admin_updateLimitTitle": "Aktualizuj limity użytkowników",
|
||||
"admin_registeredHint": "Liczba użytkowników zarejestrowanych na Twojej instancji",
|
||||
"admin_registeredTitle": "Użytkownicy zarejestrowani",
|
||||
"admin_registeredHint": "Liczba aktywnych dysków w Twojej instancji",
|
||||
"admin_registeredTitle": "Dyski indywidualne i zespołowe",
|
||||
"admin_activePadsHint": "Liczba unikalnych dokumentów, które są aktualnie przeglądane lub edytowane",
|
||||
"admin_activePadsTitle": "Aktywne dokumenty",
|
||||
"admin_activeSessionsHint": "Liczba aktywnych połączeń websocket (i połączonych unikalnych adresów IP)",
|
||||
@ -641,7 +641,7 @@
|
||||
"privacy": "Polityka prywatności",
|
||||
"about": "O stronie",
|
||||
"main_catch_phrase": "Pakiet do współpracy<br>end-to-end szyfrowany i oparty na otwartym kodzie źródłowym",
|
||||
"home_host": "To jest niezależna wersja społecznościowa CryptPada.",
|
||||
"home_host": "Niezależna społecznościowa instancja CryptPada.",
|
||||
"mdToolbar_toc": "Spis Treści",
|
||||
"mdToolbar_code": "Kod",
|
||||
"mdToolbar_check": "Lista zadań",
|
||||
@ -1057,7 +1057,7 @@
|
||||
"admin_defaultlimitHint": "Maksymalny limit przechowywania danych w CryptDrive (użytkownicy i zespoły), gdy nie jest stosowana żadna reguła własna",
|
||||
"admin_defaultlimitTitle": "Maksymalny limit przechowywania danych (MB)",
|
||||
"admin_registrationTitle": "Zamknij rejestrację",
|
||||
"admin_registrationHint": "Nie zezwalaj nowym użytkownikom na rejestrację",
|
||||
"admin_registrationHint": "Goście odwiedzający instancję nie mają możliwości stworzenia konta. Zaproszenia mogą być tworzone przez administratorów.",
|
||||
"snapshots_cantMake": "Nie można utworzyć obrazu. Jesteś rozłączony.",
|
||||
"snapshots_notFound": "Ten obraz już nie istnieje, ponieważ historia tego dokumentu została usunięta.",
|
||||
"snapshot_error_exists": "Istnieje już obraz tej wersji",
|
||||
@ -1397,7 +1397,7 @@
|
||||
"ui_saved": "{0} zapisano",
|
||||
"admin_nameHint": "Wyświetlana nazwa dla tego urządzenia z listy publicznych instacji na stronie cryptpad.org",
|
||||
"admin_archiveNote": "Notatka",
|
||||
"common_connectionLost": "<b>Utracono połączenie z serwerem</b><br>Dopóki połączenie nie wróci, włączony będzie tryb tylko do odczytu.",
|
||||
"common_connectionLost": "<b>Utracono Połączenie z Serwerem</b><br>Dopóki połączenie nie wróci, włączony będzie tryb tylko do odczytu.",
|
||||
"admin_infoNotice2": "Więcej informacji można znaleźć w zakładce \"Sieć\".",
|
||||
"admin_nameTitle": "Nazwa instancji",
|
||||
"support_warning_abuse": "Prosimy o zgłaszanie treści, które naruszają <a>Warunki korzystania z usługi</a>. Prosimy o podanie linków do obraźliwych dokumentów lub profili użytkowników i opisanie, w jaki sposób naruszają one warunki. Wszelkie dodatkowe informacje na temat kontekstu, w którym odkryłeś treść lub zachowanie, mogą pomóc administratorom w zapobieganiu przyszłym naruszeniom",
|
||||
@ -1672,7 +1672,7 @@
|
||||
"kanban_showTags": "Wszystkie tagi",
|
||||
"kanban_hideTags": "Mniej tagów",
|
||||
"admin_forcemfaTitle": "Wymagaj Uwierzytelniania Dwuetapowego",
|
||||
"admin_forcemfaHint": "Wszyscy użytkownicy tej instancji zostaną poproszeni o skonfigurowanie uwierzytelniania dwuetapowego, aby zalogować się do swojego konta.",
|
||||
"admin_forcemfaHint": "Wszyscy użytkownicy tej instancji zostaną poproszeni o skonfigurowanie uwierzytelniania dwuetapowego, aby zalogować się do swojego konta. Użytkownicy już istniejący również nie będą mogli korzystać ze swojego konta bez konfiguracji aplikacji TOTP.",
|
||||
"support_recordedContent": "Treść",
|
||||
"support_legacyDump": "Wyeksportuj wszystko",
|
||||
"support_legacyClear": "Usuń to konto",
|
||||
@ -1763,5 +1763,22 @@
|
||||
"admin_usersHint": "Lista znanych Ci kont na tej instancji. Zaznacz poniżej aby dodać konta automatycznie, lub wprowadź informacje ręcznie za pomocą formularza.",
|
||||
"admin_supportSetupHint": "Stwórz lub aktualizuj klucze pomocy.",
|
||||
"admin_supportRotateNotify": "Uwaga: nowe klucze zostały wygenerowane, ale nieoczekiwany błąd nie pozwolił systemowi wysłać ich moderatorom. Usuń i dodaj ponownie członków zespołu",
|
||||
"support_notificationsHint": "Zaznacz tę opcję, żeby wyłączyć powiadomienia o nowych zgłoszeniach i odpowiedziach"
|
||||
"support_notificationsHint": "Zaznacz tę opcję, żeby wyłączyć powiadomienia o nowych zgłoszeniach i odpowiedziach",
|
||||
"admin_appSelection": "Konfiguracja aplikacji",
|
||||
"admin_appsTitle": "Aplikacje instancji",
|
||||
"admin_appsHint": "Wybierz, które aplikacje włączyć dla tej instancji.",
|
||||
"admin_cat_apps": "Aplikacje",
|
||||
"install_token": "Token instalacyjny",
|
||||
"install_header": "Instalacja",
|
||||
"install_instance": "Stwórz pierwsze konto administratora, a następnie dostosuj instancję",
|
||||
"install_launch": "Konfiguracja instancji",
|
||||
"install_notes": "<ul class=\"cp-notes-list\"><li>Na tej stronie stworzysz swoje pierwsze konto administratora. Administratorzy zarządzają ustawieniami instancji, w tym przydziałem przestrzeni dyskowej i mają dostęp do narzędzi moderacji.</li><li>Twoje hasło to tajny klucz, szyfrujący wszystkie Twoje dokumenty i przywileje administratora na tej instancji. <span class=\"red\">Jeśli je zgubisz, nie mamy możliwości odzyskania Twoich danych.</span></li><li>Jeśli dzielisz komputer z innymi osobami, <span class=\"red\">pamiętaj, aby się wylogować</span> po zakończeniu pracy. Zamknięcie jedynie okna przeglądarki naraża bezpieczeństwo Twojego konta.</li></ul>",
|
||||
"onboarding_upload": "Wybierz logo",
|
||||
"onboarding_save_error": "Niektóre opcje nie zostały prawidłowo zapisane. Odwiedź panel administratora, aby sprawdzić ich wartości.",
|
||||
"admin_onboardingNameHint": "Wybierz tytuł, opis, kolor wiodący i logo (opcjonalnie)",
|
||||
"admin_onboardingNameTitle": "Witaj w Twojej instancji CryptPad",
|
||||
"admin_onboardingOptionsTitle": "Opcje instancji",
|
||||
"admin_onboardingNamePlaceholder": "Tytuł instancji",
|
||||
"admin_onboardingDescPlaceholder": "Treść opisu instancji",
|
||||
"admin_onboardingOptionsHint": "Zaznacz właściwą opcję dla Twojej instancji.<br>To ustawienie może być później zmienione w panelu administratora."
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -931,8 +931,8 @@
|
||||
"admin_updateLimitButton": "Обновить квоты",
|
||||
"admin_updateLimitHint": "Принудительное обновление лимитов пользовательского хранилища можно выполнить в любое время, но это необходимо только в случае ошибки",
|
||||
"admin_updateLimitTitle": "Обновить пользовательские квоты",
|
||||
"admin_registeredHint": "Количество пользователей, зарегистрированных на вашем экземпляре",
|
||||
"admin_registeredTitle": "Зарегистрированные пользователи",
|
||||
"admin_registeredHint": "Количество активных дисков на Вашем экземпляре",
|
||||
"admin_registeredTitle": "Дисков пользователей и команд",
|
||||
"admin_activePadsHint": "Количество уникальных документов, просматриваемых или редактируемых в данный момент",
|
||||
"admin_activePadsTitle": "Активные документы",
|
||||
"admin_activeSessionsHint": "Количество активных подключений к веб-сокетам (и подключенных уникальных IP-адресов)",
|
||||
@ -1780,5 +1780,7 @@
|
||||
"admin_onboardingOptionsHint": "Пожалуйста, выберите подходящий вариант для Вашего экземпляра.<br>Эти настройки можно изменить позже в панели администратора.",
|
||||
"admin_onboardingNamePlaceholder": "Заголовок экземпляра",
|
||||
"install_instance": "Создайте первую учетную запись администратора, а затем приступайте к настройке этого экземпляра",
|
||||
"admin_onboardingDescPlaceholder": "Текст описания экземпляра"
|
||||
"admin_onboardingDescPlaceholder": "Текст описания экземпляра",
|
||||
"admin_mfa_confirm_disable": "Вы уверены, что хотите выключить многофакторную аутентификацию (MFA)?",
|
||||
"admin_mfa_confirm_enable": "Вы уверены, что хотите включить многофакторную аутентификацию (MFA)?"
|
||||
}
|
||||
|
||||
@ -4,20 +4,24 @@
|
||||
|
||||
// This is the initialization loading the CryptPad libraries
|
||||
define([
|
||||
'jquery',
|
||||
'/common/sframe-app-framework.js',
|
||||
'/customize/messages.js', // translation keys
|
||||
'/components/pako/dist/pako.min.js',
|
||||
'/components/x2js/x2js.js',
|
||||
'/diagram/util.js',
|
||||
'/common/common-ui-elements.js',
|
||||
'/components/tweetnacl/nacl-fast.min.js',
|
||||
'less!/diagram/app-diagram.less',
|
||||
'css!/diagram/drawio.css',
|
||||
], function (
|
||||
$,
|
||||
Framework,
|
||||
Messages,
|
||||
pako,
|
||||
X2JS,
|
||||
DiagramUtil
|
||||
DiagramUtil,
|
||||
UIElements
|
||||
) {
|
||||
const Nacl = window.nacl;
|
||||
const APP = window.APP = {};
|
||||
@ -61,6 +65,15 @@ define([
|
||||
return JSON.stringify(o1) === JSON.stringify(o2);
|
||||
};
|
||||
|
||||
var mkHelpMenu = function (framework) {
|
||||
var $codeMirrorContainer = $('#cp-app-diagram-container');
|
||||
var helpMenu = framework._.sfCommon.createHelpMenu(['diagram']);
|
||||
$codeMirrorContainer.prepend(helpMenu.menu);
|
||||
|
||||
var $helpMenuButton = UIElements.getEntryFromButton(helpMenu.button);
|
||||
framework._.toolbar.$drawer.append($helpMenuButton);
|
||||
};
|
||||
|
||||
// This is the main initialization loop
|
||||
var onFrameworkReady = function (framework) {
|
||||
var EMPTY_DRAWIO = "<mxfile type=\"embed\"><diagram id=\"bWoO5ACGZIaXrIiKNTKd\" name=\"Page-1\"><mxGraphModel dx=\"1259\" dy=\"718\" grid=\"1\" gridSize=\"10\" guides=\"1\" tooltips=\"1\" connect=\"1\" arrows=\"1\" fold=\"1\" page=\"1\" pageScale=\"1\" pageWidth=\"827\" pageHeight=\"1169\" math=\"0\" shadow=\"0\"><root><mxCell id=\"0\"/><mxCell id=\"1\" parent=\"0\"/></root></mxGraphModel></diagram></mxfile>";
|
||||
@ -69,6 +82,11 @@ define([
|
||||
var lastContent = x2js.xml2js(EMPTY_DRAWIO);
|
||||
var drawIoInitalized = false;
|
||||
|
||||
var privateData = framework._.cpNfInner.metadataMgr.getPrivateData();
|
||||
if (!privateData.isEmbed) {
|
||||
mkHelpMenu(framework);
|
||||
}
|
||||
|
||||
var postMessageToDrawio = function(msg) {
|
||||
if (!drawIoInitalized) {
|
||||
return;
|
||||
|
||||
@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
</head>
|
||||
<body class="cp-app-drive cp-body-drive">
|
||||
<div id="cp-toolbar" class="cp-toolbar-container"></div>
|
||||
<div class="cp-app-drive-container" tabindex="0">
|
||||
<div class="cp-app-drive-container">
|
||||
<div id="cp-app-drive-tree">
|
||||
</div>
|
||||
<div id="cp-app-drive-content-container">
|
||||
|
||||
@ -189,7 +189,7 @@ define([
|
||||
anywhere else then we can deprecate them and make this a
|
||||
custom modal in common-interface (or here). */
|
||||
cancelClass: 'btn.btn-cancel.btn-register',
|
||||
okClass: 'btn.btn-danger.btn-register',
|
||||
okClass: 'btn.btn-danger.btn-register.btn-confirm',
|
||||
reverseOrder: true,
|
||||
done: function ($dialog) {
|
||||
$dialog.find('> div').addClass('half');
|
||||
|
||||
@ -177,8 +177,8 @@
|
||||
align-self: flex-start;
|
||||
outline-style: none;
|
||||
border-radius: @variables_radius;
|
||||
&:focus {
|
||||
outline: @cryptpad_color_brand solid 2px;
|
||||
&:focus-visible {
|
||||
outline: @variables_focus_style;
|
||||
}
|
||||
@media (hover: none) {
|
||||
margin-right: 20px;
|
||||
@ -413,8 +413,8 @@
|
||||
.fa {
|
||||
margin-right: 5px;
|
||||
}
|
||||
&:focus{
|
||||
outline: @cryptpad_color_brand solid 2px;
|
||||
&:focus-visible {
|
||||
outline: @variables_focus_style;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -651,8 +651,8 @@
|
||||
&:hover {
|
||||
background-color: @cp_kanban-add-hover;
|
||||
}
|
||||
&:focus {
|
||||
outline: @cryptpad_color_brand solid 2px;
|
||||
&:focus-visible {
|
||||
outline: @variables_focus_style;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -175,7 +175,7 @@ define([
|
||||
anywhere else then we can deprecate them and make this a
|
||||
custom modal in common-interface (or here). */
|
||||
cancelClass: 'btn.btn-cancel.btn-register',
|
||||
okClass: 'btn.btn-danger.btn-register',
|
||||
okClass: 'btn.btn-danger.btn-register.btn-confirm',
|
||||
reverseOrder: true,
|
||||
done: function ($dialog) {
|
||||
$dialog.find('> div').addClass('half');
|
||||
|
||||
@ -20,6 +20,9 @@ define([
|
||||
'/common/userObject.js',
|
||||
'/common/clipboard.js',
|
||||
'/common/outer/login-block.js',
|
||||
'/common/outer/roster.js',
|
||||
'/common/rpc.js',
|
||||
'/common/pinpad.js',
|
||||
|
||||
|
||||
'/components/tweetnacl/nacl-fast.min.js',
|
||||
@ -27,7 +30,7 @@ define([
|
||||
'less!/customize/src/less2/pages/page-report.less',
|
||||
], function ($, ApiConfig, h, Messages,
|
||||
nThen, Hash, Util, Constants, Crypt, Cryptpad, Cache, UI, CPNetflux,
|
||||
Crypto, UserObject, Clipboard, Block) {
|
||||
Crypto, UserObject, Clipboard, Block, Roster, Rpc, Pinpad) {
|
||||
var $report = $('#cp-report');
|
||||
var blockHash = localStorage.Block_hash;
|
||||
if (!blockHash) {
|
||||
@ -41,6 +44,21 @@ define([
|
||||
});
|
||||
}
|
||||
|
||||
let report = true;
|
||||
let fixRoster = false;
|
||||
|
||||
let urlHash = window.location.hash.slice(1);
|
||||
if (urlHash) {
|
||||
let s = urlHash.split('=');
|
||||
let key = s[0];
|
||||
let value = s[1];
|
||||
if (key === "roster") {
|
||||
report = false;
|
||||
fixRoster = value; // teamid
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var addReport = function (str) {
|
||||
$report.append(h('div', str));
|
||||
};
|
||||
@ -153,6 +171,8 @@ define([
|
||||
addReport('Teams: ' + Object.keys(proxy.teams || {}).join(', '));
|
||||
addReport('-------------------');
|
||||
|
||||
if (!report) { return; }
|
||||
|
||||
var n = nThen;
|
||||
Object.keys(drive.sharedFolders || {}).forEach(function (id) {
|
||||
n = n(function (w) {
|
||||
@ -188,16 +208,67 @@ define([
|
||||
addReport('===================');
|
||||
var n = nThen;
|
||||
Object.keys(proxy.teams || {}).forEach(function (id) {
|
||||
// If we're in "repair" mode, only load the affected team
|
||||
var obj = proxy.teams[id];
|
||||
if (!report && fixRoster !== obj.channel) { return; }
|
||||
n = n(function (w) {
|
||||
var next = w();
|
||||
var obj = proxy.teams[id];
|
||||
var team;
|
||||
addReport('Load team. ID: ' + id + '. Channel ID: '+ obj.channel);
|
||||
addReport('Roster channel: ' + obj.keys.roster.channel);
|
||||
addReport('Roster lkh: ' + obj.keys.roster.lastKnownHash);
|
||||
if (!obj.hash) { addReport("View only"); }
|
||||
|
||||
var teamSecret = Hash.getSecrets('team', obj.hash || obj.roHash, obj.password);
|
||||
var cryptor = UserObject.createCryptor(teamSecret.keys.secondaryKey);
|
||||
|
||||
|
||||
// Repair roster mode
|
||||
if (!report) {
|
||||
let roster = obj.keys.roster;
|
||||
let rpc, anon_rpc;
|
||||
if (!obj.owner || !roster.edit) {
|
||||
return void addReport('Roster error: only owners can repair');
|
||||
}
|
||||
n(w => {
|
||||
Rpc.createAnonymous(network, w(function (e, call) {
|
||||
anon_rpc = call;
|
||||
}));
|
||||
Pinpad.create(network, proxy, w(function (e, call) {
|
||||
rpc = call;
|
||||
}));
|
||||
}).nThen(w => {
|
||||
if (!anon_rpc || !rpc) {
|
||||
return void addReport('RPC error');
|
||||
}
|
||||
var rosterKeys = Crypto.Team.deriveMemberKeys(roster.edit, {
|
||||
curvePublic: proxy.curvePublic,
|
||||
curvePrivate: proxy.curvePrivate
|
||||
});
|
||||
let store = { anon_rpc };
|
||||
Roster.create({
|
||||
network: network,
|
||||
channel: roster.channel,
|
||||
keys: rosterKeys,
|
||||
store: store,
|
||||
lastKnownHash: roster.lastKnownHash
|
||||
}, w(function (err, _roster) {
|
||||
if (err) { return void addReport('Fix roster error', err); }
|
||||
_roster.checkpoint(w((err, state, hash) => {
|
||||
if (err) { addReport('Fix roster error', err); }
|
||||
rpc.trimHistory({
|
||||
channel: roster.channel,
|
||||
hash
|
||||
}, w(function (err) {
|
||||
console.error(arguments);
|
||||
if (err) { addReport('Trim roster error', err); }
|
||||
}));
|
||||
}));
|
||||
}));
|
||||
}).nThen(next);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check team drive
|
||||
nThen(function (ww) {
|
||||
addReport('Team drive');
|
||||
|
||||
@ -38,8 +38,24 @@
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.alertify {
|
||||
nav {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
.btn-confirm {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#cp-sidebarlayout-container {
|
||||
#cp-sidebarlayout-rightside {
|
||||
input, button, span[tabindex="0"] {
|
||||
&:focus-visible {
|
||||
outline: @variables_focus_style;
|
||||
}
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
vertical-align: middle;
|
||||
margin-right: 5px;
|
||||
@ -112,6 +128,14 @@
|
||||
margin-top:0;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.cp-password-reveal {
|
||||
border-radius:@variables_radius;
|
||||
}
|
||||
}
|
||||
nav {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-end;
|
||||
}
|
||||
@media (max-width: 840px) {
|
||||
|
||||
|
||||
@ -807,7 +807,7 @@ define([
|
||||
}, {
|
||||
ok: Messages.register_writtenPassword,
|
||||
cancel: Messages.register_cancel,
|
||||
okClass: 'btn.btn-danger',
|
||||
okClass: 'btn.btn-danger.btn-confirm',
|
||||
reverseOrder: true,
|
||||
done: function($dialog) {
|
||||
$dialog.find('> div').addClass('half');
|
||||
|
||||
@ -62,6 +62,14 @@
|
||||
div#cp-sidebarlayout-leftside {
|
||||
background-color: @cp_teams-leftside-bg;
|
||||
}
|
||||
#cp-sidebarlayout-rightside{
|
||||
input, button, textarea {
|
||||
&:focus-visible {
|
||||
outline: @variables_focus_style;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
div#cp-sidebarlayout-rightside.cp-rightside-drive {
|
||||
padding: 0;
|
||||
& > .cp-team-chat {
|
||||
@ -73,7 +81,11 @@
|
||||
}
|
||||
.cp-app-contacts-input {
|
||||
textarea {
|
||||
border: 0px;
|
||||
border: 0;
|
||||
margin-right: 2px;
|
||||
}
|
||||
.fa-paper-plane{
|
||||
border-radius: @variables_radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -96,7 +108,7 @@
|
||||
}
|
||||
}
|
||||
.cp-team-cat-header {
|
||||
justify-content: center;
|
||||
justify-content: left;
|
||||
background-color: transparent !important;
|
||||
box-shadow: none !important;
|
||||
.avatar_main(30px);
|
||||
@ -106,6 +118,7 @@
|
||||
}
|
||||
media-tag, .cp-avatar-default {
|
||||
margin-right: 3px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
media-tag {
|
||||
order: -1;
|
||||
@ -113,7 +126,12 @@
|
||||
cursor: default !important;
|
||||
font-size: 18px;
|
||||
span.cp-sidebarlayout-category-name {
|
||||
max-width: 12rem;
|
||||
padding-left: 5px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
.cp-team-cat-chat {
|
||||
@ -142,6 +160,9 @@
|
||||
height: 384px;
|
||||
padding: 50px;
|
||||
margin: 5px;
|
||||
&:focus-visible {
|
||||
outline: @variables_focus_style;
|
||||
}
|
||||
&.create, &:not(.empty) {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
@ -292,6 +313,15 @@
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.cp-team-trim {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.fa {
|
||||
font-size: 30px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.cp-teams-invite-uses {
|
||||
input {
|
||||
|
||||
@ -191,6 +191,7 @@ define([
|
||||
'cp-team-avatar',
|
||||
'cp-team-export',
|
||||
'cp-team-delete',
|
||||
'cp-team-history',
|
||||
],
|
||||
};
|
||||
|
||||
@ -398,6 +399,43 @@ define([
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
let AUTOTRIM_LIMIT = 102400; // 100kB history before auto trim
|
||||
var trimHistory = function () {
|
||||
var size;
|
||||
var channels = [];
|
||||
nThen(function(waitFor) {
|
||||
APP.history.execCommand('GET_HISTORY_SIZE', {
|
||||
team: APP.team,
|
||||
channels: []
|
||||
}, waitFor(function(obj) {
|
||||
if (obj && obj.error) {
|
||||
waitFor.abort();
|
||||
console.error(obj.error);
|
||||
return;
|
||||
}
|
||||
channels = obj.channels;
|
||||
size = Number(obj.size);
|
||||
}));
|
||||
}).nThen(function() {
|
||||
if (!size || size < AUTOTRIM_LIMIT) {
|
||||
// Nothing to delete
|
||||
return;
|
||||
}
|
||||
var div = h('div.cp-team-trim', [
|
||||
h('span.fa.fa-spin.fa-spinner'),
|
||||
h('span', Messages.team_autoTrim)
|
||||
]);
|
||||
UI.openCustomModal(UI.dialog.customModal(div, {buttons: []}));
|
||||
console.log('Trimming team history', APP.team, size);
|
||||
APP.history.execCommand('TRIM_HISTORY', {
|
||||
channels: channels
|
||||
}, function(obj) {
|
||||
if (obj && obj.error) { console.error(obj.error); }
|
||||
UI.removeModals();
|
||||
});
|
||||
});
|
||||
};
|
||||
var openTeam = function (common, id, team) {
|
||||
var sframeChan = common.getSframeChannel();
|
||||
APP.module.execCommand('SUBSCRIBE', id, function () {
|
||||
@ -422,6 +460,7 @@ define([
|
||||
APP.team = id;
|
||||
APP.teamEdPublic = Util.find(team, ['keys', 'drive', 'edPublic']);
|
||||
buildUI(common, true, team.owner);
|
||||
if (team.owner) { trimHistory(common); }
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -1556,6 +1595,7 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
APP.history = common.makeUniversal('history');
|
||||
APP.module = common.makeUniversal('team', {
|
||||
onEvent: onEvent
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user