mirror of
https://github.com/cryptpad/cryptpad.git
synced 2026-09-14 11:05:41 +05:00
Merge branch 'staging' into forms-footer-improvements
This commit is contained in:
commit
7eda1715cb
75
CHANGELOG.md
75
CHANGELOG.md
@ -4,6 +4,81 @@ SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and cont
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
# 🌷 Spring release (2026.5.0)
|
||||
|
||||
## Goals
|
||||
|
||||
This release introduces an updated version of the Diagram app, now powered by Drawio 29. The app now defaults to the "sketch" theme, a simple infinite canvas suited to many uses from mind-mapping to freehand drawing. We introduce a theme switcher so that everyone can choose the right level of complexity for their needs. This release also comes with lots of fixes and improvements across CryptPad.
|
||||
|
||||
## Features
|
||||
|
||||
- Upgrade Diagram app to Drawio 29.6.7 [22fe846](https://github.com/cryptpad/cryptpad/pull/2192/changes/22fe846d75a7cdbc9a98878dd9c131ed5dbf5fe0)
|
||||
- Button to switch diagram mode [#2192](https://github.com/cryptpad/cryptpad/pull/2192)
|
||||
- Notifications for private messages [#2133](https://github.com/cryptpad/cryptpad/pull/2133)
|
||||
|
||||
|
||||
## Improvements
|
||||
|
||||
- Improve Form accessibility [#2260](https://github.com/cryptpad/cryptpad/pull/2260)
|
||||
- Enable zh-Hant/zh-Hans locales (#2237) and add alias system for locales [#2254](https://github.com/cryptpad/cryptpad/pull/2254) by @toomore
|
||||
- Improve crowdfunding banner UI and show logic [#2242](https://github.com/cryptpad/cryptpad/pull/2242)
|
||||
- Contacts page improvements [#2219](https://github.com/cryptpad/cryptpad/pull/2219)
|
||||
|
||||
|
||||
## Fixes
|
||||
|
||||
- fix: set bearer secret in env [#2268](https://github.com/cryptpad/cryptpad/pull/2268) by @ebuildy
|
||||
- Diagram initialized in read-only mode until document is ready [#2238](https://github.com/cryptpad/cryptpad/pull/2238)
|
||||
- Fix #2216: Table of contents not clickable in read-only mode [#2229](https://github.com/cryptpad/cryptpad/pull/2229) by @sliortega295-ops
|
||||
- Enforce immediate access-list lockout and prevent stale content visibility on refresh [#2226](https://github.com/cryptpad/cryptpad/pull/2226)
|
||||
- Fix app icons and spacing in Drive "Open in" context menu [#2213](https://github.com/cryptpad/cryptpad/pull/2213)
|
||||
- Fix leftside sidebar buttons' text overflow and prevent icon shrinking [#2212](https://github.com/cryptpad/cryptpad/pull/2212)
|
||||
- Fix paragraph selection in richtext for mobile [#2208](https://github.com/cryptpad/cryptpad/pull/2208)
|
||||
- Remove kanban tags when board is deleted [#2188](https://github.com/cryptpad/cryptpad/pull/2188)
|
||||
- Check for other users before OnlyOffice upload [#2228](https://github.com/cryptpad/cryptpad/pull/2228)
|
||||
- Update status for trashed OnlyOffice documents [#2183](https://github.com/cryptpad/cryptpad/pull/2183)
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Upgrades
|
||||
- chainpad-server: from ^5.2.4 to ^5.3.0
|
||||
- drawio-npm: from 21.8.2+6 to 29.6.7+3
|
||||
|
||||
## Upgrade notes
|
||||
|
||||
### SSO plugin
|
||||
|
||||
If your instance relies on the SSO plugin for authentication, please upgrade the plugin to [0.5.0](https://github.com/cryptpad/sso/releases/tag/0.5.0) as part of this upgrade.
|
||||
|
||||
### CryptPad
|
||||
|
||||
If you are upgrading from a version older than `2026.2.2` please read the upgrade notes of all versions between yours and `2026.5.0` to avoid configuration issues.
|
||||
|
||||
To upgrade:
|
||||
|
||||
1. Stop your server
|
||||
2. Get the latest code with git
|
||||
|
||||
```bash
|
||||
git fetch --depth 1 origin tag 2026.5.0
|
||||
git checkout 2026.5.0
|
||||
npm ci
|
||||
npm run install:components
|
||||
./install-onlyoffice.sh
|
||||
```
|
||||
|
||||
3. Restart your server
|
||||
4. Review your instance's checkup page to ensure that you are passing all tests
|
||||
|
||||
|
||||
## Contributors
|
||||
|
||||
Community: @toomore @sliortega295-ops @ebuildy
|
||||
|
||||
CryptPad team: @AAAMON @Chouhartem @dariiing @davidbenque @DianaXWiki @wginolas @yflory @zuzanna-maria
|
||||
|
||||
|
||||
# ❄️🩹🩹 Winter fix release 2 (2026.2.2)
|
||||
|
||||
## Goals
|
||||
|
||||
@ -13,7 +13,8 @@ CKEDITOR.editorConfig = function( config ) {
|
||||
config.removeButtons= 'Source,Maximize';
|
||||
// magicline plugin inserts html crap into the document which is not part of the
|
||||
// document itself and causes problems when it's sent across the wire and reflected back
|
||||
config.removePlugins= 'resize,elementspath,liststyle';
|
||||
var isMobile = window.matchMedia ? window.matchMedia('(pointer: coarse)').matches : navigator.maxTouchPoints > 0;
|
||||
config.removePlugins= 'resize,elementspath,liststyle' + (isMobile ? ',contextmenu,tabletools,tableselection' : '');
|
||||
config.resize_enabled= false; //bottom-bar
|
||||
config.extraPlugins= 'autolink,colorbutton,colordialog,font,indentblock,justify,mediatag,print,blockbase64,mathjax,wordcount,comments';
|
||||
config.toolbarGroups= [
|
||||
|
||||
@ -49,12 +49,17 @@ define([
|
||||
};
|
||||
if (window.location.hash) { setRedirectTo(); }
|
||||
|
||||
Exports.ssoRedirectTo = (localData) => {
|
||||
redirectTo = localData?.redirectTo || redirectTo;
|
||||
};
|
||||
Exports.ssoAuth = function (provider, cb) {
|
||||
var keys = Nacl.sign.keyPair();
|
||||
var inviteToken = window.location.hash.slice(1);
|
||||
|
||||
localStorage.CP_sso_auth = JSON.stringify({
|
||||
s: Util.encodeBase64(keys.secretKey),
|
||||
p: Util.encodeBase64(keys.publicKey),
|
||||
redirectTo,
|
||||
token: inviteToken
|
||||
});
|
||||
ServerCommand(keys, {
|
||||
|
||||
@ -29,19 +29,42 @@ var map = {
|
||||
'sv': 'Svenska',
|
||||
//'te': 'తెలుగు',
|
||||
'uk': 'Українська',
|
||||
'zh': '中文(簡體)',
|
||||
'zh': '中文(簡體)', // simplified
|
||||
'zh_Hant': '中文(正體)', // traditional
|
||||
};
|
||||
|
||||
var Messages = {};
|
||||
var LS_LANG = "CRYPTPAD_LANG";
|
||||
var getStoredLanguage = function () { return localStorage && localStorage.getItem(LS_LANG); };
|
||||
var getBrowserLanguage = function () { return navigator.language || navigator.userLanguage || ''; };
|
||||
// Normalize browser/localStorage language labels to CryptPad internal keys.
|
||||
// We keep this centralized to avoid scattered `if (l === 'zh') ...` logic.
|
||||
var langAliases = {
|
||||
'zh-cn': 'zh',
|
||||
'zh-sg': 'zh',
|
||||
'zh-hans': 'zh',
|
||||
|
||||
'zh-tw': 'zh_Hant',
|
||||
'zh-hk': 'zh_Hant',
|
||||
'zh-mo': 'zh_Hant',
|
||||
'zh-hant': 'zh_Hant',
|
||||
};
|
||||
var normalizeLanguage = function (l) {
|
||||
if (!l) { return l; }
|
||||
// If it already matches a supported internal key, return as-is.
|
||||
if (map[l]) { return l; }
|
||||
var lLower = String(l).toLowerCase().replace('_', '-');
|
||||
return langAliases[lLower] || l;
|
||||
};
|
||||
var getLanguage = Messages._getLanguage = function () {
|
||||
if (window.cryptpadLanguage) { return window.cryptpadLanguage; }
|
||||
|
||||
var l = getBrowserLanguage();
|
||||
try {
|
||||
l = getStoredLanguage() || getBrowserLanguage();
|
||||
} catch (e) { console.log(e); }
|
||||
l = normalizeLanguage(l);
|
||||
|
||||
return map[l] ? l :
|
||||
(map[l.split('-')[0]] ? l.split('-')[0] :
|
||||
(map[l.split('_')[0]] ? l.split('_')[0] : 'en'));
|
||||
|
||||
@ -396,6 +396,14 @@
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: @browser_media-medium-screen) {
|
||||
.cp-crowdfunding-modal nav {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -33,6 +33,16 @@
|
||||
li {
|
||||
padding: 0;
|
||||
font-size: @colortheme_app-font-size;
|
||||
a.cp-app-drive-context-openincode,
|
||||
a.cp-app-drive-context-openinsheet,
|
||||
a.cp-app-drive-context-openindoc,
|
||||
a.cp-app-drive-context-openinpresentation {
|
||||
& > svg.lucide {
|
||||
margin: 0 0.25rem;
|
||||
vector-effect: non-scaling-stroke;
|
||||
stroke-width: 1.7;
|
||||
}
|
||||
}
|
||||
&.dropdown-submenu {
|
||||
position: relative;
|
||||
&> a {
|
||||
|
||||
@ -976,6 +976,12 @@
|
||||
stroke-width: 3.5;
|
||||
vector-effect: non-scaling-stroke;
|
||||
}
|
||||
&.cp-app-drive-content-grid {
|
||||
li.cp-app-drive-element-row > svg.lucide * {
|
||||
stroke-width: 3.5;
|
||||
vector-effect: non-scaling-stroke;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#cp-app-drive-new-ghost-dialog.cp-modal-container {
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
@import (reference) './avatar.less';
|
||||
@import (reference) './badges.less';
|
||||
@import (reference) "./colortheme-all.less";
|
||||
@import (reference) "./browser.less";
|
||||
|
||||
.messenger_vars (
|
||||
@bg-color: @cp_messenger-bg,
|
||||
@ -89,7 +90,6 @@
|
||||
background-color: var(--msg-bg-color);
|
||||
color: @msg-color;
|
||||
color: var(--msg-color);
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
.cp-app-contacts-friend {
|
||||
@ -100,9 +100,15 @@
|
||||
margin-bottom: 0;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
height: @room-height;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
.cp-avatar {
|
||||
flex-shrink: 0;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.cp-app-contacts-right-col {
|
||||
margin-left: 5px;
|
||||
margin-left: 0;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
flex: 1;
|
||||
@ -112,13 +118,53 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.cp-app-contacts-icons {
|
||||
text-align: right;
|
||||
& > span:hover {
|
||||
color: @msg-color-hover;
|
||||
.cp-app-contacts-bottom-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
.cp-app-contacts-mute-indicator {
|
||||
font-size: 0.75em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
svg {
|
||||
margin-right: 0.1rem;
|
||||
.cp-app-contacts-icons {
|
||||
margin-left: auto;
|
||||
.cp-app-contacts-dropdown-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: none;
|
||||
border: none;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
padding: 2px;
|
||||
border-radius: @variables_radius;
|
||||
flex-shrink: 0;
|
||||
&:hover {
|
||||
background-color: var(--msg-bg-color-dark);
|
||||
}
|
||||
&:focus-visible {
|
||||
outline: @variables_focus_style;
|
||||
border-radius: @variables_radius;
|
||||
}
|
||||
svg {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.cp-dropdown-content {
|
||||
min-width: auto;
|
||||
a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
svg {
|
||||
margin: 0;
|
||||
}
|
||||
li:focus-visible {
|
||||
outline: @variables_focus_style;
|
||||
border-radius: @variables_radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -134,6 +180,10 @@
|
||||
border-color: var(--msg-bg-color-darker);
|
||||
}
|
||||
}
|
||||
&:focus-visible {
|
||||
outline: @variables_focus_style;
|
||||
border-radius: @variables_radius;
|
||||
}
|
||||
&.cp-app-contacts-notify {
|
||||
animation: notif 2s ease-in-out infinite;
|
||||
}
|
||||
@ -166,7 +216,7 @@
|
||||
.cp-app-contacts-category-content {
|
||||
order: 2;
|
||||
display: flex;
|
||||
flex-flow: column-reverse;
|
||||
flex-flow: column;
|
||||
padding-bottom: 10px;
|
||||
&:empty {
|
||||
display: none;
|
||||
@ -236,6 +286,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.cp-app-contacts-no-chats {
|
||||
#cp-app-contacts-friendlist {
|
||||
display: none;
|
||||
}
|
||||
#cp-app-contacts-messaging {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
#cp-app-contacts-messaging {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
@ -245,6 +305,17 @@
|
||||
|
||||
.cp-app-contacts-info {
|
||||
padding: 20px;
|
||||
a {
|
||||
color: @msg-color;
|
||||
color: var(--msg-color);
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
svg {
|
||||
outline: @variables_focus_style;
|
||||
border-radius: @variables_radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-app-contacts-header {
|
||||
background-color: @msg-bg-color-lighter;
|
||||
@ -257,19 +328,17 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
min-height: 50px;
|
||||
svg {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.hover () {
|
||||
cursor: pointer;
|
||||
height: 100%;
|
||||
line-height: 30px;
|
||||
padding: 10px;
|
||||
border-radius: @variables_radius;
|
||||
&:hover {
|
||||
background-color: @msg-bg-color-darker;
|
||||
background-color: var(--msg-color-darker);
|
||||
background-color: var(--msg-bg-color-dark);
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,9 +346,23 @@
|
||||
.cp-app-contacts-right-col {
|
||||
flex: 1 1 auto;
|
||||
text-align: center;
|
||||
min-width: 0;
|
||||
.cp-app-contacts-name {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
.cp-app-contacts-right-col {
|
||||
overflow: hidden;
|
||||
}
|
||||
.cp-app-contacts-remove-history {
|
||||
.hover;
|
||||
margin-right: 3px;
|
||||
&:focus-visible {
|
||||
outline: @variables_focus_style;
|
||||
border-radius: @variables_radius;
|
||||
}
|
||||
}
|
||||
.cp-avatar-container {
|
||||
display: flex;
|
||||
@ -294,6 +377,10 @@
|
||||
&.cp-app-contacts-faded {
|
||||
color: @cryptpad_text_col;
|
||||
}
|
||||
&:focus-visible {
|
||||
outline: @variables_focus_style;
|
||||
border-radius: @variables_radius;
|
||||
}
|
||||
}
|
||||
|
||||
.cp-app-contacts-header-title {
|
||||
@ -320,60 +407,79 @@
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
.cp-app-contacts-history-cleared {
|
||||
font-style: italic;
|
||||
background-color: fade(@cp_help-bg, 50%);
|
||||
padding: 2px 10px;
|
||||
> svg {
|
||||
flex-shrink: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.cp-app-contacts-messages {
|
||||
padding: 0 20px;
|
||||
margin: 10px 0;
|
||||
padding: 0 10px;
|
||||
margin: 10px 2px;
|
||||
flex: 1;
|
||||
overflow-x: auto;
|
||||
&:focus-visible {
|
||||
outline: @variables_focus_style;
|
||||
border-radius: @variables_radius;
|
||||
}
|
||||
.cp-app-contacts-message {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
& > div {
|
||||
padding: 0 10px;
|
||||
flex-direction: column;
|
||||
.cp-app-contacts-sender {
|
||||
margin-top: 10px;
|
||||
font-weight: bold;
|
||||
background-color: var(--msg-bg-color-dark);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-radius: @variables_radius;
|
||||
padding: 0 5px;
|
||||
}
|
||||
.cp-app-contacts-message-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.cp-app-contacts-content {
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
padding: 0 10px;
|
||||
&> * {
|
||||
margin: 0;
|
||||
}
|
||||
flex: 1;
|
||||
min-width: 70%;
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
}
|
||||
.cp-app-contacts-date {
|
||||
display: none;
|
||||
font-style: italic;
|
||||
}
|
||||
.cp-app-contacts-sender {
|
||||
margin-top: 10px;
|
||||
font-weight: bold;
|
||||
background-color: rgba(0,0,0,0.1);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
.cp-app-contacts-time {
|
||||
display: none;
|
||||
display: flex;
|
||||
font-size: 0.8em;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-end;
|
||||
color: @msg-color;
|
||||
font-weight: bold;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0,0,0,0.3);
|
||||
border-top-left-radius: 50%;
|
||||
border-bottom-left-radius: 50%;
|
||||
padding: 0 10px;
|
||||
padding-right: 5px;
|
||||
opacity: 0.4;
|
||||
transition: opacity 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
&:hover {
|
||||
.cp-app-contacts-time {
|
||||
display: flex;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-app-contacts-system-notification {
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
color: fade(@cryptpad_text_col, 80%);
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-app-contacts-input {
|
||||
@ -386,7 +492,7 @@
|
||||
justify-content: center;
|
||||
padding: 0 5%;
|
||||
textarea {
|
||||
margin: 5px 0;
|
||||
margin: 5px 2px 5px 0;
|
||||
padding: 5px 10px;
|
||||
border: none;
|
||||
height: 54px; // 2 lines (22px height) + 2 margins (5px)
|
||||
@ -411,6 +517,7 @@
|
||||
color: @cp_messenger-fg;
|
||||
background-color: @msg-bg-color-darker;
|
||||
background-color: var(--msg-bg-color-darker);
|
||||
border-radius: @variables_radius;
|
||||
&:hover {
|
||||
background-color: @msg-bg-color-dark;
|
||||
background-color: var(--msg-bg-color-dark);
|
||||
@ -425,4 +532,47 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cp-app-contacts-back {
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
&:hover {
|
||||
background-color: var(--msg-bg-color-dark);
|
||||
border-radius: @variables_radius;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: @browser_media-medium-screen) {
|
||||
#cp-app-contacts-container {
|
||||
#cp-app-contacts-friendlist {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
#cp-app-contacts-messaging {
|
||||
display: none;
|
||||
}
|
||||
&.cp-app-contacts-chat-open {
|
||||
#cp-app-contacts-friendlist {
|
||||
display: none;
|
||||
}
|
||||
#cp-app-contacts-messaging {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
&.cp-app-contacts-no-chats {
|
||||
#cp-app-contacts-friendlist {
|
||||
display: none;
|
||||
}
|
||||
#cp-app-contacts-messaging {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cp-app-contacts-back {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,6 +46,14 @@
|
||||
.cp-sidebarlayout-category {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
svg {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.cp-sidebarlayout-category-name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.leftside-menu-category_main();
|
||||
box-shadow: @cryptpad_ui_shadow;
|
||||
&:focus-visible {
|
||||
|
||||
17
customize.dist/translations/messages.zh_Hant.js
Normal file
17
customize.dist/translations/messages.zh_Hant.js
Normal file
@ -0,0 +1,17 @@
|
||||
// SPDX-FileCopyrightText: 2026 XWiki CryptPad Team <contact@cryptpad.org> and contributors
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
/*
|
||||
* You can override the translation text using this file.
|
||||
* The recommended method is to make a copy of this file (/customize.dist/translations/messages.{LANG}.js)
|
||||
in a 'customize' directory (/customize/translations/messages.{LANG}.js).
|
||||
* If you want to check all the existing translation keys, you can open the internal language file
|
||||
but you should not change it directly (/common/translations/messages.{LANG}.js)
|
||||
*/
|
||||
define(['/common/translations/messages.zh_Hant.js'], function (Messages) {
|
||||
// Replace the existing keys in your copied file here:
|
||||
// Messages.button_newpad = "New Rich Text Document";
|
||||
|
||||
return Messages;
|
||||
});
|
||||
@ -94,6 +94,7 @@ nThen(function (w) {
|
||||
// if one does not exist, then create one and remember it
|
||||
// 256 bits
|
||||
var bearerSecret = Util.encodeBase64(Nacl.randomBytes(32));
|
||||
Env.bearerSecret = bearerSecret;
|
||||
Env.Log.info("GENERATING_BEARER_SECRET", {});
|
||||
Decrees.write(Env, [
|
||||
'SET_BEARER_SECRET',
|
||||
|
||||
@ -7,9 +7,10 @@ const Invitation = module.exports;
|
||||
const Invite = require('../storage/invite');
|
||||
const Util = require("../common-util");
|
||||
const Users = require("./users");
|
||||
const Crypto = require('node:crypto');
|
||||
|
||||
const getUid = () => {
|
||||
return Util.uid() + Util.uid() + Util.uid();
|
||||
return Crypto.randomBytes(18).toString('hex');
|
||||
};
|
||||
|
||||
Invitation.getAll = (Env, cb) => {
|
||||
|
||||
@ -129,7 +129,7 @@ var handleCommand = function (Env, req, res) {
|
||||
COMMANDS[command](Env, body, function (err) {
|
||||
if (err) {
|
||||
Env.Log.error('CHALLENGE_COMMAND_EXECUTION_ERROR', {
|
||||
body: body,
|
||||
command,
|
||||
error: Util.serializeError(err),
|
||||
});
|
||||
// errors returned from commands are passed back to the client
|
||||
@ -229,16 +229,6 @@ var handleResponse = function (Env, req, res) {
|
||||
});
|
||||
}
|
||||
|
||||
// garbage collection can clean this up later
|
||||
Challenge.delete(Env, txid, function (err) {
|
||||
if (err) {
|
||||
Env.Log.error("CHALLENGE_DELETION_ERROR", {
|
||||
txid: txid,
|
||||
error: Util.serializeError(err),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var json = Util.tryParse(text);
|
||||
|
||||
if (!json) {
|
||||
@ -284,8 +274,7 @@ var handleResponse = function (Env, req, res) {
|
||||
u8_publicKey = Util.decodeBase64(publicKey);
|
||||
} catch (err3) {
|
||||
Env.Log.error('CHALLENGE_RESPONSE_DECODING_ERROR', {
|
||||
text: text,
|
||||
sig: sig,
|
||||
command: json.command,
|
||||
publicKey: publicKey,
|
||||
error: Util.serializeError(err3),
|
||||
});
|
||||
@ -305,6 +294,16 @@ var handleResponse = function (Env, req, res) {
|
||||
});
|
||||
}
|
||||
|
||||
// garbage collection can clean this up later
|
||||
Challenge.delete(Env, txid, function (err) {
|
||||
if (err) {
|
||||
Env.Log.error("CHALLENGE_DELETION_ERROR", {
|
||||
txid: txid,
|
||||
error: Util.serializeError(err),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// execute the command
|
||||
action(Env, json, function (err, content) {
|
||||
if (err) {
|
||||
|
||||
@ -258,7 +258,7 @@ app.use('/ssoauth', (req, res, next) => {
|
||||
Log.error('E_SSO_WRITE_REQ', err);
|
||||
return res.sendStatus(500);
|
||||
}
|
||||
let value = `samltoken="${token}"; SameSite=Strict; HttpOnly`;
|
||||
let value = `samltoken="${token}"; SameSite=Strict; HttpOnly; Path=/; Secure`;
|
||||
res.setHeader('Set-Cookie', value);
|
||||
next();
|
||||
});
|
||||
@ -750,22 +750,6 @@ var send500 = function (res, path) {
|
||||
});
|
||||
};
|
||||
|
||||
app.get('/api/updatequota', function (req, res) {
|
||||
if (!Env.accounts_api) {
|
||||
res.status(404);
|
||||
return void send404(res);
|
||||
}
|
||||
sendMessage({
|
||||
command: 'UPDATE_QUOTA',
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
res.status(500);
|
||||
return void send500(res);
|
||||
}
|
||||
res.send();
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/profiling', function (req, res) {
|
||||
if (!Env.enableProfiling) { return void send404(res); }
|
||||
sendMessage({
|
||||
|
||||
@ -89,3 +89,7 @@ Basic.restore = function (Env, archivePath, path, cb) {
|
||||
cb(err);
|
||||
});
|
||||
};
|
||||
|
||||
Basic.isValidId = id => {
|
||||
return id && typeof(id) === "string" && /^[a-zA-Z0-9-+=]+$/.test(id);
|
||||
};
|
||||
|
||||
@ -22,7 +22,9 @@ const Challenge = module.exports;
|
||||
*/
|
||||
|
||||
const pathFromId = function (Env, id) {
|
||||
if (!id || typeof(id) !== 'string') { return void console.error('CHALLENGE_BAD_ID', id); }
|
||||
if (!Basic.isValidId(id)) {
|
||||
return void console.error('CHALLENGE_BAD_ID', id);
|
||||
}
|
||||
return Path.join(Env.paths.base, "challenges", id.slice(0, 2), id);
|
||||
};
|
||||
|
||||
|
||||
@ -17,7 +17,9 @@ const Invite = module.exports;
|
||||
*/
|
||||
|
||||
const pathFromId = function (Env, id) {
|
||||
if (!id || typeof(id) !== 'string') { return void console.error('INVITE_BAD_ID', id); }
|
||||
if (!Basic.isValidId(id)) {
|
||||
return void console.error('INVITE_BAD_ID', id);
|
||||
}
|
||||
return Path.join(Env.paths.base, "invitations", id.slice(0, 2), id);
|
||||
};
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@ so that it can be accessed quickly.
|
||||
var pathFromId = function (Env, id) {
|
||||
if (!id || typeof(id) !== 'string') { return; }
|
||||
id = Util.escapeKeyCharacters(id);
|
||||
if (!Basic.isValidId(id)) { return; }
|
||||
return Path.join(Env.paths.base, "mfa", id.slice(0, 2), `${id}.json`);
|
||||
};
|
||||
|
||||
|
||||
@ -14,7 +14,9 @@ const Moderator = module.exports;
|
||||
*/
|
||||
|
||||
const pathFromId = function (Env, id) {
|
||||
if (!id || typeof(id) !== 'string') { return void console.error('KNWONUSER_BAD_ID', id); }
|
||||
if (!Basic.isValidId(id)) {
|
||||
return void console.error('MODERATOR_BAD_ID', id);
|
||||
}
|
||||
return Path.join(Env.paths.base, "support", id.slice(0, 2), id);
|
||||
};
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ const Sessions = module.exports;
|
||||
|
||||
var pathFromId = function (Env, id, ref) {
|
||||
if (!id || typeof(id) !== 'string') { return; }
|
||||
if (!Basic.isValidId(ref)) { return; }
|
||||
id = Util.escapeKeyCharacters(id);
|
||||
return Path.join(Env.paths.base, "sessions", id.slice(0, 2), id, ref);
|
||||
};
|
||||
|
||||
@ -20,6 +20,7 @@ The path for the user database is based on their persistent identifier (id) from
|
||||
var pathFromId = function (Env, id, subPath) {
|
||||
if (!id || typeof(id) !== 'string') { return; }
|
||||
id = Util.escapeKeyCharacters(id);
|
||||
if (!Basic.isValidId(id)) { return; }
|
||||
return Path.join(Env.paths.base, subPath, id.slice(0, 2), `${id}.json`);
|
||||
};
|
||||
var reqPathFromId = function (Env, id) {
|
||||
|
||||
@ -14,7 +14,9 @@ const User = module.exports;
|
||||
*/
|
||||
|
||||
const pathFromId = function (Env, id) {
|
||||
if (!id || typeof(id) !== 'string') { return void console.error('KNWONUSER_BAD_ID', id); }
|
||||
if (!Basic.isValidId(id)) {
|
||||
return void console.error('USER_BAD_ID', id);
|
||||
}
|
||||
return Path.join(Env.paths.base, "users", id.slice(0, 2), id);
|
||||
};
|
||||
|
||||
|
||||
24
package-lock.json
generated
24
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "cryptpad",
|
||||
"version": "2026.2.2",
|
||||
"version": "2026.5.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cryptpad",
|
||||
"version": "2026.2.2",
|
||||
"version": "2026.5.0",
|
||||
"license": "AGPL-3.0+",
|
||||
"dependencies": {
|
||||
"@mcrowe/minibloom": "^0.2.0",
|
||||
@ -19,14 +19,14 @@
|
||||
"chainpad-crypto": "^0.3.0",
|
||||
"chainpad-listmap": "^1.2.0",
|
||||
"chainpad-netflux": "^1.3.0",
|
||||
"chainpad-server": "^5.2.4",
|
||||
"chainpad-server": "^5.3.0",
|
||||
"ckeditor": "npm:ckeditor4@~4.22.1",
|
||||
"codemirror": "^5.19.0",
|
||||
"connect-gzip-static": "^4.2.1",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"croppie": "^2.5.0",
|
||||
"dragula": "3.7.2",
|
||||
"drawio": "github:cryptpad/drawio-npm#npm-21.8.2+6",
|
||||
"drawio": "github:cryptpad/drawio-npm#npm-29.6.7+3",
|
||||
"express": "~4.22.1",
|
||||
"file-saver": "1.3.1",
|
||||
"fs-extra": "^7.0.0",
|
||||
@ -1598,7 +1598,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/chainpad-server": {
|
||||
"version": "5.2.4"
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/chainpad-server/-/chainpad-server-5.3.1.tgz",
|
||||
"integrity": "sha512-yDDsN0Oa8ojtNNJRBpqy/AwnRWkettJfGxUxRGZmMf+bbbiKTPWum8ixP9l8UzErTlYR4wzaTZIKmNpWWcyOtg=="
|
||||
},
|
||||
"node_modules/ckeditor": {
|
||||
"name": "ckeditor4",
|
||||
@ -1900,8 +1902,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/drawio": {
|
||||
"version": "21.8.2+6",
|
||||
"resolved": "git+ssh://git@github.com/cryptpad/drawio-npm.git#61699f777690d23aab727461ff58b60e198ab0e4"
|
||||
"version": "29.6.7+3",
|
||||
"resolved": "git+ssh://git@github.com/cryptpad/drawio-npm.git#c3493636b4f3a65d9f068245a968887fd1332326"
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
@ -2373,7 +2375,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -3537,7 +3541,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/netflux-websocket": {
|
||||
"version": "1.3.0",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/netflux-websocket/-/netflux-websocket-1.3.1.tgz",
|
||||
"integrity": "sha512-vom3V+3oXf6dnT6UkCkpi76zZLGYNXTaWA0Azg/CsIfMHP66owYynkOFXoZ8N2gaQNoEWMT6msz9xjSOnAH0cw==",
|
||||
"license": "LGPL-2.1"
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cryptpad",
|
||||
"description": "a collaborative office suite that is end-to-end encrypted and open-source",
|
||||
"version": "2026.2.2",
|
||||
"version": "2026.5.0",
|
||||
"license": "AGPL-3.0+",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -22,14 +22,14 @@
|
||||
"chainpad-crypto": "^0.3.0",
|
||||
"chainpad-listmap": "^1.2.0",
|
||||
"chainpad-netflux": "^1.3.0",
|
||||
"chainpad-server": "^5.2.4",
|
||||
"chainpad-server": "^5.3.0",
|
||||
"ckeditor": "npm:ckeditor4@~4.22.1",
|
||||
"codemirror": "^5.19.0",
|
||||
"connect-gzip-static": "^4.2.1",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"croppie": "^2.5.0",
|
||||
"dragula": "3.7.2",
|
||||
"drawio": "github:cryptpad/drawio-npm#npm-21.8.2+6",
|
||||
"drawio": "github:cryptpad/drawio-npm#npm-29.6.7+3",
|
||||
"express": "~4.22.1",
|
||||
"file-saver": "1.3.1",
|
||||
"fs-extra": "^7.0.0",
|
||||
|
||||
@ -61,7 +61,6 @@ const factory = (Crypto, CPNetflux, Netflux, Util,
|
||||
nThen(function (waitFor) {
|
||||
Session.accessKeys.forEach(function (obj) {
|
||||
Pinpad.create(config.network, obj, waitFor(function (e) {
|
||||
console.log('done', obj);
|
||||
if (e) { console.error(e); }
|
||||
}));
|
||||
});
|
||||
|
||||
@ -209,12 +209,13 @@ const factory = (Util, ApiConfig = {}, ServerCommand, Nacl) => {
|
||||
};
|
||||
|
||||
Block.updateSSOBlock = function (data, cb) {
|
||||
const { blockKeys, oldBlockKeys } = data;
|
||||
const { blockKeys, oldBlockKeys, hasPassword } = data;
|
||||
var oldProof = oldBlockKeys && Block.proveAncestor(oldBlockKeys);
|
||||
|
||||
ServerCommand(blockKeys.sign, {
|
||||
command: 'SSO_UPDATE_BLOCK',
|
||||
ancestorProof: oldProof
|
||||
ancestorProof: oldProof,
|
||||
hasPassword
|
||||
}, cb);
|
||||
|
||||
};
|
||||
|
||||
@ -859,6 +859,22 @@ const factory = (Messaging, Hash, Util, Crypto, Block) => {
|
||||
delete sfDeleted[id];
|
||||
};
|
||||
|
||||
var msgNotif = {};
|
||||
handlers['SEND_CHAT_MESSAGE'] = function (ctx, box, data, cb) {
|
||||
|
||||
var msgSender = data.msg.author;
|
||||
|
||||
//Check if sender is in contacts and not muted
|
||||
if (msgNotif[msgSender] || isMuted(ctx, data) || !ctx.store.proxy.friends[msgSender]) { return void cb(true); }
|
||||
msgNotif[msgSender] = 1;
|
||||
|
||||
cb(false);
|
||||
};
|
||||
removeHandlers['SEND_CHAT_MESSAGE'] = function (ctx, box, data) {
|
||||
var msgSender = data.author;
|
||||
delete msgNotif[msgSender];
|
||||
};
|
||||
|
||||
// New support
|
||||
handlers['NEW_TICKET'] = function (ctx, box, data, cb) {
|
||||
var msg = data.msg;
|
||||
|
||||
@ -167,6 +167,7 @@ const _join: Callback = (ctx, clientId, data) => {
|
||||
}
|
||||
|
||||
const isNew = typeof channels[channelId] === "undefined";
|
||||
const isAnonymousSession = !store.loggedIn;
|
||||
|
||||
// Create or get existing channel object
|
||||
const channel = channels[channelId] ||= {
|
||||
@ -201,7 +202,7 @@ const _join: Callback = (ctx, clientId, data) => {
|
||||
}
|
||||
|
||||
// Existing pad already loaded: send userlist and history
|
||||
if (!isNew && channel.wc) {
|
||||
const sendExistingState = () => {
|
||||
postMessage(clientId, "PAD_CONNECT", { // Initialize
|
||||
myID: channel.wc.myID,
|
||||
id: channel.wc.id,
|
||||
@ -218,7 +219,29 @@ const _join: Callback = (ctx, clientId, data) => {
|
||||
});
|
||||
});
|
||||
postMessage(clientId, "PAD_READY"); // Ready
|
||||
return;
|
||||
};
|
||||
const preflightAnonAccess = (cb) => {
|
||||
const allow = () => { cb(true); };
|
||||
const deny = () => {
|
||||
Cache?.clearChannel?.(channelId);
|
||||
channel.bcast("PAD_ERROR", { type: "ERESTRICTED" });
|
||||
ctx.leavePad(null, data, function () {});
|
||||
cb(false);
|
||||
};
|
||||
|
||||
if (!isAnonymousSession) { return void allow(); }
|
||||
if (!store.anon_rpc) { return void allow(); }
|
||||
_getMetadata(ctx, clientId, { channel: channelId }, md => {
|
||||
if (md?.rejected) { return void deny(); }
|
||||
allow();
|
||||
});
|
||||
};
|
||||
|
||||
if (!isNew) {
|
||||
return void preflightAnonAccess((ok) => {
|
||||
if (!ok) { return; }
|
||||
sendExistingState();
|
||||
});
|
||||
}
|
||||
|
||||
// chainpad-netflux config
|
||||
@ -231,7 +254,7 @@ const _join: Callback = (ctx, clientId, data) => {
|
||||
}
|
||||
channel.bcast("PAD_ERROR", err);
|
||||
|
||||
if (type === "EDELETED" && Cache?.clearChannel) {
|
||||
if (["EDELETED", "EEXPIRED", "ERESTRICTED"].includes(type) && Cache?.clearChannel) {
|
||||
Cache.clearChannel(channelId);
|
||||
}
|
||||
|
||||
@ -343,7 +366,10 @@ const _join: Callback = (ctx, clientId, data) => {
|
||||
});
|
||||
}
|
||||
};
|
||||
channel.cpNf = CpNetflux.start(conf);
|
||||
preflightAnonAccess((ok) => {
|
||||
if (!ok) { return; }
|
||||
channel.cpNf = CpNetflux.start(conf);
|
||||
});
|
||||
};
|
||||
|
||||
// Send a message to a pad we already joined
|
||||
|
||||
@ -62,7 +62,7 @@ const factory = (SRpc, Channel, Util) => {
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query ' + q);
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
//console.log(data);
|
||||
}
|
||||
if (q === "DISCONNECT") {
|
||||
onClose();
|
||||
@ -99,7 +99,7 @@ const factory = (SRpc, Channel, Util) => {
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query JOIN_PAD');
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
//console.log(data);
|
||||
}
|
||||
});
|
||||
chan.on('SEND_PAD_MSG', function (msg, cb) {
|
||||
@ -112,7 +112,7 @@ const factory = (SRpc, Channel, Util) => {
|
||||
} catch (e) {
|
||||
console.error('Error in webworker when executing query SEND_PAD_MSG');
|
||||
console.error(e);
|
||||
console.log(data);
|
||||
//console.log(data);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ const factory = (Crypto, Hash, Util, Realtime, Messaging,
|
||||
|
||||
var Types = {
|
||||
message: 'MSG',
|
||||
cleared: 'CLEARED',
|
||||
unfriend: 'UNFRIEND',
|
||||
mapId: 'MAP_ID',
|
||||
mapIdAck: 'MAP_ID_ACK'
|
||||
@ -116,7 +117,6 @@ const factory = (Crypto, Hash, Util, Realtime, Messaging,
|
||||
|
||||
var getChannelMessagesSince = function (ctx, channel, data, keys) {
|
||||
var network = ctx.store.network;
|
||||
console.log('Fetching [%s] messages since [%s]', channel.id, data.lastKnownHash || '');
|
||||
|
||||
if (channel.isPadChat || channel.isTeamChat) {
|
||||
// We need to use GET_HISTORY_RANGE to make sure we won't get the full history
|
||||
@ -271,6 +271,11 @@ const factory = (Crypto, Hash, Util, Realtime, Messaging,
|
||||
return true;
|
||||
}
|
||||
var proxy = ctx.store.proxy;
|
||||
if (parsedMsg[0] === Types.cleared) {
|
||||
channel.messages = [];
|
||||
ctx.emit('CLEAR_CHANNEL', channel.id, channel.clients);
|
||||
return;
|
||||
}
|
||||
if (parsedMsg[0] === Types.unfriend) {
|
||||
curvePublic = parsedMsg[1];
|
||||
|
||||
@ -917,11 +922,21 @@ const factory = (Crypto, Hash, Util, Realtime, Messaging,
|
||||
var channel = ctx.channels[id];
|
||||
if (!channel) { return void cb({error: 'NO_CHANNEL'}); }
|
||||
if (!ctx.store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||
var proxy = ctx.store.proxy || {};
|
||||
ctx.store.rpc.clearOwnedChannel(id, function (err) {
|
||||
cb({error:err});
|
||||
if (!err) {
|
||||
channel.messages = [];
|
||||
ctx.emit('CLEAR_CHANNEL', id, channel.clients);
|
||||
var msg = [Types.cleared, proxy.curvePublic, +new Date()];
|
||||
var msgStr = JSON.stringify(msg);
|
||||
var cryptMsg = channel.encrypt(msgStr);
|
||||
|
||||
channel.wc.bcast(cryptMsg).then(function () {
|
||||
// Success (message sent)
|
||||
}, function (err) {
|
||||
console.error('Failed to send message:', err);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@ -170,7 +170,6 @@ const factory = (Util, Hash, Constants, Realtime,
|
||||
ctx.emit('UPDATE', ctx.listmap.proxy, ctx.clients);
|
||||
};
|
||||
profile.execCommand = function (clientId, obj, cb) {
|
||||
console.log(obj);
|
||||
var cmd = obj.cmd;
|
||||
var data = obj.data;
|
||||
if (cmd === 'SUBSCRIBE') {
|
||||
|
||||
@ -28,7 +28,7 @@ define([
|
||||
"grid": "layout-grid",
|
||||
"list": "list",
|
||||
"document-owner": "id-card-lanyard",
|
||||
"burn-drive": "ban",
|
||||
"burn-drive": "eraser",
|
||||
// Teams
|
||||
"teams": "users-round",
|
||||
"promote": "chevrons-up",
|
||||
@ -85,7 +85,7 @@ define([
|
||||
"share": "share-2",
|
||||
"download": "hard-drive-download",
|
||||
"destroy": "shredder",
|
||||
"donate": "hand-coins",
|
||||
"donate": "hand-heart",
|
||||
"send": "send",
|
||||
"cloud-upload": "cloud-upload",
|
||||
"print": "printer",
|
||||
@ -250,7 +250,10 @@ define([
|
||||
"badge-error": "circle-alert",
|
||||
// Other
|
||||
"maintenance": "construction",
|
||||
"release-notes": "notepad-text"
|
||||
"release-notes": "notepad-text",
|
||||
"crowdfunding-donate": "hand-heart",
|
||||
"crowdfunding-snooze": "timer",
|
||||
"crowdfunding-donate2": "ticket"
|
||||
};
|
||||
|
||||
Icons.add = (newIcons) => {
|
||||
|
||||
@ -213,7 +213,6 @@ define([
|
||||
opt.channelHex = secret.channel;
|
||||
}
|
||||
|
||||
console.warn(opt);
|
||||
return opt;
|
||||
};
|
||||
|
||||
|
||||
@ -3271,6 +3271,10 @@ define([
|
||||
}
|
||||
|
||||
if (toolbar && typeof toolbar.failed === "function") { toolbar.failed(true); }
|
||||
sframeChan.event('EV_SHARE_OPEN', {hidden: true});
|
||||
UI.errorLoadingScreen(msg, false, false);
|
||||
(cb || function () {})();
|
||||
return;
|
||||
} else if (err.type === 'HASH_NOT_FOUND' && priv.isHistoryVersion) {
|
||||
msg = Messages.oo_deletedVersion;
|
||||
if (toolbar && typeof toolbar.failed === "function") { toolbar.failed(true); }
|
||||
@ -3388,39 +3392,70 @@ define([
|
||||
UIElements.displayCrowdfunding = function (common, force) {
|
||||
if (crowdfundingState) { return; }
|
||||
var priv = common.getMetadataMgr().getPrivateData();
|
||||
if (priv.app === 'form' && !priv.canEdit && !priv.form_auditorKey) { return; }
|
||||
if (priv.app === 'drive') { return; }
|
||||
if (!priv.channel) { return; }
|
||||
if (priv.app === 'form' && priv.readOnly && !priv.form_auditorHash && !priv.form_auditorKey) { return; }
|
||||
|
||||
var todo = function () {
|
||||
crowdfundingState = true;
|
||||
// Display the popup
|
||||
var text = Messages.crowdfunding_popup_text;
|
||||
var yes = h('button.cp-corner-primary', [
|
||||
Icons.get('external-link'),
|
||||
'OpenCollective'
|
||||
]);
|
||||
var no = h('button.cp-corner-cancel', Messages.crowdfunding_popup_no);
|
||||
var actions = h('div', [no, yes]);
|
||||
|
||||
var recordShown = function () {
|
||||
common.getSframeChannel().query('Q_RECORD_CROWDFUNDING_SHOWN', {}, function () {});
|
||||
};
|
||||
var dontShowAgain = function () {
|
||||
common.setAttribute(['general', 'crowdfunding'], false);
|
||||
Feedback.send('CROWDFUNDING_NEVER');
|
||||
};
|
||||
|
||||
var modal = UI.cornerPopup(text, actions, '', {
|
||||
big: true,
|
||||
alt: true,
|
||||
dontShowAgain: dontShowAgain
|
||||
});
|
||||
|
||||
$(yes).click(function () {
|
||||
modal.delete();
|
||||
common.openURL(priv.accounts.donateURL);
|
||||
Feedback.send('CROWDFUNDING_YES');
|
||||
});
|
||||
$(no).click(function () {
|
||||
modal.delete();
|
||||
Feedback.send('CROWDFUNDING_NO');
|
||||
var content = Messages.crowdfunding_popup_text;
|
||||
var buttons = [{
|
||||
name: Messages.dontShowAgain,
|
||||
className: 'cancel left',
|
||||
iconClass: 'close',
|
||||
onClick: function () {
|
||||
recordShown();
|
||||
dontShowAgain();
|
||||
}
|
||||
}, {
|
||||
name: Messages.crowdfunding_popup_no,
|
||||
className: 'cancel',
|
||||
iconClass: 'crowdfunding-snooze',
|
||||
onClick: function () {
|
||||
recordShown();
|
||||
Feedback.send('CROWDFUNDING_NO');
|
||||
}
|
||||
}];
|
||||
if (!Config.removeDonateButton) {
|
||||
buttons.push({
|
||||
name: Messages.crowdfunding_button2,
|
||||
className: 'primary',
|
||||
iconClass: 'crowdfunding-donate',
|
||||
onClick: function () {
|
||||
recordShown();
|
||||
common.openURL(priv.accounts.donateURL);
|
||||
Feedback.send('CROWDFUNDING_YES');
|
||||
}
|
||||
});
|
||||
}
|
||||
if (Config.accounts_api && common.isLoggedIn()) {
|
||||
content += ' ' + Messages.crowdfunding_popup_text2;
|
||||
buttons.push({
|
||||
name: Messages.features_f_subscribe,
|
||||
className: 'primary',
|
||||
iconClass: 'crowdfunding-donate2',
|
||||
onClick: function () {
|
||||
recordShown();
|
||||
common.openURL('/accounts/');
|
||||
Feedback.send('CROWDFUNDING_SUBSCRIBE');
|
||||
}
|
||||
});
|
||||
}
|
||||
var modal = UI.dialog.customModal(content, {
|
||||
force: true,
|
||||
scrollable: true,
|
||||
buttons: buttons
|
||||
});
|
||||
$(modal).addClass('cp-crowdfunding-modal');
|
||||
UI.openCustomModal(modal, { wide: true });
|
||||
};
|
||||
|
||||
if (force) {
|
||||
@ -3430,13 +3465,16 @@ define([
|
||||
|
||||
if (AppConfig.disableCrowdfundingMessages) { return; }
|
||||
if (priv.plan) { return; }
|
||||
if (Config.removeDonateButton && !Config.accounts_api) { return; }
|
||||
|
||||
crowdfundingState = true;
|
||||
common.getAttribute(['general', 'crowdfunding'], function (err, val) {
|
||||
if (err || val === false) { return; }
|
||||
common.getSframeChannel().query('Q_GET_PINNED_USAGE', null, function (err, obj) {
|
||||
var quotaMb = obj.quota / (1024 * 1024);
|
||||
if (quotaMb < 10) { return; }
|
||||
if (err || val === false) { crowdfundingState = false; return; }
|
||||
common.getSframeChannel().query('Q_CROWDFUNDING_SHOULD_SHOW', null, function (err, result) {
|
||||
if (err || !result || !result.show) {
|
||||
crowdfundingState = false;
|
||||
return;
|
||||
}
|
||||
todo();
|
||||
});
|
||||
});
|
||||
@ -3457,7 +3495,7 @@ define([
|
||||
|
||||
// This pad will be deleted automatically, it shouldn't be stored
|
||||
if (priv.burnAfterReading) { return; }
|
||||
if (priv.app === 'form' && !priv.canEdit && !priv.form_auditorKey && !common.isLoggedIn()) { return; }
|
||||
if (priv.app === 'form' && priv.readOnly && !priv.form_auditorHash && !priv.form_auditorKey && !common.isLoggedIn()) { return; }
|
||||
var typeMsg = priv.pathname.indexOf('/file/') !== -1 ? Messages.autostore_file :
|
||||
priv.pathname.indexOf('/drive/') !== -1 ? Messages.autostore_sf :
|
||||
Messages.autostore_pad;
|
||||
|
||||
@ -2010,6 +2010,7 @@ define([
|
||||
var oldBlockKeys = oldAllocated.blockKeys;
|
||||
var blockKeys = newAllocated.blockKeys;
|
||||
var auth = data.auth;
|
||||
var hasPassword = Boolean(data.newPassword);
|
||||
|
||||
nThen(function (waitFor) {
|
||||
// Check if our drive is already owned
|
||||
@ -2135,6 +2136,7 @@ define([
|
||||
// Update "sso_block" data for SSO accounts
|
||||
Block.updateSSOBlock({
|
||||
blockKeys: blockKeys,
|
||||
hasPassword: hasPassword,
|
||||
oldBlockKeys: oldBlockKeys
|
||||
}, waitFor(function (err) {
|
||||
if (err) {
|
||||
|
||||
@ -369,8 +369,9 @@ define([
|
||||
|
||||
APP.premiumPlan = priv.plan;
|
||||
var getOpenIn = function (app) {
|
||||
var icon = AppConfig.applicationsIcon[app];
|
||||
var html = '<i data-lucide="'+icon+'"></i>' + Messages.type[app];
|
||||
var icon = AppConfig.applicationsIcon[app] || app;
|
||||
var iconHtml = Icons.get(icon).outerHTML;
|
||||
var html = iconHtml + Messages.type[app];
|
||||
return Messages._getKey('fc_openIn', [html]);
|
||||
};
|
||||
var restricted = {};
|
||||
|
||||
@ -370,15 +370,17 @@ define([
|
||||
Object.keys(categories).forEach(function (key, i) {
|
||||
if (!active && !i) { active = key; }
|
||||
var category = categories[key];
|
||||
var name = category.name || Messages[`${app}_cat_${key}`] || key;
|
||||
var icon;
|
||||
if (category.icon) { icon = Icons.get(category.icon); }
|
||||
var item = h('li.cp-sidebarlayout-category', {
|
||||
'role': 'menuitem',
|
||||
'tabindex': 0,
|
||||
'data-category': key
|
||||
'data-category': key,
|
||||
'aria-label': name
|
||||
}, [
|
||||
icon,
|
||||
category.name || Messages[`${app}_cat_${key}`] || key,
|
||||
h('span.cp-sidebarlayout-category-name', name),
|
||||
]);
|
||||
var $item = $(item).appendTo(container);
|
||||
Util.onClickEnter($item, function () {
|
||||
|
||||
@ -120,6 +120,7 @@ var factory = function (Util) {
|
||||
var iframe = document.createElement('iframe');
|
||||
if (cfg.pdf.viewer) { // PDFJS
|
||||
var viewerUrl = cfg.pdf.viewer + '?file=' + url;
|
||||
iframe.setAttribute('sandbox', 'allow-scripts allow-downloads allow-same-origin allow-modals');
|
||||
iframe.src = viewerUrl + '#' + window.encodeURIComponent(metadata.name);
|
||||
iframe.onload = function () {
|
||||
if (!metadata.name) { return; }
|
||||
@ -574,7 +575,11 @@ var factory = function (Util) {
|
||||
var copyAttributes = function (origin, dest) {
|
||||
Object.keys(origin.attributes).forEach(function (i) {
|
||||
if (!/^data-attr/.test(origin.attributes[i].name)) { return; }
|
||||
var name = origin.attributes[i].name.slice(10);
|
||||
var name = origin.attributes[i].name.slice(10).toLowerCase();
|
||||
// Ignore attributes filtered out by the sanitizer
|
||||
if (name === "src") { return; }
|
||||
if (name === "srcdoc") { return; }
|
||||
if (/^on/i.test(name)) { return; }
|
||||
var value = origin.attributes[i].value;
|
||||
dest.setAttribute(name, value);
|
||||
});
|
||||
|
||||
@ -8,11 +8,14 @@ define([
|
||||
'/common/common-util.js',
|
||||
'/common/common-interface.js',
|
||||
'/common/common-ui-elements.js',
|
||||
'/common/visible.js',
|
||||
'/common/notify.js',
|
||||
'/common/inner/badges.js',
|
||||
'/common/hyperscript.js',
|
||||
'/common/diffMarked.js',
|
||||
'/common/common-icons.js',
|
||||
], function ($, Messages, Util, UI, UIElements, Badges, h, DiffMd, Icons) {
|
||||
'/customize/pages.js',
|
||||
], function ($, Messages, Util, UI, UIElements, Visible, Notification, Badges, h, DiffMd, Icons, Pages) {
|
||||
'use strict';
|
||||
|
||||
var debug = console.log;
|
||||
@ -64,7 +67,6 @@ define([
|
||||
h('h2', Messages.contacts_info1),
|
||||
h('ul', [
|
||||
h('li', Messages.contacts_info2),
|
||||
h('li', Messages.contacts_info3),
|
||||
h('li', Messages.contacts_info4),
|
||||
])
|
||||
])
|
||||
@ -76,11 +78,11 @@ define([
|
||||
h('div.cp-app-contacts-category-content')
|
||||
]),
|
||||
h('div.cp-app-contacts-friends.cp-app-contacts-category', [
|
||||
h('div.cp-app-contacts-category-content.cp-contacts-friends'),
|
||||
h('button.btn.btn-default.cp-app-contacts-muted-button', {tabindex:0},[
|
||||
Icons.get('mute'),
|
||||
Messages.contacts_manageMuted
|
||||
]),
|
||||
h('div.cp-app-contacts-category-content.cp-contacts-friends')
|
||||
])
|
||||
]),
|
||||
h('div.cp-app-contacts-rooms.cp-app-contacts-category', [
|
||||
h('div.cp-app-contacts-category-content'),
|
||||
@ -150,13 +152,14 @@ define([
|
||||
var channels = Object.keys(state.channels).sort(function (a, b) {
|
||||
var m1 = state.channels[a].messages.slice(-1)[0];
|
||||
var m2 = state.channels[b].messages.slice(-1)[0];
|
||||
if (!m2) { return !m1 ? 0 : 1; }
|
||||
if (!m1) { return -1; }
|
||||
return m1.time - m2.time;
|
||||
if (!m2) { return !m1 ? 0 : -1; }
|
||||
if (!m1) { return 1; }
|
||||
return m2.time - m1.time;
|
||||
});
|
||||
|
||||
channels.forEach(function (c, i) {
|
||||
$userlist.find(dataQuery(c)).css('order', i);
|
||||
channels.forEach(function (c) {
|
||||
var $el = $userlist.find(dataQuery(c));
|
||||
$el.appendTo($el.parent());
|
||||
});
|
||||
|
||||
// Make sure the width is correct even if there is a scrollbar
|
||||
@ -184,7 +187,8 @@ define([
|
||||
});
|
||||
|
||||
var time = h('div.cp-app-contacts-time', hour);
|
||||
$d.append(time);
|
||||
var row = h('div.cp-app-contacts-message-row', [d, time]);
|
||||
return row;
|
||||
} catch (e) {
|
||||
console.error(md);
|
||||
console.error(e);
|
||||
@ -239,19 +243,45 @@ define([
|
||||
};
|
||||
|
||||
var clearChannel = function (id) {
|
||||
$(getChat(id)).find('.cp-app-contacts-messages').html('');
|
||||
var $chat = $(getChat(id));
|
||||
if (state.channels && state.channels[id]) {
|
||||
state.channels[id].messages = [];
|
||||
}
|
||||
if ($chat.length) {
|
||||
$chat.find('.cp-app-contacts-messages').html('');
|
||||
$chat.find('.cp-app-contacts-message').remove();
|
||||
}
|
||||
};
|
||||
|
||||
var displaySystemMessage = function (id, message, icon) {
|
||||
var $messagebox = $(getChat(id)).find('.cp-app-contacts-messages');
|
||||
if (!$messagebox.length) { return; }
|
||||
var content = icon ? [Icons.get(icon), h('span', message)] : message;
|
||||
$messagebox.append(h('div.cp-app-contacts-message.cp-app-contacts-system-notification', content));
|
||||
normalizeLabels($messagebox);
|
||||
scrollChatToBottom();
|
||||
};
|
||||
var userInfo;
|
||||
markup.chatbox = function (id, data, curvePublic) {
|
||||
var moreHistory = h('span', {
|
||||
class: 'cp-app-contacts-more-history',
|
||||
tabindex: '0',
|
||||
role: 'button',
|
||||
'aria-label': Messages.contacts_fetchHistory,
|
||||
title: Messages.contacts_fetchHistory
|
||||
});
|
||||
moreHistory.append(Icons.get('history', {title: Messages.contacts_fetchHistory}));
|
||||
moreHistory.append(Icons.get('history'));
|
||||
|
||||
var chan = state.channels[id];
|
||||
var displayName = UI.getDisplayName(chan.name || chan.displayName);
|
||||
|
||||
var fetching = false;
|
||||
var $moreHistory = $(moreHistory).click(function () {
|
||||
var $moreHistory = $(moreHistory).on('keydown', function (e) {
|
||||
if (e.which === 13 || e.which === 32) {
|
||||
e.preventDefault();
|
||||
$(this).click();
|
||||
}
|
||||
}).click(function () {
|
||||
if (fetching) { return; }
|
||||
|
||||
// get oldest known message...
|
||||
@ -308,11 +338,20 @@ define([
|
||||
});
|
||||
|
||||
var removeHistory = h('span', {
|
||||
'class': 'cp-app-contacts-remove-history'
|
||||
'class': 'cp-app-contacts-remove-history',
|
||||
'tabindex': '0',
|
||||
'role': 'button',
|
||||
'aria-label': Messages.contacts_removeHistoryTitle,
|
||||
'title': Messages.contacts_removeHistoryTitle
|
||||
});
|
||||
removeHistory.append(Icons.get('remove-history', {title: Messages.contacts_removeHistoryTitle}));
|
||||
removeHistory.append(Icons.get('remove-history'));
|
||||
|
||||
$(removeHistory).click(function () {
|
||||
$(removeHistory).on('keydown', function (e) {
|
||||
if (e.which === 13 || e.which === 32) {
|
||||
e.preventDefault();
|
||||
$(this).click();
|
||||
}
|
||||
}).click(function () {
|
||||
UI.confirm(Messages.contacts_confirmRemoveHistory, function (yes) {
|
||||
if (!yes) { return; }
|
||||
|
||||
@ -322,6 +361,7 @@ define([
|
||||
UI.alert(Messages.contacts_removeHistoryServerError);
|
||||
return;
|
||||
}
|
||||
clearChannel(id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -329,13 +369,23 @@ define([
|
||||
var avatar = h('div.cp-avatar');
|
||||
var avatarDiv = h('div.cp-avatar-container', avatar);
|
||||
|
||||
var backButton = h('span.cp-app-contacts-back', {
|
||||
'aria-label': Messages.form_backButton,
|
||||
title: Messages.form_backButton,
|
||||
}, Icons.get('arrow-left'));
|
||||
$(backButton).click(function () {
|
||||
$container.removeClass('cp-app-contacts-chat-open');
|
||||
});
|
||||
|
||||
var headerContent = [
|
||||
backButton,
|
||||
avatarDiv,
|
||||
moreHistory,
|
||||
data.isFriendChat ? removeHistory : undefined
|
||||
];
|
||||
if (isApp) {
|
||||
headerContent = [
|
||||
backButton,
|
||||
h('div.cp-app-contacts-header-title', Messages.contacts_padTitle),
|
||||
moreHistory
|
||||
];
|
||||
@ -363,6 +413,7 @@ define([
|
||||
});
|
||||
var sendButton = h('button.btn.btn-primary', {
|
||||
title: Messages.contacts_send,
|
||||
'aria-label': Messages.contacts_send
|
||||
}, Icons.get('send'));
|
||||
|
||||
var rightCol = h('span.cp-app-contacts-right-col', [
|
||||
@ -396,6 +447,28 @@ define([
|
||||
// failed to send
|
||||
return void console.error('failed to send', e);
|
||||
}
|
||||
|
||||
//Send mailbox message if:
|
||||
//- recipient is a contact
|
||||
//- recipient is offline
|
||||
//- no previous messages to recipient sent while current tab is open
|
||||
var messageSent = {};
|
||||
execCommand('GET_STATUS', id, function (e, online) {
|
||||
if (online) {
|
||||
delete messageSent[id];
|
||||
} else {
|
||||
if (friend && !messageSent[id]) {
|
||||
common.mailbox.sendTo("SEND_CHAT_MESSAGE", {
|
||||
name: userInfo.displayName,
|
||||
}, {
|
||||
channel: contactsData[chan.curvePublic].notifications,
|
||||
curvePublic: chan.curvePublic
|
||||
});
|
||||
messageSent[id] = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
input.value = '';
|
||||
sending = false;
|
||||
debug('sent successfully');
|
||||
@ -456,6 +529,32 @@ define([
|
||||
$messages.find('.cp-app-contacts-info').show();
|
||||
};
|
||||
|
||||
var updateInfoMessage = function () {
|
||||
var chats = Object.keys(state.channels).length;
|
||||
var $info = $messages.find('.cp-app-contacts-info');
|
||||
if (chats === 0) {
|
||||
$info.html([
|
||||
h('h2', Messages.contacts_noFriends),
|
||||
h('ul', [
|
||||
h('li', [
|
||||
UI.createHelper(Pages.localizeDocsLink(' https://docs.cryptpad.org/en/user_guide/collaboration.html#contacts'), Messages.contacts_noFriendsInfo),
|
||||
Messages.contacts_noFriendsInfo
|
||||
]),
|
||||
])
|
||||
]);
|
||||
$container.addClass('cp-app-contacts-no-chats');
|
||||
} else {
|
||||
$info.html([
|
||||
h('h2', Messages.contacts_info1),
|
||||
h('ul', [
|
||||
h('li', Messages.contacts_info2),
|
||||
h('li', Messages.contacts_info4),
|
||||
])
|
||||
]);
|
||||
$container.removeClass('cp-app-contacts-no-chats');
|
||||
}
|
||||
};
|
||||
|
||||
var updateStatus = function (id) {
|
||||
if (!state.channels[id]) { return; }
|
||||
var $status = find.inList(id).find('.cp-app-contacts-status');
|
||||
@ -491,6 +590,7 @@ define([
|
||||
|
||||
setActive(chanId);
|
||||
unnotify(chanId);
|
||||
$container.addClass('cp-app-contacts-chat-open');
|
||||
var $chat = getChat(chanId);
|
||||
hideInfo();
|
||||
$messages.find('div.cp-app-contacts-chat[data-key]').hide();
|
||||
@ -534,6 +634,7 @@ define([
|
||||
'tabindex': '0',
|
||||
'data-key': id,
|
||||
'data-user': room.isFriendChat ? userlist[0].curvePublic : '',
|
||||
'aria-label': room.isFriendChat ? UI.getDisplayName(room.name) : room.name
|
||||
});
|
||||
|
||||
|
||||
@ -543,85 +644,146 @@ define([
|
||||
curve = __channel.curvePublic;
|
||||
}
|
||||
|
||||
var unmute = h('span', Icons.get('notification'), {
|
||||
class: 'cp-app-contacts-remove cp-unmute-icon',
|
||||
title: Messages.contacts_unmute || 'unmute',
|
||||
style: (curve && mutedUsers[curve]) ? undefined : 'display: none;'
|
||||
var isMuted = curve && mutedUsers[curve];
|
||||
|
||||
var friendData = room.isFriendChat ? userlist[0] : {};
|
||||
|
||||
var dropdownOptions = [];
|
||||
|
||||
var removeOption = {
|
||||
tag: 'a',
|
||||
content: [Icons.get('unfriend'), h('span', Messages.contacts_remove)],
|
||||
action: function () {
|
||||
var channel = state.channels[id];
|
||||
if (!channel.isFriendChat) {
|
||||
UI.warn(Messages.error);
|
||||
return;
|
||||
}
|
||||
var curvePublic = channel.curvePublic;
|
||||
var friend = contactsData[curvePublic] || friendData;
|
||||
var name = Util.fixHTML(UI.getDisplayName(friend.name || friend.displayName));
|
||||
var content = h('div', [
|
||||
UI.setHTML(h('p'), Messages._getKey('contacts_confirmRemove', [ name ])),
|
||||
]);
|
||||
UI.confirm(content, function (yes) {
|
||||
if (!yes) { return; }
|
||||
removeFriend(curvePublic);
|
||||
// TODO remove friend from userlist ui
|
||||
// FIXME seems to trigger EJOINED from netflux-websocket (from server);
|
||||
// (tried to join a channel in which you were already present)
|
||||
});
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var viewProfileOption = {
|
||||
tag: 'a',
|
||||
content: [Icons.get('user-profile'), h('span', Messages.userlist_visitProfile)],
|
||||
action: function () {
|
||||
if (friendData.profile) { window.open(origin + '/profile/#' + friendData.profile); }
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
let $dropdown, muteOption, unmuteOption;
|
||||
var rebuildDropdown = function () {
|
||||
if (!$dropdown || !$dropdown.setOptions) { return; }
|
||||
var opts = [];
|
||||
if (room.isFriendChat) {
|
||||
var isCurrentlyMuted = curve && mutedUsers[curve];
|
||||
opts.push(isCurrentlyMuted ? unmuteOption : muteOption);
|
||||
if (friendData.profile) {
|
||||
opts.push(viewProfileOption);
|
||||
}
|
||||
opts.push(removeOption);
|
||||
}
|
||||
$dropdown.setOptions(opts);
|
||||
$dropdown.find('.cp-dropdown-content').hide();
|
||||
};
|
||||
|
||||
muteOption = {
|
||||
tag: 'a',
|
||||
content: [Icons.get('mute'), h('span', Messages.contacts_mute)],
|
||||
action: function () {
|
||||
var channel = state.channels[id];
|
||||
if (!channel.isFriendChat) { return true; }
|
||||
var curvePublic = channel.curvePublic;
|
||||
var friend = contactsData[curvePublic] || friendData;
|
||||
muteUser(friend);
|
||||
rebuildDropdown();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
unmuteOption = {
|
||||
tag: 'a',
|
||||
content: [Icons.get('notification'), h('span', Messages.contacts_unmute || 'Unmute')],
|
||||
action: function () {
|
||||
var channel = state.channels[id];
|
||||
if (!channel.isFriendChat) { return true; }
|
||||
var curvePublic = channel.curvePublic;
|
||||
unmuteUser(curvePublic);
|
||||
rebuildDropdown();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
if (room.isFriendChat) {
|
||||
dropdownOptions.push(isMuted ? unmuteOption : muteOption);
|
||||
if (friendData.profile) {
|
||||
dropdownOptions.push(viewProfileOption);
|
||||
}
|
||||
dropdownOptions.push(removeOption);
|
||||
}
|
||||
|
||||
$dropdown = UIElements.createDropdown({
|
||||
iconCls: 'settings',
|
||||
options: dropdownOptions,
|
||||
buttonCls: 'cp-app-contacts-dropdown-btn',
|
||||
buttonTitle: Messages.settingsButton
|
||||
});
|
||||
var mute = h('span', Icons.get('mute'), {
|
||||
class: 'cp-app-contacts-remove cp-mute-icon',
|
||||
title: Messages.contacts_mute || 'mute',
|
||||
style: (curve && mutedUsers[curve]) ? 'display: none;' : undefined
|
||||
$dropdown.addClass('cp-app-contacts-icons');
|
||||
$dropdown.on('click dblclick', function (e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
var remove = h('span', Icons.get('unfriend', {
|
||||
class: 'cp-app-contacts-remove',
|
||||
title: Messages.contacts_remove
|
||||
}));
|
||||
var leaveRoom = h('span', Icons.get('logout', {
|
||||
class: 'cp-app-contacts-remove',
|
||||
title: Messages.contacts_leaveRoom
|
||||
}));
|
||||
|
||||
var $dropdownMenu = $dropdown.find('.cp-dropdown-content');
|
||||
$dropdownMenu.css('position', 'fixed');
|
||||
$dropdown.find('button').on('click', function () {
|
||||
rebuildDropdown();
|
||||
var rect = this.getBoundingClientRect();
|
||||
var menuWidth = $dropdownMenu.outerWidth() || 150;
|
||||
var viewportWidth = window.innerWidth;
|
||||
var left = rect.left;
|
||||
if (left + menuWidth > viewportWidth) {
|
||||
left = rect.right - menuWidth;
|
||||
}
|
||||
if (left < 0) { left = 0; }
|
||||
$dropdownMenu.css({
|
||||
top: rect.bottom + 'px',
|
||||
left: left + 'px',
|
||||
});
|
||||
});
|
||||
|
||||
var status = h('span.cp-app-contacts-status', {
|
||||
title: Messages.contacts_online
|
||||
});
|
||||
var mute = h('span.cp-app-contacts-mute-indicator', {
|
||||
title: Messages.contacts_muted,
|
||||
style: isMuted ? '' : 'display: none;'
|
||||
}, Icons.get('mute'));
|
||||
var bottomRow = h('span.cp-app-contacts-bottom-row', [
|
||||
mute,
|
||||
$dropdown[0],
|
||||
]);
|
||||
var rightCol = h('span.cp-app-contacts-right-col', [
|
||||
h('span.cp-app-contacts-name', [room.isFriendChat? UI.getDisplayName(room.name): room.name]),
|
||||
h('span.cp-app-contacts-icons', [
|
||||
room.isFriendChat ? mute : undefined,
|
||||
room.isFriendChat ? unmute : undefined,
|
||||
room.isFriendChat ? remove :
|
||||
(room.isPadChat || room.isTeamChat) ? undefined : leaveRoom,
|
||||
])
|
||||
bottomRow,
|
||||
]);
|
||||
|
||||
var friendData = room.isFriendChat ? userlist[0] : {};
|
||||
|
||||
var $room = $(roomEl).on('click keypress', function (event) {
|
||||
if (event.type === 'click' || (event.type === 'keypress' && event.which === 13)) {
|
||||
display(id);
|
||||
}
|
||||
}).dblclick(function () {
|
||||
if (friendData.profile) { window.open(origin + '/profile/#' + friendData.profile); }
|
||||
});
|
||||
|
||||
$(unmute).on('click dblclick', function (e) {
|
||||
e.stopPropagation();
|
||||
var channel = state.channels[id];
|
||||
if (!channel.isFriendChat) { return; }
|
||||
var curvePublic = channel.curvePublic;
|
||||
$(mute).show();
|
||||
$(unmute).hide();
|
||||
unmuteUser(curvePublic);
|
||||
});
|
||||
|
||||
$(mute).on('click dblclick', function (e) {
|
||||
e.stopPropagation();
|
||||
var channel = state.channels[id];
|
||||
if (!channel.isFriendChat) { return; }
|
||||
var curvePublic = channel.curvePublic;
|
||||
var friend = contactsData[curvePublic] || friendData;
|
||||
$(mute).hide();
|
||||
$(unmute).show();
|
||||
muteUser(friend);
|
||||
});
|
||||
|
||||
$(remove).click(function (e) {
|
||||
e.stopPropagation();
|
||||
var channel = state.channels[id];
|
||||
if (!channel.isFriendChat) { return; }
|
||||
var curvePublic = channel.curvePublic;
|
||||
var friend = contactsData[curvePublic] || friendData;
|
||||
var name = Util.fixHTML(UI.getDisplayName(friend.name || friend.displayName));
|
||||
var content = h('div', [
|
||||
UI.setHTML(h('p'), Messages._getKey('contacts_confirmRemove', [ name ])),
|
||||
]);
|
||||
UI.confirm(content, function (yes) {
|
||||
if (!yes) { return; }
|
||||
removeFriend(curvePublic);
|
||||
// TODO remove friend from userlist ui
|
||||
// FIXME seems to trigger EJOINED from netflux-websocket (from server);
|
||||
// (tried to join a channel in which you were already present)
|
||||
});
|
||||
});
|
||||
|
||||
const $avatar = $(h('div.cp-avatar')).appendTo($room);
|
||||
@ -668,6 +830,11 @@ define([
|
||||
}
|
||||
notifyToolbar();
|
||||
|
||||
if (!Visible.currently()) {
|
||||
common.notify();
|
||||
Notification.create();
|
||||
}
|
||||
|
||||
channel.messages.push(message);
|
||||
|
||||
var $chat = $(chat);
|
||||
@ -678,6 +845,8 @@ define([
|
||||
var $messagebox = $chat.find('.cp-app-contacts-messages');
|
||||
var shouldScroll = isBottomedOut($messagebox);
|
||||
|
||||
$messagebox.find('.cp-app-contacts-system-notification').remove();
|
||||
|
||||
$messagebox.append(el_message);
|
||||
|
||||
if (shouldScroll) {
|
||||
@ -801,7 +970,6 @@ define([
|
||||
$messagebox.append(el_message);
|
||||
});
|
||||
normalizeLabels($messagebox);
|
||||
|
||||
var roomEl = markup.room(id, room, list);
|
||||
|
||||
var $parentEl;
|
||||
@ -835,6 +1003,7 @@ define([
|
||||
if (err) { return void console.error(err); }
|
||||
debug('rooms: ' + JSON.stringify(rooms));
|
||||
rooms.forEach(initializeRoom);
|
||||
updateInfoMessage();
|
||||
});
|
||||
};
|
||||
|
||||
@ -848,6 +1017,7 @@ define([
|
||||
if (channel && channel.curvePublic === curvePublic) {
|
||||
showInfo();
|
||||
}
|
||||
updateInfoMessage();
|
||||
if (!removedByMe) {
|
||||
// TODO UI.alert if this is triggered by the other guy
|
||||
}
|
||||
@ -895,6 +1065,8 @@ define([
|
||||
.find('.cp-mute-icon').show();
|
||||
$('.cp-app-contacts-friend[data-user]')
|
||||
.find('.cp-unmute-icon').hide();
|
||||
$('.cp-app-contacts-friend[data-user]')
|
||||
.find('.cp-app-contacts-mute-indicator').hide();
|
||||
if (!muted || Object.keys(muted).length === 0) {
|
||||
$button.hide();
|
||||
return;
|
||||
@ -905,6 +1077,8 @@ define([
|
||||
.find('.cp-mute-icon').hide();
|
||||
$('.cp-app-contacts-friend[data-user="'+curve+'"]')
|
||||
.find('.cp-unmute-icon').show();
|
||||
$('.cp-app-contacts-friend[data-user="'+curve+'"]')
|
||||
.find('.cp-app-contacts-mute-indicator').show();
|
||||
var data = muted[curve];
|
||||
var avatar = h('span.cp-avatar');
|
||||
var button = h('button.btn', {
|
||||
@ -915,15 +1089,15 @@ define([
|
||||
]);
|
||||
common.displayAvatar($(avatar), data.avatar, data.name, Util.noop, data.uid, data.badge);
|
||||
$(button).click(function () {
|
||||
unmuteUser(curve, button);
|
||||
execCommand('UNMUTE_USER', curve, function (e, data) {
|
||||
if (e) { return void console.error(e); }
|
||||
unmuteUser(curve, function () {
|
||||
$(button).closest('div').remove();
|
||||
if (!data) { $button.hide(); }
|
||||
$('.cp-app-contacts-friend[data-user="'+curve+'"]')
|
||||
.find('.cp-unmute-icon').hide();
|
||||
$('.cp-app-contacts-friend[data-user="'+curve+'"]')
|
||||
.find('.cp-mute-icon').show();
|
||||
$('.cp-app-contacts-friend[data-user="'+curve+'"]')
|
||||
.find('.cp-app-contacts-mute-indicator').hide();
|
||||
if ($('.cp-contacts-muted-table').find('.cp-contacts-muted-user').length === 0) {
|
||||
UI.findOKButton().click();
|
||||
}
|
||||
@ -957,6 +1131,7 @@ define([
|
||||
|
||||
debug('rooms: ' + JSON.stringify(rooms));
|
||||
rooms.forEach(initializeRoom);
|
||||
updateInfoMessage();
|
||||
});
|
||||
|
||||
updateMutedList();
|
||||
@ -1015,6 +1190,7 @@ define([
|
||||
}
|
||||
if (cmd === 'CLEAR_CHANNEL') {
|
||||
clearChannel(data);
|
||||
displaySystemMessage(data, Messages.contacts_historyCleared, 'clear-canvas');
|
||||
return;
|
||||
}
|
||||
if (cmd === 'PADCHAT_READY') {
|
||||
@ -1072,10 +1248,10 @@ define([
|
||||
});
|
||||
};
|
||||
//});
|
||||
|
||||
execCommand('GET_MY_INFO', null, function (e, info) {
|
||||
if (e) { return; }
|
||||
contactsData[info.curvePublic] = info;
|
||||
userInfo = info;
|
||||
});
|
||||
|
||||
|
||||
|
||||
@ -148,6 +148,25 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
// Send chat message
|
||||
handlers['SEND_CHAT_MESSAGE'] = function(common, data) {
|
||||
var content = data.content;
|
||||
var msg = content.msg;
|
||||
var key = 'sent_chatMessage';
|
||||
|
||||
var name = Util.fixHTML(msg.content.name) || Messages.anonymous;
|
||||
content.getFormatText = function() {
|
||||
return Messages._getKey(key, [name]);
|
||||
};
|
||||
content.handler = function() {
|
||||
common.openURL('/contacts/');
|
||||
defaultDismiss(common, data)();
|
||||
};
|
||||
if (!content.archived) {
|
||||
content.dismissHandler = defaultDismiss(common, data);
|
||||
}
|
||||
};
|
||||
|
||||
// New support message from the admins
|
||||
handlers['SUPPORT_MESSAGE'] = function(common, data) {
|
||||
var content = data.content;
|
||||
|
||||
@ -2813,14 +2813,21 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
|
||||
});
|
||||
};
|
||||
|
||||
var importFile = function(content) {
|
||||
// Abort if there is another real user in the channel (history keeper excluded)
|
||||
// Abort if there is another real user in the channel (history keeper excluded)
|
||||
var checkChannelUsers = function () {
|
||||
var m = metadataMgr.getChannelMembers().slice().filter(function (nId) {
|
||||
return nId.length === 32;
|
||||
});
|
||||
if (m.length > 1) {
|
||||
UI.removeModals();
|
||||
return void UI.alert(Messages.oo_cantUpload);
|
||||
UI.alert(Messages.oo_cantUpload);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var importFile = function(content) {
|
||||
if (checkChannelUsers()) {
|
||||
return;
|
||||
}
|
||||
if (!content) {
|
||||
UI.removeModals();
|
||||
@ -2862,6 +2869,10 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
|
||||
if (!supportsXLSX()) {
|
||||
return void UI.alert(Messages.oo_invalidFormat);
|
||||
}
|
||||
if (checkChannelUsers()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var div = h('div.cp-oo-x2tXls', [
|
||||
Icons.get('loading'),
|
||||
h('span', Messages.oo_importInProgress)
|
||||
@ -3460,6 +3471,7 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
|
||||
var $forgetButton = common.createButton('forget', true, {}, function (err) {
|
||||
if (err) { return; }
|
||||
setEditable(false);
|
||||
toolbar.forgotten();
|
||||
});
|
||||
var $forget = UIElements.getEntryFromButton($forgetButton);
|
||||
toolbar.$drawer.append($forget);
|
||||
@ -3796,8 +3808,12 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
|
||||
const integrationHasUnsavedChanges = function(unsavedChanges, cb) {
|
||||
integrationChannel.query('Q_INTEGRATION_HAS_UNSAVED_CHANGES', unsavedChanges, cb);
|
||||
};
|
||||
const onUserlistChange = (list) => {
|
||||
integrationChannel.event('Q_INTEGRATION_USERLIST_CHANGE', list);
|
||||
};
|
||||
var inte = common.createIntegration(integrationSave,
|
||||
integrationHasUnsavedChanges);
|
||||
integrationHasUnsavedChanges,
|
||||
onUserlistChange);
|
||||
if (inte && cfg.autosave) {
|
||||
evIntegrationSave.reg(function () {
|
||||
inte.changed();
|
||||
|
||||
@ -151,8 +151,8 @@ define([
|
||||
sessionStorage.clear();
|
||||
try {
|
||||
Object.keys(localStorage || {}).forEach(function (k) {
|
||||
// Remvoe everything in localStorage except CACHE and FS_hash
|
||||
if (/^CRYPTPAD_CACHE/.test(k) || /^LESS_CACHE/.test(k) || k === Constants.fileHashKey || /^CRYPTPAD_STORE|colortheme/.test(k)) { return; }
|
||||
// Remove everything in localStorage except CACHE and FS_hash
|
||||
if (/^CRYPTPAD_CACHE/.test(k) || /^LESS_CACHE/.test(k) || k === Constants.fileHashKey || /^CRYPTPAD_STORE|colortheme/.test(k) || (!isDeletion && /^cp_crowdfunding_/.test(k))) { return; }
|
||||
delete localStorage[k];
|
||||
});
|
||||
} catch (e) { console.error(e); }
|
||||
|
||||
@ -103,7 +103,7 @@ define([
|
||||
|
||||
const sendMyID = () => {
|
||||
const user = Util.clone(privateData?.integrationConfig?.
|
||||
_?.editorConfig?.user);
|
||||
_?.editorConfig?.user) || {};
|
||||
user.readOnly = isView;
|
||||
execCommand?.('SEND', {
|
||||
msg: 'MYID',
|
||||
|
||||
@ -1028,6 +1028,110 @@ define([
|
||||
cb({error:e});
|
||||
});
|
||||
});
|
||||
var CROWDFUNDING_PREFIX = 'cp_crowdfunding_';
|
||||
var CROWDFUNDING_DRIVE_KEY = ['general', 'crowdfunding_metrics'];
|
||||
// First action (opening or creating a document) count threshold before showing the banner
|
||||
var CROWDFUNDING_MIN_ACTIONS = 5;
|
||||
// Additional actions required after each shown banner
|
||||
var CROWDFUNDING_ACTIONS_INTERVAL = 10;
|
||||
// Quota usage threshold for quota-based banner display
|
||||
var CROWDFUNDING_MIN_QUOTA_MB = 50;
|
||||
// Cooldown between banner displays based on last shown timestamp in milliseconds
|
||||
var CROWDFUNDING_COOLDOWN_MS = 24 * 60 * 60 * 1000;
|
||||
var crowdfundingGetLS = function () {
|
||||
var get = function (suffix) {
|
||||
var k = CROWDFUNDING_PREFIX + suffix;
|
||||
var val = localStorage.getItem(k);
|
||||
if (val !== null && val !== '') { return Number(val) || null; }
|
||||
return null;
|
||||
};
|
||||
return {
|
||||
visitCount: get('visitCount') || 0,
|
||||
firstSeen: get('firstSeen') || null,
|
||||
lastShownAtCount: get('lastShownAtCount') || 0,
|
||||
lastShownAtTime: get('lastShownAtTime') || 0
|
||||
};
|
||||
};
|
||||
// Read metrics: encrypted drive for logged-in users, localStorage for guests
|
||||
var crowdfundingReadMetrics = function (cb) {
|
||||
if (!Utils.LocalStore.isLoggedIn()) { return cb(crowdfundingGetLS()); }
|
||||
Cryptpad.getAttribute(CROWDFUNDING_DRIVE_KEY, function (e, metrics) {
|
||||
if (e || !metrics || typeof metrics !== 'object') {
|
||||
return cb({
|
||||
visitCount: 0,
|
||||
firstSeen: null,
|
||||
lastShownAtCount: 0,
|
||||
lastShownAtTime: 0
|
||||
});
|
||||
}
|
||||
cb(metrics);
|
||||
});
|
||||
};
|
||||
// Write metrics: encrypted drive for logged-in users, localStorage for guests
|
||||
var crowdfundingWriteMetrics = function (metrics, cb) {
|
||||
if (!Utils.LocalStore.isLoggedIn()) {
|
||||
try {
|
||||
['visitCount', 'firstSeen', 'lastShownAtCount', 'lastShownAtTime'].forEach(function (k) {
|
||||
if (metrics[k] !== null && metrics[k] !== undefined) {
|
||||
localStorage.setItem(CROWDFUNDING_PREFIX + k, String(metrics[k]));
|
||||
}
|
||||
});
|
||||
} catch (e) {}
|
||||
return cb && cb();
|
||||
}
|
||||
Cryptpad.setAttribute(CROWDFUNDING_DRIVE_KEY, metrics, function () {
|
||||
cb && cb();
|
||||
});
|
||||
};
|
||||
var crowdfundingIncrementAction = function (cb) {
|
||||
crowdfundingReadMetrics(function (metrics) {
|
||||
metrics.visitCount = (metrics.visitCount || 0) + 1;
|
||||
if (!metrics.firstSeen) { metrics.firstSeen = Date.now(); }
|
||||
crowdfundingWriteMetrics(metrics, cb);
|
||||
});
|
||||
};
|
||||
|
||||
sframeChan.on('Q_CROWDFUNDING_SHOULD_SHOW', function (data, cb) {
|
||||
crowdfundingReadMetrics(function (metrics) {
|
||||
var actionCount = metrics.visitCount || 0;
|
||||
var lastShownAtCount = metrics.lastShownAtCount || 0;
|
||||
var lastShownAtTime = metrics.lastShownAtTime || 0;
|
||||
var now = Date.now();
|
||||
var nextThreshold = lastShownAtCount === 0 ? CROWDFUNDING_MIN_ACTIONS : lastShownAtCount + CROWDFUNDING_ACTIONS_INTERVAL;
|
||||
var enoughTimePassed = lastShownAtTime === 0 || (now - lastShownAtTime >= CROWDFUNDING_COOLDOWN_MS);
|
||||
var showFromActions = actionCount >= nextThreshold && enoughTimePassed;
|
||||
if (showFromActions) {
|
||||
return cb({
|
||||
show: true,
|
||||
actionCount: actionCount
|
||||
});
|
||||
}
|
||||
if (CROWDFUNDING_MIN_QUOTA_MB <= 0) {
|
||||
return cb({
|
||||
show: false,
|
||||
actionCount: actionCount
|
||||
});
|
||||
}
|
||||
Cryptpad.getPinnedUsage({}, function (e, used) {
|
||||
var usedMb = (typeof used === 'number') ? (used / (1024 * 1024)) : 0;
|
||||
cb({
|
||||
show: !e && usedMb >= CROWDFUNDING_MIN_QUOTA_MB && enoughTimePassed,
|
||||
actionCount: actionCount
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
sframeChan.on('Q_RECORD_CROWDFUNDING_SHOWN', function (data, cb) {
|
||||
crowdfundingReadMetrics(function (metrics) {
|
||||
metrics.lastShownAtCount = (data && typeof data.count === 'number') ? data.count : (metrics.visitCount || 0);
|
||||
metrics.lastShownAtTime = Date.now();
|
||||
crowdfundingWriteMetrics(metrics, cb);
|
||||
});
|
||||
});
|
||||
sframeChan.on('Q_CROWDFUNDING_INCREMENT_OPEN', function (data, cb) {
|
||||
if (readOnly) { return cb && cb(); }
|
||||
crowdfundingIncrementAction(cb);
|
||||
});
|
||||
|
||||
Cryptpad.mailbox.onEvent.reg(function (data, cb) {
|
||||
sframeChan.query('EV_MAILBOX_EVENT', data, function (err, obj) {
|
||||
|
||||
@ -768,7 +768,7 @@ define([
|
||||
// Ctrl+E: New pad modal
|
||||
var priv = ctx.metadataMgr.getPrivateData();
|
||||
if (e.which === 69 && isApp) {
|
||||
if (priv.app === 'form' && !priv.canEdit && !priv.form_auditorKey) { return; }
|
||||
if (priv.app === 'form' && priv.readOnly && !priv.form_auditorHash && !priv.form_auditorKey) { return; }
|
||||
e.preventDefault();
|
||||
return void funcs.createNewPadModal();
|
||||
}
|
||||
@ -1063,10 +1063,16 @@ define([
|
||||
document.title = title;
|
||||
});
|
||||
|
||||
funcs.isPadStored(function (err, val) {
|
||||
if (err || !val) { return; }
|
||||
var showCrowdfunding = function () {
|
||||
UIElements.displayCrowdfunding(funcs);
|
||||
});
|
||||
};
|
||||
var privateData = ctx.metadataMgr.getPrivateData();
|
||||
var isDriveContext = privateData.app === 'drive';
|
||||
var isReadOnlyFormResponse = privateData.app === 'form' && privateData.readOnly && !privateData.form_auditorHash && !privateData.form_auditorKey;
|
||||
var skipCrowdfunding = privateData.secureIframe === true || privateData.unsafeIframe === true || isReadOnlyFormResponse || isDriveContext;
|
||||
if (!skipCrowdfunding) {
|
||||
ctx.sframeChan.query('Q_CROWDFUNDING_INCREMENT_OPEN', {}, showCrowdfunding);
|
||||
}
|
||||
|
||||
ctx.sframeChan.ready();
|
||||
|
||||
|
||||
@ -509,12 +509,15 @@ MessengerUI, Messages, Pages, PadTypes, Icons) {
|
||||
};
|
||||
|
||||
var createCollapse = function (toolbar) {
|
||||
var icon = Icons.get('chevron-up', {title: Messages.toolbar_collapse});
|
||||
var icon = Icons.get('chevron-up');
|
||||
var notif = h('span.cp-collapsed-notif');
|
||||
|
||||
var $button = $(h('button.cp-toolbar-collapse',[
|
||||
icon,
|
||||
notif
|
||||
var $button = $(h('button.cp-toolbar-collapse', {
|
||||
'aria-label': Messages.toolbar_collapse,
|
||||
'title': Messages.toolbar_collapse
|
||||
}, [
|
||||
icon,
|
||||
notif
|
||||
]));
|
||||
toolbar.$bottomR.prepend($button);
|
||||
$(notif).hide();
|
||||
@ -530,10 +533,14 @@ MessengerUI, Messages, Pages, PadTypes, Icons) {
|
||||
$button.toggleClass('cp-toolbar-button-active');
|
||||
|
||||
const newIcon = hidden ?
|
||||
Icons.get('chevron-down', {title: Messages.toolbar_expand}):
|
||||
Icons.get('chevron-up', {title: Messages.toolbar_collapse});
|
||||
Icons.get('chevron-down'):
|
||||
Icons.get('chevron-up');
|
||||
|
||||
$button.find('[data-lucide]').replaceWith(newIcon);
|
||||
$button.attr({
|
||||
'aria-label': hidden ? Messages.toolbar_expand : Messages.toolbar_collapse,
|
||||
'title': hidden ? Messages.toolbar_expand : Messages.toolbar_collapse
|
||||
});
|
||||
|
||||
if (!hidden) { $(notif).hide(); }
|
||||
|
||||
|
||||
@ -227,7 +227,7 @@
|
||||
"contacts_info3": "Auf den Avatar doppelklicken, um das entsprechende Profil anzuzeigen",
|
||||
"contacts_info4": "Jeder Teilnehmer kann den Chatverlauf endgültig löschen",
|
||||
"contacts_removeHistoryTitle": "Den Chatverlauf löschen",
|
||||
"contacts_confirmRemoveHistory": "Bist du sicher, dass du den Chatverlauf endgültig löschen willst? Die Daten sind dann weg",
|
||||
"contacts_confirmRemoveHistory": "Bist du sicher, dass du den Chatverlauf löschen willst? Die Nachrichten werden für alle gelöscht und können nicht wiederhergestellt werden.",
|
||||
"contacts_removeHistoryServerError": "Es gab einen Fehler bei dem Löschen des Chatverlaufs. Versuche es später noch einmal",
|
||||
"contacts_fetchHistory": "Den früheren Verlauf laden",
|
||||
"contacts_rooms": "Chaträume",
|
||||
@ -590,7 +590,7 @@
|
||||
"autostore_forceSave": "Speichere die Datei in deinem CryptDrive",
|
||||
"autostore_notAvailable": "Du musst dieses Dokument in deinem CryptDrive speichern, bevor du diese Funktion benutzen kannst.",
|
||||
"crowdfunding_button": "Unterstütze CryptPad",
|
||||
"crowdfunding_popup_text": "<h3>Wir brauchen deine Hilfe!</h3>Um sicherzustellen, dass CryptPad weiter aktiv entwickelt wird, unterstütze bitte das Projekt über die OpenCollective Seite, wo du unsere <b>Roadmap</b> und <b>Funding-Ziele</b> lesen kannst.",
|
||||
"crowdfunding_popup_text": "<h3>Wir brauchen deine Hilfe!</h3>Um sicherzustellen, dass CryptPad weiter aktiv entwickelt wird, unterstütze bitte das Projekt mit einer Spende auf OpenCollective.",
|
||||
"crowdfunding_popup_no": "Nicht jetzt",
|
||||
"invalidHashError": "Das angeforderte Dokument hat eine ungültige URL.",
|
||||
"oo_cantUpload": "Das Hochladen von Dateien ist nicht erlaubt, während andere Nutzer anwesend sind.",
|
||||
@ -1868,5 +1868,18 @@
|
||||
"support_moreTickets": "{0} Tickets verfügbar",
|
||||
"oo_rtChannelMissingNoSupport": "Beim Laden deines Dokuments ist ein Fehler aufgetreten. Es ist nun schreibgeschützt, um Beschädigung oder Verlust der Daten zu vermeiden. Bitte sende die folgenden Informationen an den Administrator deiner Instanz.",
|
||||
"oo_rtChannelMissing": "Beim Laden deines Dokuments ist ein Fehler aufgetreten. Es ist nun schreibgeschützt, um Beschädigung oder Verlust der Daten zu vermeiden. Bitte verwende die folgende Schaltfläche, um die entsprechenden Informationen an das Support-Team zu senden.",
|
||||
"oo_rtChannelMissingDate": "Nachricht gesendet am {0}"
|
||||
"oo_rtChannelMissingDate": "Nachricht gesendet am {0}",
|
||||
"contacts_noFriends": "Deine Kontaktliste ist leer",
|
||||
"contacts_historyCleared": "Der Chatverlauf wurde für alle gelöscht",
|
||||
"support_moderatorNotification": "{0} hat dich als Moderator zum Support-Team hinzugefügt",
|
||||
"crowdfunding_popup_text2": "Alternativ kannst du auf dieser Instanz ein Abonnement abschließen.",
|
||||
"form_input_ph_text": "Deine Antwort hier",
|
||||
"form_input_ph_number": "Gib eine Zahl ein",
|
||||
"form_date_time": "Wähle Datum und Uhrzeit aus",
|
||||
"contacts_muted": "Stummgeschaltet",
|
||||
"contacts_noFriendsInfo": "<a>Füge einen Kontakt hinzu</a>, um eine Unterhaltung zu beginnen.",
|
||||
"diagram_modesOptionLabel": "Zum Theme {0} wechseln, lädt die Anwendung neu",
|
||||
"diagram_sketchTheme": "Skizze",
|
||||
"diagram_simpleTheme": "Einfach",
|
||||
"diagram_classicTheme": "Klassisch"
|
||||
}
|
||||
|
||||
@ -231,7 +231,7 @@
|
||||
"contacts_info3": "Double-cliquer sur son nom pour voir son profil",
|
||||
"contacts_info4": "Chaque participant·e peut nettoyer définitivement l'historique d'une discussion",
|
||||
"contacts_removeHistoryTitle": "Supprimer l'historique du chat",
|
||||
"contacts_confirmRemoveHistory": "Êtes-vous sûr de vouloir supprimer définitivement l'historique de votre chat ? Les messages ne pourront pas être restaurés",
|
||||
"contacts_confirmRemoveHistory": "Êtes-vous sûr de vouloir supprimer définitivement l'historique de votre chat ? Les messages seront supprimés pour tout le monde et ne pourront être restaurés.",
|
||||
"contacts_removeHistoryServerError": "Une erreur est survenue lors de la supprimer de l'historique du chat. Veuillez réessayer plus tard",
|
||||
"contacts_fetchHistory": "Récupérer l'historique plus ancien",
|
||||
"contacts_rooms": "Salons",
|
||||
@ -353,7 +353,7 @@
|
||||
"settings_restore": "Restaurer",
|
||||
"settings_backupHint2": "Téléchargez tous les documents dans votre drive. Les documents seront téléchargés dans un format lisible par d'autres applications si un tel format est disponible. Lorsqu'un tel format n'est pas disponible, les documents seront téléchargés dans un format lisible par CryptPad.",
|
||||
"settings_backup2": "Télécharger mon CryptDrive",
|
||||
"settings_backup2Confirm": "Vous allez télécharger tous les documents de votre CryptDrive. Si vous souhaitez continuer, choisissez un nom et appuyez sur OK.",
|
||||
"settings_backup2Confirm": "Vous allez télécharger tous les documents et fichiers de votre CryptDrive. Si vous souhaitez continuer, choisissez un nom et appuyez sur OK",
|
||||
"settings_exportTitle": "Téléchargement de votre CryptDrive",
|
||||
"settings_exportDescription": "Veuillez patienter pendant que nous téléchargeons et déchiffrons vos documents. Cette opération peut prendre plusieurs minutes. Fermer l'onglet du navigateur interrompra le processus.",
|
||||
"settings_exportFailed": "Si un pad nécessite plus d'une minute pour être traité, il ne sera pas inclus dans l'archive. Une liste des documents n'ayant pas été exportés sera disponible à la fin.",
|
||||
@ -558,8 +558,8 @@
|
||||
"password_submit": "Valider",
|
||||
"properties_addPassword": "Ajouter un mot de passe",
|
||||
"properties_changePassword": "Modifier le mot de passe",
|
||||
"properties_confirmNew": "Êtes-vous sûr·e ? Ajouter un mot de passe changera l'URL de ce document et supprimera son historique. Les utilisateur·ices ne connaissant pas le nouveau mot de passe perdront l'accès au document.",
|
||||
"properties_confirmChange": "Êtes-vous sûr·e ? Changer le mot de passe supprimera l'historique de ce document. Les utilisateur·ices ne connaissant pas le nouveau mot de passe perdront l'accès au document.",
|
||||
"properties_confirmNew": "Êtes-vous sûr·e ? Ajouter un mot de passe changera l'URL de ce document et supprimera son historique. Les utilisateur·ices ne connaissant pas le mot de passe perdront l'accès à ce document",
|
||||
"properties_confirmChange": "Êtes-vous sûr·e ? Changer le mot de passe supprimera l'historique. Les utilisateur·ices ne connaissant pas le nouveau mot de passe perdront l'accès à ce document",
|
||||
"properties_passwordSame": "Le nouveau mot de passe doit être différent de celui existant.",
|
||||
"properties_passwordError": "Une erreur est survenue lors de la modification du mot de passe. Veuillez réessayer.",
|
||||
"properties_passwordWarning": "Le mot de passe a été modifié avec succès mais nous n'avons pas réussi à mettre à jour votre CryptDrive avec les nouvelles informations. Vous devrez peut-être supprimer manuellement l'ancienne version de ce pad.<br>Appuyez sur OK pour recharger le document et mettre à jour vos droits d'accès.",
|
||||
@ -595,7 +595,7 @@
|
||||
"autostore_notAvailable": "Vous devez stocker ce document dans votre CryptDrive avant de pouvoir utiliser cette fonctionnalité.",
|
||||
"crowdfunding_button": "Soutenir CryptPad",
|
||||
"crowdfunding_button2": "Faire un don",
|
||||
"crowdfunding_popup_text": "<h3>Aider CryptPad</h3>Pour vous assurer que CryptPad soit activement développé, nous vous invitons à supporter le projet via la page OpenCollective, où vous pouvez trouver notre <b>Roadmap</b> et nos <b>objectifs de financement</b>.",
|
||||
"crowdfunding_popup_text": "<h3>Nous avons besoin de votre aide !</h3>Pour vous assurer que CryptPad soit activement développé, nous vous invitons à supporter le projet via la page OpenCollective.",
|
||||
"crowdfunding_popup_no": "Pas maintenant",
|
||||
"survey": "Enquête CryptPad",
|
||||
"markdown_toc": "Sommaire",
|
||||
@ -1868,5 +1868,18 @@
|
||||
"loadAll": "Charger tous les tickets",
|
||||
"oo_rtChannelMissing": "Une erreur s'est produite lors du chargement de votre document. Celui-ci est désormais en lecture seule afin d'éviter toute corruption ou perte de données. Veuillez utiliser le bouton ci-dessous pour envoyer les informations nécessaires à l'équipe de support.",
|
||||
"oo_rtChannelMissingDate": "Message envoyé le {0}",
|
||||
"oo_rtChannelMissingNoSupport": "Une erreur s'est produite lors du chargement de votre document. Celui-ci est désormais en lecture seule afin d'éviter toute corruption ou perte de données. Veuillez transmettre les informations suivantes à l'administrateur·ice de votre instance."
|
||||
"oo_rtChannelMissingNoSupport": "Une erreur s'est produite lors du chargement de votre document. Celui-ci est désormais en lecture seule afin d'éviter toute corruption ou perte de données. Veuillez transmettre les informations suivantes à l'administrateur·ice de votre instance.",
|
||||
"support_moderatorNotification": "{0} vous a ajouté à l'équipe de support en tant que modérateur",
|
||||
"crowdfunding_popup_text2": "Vous pouvez également vous abonner sur cette instance.",
|
||||
"form_input_ph_text": "Votre réponse ici",
|
||||
"form_input_ph_number": "Entrez un nombre",
|
||||
"form_date_time": "Sélectionner la date et l'heure",
|
||||
"contacts_muted": "Masqué",
|
||||
"contacts_noFriends": "Votre liste de contacts est vide",
|
||||
"contacts_noFriendsInfo": "Commencez par <a>ajouter un contact</a> pour débuter une conversation.",
|
||||
"contacts_historyCleared": "L'historique de conversation a été supprimé pour tout le monde",
|
||||
"diagram_modesOptionLabel": "Changer le thème pour {0}, réactualise l'application",
|
||||
"diagram_sketchTheme": "Croquis",
|
||||
"diagram_simpleTheme": "Simple",
|
||||
"diagram_classicTheme": "Classique"
|
||||
}
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
{
|
||||
"synchronizing": "Szinkronizálás",
|
||||
"reconnecting": "Újracsatlakozás",
|
||||
"main_title": "CryptPad: titkosított, együttműködő, valós idejű szerkesztés",
|
||||
"main_title": "CryptPad: zéró-tudás, együttműködő valós idejű szerkesztés",
|
||||
"type": {
|
||||
"pad": "Formázott szöveg",
|
||||
"code": "Kód",
|
||||
"poll": "Szavazás",
|
||||
"kanban": "Kanban",
|
||||
"slide": "Markdown prezentáció",
|
||||
"slide": "Markdown diák",
|
||||
"drive": "CryptDrive",
|
||||
"whiteboard": "Rajztábla",
|
||||
"whiteboard": "Fehér tábla",
|
||||
"file": "Fájl",
|
||||
"media": "Média",
|
||||
"todo": "Teendők",
|
||||
@ -19,16 +19,16 @@
|
||||
"form": "Űrlap",
|
||||
"doc": "Dokumentum",
|
||||
"presentation": "Prezentáció",
|
||||
"diagram": "Diagram"
|
||||
"diagram": "Diagramm"
|
||||
},
|
||||
"common_connectionLost": "<b>Megszakadt a kapcsolat a serverrel</b><br>Amíg a kapcsolat helyreáll csak olvasási mód érhető el.",
|
||||
"onLogout": "Kiléptél, {0}kattints ide{1} a belépéshez<br>vagy nyomj egy Esc-et a csak olvasható módban megnyitáshoz.",
|
||||
"padNotPinnedVariable": "Ez a dokumentum {4} nap inaktivitás után lejár, {0}lépj be{1} vagy {2}regisztrálj{3} ahhoz, hogy megőrizd.",
|
||||
"anonymousStoreDisabled": "A CryptPad-példány rendszergazdája letiltotta a vendégek számára a tárolást. Jelentkezz be a CryptDrive használatához.",
|
||||
"expiredError": "Ez a dokumentum elérte a megsemmisítési időhatárt, és már nem érhető el.",
|
||||
"inactiveError": "Ez a dokumentum tétlenség miatt törlésre került. Nyomd meg az Esc gombot egy új dokumentum létrehozásához.",
|
||||
"invalidHashError": "A kért dokumentum webcíme hibás.",
|
||||
"errorCopy": " Az aktuális változatot még használhatod csak olvasható módban az Esc megnyomásával.",
|
||||
"onLogout": "Kijelentkezett, bejelentkezéshez {0}kattintson ide{1}<br>vagy nyomjon Esc-et a dokumentum olvasási módban való megnyitásához.",
|
||||
"padNotPinnedVariable": "Ez a dokumentum {4} nap inaktivitás után lejár, a megtartásához {0}lépjen be{1} vagy {2}regisztráljon{3}.",
|
||||
"anonymousStoreDisabled": "Ezen CryptPad példány rendszergazdája letiltotta a tárhelyet vendégek számára. Jelentkezzen be, hogy hozzáférjen a saját CryptDrive-jához.",
|
||||
"expiredError": "Ez a dokumentum elérte a megsemmisítési időpontot, és már nem elérhető.",
|
||||
"inactiveError": "Ez a dokumentum tétlenség miatt törlésre került. Új dokumentum létrehozásához nyomjon Esc-et.",
|
||||
"invalidHashError": "A kért dokumentum webcíme érvénytelen.",
|
||||
"errorCopy": " Az Esc billentyű lenyomásával olvasási módban még mindig használhatja a jelenlegi verziót.",
|
||||
"errorRedirectToHome": "Nyomd meg az Esc-et hogy visszakerülj a CryptDrive-odba.",
|
||||
"loading": "Betöltés…",
|
||||
"error": "Hiba",
|
||||
@ -60,9 +60,9 @@
|
||||
"pinLimitDrive": "Elérted a tárolási korlátot.<br>Nem készíthetsz több dokumentumot.",
|
||||
"importButton": "Importálás",
|
||||
"typeError": "Ez a dokumentum nem kompatibilis a kiválasztott alkalmazással",
|
||||
"padNotPinned": "Ez a dokumentum 3 hónap inaktivitás után lejár, {0}lépj be{1} vagy {2}regisztrálj{3} ahhoz, hogy megőrizd.",
|
||||
"deletedError": "Ez a dokumentum törlésre került, és már nem érhető el.",
|
||||
"chainpadError": "A frissítés közben kritikus hiba történt. A lap csak olvasható módba került hogy ne veszítsd el a munkádat.<br>nyomd meg az Esc-et a dokumentum további megtekintéséhez, vagy töltsd újra hogy megpróbáld szerkeszteni.",
|
||||
"padNotPinned": "Ez a dokumentum 3 hónap inaktivitás után lejár, a megőrzéséhez {0}lépjen be{1} vagy {2}regisztráljon{3}.",
|
||||
"deletedError": "Ez a dokumentum törlésre került, és már nem elérhető.",
|
||||
"chainpadError": "Kritikus hiba lépett fel a tartalom frissítése során. Az oldal olvasási módra váltott, hogy ne veszítse el munkáját.<br>Nyomjon Esc-et a dokumentum megtekintéséhez, vagy frissítse az oldalt a szerkesztés újrapróbálásához.",
|
||||
"newVersionError": "A CryptPad új verziója érhető el.<br><a href='#'>Töltsd újra</a> ezt a lapot a használatához vagy nyomj Esc-et hogy <b>offline módban</b> érd el a dokumentumot.",
|
||||
"disconnected": "Nincs kapcsolat",
|
||||
"typing": "Szerkesztés",
|
||||
@ -360,7 +360,7 @@
|
||||
"fc_color": "Szín megváltoztatása",
|
||||
"fc_open": "Megnyitás",
|
||||
"fc_open_ro": "Megnyitás (csak olvasás)",
|
||||
"fc_expandAll": "Minden kinyitása",
|
||||
"fc_expandAll": "Összes kinyitása",
|
||||
"fc_delete": "Kukába helyezés",
|
||||
"fc_delete_owned": "Megsemmisítés",
|
||||
"fc_restore": "Visszaállítás",
|
||||
@ -427,7 +427,7 @@
|
||||
"fm_noResult": "Keresés eredménytelen",
|
||||
"settings_cat_style": "Megjelenés",
|
||||
"logoutButton": "Kijelentkezés",
|
||||
"login_noSuchUser": "Hibás felhasználónév vagy jelszó",
|
||||
"login_noSuchUser": "Érvénytelen felhasználónév vagy jelszó",
|
||||
"login_invalUser": "Felhasználónév szükséges",
|
||||
"login_unhandledError": "Váratlan hiba történt :(",
|
||||
"register_passwordsDontMatch": "A jelszavak nem egyeznek!",
|
||||
@ -450,5 +450,11 @@
|
||||
"support_cat_all": "Összes",
|
||||
"support_addAttachment": "Csatolmány hozzáadása",
|
||||
"oo_refresh": "Frissítés",
|
||||
"support_formCategoryError": "Hiba: üres kategória"
|
||||
"support_formCategoryError": "Hiba: üres kategória",
|
||||
"login_register": "Regisztráció",
|
||||
"login_confirm": "Jelszó megerősítése",
|
||||
"login_hashing": "Jelszó hashelése, ez eltarthat egy ideig.",
|
||||
"login_invalPass": "Jelszó szükséges",
|
||||
"register_importRecent": "Dokumentumok importálása a vendég munkamenetből",
|
||||
"import_note": "A bejelentkezés nélkül létrehozott és/vagy tárolt dokumentumok megőrzéséhez pipálja be a \"Dokumentumok importálása a vendég munkamenetből\" opciót."
|
||||
}
|
||||
|
||||
@ -233,7 +233,7 @@
|
||||
"contacts_info3": "Double-click their icon to view their profile",
|
||||
"contacts_info4": "Either participant can clear permanently a chat history",
|
||||
"contacts_removeHistoryTitle": "Clean the chat history",
|
||||
"contacts_confirmRemoveHistory": "Are you sure you want to permanently remove your chat history? Data cannot be restored",
|
||||
"contacts_confirmRemoveHistory": "Are you sure you want to delete chat history? Messages will be removed for everyone and cannot be restored.",
|
||||
"contacts_removeHistoryServerError": "There was an error while removing your chat history. Try again later",
|
||||
"contacts_fetchHistory": "Retrieve older history",
|
||||
"contacts_rooms": "Rooms",
|
||||
@ -615,7 +615,7 @@
|
||||
"autostore_notAvailable": "You must store this document in your CryptDrive before being able to use this feature.",
|
||||
"crowdfunding_button": "Support CryptPad",
|
||||
"crowdfunding_button2": "Donate",
|
||||
"crowdfunding_popup_text": "<h3>We need your help!</h3>To ensure that CryptPad is actively developed, consider supporting the project via the OpenCollective page, where you can see our <b>Roadmap</b> and <b>Funding goals</b>.",
|
||||
"crowdfunding_popup_text": "<h3>We need your help!</h3>To ensure that CryptPad is actively developed, consider supporting the project with a donation on OpenCollective.",
|
||||
"crowdfunding_popup_no": "Not now",
|
||||
"survey": "CryptPad survey",
|
||||
"markdown_toc": "Contents",
|
||||
@ -1868,5 +1868,18 @@
|
||||
"loadAll": "Load all tickets",
|
||||
"oo_rtChannelMissing": "An error occured while loading your document. It is now read-only to prevent data being corrupted or lost. Please use the button below to send the relevant information to the support team.",
|
||||
"oo_rtChannelMissingDate": "Message sent on {0}",
|
||||
"oo_rtChannelMissingNoSupport": "An error occured while loading your document. It is now read-only to prevent data being corrupted or lost. Please send the following information to your instance administrator."
|
||||
"oo_rtChannelMissingNoSupport": "An error occured while loading your document. It is now read-only to prevent data being corrupted or lost. Please send the following information to your instance administrator.",
|
||||
"support_moderatorNotification": "{0} has added you to the support team as a moderator",
|
||||
"crowdfunding_popup_text2": "Alternatively, you can subscribe on this instance.",
|
||||
"form_input_ph_text": "Your answer here",
|
||||
"form_input_ph_number": "Enter a number",
|
||||
"form_date_time": "Select date and time",
|
||||
"contacts_muted": "Muted",
|
||||
"contacts_noFriends": "Your contact list is empty",
|
||||
"contacts_noFriendsInfo": "Start by <a>adding a contact</a> to begin a conversation.",
|
||||
"contacts_historyCleared": "The chat history has been deleted for everyone",
|
||||
"diagram_modesOptionLabel": "Change theme to {0}, will refresh app",
|
||||
"diagram_sketchTheme": "Sketch",
|
||||
"diagram_simpleTheme": "Simple",
|
||||
"diagram_classicTheme": "Classic"
|
||||
}
|
||||
|
||||
@ -169,7 +169,7 @@
|
||||
"contacts_rooms": "Pokoje",
|
||||
"contacts_fetchHistory": "Odzyskaj starszą historię",
|
||||
"contacts_removeHistoryServerError": "Wystąpił błąd podczas usuwania historii czatu. Spróbuj ponownie później",
|
||||
"contacts_confirmRemoveHistory": "Czy na pewno chcesz trwale usunąć historię czatu? Danych nie można przywrócić",
|
||||
"contacts_confirmRemoveHistory": "Czy na pewno chcesz usunąć historię czatu? Wiadomości zostaną usunięte dla wszystkich użytkowników i nie będzie można ich przywrócić.",
|
||||
"contacts_removeHistoryTitle": "Wyczyść historię czatu",
|
||||
"contacts_info4": "Każdy z uczestników może trwale wyczyścić historię czatu",
|
||||
"contacts_info3": "Kliknij dwukrotnie na ich ikonę, aby wyświetlić ich profil",
|
||||
@ -523,7 +523,7 @@
|
||||
"markdown_toc": "Zawartość",
|
||||
"survey": "Ankieta CryptPad",
|
||||
"crowdfunding_popup_no": "Nie teraz",
|
||||
"crowdfunding_popup_text": "<h3>Potrzebujemy Twojej pomocy!</h3>Aby zapewnić aktywny rozwój pakietu CryptPad, rozważ wsparcie projektu poprzez stronę OpenCollective, gdzie możesz zobaczyć nasz <b>Plan działania</b> i <b>Cele Finansowania</b>.",
|
||||
"crowdfunding_popup_text": "<h3>Potrzebujemy Twojej pomocy!</h3>Aby zapewnić dalszy rozwój pakietu CryptPad, rozważ wsparcie projektu poprzez darowiznę na platformie OpenCollective.",
|
||||
"crowdfunding_button2": "Przekaż darowiznę",
|
||||
"crowdfunding_button": "Wspieraj CryptPad",
|
||||
"autostore_notAvailable": "Aby móc korzystać z tej funkcji, dokument musi znajdować się na Twoim CryptDrive.",
|
||||
@ -1863,5 +1863,19 @@
|
||||
"loading_encrypted": "szyfrowane od końca do końca",
|
||||
"form_poll_required_hint": "Ta ankieta jest obowiązkowa, należy wybrać co najmniej jedną opcję.",
|
||||
"fo_trash_move_error": "Zawartości folderów współdzielonych nie można przenieść do kosza. Zamiast tego użyj opcji „Usuń”.",
|
||||
"fo_sharedFolder_move_error": "Folderów współdzielonych nie można przenosić do innych folderów współdzielonych."
|
||||
"fo_sharedFolder_move_error": "Folderów współdzielonych nie można przenosić do innych folderów współdzielonych.",
|
||||
"support_moreTickets": "{0} dostępnych zgłoszeń",
|
||||
"loadAll": "Załaduj wszystkie zgłoszenia",
|
||||
"oo_rtChannelMissing": "Podczas ładowania dokumentu wystąpił błąd. Dokument ma teraz status „tylko do odczytu”, aby zapobiec uszkodzeniu lub utracie danych. Prosimy o skorzystanie z poniższego przycisku, aby przesłać odpowiednie informacje do zespołu pomocy technicznej.",
|
||||
"oo_rtChannelMissingDate": "Wiadomość wysłana {0}",
|
||||
"oo_rtChannelMissingNoSupport": "Podczas ładowania dokumentu wystąpił błąd. Dokument ma teraz status „tylko do odczytu”, aby zapobiec uszkodzeniu lub utracie danych. Prosimy o przesłanie poniższych informacji do administratora instancji.",
|
||||
"support_moderatorNotification": "{0} dodał(a) Cię do zespołu wsparcia jako moderatora",
|
||||
"crowdfunding_popup_text2": "Możesz też zapisać się w tej instancji.",
|
||||
"form_input_ph_text": "Twoja odpowiedź tutaj",
|
||||
"form_input_ph_number": "Wpisz liczbę",
|
||||
"form_date_time": "Wybierz datę i godzinę",
|
||||
"contacts_muted": "Wyciszone",
|
||||
"contacts_noFriends": "Twoja lista kontaktów jest pusta",
|
||||
"contacts_noFriendsInfo": "Zacznij od <a>dodania kontaktu</a>, aby rozpocząć rozmowę.",
|
||||
"contacts_historyCleared": "Historia czatu została usunięta dla wszystkich"
|
||||
}
|
||||
|
||||
@ -1693,5 +1693,18 @@
|
||||
"admin_invitationDeleteConfirm": "Tem certeza de que deseja apagar esse convite?",
|
||||
"admin_usersTitle": "Diretório do usuário",
|
||||
"admin_usersHint": "Lista de contas conhecidas nesta instância. Selecione abaixo para adicionar contas automaticamente, ou insira as informações manualmente utilizando o formulário.",
|
||||
"admin_usersAdd": "Adicionar um usuário conhecido"
|
||||
"admin_usersAdd": "Adicionar um usuário conhecido",
|
||||
"support_moderatorNotification": "{0} adicionou você como moderador da equipe de suporte",
|
||||
"crowdfunding_popup_text2": "Alternativamente, você pode se subscrever nessa instância.",
|
||||
"form_input_ph_text": "Sua resposta aqui",
|
||||
"form_input_ph_number": "Insira um número",
|
||||
"form_date_time": "Selecione uma data e horário",
|
||||
"contacts_muted": "Mudo",
|
||||
"contacts_noFriends": "Sua lista de contatos está vazia",
|
||||
"contacts_noFriendsInfo": "Inicie <a>adicionando um contato</a> para iniciar uma conversa.",
|
||||
"contacts_historyCleared": "O histórico da conversa foi apagado para todos",
|
||||
"diagram_modesOptionLabel": "Modificar o tema para {0} atualizará o aplicativo",
|
||||
"diagram_sketchTheme": "Esboço",
|
||||
"diagram_simpleTheme": "Simples",
|
||||
"diagram_classicTheme": "Clássico"
|
||||
}
|
||||
|
||||
@ -209,7 +209,7 @@
|
||||
"contact": "Contact",
|
||||
"terms": "Temos de Serviço",
|
||||
"header_logoTitle": "Go to the main page",
|
||||
"edit": "edit",
|
||||
"edit": "editar",
|
||||
"view": "view",
|
||||
"feedback_about": "Se você está lendo isso, provavelmente está curioso para saber por que o CryptPad está solicitando páginas da web quando você executa certas ações.",
|
||||
"feedback_privacy": "We care about your privacy, and at the same time we want CryptPad to be very easy to use. We use this file to figure out which UI features matter to our users, by requesting it along with a parameter specifying which action was taken.",
|
||||
@ -444,7 +444,7 @@
|
||||
"contacts_confirmRemove": "Tem certeza de que quer remover <em>{0}</em> dos seus contactos?",
|
||||
"contacts_remove": "Remover este contacto",
|
||||
"contacts_send": "Enviar",
|
||||
"contacts_request": "<em>{0}</em> quer adiciona-lo como contacto. <b>Aceitar</b>?",
|
||||
"contacts_request": "<em>{0}</em> quer adicioná-lo como contacto. <b>Aceitar</b>?",
|
||||
"contacts_rejected": "Convite de contacto rejeitado",
|
||||
"contacts_added": "Convite de contacto aceite.",
|
||||
"contacts_title": "Contactos",
|
||||
@ -1480,5 +1480,97 @@
|
||||
"admin_planlimit": "Limite de armazenamento",
|
||||
"calendar_rec_no": "Uma vez",
|
||||
"calendar_rec_updated": "Regra atualizada a {0}",
|
||||
"admin_documentCreationTime": "Criado"
|
||||
"admin_documentCreationTime": "Criado",
|
||||
"og_pricing": "{0} Preços",
|
||||
"admin_nameTitle": "Nome da instância",
|
||||
"footer_source": "Código-fonte",
|
||||
"info_termsFlavour": "<a>Termos de uso</a> desta instância",
|
||||
"admin_descriptionTitle": "Descrição da instância",
|
||||
"info_sourceFlavour": "<a>Código-fonte</a> de CryptPad",
|
||||
"support_cat_drives": "Drive ou equipa",
|
||||
"support_cat_document": "Documento",
|
||||
"fivehundred_internalServerError": "Erro interno do servidor",
|
||||
"ui_ms": "milissegundos",
|
||||
"register_instance": "Criar uma nova conta em {0}",
|
||||
"error_embeddingDisabled": "O embutimento está desativado nesta instância do CryptPad",
|
||||
"error_embeddingDisabledSpecific": "O embutimento está desativado nesta aplicação do CryptPad.",
|
||||
"error_incorrectAccess": "Só se pode aceder a esta página através {0}.",
|
||||
"admin_cat_database": "Base de dados",
|
||||
"admin_accountMetadataTitle": "Informações sobre a conta",
|
||||
"admin_documentMetadataTitle": "Informações sobre o documento",
|
||||
"admin_documentSize": "Tamanho do documento",
|
||||
"admin_documentMetadata": "Metadados atuais",
|
||||
"ui_true": "verdadeiro",
|
||||
"ui_false": "falso",
|
||||
"home_morestorage": "Para obter mais espaço de armazenamento:",
|
||||
"ui_none": "nenhum",
|
||||
"ui_confirm": "Confirmar",
|
||||
"admin_documentModifiedTime": "Última modificação",
|
||||
"admin_currentlyOpen": "Atualmente aberto",
|
||||
"admin_planName": "Nome do plano",
|
||||
"admin_logoButton": "Carregar novo logotipo",
|
||||
"admin_supportSetupHint": "Criar ou atualizar as chaves de suporte.",
|
||||
"admin_cat_customize": "Personalizar",
|
||||
"admin_logoTitle": "Logotipo personalizado",
|
||||
"admin_usersAdd": "Adicionar utilizador/a conhecido/a",
|
||||
"duplicate": "Duplicar",
|
||||
"dph_account_destroyed": "Esta conta foi eliminada pelo seu/sua dono/a",
|
||||
"team_nameTooLong": "O nome da equipa é demasiado comprido (máx. 50 caracteres)",
|
||||
"admin_totpDisableButton": "Desativar",
|
||||
"goLeft": "Esquerda",
|
||||
"goRight": "Direita",
|
||||
"date": "Data",
|
||||
"calendar_settings": "Configurações do calendário",
|
||||
"admin_totpDisable": "Desativar a autenticação de dois fatores para esta conta",
|
||||
"login_notFilledPass": "Inserir uma palavra-passe",
|
||||
"login_notFilledUser": "Inserir um nome de utilizador",
|
||||
"admin_totpEnabled": "A autenticação de dois fatores está ativada",
|
||||
"register_nameTooLong": "O nome de utilizador deve conter menos de {0} caracteres",
|
||||
"mfa_disable": "Desativar autenticação de dois fatores",
|
||||
"mfa_enable": "Ativar autenticação de dois fatores",
|
||||
"mfa_revoke_button": "Confirmar a desativação da autenticação de dois fatores",
|
||||
"mfa_setup_button": "Iniciar a configuração da autenticação de dois fatores",
|
||||
"continue": "Continuar",
|
||||
"form_type_date": "Data",
|
||||
"settings_mfaTitle": "Autenticação de dois fatores (A2F)",
|
||||
"settings_otp_code": "Código de verificação",
|
||||
"contacts_noFriends": "A sua lista de contactos está vazia",
|
||||
"contacts_historyCleared": "O histórico de conversas foi eliminado para todos",
|
||||
"diagram_classicTheme": "Clássico",
|
||||
"diagram_modesOptionLabel": "Mudar o tema para {0} irá recarregar a aplicação",
|
||||
"crowdfunding_popup_text2": "Em alternativa, pode subscrever nesta instância.",
|
||||
"diagram_simpleTheme": "Simples",
|
||||
"help_close_button": "Fechar a notificação de ajuda",
|
||||
"admin_logoSize_error": "O tamanho do logotipo è demasiado grande",
|
||||
"admin_onboardingOptionsHint": "Escolha a opção apropriada para a sua instância.<br>Estas configurações podem ser mudadas mais tarde no painel de administração.",
|
||||
"admin_listAdminsHint": "Ver e remover administradores/as",
|
||||
"install_header": "Instalação",
|
||||
"admin_appSelection": "Configuração da aplicação",
|
||||
"support_legacyDump": "Exportar tudo",
|
||||
"install_launch": "Configuração da instância",
|
||||
"admin_listAction": "Remover os direitos de administração",
|
||||
"error_positiveNumber": "Insira um número positivo",
|
||||
"admin_addKeyLabel": "Adicionar um/a administrador/a através da chave pública dele/a",
|
||||
"form_answerType_error": "Escolha como responder ao formulário",
|
||||
"moveItemLeft": "Mover o elemento para a esquerda",
|
||||
"admin_addAdminsHint": "Adicionar administradores/as através da chave pública deles/as ou a partir da sua lista de contactos",
|
||||
"moveItemRight": "Mover o elemento para a direita",
|
||||
"moveItemUp": "Mover elemento para cima",
|
||||
"moveItemDown": "Mover elemento para baixo",
|
||||
"badges_moderator": "Suporte e moderação",
|
||||
"support_legacyClear": "Eliminar para esta conta",
|
||||
"support_movePending": "Mover para o arquivo",
|
||||
"support_pasteUserData": "Colar os dados do/a utilizador/a aqui",
|
||||
"admin_supportDelete": "Desativar suporte",
|
||||
"support_cat_open": "Caixa de entrada",
|
||||
"support_cat_closed": "Fechado",
|
||||
"support_cat_search": "Procurar",
|
||||
"support_cat_settings": "Configurações",
|
||||
"support_pending": "Tickets arquivados:",
|
||||
"support_pending_tag": "Arquivado",
|
||||
"support_active_tag": "Caixa de entrada",
|
||||
"support_notificationsTitle": "Desativar as notificações",
|
||||
"support_notificationsHint": "Escolha esta opção para desativar as notificações de novos tickets e respostas",
|
||||
"admin_supportTeamHint": "Adicionar e remover pessoas da equipa de suporte da instância",
|
||||
"support_team": "A equipa de suporte"
|
||||
}
|
||||
|
||||
@ -334,7 +334,7 @@
|
||||
"contacts_info3": "Dă dublu-click pe iconița contactului pentru a-i vizualiza profilul",
|
||||
"contacts_info4": "Ambii participanți pot șterge definitiv istoricul unui chat",
|
||||
"contacts_removeHistoryTitle": "Șterge istoricul chat-ului",
|
||||
"contacts_confirmRemoveHistory": "Ești sigur că vrei să ștergi definitiv istoricul chat-ului? Datele nu vor putea fi recuperate",
|
||||
"contacts_confirmRemoveHistory": "Ești sigur că vrei să ștergi istoricul chat-ului? Mesajele vor fi șterse pentru toată lumea și nu vor putea fi recuperate.",
|
||||
"contacts_removeHistoryServerError": "A apărut o eroare în timpul ștergerii istoricului chat-ului. Te rugăm să încerci mai târziu",
|
||||
"contacts_fetchHistory": "Recuperează istoricul mai vechi",
|
||||
"contacts_rooms": "Camere",
|
||||
@ -662,7 +662,7 @@
|
||||
"crowdfunding_button": "Susțineți CryptPad",
|
||||
"crowdfunding_popup_no": "Nu acum",
|
||||
"survey": "Sondaj CryptPad",
|
||||
"crowdfunding_popup_text": "<h3>Avem nevoie de ajutorul vostru!</h3>Pentru a ne asigura că dezvoltăm în mod activ CryptPad, luați în considerare susținerea proiectului prin intermediul paginii OpenCollective, unde puteți vedea <b>foaia noastră de parcurs</b> și <b>obiectivele de finanțare</b>.",
|
||||
"crowdfunding_popup_text": "<h3>Avem nevoie de ajutorul vostru!</h3>Pentru a ne asigura că dezvoltăm în mod activ CryptPad, luați în considerare susținerea proiectului printr-o donație pe OpenCollective.",
|
||||
"sharedFolders_share": "Distribuie acest link altor utilizatori înregistrați pentru a le da acces la dosarul distribuit. Odată ce acest link este accesat, dosarul distribuit va fi adăugat la CryptDrive lor.",
|
||||
"convertFolderToSF_SFParent": "Acest dosar nu poate fi convertit într-un dosar distribuit în locația lui actuală. Mută-l în afara dosarului distribuit în care se află pentru a continua.",
|
||||
"convertFolderToSF_SFChildren": "Acest dosar nu poate fi convertit într-un dosar distribuit deoarece el deja conține dosare distribuite. Mută dosarele distribuite altundeva pentru a continua.",
|
||||
@ -1865,5 +1865,21 @@
|
||||
"fo_trash_move_error": "Conținutul unui dosar distribuit nu poate fi mutat la gunoi. În schimb, folosiți funcția \"Elimină\".",
|
||||
"fo_sharedFolder_move_error": "Dosarele distribuite nu pot fi mutate în alte dosare distribuite.",
|
||||
"loadAll": "Încarcă toate tichetele",
|
||||
"support_moreTickets": "{0} tichete disponibile"
|
||||
"support_moreTickets": "{0} tichete disponibile",
|
||||
"diagram_classicTheme": "Clasic",
|
||||
"oo_rtChannelMissing": "A apărut o eroare la încărcarea documentului. Acesta a fost trecut în modul read-only pentru a preveni coruperea sau pierderea acestuia. Vă rugăm să folosiți butonul de mai jos pentru a trimite informațiile relevante către echipa de suport.",
|
||||
"oo_rtChannelMissingDate": "Mesaj trimis la {0}",
|
||||
"oo_rtChannelMissingNoSupport": "A apărut o eroare la încărcarea documentului. Acesta a fost trecut în modul read-only pentru a preveni coruperea sau pierderea acestuia. Vă rugăm să trimiteți următoarele informații către administratorul instanței dumneavoastră.",
|
||||
"support_moderatorNotification": "{0} te-a adăugat în echipa de suport ca și moderator",
|
||||
"crowdfunding_popup_text2": "În mod alternativ, puteți să vă abonați pe această instanță.",
|
||||
"form_input_ph_text": "Răspunsul tău aici",
|
||||
"form_input_ph_number": "Inserați un număr",
|
||||
"form_date_time": "Selectați data și ora",
|
||||
"contacts_muted": "Mut",
|
||||
"contacts_noFriends": "Lista dumneavoastră de contacte este goală",
|
||||
"contacts_noFriendsInfo": "Începeți prin <a>adăugarea unui contact</a> pentru a porni o conversație.",
|
||||
"contacts_historyCleared": "Istoricul chat-ului a fost șters pentru toată lumea",
|
||||
"diagram_modesOptionLabel": "Schimbă tema cu {0}, va reîmprospăta aplicația",
|
||||
"diagram_sketchTheme": "Schiță",
|
||||
"diagram_simpleTheme": "Simplu"
|
||||
}
|
||||
|
||||
@ -260,7 +260,7 @@
|
||||
"contacts_warning": "Все, что вы вводите здесь, является постоянным и доступно для всех существующих и будущих пользователей этого документа. Будьте осторожны с конфиденциальной информацией!",
|
||||
"contacts_info2": "Нажмите на значок контакта, чтобы пообщаться с ним",
|
||||
"contacts_info4": "Любой участник может полностью очистить историю чата",
|
||||
"contacts_confirmRemoveHistory": "Вы уверены, что хотите навсегда удалить свою историю чата? Данные не могут быть восстановлены",
|
||||
"contacts_confirmRemoveHistory": "Вы уверены, что хотите удалить историю чата? Сообщения будут удалены для всех и не могут быть восстановлены.",
|
||||
"contacts_removeHistoryServerError": "При удалении истории чата произошла ошибка. Попробуйте позже еще раз",
|
||||
"contacts_online": "Другой пользователь из этой комнаты находится онлайн",
|
||||
"fm_newButtonTitle": "Создать новый документ или папку, импортировать файл в текущую папку.",
|
||||
@ -938,7 +938,7 @@
|
||||
"admin_activeSessionsTitle": "Активные сессии",
|
||||
"admin_authError": "Только администраторы могут получить доступ к этой странице",
|
||||
"survey": "Опрос CryptPad",
|
||||
"crowdfunding_popup_text": "<h3>Нам нужна ваша помощь!</h3>Чтобы быть уверенными, что CryptPad активно развивается - рассмотрите возможность поддержки проекта через страницу OpenCollective, где Вы можете увидеть нашу <b>Дорожную карту</b> и <b>Цели финансирования</b>.",
|
||||
"crowdfunding_popup_text": "<h3>Нам нужна ваша помощь!</h3>Чтобы быть уверенными, что CryptPad активно развивается - рассмотрите возможность поддержки проекта через страницу OpenCollective.",
|
||||
"crowdfunding_button2": "Помочь деньгами",
|
||||
"autostore_notAvailable": "Вы должны сохранить этот документ на Вашем CryptDrive, прежде чем сможете использовать эту функцию.",
|
||||
"autostore_forceSave": "Сохраните файл в Вашем CryptDrive",
|
||||
@ -1868,5 +1868,18 @@
|
||||
"loadAll": "Загрузить все обращения",
|
||||
"oo_rtChannelMissing": "Произошла ошибка при загрузке Вашего документа. Теперь он доступен только для чтения, чтобы предотвратить повреждение или потерю данных. Пожалуйста, используйте кнопку ниже, чтобы отправить соответствующую информацию в службу поддержки.",
|
||||
"oo_rtChannelMissingDate": "Сообщение отправлено в {0}",
|
||||
"oo_rtChannelMissingNoSupport": "Произошла ошибка при загрузке Вашего документа. Теперь он доступен только для чтения, чтобы предотвратить повреждение или потерю данных. Пожалуйста, отправьте следующую информацию администратору Вашего экземпляра."
|
||||
"oo_rtChannelMissingNoSupport": "Произошла ошибка при загрузке Вашего документа. Теперь он доступен только для чтения, чтобы предотвратить повреждение или потерю данных. Пожалуйста, отправьте следующую информацию администратору Вашего экземпляра.",
|
||||
"support_moderatorNotification": "{0} добавил(а) Вас в команду поддержки в качестве модератора",
|
||||
"crowdfunding_popup_text2": "В качестве альтернативы Вы можете оформить подписку на этом экземпляре.",
|
||||
"form_input_ph_text": "Ваш ответ здесь",
|
||||
"form_input_ph_number": "Введите номер",
|
||||
"form_date_time": "Выберите дату и время",
|
||||
"contacts_muted": "Без звука",
|
||||
"contacts_noFriends": "Ваш список контактов пуст",
|
||||
"contacts_noFriendsInfo": "Начните с <a>добавления контакта</a>, чтобы начать разговор.",
|
||||
"contacts_historyCleared": "История чата удалена для всех",
|
||||
"diagram_modesOptionLabel": "Сменить тему на {0}, приложение перезагрузится",
|
||||
"diagram_sketchTheme": "Скетч",
|
||||
"diagram_simpleTheme": "Просто",
|
||||
"diagram_classicTheme": "Классика"
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -58,7 +58,7 @@
|
||||
"movedToTrash": "该文档已移至回收站。<br><a>访问我的云盘</a>",
|
||||
"shareButton": "分享",
|
||||
"shareSuccess": "链接已复制到剪贴板",
|
||||
"newButton": "新",
|
||||
"newButton": "新建",
|
||||
"newButtonTitle": "创建新文档",
|
||||
"saveTemplateButton": "另存为模板",
|
||||
"saveTemplatePrompt": "为模板选择标题",
|
||||
@ -110,7 +110,7 @@
|
||||
"fm_templateName": "模板",
|
||||
"fm_searchName": "搜索",
|
||||
"fm_searchPlaceholder": "搜索…",
|
||||
"fm_newButton": "新的",
|
||||
"fm_newButton": "新建",
|
||||
"fm_newButtonTitle": "创建新文档或文件夹,在当前文件夹中导入文件。",
|
||||
"fm_newFolder": "新建文件夹",
|
||||
"fm_newFile": "新文档",
|
||||
@ -147,7 +147,7 @@
|
||||
"fc_restore": "还原",
|
||||
"fc_remove": "删除",
|
||||
"fc_empty": "清空回收站",
|
||||
"fc_prop": "Properties",
|
||||
"fc_prop": "属性",
|
||||
"fo_moveUnsortedError": "您无法将文件夹移动到模板列表",
|
||||
"fo_existingNameError": "名称已在该目录中使用,请选择其他名称。",
|
||||
"fo_moveFolderToChildError": "您无法将文件夹移至其子文件夹",
|
||||
@ -265,7 +265,7 @@
|
||||
"contacts_rooms": "房间",
|
||||
"contacts_fetchHistory": "检索更早的历史记录",
|
||||
"contacts_removeHistoryServerError": "移除聊天记录时出错,请稍后重试",
|
||||
"contacts_confirmRemoveHistory": "是否确定要永久移除您的聊天记录?数据无法恢复",
|
||||
"contacts_confirmRemoveHistory": "是否确定要删除聊天记录?所有人的消息都将被移除,且无法恢复。",
|
||||
"contacts_removeHistoryTitle": "清理聊天记录",
|
||||
"contacts_info4": "任一参与者均可永久清除聊天记录",
|
||||
"contacts_info3": "双击联系人图标查看其个人资料",
|
||||
@ -517,7 +517,7 @@
|
||||
"markdown_toc": "内容",
|
||||
"survey": "CryptPad调查",
|
||||
"crowdfunding_popup_no": "以后",
|
||||
"crowdfunding_popup_text": "<h3>我们需要您的帮助!</h3>为使 CryptPad 能活跃地开发,请通过 OpenCollective 页面支持开发,在那里您可以看到我们的<b>开发路线图</b>和<b>创立目标</b>。",
|
||||
"crowdfunding_popup_text": "<h3>我们需要您的帮助!</h3>为确保 CryptPad 持续活跃开发,请考虑在 OpenCollective 上捐款支持该项目。",
|
||||
"crowdfunding_button2": "捐款",
|
||||
"crowdfunding_button": "支持CryptPad",
|
||||
"autostore_notAvailable": "在使用这个功能之前您必须将文档保存到您的 CryptDrive 中。",
|
||||
@ -712,11 +712,11 @@
|
||||
"team_inviteLinkSetPassword": "使用密码保护链接(推荐)",
|
||||
"team_inviteLinkTempName": "临时名称(在待处理的邀请列表中可见)",
|
||||
"team_inviteLinkTitle": "为该团队创建个性化邀请",
|
||||
"contacts_muteInfo": "您不会收到来自禁言用户的任何通知或消息。<br>他们不会知道您已将他们禁言。 ",
|
||||
"contacts_mutedUsers": "已禁言账号",
|
||||
"contacts_manageMuted": "管理禁言",
|
||||
"contacts_unmute": "取消禁言",
|
||||
"contacts_mute": "禁言",
|
||||
"contacts_muteInfo": "您将不会收到来自已静音用户的任何通知或消息。<br>他们不会知道您已将他们静音。 ",
|
||||
"contacts_mutedUsers": "已静音账号",
|
||||
"contacts_manageMuted": "管理静音",
|
||||
"contacts_unmute": "取消静音",
|
||||
"contacts_mute": "静音",
|
||||
"share_noContactsNotLoggedIn": "登录或注册以查看您现有的联系人并添加新联系人。",
|
||||
"share_copyProfileLink": "复制个人资料链接",
|
||||
"share_noContactsLoggedIn": "您尚未与 CryptPad 上的任何人创建联系。 分享指向您的个人资料的链接,以便人们向您发送联系请求。",
|
||||
@ -987,7 +987,7 @@
|
||||
"allow_text": "使用访问列表意味着只有选定的用户和所有者才能访问此文档。",
|
||||
"logoutEverywhere": "全部登出",
|
||||
"owner_text": "文档的所有者是唯一有权执行以下操作的用户:添加/删除所有者、使用访问列表限制对文档的访问或删除文档。",
|
||||
"access_muteRequests": "忽略此文档的访问请求",
|
||||
"access_muteRequests": "静音此文档的访问请求",
|
||||
"allow_label": "访问列表:{0}",
|
||||
"allow_disabled": "禁用",
|
||||
"allow_enabled": "启用",
|
||||
@ -1868,5 +1868,18 @@
|
||||
"loadAll": "加载所有工单",
|
||||
"oo_rtChannelMissing": "加载文档时出错。为防止数据损坏或丢失,文档现已设为只读。请使用下方按钮将相关信息发送给支持团队。",
|
||||
"oo_rtChannelMissingDate": "消息已于 {0} 发送",
|
||||
"oo_rtChannelMissingNoSupport": "加载文档时出错。为防止数据损坏或丢失,文档现已设为只读。请将以下信息发送给您的实例管理员。"
|
||||
"oo_rtChannelMissingNoSupport": "加载文档时出错。为防止数据损坏或丢失,文档现已设为只读。请将以下信息发送给您的实例管理员。",
|
||||
"support_moderatorNotification": "{0} 已将您添加为支持团队的审核员",
|
||||
"crowdfunding_popup_text2": "或者,您也可以在此实例上订阅。",
|
||||
"form_input_ph_text": "请在此输入您的回答",
|
||||
"form_input_ph_number": "输入一个数字",
|
||||
"form_date_time": "选择日期和时间",
|
||||
"contacts_muted": "已静音",
|
||||
"contacts_noFriends": "您的联系人列表为空",
|
||||
"contacts_noFriendsInfo": "请先<a>添加联系人</a>以开始对话。",
|
||||
"contacts_historyCleared": "所有人的聊天记录已被删除",
|
||||
"diagram_modesOptionLabel": "将主题更改为 {0},应用将刷新",
|
||||
"diagram_sketchTheme": "草图",
|
||||
"diagram_simpleTheme": "简洁",
|
||||
"diagram_classicTheme": "经典"
|
||||
}
|
||||
|
||||
3
www/common/translations/messages.zh_Hant.json.license
Normal file
3
www/common/translations/messages.zh_Hant.json.license
Normal file
@ -0,0 +1,3 @@
|
||||
SPDX-FileCopyrightText: 2026 XWiki CryptPad Team <contact@cryptpad.org> and contributors
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
2
www/common/worker.bundle.min.js
vendored
2
www/common/worker.bundle.min.js
vendored
File diff suppressed because one or more lines are too long
@ -29,6 +29,9 @@
|
||||
align-items: center;
|
||||
span:nth-child(2) {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,6 +53,7 @@ define([
|
||||
$container: $(toolbarElement),
|
||||
pageTitle: Messages.contacts_title,
|
||||
metadataMgr: common.getMetadataMgr(),
|
||||
skipLink: '#cp-app-contacts-container'
|
||||
};
|
||||
APP.toolbar = Toolbar.create(configTb);
|
||||
APP.toolbar.$rightside.hide();
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
var factory = function (/*Hash*/) {
|
||||
let devMode = false;
|
||||
try { devMode = localStorage.CryptPad_dev === "1"; } catch (e) {}
|
||||
|
||||
// This API is used to load a CryptPad editor for a provided document in
|
||||
// an external platform.
|
||||
@ -48,7 +50,7 @@
|
||||
var msg = data.msg;
|
||||
var txid = data.txid;
|
||||
if (commands[msg.q]) {
|
||||
console.warn('OUTER RECEIVED QUERY', msg.q, msg.data);
|
||||
if (devMode) { console.warn('OUTER RECEIVED QUERY', msg.q, msg.data); }
|
||||
commands[msg.q](msg.data, function (args) {
|
||||
_sendCb(txid, args);
|
||||
});
|
||||
@ -62,7 +64,7 @@
|
||||
var txid = getTxid();
|
||||
if (cb) { handlers[txid] = cb; }
|
||||
|
||||
console.warn('OUTER SENT QUERY', q, data);
|
||||
if (devMode) { console.warn('OUTER SENT QUERY', q, data); }
|
||||
iWindow.postMessage({ msg: {
|
||||
q: q,
|
||||
data: data,
|
||||
|
||||
@ -152,7 +152,40 @@ define([
|
||||
}, true
|
||||
);
|
||||
|
||||
var parameters;
|
||||
var checkDefaultTheme = function(cb) {
|
||||
var privateData = framework._.cpNfInner.metadataMgr.getPrivateData();
|
||||
if (!privateData.settings['diagram'] || !privateData.settings['diagram'].mode) {
|
||||
cb('sketch');
|
||||
} else {
|
||||
cb(privateData.settings['diagram'].mode);
|
||||
}
|
||||
};
|
||||
|
||||
var parameters = new URLSearchParams({
|
||||
test: 1,
|
||||
stealth: 1,
|
||||
embed: 1,
|
||||
drafts: 0,
|
||||
p: 'cryptpad',
|
||||
integrated: framework.isIntegrated() ? 'true' : 'false',
|
||||
chrome: 0,
|
||||
dark: window.CryptPad_theme === "dark" ? 1 : 0,
|
||||
|
||||
// Hide save and exit buttons
|
||||
noSaveBtn: 1,
|
||||
saveAndExit: 0,
|
||||
noExitBtn: 1,
|
||||
browser: 0,
|
||||
pv: 0,
|
||||
|
||||
noDevice: 1,
|
||||
filesupport: 0,
|
||||
|
||||
modified: 'unsavedChanges',
|
||||
proto: 'json',
|
||||
|
||||
lang: Messages._languageUsed
|
||||
});
|
||||
|
||||
framework.onEditableChange(function () {
|
||||
var readOnly = framework.isReadOnly() || framework.isLocked();
|
||||
@ -163,41 +196,31 @@ define([
|
||||
parameters.set('chrome', '1');
|
||||
parameters.set('grid', '1');
|
||||
}
|
||||
});
|
||||
|
||||
var loadDiagram = function () {
|
||||
checkDefaultTheme(function(theme) {
|
||||
var defaultTheme = theme;
|
||||
parameters.set('ui', defaultTheme);
|
||||
});
|
||||
var isReadOnly = framework.isReadOnly() ? 0 : 1;
|
||||
parameters.set('chrome', isReadOnly);
|
||||
drawioFrame.src = ApiConfig.httpSafeOrigin + '/components/drawio/src/main/webapp/index.html?'
|
||||
+ parameters;
|
||||
});
|
||||
};
|
||||
|
||||
// starting the CryptPad framework
|
||||
framework.start();
|
||||
|
||||
parameters = new URLSearchParams({
|
||||
test: 1,
|
||||
stealth: 1,
|
||||
embed: 1,
|
||||
drafts: 0,
|
||||
p: 'cryptpad',
|
||||
integrated: framework.isIntegrated() ? 'true' : 'false',
|
||||
|
||||
chrome: framework.isReadOnly() ? 0 : 1,
|
||||
dark: window.CryptPad_theme === "dark" ? 1 : 0,
|
||||
|
||||
// Hide save and exit buttons
|
||||
noSaveBtn: 1,
|
||||
saveAndExit: 0,
|
||||
noExitBtn: 1,
|
||||
browser: 0,
|
||||
|
||||
noDevice: 1,
|
||||
filesupport: 0,
|
||||
|
||||
modified: 'unsavedChanges',
|
||||
proto: 'json',
|
||||
|
||||
lang: Messages._languageUsed
|
||||
});
|
||||
|
||||
drawioFrame.src = ApiConfig.httpSafeOrigin + '/components/drawio/src/main/webapp/index.html?'
|
||||
+ parameters;
|
||||
//wait for metadata to update before checking the theme and loading Drawio UI
|
||||
var metadataMgr = framework._.sfCommon.getMetadataMgr();
|
||||
var onChange = function () {
|
||||
var privateData = metadataMgr.getPrivateData();
|
||||
if (!privateData.settings.toolbar) { return; }
|
||||
loadDiagram();
|
||||
metadataMgr.off('change', onChange);
|
||||
};
|
||||
metadataMgr.onChange(onChange);
|
||||
|
||||
window.addEventListener("message", (event) => {
|
||||
if (event.source === drawioFrame.contentWindow) {
|
||||
@ -209,6 +232,58 @@ define([
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
|
||||
var setTheme = function (theme, cb) {
|
||||
framework._.sfCommon.setAttribute(['diagram', 'mode'], theme, function() {
|
||||
cb();
|
||||
});
|
||||
};
|
||||
|
||||
var mkModeButton = function (framework) {
|
||||
var modes = [Messages.diagram_sketchTheme, Messages.diagram_simpleTheme, Messages.diagram_classicTheme];
|
||||
var types = [];
|
||||
|
||||
modes.forEach(function(mode){
|
||||
types.push({
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
'data-value': mode,
|
||||
'aria-label': Messages._getKey('diagram_modesOptionLabel', [mode]),
|
||||
},
|
||||
content: mode,
|
||||
action: function () {
|
||||
var $self = $('a[data-value="' + mode + '"]');
|
||||
parameters.set('ui', mode);
|
||||
drawioFrame.src = ApiConfig.httpSafeOrigin + '/components/drawio/src/main/webapp/index.html?'
|
||||
+ parameters;
|
||||
setTheme(mode, function() {
|
||||
$('.cp-dropdown-content').find('.cp-dropdown-element-active').removeClass('cp-dropdown-element-active');
|
||||
$self.addClass('cp-dropdown-element-active');
|
||||
$self.closest('li').focus();
|
||||
$('.cp-dropdown-content').hide();
|
||||
} );
|
||||
},
|
||||
});
|
||||
});
|
||||
checkDefaultTheme(function(theme) {
|
||||
var $drawer = UIElements.createDropdown({
|
||||
text: Messages.themeButton,
|
||||
options: types,
|
||||
common: framework._.sfCommon,
|
||||
iconCls: 'color-palette',
|
||||
initialValue: parameters.get('ui') || theme
|
||||
});
|
||||
framework._.toolbar.$theme = $drawer.find('ul.cp-dropdown-content');
|
||||
framework._.toolbar.$bottomL.append($drawer);
|
||||
$drawer.addClass('cp-toolbar-appmenu');
|
||||
$drawer.on('click', function () {
|
||||
$('a[data-value="' + parameters.get('ui') || theme + '"]').addClass('cp-dropdown-element-active');
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
mkModeButton(framework);
|
||||
|
||||
};
|
||||
|
||||
$('#cp-app-diagram-editor').hide();
|
||||
|
||||
@ -41,7 +41,19 @@ define([
|
||||
for (var k in objRef) { delete objRef[k]; }
|
||||
$.extend(true, objRef, objToCopy);
|
||||
};
|
||||
var updateSharedFolders = function (sframeChan, manager, drive, folders, cb) {
|
||||
var lockoutAnonSharedFolder = function (common) {
|
||||
APP.newSharedFolder = null;
|
||||
APP.closed = true;
|
||||
var msg = Messages.restrictedError;
|
||||
if (common && !common.isLoggedIn()) {
|
||||
msg = UIElements.loginErrorScreenContent(common);
|
||||
}
|
||||
setTimeout(function () {
|
||||
UI.errorLoadingScreen(msg, false, false);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
var updateSharedFoldersCore = function (common, sframeChan, manager, drive, folders, cb) {
|
||||
if (!drive || !drive.sharedFolders) {
|
||||
return void cb();
|
||||
}
|
||||
@ -57,6 +69,12 @@ define([
|
||||
sharedFolder: fId
|
||||
}, waitFor(function (err, newObj) {
|
||||
if (!APP.loggedIn && APP.newSharedFolder) {
|
||||
if (newObj && newObj.restricted) {
|
||||
lockoutAnonSharedFolder(common);
|
||||
waitFor.abort();
|
||||
return;
|
||||
}
|
||||
if (err) { return; }
|
||||
if (!newObj || !Object.keys(newObj).length) {
|
||||
// Empty anon drive: deleted
|
||||
var msg = Messages.deletedError + '<br>' + Messages.errorRedirectToHome;
|
||||
@ -103,6 +121,11 @@ define([
|
||||
cb();
|
||||
});
|
||||
};
|
||||
var updateSharedFolders = function (common) {
|
||||
return function (sframeChan, manager, drive, folders, cb) {
|
||||
updateSharedFoldersCore(common, sframeChan, manager, drive, folders, cb);
|
||||
};
|
||||
};
|
||||
var updateObject = function (sframeChan, obj, cb) {
|
||||
sframeChan.query('Q_DRIVE_GETOBJECT', null, function (err, newObj) {
|
||||
copyObjectValue(obj, newObj);
|
||||
@ -303,7 +326,7 @@ define([
|
||||
proxy: proxy,
|
||||
folders: folders,
|
||||
updateObject: updateObject,
|
||||
updateSharedFolders: updateSharedFolders,
|
||||
updateSharedFolders: updateSharedFolders(common),
|
||||
history: history,
|
||||
toolbar: toolbar,
|
||||
APP: APP
|
||||
|
||||
@ -136,7 +136,8 @@ define([
|
||||
type:"number",
|
||||
value: opts.maxLength,
|
||||
min: 100,
|
||||
max: 5000
|
||||
max: 5000,
|
||||
'aria-label': Messages.form_editMaxLength
|
||||
});
|
||||
maxLength = h('div.cp-form-edit-max-options', [
|
||||
h('span', Messages.form_editMaxLength),
|
||||
@ -244,7 +245,8 @@ define([
|
||||
type:"number",
|
||||
value: v.max,
|
||||
min: 1,
|
||||
max: v.values.length
|
||||
max: v.values.length,
|
||||
'aria-label': Messages.form_editMax
|
||||
});
|
||||
maxOptions = h('div.cp-form-edit-max-options', [
|
||||
h('span', Messages.form_editMax),
|
||||
@ -291,7 +293,7 @@ define([
|
||||
var $add, $addItem;
|
||||
var addMultiple;
|
||||
var getOption = function (val, placeholder, isItem, uid) {
|
||||
var input = h('input', {value:val});
|
||||
var input = h('input', {value:val, 'aria-label': val});
|
||||
var $input = $(input);
|
||||
if (placeholder) {
|
||||
input.placeholder = val;
|
||||
@ -332,7 +334,7 @@ define([
|
||||
setCursor();
|
||||
}
|
||||
|
||||
var del = h('button.btn.btn-danger-outline', Icons.get('close'));
|
||||
var del = h('button.btn.btn-danger-outline',{ 'aria-label': Messages.poll_remove }, Icons.get('close'));
|
||||
var formHandle;
|
||||
if (v.type !== 'time') {
|
||||
formHandle = h('span.cp-form-handle', [
|
||||
@ -390,6 +392,7 @@ define([
|
||||
}
|
||||
|
||||
$(input).on('input', function () {
|
||||
$input.attr('aria-label', $(input).val());
|
||||
evOnSave.fire();
|
||||
});
|
||||
|
||||
@ -1330,7 +1333,8 @@ define([
|
||||
options: qOptions, // Entries displayed in the menu
|
||||
isSelect: true,
|
||||
caretDown: true,
|
||||
buttonCls: 'btn btn-secondary'
|
||||
buttonCls: 'btn btn-secondary',
|
||||
buttonTitle: Messages.form_condition_q
|
||||
};
|
||||
qSelect = UIElements.createDropdown(qConfig);
|
||||
qSelect[0].dropdown = qSelect;
|
||||
@ -1362,7 +1366,10 @@ define([
|
||||
$(iSelect).attr('data-drop', 'i').hide();
|
||||
iSelect.onChange.reg(function () { onChange(); });
|
||||
|
||||
var remove = h('button.btn.btn-danger-alt.cp-condition-remove', [
|
||||
var remove = h('button.btn.btn-danger-alt.cp-condition-remove',{
|
||||
'title': Messages.poll_remove,
|
||||
'aria-label': Messages.poll_remove
|
||||
}, [
|
||||
Icons.get('close', {'class': 'nomargin'})
|
||||
]);
|
||||
$(remove).on('click', function () {
|
||||
@ -1699,10 +1706,13 @@ define([
|
||||
var tag = h('input', {
|
||||
type: opts.type,
|
||||
step: "any",
|
||||
placeholder: Messages['form_input_ph_'+opts.type] || ''
|
||||
'aria-label': Messages['form_input_ph_'+opts.type],
|
||||
placeholder: Messages['form_input_ph_'+opts.type]
|
||||
});
|
||||
var $tag = $(tag);
|
||||
$tag.on('change keypress keydown', Util.throttle(function () {
|
||||
let currentVal = $tag.val().trim();
|
||||
$tag.attr('aria-label', currentVal || Messages['form_input_ph_'+opts.type] || '');
|
||||
evOnChange.fire();
|
||||
}, 500));
|
||||
var cursorGetter;
|
||||
@ -1774,6 +1784,7 @@ define([
|
||||
$text.val($text.val().slice(0, opts.maxLength));
|
||||
l = $text.val().length;
|
||||
}
|
||||
$text.attr('aria-label', $text.val() || Messages.form_input_ph_text);
|
||||
$(charCount).text(Messages._getKey('form_maxLength', [
|
||||
$text.val().length,
|
||||
opts.maxLength
|
||||
@ -2106,13 +2117,18 @@ define([
|
||||
get: function (opts, a, n, evOnChange) {
|
||||
opts = Util.clone(TYPES.date.defaultOpts);
|
||||
|
||||
var tag = h('input');
|
||||
var tag = h('input', {'aria-label': Messages.form_date_time});
|
||||
|
||||
var picker = Flatpickr(tag, {
|
||||
disableMobile: true,
|
||||
enableTime: true,
|
||||
time_24hr: is24h,
|
||||
dateFormat: dateFormat,
|
||||
onChange: function(date) {
|
||||
if (date) {
|
||||
$(tag).attr('aria-label', date);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var $tag = $(tag);
|
||||
@ -2532,7 +2548,7 @@ define([
|
||||
$(div).data('val', data);
|
||||
return div;
|
||||
});
|
||||
var tag = h('div.cp-form-type-sort-container', { 'role': 'listbox' },[
|
||||
var tag = h('div.cp-form-type-sort-container', { 'role': 'listbox', 'aria-label': Messages._getKey('form_sort_hint', [els.length]) },[
|
||||
h('div.cp-form-sort-hint', Messages._getKey('form_sort_hint', [els.length])),
|
||||
els
|
||||
]);
|
||||
@ -3932,6 +3948,7 @@ define([
|
||||
|
||||
var btn = h('button.btn.btn-secondary', {
|
||||
title: full ? '' : Messages['form_type_'+type],
|
||||
'aria-label': full ? '' : Messages['form_type_'+type],
|
||||
'data-type': type
|
||||
}, [
|
||||
(TYPES[type] || STATIC_TYPES[type]).icon.cloneNode(),
|
||||
@ -3987,7 +4004,8 @@ define([
|
||||
var add = h('div', [Icons.get('add')]);
|
||||
if (!full) {
|
||||
add = h('button.btn.cp-form-creator-inline-add', {
|
||||
title: Messages.tag_add
|
||||
title: Messages.tag_add,
|
||||
'aria-label': Messages.tag_add
|
||||
}, [
|
||||
Icons.get('add', {class: 'add-open'}),
|
||||
Icons.get('close', {class: 'add-close'}),
|
||||
@ -4082,7 +4100,7 @@ define([
|
||||
});
|
||||
requiredContent.push(infoIcon);
|
||||
}
|
||||
requiredTag = h('span.cp-form-required-tag', requiredContent);
|
||||
requiredTag = h('span.cp-form-required-tag', { 'id': 'cp-required-' + (n-1) }, requiredContent);
|
||||
}
|
||||
|
||||
var dragHandle;
|
||||
@ -4202,6 +4220,7 @@ define([
|
||||
}
|
||||
if (saving && !e) { return; } // Prevent spam Enter
|
||||
block.q = v.trim();
|
||||
$inputQ.attr('aria-label', block.q);
|
||||
framework.localChange();
|
||||
saving = true;
|
||||
framework._.cpNfInner.chainpad.onSettle(function () {
|
||||
@ -4213,6 +4232,7 @@ define([
|
||||
};
|
||||
var onCancelQ = function () {
|
||||
$inputQ.val(block.q || Messages.form_default);
|
||||
$inputQ.attr('aria-label', block.q || Messages.form_default);
|
||||
cancel = true;
|
||||
$inputQ.blur();
|
||||
$(q).removeClass('editing');
|
||||
@ -4225,6 +4245,8 @@ define([
|
||||
$(q).addClass('editing');
|
||||
});
|
||||
$inputQ.blur(onSaveQ);
|
||||
$inputQ.attr('aria-label', block.q || Messages.form_default);
|
||||
|
||||
q = h('div.cp-form-input-block', [inputQ]);
|
||||
|
||||
// Delete question
|
||||
@ -4420,11 +4442,16 @@ define([
|
||||
|
||||
var editableCls = editable ? ".editable" : "";
|
||||
var draggable = APP.drag ? '' : '.nodrag';
|
||||
elements.push(h('fieldset.cp-form-block'+editableCls+draggable, {
|
||||
var ariaLabelledby = 'cp-question-' + (n-1);
|
||||
if (requiredTag) {
|
||||
ariaLabelledby += ' cp-required-' + (n-1);
|
||||
}
|
||||
var attributes = {
|
||||
'data-id':uid,
|
||||
'data-type':type,
|
||||
'aria-labelledby': 'cp-question-' + (n-1)
|
||||
}, [
|
||||
'aria-labelledby': ariaLabelledby
|
||||
};
|
||||
elements.push(h('fieldset.cp-form-block'+editableCls+draggable, attributes, [
|
||||
h('header', [
|
||||
APP.isEditor ? dragHandle : undefined,
|
||||
shiftButtons,
|
||||
@ -5144,13 +5171,18 @@ define([
|
||||
return;
|
||||
}
|
||||
// Otherwise add it
|
||||
var datePicker = h('input');
|
||||
var datePicker = h('input', {'aria-label': Messages.form_date_time});
|
||||
var picker = Flatpickr(datePicker, {
|
||||
disableMobile: true,
|
||||
enableTime: true,
|
||||
time_24hr: is24h,
|
||||
dateFormat: dateFormat,
|
||||
minDate: new Date()
|
||||
minDate: new Date(),
|
||||
onChange: function(date) {
|
||||
if (date) {
|
||||
$(datePicker).attr('aria-label', date);
|
||||
}
|
||||
}
|
||||
});
|
||||
var save = h('button.btn.btn-primary', Messages.settings_save);
|
||||
$(save).click(function () {
|
||||
@ -5164,7 +5196,7 @@ define([
|
||||
refreshEndDate();
|
||||
});
|
||||
});
|
||||
var cancel = h('button.btn.btn-danger',Icons.get('close', {'class': 'nomargin'}));
|
||||
var cancel = h('button.btn.btn-danger',{'aria-label': Messages.poll_remove}, Icons.get('close', {'class': 'nomargin'}));
|
||||
$(cancel).click(function () {
|
||||
refreshEndDate();
|
||||
});
|
||||
@ -5335,8 +5367,8 @@ define([
|
||||
|
||||
var toggleOffclass = 'ontouchstart' in window ? 'cp-toggle-active' : undefined;
|
||||
var toggleOnclass = 'ontouchstart' in window ? undefined : 'cp-toggle-active';
|
||||
var toggleDragOff = h(`button#cp-toggle-drag-off.cp-form-view-drag.${toggleOffclass}`, {'title': Messages.toggleArrows, 'tabindex': 0}, Icons.get('select'));
|
||||
var toggleDragOn = h(`button#cp-toggle-drag-on.cp-form-view-drag.${toggleOnclass}`, {'title': Messages.toggleDrag, 'tabindex': 0}, Icons.get('touch-mode'));
|
||||
var toggleDragOff = h(`button#cp-toggle-drag-off.cp-form-view-drag.${toggleOffclass}`, {'title': Messages.toggleArrows, 'tabindex': 0, 'aria-label': Messages.toggleArrows}, Icons.get('select'));
|
||||
var toggleDragOn = h(`button#cp-toggle-drag-on.cp-form-view-drag.${toggleOnclass}`, {'title': Messages.toggleDrag, 'tabindex': 0, 'aria-label': Messages.toggleDrag}, Icons.get('touch-mode'));
|
||||
const updateDrag = state => {
|
||||
return function () {
|
||||
var $container = $('.cp-form-creator-content');
|
||||
|
||||
@ -14,7 +14,10 @@ define([
|
||||
return Math.random().toString(16).replace('0.', '');
|
||||
};
|
||||
var init = function () {
|
||||
console.warn('INIT');
|
||||
let devMode = false;
|
||||
try { devMode = localStorage.CryptPad_dev === "1"; } catch (e) {}
|
||||
if (devMode) { console.warn('INIT'); }
|
||||
|
||||
var p = window.parent;
|
||||
var txid = getTxid();
|
||||
p.postMessage({ q: 'INTEGRATION_READY', txid: txid }, '*');
|
||||
@ -86,7 +89,7 @@ define([
|
||||
http.open('HEAD', url);
|
||||
http.onreadystatechange = function() {
|
||||
if (this.readyState === this.DONE) {
|
||||
console.error(oldKey, this.status);
|
||||
if (devMode) { console.error(oldKey, this.status); }
|
||||
if (this.status === 200) {
|
||||
return cb({state: true});
|
||||
}
|
||||
@ -150,7 +153,7 @@ define([
|
||||
}
|
||||
if (data.keepOld) { // they provide their own key, we must turn it into a hash
|
||||
var key = sanitizeKey(data.key) + "000000000000000000000000000000000";
|
||||
console.warn('KEY', key);
|
||||
if (devMode) { console.warn('KEY', key); }
|
||||
let hash = `/2/integration/edit/${key.slice(0,24)}/`;
|
||||
return void cb({
|
||||
key: hash,
|
||||
@ -265,7 +268,7 @@ define([
|
||||
xhr.responseType = 'blob';
|
||||
//xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.onload = function () {
|
||||
console.error(this.status);
|
||||
if (devMode) { console.error(this.status); }
|
||||
if (this.status === 200) {
|
||||
cb();
|
||||
} else {
|
||||
@ -278,7 +281,7 @@ define([
|
||||
xhr.send(blob);
|
||||
};
|
||||
chan.on('START', function (data, cb) {
|
||||
console.warn('INNER START', data);
|
||||
if (devMode) { console.warn('INNER START', data); }
|
||||
// data.key is a hash
|
||||
var href = Hash.hashToHref(data.key, data.application);
|
||||
if (data.editorConfig.lang) {
|
||||
@ -303,7 +306,7 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
console.error(Hash.hrefToHexChannelId(href));
|
||||
if (devMode) { console.error(Hash.hrefToHexChannelId(href)); }
|
||||
let startApp = function (blob) {
|
||||
window.CP_integration_outer = {
|
||||
pathname: `/${data.application}/`,
|
||||
@ -336,7 +339,6 @@ define([
|
||||
path = '/common/onlyoffice/main.js';
|
||||
}
|
||||
require([path], function () {
|
||||
console.warn('SAO REQUIRED');
|
||||
delete window.CP_integration_outer;
|
||||
cb();
|
||||
});
|
||||
|
||||
@ -432,6 +432,10 @@ define([
|
||||
var list = boards.list || [];
|
||||
var idx = list.indexOf(id);
|
||||
if (idx !== -1) { list.splice(idx, 1); }
|
||||
var boardItems = (boards.data || {})[id].item;
|
||||
boardItems.forEach(function(item) {
|
||||
delete kanban.options.boards.items[item];
|
||||
});
|
||||
delete (boards.data || {})[id];
|
||||
kanban.removeBoard(id);
|
||||
return void commit();
|
||||
|
||||
@ -313,9 +313,16 @@ define([
|
||||
if (target.classList.contains('kanban-trash')) {
|
||||
list.splice(index1, 1);
|
||||
if (list.indexOf(id) === -1) {
|
||||
var board = self.options.boards.data[id];
|
||||
var boardItems = board.item;
|
||||
boardItems.forEach(function(item) {
|
||||
delete self.options.boards.items[item];
|
||||
});
|
||||
delete self.options.boards.data[id];
|
||||
}
|
||||
self.onChange();
|
||||
self.setBoards(self.options.boards);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -218,7 +218,8 @@ define([
|
||||
var active = privateData.category || 'all';
|
||||
common.setHash(active);
|
||||
Object.keys(categories).forEach(function (key) {
|
||||
var $category = $('<div>', {'class': 'cp-sidebarlayout-category', 'tabindex': 0, 'role': 'menuitem'}).appendTo($categories);
|
||||
var name = Messages['notifications_cat_'+key] || key;
|
||||
var $category = $('<div>', {'class': 'cp-sidebarlayout-category', 'tabindex': 0, 'role': 'menuitem', 'aria-label': name}).appendTo($categories);
|
||||
if (key === 'all') { $category.append($(Icons.get('all'))); }
|
||||
if (key === 'friends') { $category.append($(Icons.get('contacts-book'))); }
|
||||
if (key === 'pads') { $category.append($(Icons.get('file-pad'))); }
|
||||
@ -245,7 +246,7 @@ define([
|
||||
showCategories(categories[key]);
|
||||
});
|
||||
|
||||
$category.append(Messages['notifications_cat_'+key] || key);
|
||||
$category.append(h('span.cp-sidebarlayout-category-name', name));
|
||||
});
|
||||
showCategories(categories[active]);
|
||||
};
|
||||
|
||||
@ -728,13 +728,21 @@ define([
|
||||
var href = el.getAttribute('href');
|
||||
if (/^#/.test(href)) {
|
||||
try {
|
||||
var foundAnchor = false;
|
||||
$inner.find('.cke_anchor[data-cke-realelement]').each(function (j, el) {
|
||||
if (foundAnchor) { return; }
|
||||
var i = editor.restoreRealElement($(el));
|
||||
var node = i.$;
|
||||
if (node.id === href.slice(1)) {
|
||||
el.scrollIntoView();
|
||||
foundAnchor = true;
|
||||
}
|
||||
});
|
||||
// Fallback: try to find anchor by ID directly
|
||||
if (!foundAnchor) {
|
||||
var anchorsById = $inner.find('#' + href.slice(1));
|
||||
if (anchorsById.length) { anchorsById[0].scrollIntoView(); }
|
||||
}
|
||||
} catch (err) {}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2053,7 +2053,7 @@ define([
|
||||
Messages.settings_cat_notifications = Messages.notificationsPage;
|
||||
Messages.settings_cat_profile = Messages.profileButton;
|
||||
var createLeftside = function() {
|
||||
var $categories = $('<div>', { 'class': 'cp-sidebarlayout-categories' })
|
||||
var $categories = $('<div>', { 'class': 'cp-sidebarlayout-categories', 'role': 'menu'})
|
||||
.appendTo(APP.$leftside);
|
||||
APP.$usage = $('<div>', { 'class': 'usage' }).appendTo(APP.$leftside);
|
||||
var active = privateData.category || 'account';
|
||||
@ -2068,11 +2068,13 @@ define([
|
||||
let name = SIDEBAR_NAMES[key] ||
|
||||
Messages['settings_cat_' + key] || key;
|
||||
var $category = $(h('div.cp-sidebarlayout-category', {
|
||||
'role': 'menuitem',
|
||||
'tabindex': 0,
|
||||
'data-category': key
|
||||
'data-category': key,
|
||||
'aria-label': name
|
||||
}, [
|
||||
icon,
|
||||
name,
|
||||
h('span.cp-sidebarlayout-category-name', name),
|
||||
])).appendTo($categories);
|
||||
|
||||
|
||||
|
||||
@ -14,10 +14,11 @@ define([
|
||||
'/common/outer/http-command.js',
|
||||
'/common/outer/local-store.js',
|
||||
'/common/outer/login-block.js',
|
||||
'/customize/login.js',
|
||||
'/customize/messages.js',
|
||||
'/common/common-icons.js',
|
||||
], function (ApiConfig, $, h, Util, Cred, UI, Login, Constants,
|
||||
ServerCommand, LocalStore, Block, Messages, Icons) {
|
||||
ServerCommand, LocalStore, Block, CLogin, Messages, Icons) {
|
||||
if (window.top !== window) { return; }
|
||||
|
||||
let ssoAuthCb = function (cb) {
|
||||
@ -31,6 +32,7 @@ define([
|
||||
publicKey: Util.decodeBase64(b64Keys.p)
|
||||
};
|
||||
var inviteToken = b64Keys.token;
|
||||
CLogin.ssoRedirectTo(b64Keys);
|
||||
ServerCommand(keys, {
|
||||
command: 'SSO_AUTH_CB',
|
||||
url: window.location.href
|
||||
@ -63,7 +65,7 @@ define([
|
||||
return void UI.warn(msg);
|
||||
}
|
||||
LocalStore.setSSOSeed(seed.toLowerCase());
|
||||
window.location.href = '/drive/';
|
||||
CLogin.redirect();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -296,7 +296,7 @@ define([
|
||||
};
|
||||
|
||||
var createLeftside = function () {
|
||||
var $categories = $('<div>', {'class': 'cp-sidebarlayout-categories'})
|
||||
var $categories = $('<div>', {'class': 'cp-sidebarlayout-categories', 'role': 'menu'})
|
||||
.appendTo(APP.$leftside);
|
||||
var metadataMgr = common.getMetadataMgr();
|
||||
var privateData = metadataMgr.getPrivateData();
|
||||
@ -304,10 +304,13 @@ define([
|
||||
if (!categories[active]) { active = 'tickets'; }
|
||||
common.setHash(active);
|
||||
Object.keys(categories).forEach(function (key) {
|
||||
var name = Messages['support_cat_'+key] || key;
|
||||
var $category = $('<div>', {
|
||||
'class': 'cp-sidebarlayout-category',
|
||||
'data-category': key,
|
||||
'tabindex': 0
|
||||
'tabindex': 0,
|
||||
'role': 'menuitem',
|
||||
'aria-label': name
|
||||
}).appendTo($categories);
|
||||
var iconClass = icons[key];
|
||||
if (iconClass) {
|
||||
@ -329,7 +332,7 @@ define([
|
||||
showCategories(categories[key]);
|
||||
});
|
||||
|
||||
$category.append(Messages['support_cat_'+key] || key);
|
||||
$category.append(h('span.cp-sidebarlayout-category-name', name));
|
||||
});
|
||||
showCategories(categories[active]);
|
||||
};
|
||||
|
||||
@ -106,6 +106,7 @@ define([
|
||||
origin: window.location.origin,
|
||||
pathname: window.location.pathname,
|
||||
feedbackAllowed: Utils.Feedback.state,
|
||||
unsafeIframe: true,
|
||||
};
|
||||
for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; }
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user