diff --git a/.gitignore b/.gitignore index 323e7db9e..bac376dcd 100644 --- a/.gitignore +++ b/.gitignore @@ -17,12 +17,11 @@ customize messages.log www/scratch data -pins/ -blob/ -block/ -blobstage/ -block/ -logs/ +pins +blob +block +blobstage +logs privileged.conf config/config.js config/sso.js diff --git a/.reuse/dep5 b/.reuse/dep5 index bd6e25083..227d0b182 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -29,6 +29,10 @@ Files: .stylelintrc.js Copyright: 2023 XWiki CryptPad Team and contributors License: AGPL-3.0-or-later +Files: scripts/tests/test-data/* +Copyright: 2024 XWiki CryptPad Team and contributors +License: AGPL-3.0-or-later + ## Dependencies Files: www/common/theme/* diff --git a/CHANGELOG.md b/CHANGELOG.md index 813930b3b..a07f5aa0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,7 +59,7 @@ This release introduces a new onboarding flow to guide administrators through th - Onboarding screens & app configuration [#1513](https://github.com/cryptpad/cryptpad/pull/1513) - Bahasa Indonesia is a new available language [fe78b6a](https://github.com/cryptpad/cryptpad/commit/fe78b6ab1dc76ce9eb8d5361c309db8e92117fa8) - - Thanks to our [Weblate](https://weblate.cryptpad.org) contributors who made that happen! + - Thanks to our [Weblate](https://weblate.cryptpad.org) contributors who made that happen! ## Improvements @@ -92,7 +92,7 @@ This release introduces a new onboarding flow to guide administrators through th - Switch to new `http2` Nginx option [#1516](https://github.com/cryptpad/cryptpad/pull/1516) - Server fixes and aggregated stats [#1509](https://github.com/cryptpad/cryptpad/pull/1509) - Create the block folder at boot [#911](https://github.com/cryptpad/cryptpad/pull/911) - - Remove obsolete `version` from `docker-compose.yml` [2e716eb](https://github.com/cryptpad/cryptpad/commit/2e716eb4e39fb835f95a1fa1a340e01142d11b1c) + - Remove obsolete `version` from `docker-compose.yml` [2e716eb](https://github.com/cryptpad/cryptpad/commit/2e716eb4e39fb835f95a1fa1a340e01142d11b1c) - Other - Unsharp the corners when hovering the dismiss button on notification drop-down menu [#1466](https://github.com/cryptpad/cryptpad/pull/1466) - Fix contextual menu `Open` on anonymous drive [#1464](https://github.com/cryptpad/cryptpad/pull/1464) diff --git a/Dockerfile b/Dockerfile index e2be11805..c6d343942 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ COPY . /cryptpad RUN sed -i "s@//httpAddress: 'localhost'@httpAddress: '0.0.0.0'@" /cryptpad/config/config.example.js RUN sed -i "s@installMethod: 'unspecified'@installMethod: 'docker'@" /cryptpad/config/config.example.js - + # Install dependencies RUN npm install --production \ && npm run install:components diff --git a/LICENSES/BSD-3-Clause.txt b/LICENSES/BSD-3-Clause.txt index ea890afbc..086d3992c 100644 --- a/LICENSES/BSD-3-Clause.txt +++ b/LICENSES/BSD-3-Clause.txt @@ -1,4 +1,4 @@ -Copyright (c) . +Copyright (c) . Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/LICENSES/MPL-2.0.txt b/LICENSES/MPL-2.0.txt index ee6256cdb..d0a1fa148 100644 --- a/LICENSES/MPL-2.0.txt +++ b/LICENSES/MPL-2.0.txt @@ -35,7 +35,7 @@ Mozilla Public License Version 2.0 means any form of the work other than Source Code Form. 1.7. "Larger Work" - means a work that combines Covered Software with other material, in + means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" diff --git a/customize.dist/messages.js b/customize.dist/messages.js index 7c3965771..a583240a6 100755 --- a/customize.dist/messages.js +++ b/customize.dist/messages.js @@ -38,11 +38,10 @@ var getStoredLanguage = function () { return localStorage && localStorage.getIte var getBrowserLanguage = function () { return navigator.language || navigator.userLanguage || ''; }; var getLanguage = Messages._getLanguage = function () { if (window.cryptpadLanguage) { return window.cryptpadLanguage; } - try { - if (getStoredLanguage()) { return getStoredLanguage(); } - } catch (e) { console.log(e); } var l = getBrowserLanguage(); - // Edge returns 'fr-FR' --> transform it to 'fr' and check again + try { + l = getStoredLanguage() || getBrowserLanguage(); + } catch (e) { console.log(e); } return map[l] ? l : (map[l.split('-')[0]] ? l.split('-')[0] : (map[l.split('_')[0]] ? l.split('_')[0] : 'en')); @@ -135,8 +134,7 @@ define(req, function(AppConfig, Default, Language) { return text; } }; - Messages.admin_mfa_confirm_enable = "Are you sure you want to enable Multi-Factor Authentication?"; // XXX - Messages.admin_mfa_confirm_disable = "Are you sure you want to disable Multi-Factor Authentication?"; // XXX + return Messages; }); diff --git a/customize.dist/pages/features.js b/customize.dist/pages/features.js index 50bc05069..0b863b34e 100644 --- a/customize.dist/pages/features.js +++ b/customize.dist/pages/features.js @@ -28,8 +28,9 @@ define([ var premiumButton = h('a', { href: accounts.upgradeURL, target: '_blank', - rel: 'noopener noreferrer' - }, h('button.cp-features-register-button', Msg.features_f_subscribe)); + rel: 'noopener noreferrer', + class: 'cp-features-register-button', + }, Msg.features_f_subscribe); var groupItemTemplate = function (title, content) { return h('li.list-group-item', [ @@ -119,14 +120,15 @@ define([ h('div.card-body',[ h('div.cp-features-register#cp-features-register', [ h('a', { - href: '/register/' - }, h('button.cp-features-register-button', Msg.features_f_register)) + href: '/register/', + class: 'cp-features-register-button', + }, Msg.features_f_register) ]), ]), ]), ]); var premiumFeatures = - h('div.col-12.col-sm-4.cp-anon-user',[ + h('div.col-12.col-sm-4.cp-premium-user',[ h('div.card',[ h('div.title-card',[ h('h3.text-center',Msg.features_premium) diff --git a/customize.dist/pages/index.js b/customize.dist/pages/index.js index 3b919643f..b732d2558 100644 --- a/customize.dist/pages/index.js +++ b/customize.dist/pages/index.js @@ -153,10 +153,10 @@ define([ if (Pages.areSubscriptionsAllowed() && !LocalStore.getPremium()) { var sub = h('div.cp-sub-prompt', [ h('span', Msg.home_morestorage), - h('a', {href:"/accounts/"}, h('button', [ + h('a', {href:"/accounts/", class:'subscribe-btn'}, [ h('i.fa.fa-ticket'), Msg.features_f_subscribe - ])) + ]) ]); return sub; } else { diff --git a/customize.dist/src/less2/include/alertify.less b/customize.dist/src/less2/include/alertify.less index 0890d8010..acd8fcef1 100644 --- a/customize.dist/src/less2/include/alertify.less +++ b/customize.dist/src/less2/include/alertify.less @@ -200,8 +200,8 @@ } .cp-usergrid-user, textarea, a, .fa-times { outline: none; - &:focus { - outline: @cryptpad_color_brand solid 2px; + &:focus-visible { + outline: @variables_focus_style; } } } @@ -244,8 +244,8 @@ } } outline: none; - &:focus { - outline: @cryptpad_color_brand solid 2px; + &:focus-visible { + outline: @variables_focus_style; } } span.alertify-tabs-active { @@ -275,7 +275,7 @@ .tools_placeholder-color(); outline: none; &:focus-visible { - outline: @cryptpad_color_brand solid 2px; + outline: @variables_focus_style; } } diff --git a/customize.dist/src/less2/include/checkmark.less b/customize.dist/src/less2/include/checkmark.less index 18e2c4ac9..bd2d96e58 100644 --- a/customize.dist/src/less2/include/checkmark.less +++ b/customize.dist/src/less2/include/checkmark.less @@ -129,8 +129,8 @@ box-sizing: border-box; } outline: none; - &:focus { - outline: @cryptpad_color_brand solid 2px; + &:focus-visible { + outline: @variables_focus_style; } } @@ -217,8 +217,8 @@ height: var(--checkmark-dim1); } outline: none; - &:focus { - outline: @cryptpad_color_brand solid 2px; + &:focus-visible { + outline: @variables_focus_style; } } diff --git a/customize.dist/src/less2/include/drive.less b/customize.dist/src/less2/include/drive.less index 424e12519..f9982fe2b 100644 --- a/customize.dist/src/less2/include/drive.less +++ b/customize.dist/src/less2/include/drive.less @@ -30,6 +30,11 @@ .cp-unselectable { .tools_unselectable(); } + .btn-primary{ + &:focus-visible { + outline: @variables_focus_style; + } + } /* local mixins */ @drive_icon-margin: 10px; @@ -329,11 +334,10 @@ overflow: hidden; text-overflow: ellipsis; .leftside-menu-category_main(); - margin: 0; display: flex; align-items: center; cursor: pointer; - margin-left: -5px; + margin: 0 0 2px -5px; padding-left: 5px; .fa, .cptools { display: inline-block; @@ -348,6 +352,9 @@ overflow: hidden; text-overflow: ellipsis; } + &:focus-visible { + outline: @variables_focus_style; + } } } } @@ -373,6 +380,9 @@ padding-left: 20px; .leftside-menu-category_main(); margin: 0; + &:focus-visible { + outline: @variables_focus_style; + } } } } @@ -399,6 +409,10 @@ position: relative; top: -1px; } + &:focus-visible { + outline: @variables_focus_style; + border-radius: @variables_radius_S; + } } .cp-app-drive-tree-docs { box-shadow: @cryptpad_ui_shadow; diff --git a/customize.dist/src/less2/include/forms.less b/customize.dist/src/less2/include/forms.less index 065b9db1d..3c0b206db 100644 --- a/customize.dist/src/less2/include/forms.less +++ b/customize.dist/src/less2/include/forms.less @@ -28,6 +28,9 @@ } &.tui-full-calendar-content { font-size: @colortheme_app-font-size; + &:focus-visible { + outline: @variables_focus_style; + } } &[readonly] { //margin-top:1rem; @@ -138,8 +141,8 @@ color: @cryptpad_text_col; } outline: none; - &:focus { - outline: @cryptpad_color_brand solid 2px; + &:focus-visible { + outline: @variables_focus_style; } } } @@ -148,8 +151,8 @@ background-color: @cp_buttons-cancel; box-sizing: border-box; align-items: center; - padding: 0 6px; - line-height: 36px; + line-height: 20px; + padding: 8px 6px; white-space: nowrap; text-align: center; text-transform: uppercase; @@ -257,8 +260,8 @@ outline: none; - &:focus { - outline: @cryptpad_color_brand solid 2px; + &:focus-visible { + outline: @variables_focus_style; } &::-moz-focus-inner { border: 0; diff --git a/customize.dist/src/less2/include/infopages.less b/customize.dist/src/less2/include/infopages.less index 7050199bf..2635ba6e9 100644 --- a/customize.dist/src/less2/include/infopages.less +++ b/customize.dist/src/less2/include/infopages.less @@ -6,6 +6,7 @@ @import (reference) "./colortheme-all.less"; @import (reference) "./font.less"; +@import (reference) "./variables.less"; @infopages-radius: 5px; @infopages-radius-L: 10px; @@ -46,6 +47,10 @@ body.html { a:hover { opacity: 1; } + a:focus-visible { + outline: @variables_focus_style; + border-radius: @variables_radius; + } border: 0; padding: 0; margin: 0; @@ -231,6 +236,9 @@ body.html { border: 0px; border-radius: @infopages-radius; padding: 0.5em 0.7em; + &:focus-visible { + outline: @variables_focus_style; + } } } .cp-footer-version { @@ -293,6 +301,9 @@ body.html { &:hover { color: @cryptpad_text_col; } + &:focus-visible { + outline: @variables_focus_style; + } i { margin-right: 5px; } diff --git a/customize.dist/src/less2/include/sidebar-layout.less b/customize.dist/src/less2/include/sidebar-layout.less index 5b616e0e8..90eb873ef 100644 --- a/customize.dist/src/less2/include/sidebar-layout.less +++ b/customize.dist/src/less2/include/sidebar-layout.less @@ -47,6 +47,9 @@ align-items: center; .leftside-menu-category_main(); box-shadow: @cryptpad_ui_shadow; + &:focus-visible { + outline: @variables_focus_style; + } } } &.cp-leftside-narrow { diff --git a/customize.dist/src/less2/include/tokenfield.less b/customize.dist/src/less2/include/tokenfield.less index 53adb1ffe..03ec82ff1 100644 --- a/customize.dist/src/less2/include/tokenfield.less +++ b/customize.dist/src/less2/include/tokenfield.less @@ -81,8 +81,8 @@ padding-left: 4px; vertical-align: middle; outline: none; - &:focus { - outline: @cryptpad_color_brand solid 2px; + &:focus-visible { + outline: @variables_focus_style; } } .close { diff --git a/customize.dist/src/less2/include/toolbar-history.less b/customize.dist/src/less2/include/toolbar-history.less index d2c854034..27b2552aa 100644 --- a/customize.dist/src/less2/include/toolbar-history.less +++ b/customize.dist/src/less2/include/toolbar-history.less @@ -29,35 +29,6 @@ font: @colortheme_app-font; } - - @media screen and (max-width: 870px) { - flex-flow: column; - .cp-toolbar-history-actions { - width: 100%; - .cp-history-actions-first { - margin-right: 0 !important; - } - } - .cp-toolbar-history-timeline { - width: ~"calc(100% - 20px)"; - margin-right: 10px !important; - } - } - - @media screen and (max-height: 500px) { - padding-top: 0px; - .cp-history-timeline-line { - display: none !important; - } - .cp-toolbar-history-timeline { - width: 100% !important; - margin: 0 !important; - } - .cp-history-timeline-actions { - margin-left: 0 !important; - } - } - &.cp-history-init { padding: 0; height: 32px; @@ -102,8 +73,8 @@ text-transform: uppercase; display: inline-flex; align-items: center; - .fa:not(:last-child) { - margin-right: 5px; + i:not(:last-child) { + margin-right: 0.5rem; } &:hover { background-color: fade(@cp_toolbar-fg, 30%); @@ -293,6 +264,39 @@ left: ~"calc(50% - 6px)"; } } + @media screen and (max-width: 870px) { + flex-flow: column; + .cp-toolbar-history-actions { + width: 100%; + margin: 0.6rem 0; + height: auto; + .cp-history-actions-first { + margin-right: 0 !important; + } + button { + margin: 0.1rem; + } + } + .cp-toolbar-history-timeline { + width: ~"calc(100% - 20px)"; + margin-right: 10px !important; + } + } + + @media screen and (max-height: 500px) { + padding-top: 0px; + .cp-history-timeline-line { + display: none !important; + } + .cp-toolbar-history-timeline { + width: 100% !important; + margin: 0 !important; + } + .cp-history-timeline-actions { + margin-left: 0 !important; + } + } + } } diff --git a/customize.dist/src/less2/include/toolbar.less b/customize.dist/src/less2/include/toolbar.less index 1b43df84e..9d11dde7d 100644 --- a/customize.dist/src/less2/include/toolbar.less +++ b/customize.dist/src/less2/include/toolbar.less @@ -77,8 +77,8 @@ &:hover { background-color: contrast(@cp_toolbar-bg, darken(@cp_toolbar-bg, 5%), lighten(@cp_toolbar-bg, 5%)); } - &:focus { - outline: @cryptpad_color_brand solid 2px; + &:focus-visible { + outline: @variables_focus_style; } } button:nth-of-type(1) { @@ -373,7 +373,7 @@ * { outline-style: none; &:focus-visible { - outline: @cryptpad_color_brand solid 2px; + outline: @variables_focus_style; } } @@ -676,6 +676,10 @@ overflow: hidden; text-overflow: ellipsis; border-collapse: collapse; + &:focus { + margin: 2px; + border-radius: @variables_radius; + } } input { color: @cryptpad_text_col; @@ -983,11 +987,8 @@ height: @toolbar_line-height; display: inline-flex; align-items: center; - .fa, .cptools { - margin-right: 5px; - } - .cp-dropdown-button-title .cp-icon { - margin-left: 5px; + i ~ span { + margin-left: 0.5rem; } &:hover { background-color: fade(@cp_toolbar-bottom-bg, 70%); @@ -1109,15 +1110,6 @@ } } .cp-toolbar-bottom-left { - .cp-toolbar-appmenu, .cp-toolbar-file { - button { - &::before, .fa { - min-width: 20px; - text-align: center; - margin-right: 0; - } - } - } .cp-toolbar-file { order: 1; } diff --git a/customize.dist/src/less2/include/variables.less b/customize.dist/src/less2/include/variables.less index 8fa27773d..1df205e11 100644 --- a/customize.dist/src/less2/include/variables.less +++ b/customize.dist/src/less2/include/variables.less @@ -18,6 +18,8 @@ @variables_shadow: 0 8px 32px 0 @cp_shadow-color; // Rounded corners +@variables_radius_S: 3px; @variables_radius: 5px; @variables_radius_L: 10px; +@variables_focus_style: @cryptpad_color_brand solid 2px; diff --git a/customize.dist/src/less2/pages/page-features.less b/customize.dist/src/less2/pages/page-features.less index e13fc7896..261f74cd7 100644 --- a/customize.dist/src/less2/pages/page-features.less +++ b/customize.dist/src/less2/pages/page-features.less @@ -15,12 +15,19 @@ padding: 20px; } .cp-features-register-button { + text-decoration: none; + text-align: center; + background-color: @cp_buttons-primary; + color: @cryptpad_text_col; font-size: 20px; - color: @cryptpad_color_white; - background: @cryptpad_color_brand; - border-radius: 0; - &:hover { - cursor: pointer; + margin: 10px 0; + border-radius: @infopages-radius; + padding: 10px 20px; + &:hover, &:focus { + background-color: contrast(@cp_buttons-primary-text, darken(@cp_buttons-primary, 10%), lighten(@cp_buttons-primary, 10%)); + } + &:visited { + color: @cryptpad_text_col; } } .cp-features-web { @@ -114,5 +121,10 @@ margin-top: 3em; } } + .cp-premium-user { + @media (max-width:575px) { + margin-top: 3em; + } + } } diff --git a/customize.dist/src/less2/pages/page-index.less b/customize.dist/src/less2/pages/page-index.less index 859e376b1..c8cc5a3da 100644 --- a/customize.dist/src/less2/pages/page-index.less +++ b/customize.dist/src/less2/pages/page-index.less @@ -255,9 +255,10 @@ * { display: block; } - button { - background-color: @cryptpad_color_link; - color: @cryptpad_text_col_inv; + .subscribe-btn { + text-align: center; + background-color: @cp_buttons-primary; + color: @cryptpad_text_col; font-size: 1.4rem; margin: 10px 0; border-radius: @infopages-radius-L; @@ -267,8 +268,7 @@ margin-right: 10px; } &:hover, &:focus { - background-color: @cp_static-card-bg; - color: @cryptpad_text_col; + background-color: contrast(@cp_buttons-primary-text, darken(@cp_buttons-primary, 10%), lighten(@cp_buttons-primary, 10%)); } } } diff --git a/customize.dist/src/less2/pages/page-install.less b/customize.dist/src/less2/pages/page-install.less index 47579bd12..b273677a8 100644 --- a/customize.dist/src/less2/pages/page-install.less +++ b/customize.dist/src/less2/pages/page-install.less @@ -43,13 +43,10 @@ nav { display: flex; - align-items: center; justify-content: flex-end; - } - - @media screen and (max-width: 600px) { - nav .btn-danger { - line-height: inherit; + align-items: flex-end; + .btn-confirm { + white-space: normal; } } diff --git a/customize.dist/src/less2/pages/page-register.less b/customize.dist/src/less2/pages/page-register.less index 9e5d5caee..d32d97817 100644 --- a/customize.dist/src/less2/pages/page-register.less +++ b/customize.dist/src/less2/pages/page-register.less @@ -68,13 +68,10 @@ nav { display: flex; - align-items: center; justify-content: flex-end; - } - - @media screen and (max-width: 600px) { - nav .btn-danger { - line-height: inherit; + align-items: flex-end; + .btn-confirm { + white-space: normal; } } diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 4629fe690..91defb3c4 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -22,9 +22,9 @@ if [ ! -f "$CPAD_CONF" ]; then eg: docker run -v /path/to/config.js:/cryptpad/config/config.js \n\ #################################################################### \n" - cp "$CPAD_HOME"/config/config.example.js "$CPAD_CONF" + cp "$CPAD_HOME"/config/config.example.js "$CPAD_CONF" - sed -i -e "s@\(httpUnsafeOrigin:\).*[^,]@\1 '$CPAD_MAIN_DOMAIN'@" \ + sed -i -e "s@\(httpUnsafeOrigin:\).*[^,]@\1 '$CPAD_MAIN_DOMAIN'@" \ -e "s@\(^ *\).*\(httpSafeOrigin:\).*[^,]@\1\2 '$CPAD_SANDBOX_DOMAIN'@" "$CPAD_CONF" fi diff --git a/docs/community/README.md b/docs/community/README.md new file mode 100644 index 000000000..987251ee6 --- /dev/null +++ b/docs/community/README.md @@ -0,0 +1,10 @@ + + +# Community configuration files +This folder is listing community-contributed configuration files. They **aren't** supported and are provided as-is, without any warranty. + +If you are using CryptPad in production and require professional support please contact sales@cryptpad.org diff --git a/docs/community/example-advanced.caddy.conf b/docs/community/example-advanced.caddy.conf new file mode 100644 index 000000000..2aecda43e --- /dev/null +++ b/docs/community/example-advanced.caddy.conf @@ -0,0 +1,245 @@ +# SPDX-FileCopyrightText: 2024 XWiki CryptPad Team and contributors +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +# This file is included strictly as an example of how Caddy can be configured +# to work with CryptPad. This example WILL NOT WORK AS IS. For best results, +# compare the sections of this configuration file against a working CryptPad +# installation (http server by the Nodejs process). If you are using CryptPad +# in production and require professional support please contact sales@cryptpad.org + +(trustedProxies) { + # Force Caddy to accept `X-Forwarded-For` and other origin headers. + # Modify the line below if you want to restrict the scope of direct downstream sending these headers. + trusted_proxies 0.0.0.0/0 ::/0 +} + +# Caddy does not have variables for server names, so domains need to be hardcoded. +# You can bulk replace "your-main-domain.com" and "your-sandbox-domain.com" safely. +your-main-domain.com:443, +your-sandbox-domain.com:443 { + # Define your certificates below. + # No need to adjust TLS configurations, as the defaults in Caddy are already secure. + tls /path/to/fullchain/publicKey.pem /path/to/certificate/privateKey.pem + + # Enable HSTS. + # Do not enable this line when configuring over mixnet, e.g. Tor. + header Strict-Transport-Security "max-age=63072000; includeSubDomains" + + # Security headers + header X-XSS-Protection "1; mode=block" + header X-Content-Type-Options "nosniff" + header Access-Control-Allow-Credentials "true" + #header X-Frame-Options "SAMEORIGIN" + + # OnlyOffice fonts may be loaded from both domains. + @onlyOfficeFonts { + path_regexp "^\\/common\\/onlyoffice\\/.*\\/fonts\\/.*$" + } + header Access-Control-Allow-Origin "*" + + # By default CryptPad forbids remote domains from embedding CryptPad documents in iframes. + # The sandbox domain must always be permitted in order for the platform to function. + # If you wish to enable remote embedding you may change the value below to "*" + # as per the commented value. + header ?Access-Control-Allow-Origin "https://your-sandbox-domain.com" + #header ?Access-Control-Allow-Origin "*" + + # Opt out of Google's FLoC Network + header Permissions-Policy "interest-cohort=()" + + # Enable SharedArrayBuffer in Firefox (for .xlsx export) + header ?Cross-Origin-Resource-Policy "cross-origin" + header ?Cross-Origin-Embedder-Policy "require-corp" + + # Specify the relative path to root of your custom error page. + # This error page won't only be served for 404 errors. + handle_errors { + rewrite * /error.htm + header Cache-Control "no-cache, no-store" + file_server + templates + } + + # Insert the path to your CryptPad repository root here + root /home/cryptpad/cryptpad + + # Any static assets loaded with "vers=" in their URL will be cached for a year + @staticAssets { + query "ver=*" + } + header @staticAssets Cache-Control "max-age=31536000" + + vars { + # CSS can be dynamically set inline, loaded from the same domain, or from your main domain. + styleSrc "'unsafe-inline' 'self' https://your-main-domain.com" + + # connect-src restricts URLs which can be loaded using script interfaces. + # If you have configured your instance to use a dedicated file delivery domain or API domain, + # you will need to add them below. + connectSrc "'self' https://your-main-domain.com blob: wss://api.your-main-domain.com https://your-sandbox-domain.com" + + # Fonts can be loaded from data-URLs or the main domain. + fontSrc "'self' data: https://your-main-domain.com" + + # Images can be loaded from anywhere, though we'd like to deprecate this as it allows + # the use of images for tracking. + imgSrc "'self' data: blob: https://your-main-domain.com" + + # frame-src specifies valid sources for nested browsing contexts. + # This prevents loading any iframes from anywhere other than the sandbox domain. + frameSrc "'self' https://your-sandbox-domain.com blob:" + + # media-src specifies valid sources for loading media using video or audio. + mediaSrc "blob:" + + # child-src defines valid sources for webworkers and nested browser contexts. + # It is deprecated in favour of worker-src and frame-src. + childSrc "https://your-main-domain.com" + + # worker-src valid sources for Worker, Shared Worker, or Service Worker scripts. + # Supercedes child-src, but is unfortunately not yet universally supported. + workerSrc "'self'" + + # script-src specifies valid sources for JavaScript, including inline handlers. + scriptSrc "'self' resource: https://your-main-domain.com" + + # frame-ancestors specifies which origins can embed your CryptPad instance. + # This must include 'self' and your main domain (over HTTPS) in order for CryptPad to work, + # if you have enabled remote embedding via the admin panel, then this must be more permissive. + # Note: cryptpad.fr permits web pages served via https: and vector: (element desktop app) + frameAncestors "'self' https://your-main-domain.com" + #frameAncestors "'self' https: vector:" + + # A few assets are loaded via the sandbox domain. + # They unfortunately still require exceptions to the sandboxing to work correctly. + # Everything except the sandbox domain is a privileged scope, as they might be used to handle keys. + # Unsafe iframes are exceptions. Office file formats are converted outside of the sandboxed scope, + # because of bugs in Chromium-based browsers that incorrectly ignore headers supposed to enable + # the use of some modern APIs, that are required when JavaScript is run in a cross-origin context. + # We've applied other sandboxing techniques to mitigate the risk of running WebAssembly + # in this privileged scope. + # Privileged contexts allow a few more rights than unprivileged contexts, though limits are still applied. + scriptSrcUnsafe "'self' 'unsafe-eval' 'unsafe-inline' resource: https://your-main-domain.com" + } + + # Finally, set all the security rules you have composed above. + @privilegedScope1 { + host "your-sandbox-domain.com" + path_regexp "^\\/(sheet|doc|presentation)\\/inner.html.*$" + } + @privilegedScope2 { + host "your-sandbox-domain.com" + path_regexp "^\\/common\\/onlyoffice\\/.*\\/.*\\.html.*$" + } + @privilegedScope3 { + host "your-sandbox-domain.com" + path_regexp "^\\/unsafeiframe\\/inner\\.html.*$" + } + header @privilegedScope1 Content-Security-Policy "default-src 'none'; child-src {vars.childSrc}; worker-src {vars.workerSrc}; media-src {vars.mediaSrc}; style-src {vars.styleSrc}; script-src {vars.scriptSrcUnsafe}; connect-src {vars.connectSrc}; font-src {vars.fontSrc}; img-src {vars.imgSrc}; frame-src {vars.frameSrc}; frame-ancestors {vars.frameAncestors}" + header @privilegedScope2 Content-Security-Policy "default-src 'none'; child-src {vars.childSrc}; worker-src {vars.workerSrc}; media-src {vars.mediaSrc}; style-src {vars.styleSrc}; script-src {vars.scriptSrcUnsafe}; connect-src {vars.connectSrc}; font-src {vars.fontSrc}; img-src {vars.imgSrc}; frame-src {vars.frameSrc}; frame-ancestors {vars.frameAncestors}" + header @privilegedScope3 Content-Security-Policy "default-src 'none'; child-src {vars.childSrc}; worker-src {vars.workerSrc}; media-src {vars.mediaSrc}; style-src {vars.styleSrc}; script-src {vars.scriptSrcUnsafe}; connect-src {vars.connectSrc}; font-src {vars.fontSrc}; img-src {vars.imgSrc}; frame-src {vars.frameSrc}; frame-ancestors {vars.frameAncestors}" + header ?Content-Security-Policy "default-src 'none'; child-src {vars.childSrc}; worker-src {vars.workerSrc}; media-src {vars.mediaSrc}; style-src {vars.styleSrc}; script-src {vars.scriptSrc}; connect-src {vars.connectSrc}; font-src {vars.fontSrc}; img-src {vars.imgSrc}; frame-src {vars.frameSrc}; frame-ancestors {vars.frameAncestors}" + + # Add support for .mjs files used by pdfjs + @fileModuleJS { + path "*.mjs" + } + header @fileModuleJS Content-Type "application/javascript" + + # The Node.js process can handle all traffic, whether accessed over websocket or as static assets. + # We prefer to serve static content from Caddy directly, and to leave the API server to handle the + # the dynamic content that only it can manage. This is primarily for optimization. + handle /cryptpad_websocket/* { + reverse_proxy * { + to 127.0.0.1:3003 + header_up Host "{host}" + header_up X-Real-IP "{remote_host}" + + # Caddy supports WebSockets directly. No additional headers are needed. + + import trustedProxies + } + } + + handle_path /customize.dist/* { + # This is needed in order to prevent infinite recursion between /customize/ and the root. + } + + # Try to load customizeable content via /customize/ and fall back to the default content located + # at /customize.dist/ . + # This is what allows you to override behaviour. + handle_path /customize/* { + try_files /customize/{path} /customize.dist/{path} + file_server { + index index.html index.htm default.html default.htm + } + } + + # /api/config is loaded once per page load, and is used to retrieve the caching variable, + # which is applied to every other resource loaded during that session. + @sharedReverseProxy { + path /api/* + path /extensions.js + } + handle @sharedReverseProxy { + reverse_proxy * { + to 127.0.0.1:3000 + header_up Host "{host}" + header_up X-Real-IP "{remote_host}" + + # These settings prevent both Caddy and the API server from setting duplicate headers. + header_down Cross-Origin-Resource-Policy cross-origin + header_down Cross-Origin-Embedder-Policy require-corp + + import trustedProxies + } + } + + # Requests for blobs and blocks are now proxied to the API server. + # This simplifies Caddy path configuration, in the event they are being hosted in a non-standard location + # or with odd unexpected permissions. Serving blobs in this manner also means that it will be possible to + # enforce access control for them, though this is not yet implemented. + # Access control (via TOTP 2FA) has been added to blocks, so they can be handled with the same directives. + @blobsAndBlocks { + path /blob/* + path /block/* + } + handle @blobsAndBlocks { + @corsPreflight { + method OPTIONS + } + handle @corsPreflight { + header Access-Control-Allow-Origin "https://your-sandbox-domain.com" + header Access-Control-Allow-Credentials "true" + header Access-Control-Allow-Methods "GET, POST, OPTIONS" + header Access-Control-Allow-Headers "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range" + header Access-Control-Max-Age "1728000" + header Content-Type "application/octet-stream; charset=utf-8" + header Content-Length "0" + respond 204 + } + reverse_proxy * { + to 127.0.0.1:3000 + # Preventing these headers from getting duplicated, since we are proxying to the API server. + header_down -X-Content-Type-Options + header_down -Access-Control-Allow-Origin + header_down -Permissions-Policy + header_down -X-XSS-Protection + header_down -Cross-Origin-Resource-Policy + header_down -Cross-Origin-Embedder-Policy + } + } + + # The Node.JS server has some built-in forwarding rulesets to prevent URLs not suffixed with a slash + # from resulting in a 404 error. This simply adds a trailing slash to a variety of applications. + @preventNotFound { + path_regexp "^/(register|login|recovery|settings|user|pad|drive|poll|slide|code|whiteboard|file|media|profile|contacts|todo|filepicker|debug|kanban|sheet|support|admin|notifications|teams|calendar|presentation|doc|form|report|convert|checkup|diagram)$" + } + redir @preventNotFound "{path}/" + + # Enable file serving + file_server { + index index.html index.htm default.html default.htm + } +} diff --git a/docs/community/example.caddy.conf b/docs/community/example.caddy.conf new file mode 100644 index 000000000..1f17cb9fc --- /dev/null +++ b/docs/community/example.caddy.conf @@ -0,0 +1,64 @@ +# SPDX-FileCopyrightText: 2024 XWiki CryptPad Team and contributors +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +# This file is included strictly as an example of how Caddy can be configured +# to work with CryptPad. This example WILL NOT WORK AS IS. For best results, +# compare the sections of this configuration file against a working CryptPad +# installation (http server by the Nodejs process). If you are using CryptPad +# in production and require professional support please contact sales@cryptpad.org + +(trustedProxies) { + # Force Caddy to accept `X-Forwarded-For` and other origin headers. + # Modify the line below if you want to restrict the scope of direct downstream sending these headers. + trusted_proxies 0.0.0.0/0 ::/0 +} + +# Caddy does not have variables for server names, so domains need to be hardcoded. +# You can bulk replace "your-main-domain.com" and "your-sandbox-domain.com" safely. +your-main-domain.com:443, +your-sandbox-domain.com:443 { + # Define your certificates below. + # No need to adjust TLS configurations, as the defaults in Caddy are already secure. + tls /path/to/fullchain/publicKey.pem /path/to/certificate/privateKey.pem + + # Enable HSTS. + # Do not enable this line when configuring over mixnet, e.g. Tor. + header Strict-Transport-Security "max-age=63072000; includeSubDomains" + + # Specify the relative path to root of your custom error page. + # This error page won't only be served for 404 errors. + handle_errors { + rewrite * /error.htm + header Cache-Control "no-cache, no-store" + file_server + templates + } + + # The Node.js process can handle all traffic, whether accessed over websocket or as static assets. + reverse_proxy /cryptpad_websocket/* { + to 127.0.0.1:3003 + header_up Host "{host}" + header_up X-Real-IP "{remote_host}" + + # Caddy supports WebSockets directly. No additional headers are needed. + + import trustedProxies + } + reverse_proxy * { + to 127.0.0.1:3000 + header_up Host "{host}" + header_up X-Real-IP "{remote_host}" + + # These settings prevent both Caddy and the API server from setting duplicate headers. + header_down Cross-Origin-Resource-Policy cross-origin + header_down Cross-Origin-Embedder-Policy require-corp + + import trustedProxies + } + + # Enable file serving + file_server { + index index.html index.htm default.html default.htm + } +} diff --git a/docs/example.httpd.conf b/docs/community/example.httpd.conf similarity index 99% rename from docs/example.httpd.conf rename to docs/community/example.httpd.conf index 28654df76..34c3bd2e9 100644 --- a/docs/example.httpd.conf +++ b/docs/community/example.httpd.conf @@ -4,7 +4,7 @@ # This file is included strictly as an example of how Apache httpd can be # configured to work with CryptPad. If you are using CryptPad in production -# and require professional support please contact sales@cryptpad.fr +# and require professional support please contact sales@cryptpad.org # This configuration requires mod_ssl, mod_socache_shmcb, mod_proxy, # mod_proxy_http and mod_headers diff --git a/docs/example-advanced.nginx.conf b/docs/example-advanced.nginx.conf index 556829935..00bec9369 100644 --- a/docs/example-advanced.nginx.conf +++ b/docs/example-advanced.nginx.conf @@ -8,6 +8,21 @@ # installation (http server by the Nodejs process). If you are using CryptPad # in production and require professional support please contact sales@cryptpad.fr +server { + listen 80; + listen [::]:80; + server_name your-main-domain.com your-sandbox-domain.com; + + access_log /dev/null; + error_log /dev/null emerg; + + # Let's Encrypt webroot + include letsencrypt-webroot; + + # Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response. + return 301 https://$host$request_uri; +} + server { listen 443 ssl; listen [::]:443 ssl; diff --git a/docs/example.nginx.conf b/docs/example.nginx.conf index dd61615a1..eae30310a 100644 --- a/docs/example.nginx.conf +++ b/docs/example.nginx.conf @@ -8,6 +8,21 @@ # installation (http server by the Nodejs process). If you are using CryptPad # in production and require professional support please contact sales@cryptpad.fr +server { + listen 80; + listen [::]:80; + server_name your-main-domain.com your-sandbox-domain.com; + + access_log /dev/null; + error_log /dev/null emerg; + + # Let's Encrypt webroot + include letsencrypt-webroot; + + # Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response. + return 301 https://$host$request_uri; +} + server { listen 443 ssl; listen [::]:443 ssl; diff --git a/lib/api.js b/lib/api.js index c0e570095..bccf917f8 100644 --- a/lib/api.js +++ b/lib/api.js @@ -190,6 +190,9 @@ nThen(function (w) { removed++; } }); + if (Env.store) { + Env.store.closeInactiveChannels(active); + } if (removed) { Env.Log.info("CLEANED_ACTIVE_CHANNELS_MAP", {removed}); } diff --git a/lib/hk-util.js b/lib/hk-util.js index 7080d3d4a..93c3f9c0e 100644 --- a/lib/hk-util.js +++ b/lib/hk-util.js @@ -146,6 +146,7 @@ const dropChannel = HK.dropChannel = function (Env, chanName) { expireChannel(Env, chanName); }, TEMPORARY_CHANNEL_LIFETIME); } + if (Env.store) { Env.store.closeChannel(chanName, function () {}); } }; /* checkExpired diff --git a/lib/storage/file.js b/lib/storage/file.js index 2c2888f7a..ff57d922b 100644 --- a/lib/storage/file.js +++ b/lib/storage/file.js @@ -286,6 +286,16 @@ var closeChannel = function (env, channelName, cb) { } }; +var closeInactiveChannels = function (env, schedule, active) { + Object.keys(env.channels).forEach(channelName => { + if (!active.includes(channelName)) { + schedule.ordered(channelName, function (next) { + closeChannel(env, channelName, next); + }); + } + }); +}; + var clearOffset = function (env, channelId, cb) { var path = mkOffsetPath(env, channelId); // we should always be able to recover from invalid offsets, so failure to delete them @@ -429,7 +439,27 @@ How to proceed */ + let requiresChannel = true; + let all = []; nThen(function (w) { + let first = true; + getDedicatedMetadata(env, channelId, (err, line) => { + if (first && !err) { + if (!Array.isArray(line)) { + requiresChannel = false; + } + first = false; + } + all.push({err, line}); + }, w(function (err) { + if (err) { + // stream errors? + w.abort(); + return void cb(err); + } + })); + }).nThen(function (w) { + if (!requiresChannel) { return; } // returns the first line of a channel, parsed... getChannelMetadata(env, channelId, w(function (err, data) { if (err) { @@ -445,13 +475,10 @@ How to proceed handler(null, data); })); }).nThen(function () { - getDedicatedMetadata(env, channelId, handler, function (err) { - if (err) { - // stream errors? - return void cb(err); - } - cb(); + all.forEach(({err, line}) => { + handler(err, line); }); + cb(); }); }; @@ -1445,6 +1472,9 @@ module.exports.create = function (conf, _cb) { closeChannel(env, channelName, Util.both(cb, next)); }); }, + closeInactiveChannels: function (active) { + closeInactiveChannels(env, schedule, active); + }, // write to a log file log: function (channelName, content, cb) { // you probably want the events in your log to be in the correct order. diff --git a/package-lock.json b/package-lock.json index a581c4a5a..d4f69af57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cryptpad", - "version": "2024.6.1", + "version": "2024.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cryptpad", - "version": "2024.6.1", + "version": "2024.9.0", "license": "AGPL-3.0+", "dependencies": { "@mcrowe/minibloom": "^0.2.0", @@ -19,7 +19,7 @@ "chainpad-crypto": "^0.2.5", "chainpad-listmap": "^1.1.0", "chainpad-netflux": "^1.2.0", - "chainpad-server": "^5.2.0", + "chainpad-server": "^5.2.2", "ckeditor": "npm:ckeditor4@~4.22.1", "codemirror": "^5.19.0", "components-font-awesome": "^4.6.3", @@ -27,7 +27,7 @@ "croppie": "^2.5.0", "dragula": "3.7.2", "drawio": "github:cryptpad/drawio-npm#npm-21.8.2+5", - "express": "~4.19.2", + "express": "~4.20.0", "file-saver": "1.3.1", "fs-extra": "^7.0.0", "get-folder-size": "^2.0.1", @@ -41,7 +41,7 @@ "localforage": "^1.5.2", "marked": "^4.3.0", "mathjax": "3.0.5", - "netflux-websocket": "^1.2.0", + "netflux-websocket": "^1.2.1", "notp": "^2.0.3", "nthen": "0.1.8", "open-sans-fontface": "^1.4.0", @@ -1203,9 +1203,9 @@ } }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -1215,7 +1215,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -1225,6 +1225,20 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/bootstrap": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz", @@ -1326,22 +1340,27 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001591", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz", - "integrity": "sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==", + "version": "1.0.30001660", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", + "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==", "dev": true, "funding": [ { @@ -1396,9 +1415,9 @@ } }, "node_modules/chainpad-server": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/chainpad-server/-/chainpad-server-5.2.1.tgz", - "integrity": "sha512-r6kNMCbMuwUMxsFIZJgyXtdGxwoXm+gFVIUBxKLOzhvbeMm0RSBYa6BJ5qgzYIwUhYgqyUxmm9tXzYecmPUHIA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/chainpad-server/-/chainpad-server-5.2.2.tgz", + "integrity": "sha512-fUCffhnd8PlO92A6cYH5lSIiiI6bpJymP0yC3O2KI+4NPyxFK7I2iuadMrC7RzK3eAI1w72Y1nbMN8rQImppoQ==", "dependencies": { "nthen": "0.1.8", "pull-stream": "^3.6.9", @@ -1684,16 +1703,19 @@ "dev": true }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-property": { @@ -1825,6 +1847,25 @@ "node": ">=0.10.0" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -2144,36 +2185,36 @@ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.20.0.tgz", + "integrity": "sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.2.0", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -2184,6 +2225,14 @@ "node": ">= 0.10.0" } }, + "node_modules/express/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", @@ -2416,15 +2465,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2588,20 +2641,20 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "engines": { "node": ">= 0.4" }, @@ -3326,9 +3379,12 @@ } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge2": { "version": "1.4.1", @@ -3520,9 +3576,9 @@ } }, "node_modules/netflux-websocket": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/netflux-websocket/-/netflux-websocket-1.2.0.tgz", - "integrity": "sha512-CNQF1wd/UOCmnfaWDATi5vTMfIpMm0Zghy1Fkvr5p7Q70wJdT85RdtnQvsqfLU5AXMUKwjeP1GgxMyAAdRUeJA==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/netflux-websocket/-/netflux-websocket-1.2.1.tgz", + "integrity": "sha512-8oQOyEyh0MCnifNIfyvkNXpEELlVRzDleRivRgsDGh+5ZDG109axweVZ0RIOIpkQmTZk4/xVsBk/oLXpMSWEOw==" }, "node_modules/node-releases": { "version": "2.0.14", @@ -3791,9 +3847,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "node_modules/picocolors": { "version": "1.0.1", @@ -4401,9 +4457,9 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -4429,9 +4485,9 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.0.tgz", + "integrity": "sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==", "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", @@ -4442,15 +4498,45 @@ "node": ">= 0.8.0" } }, - "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "node_modules/serve-static/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static/node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4512,13 +4598,17 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" diff --git a/package.json b/package.json index a41e632d0..a54f753f4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cryptpad", "description": "a collaborative office suite that is end-to-end encrypted and open-source", - "version": "2024.6.1", + "version": "2024.9.0", "license": "AGPL-3.0+", "repository": { "type": "git", @@ -22,7 +22,7 @@ "chainpad-crypto": "^0.2.5", "chainpad-listmap": "^1.1.0", "chainpad-netflux": "^1.2.0", - "chainpad-server": "^5.2.0", + "chainpad-server": "^5.2.2", "ckeditor": "npm:ckeditor4@~4.22.1", "codemirror": "^5.19.0", "components-font-awesome": "^4.6.3", @@ -30,7 +30,7 @@ "croppie": "^2.5.0", "dragula": "3.7.2", "drawio": "github:cryptpad/drawio-npm#npm-21.8.2+5", - "express": "~4.19.2", + "express": "~4.20.0", "file-saver": "1.3.1", "fs-extra": "^7.0.0", "get-folder-size": "^2.0.1", @@ -44,7 +44,7 @@ "localforage": "^1.5.2", "marked": "^4.3.0", "mathjax": "3.0.5", - "netflux-websocket": "^1.2.0", + "netflux-websocket": "^1.2.1", "notp": "^2.0.3", "nthen": "0.1.8", "open-sans-fontface": "^1.4.0", diff --git a/readme.md b/readme.md index d5766eed6..f24355c3d 100644 --- a/readme.md +++ b/readme.md @@ -28,7 +28,7 @@ The most recent version and all past release notes can be found on the [releases ## Setup using Docker -You can find `Dockerfile`, `docker-compose.yml` and `docker-entrypoint.sh` files at the root of this repository. We also publish every release on [Docker Hub](https://hub.docker.com/r/cryptpad/cryptpad) as AMD64 & ARM64 official images. +You can find `Dockerfile`, `docker-compose.yml` and `docker-entrypoint.sh` files at the root of this repository. We also publish every release on [Docker Hub](https://hub.docker.com/r/cryptpad/cryptpad) as AMD64 & ARM64 official images. Previously, Docker images were community maintained, had their own repository and weren't official supported. We changed that with v5.4.0 during July 2023. Thanks to @promasu for all the work on the community images. diff --git a/scripts/convert-favicons-to-ico.sh b/scripts/convert-favicons-to-ico.sh index 6f6980876..972b93df7 100755 --- a/scripts/convert-favicons-to-ico.sh +++ b/scripts/convert-favicons-to-ico.sh @@ -5,6 +5,6 @@ # SPDX-License-Identifier: AGPL-3.0-or-later for f in ../customize.dist/favicon/*.png; do - base="$(basename $f ".png")" - magick convert $f -define icon:auto-resize=16,24,32,48,64,72,96,128,256 "$base.ico" + base="$(basename $f ".png")" + magick convert $f -define icon:auto-resize=16,24,32,48,64,72,96,128,256 "$base.ico" done diff --git a/scripts/tests/test-data/00/00000000000000000000000000000000.ndjson b/scripts/tests/test-data/00/00000000000000000000000000000000.ndjson new file mode 100644 index 000000000..cdbaaefa2 --- /dev/null +++ b/scripts/tests/test-data/00/00000000000000000000000000000000.ndjson @@ -0,0 +1,7 @@ +{"owners":["TestOwner"],"validateKey":"TestKey","channel":"0","created":1721035117462} +[0,"test","MSG","test",1721035117969] +[0,"test","MSG","test2",1721035117969] +[0,"test","MSG","test3",1721035117969] +[0,"test","MSG","test4",1721035117969] +[0,"test","MSG","test5",1721035117969] +[0,"test","MSG","test6",1721035117969] diff --git a/scripts/tests/test-data/00/00000000000000000000000000000001.metadata.ndjson b/scripts/tests/test-data/00/00000000000000000000000000000001.metadata.ndjson new file mode 100644 index 000000000..1cafb44c2 --- /dev/null +++ b/scripts/tests/test-data/00/00000000000000000000000000000001.metadata.ndjson @@ -0,0 +1,5 @@ +["RESTRICT_ACCESS",[true],1721035145090] +["ADD_ALLOWED",["NewAllowedKeyNewAllowedKeyNewAllowedKeyNewAl"],1721035148115] +["ADD_PENDING_OWNERS",["PendingOwner"],1721035151295] +["RESTRICT_ACCESS",[false],1721035155836] +["RESTRICT_ACCESS",[true],1721035156728] diff --git a/scripts/tests/test-data/00/00000000000000000000000000000001.ndjson b/scripts/tests/test-data/00/00000000000000000000000000000001.ndjson new file mode 100644 index 000000000..cdbaaefa2 --- /dev/null +++ b/scripts/tests/test-data/00/00000000000000000000000000000001.ndjson @@ -0,0 +1,7 @@ +{"owners":["TestOwner"],"validateKey":"TestKey","channel":"0","created":1721035117462} +[0,"test","MSG","test",1721035117969] +[0,"test","MSG","test2",1721035117969] +[0,"test","MSG","test3",1721035117969] +[0,"test","MSG","test4",1721035117969] +[0,"test","MSG","test5",1721035117969] +[0,"test","MSG","test6",1721035117969] diff --git a/scripts/tests/test-data/00/00000000000000000000000000000002.metadata.ndjson b/scripts/tests/test-data/00/00000000000000000000000000000002.metadata.ndjson new file mode 100644 index 000000000..7c47d09d3 --- /dev/null +++ b/scripts/tests/test-data/00/00000000000000000000000000000002.metadata.ndjson @@ -0,0 +1 @@ +{"owners":["TestOwner"],"validateKey":"TestKey","channel":"0","created":1721035117462} diff --git a/scripts/tests/test-data/00/00000000000000000000000000000002.ndjson b/scripts/tests/test-data/00/00000000000000000000000000000002.ndjson new file mode 100644 index 000000000..99973c454 --- /dev/null +++ b/scripts/tests/test-data/00/00000000000000000000000000000002.ndjson @@ -0,0 +1,6 @@ +[0,"test","MSG","test",1721035117969] +[0,"test","MSG","test2",1721035117969] +[0,"test","MSG","test3",1721035117969] +[0,"test","MSG","test4",1721035117969] +[0,"test","MSG","test5",1721035117969] +[0,"test","MSG","test6",1721035117969] diff --git a/scripts/tests/test-data/00/00000000000000000000000000000003.metadata.ndjson b/scripts/tests/test-data/00/00000000000000000000000000000003.metadata.ndjson new file mode 100644 index 000000000..ffcb25082 --- /dev/null +++ b/scripts/tests/test-data/00/00000000000000000000000000000003.metadata.ndjson @@ -0,0 +1,6 @@ +{"owners":["TestOwner"],"validateKey":"TestKey","channel":"0","created":1721035117462} +["RESTRICT_ACCESS",[true],1721035145090] +["ADD_ALLOWED",["NewAllowedKeyNewAllowedKeyNewAllowedKeyNewAl"],1721035148115] +["ADD_PENDING_OWNERS",["PendingOwner"],1721035151295] +["RESTRICT_ACCESS",[false],1721035155836] +["RESTRICT_ACCESS",[true],1721035156728] diff --git a/scripts/tests/test-data/00/00000000000000000000000000000003.ndjson b/scripts/tests/test-data/00/00000000000000000000000000000003.ndjson new file mode 100644 index 000000000..99973c454 --- /dev/null +++ b/scripts/tests/test-data/00/00000000000000000000000000000003.ndjson @@ -0,0 +1,6 @@ +[0,"test","MSG","test",1721035117969] +[0,"test","MSG","test2",1721035117969] +[0,"test","MSG","test3",1721035117969] +[0,"test","MSG","test4",1721035117969] +[0,"test","MSG","test5",1721035117969] +[0,"test","MSG","test6",1721035117969] diff --git a/scripts/tests/test-data/00/00000000000000000000000000000004.ndjson b/scripts/tests/test-data/00/00000000000000000000000000000004.ndjson new file mode 100644 index 000000000..99973c454 --- /dev/null +++ b/scripts/tests/test-data/00/00000000000000000000000000000004.ndjson @@ -0,0 +1,6 @@ +[0,"test","MSG","test",1721035117969] +[0,"test","MSG","test2",1721035117969] +[0,"test","MSG","test3",1721035117969] +[0,"test","MSG","test4",1721035117969] +[0,"test","MSG","test5",1721035117969] +[0,"test","MSG","test6",1721035117969] diff --git a/scripts/tests/test-metadata.js b/scripts/tests/test-metadata.js new file mode 100644 index 000000000..08857dba6 --- /dev/null +++ b/scripts/tests/test-metadata.js @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: 2023 XWiki CryptPad Team and contributors +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +const Store = require("../../lib/storage/file"); +const Meta = require("../../lib/metadata"); +const nThen = require('nthen'); + +let chanOld = '00000000000000000000000000000000'; +let chanOldUpdated = '00000000000000000000000000000001'; +let chanNew = '00000000000000000000000000000002'; +let chanNewUpdated = '00000000000000000000000000000003'; +let chanNoMeta = '00000000000000000000000000000004'; + +Store.create({ + filePath: './test-data/' +}, (err, store) => { + if (err) { return void console.error(err); } + + const readMetadata = (channel, cb) => { + const ref = {}; + const h = Meta.createLineHandler(ref, console.error); + store.readChannelMetadata(channel, h, () => { + cb(ref && ref.meta); + }); + }; + + nThen(w => { + readMetadata(chanOld, w(meta => { + if (!meta || meta.validateKey !== "TestKey") { + console.log('OldChanNoUpdate', meta); + throw new Error("Error with old channel without metadata update"); + } + })); + readMetadata(chanOldUpdated, w(meta => { + if (!meta || meta.validateKey !== "TestKey" || !meta.restricted || !meta.allowed.includes('NewAllowedKeyNewAllowedKeyNewAllowedKeyNewAl')) { + console.log('OldChanUpdate', meta); + throw new Error("Error with old channel with metadata updates"); + } + })); + readMetadata(chanNew, w(meta => { + if (!meta || meta.validateKey !== "TestKey") { + console.log('NewChanNoUpdate', meta); + throw new Error("Error with new channel without metadata update"); + } + })); + readMetadata(chanNewUpdated, w(meta => { + if (!meta || meta.validateKey !== "TestKey" || !meta.restricted || !meta.allowed.includes('NewAllowedKeyNewAllowedKeyNewAllowedKeyNewAl')) { + console.log('NewChanUpdate', meta); + throw new Error("Error with new channel with metadata updates"); + } + })); + readMetadata(chanNoMeta, w(meta => { + if (meta && Object.keys(meta).length) { + console.log('NoMetadataChan', meta); + throw new Error("Error with channel without metadata"); + } + })); + }).nThen(() => { + console.log('Success'); + process.exit(1); + }); +}); + diff --git a/www/admin/app-admin.less b/www/admin/app-admin.less index 8bc3c3d85..dfec83a83 100644 --- a/www/admin/app-admin.less +++ b/www/admin/app-admin.less @@ -22,6 +22,11 @@ flex-flow: column; font: @colortheme_app-font; + input, textarea, .cp-appblock { + &:focus-visible { + outline: @variables_focus_style; + } + } .cp-admin-customize-logo { padding: 1em; img { diff --git a/www/calendar/app-calendar.less b/www/calendar/app-calendar.less index 91974d210..2b5db6ca5 100644 --- a/www/calendar/app-calendar.less +++ b/www/calendar/app-calendar.less @@ -39,7 +39,7 @@ .tui-full-calendar-confirm { span, i { - padding: 0.5rem; + padding: 0 0.5rem; } span { padding-left: 0; @@ -133,6 +133,13 @@ width: 540px; max-width: 100%; max-height: 100%; + border-radius: @variables_radius_L; + @media screen and (max-width : @browser_media-medium-screen){ + position: fixed; + top: 5.5rem; + overflow-y: auto; + max-height: calc(100% - 6rem); + } } } #tui-full-calendar-popup-arrow { @@ -153,9 +160,6 @@ border-color: @cp_calendar-border !important; } - .tui-full-calendar-popup { - border-radius: @variables_radius_L; - } .tui-full-calendar-popup-container { .tui-full-calendar-section-allday{ &:focus { @@ -420,10 +424,7 @@ // margin-bottom: 20px; input { width: 80px; - margin-right: 0.3rem; - } - .fa-plus{ - margin-left:0.3rem; + margin-right: 0.5rem; } } } @@ -646,6 +647,9 @@ color: @cp_sidebar-left-active-fg; border: 0px; } + &:focus-visible { + outline: @variables_focus_style; + } i { margin-left: 5px; } diff --git a/www/common/common-interface.js b/www/common/common-interface.js index 19498d83a..fb9ecbe2b 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -780,7 +780,6 @@ define([ UI.confirm = function (msg, cb, opt, force) { cb = cb || function () {}; opt = opt || {}; - var message; if (typeof(msg) === 'string') { if (!force) { msg = Util.fixHTML(msg); } diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 10db8c37c..ae0dea302 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -623,13 +623,22 @@ define([ var appType = (common.getMetadataMgr().getMetadata().type || 'pad').toUpperCase(); data = data || {}; if (!callback && data.callback) { callback = data.callback; } + + let makeButton = function(iconClasses, buttonClasses, title, text) { + const ariaLabel = title || text || ''; + return $(h('button', { + class: buttonClasses, + title: title, + 'aria-label': ariaLabel + }, [ + iconClasses ? h('i', { class: iconClasses }) : null, + text ? h('span', { class: 'cp-toolbar-drawer-element' }, text) : null + ])); + }; + switch (type) { case 'export': - button = $('