mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-23 00:14:40 +00:00
Compare commits
165
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0f3facab8 | ||
|
|
b9512c9e93 | ||
|
|
dd724220f8 | ||
|
|
94410f1839 | ||
|
|
cc8aa00326 | ||
|
|
e8e2fcb0b3 | ||
|
|
151b35355d | ||
|
|
37d5353f77 | ||
|
|
7b6953445f | ||
|
|
fcd3db63ce | ||
|
|
d67085d57d | ||
|
|
1f60d9f5e8 | ||
|
|
5378769467 | ||
|
|
913ed5e109 | ||
|
|
f4cfe19fdc | ||
|
|
a001dfd8a8 | ||
|
|
893a4bcae9 | ||
|
|
e61fb74367 | ||
|
|
fbbbc1cb40 | ||
|
|
f27f3c46c1 | ||
|
|
83b557b6be | ||
|
|
32a0df06d0 | ||
|
|
5350e07f62 | ||
|
|
cc5c46743b | ||
|
|
bc7f3fe7db | ||
|
|
750ca3f39c | ||
|
|
2bbffc3623 | ||
|
|
36dd98127d | ||
|
|
6d55331ef2 | ||
|
|
181e45a596 | ||
|
|
3c8e506805 | ||
|
|
f0a7379575 | ||
|
|
0d12b11c48 | ||
|
|
14a025f769 | ||
|
|
598fc9bbc8 | ||
|
|
fbab2df00f | ||
|
|
48c2d49c86 | ||
|
|
00d68393f6 | ||
|
|
7a50ba0b43 | ||
|
|
b2bee99286 | ||
|
|
88d384afdf | ||
|
|
7af82b831f | ||
|
|
a86f42fa44 | ||
|
|
70b0413777 | ||
|
|
577a77af38 | ||
|
|
3ba7e9ba00 | ||
|
|
b2250e8ce0 | ||
|
|
f2a480c7a0 | ||
|
|
bda38096e1 | ||
|
|
7e66a61294 | ||
|
|
3aa627a467 | ||
|
|
ae1b5fc925 | ||
|
|
b1076f9c86 | ||
|
|
ede1c72595 | ||
|
|
d2fcd5b64f | ||
|
|
6d9704ffa2 | ||
|
|
40df3d015a | ||
|
|
4e22706d92 | ||
|
|
341710f724 | ||
|
|
8386183f7a | ||
|
|
b76a7d6895 | ||
|
|
4a397a8151 | ||
|
|
2239a3e6a3 | ||
|
|
5318368879 | ||
|
|
41a279da9d | ||
|
|
478c69f82c | ||
|
|
e3073a3613 | ||
|
|
3333dc7f22 | ||
|
|
12ec6fbec8 | ||
|
|
034278c0a0 | ||
|
|
92b3a9e1bd | ||
|
|
fc7169f2a0 | ||
|
|
c39710ede0 | ||
|
|
cc0b6aed15 | ||
|
|
f8a83c9cc8 | ||
|
|
43ce64f01e | ||
|
|
d585a03f76 | ||
|
|
f953be6237 | ||
|
|
e81bbe197b | ||
|
|
738fa4da12 | ||
|
|
9a08dcc46d | ||
|
|
74ef8d915d | ||
|
|
025a7a5582 | ||
|
|
3b2ddcf98d | ||
|
|
c10fc0ee5b | ||
|
|
aaa37e2c7a | ||
|
|
7af7c920e3 | ||
|
|
a4f79fb98a | ||
|
|
f429f2d3e7 | ||
|
|
4f6e091c13 | ||
|
|
2df779f7ab | ||
|
|
5b9d9150c5 | ||
|
|
f0aaec2058 | ||
|
|
4adc4a9175 | ||
|
|
5a97060fac | ||
|
|
db96074119 | ||
|
|
7ecc10bc79 | ||
|
|
44fe4a6f8a | ||
|
|
76a4ef50cb | ||
|
|
ca2f80024a | ||
|
|
dcd7077b08 | ||
|
|
31482d7d18 | ||
|
|
5ef666154e | ||
|
|
a810eff797 | ||
|
|
664158eb84 | ||
|
|
6b43881909 | ||
|
|
126004b1f8 | ||
|
|
ba34cfa9d5 | ||
|
|
03b4ad85ef | ||
|
|
1abfcd495f | ||
|
|
69964a80f1 | ||
|
|
aa8306ea4a | ||
|
|
3fecde37f4 | ||
|
|
2fc5fd99fb | ||
|
|
7edacfea70 | ||
|
|
7020fa49d9 | ||
|
|
7cea0bb33c | ||
|
|
3d622b18dc | ||
|
|
62cd506588 | ||
|
|
cd830f27a0 | ||
|
|
3f75018369 | ||
|
|
270a4c594d | ||
|
|
a5fdc5755b | ||
|
|
0a5a554714 | ||
|
|
976f22c27a | ||
|
|
8267574697 | ||
|
|
9077c964b3 | ||
|
|
f80467a3a4 | ||
|
|
8c462c4cb7 | ||
|
|
b25b5bd5a3 | ||
|
|
6365c22297 | ||
|
|
d7dd24b94e | ||
|
|
aee8844558 | ||
|
|
c536149c62 | ||
|
|
ca1ae8181b | ||
|
|
2b19c9757b | ||
|
|
d6871e3d82 | ||
|
|
0432a64537 | ||
|
|
06988b06c7 | ||
|
|
9b72eb8219 | ||
|
|
ea188f6176 | ||
|
|
cf67d022c8 | ||
|
|
7ba6e12799 | ||
|
|
b512518ccc | ||
|
|
93dfd35627 | ||
|
|
50b92f9510 | ||
|
|
7cca8922d8 | ||
|
|
c00889b1c4 | ||
|
|
b6224f355d | ||
|
|
9a8f96d432 | ||
|
|
3cc199227b | ||
|
|
3d0647bce3 | ||
|
|
192f35a9dd | ||
|
|
f2b4c9a35b | ||
|
|
3b8fec4c5a | ||
|
|
60b74a018d | ||
|
|
be69e04f51 | ||
|
|
194f6009df | ||
|
|
910a2eace8 | ||
|
|
3c95618eec | ||
|
|
fc709eb700 | ||
|
|
a6c60ac5d0 | ||
|
|
b88b9c8f92 | ||
|
|
2b8cd36b7b | ||
|
|
3d23deb744 |
@@ -1,7 +1,14 @@
|
|||||||
name: Bug Report
|
name: Bug Report
|
||||||
description: Create a report to help us improve
|
description: Create a report to help us improve
|
||||||
labels: ["type:bug"]
|
type: "Bug"
|
||||||
body:
|
body:
|
||||||
|
- type: input
|
||||||
|
id: summary
|
||||||
|
attributes:
|
||||||
|
label: Summary
|
||||||
|
placeholder: Please summarize your problem.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
id: terms
|
id: terms
|
||||||
attributes:
|
attributes:
|
||||||
@@ -11,13 +18,6 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: I agree to follow this project's [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md)
|
- label: I agree to follow this project's [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md)
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
|
||||||
id: summary
|
|
||||||
attributes:
|
|
||||||
label: Summary
|
|
||||||
placeholder: Please summarize your problem.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: software-versions
|
id: software-versions
|
||||||
attributes:
|
attributes:
|
||||||
@@ -97,4 +97,4 @@ body:
|
|||||||
id: further-info
|
id: further-info
|
||||||
attributes:
|
attributes:
|
||||||
label: Anything else?
|
label: Anything else?
|
||||||
description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue?
|
description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue?
|
||||||
@@ -1,7 +1,14 @@
|
|||||||
name: Feature Request
|
name: Feature Request
|
||||||
description: Suggest an idea for this project
|
description: Suggest an idea for this project
|
||||||
labels: ["type:feature-request"]
|
type: "Feature"
|
||||||
body:
|
body:
|
||||||
|
- type: input
|
||||||
|
id: summary
|
||||||
|
attributes:
|
||||||
|
label: Summary
|
||||||
|
placeholder: Please summarize your feature request.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
id: terms
|
id: terms
|
||||||
attributes:
|
attributes:
|
||||||
@@ -11,13 +18,6 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: I agree to follow this project's [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md)
|
- label: I agree to follow this project's [Code of Conduct](https://github.com/cryptomator/cryptomator/blob/develop/.github/CODE_OF_CONDUCT.md)
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
|
||||||
id: summary
|
|
||||||
attributes:
|
|
||||||
label: Summary
|
|
||||||
placeholder: Please summarize your feature request.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: motivation
|
id: motivation
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: '23.0.1+11'
|
JAVA_VERSION: '23.0.1+11'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -29,12 +29,12 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
appimage-suffix: x86_64
|
appimage-suffix: x86_64
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_linux-x64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_linux-x64_bin-jmods.zip'
|
||||||
openjfx-sha: 'd44bff3b94d5668fdee18a938d7b1269026d663d44765f02d29a9bdfd3fa1eb0'
|
openjfx-sha: '2164bca470bf70a5e2764645e2078ba7f787b274e5be3d7df30d87c5bb62bba6'
|
||||||
- os: [self-hosted, Linux, ARM64]
|
- os: ubuntu-24.04-arm
|
||||||
appimage-suffix: aarch64
|
appimage-suffix: aarch64
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_linux-aarch64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_linux-aarch64_bin-jmods.zip'
|
||||||
openjfx-sha: '3d5457136690c4f5bb9522d38b45218e045bdac13c24aa4c808c7c8d17d039c7'
|
openjfx-sha: '09c92fa9fa0b82adefd88640a14ebb2a49e5f3f733a57d1542f5590d060ffe1b'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
@@ -98,7 +98,7 @@ jobs:
|
|||||||
--dest appdir
|
--dest appdir
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2024 Skymatic GmbH"
|
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
||||||
--java-options "--enable-preview"
|
--java-options "--enable-preview"
|
||||||
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator"
|
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator"
|
||||||
@@ -116,6 +116,7 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||||
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\""
|
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\""
|
||||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\""
|
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\""
|
||||||
|
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\""
|
||||||
--resource-dir dist/linux/resources
|
--resource-dir dist/linux/resources
|
||||||
- name: Patch Cryptomator.AppDir
|
- name: Patch Cryptomator.AppDir
|
||||||
run: |
|
run: |
|
||||||
@@ -132,13 +133,13 @@ jobs:
|
|||||||
cp dist/linux/common/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop
|
cp dist/linux/common/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop
|
||||||
cp dist/linux/common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
cp dist/linux/common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
||||||
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/org.cryptomator.Cryptomator.svg
|
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/org.cryptomator.Cryptomator.svg
|
||||||
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/Cryptomator.svg
|
|
||||||
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon
|
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon
|
||||||
ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/Cryptomator.desktop
|
ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/org.cryptomator.Cryptomator.desktop
|
||||||
|
ln -s org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.appdata.xml
|
||||||
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
||||||
- name: Download AppImageKit
|
- name: Download AppImageKit
|
||||||
run: |
|
run: |
|
||||||
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${{ matrix.appimage-suffix }}.AppImage -o appimagetool.AppImage
|
curl -L https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${{ matrix.appimage-suffix }}.AppImage -o appimagetool.AppImage
|
||||||
chmod +x appimagetool.AppImage
|
chmod +x appimagetool.AppImage
|
||||||
./appimagetool.AppImage --appimage-extract
|
./appimagetool.AppImage --appimage-extract
|
||||||
- name: Prepare GPG-Agent for signing with key 615D449FE6E6A235
|
- name: Prepare GPG-Agent for signing with key 615D449FE6E6A235
|
||||||
@@ -152,7 +153,7 @@ jobs:
|
|||||||
run: >
|
run: >
|
||||||
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.appimage-suffix }}.AppImage
|
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.appimage-suffix }}.AppImage
|
||||||
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.appimage-suffix }}.AppImage.zsync'
|
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.appimage-suffix }}.AppImage.zsync'
|
||||||
--sign --sign-key=615D449FE6E6A235 --sign-args="--batch --pinentry-mode loopback"
|
--sign --sign-key=615D449FE6E6A235
|
||||||
- name: Create detached GPG signatures
|
- name: Create detached GPG signatures
|
||||||
run: |
|
run: |
|
||||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage
|
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage
|
||||||
@@ -175,4 +176,4 @@ jobs:
|
|||||||
files: |
|
files: |
|
||||||
cryptomator-*.AppImage
|
cryptomator-*.AppImage
|
||||||
cryptomator-*.zsync
|
cryptomator-*.zsync
|
||||||
cryptomator-*.asc
|
cryptomator-*.asc
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ on:
|
|||||||
types: [labeled]
|
types: [labeled]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: 23
|
JAVA_VERSION: 23
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JDK_VERSION: '23.0.1+11'
|
JDK_VERSION: '23.0.1+11'
|
||||||
JDK_VENDOR: zulu
|
JDK_VENDOR: temurin
|
||||||
RUNTIME_VERSION_HELPER: >
|
RUNTIME_VERSION_HELPER: >
|
||||||
public class Test {
|
public class Test {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
@@ -16,19 +16,19 @@ on:
|
|||||||
type: boolean
|
type: boolean
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: '23.0.1+11'
|
JAVA_VERSION: '23.0.1+11'
|
||||||
COFFEELIBS_JDK: 23
|
COFFEELIBS_JDK: 23
|
||||||
COFFEELIBS_JDK_VERSION: '23.0.1+11-0ppa1'
|
COFFEELIBS_JDK_VERSION: '23.0.1+11-0ppa1'
|
||||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_linux-x64_bin-jmods.zip'
|
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_linux-x64_bin-jmods.zip'
|
||||||
OPENJFX_JMODS_AMD64_HASH: 'd44bff3b94d5668fdee18a938d7b1269026d663d44765f02d29a9bdfd3fa1eb0'
|
OPENJFX_JMODS_AMD64_HASH: '2164bca470bf70a5e2764645e2078ba7f787b274e5be3d7df30d87c5bb62bba6'
|
||||||
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_linux-aarch64_bin-jmods.zip'
|
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_linux-aarch64_bin-jmods.zip'
|
||||||
OPENJFX_JMODS_AARCH64_HASH: '3d5457136690c4f5bb9522d38b45218e045bdac13c24aa4c808c7c8d17d039c7'
|
OPENJFX_JMODS_AARCH64_HASH: '09c92fa9fa0b82adefd88640a14ebb2a49e5f3f733a57d1542f5590d060ffe1b'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build Debian Package
|
name: Build Debian Package
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- id: versions
|
- id: versions
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ on:
|
|||||||
value: ${{ jobs.determine-version.outputs.type }}
|
value: ${{ jobs.determine-version.outputs.type }}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: 23
|
JAVA_VERSION: 23
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ on:
|
|||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: '23.0.1+11'
|
JAVA_VERSION: '23.0.1+11'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -35,8 +35,8 @@ jobs:
|
|||||||
architecture: x64
|
architecture: x64
|
||||||
output-suffix: x64
|
output-suffix: x64
|
||||||
fuse-lib: macFUSE
|
fuse-lib: macFUSE
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_osx-x64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_osx-x64_bin-jmods.zip'
|
||||||
openjfx-sha: '115cb08bb59d880cfff6e51e0bf0dcc45785ed9d456b8b8425597b04da6ab3d4'
|
openjfx-sha: '8857965975c464a0e5d57709292ce357d0ebb39f6168c41d5ca38301e42c3c8e'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
@@ -100,7 +100,7 @@ jobs:
|
|||||||
--dest appdir
|
--dest appdir
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2024 Skymatic GmbH"
|
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}"
|
--app-version "${{ needs.get-version.outputs.semVerNum }}"
|
||||||
--java-options "--enable-preview"
|
--java-options "--enable-preview"
|
||||||
--java-options "--enable-native-access=org.cryptomator.jfuse.mac"
|
--java-options "--enable-native-access=org.cryptomator.jfuse.mac"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ on:
|
|||||||
type: boolean
|
type: boolean
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: '23.0.1+11'
|
JAVA_VERSION: '23.0.1+11'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -36,8 +36,8 @@ jobs:
|
|||||||
architecture: aarch64
|
architecture: aarch64
|
||||||
output-suffix: arm64
|
output-suffix: arm64
|
||||||
fuse-lib: FUSE-T
|
fuse-lib: FUSE-T
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_osx-aarch64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_osx-aarch64_bin-jmods.zip'
|
||||||
openjfx-sha: '813c6748f7c99cb7a579d48b48a087b4682b1fad1fc1a4fe5f9b21cf872b15a7'
|
openjfx-sha: 'a800724a1f3e6757ecfa0bd5bf7ed64d2e6a7a3f5b3522650a70b8cfc7782fb6'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
@@ -101,7 +101,7 @@ jobs:
|
|||||||
--dest appdir
|
--dest appdir
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2024 Skymatic GmbH"
|
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}"
|
--app-version "${{ needs.get-version.outputs.semVerNum }}"
|
||||||
--java-options "--enable-preview"
|
--java-options "--enable-preview"
|
||||||
--java-options "--enable-native-access=org.cryptomator.jfuse.mac"
|
--java-options "--enable-native-access=org.cryptomator.jfuse.mac"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: 23
|
JAVA_VERSION: 23
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ defaults:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: 23
|
JAVA_VERSION: 23
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ on:
|
|||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'zulu'
|
||||||
JAVA_VERSION: '23.0.1+11'
|
JAVA_VERSION: '23.0.1+11'
|
||||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_windows-x64_bin-jmods.zip'
|
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/23.0.1/openjfx-23.0.1_windows-x64_bin-jmods.zip'
|
||||||
OPENJFX_JMODS_AMD64_HASH: 'f9376d200f5c5b85327d575c1ec1482e6455f19916577f7e2fc9be2f48bb29b6'
|
OPENJFX_JMODS_AMD64_HASH: 'ee176dcee3bd78bde7910735bd67f67c792882f5b89626796ae06f7a1c0119d3'
|
||||||
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi'
|
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi'
|
||||||
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
|
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ jobs:
|
|||||||
--verbose
|
--verbose
|
||||||
--output runtime
|
--output runtime
|
||||||
--module-path "jfxjmods;${JAVA_HOME}/jmods"
|
--module-path "jfxjmods;${JAVA_HOME}/jmods"
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.crypto.mscapi,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
|
||||||
--strip-native-commands
|
--strip-native-commands
|
||||||
--no-header-files
|
--no-header-files
|
||||||
--no-man-pages
|
--no-man-pages
|
||||||
@@ -110,7 +110,7 @@ jobs:
|
|||||||
--dest appdir
|
--dest appdir
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2024 Skymatic GmbH"
|
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
||||||
--java-options "--enable-preview"
|
--java-options "--enable-preview"
|
||||||
--java-options "--enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win"
|
--java-options "--enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win"
|
||||||
@@ -218,7 +218,7 @@ jobs:
|
|||||||
--dest installer
|
--dest installer
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2024 Skymatic GmbH"
|
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
|
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
|
||||||
--win-menu
|
--win-menu
|
||||||
--win-dir-chooser
|
--win-dir-chooser
|
||||||
@@ -304,7 +304,7 @@ jobs:
|
|||||||
-out dist/win/bundle/
|
-out dist/win/bundle/
|
||||||
-dBundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
-dBundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
||||||
-dBundleVendor="Skymatic GmbH"
|
-dBundleVendor="Skymatic GmbH"
|
||||||
-dBundleCopyright="(C) 2016 - 2024 Skymatic GmbH"
|
-dBundleCopyright="(C) 2016 - 2025 Skymatic GmbH"
|
||||||
-dAboutUrl="https://cryptomator.org"
|
-dAboutUrl="https://cryptomator.org"
|
||||||
-dHelpUrl="https://cryptomator.org/contact"
|
-dHelpUrl="https://cryptomator.org/contact"
|
||||||
-dUpdateUrl="https://cryptomator.org/downloads/"
|
-dUpdateUrl="https://cryptomator.org/downloads/"
|
||||||
|
|||||||
Generated
+9
-8
@@ -14,17 +14,16 @@
|
|||||||
<option name="dagger.fastInit" value="enabled" />
|
<option name="dagger.fastInit" value="enabled" />
|
||||||
<option name="dagger.formatGeneratedSource" value="enabled" />
|
<option name="dagger.formatGeneratedSource" value="enabled" />
|
||||||
<processorPath useClasspath="false">
|
<processorPath useClasspath="false">
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.52/dagger-compiler-2.52.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.55/dagger-compiler-2.55.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.52/dagger-2.52.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.55/dagger-2.55.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/jakarta/inject/jakarta.inject-api/2.0.1/jakarta.inject-api-2.0.1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/jakarta/inject/jakarta.inject-api/2.0.1/jakarta.inject-api-2.0.1.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.52/dagger-spi-2.52.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.55/dagger-spi-2.55.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.9.24-1.0.20/symbol-processing-api-1.9.24-1.0.20.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/2.0.21-1.0.28/symbol-processing-api-2.0.21-1.0.28.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.0/kotlin-stdlib-jdk8-1.9.0.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.21/kotlin-stdlib-2.0.21.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.24/kotlin-stdlib-1.9.24.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.0/kotlin-stdlib-jdk7-1.9.0.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/33.0.0-jre/guava-33.0.0-jre.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/33.0.0-jre/guava-33.0.0-jre.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
|
||||||
@@ -35,6 +34,8 @@
|
|||||||
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.10/kotlin-stdlib-jdk7-1.6.10.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
|
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar" />
|
||||||
@@ -45,7 +46,7 @@
|
|||||||
</component>
|
</component>
|
||||||
<component name="JavacSettings">
|
<component name="JavacSettings">
|
||||||
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
|
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
|
||||||
<module name="cryptomator" options="-Adagger.fastInit=enabled -Adagger.formatGeneratedSource=enabled --enable-preview" />
|
<module name="cryptomator" options="-Adagger.fastInit=enabled -Adagger.formatGeneratedSource=enabled" />
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
[](https://cryptomator.org/)
|
[](https://cryptomator.org/)
|
||||||
|
|
||||||
[](https://github.com/cryptomator/cryptomator/actions?query=workflow%3ABuild)
|
[](https://github.com/cryptomator/cryptomator/actions/workflows/build.yml?query=branch%3Adevelop)
|
||||||
[](https://snyk.io/test/github/cryptomator/cryptomator)
|
[](https://snyk.io/test/github/cryptomator/cryptomator)
|
||||||
[](https://sonarcloud.io/dashboard?id=cryptomator_cryptomator)
|
[](https://sonarcloud.io/dashboard?id=cryptomator_cryptomator)
|
||||||
[](http://twitter.com/Cryptomator)
|
[](https://mastodon.online/@cryptomator)
|
||||||
[](https://translate.cryptomator.org/)
|
[](https://translate.cryptomator.org/)
|
||||||
[](https://github.com/cryptomator/cryptomator/releases/latest)
|
[](https://github.com/cryptomator/cryptomator/releases/latest)
|
||||||
[](https://community.cryptomator.org)
|
[](https://community.cryptomator.org)
|
||||||
@@ -32,9 +32,9 @@ Become our Gold Sponsor and showcase your brand to a targeted audience! Please c
|
|||||||
|
|
||||||
### Special Shoutout
|
### Special Shoutout
|
||||||
|
|
||||||
Continuous integration hosting for ARM64 builds is provided by [MacStadium](https://www.macstadium.com/opensource).
|
Continuous integration hosting for ARM64 builds is provided by [MacStadium](https://www.macstadium.com/company/opensource).
|
||||||
|
|
||||||
<a href="https://www.macstadium.com/opensource"><img src="https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png" alt="MacStadium" height="100"></a>
|
<a href="https://www.macstadium.com/company/opensource"><img src="https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png" alt="MacStadium" height="100"></a>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator
|
|||||||
- File names get encrypted
|
- File names get encrypted
|
||||||
- Folder structure gets obfuscated
|
- Folder structure gets obfuscated
|
||||||
- Use as many vaults in your Dropbox as you want, each having individual passwords
|
- Use as many vaults in your Dropbox as you want, each having individual passwords
|
||||||
- Four thousand commits for the security of your data!! :tada:
|
- More than Five thousand commits for the security of your data!! :tada:
|
||||||
|
|
||||||
### Privacy
|
### Privacy
|
||||||
|
|
||||||
@@ -72,13 +72,13 @@ Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator
|
|||||||
|
|
||||||
### Security Architecture
|
### Security Architecture
|
||||||
|
|
||||||
For more information on the security details visit [cryptomator.org](https://docs.cryptomator.org/en/latest/security/architecture/).
|
For more information on the security details visit [cryptomator.org](https://docs.cryptomator.org/security/architecture/).
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* JDK 22 (e.g. temurin, zulu)
|
* JDK 23 (e.g. temurin, zulu)
|
||||||
* Maven 3
|
* Maven 3
|
||||||
|
|
||||||
### Run Maven
|
### Run Maven
|
||||||
|
|||||||
Vendored
+7
-6
@@ -25,10 +25,10 @@ cp ../../../target/cryptomator-*.jar ../../../target/mods
|
|||||||
|
|
||||||
JAVAFX_VERSION=22.0.2
|
JAVAFX_VERSION=22.0.2
|
||||||
JAVAFX_ARCH="x64"
|
JAVAFX_ARCH="x64"
|
||||||
JAVAFX_JMODS_SHA256='d44bff3b94d5668fdee18a938d7b1269026d663d44765f02d29a9bdfd3fa1eb0'
|
JAVAFX_JMODS_SHA256='2164bca470bf70a5e2764645e2078ba7f787b274e5be3d7df30d87c5bb62bba6'
|
||||||
if [ "${CPU_ARCH}" = "aarch64" ]; then
|
if [ "${CPU_ARCH}" = "aarch64" ]; then
|
||||||
JAVAFX_ARCH="aarch64"
|
JAVAFX_ARCH="aarch64"
|
||||||
JAVAFX_JMODS_SHA256='3d5457136690c4f5bb9522d38b45218e045bdac13c24aa4c808c7c8d17d039c7'
|
JAVAFX_JMODS_SHA256='09c92fa9fa0b82adefd88640a14ebb2a49e5f3f733a57d1542f5590d060ffe1b'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# download javaFX jmods
|
# download javaFX jmods
|
||||||
@@ -76,7 +76,7 @@ ${JAVA_HOME}/bin/jpackage \
|
|||||||
--vendor "Skymatic GmbH" \
|
--vendor "Skymatic GmbH" \
|
||||||
--java-options "--enable-preview" \
|
--java-options "--enable-preview" \
|
||||||
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
|
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
|
||||||
--copyright "(C) 2016 - 2024 Skymatic GmbH" \
|
--copyright "(C) 2016 - 2025 Skymatic GmbH" \
|
||||||
--java-options "-Xss5m" \
|
--java-options "-Xss5m" \
|
||||||
--java-options "-Xmx256m" \
|
--java-options "-Xmx256m" \
|
||||||
--app-version "${VERSION}.${REVISION_NO}" \
|
--app-version "${VERSION}.${REVISION_NO}" \
|
||||||
@@ -91,6 +91,7 @@ ${JAVA_HOME}/bin/jpackage \
|
|||||||
--java-options "-Dcryptomator.showTrayIcon=true" \
|
--java-options "-Dcryptomator.showTrayIcon=true" \
|
||||||
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\"" \
|
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\"" \
|
||||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
|
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
|
||||||
|
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \
|
||||||
--resource-dir ../resources
|
--resource-dir ../resources
|
||||||
|
|
||||||
# transform AppDir
|
# transform AppDir
|
||||||
@@ -108,13 +109,13 @@ cp ../common/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/usr/share/ap
|
|||||||
cp ../common/org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.metainfo.xml
|
cp ../common/org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.metainfo.xml
|
||||||
cp ../common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
cp ../common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
||||||
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/org.cryptomator.Cryptomator.svg
|
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/org.cryptomator.Cryptomator.svg
|
||||||
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/Cryptomator.svg
|
|
||||||
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon
|
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon
|
||||||
ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/Cryptomator.desktop
|
ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/org.cryptomator.Cryptomator.desktop
|
||||||
|
ln -s org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.appdata.xml
|
||||||
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
||||||
|
|
||||||
# load AppImageTool
|
# load AppImageTool
|
||||||
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${CPU_ARCH}.AppImage -o /tmp/appimagetool.AppImage
|
curl -L https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${CPU_ARCH}.AppImage -o /tmp/appimagetool.AppImage
|
||||||
chmod +x /tmp/appimagetool.AppImage
|
chmod +x /tmp/appimagetool.AppImage
|
||||||
|
|
||||||
# create AppImage
|
# create AppImage
|
||||||
|
|||||||
+21
-6
@@ -1,11 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Copyright 2018 Armin Schrenk <armin.schrenk@zoho.eu> -->
|
|
||||||
<component type="desktop-application">
|
<component type="desktop-application">
|
||||||
<id>org.cryptomator.Cryptomator</id>
|
<id>org.cryptomator.Cryptomator</id>
|
||||||
<metadata_license>FSFAP</metadata_license>
|
<metadata_license>FSFAP</metadata_license>
|
||||||
<project_license>GPL-3.0-or-later</project_license>
|
<project_license>GPL-3.0-or-later</project_license>
|
||||||
<name>Cryptomator</name>
|
<name>Cryptomator</name>
|
||||||
<summary>Encryption made easy and optimized for the cloud</summary>
|
<summary>Encryption for your cloud made easy</summary>
|
||||||
|
|
||||||
|
<keywords>
|
||||||
|
<keyword>encryption</keyword>
|
||||||
|
<keyword>security</keyword>
|
||||||
|
<keyword>privacy</keyword>
|
||||||
|
</keywords>
|
||||||
|
|
||||||
<description>
|
<description>
|
||||||
<p>
|
<p>
|
||||||
@@ -44,12 +49,16 @@
|
|||||||
|
|
||||||
<screenshots>
|
<screenshots>
|
||||||
<screenshot type="default">
|
<screenshot type="default">
|
||||||
<caption>Light theme</caption>
|
<caption>Encrypts your data, protects your privacy</caption>
|
||||||
<image>https://user-images.githubusercontent.com/11858409/156986109-6e58f59c-8b8c-4501-b33b-bb1e33007cea.png</image>
|
<image>https://static.cryptomator.org/desktop/flathubScreenshots/MainWindowUnlockDialog_light.png</image>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<caption>Dark theme</caption>
|
<caption>Dark theme available</caption>
|
||||||
<image>https://user-images.githubusercontent.com/11858409/156986113-6c5d7801-86e0-4643-bc2f-aff9d95d3ce0.png</image>
|
<image>https://static.cryptomator.org/desktop/flathubScreenshots/MainWindowUnlocked_dark.png</image>
|
||||||
|
</screenshot>
|
||||||
|
<screenshot>
|
||||||
|
<caption>Easy to use - work on encrypted files as if they were not</caption>
|
||||||
|
<image>https://static.cryptomator.org/desktop/flathubScreenshots/MainWindowUnlocked_light.png</image>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
</screenshots>
|
</screenshots>
|
||||||
|
|
||||||
@@ -74,6 +83,12 @@
|
|||||||
</content_rating>
|
</content_rating>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
|
<release date="2025-02-05" version="1.15.1">
|
||||||
|
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.15.1</url>
|
||||||
|
</release>
|
||||||
|
<release date="2025-02-03" version="1.15.0">
|
||||||
|
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.15.0</url>
|
||||||
|
</release>
|
||||||
<release date="2024-11-19" version="1.14.2">
|
<release date="2024-11-19" version="1.14.2">
|
||||||
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.14.2</url>
|
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.14.2</url>
|
||||||
</release>
|
</release>
|
||||||
|
|||||||
Vendored
+1
-1
@@ -2,7 +2,7 @@ Source: cryptomator
|
|||||||
Maintainer: Cryptobot <releases@cryptomator.org>
|
Maintainer: Cryptobot <releases@cryptomator.org>
|
||||||
Section: utils
|
Section: utils
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Build-Depends: debhelper (>=10), coffeelibs-jdk-22 (>= 22.0.1+8-0ppa1), libgtk-3-0, libxxf86vm1, libgl1
|
Build-Depends: debhelper (>=10), coffeelibs-jdk-23 (>= 23.0.1+11-0ppa1), libgtk-3-0, libxxf86vm1, libgl1
|
||||||
Standards-Version: 4.5.0
|
Standards-Version: 4.5.0
|
||||||
Homepage: https://cryptomator.org
|
Homepage: https://cryptomator.org
|
||||||
Vcs-Git: https://github.com/cryptomator/cryptomator.git
|
Vcs-Git: https://github.com/cryptomator/cryptomator.git
|
||||||
|
|||||||
Vendored
+2
-2
@@ -4,11 +4,11 @@ Upstream-Contact: Cryptomator <info@cryptomator.org>
|
|||||||
Source: https://cryptomator.org
|
Source: https://cryptomator.org
|
||||||
|
|
||||||
Files: *
|
Files: *
|
||||||
Copyright: 2016-2024 Skymatic GmbH
|
Copyright: 2016-2025 Skymatic GmbH
|
||||||
License: GPL-3+
|
License: GPL-3+
|
||||||
|
|
||||||
Files: debian/org.cryptomator.Cryptomator.appdata.xml
|
Files: debian/org.cryptomator.Cryptomator.appdata.xml
|
||||||
Copyright: 2016-2024 Skymatic GmbH
|
Copyright: 2016-2025 Skymatic GmbH
|
||||||
License: FSFAP
|
License: FSFAP
|
||||||
|
|
||||||
License: GPL-3+
|
License: GPL-3+
|
||||||
|
|||||||
Vendored
+2
-1
@@ -45,7 +45,7 @@ override_dh_auto_build:
|
|||||||
--vendor "Skymatic GmbH" \
|
--vendor "Skymatic GmbH" \
|
||||||
--java-options "--enable-preview" \
|
--java-options "--enable-preview" \
|
||||||
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
|
--java-options "--enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
|
||||||
--copyright "(C) 2016 - 2024 Skymatic GmbH" \
|
--copyright "(C) 2016 - 2025 Skymatic GmbH" \
|
||||||
--java-options "-Xss5m" \
|
--java-options "-Xss5m" \
|
||||||
--java-options "-Xmx256m" \
|
--java-options "-Xmx256m" \
|
||||||
--java-options "-Dfile.encoding=\"utf-8\"" \
|
--java-options "-Dfile.encoding=\"utf-8\"" \
|
||||||
@@ -62,6 +62,7 @@ override_dh_auto_build:
|
|||||||
--java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" \
|
--java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" \
|
||||||
--java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \
|
--java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \
|
||||||
--java-options "-Dcryptomator.integrationsLinux.autoStartCmd=\"cryptomator\"" \
|
--java-options "-Dcryptomator.integrationsLinux.autoStartCmd=\"cryptomator\"" \
|
||||||
|
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \
|
||||||
--app-version "${VERSION_NUM}.${REVISION_NUM}" \
|
--app-version "${VERSION_NUM}.${REVISION_NUM}" \
|
||||||
--resource-dir resources \
|
--resource-dir resources \
|
||||||
--verbose
|
--verbose
|
||||||
|
|||||||
Vendored
+4
-4
@@ -24,7 +24,7 @@ rm -rf runtime dmg *.app *.dmg
|
|||||||
# set variables
|
# set variables
|
||||||
APP_NAME="Cryptomator"
|
APP_NAME="Cryptomator"
|
||||||
VENDOR="Skymatic GmbH"
|
VENDOR="Skymatic GmbH"
|
||||||
COPYRIGHT_YEARS="2016 - 2024"
|
COPYRIGHT_YEARS="2016 - 2025"
|
||||||
PACKAGE_IDENTIFIER="org.cryptomator"
|
PACKAGE_IDENTIFIER="org.cryptomator"
|
||||||
MAIN_JAR_GLOB="cryptomator-*.jar"
|
MAIN_JAR_GLOB="cryptomator-*.jar"
|
||||||
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
|
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
|
||||||
@@ -32,15 +32,15 @@ REVISION_NO=`git rev-list --count HEAD`
|
|||||||
VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
|
VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
|
||||||
FUSE_LIB="FUSE-T"
|
FUSE_LIB="FUSE-T"
|
||||||
|
|
||||||
JAVAFX_VERSION=22.0.2
|
JAVAFX_VERSION=23.0.1
|
||||||
JAVAFX_ARCH="undefined"
|
JAVAFX_ARCH="undefined"
|
||||||
JAVAFX_JMODS_SHA256="undefined"
|
JAVAFX_JMODS_SHA256="undefined"
|
||||||
if [ "$(machine)" = "arm64e" ]; then
|
if [ "$(machine)" = "arm64e" ]; then
|
||||||
JAVAFX_ARCH="aarch64"
|
JAVAFX_ARCH="aarch64"
|
||||||
JAVAFX_JMODS_SHA256="813c6748f7c99cb7a579d48b48a087b4682b1fad1fc1a4fe5f9b21cf872b15a7"
|
JAVAFX_JMODS_SHA256="a800724a1f3e6757ecfa0bd5bf7ed64d2e6a7a3f5b3522650a70b8cfc7782fb6"
|
||||||
else
|
else
|
||||||
JAVAFX_ARCH="x64"
|
JAVAFX_ARCH="x64"
|
||||||
JAVAFX_JMODS_SHA256="115cb08bb59d880cfff6e51e0bf0dcc45785ed9d456b8b8425597b04da6ab3d4"
|
JAVAFX_JMODS_SHA256="8857965975c464a0e5d57709292ce357d0ebb39f6168c41d5ca38301e42c3c8e"
|
||||||
fi
|
fi
|
||||||
JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-${JAVAFX_ARCH}_bin-jmods.zip"
|
JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-${JAVAFX_ARCH}_bin-jmods.zip"
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@
|
|||||||
\f1\b0 \
|
\f1\b0 \
|
||||||
\
|
\
|
||||||
|
|
||||||
\f0\b \'a9 2016 \'96 2024 Skymatic GmbH
|
\f0\b \'a9 2016 \'96 2025 Skymatic GmbH
|
||||||
\f1\b0 \
|
\f1\b0 \
|
||||||
\
|
\
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\
|
||||||
|
|||||||
Vendored
+1
-1
@@ -11,7 +11,7 @@ SET HELP_URL="https://cryptomator.org/contact/"
|
|||||||
SET MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
|
SET MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
|
||||||
SET LOOPBACK_ALIAS="cryptomator-vault"
|
SET LOOPBACK_ALIAS="cryptomator-vault"
|
||||||
|
|
||||||
powershell -NoLogo -NoProfile -ExecutionPolicy Unrestricted -Command .\build.ps1^
|
pwsh -NoLogo -NoProfile -ExecutionPolicy Unrestricted -Command .\build.ps1^
|
||||||
-AppName %APPNAME%^
|
-AppName %APPNAME%^
|
||||||
-MainJarGlob "%MAIN_JAR_GLOB%"^
|
-MainJarGlob "%MAIN_JAR_GLOB%"^
|
||||||
-ModuleAndMainClass "%MODULE_AND_MAIN_CLASS%"^
|
-ModuleAndMainClass "%MODULE_AND_MAIN_CLASS%"^
|
||||||
|
|||||||
Vendored
+5
-5
@@ -51,23 +51,23 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
## download jfx jmods
|
## download jfx jmods
|
||||||
$javaFxVersion='22.0.2'
|
$javaFxVersion='23.0.1'
|
||||||
$javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip"
|
$javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip"
|
||||||
$javaFxJmodsSHA256 = 'f9376d200f5c5b85327d575c1ec1482e6455f19916577f7e2fc9be2f48bb29b6'
|
$javaFxJmodsSHA256 = 'ee176dcee3bd78bde7910735bd67f67c792882f5b89626796ae06f7a1c0119d3'
|
||||||
$javaFxJmods = '.\resources\jfxJmods.zip'
|
$javaFxJmods = '.\resources\jfxJmods.zip'
|
||||||
if( !(Test-Path -Path $javaFxJmods) ) {
|
if( !(Test-Path -Path $javaFxJmods) ) {
|
||||||
Write-Output "Downloading ${javaFxJmodsUrl}..."
|
Write-Output "Downloading ${javaFxJmodsUrl}..."
|
||||||
Invoke-WebRequest $javaFxJmodsUrl -OutFile $javaFxJmods # redirects are followed by default
|
Invoke-WebRequest $javaFxJmodsUrl -OutFile $javaFxJmods # redirects are followed by default
|
||||||
}
|
}
|
||||||
|
|
||||||
$jmodsChecksumActual = $(Get-FileHash -Path $javaFxJmods -Algorithm SHA256).Hash
|
$jmodsChecksumActual = $(Get-FileHash -Path $javaFxJmods -Algorithm SHA256).Hash.ToLower()
|
||||||
if( $jmodsChecksumActual -ne $javaFxJmodsSHA256 ) {
|
if( $jmodsChecksumActual -ne $javaFxJmodsSHA256 ) {
|
||||||
Write-Error "Checksum mismatch for jfxJmods.zip. Expected: $javaFxJmodsSHA256
|
Write-Error "Checksum mismatch for jfxJmods.zip. Expected: $javaFxJmodsSHA256
|
||||||
, actual: $jmodsChecksumActual"
|
, actual: $jmodsChecksumActual"
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
Expand-Archive -Path $javaFxJmods -Force -DestinationPath ".\resources\"
|
Expand-Archive -Path $javaFxJmods -Force -DestinationPath ".\resources\"
|
||||||
Remove-Item -Recurse -Force -Path ".\resources\javafx-jmods"
|
Remove-Item -Recurse -Force -Path ".\resources\javafx-jmods" -ErrorAction Ignore
|
||||||
Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\javafx-jmods" -ErrorAction Stop
|
Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\javafx-jmods" -ErrorAction Stop
|
||||||
|
|
||||||
## create custom runtime
|
## create custom runtime
|
||||||
@@ -75,7 +75,7 @@ Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\ja
|
|||||||
--verbose `
|
--verbose `
|
||||||
--output runtime `
|
--output runtime `
|
||||||
--module-path "$Env:JAVA_HOME/jmods;$buildDir/resources/javafx-jmods" `
|
--module-path "$Env:JAVA_HOME/jmods;$buildDir/resources/javafx-jmods" `
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,jdk.crypto.mscapi,java.compiler,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
|
||||||
--strip-native-commands `
|
--strip-native-commands `
|
||||||
--no-header-files `
|
--no-header-files `
|
||||||
--no-man-pages `
|
--no-man-pages `
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
\vieww12000\viewh15840\viewkind0
|
\vieww12000\viewh15840\viewkind0
|
||||||
\pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
|
\pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
|
||||||
\par
|
\par
|
||||||
\b\'a9 2016 \'96 2024 Skymatic GmbH \b0\par
|
\b\'a9 2016 \'96 2025 Skymatic GmbH \b0\par
|
||||||
\par
|
\par
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
|
||||||
\par
|
\par
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
\vieww12000\viewh15840\viewkind0
|
\vieww12000\viewh15840\viewkind0
|
||||||
\pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
|
\pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
|
||||||
\par
|
\par
|
||||||
\b\'a9 2016 \'96 2024 Skymatic GmbH \b0\par
|
\b\'a9 2016 \'96 2025 Skymatic GmbH \b0\par
|
||||||
\par
|
\par
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
|
||||||
\par
|
\par
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>cryptomator</artifactId>
|
<artifactId>cryptomator</artifactId>
|
||||||
<version>1.15.0-SNAPSHOT</version>
|
<version>1.16.0-SNAPSHOT</version>
|
||||||
<name>Cryptomator Desktop App</name>
|
<name>Cryptomator Desktop App</name>
|
||||||
|
|
||||||
<organization>
|
<organization>
|
||||||
@@ -33,43 +33,46 @@
|
|||||||
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
|
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
|
||||||
|
|
||||||
<!-- cryptomator dependencies -->
|
<!-- cryptomator dependencies -->
|
||||||
<cryptomator.cryptofs.version>2.7.1</cryptomator.cryptofs.version>
|
<cryptomator.cryptofs.version>2.9.0-beta2</cryptomator.cryptofs.version>
|
||||||
<cryptomator.integrations.version>1.4.0</cryptomator.integrations.version>
|
<cryptomator.integrations.version>1.5.0</cryptomator.integrations.version>
|
||||||
<cryptomator.integrations.win.version>1.3.0</cryptomator.integrations.win.version>
|
<cryptomator.integrations.win.version>1.3.0</cryptomator.integrations.win.version>
|
||||||
<cryptomator.integrations.mac.version>1.2.4</cryptomator.integrations.mac.version>
|
<cryptomator.integrations.mac.version>1.2.4</cryptomator.integrations.mac.version>
|
||||||
<cryptomator.integrations.linux.version>1.5.1</cryptomator.integrations.linux.version>
|
<cryptomator.integrations.linux.version>1.5.2</cryptomator.integrations.linux.version>
|
||||||
<cryptomator.fuse.version>5.0.2</cryptomator.fuse.version>
|
<cryptomator.fuse.version>5.0.2</cryptomator.fuse.version>
|
||||||
<cryptomator.webdav.version>2.0.7</cryptomator.webdav.version>
|
<cryptomator.webdav.version>2.0.7</cryptomator.webdav.version>
|
||||||
|
|
||||||
<!-- 3rd party dependencies -->
|
<!-- 3rd party dependencies -->
|
||||||
<commons-lang3.version>3.17.0</commons-lang3.version>
|
<commons-lang3.version>3.17.0</commons-lang3.version>
|
||||||
<dagger.version>2.52</dagger.version>
|
<dagger.version>2.55</dagger.version>
|
||||||
<easybind.version>2.2</easybind.version>
|
<easybind.version>2.2</easybind.version>
|
||||||
<jackson.version>2.18.1</jackson.version>
|
<jackson.version>2.18.2</jackson.version>
|
||||||
<javafx.version>22.0.2</javafx.version>
|
<javafx.version>23.0.1</javafx.version>
|
||||||
<jwt.version>4.4.0</jwt.version>
|
<jwt.version>4.4.0</jwt.version>
|
||||||
<nimbus-jose.version>9.37.3</nimbus-jose.version>
|
<nimbus-jose.version>9.37.3</nimbus-jose.version>
|
||||||
<logback.version>1.5.12</logback.version>
|
<logback.version>1.5.16</logback.version>
|
||||||
<slf4j.version>2.0.16</slf4j.version>
|
<slf4j.version>2.0.16</slf4j.version>
|
||||||
<tinyoauth2.version>0.8.0</tinyoauth2.version>
|
<tinyoauth2.version>0.8.0</tinyoauth2.version>
|
||||||
<zxcvbn.version>1.9.0</zxcvbn.version>
|
<zxcvbn.version>1.9.0</zxcvbn.version>
|
||||||
|
|
||||||
<!-- test dependencies -->
|
<!-- test dependencies -->
|
||||||
<junit.jupiter.version>5.11.3</junit.jupiter.version>
|
<junit.jupiter.version>5.11.4</junit.jupiter.version>
|
||||||
<mockito.version>5.14.2</mockito.version>
|
<mockito.version>5.15.2</mockito.version>
|
||||||
<hamcrest.version>3.0</hamcrest.version>
|
<hamcrest.version>3.0</hamcrest.version>
|
||||||
|
|
||||||
<!-- build-time dependencies -->
|
<!-- build-time dependencies -->
|
||||||
<jetbrains.annotations.version>26.0.1</jetbrains.annotations.version>
|
<jetbrains.annotations.version>26.0.1</jetbrains.annotations.version>
|
||||||
<dependency-check.version>11.1.0</dependency-check.version>
|
<dependency-check.version>12.1.0</dependency-check.version>
|
||||||
<jacoco.version>0.8.12</jacoco.version>
|
<jacoco.version>0.8.12</jacoco.version>
|
||||||
<license-generator.version>2.4.0</license-generator.version>
|
<license-generator.version>2.5.0</license-generator.version>
|
||||||
<junit-tree-reporter.version>1.3.0</junit-tree-reporter.version>
|
<junit-tree-reporter.version>1.4.0</junit-tree-reporter.version>
|
||||||
<mvn-compiler.version>3.13.0</mvn-compiler.version>
|
<mvn-compiler.version>3.13.0</mvn-compiler.version>
|
||||||
<mvn-resources.version>3.3.1</mvn-resources.version>
|
<mvn-resources.version>3.3.1</mvn-resources.version>
|
||||||
<mvn-dependency.version>3.8.1</mvn-dependency.version>
|
<mvn-dependency.version>3.8.1</mvn-dependency.version>
|
||||||
<mvn-surefire.version>3.5.2</mvn-surefire.version>
|
<mvn-surefire.version>3.5.2</mvn-surefire.version>
|
||||||
<mvn-jar.version>3.4.2</mvn-jar.version>
|
<mvn-jar.version>3.4.2</mvn-jar.version>
|
||||||
|
|
||||||
|
<!-- Property used by surefire to determine jacoco engine -->
|
||||||
|
<surefire.jacoco.args></surefire.jacoco.args>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@@ -201,6 +204,12 @@
|
|||||||
<version>2.0.1</version>
|
<version>2.0.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Caffeine -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
|
<artifactId>caffeine</artifactId>
|
||||||
|
<version>3.2.0</version>
|
||||||
|
</dependency>
|
||||||
<!-- JUnit / Mockito / Hamcrest -->
|
<!-- JUnit / Mockito / Hamcrest -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
@@ -302,7 +311,6 @@
|
|||||||
<compilerArgs>
|
<compilerArgs>
|
||||||
<arg>-Adagger.fastInit=enabled</arg>
|
<arg>-Adagger.fastInit=enabled</arg>
|
||||||
<arg>-Adagger.formatGeneratedSource=enabled</arg>
|
<arg>-Adagger.formatGeneratedSource=enabled</arg>
|
||||||
<arg>--enable-preview</arg>
|
|
||||||
</compilerArgs>
|
</compilerArgs>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
@@ -329,11 +337,11 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<configuration>
|
<configuration>
|
||||||
<argLine>--enable-preview</argLine>
|
|
||||||
<reportFormat>plain</reportFormat>
|
<reportFormat>plain</reportFormat>
|
||||||
<consoleOutputReporter>
|
<consoleOutputReporter>
|
||||||
<disable>true</disable>
|
<disable>true</disable>
|
||||||
</consoleOutputReporter>
|
</consoleOutputReporter>
|
||||||
|
<argLine>@{surefire.jacoco.args} -javaagent:${org.mockito:mockito-core:jar}</argLine>
|
||||||
<statelessTestsetInfoReporter
|
<statelessTestsetInfoReporter
|
||||||
implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter">
|
implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter">
|
||||||
</statelessTestsetInfoReporter>
|
</statelessTestsetInfoReporter>
|
||||||
@@ -343,6 +351,13 @@
|
|||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
<executions>
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>jar-paths-to-properties</id>
|
||||||
|
<phase>validate</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>properties</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
<!-- sort jars into two buckets (classpath and modulepath). exclude openjfx, which gets jlinked separately -->
|
<!-- sort jars into two buckets (classpath and modulepath). exclude openjfx, which gets jlinked separately -->
|
||||||
<execution>
|
<execution>
|
||||||
<id>copy-mods</id>
|
<id>copy-mods</id>
|
||||||
@@ -415,6 +430,9 @@
|
|||||||
<goals>
|
<goals>
|
||||||
<goal>prepare-agent</goal>
|
<goal>prepare-agent</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<propertyName>surefire.jacoco.args</propertyName>
|
||||||
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
<execution>
|
<execution>
|
||||||
<id>report</id>
|
<id>report</id>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import ch.qos.logback.classic.spi.Configurator;
|
import ch.qos.logback.classic.spi.Configurator;
|
||||||
|
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
|
||||||
import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider;
|
||||||
import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider;
|
||||||
import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider;
|
||||||
@@ -13,6 +14,9 @@ import org.cryptomator.common.locationpresets.OneDriveLinuxLocationPresetsProvid
|
|||||||
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
|
||||||
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
|
||||||
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
|
||||||
|
import org.cryptomator.networking.SSLContextWithMacKeychain;
|
||||||
|
import org.cryptomator.networking.SSLContextProvider;
|
||||||
|
import org.cryptomator.networking.SSLContextWithWindowsCertStore;
|
||||||
import org.cryptomator.integrations.tray.TrayMenuController;
|
import org.cryptomator.integrations.tray.TrayMenuController;
|
||||||
import org.cryptomator.logging.LogbackConfiguratorFactory;
|
import org.cryptomator.logging.LogbackConfiguratorFactory;
|
||||||
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
||||||
@@ -51,11 +55,14 @@ open module org.cryptomator.desktop {
|
|||||||
requires jakarta.inject;
|
requires jakarta.inject;
|
||||||
requires static javax.inject;
|
requires static javax.inject;
|
||||||
requires java.compiler;
|
requires java.compiler;
|
||||||
|
requires com.github.benmanes.caffeine;
|
||||||
|
|
||||||
uses org.cryptomator.common.locationpresets.LocationPresetsProvider;
|
uses org.cryptomator.common.locationpresets.LocationPresetsProvider;
|
||||||
|
uses SSLContextProvider;
|
||||||
|
|
||||||
provides TrayMenuController with AwtTrayMenuController;
|
provides TrayMenuController with AwtTrayMenuController;
|
||||||
provides Configurator with LogbackConfiguratorFactory;
|
provides Configurator with LogbackConfiguratorFactory;
|
||||||
|
provides SSLContextProvider with SSLContextWithWindowsCertStore, SSLContextWithMacKeychain, SSLContextWithPKCS12TrustStore;
|
||||||
provides LocationPresetsProvider with //
|
provides LocationPresetsProvider with //
|
||||||
DropboxWindowsLocationPresetsProvider, DropboxMacLocationPresetsProvider, DropboxLinuxLocationPresetsProvider, //
|
DropboxWindowsLocationPresetsProvider, DropboxMacLocationPresetsProvider, DropboxLinuxLocationPresetsProvider, //
|
||||||
GoogleDriveMacLocationPresetsProvider, GoogleDriveWindowsLocationPresetsProvider, //
|
GoogleDriveMacLocationPresetsProvider, GoogleDriveWindowsLocationPresetsProvider, //
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package org.cryptomator.common.keychain;
|
package org.cryptomator.common.keychain;
|
||||||
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
import com.google.common.cache.CacheLoader;
|
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||||
import com.google.common.cache.LoadingCache;
|
|
||||||
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||||
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
||||||
|
|
||||||
@@ -24,9 +23,9 @@ public class KeychainManager implements KeychainAccessProvider {
|
|||||||
@Inject
|
@Inject
|
||||||
KeychainManager(ObjectExpression<KeychainAccessProvider> selectedKeychain) {
|
KeychainManager(ObjectExpression<KeychainAccessProvider> selectedKeychain) {
|
||||||
this.keychain = selectedKeychain;
|
this.keychain = selectedKeychain;
|
||||||
this.passphraseStoredProperties = CacheBuilder.newBuilder() //
|
this.passphraseStoredProperties = Caffeine.newBuilder() //
|
||||||
.weakValues() //
|
.weakValues() //
|
||||||
.build(CacheLoader.from(this::createStoredPassphraseProperty));
|
.build(this::createStoredPassphraseProperty);
|
||||||
keychain.addListener(ignored -> passphraseStoredProperties.invalidateAll());
|
keychain.addListener(ignored -> passphraseStoredProperties.invalidateAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +123,7 @@ public class KeychainManager implements KeychainAccessProvider {
|
|||||||
* @see #isPassphraseStored(String)
|
* @see #isPassphraseStored(String)
|
||||||
*/
|
*/
|
||||||
public ReadOnlyBooleanProperty getPassphraseStoredProperty(String key) {
|
public ReadOnlyBooleanProperty getPassphraseStoredProperty(String key) {
|
||||||
return passphraseStoredProperties.getUnchecked(key);
|
return passphraseStoredProperties.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BooleanProperty createStoredPassphraseProperty(String key) {
|
private BooleanProperty createStoredPassphraseProperty(String key) {
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ public class VaultSettings {
|
|||||||
public final StringExpression mountName;
|
public final StringExpression mountName;
|
||||||
public final StringProperty mountService;
|
public final StringProperty mountService;
|
||||||
public final IntegerProperty port;
|
public final IntegerProperty port;
|
||||||
|
public final StringProperty lastKnownKeyLoader;
|
||||||
|
|
||||||
VaultSettings(VaultSettingsJson json) {
|
VaultSettings(VaultSettingsJson json) {
|
||||||
this.id = json.id;
|
this.id = json.id;
|
||||||
@@ -74,6 +75,7 @@ public class VaultSettings {
|
|||||||
this.mountPoint = new SimpleObjectProperty<>(this, "mountPoint", json.mountPoint == null ? null : Path.of(json.mountPoint));
|
this.mountPoint = new SimpleObjectProperty<>(this, "mountPoint", json.mountPoint == null ? null : Path.of(json.mountPoint));
|
||||||
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
|
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
|
||||||
this.port = new SimpleIntegerProperty(this, "port", json.port);
|
this.port = new SimpleIntegerProperty(this, "port", json.port);
|
||||||
|
this.lastKnownKeyLoader = new SimpleStringProperty(this, "lastKnownKeyLoader", json.lastKnownKeyLoader);
|
||||||
// mount name is no longer an explicit setting, see https://github.com/cryptomator/cryptomator/pull/1318
|
// mount name is no longer an explicit setting, see https://github.com/cryptomator/cryptomator/pull/1318
|
||||||
this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> {
|
this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> {
|
||||||
final String name;
|
final String name;
|
||||||
@@ -99,7 +101,7 @@ public class VaultSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Observable[] observables() {
|
Observable[] observables() {
|
||||||
return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode, port, mountService};
|
return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode, port, mountService, lastKnownKeyLoader};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static VaultSettings withRandomId() {
|
public static VaultSettings withRandomId() {
|
||||||
@@ -130,6 +132,7 @@ public class VaultSettings {
|
|||||||
json.mountPoint = mountPoint.map(Path::toString).getValue();
|
json.mountPoint = mountPoint.map(Path::toString).getValue();
|
||||||
json.mountService = mountService.get();
|
json.mountService = mountService.get();
|
||||||
json.port = port.get();
|
json.port = port.get();
|
||||||
|
json.lastKnownKeyLoader = lastKnownKeyLoader.get();
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ class VaultSettingsJson {
|
|||||||
@JsonProperty("mountService")
|
@JsonProperty("mountService")
|
||||||
String mountService;
|
String mountService;
|
||||||
|
|
||||||
|
@JsonProperty("lastKnownKeyLoader")
|
||||||
|
String lastKnownKeyLoader;
|
||||||
|
|
||||||
@JsonProperty("port")
|
@JsonProperty("port")
|
||||||
int port = VaultSettings.DEFAULT_PORT;
|
int port = VaultSettings.DEFAULT_PORT;
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ package org.cryptomator.common.vaults;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.Constants;
|
import org.cryptomator.common.Constants;
|
||||||
|
import org.cryptomator.event.FileSystemEventAggregator;
|
||||||
import org.cryptomator.common.mount.Mounter;
|
import org.cryptomator.common.mount.Mounter;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
@@ -18,6 +19,7 @@ import org.cryptomator.cryptofs.CryptoFileSystemProperties;
|
|||||||
import org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags;
|
import org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
||||||
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
|
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
|
||||||
|
import org.cryptomator.cryptofs.event.FilesystemEvent;
|
||||||
import org.cryptomator.cryptolib.api.CryptoException;
|
import org.cryptomator.cryptolib.api.CryptoException;
|
||||||
import org.cryptomator.cryptolib.api.MasterkeyLoader;
|
import org.cryptomator.cryptolib.api.MasterkeyLoader;
|
||||||
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
||||||
@@ -44,6 +46,7 @@ import javafx.beans.property.SimpleBooleanProperty;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.ReadOnlyFileSystemException;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -73,6 +76,7 @@ public class Vault {
|
|||||||
private final ObjectBinding<Mountpoint> mountPoint;
|
private final ObjectBinding<Mountpoint> mountPoint;
|
||||||
private final Mounter mounter;
|
private final Mounter mounter;
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
|
private final FileSystemEventAggregator fileSystemEventAggregator;
|
||||||
private final BooleanProperty showingStats;
|
private final BooleanProperty showingStats;
|
||||||
|
|
||||||
private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
|
private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
|
||||||
@@ -84,7 +88,8 @@ public class Vault {
|
|||||||
VaultState state, //
|
VaultState state, //
|
||||||
@Named("lastKnownException") ObjectProperty<Exception> lastKnownException, //
|
@Named("lastKnownException") ObjectProperty<Exception> lastKnownException, //
|
||||||
VaultStats stats, //
|
VaultStats stats, //
|
||||||
Mounter mounter, Settings settings) {
|
Mounter mounter, Settings settings, //
|
||||||
|
FileSystemEventAggregator fileSystemEventAggregator) {
|
||||||
this.vaultSettings = vaultSettings;
|
this.vaultSettings = vaultSettings;
|
||||||
this.configCache = configCache;
|
this.configCache = configCache;
|
||||||
this.cryptoFileSystem = cryptoFileSystem;
|
this.cryptoFileSystem = cryptoFileSystem;
|
||||||
@@ -101,6 +106,7 @@ public class Vault {
|
|||||||
this.mountPoint = Bindings.createObjectBinding(this::getMountPoint, state);
|
this.mountPoint = Bindings.createObjectBinding(this::getMountPoint, state);
|
||||||
this.mounter = mounter;
|
this.mounter = mounter;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
this.fileSystemEventAggregator = fileSystemEventAggregator;
|
||||||
this.showingStats = new SimpleBooleanProperty(false);
|
this.showingStats = new SimpleBooleanProperty(false);
|
||||||
this.quickAccessEntry = new AtomicReference<>(null);
|
this.quickAccessEntry = new AtomicReference<>(null);
|
||||||
}
|
}
|
||||||
@@ -111,15 +117,22 @@ public class Vault {
|
|||||||
|
|
||||||
private CryptoFileSystem createCryptoFileSystem(MasterkeyLoader keyLoader) throws IOException, MasterkeyLoadingFailedException {
|
private CryptoFileSystem createCryptoFileSystem(MasterkeyLoader keyLoader) throws IOException, MasterkeyLoadingFailedException {
|
||||||
Set<FileSystemFlags> flags = EnumSet.noneOf(FileSystemFlags.class);
|
Set<FileSystemFlags> flags = EnumSet.noneOf(FileSystemFlags.class);
|
||||||
if (vaultSettings.usesReadOnlyMode.get()) {
|
var createReadOnly = vaultSettings.usesReadOnlyMode.get();
|
||||||
|
try {
|
||||||
|
FileSystemCapabilityChecker.assertWriteAccess(getPath());
|
||||||
|
} catch (FileSystemCapabilityChecker.MissingCapabilityException e) {
|
||||||
|
if (!createReadOnly) {
|
||||||
|
throw new ReadOnlyFileSystemException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (createReadOnly) {
|
||||||
flags.add(FileSystemFlags.READONLY);
|
flags.add(FileSystemFlags.READONLY);
|
||||||
} else if (vaultSettings.maxCleartextFilenameLength.get() == -1) {
|
} else if (vaultSettings.maxCleartextFilenameLength.get() == -1) {
|
||||||
LOG.debug("Determining cleartext filename length limitations...");
|
LOG.debug("Determining cleartext filename length limitations...");
|
||||||
var checker = new FileSystemCapabilityChecker();
|
|
||||||
int shorteningThreshold = configCache.get().allegedShorteningThreshold();
|
int shorteningThreshold = configCache.get().allegedShorteningThreshold();
|
||||||
int ciphertextLimit = checker.determineSupportedCiphertextFileNameLength(getPath());
|
int ciphertextLimit = FileSystemCapabilityChecker.determineSupportedCiphertextFileNameLength(getPath());
|
||||||
if (ciphertextLimit < shorteningThreshold) {
|
if (ciphertextLimit < shorteningThreshold) {
|
||||||
int cleartextLimit = checker.determineSupportedCleartextFileNameLength(getPath());
|
int cleartextLimit = FileSystemCapabilityChecker.determineSupportedCleartextFileNameLength(getPath());
|
||||||
vaultSettings.maxCleartextFilenameLength.set(cleartextLimit);
|
vaultSettings.maxCleartextFilenameLength.set(cleartextLimit);
|
||||||
} else {
|
} else {
|
||||||
vaultSettings.maxCleartextFilenameLength.setValue(UNLIMITED_FILENAME_LENGTH);
|
vaultSettings.maxCleartextFilenameLength.setValue(UNLIMITED_FILENAME_LENGTH);
|
||||||
@@ -135,6 +148,7 @@ public class Vault {
|
|||||||
.withFlags(flags) //
|
.withFlags(flags) //
|
||||||
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength.get()) //
|
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength.get()) //
|
||||||
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
|
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
|
||||||
|
.withFilesystemEventConsumer(this::consumeVaultEvent) //
|
||||||
.build();
|
.build();
|
||||||
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
|
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
|
||||||
}
|
}
|
||||||
@@ -243,6 +257,11 @@ public class Vault {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void consumeVaultEvent(FilesystemEvent e) {
|
||||||
|
fileSystemEventAggregator.put(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
// ******************************************************************************
|
// ******************************************************************************
|
||||||
// Observable Properties
|
// Observable Properties
|
||||||
// *******************************************************************************
|
// *******************************************************************************
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import java.nio.file.NoSuchFileException;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@@ -49,9 +50,9 @@ public class VaultListManager {
|
|||||||
@Inject
|
@Inject
|
||||||
public VaultListManager(ObservableList<Vault> vaultList, //
|
public VaultListManager(ObservableList<Vault> vaultList, //
|
||||||
AutoLocker autoLocker, //
|
AutoLocker autoLocker, //
|
||||||
List<MountService> mountServices,
|
List<MountService> mountServices, //
|
||||||
VaultComponent.Factory vaultComponentFactory,
|
VaultComponent.Factory vaultComponentFactory, //
|
||||||
ResourceBundle resourceBundle,
|
ResourceBundle resourceBundle, //
|
||||||
Settings settings) {
|
Settings settings) {
|
||||||
this.vaultList = vaultList;
|
this.vaultList = vaultList;
|
||||||
this.autoLocker = autoLocker;
|
this.autoLocker = autoLocker;
|
||||||
@@ -114,6 +115,10 @@ public class VaultListManager {
|
|||||||
private Vault create(VaultSettings vaultSettings) {
|
private Vault create(VaultSettings vaultSettings) {
|
||||||
var wrapper = new VaultConfigCache(vaultSettings);
|
var wrapper = new VaultConfigCache(vaultSettings);
|
||||||
try {
|
try {
|
||||||
|
if (Objects.isNull(vaultSettings.lastKnownKeyLoader.get())) {
|
||||||
|
var keyIdScheme = wrapper.get().getKeyId().getScheme();
|
||||||
|
vaultSettings.lastKnownKeyLoader.set(keyIdScheme);
|
||||||
|
}
|
||||||
var vaultState = determineVaultState(vaultSettings.path.get());
|
var vaultState = determineVaultState(vaultSettings.path.get());
|
||||||
if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
|
if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
|
||||||
wrapper.reloadConfig();
|
wrapper.reloadConfig();
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package org.cryptomator.event;
|
||||||
|
|
||||||
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.cryptofs.event.FilesystemEvent;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public record FSEventBucket(Vault vault, Path idPath, Class<? extends FilesystemEvent> c) {}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package org.cryptomator.event;
|
||||||
|
|
||||||
|
import org.cryptomator.cryptofs.event.FilesystemEvent;
|
||||||
|
|
||||||
|
public record FSEventBucketContent(FilesystemEvent mostRecentEvent, int count) {}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package org.cryptomator.event;
|
||||||
|
|
||||||
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.cryptofs.event.BrokenDirFileEvent;
|
||||||
|
import org.cryptomator.cryptofs.event.BrokenFileNodeEvent;
|
||||||
|
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
|
||||||
|
import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
|
||||||
|
import org.cryptomator.cryptofs.event.DecryptionFailedEvent;
|
||||||
|
import org.cryptomator.cryptofs.event.FilesystemEvent;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class FileSystemEventAggregator {
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<FSEventBucket, FSEventBucketContent> map;
|
||||||
|
private final AtomicBoolean hasUpdates;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public FileSystemEventAggregator() {
|
||||||
|
this.map = new ConcurrentHashMap<>();
|
||||||
|
this.hasUpdates = new AtomicBoolean(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given event to the map. If a bucket for this event already exists, only the count is updated and the event set as the most recent one.
|
||||||
|
*
|
||||||
|
* @param v Vault where the event occurred
|
||||||
|
* @param e Actual {@link FilesystemEvent}
|
||||||
|
*/
|
||||||
|
public void put(Vault v, FilesystemEvent e) {
|
||||||
|
var key = computeKey(v, e);
|
||||||
|
hasUpdates.set(true);
|
||||||
|
map.compute(key, (k, val) -> {
|
||||||
|
if (val == null) {
|
||||||
|
return new FSEventBucketContent(e, 1);
|
||||||
|
} else {
|
||||||
|
return new FSEventBucketContent(e, val.count() + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an event bucket from the map.
|
||||||
|
*/
|
||||||
|
public FSEventBucketContent remove(FSEventBucket key) {
|
||||||
|
hasUpdates.set(true);
|
||||||
|
return map.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the event map.
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
hasUpdates.set(true);
|
||||||
|
map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean hasUpdates() {
|
||||||
|
return hasUpdates.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones the map entries into a collection.
|
||||||
|
* <p>
|
||||||
|
* The collection is first cleared, then all map entries are added in one bulk operation. Cleans the hasUpdates status.
|
||||||
|
*
|
||||||
|
* @param target collection which is first cleared and then the EntrySet copied to.
|
||||||
|
*/
|
||||||
|
public void cloneTo(Collection<Map.Entry<FSEventBucket, FSEventBucketContent>> target) {
|
||||||
|
hasUpdates.set(false);
|
||||||
|
target.clear();
|
||||||
|
target.addAll(map.entrySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to compute the identifying key for a given filesystem event
|
||||||
|
*
|
||||||
|
* @param v Vault where the event occurred
|
||||||
|
* @param event Actual {@link FilesystemEvent}
|
||||||
|
* @return a {@link FSEventBucket} used in the map and lru cache
|
||||||
|
*/
|
||||||
|
private static FSEventBucket computeKey(Vault v, FilesystemEvent event) {
|
||||||
|
var p = switch (event) {
|
||||||
|
case DecryptionFailedEvent(_, Path ciphertextPath, _) -> ciphertextPath;
|
||||||
|
case ConflictResolvedEvent(_, _, _, _, Path resolvedCiphertext) -> resolvedCiphertext;
|
||||||
|
case ConflictResolutionFailedEvent(_, _, Path conflictingCiphertext, _) -> conflictingCiphertext;
|
||||||
|
case BrokenDirFileEvent(_, Path ciphertext) -> ciphertext;
|
||||||
|
case BrokenFileNodeEvent(_, _, Path ciphertext) -> ciphertext;
|
||||||
|
};
|
||||||
|
return new FSEventBucket(v, p, event.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,8 +9,9 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.Environment;
|
import org.cryptomator.common.Environment;
|
||||||
import org.cryptomator.common.SubstitutingProperties;
|
|
||||||
import org.cryptomator.common.ShutdownHook;
|
import org.cryptomator.common.ShutdownHook;
|
||||||
|
import org.cryptomator.common.SubstitutingProperties;
|
||||||
|
import org.cryptomator.networking.SSLContextProvider;
|
||||||
import org.cryptomator.ipc.IpcCommunicator;
|
import org.cryptomator.ipc.IpcCommunicator;
|
||||||
import org.cryptomator.logging.DebugMode;
|
import org.cryptomator.logging.DebugMode;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
||||||
@@ -19,8 +20,10 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -48,14 +51,16 @@ public class Cryptomator {
|
|||||||
private final Environment env;
|
private final Environment env;
|
||||||
private final Lazy<IpcMessageHandler> ipcMessageHandler;
|
private final Lazy<IpcMessageHandler> ipcMessageHandler;
|
||||||
private final ShutdownHook shutdownHook;
|
private final ShutdownHook shutdownHook;
|
||||||
|
private final SecureRandom csprng;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Cryptomator(DebugMode debugMode, SupportedLanguages supportedLanguages, Environment env, Lazy<IpcMessageHandler> ipcMessageHandler, ShutdownHook shutdownHook) {
|
Cryptomator(DebugMode debugMode, SupportedLanguages supportedLanguages, Environment env, Lazy<IpcMessageHandler> ipcMessageHandler, ShutdownHook shutdownHook, SecureRandom csprng) {
|
||||||
this.debugMode = debugMode;
|
this.debugMode = debugMode;
|
||||||
this.supportedLanguages = supportedLanguages;
|
this.supportedLanguages = supportedLanguages;
|
||||||
this.env = env;
|
this.env = env;
|
||||||
this.ipcMessageHandler = ipcMessageHandler;
|
this.ipcMessageHandler = ipcMessageHandler;
|
||||||
this.shutdownHook = shutdownHook;
|
this.shutdownHook = shutdownHook;
|
||||||
|
this.csprng = csprng;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
@@ -89,7 +94,7 @@ public class Cryptomator {
|
|||||||
LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion(), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
|
LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion(), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
|
||||||
debugMode.initialize();
|
debugMode.initialize();
|
||||||
supportedLanguages.applyPreferred();
|
supportedLanguages.applyPreferred();
|
||||||
|
changeDefaultSSLContext();
|
||||||
/*
|
/*
|
||||||
* Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args.
|
* Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args.
|
||||||
* If no external process could be reached, the args will be handled by the loopback IPC endpoint.
|
* If no external process could be reached, the args will be handled by the loopback IPC endpoint.
|
||||||
@@ -115,6 +120,17 @@ public class Cryptomator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void changeDefaultSSLContext() {
|
||||||
|
SSLContextProvider.loadAll().findFirst().ifPresent(p -> {
|
||||||
|
try {
|
||||||
|
var context = p.getContext(csprng);
|
||||||
|
SSLContext.setDefault(context);
|
||||||
|
} catch (SSLContextProvider.SSLContextBuildException e) {
|
||||||
|
LOG.warn("Failed to change default SSL context with provider {}", p.getClass().getName(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Launches the JavaFX application, blocking the main thread until shuts down.
|
* Launches the JavaFX application, blocking the main thread until shuts down.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package org.cryptomator.networking;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
|
||||||
|
abstract class SSLContextDifferentTrustStoreBase implements SSLContextProvider {
|
||||||
|
|
||||||
|
abstract KeyStore getTruststore() throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLContext getContext(SecureRandom csprng) throws SSLContextBuildException {
|
||||||
|
try {
|
||||||
|
KeyStore truststore = getTruststore();
|
||||||
|
truststore.load(null, null);
|
||||||
|
|
||||||
|
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||||
|
tmf.init(truststore);
|
||||||
|
|
||||||
|
SSLContext context = SSLContext.getInstance("TLS");
|
||||||
|
context.init(null, tmf.getTrustManagers(), csprng);
|
||||||
|
return context;
|
||||||
|
} catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | KeyManagementException | IOException e) {
|
||||||
|
throw new SSLContextBuildException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package org.cryptomator.networking;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.IntegrationsLoader;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public interface SSLContextProvider {
|
||||||
|
|
||||||
|
SSLContext getContext(SecureRandom csprng) throws SSLContextBuildException;
|
||||||
|
|
||||||
|
class SSLContextBuildException extends Exception {
|
||||||
|
|
||||||
|
SSLContextBuildException(Throwable t) {
|
||||||
|
super(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<SSLContextProvider> loadAll() {
|
||||||
|
return IntegrationsLoader.loadAll(ServiceLoader.load(SSLContextProvider.class), SSLContextProvider.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package org.cryptomator.networking;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSLContextProvider for macOS using the macOS Keychain as truststore
|
||||||
|
*/
|
||||||
|
@OperatingSystem(OperatingSystem.Value.MAC)
|
||||||
|
public class SSLContextWithMacKeychain extends SSLContextDifferentTrustStoreBase implements SSLContextProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
KeyStore getTruststore() throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException {
|
||||||
|
return KeyStore.getInstance("KeychainStore-ROOT");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package org.cryptomator.networking;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.NoSuchFileException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSLContextProvider for Linux using a PKCS#12 file as trust store
|
||||||
|
*/
|
||||||
|
@OperatingSystem(OperatingSystem.Value.LINUX)
|
||||||
|
@CheckAvailability
|
||||||
|
public class SSLContextWithPKCS12TrustStore extends SSLContextDifferentTrustStoreBase implements SSLContextProvider {
|
||||||
|
|
||||||
|
private static final String CERT_FILE_LOCATION_PROPERTY = "cryptomator.networking.truststore.p12Path";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
KeyStore getTruststore() throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException {
|
||||||
|
var pkcs12FilePath = Path.of(System.getProperty(CERT_FILE_LOCATION_PROPERTY));
|
||||||
|
try {
|
||||||
|
return KeyStore.getInstance(pkcs12FilePath.toFile(), new char[]{});
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new NoSuchFileException(pkcs12FilePath.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isSupported() {
|
||||||
|
var pkcs12Path = System.getProperty(CERT_FILE_LOCATION_PROPERTY);
|
||||||
|
return Optional.ofNullable(pkcs12Path) //
|
||||||
|
.map(Path::of) //
|
||||||
|
.map(Files::exists).orElse(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package org.cryptomator.networking;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSLContextProvider for Windows using the Windows certificate store as trust store
|
||||||
|
* <p>
|
||||||
|
* In order to work, the jdk.crypto.mscapi jmod is needed
|
||||||
|
*/
|
||||||
|
@OperatingSystem(OperatingSystem.Value.WINDOWS)
|
||||||
|
public class SSLContextWithWindowsCertStore extends SSLContextDifferentTrustStoreBase implements SSLContextProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
KeyStore getTruststore() throws KeyStoreException {
|
||||||
|
return KeyStore.getInstance("WINDOWS-ROOT");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+1
-1
@@ -26,7 +26,7 @@ public class CreateNewVaultExpertSettingsController implements FxController {
|
|||||||
|
|
||||||
public static final int MAX_SHORTENING_THRESHOLD = 220;
|
public static final int MAX_SHORTENING_THRESHOLD = 220;
|
||||||
public static final int MIN_SHORTENING_THRESHOLD = 36;
|
public static final int MIN_SHORTENING_THRESHOLD = 36;
|
||||||
private static final String DOCS_NAME_SHORTENING_URL = "https://docs.cryptomator.org/en/1.7/security/architecture/#name-shortening";
|
private static final String DOCS_NAME_SHORTENING_URL = "https://docs.cryptomator.org/security/architecture/#name-shortening";
|
||||||
|
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final Lazy<Application> application;
|
private final Lazy<Application> application;
|
||||||
|
|||||||
+10
-8
@@ -40,7 +40,6 @@ import java.io.IOException;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
@@ -50,7 +49,7 @@ public class CreateNewVaultLocationController implements FxController {
|
|||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(CreateNewVaultLocationController.class);
|
private static final Logger LOG = LoggerFactory.getLogger(CreateNewVaultLocationController.class);
|
||||||
private static final Path DEFAULT_CUSTOM_VAULT_PATH = Paths.get(System.getProperty("user.home"));
|
private static final Path DEFAULT_CUSTOM_VAULT_PATH = Paths.get(System.getProperty("user.home"));
|
||||||
private static final String TEMP_FILE_FORMAT = ".locationTest.cryptomator.tmp";
|
private static final String TEMP_FILE_PREFIX = ".locationTest.cryptomator";
|
||||||
|
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final Lazy<Scene> chooseNameScene;
|
private final Lazy<Scene> chooseNameScene;
|
||||||
@@ -126,16 +125,19 @@ public class CreateNewVaultLocationController implements FxController {
|
|||||||
|
|
||||||
|
|
||||||
private boolean isActuallyWritable(Path p) {
|
private boolean isActuallyWritable(Path p) {
|
||||||
Path tmpFile = p.resolve(TEMP_FILE_FORMAT);
|
Path tmpDir = null;
|
||||||
try (var chan = Files.newByteChannel(tmpFile, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE, StandardOpenOption.DELETE_ON_CLOSE)) {
|
try {
|
||||||
|
tmpDir = Files.createTempDirectory(p, TEMP_FILE_PREFIX );
|
||||||
return true;
|
return true;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
if (tmpDir != null) {
|
||||||
Files.deleteIfExists(tmpFile);
|
try {
|
||||||
} catch (IOException e) {
|
Files.deleteIfExists(tmpDir);
|
||||||
LOG.warn("Unable to delete temporary file {}. Needs to be deleted manually.", tmpFile);
|
} catch (IOException e) {
|
||||||
|
LOG.warn("Unable to delete temporary directory {}. Needs to be deleted manually.", tmpDir);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,8 +76,10 @@ public class ReadmeGenerator {
|
|||||||
input.chars().forEachOrdered(c -> {
|
input.chars().forEachOrdered(c -> {
|
||||||
if (c < 128) {
|
if (c < 128) {
|
||||||
sb.append((char) c);
|
sb.append((char) c);
|
||||||
|
} else if (c <= 0xFF) {
|
||||||
|
sb.append("\\'").append(String.format("%02X", c));
|
||||||
} else if (c < 0xFFFF) {
|
} else if (c < 0xFFFF) {
|
||||||
sb.append("\\u").append(c);
|
sb.append("\\uc1\\u").append(c);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ public enum FxmlFile {
|
|||||||
CONVERTVAULT_HUBTOPASSWORD_CONVERT("/fxml/convertvault_hubtopassword_convert.fxml"), //
|
CONVERTVAULT_HUBTOPASSWORD_CONVERT("/fxml/convertvault_hubtopassword_convert.fxml"), //
|
||||||
CONVERTVAULT_HUBTOPASSWORD_SUCCESS("/fxml/convertvault_hubtopassword_success.fxml"), //
|
CONVERTVAULT_HUBTOPASSWORD_SUCCESS("/fxml/convertvault_hubtopassword_success.fxml"), //
|
||||||
ERROR("/fxml/error.fxml"), //
|
ERROR("/fxml/error.fxml"), //
|
||||||
|
EVENT_VIEW("/fxml/eventview.fxml"), //
|
||||||
FORGET_PASSWORD("/fxml/forget_password.fxml"), //
|
FORGET_PASSWORD("/fxml/forget_password.fxml"), //
|
||||||
HEALTH_START("/fxml/health_start.fxml"), //
|
HEALTH_START("/fxml/health_start.fxml"), //
|
||||||
HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), //
|
HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), //
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ public enum FontAwesome5Icon {
|
|||||||
ANCHOR("\uF13D"), //
|
ANCHOR("\uF13D"), //
|
||||||
ARROW_UP("\uF062"), //
|
ARROW_UP("\uF062"), //
|
||||||
BAN("\uF05E"), //
|
BAN("\uF05E"), //
|
||||||
|
BELL("\uF0F3"), //
|
||||||
BUG("\uF188"), //
|
BUG("\uF188"), //
|
||||||
CARET_DOWN("\uF0D7"), //
|
CARET_DOWN("\uF0D7"), //
|
||||||
CARET_RIGHT("\uF0Da"), //
|
CARET_RIGHT("\uF0Da"), //
|
||||||
@@ -15,10 +16,12 @@ public enum FontAwesome5Icon {
|
|||||||
CLIPBOARD("\uF328"), //
|
CLIPBOARD("\uF328"), //
|
||||||
COG("\uF013"), //
|
COG("\uF013"), //
|
||||||
COGS("\uF085"), //
|
COGS("\uF085"), //
|
||||||
|
COMPRESS_ALT("\uF422"), //
|
||||||
COPY("\uF0C5"), //
|
COPY("\uF0C5"), //
|
||||||
CROWN("\uF521"), //
|
CROWN("\uF521"), //
|
||||||
DONATE("\uF4B9"), //
|
DONATE("\uF4B9"), //
|
||||||
EDIT("\uF044"), //
|
EDIT("\uF044"), //
|
||||||
|
ELLIPSIS_V("\uF142"), //
|
||||||
EXCHANGE_ALT("\uF362"), //
|
EXCHANGE_ALT("\uF362"), //
|
||||||
EXCLAMATION("\uF12A"), //
|
EXCLAMATION("\uF12A"), //
|
||||||
EXCLAMATION_CIRCLE("\uF06A"), //
|
EXCLAMATION_CIRCLE("\uF06A"), //
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.cryptomator.ui.common.FxController;
|
|||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||||
|
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
|
||||||
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
|
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
|
||||||
import org.jetbrains.annotations.VisibleForTesting;
|
import org.jetbrains.annotations.VisibleForTesting;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -108,6 +109,7 @@ public class HubToPasswordConvertController implements FxController {
|
|||||||
.thenRunAsync(this::convertInternal, backgroundExecutorService) //
|
.thenRunAsync(this::convertInternal, backgroundExecutorService) //
|
||||||
.whenCompleteAsync((result, exception) -> {
|
.whenCompleteAsync((result, exception) -> {
|
||||||
if (exception == null) {
|
if (exception == null) {
|
||||||
|
vault.getVaultSettings().lastKnownKeyLoader.set(MasterkeyFileLoadingStrategy.SCHEME);
|
||||||
LOG.info("Conversion of vault {} succeeded.", vault.getPath());
|
LOG.info("Conversion of vault {} succeeded.", vault.getPath());
|
||||||
window.setScene(successScene.get());
|
window.setScene(successScene.get());
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2,31 +2,34 @@ package org.cryptomator.ui.dialogs;
|
|||||||
|
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.ui.common.StageFactory;
|
||||||
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
||||||
|
import org.cryptomator.ui.fxapp.FxApplicationScoped;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
@Singleton
|
@FxApplicationScoped
|
||||||
public class Dialogs {
|
public class Dialogs {
|
||||||
|
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
|
private final StageFactory stageFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Dialogs(ResourceBundle resourceBundle) {
|
public Dialogs(ResourceBundle resourceBundle, StageFactory stageFactory) {
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
|
this.stageFactory = stageFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(Dialogs.class);
|
private static final Logger LOG = LoggerFactory.getLogger(Dialogs.class);
|
||||||
|
|
||||||
private SimpleDialog.Builder createDialogBuilder() {
|
private SimpleDialog.Builder createDialogBuilder() {
|
||||||
return new SimpleDialog.Builder(resourceBundle);
|
return new SimpleDialog.Builder(resourceBundle, stageFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimpleDialog.Builder prepareRemoveVaultDialog(Stage window, Vault vault, ObservableList<Vault> vaults) {
|
public SimpleDialog.Builder prepareRemoveVaultDialog(Stage window, Vault vault, ObservableList<Vault> vaults) {
|
||||||
@@ -35,7 +38,7 @@ public class Dialogs {
|
|||||||
.setMessageKey("removeVault.message") //
|
.setMessageKey("removeVault.message") //
|
||||||
.setDescriptionKey("removeVault.description") //
|
.setDescriptionKey("removeVault.description") //
|
||||||
.setIcon(FontAwesome5Icon.QUESTION) //
|
.setIcon(FontAwesome5Icon.QUESTION) //
|
||||||
.setOkButtonKey("removeVault.confirmBtn") //
|
.setOkButtonKey("generic.button.remove") //
|
||||||
.setCancelButtonKey("generic.button.cancel") //
|
.setCancelButtonKey("generic.button.cancel") //
|
||||||
.setOkAction(stage -> {
|
.setOkAction(stage -> {
|
||||||
LOG.debug("Removing vault {}.", vault.getDisplayName());
|
LOG.debug("Removing vault {}.", vault.getDisplayName());
|
||||||
@@ -51,7 +54,7 @@ public class Dialogs {
|
|||||||
.setMessageKey("removeCert.message") //
|
.setMessageKey("removeCert.message") //
|
||||||
.setDescriptionKey("removeCert.description") //
|
.setDescriptionKey("removeCert.description") //
|
||||||
.setIcon(FontAwesome5Icon.QUESTION) //
|
.setIcon(FontAwesome5Icon.QUESTION) //
|
||||||
.setOkButtonKey("removeCert.confirmBtn") //
|
.setOkButtonKey("generic.button.remove") //
|
||||||
.setCancelButtonKey("generic.button.cancel") //
|
.setCancelButtonKey("generic.button.cancel") //
|
||||||
.setOkAction(stage -> {
|
.setOkAction(stage -> {
|
||||||
settings.licenseKey.set(null);
|
settings.licenseKey.set(null);
|
||||||
@@ -65,11 +68,23 @@ public class Dialogs {
|
|||||||
.setTitleKey("dokanySupportEnd.title") //
|
.setTitleKey("dokanySupportEnd.title") //
|
||||||
.setMessageKey("dokanySupportEnd.message") //
|
.setMessageKey("dokanySupportEnd.message") //
|
||||||
.setDescriptionKey("dokanySupportEnd.description") //
|
.setDescriptionKey("dokanySupportEnd.description") //
|
||||||
.setIcon(FontAwesome5Icon.QUESTION) //
|
.setIcon(FontAwesome5Icon.EXCLAMATION) //
|
||||||
.setOkButtonKey("generic.button.close") //
|
.setOkButtonKey("generic.button.close") //
|
||||||
.setCancelButtonKey("dokanySupportEnd.preferencesBtn") //
|
.setCancelButtonKey("dokanySupportEnd.preferencesBtn") //
|
||||||
.setOkAction(Stage::close) //
|
.setOkAction(Stage::close) //
|
||||||
.setCancelAction(cancelAction);
|
.setCancelAction(cancelAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SimpleDialog.Builder prepareRetryIfReadonlyDialog(Stage window, Consumer<Stage> okAction) {
|
||||||
|
return createDialogBuilder() //
|
||||||
|
.setOwner(window) //
|
||||||
|
.setTitleKey("retryIfReadonly.title") //
|
||||||
|
.setMessageKey("retryIfReadonly.message") //
|
||||||
|
.setDescriptionKey("retryIfReadonly.description") //
|
||||||
|
.setIcon(FontAwesome5Icon.EXCLAMATION) //
|
||||||
|
.setOkButtonKey("retryIfReadonly.retry") //
|
||||||
|
.setCancelButtonKey("generic.button.close") //
|
||||||
|
.setOkAction(okAction) //
|
||||||
|
.setCancelAction(Stage::close);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.cryptomator.ui.dialogs;
|
|||||||
|
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||||
|
import org.cryptomator.ui.common.StageFactory;
|
||||||
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
||||||
|
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
@@ -17,24 +18,24 @@ import java.util.function.Consumer;
|
|||||||
public class SimpleDialog {
|
public class SimpleDialog {
|
||||||
|
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
|
|
||||||
private final Stage dialogStage;
|
private final Stage dialogStage;
|
||||||
|
|
||||||
SimpleDialog(Builder builder) throws IOException {
|
SimpleDialog(Builder builder) throws IOException {
|
||||||
this.resourceBundle = builder.resourceBundle;
|
this.resourceBundle = builder.resourceBundle;
|
||||||
dialogStage = new Stage();
|
dialogStage = builder.stageFactory.create();
|
||||||
dialogStage.initOwner(builder.owner);
|
dialogStage.initOwner(builder.owner);
|
||||||
dialogStage.initModality(Modality.WINDOW_MODAL);
|
dialogStage.initModality(Modality.WINDOW_MODAL);
|
||||||
dialogStage.setTitle(resolveText(builder.titleKey, builder.titleArgs));
|
dialogStage.setTitle(resolveText(builder.titleKey, builder.titleArgs));
|
||||||
dialogStage.setResizable(false);
|
dialogStage.setResizable(false);
|
||||||
|
|
||||||
FxmlLoaderFactory loaderFactory = FxmlLoaderFactory.forController(
|
FxmlLoaderFactory loaderFactory = FxmlLoaderFactory.forController( //
|
||||||
new SimpleDialogController(resolveText(builder.messageKey, null),
|
new SimpleDialogController(resolveText(builder.messageKey, null), //
|
||||||
resolveText(builder.descriptionKey, null),
|
resolveText(builder.descriptionKey, null), //
|
||||||
builder.icon,resolveText(builder.okButtonKey, null),
|
builder.icon, //
|
||||||
resolveText(builder.cancelButtonKey, null),
|
resolveText(builder.okButtonKey, null), //
|
||||||
() -> builder.okAction.accept(dialogStage),
|
builder.cancelButtonKey != null ? resolveText(builder.cancelButtonKey, null) : null, //
|
||||||
() -> builder.cancelAction.accept(dialogStage)),
|
() -> builder.okAction.accept(dialogStage), //
|
||||||
|
() -> builder.cancelAction.accept(dialogStage)), //
|
||||||
Scene::new, builder.resourceBundle);
|
Scene::new, builder.resourceBundle);
|
||||||
|
|
||||||
dialogStage.setScene(new Scene(loaderFactory.load(FxmlFile.SIMPLE_DIALOG.getRessourcePathString()).getRoot()));
|
dialogStage.setScene(new Scene(loaderFactory.load(FxmlFile.SIMPLE_DIALOG.getRessourcePathString()).getRoot()));
|
||||||
@@ -60,19 +61,20 @@ public class SimpleDialog {
|
|||||||
|
|
||||||
private Stage owner;
|
private Stage owner;
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
|
private final StageFactory stageFactory;
|
||||||
private String titleKey;
|
private String titleKey;
|
||||||
private String[] titleArgs;
|
private String[] titleArgs;
|
||||||
private String messageKey;
|
private String messageKey;
|
||||||
private String descriptionKey;
|
private String descriptionKey;
|
||||||
private String okButtonKey;
|
private String okButtonKey;
|
||||||
private String cancelButtonKey;
|
private String cancelButtonKey;
|
||||||
|
|
||||||
private FontAwesome5Icon icon;
|
private FontAwesome5Icon icon;
|
||||||
private Consumer<Stage> okAction = Stage::close;
|
private Consumer<Stage> okAction = Stage::close;
|
||||||
private Consumer<Stage> cancelAction = Stage::close;
|
private Consumer<Stage> cancelAction = Stage::close;
|
||||||
|
|
||||||
public Builder(ResourceBundle resourceBundle) {
|
public Builder(ResourceBundle resourceBundle, StageFactory stageFactory) {
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
|
this.stageFactory = stageFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setOwner(Stage owner) {
|
public Builder setOwner(Stage owner) {
|
||||||
@@ -122,11 +124,10 @@ public class SimpleDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SimpleDialog build() {
|
public SimpleDialog build() {
|
||||||
Objects.requireNonNull(titleKey,"SimpleDialog titleKey must be set.");
|
Objects.requireNonNull(titleKey, "SimpleDialog titleKey must be set.");
|
||||||
Objects.requireNonNull(messageKey,"SimpleDialog messageKey must be set.");
|
Objects.requireNonNull(messageKey, "SimpleDialog messageKey must be set.");
|
||||||
Objects.requireNonNull(descriptionKey,"SimpleDialog descriptionKey must be set.");
|
Objects.requireNonNull(descriptionKey, "SimpleDialog descriptionKey must be set.");
|
||||||
Objects.requireNonNull(okButtonKey,"SimpleDialog okButtonKey must be set.");
|
Objects.requireNonNull(okButtonKey, "SimpleDialog okButtonKey must be set.");
|
||||||
Objects.requireNonNull(cancelButtonKey,"SimpleDialog cancelButtonKey must be set.");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return new SimpleDialog(this);
|
return new SimpleDialog(this);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ public class SimpleDialogController implements FxController {
|
|||||||
private final String cancelButtonText;
|
private final String cancelButtonText;
|
||||||
private final Runnable okAction;
|
private final Runnable okAction;
|
||||||
private final Runnable cancelAction;
|
private final Runnable cancelAction;
|
||||||
|
private final boolean cancelButtonVisible;
|
||||||
|
|
||||||
public SimpleDialogController(String message, String description, FontAwesome5Icon icon, String okButtonText, String cancelButtonText, Runnable okAction, Runnable cancelAction) {
|
public SimpleDialogController(String message, String description, FontAwesome5Icon icon, String okButtonText, String cancelButtonText, Runnable okAction, Runnable cancelAction) {
|
||||||
this.message = message;
|
this.message = message;
|
||||||
@@ -23,6 +24,11 @@ public class SimpleDialogController implements FxController {
|
|||||||
this.cancelButtonText = cancelButtonText;
|
this.cancelButtonText = cancelButtonText;
|
||||||
this.okAction = okAction;
|
this.okAction = okAction;
|
||||||
this.cancelAction = cancelAction;
|
this.cancelAction = cancelAction;
|
||||||
|
this.cancelButtonVisible = cancelButtonText != null && !cancelButtonText.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCancelButtonVisible() {
|
||||||
|
return cancelButtonVisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
|
|||||||
@@ -0,0 +1,329 @@
|
|||||||
|
package org.cryptomator.ui.eventview;
|
||||||
|
|
||||||
|
import org.cryptomator.event.FSEventBucket;
|
||||||
|
import org.cryptomator.event.FSEventBucketContent;
|
||||||
|
import org.cryptomator.event.FileSystemEventAggregator;
|
||||||
|
import org.cryptomator.common.Nullable;
|
||||||
|
import org.cryptomator.common.ObservableUtil;
|
||||||
|
import org.cryptomator.cryptofs.CryptoPath;
|
||||||
|
import org.cryptomator.cryptofs.event.BrokenDirFileEvent;
|
||||||
|
import org.cryptomator.cryptofs.event.BrokenFileNodeEvent;
|
||||||
|
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
|
||||||
|
import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
|
||||||
|
import org.cryptomator.cryptofs.event.DecryptionFailedEvent;
|
||||||
|
import org.cryptomator.integrations.revealpath.RevealFailedException;
|
||||||
|
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||||
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Side;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.ContextMenu;
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
|
import javafx.scene.control.Tooltip;
|
||||||
|
import javafx.scene.input.Clipboard;
|
||||||
|
import javafx.scene.input.ClipboardContent;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.format.FormatStyle;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class EventListCellController implements FxController {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(EventListCellController.class);
|
||||||
|
private static final DateTimeFormatter LOCAL_DATE_FORMATTER = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withZone(ZoneId.systemDefault());
|
||||||
|
private static final DateTimeFormatter LOCAL_TIME_FORMATTER = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withZone(ZoneId.systemDefault());
|
||||||
|
|
||||||
|
private final FileSystemEventAggregator fileSystemEventAggregator;
|
||||||
|
@Nullable
|
||||||
|
private final RevealPathService revealService;
|
||||||
|
private final ResourceBundle resourceBundle;
|
||||||
|
private final ObjectProperty<Map.Entry<FSEventBucket, FSEventBucketContent>> eventEntry;
|
||||||
|
private final StringProperty eventMessage;
|
||||||
|
private final StringProperty eventDescription;
|
||||||
|
private final ObjectProperty<FontAwesome5Icon> eventIcon;
|
||||||
|
private final ObservableValue<String> eventCount;
|
||||||
|
private final ObservableValue<Boolean> vaultUnlocked;
|
||||||
|
private final ObservableValue<String> readableTime;
|
||||||
|
private final ObservableValue<String> readableDate;
|
||||||
|
private final ObservableValue<String> message;
|
||||||
|
private final ObservableValue<String> description;
|
||||||
|
private final ObservableValue<FontAwesome5Icon> icon;
|
||||||
|
private final BooleanProperty actionsButtonVisible;
|
||||||
|
private final Tooltip eventTooltip;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
HBox root;
|
||||||
|
@FXML
|
||||||
|
ContextMenu eventActionsMenu;
|
||||||
|
@FXML
|
||||||
|
Button eventActionsButton;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public EventListCellController(FileSystemEventAggregator fileSystemEventAggregator, Optional<RevealPathService> revealService, ResourceBundle resourceBundle) {
|
||||||
|
this.fileSystemEventAggregator = fileSystemEventAggregator;
|
||||||
|
this.revealService = revealService.orElseGet(() -> null);
|
||||||
|
this.resourceBundle = resourceBundle;
|
||||||
|
this.eventEntry = new SimpleObjectProperty<>(null);
|
||||||
|
this.eventMessage = new SimpleStringProperty();
|
||||||
|
this.eventDescription = new SimpleStringProperty();
|
||||||
|
this.eventIcon = new SimpleObjectProperty<>();
|
||||||
|
this.eventCount = ObservableUtil.mapWithDefault(eventEntry, e -> e.getValue().count() == 1? "" : "("+ e.getValue().count() +")", "");
|
||||||
|
this.vaultUnlocked = ObservableUtil.mapWithDefault(eventEntry.flatMap(e -> e.getKey().vault().unlockedProperty()), Function.identity(), false);
|
||||||
|
this.readableTime = ObservableUtil.mapWithDefault(eventEntry, e -> LOCAL_TIME_FORMATTER.format(e.getValue().mostRecentEvent().getTimestamp()), "");
|
||||||
|
this.readableDate = ObservableUtil.mapWithDefault(eventEntry, e -> LOCAL_DATE_FORMATTER.format(e.getValue().mostRecentEvent().getTimestamp()), "");
|
||||||
|
this.message = Bindings.createStringBinding(this::selectMessage, vaultUnlocked, eventMessage);
|
||||||
|
this.description = Bindings.createStringBinding(this::selectDescription, vaultUnlocked, eventDescription);
|
||||||
|
this.icon = Bindings.createObjectBinding(this::selectIcon, vaultUnlocked, eventIcon);
|
||||||
|
this.actionsButtonVisible = new SimpleBooleanProperty();
|
||||||
|
this.eventTooltip = new Tooltip();
|
||||||
|
eventTooltip.setShowDelay(Duration.millis(500.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void initialize() {
|
||||||
|
actionsButtonVisible.bind(Bindings.createBooleanBinding(this::determineActionsButtonVisibility, root.hoverProperty(), eventActionsMenu.showingProperty(), vaultUnlocked));
|
||||||
|
vaultUnlocked.addListener((_, _, newValue) -> eventActionsMenu.hide());
|
||||||
|
Tooltip.install(root, eventTooltip);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean determineActionsButtonVisibility() {
|
||||||
|
return vaultUnlocked.getValue() && (eventActionsMenu.isShowing() || root.isHover());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEventEntry(@NotNull Map.Entry<FSEventBucket, FSEventBucketContent> item) {
|
||||||
|
eventEntry.set(item);
|
||||||
|
eventActionsMenu.hide();
|
||||||
|
eventActionsMenu.getItems().clear();
|
||||||
|
eventTooltip.setText(item.getKey().vault().getDisplayName());
|
||||||
|
addAction("generic.action.dismiss", () -> {
|
||||||
|
fileSystemEventAggregator.remove(item.getKey());
|
||||||
|
});
|
||||||
|
switch (item.getValue().mostRecentEvent()) {
|
||||||
|
case ConflictResolvedEvent fse -> this.adjustToConflictResolvedEvent(fse);
|
||||||
|
case ConflictResolutionFailedEvent fse -> this.adjustToConflictEvent(fse);
|
||||||
|
case DecryptionFailedEvent fse -> this.adjustToDecryptionFailedEvent(fse);
|
||||||
|
case BrokenDirFileEvent fse -> this.adjustToBrokenDirFileEvent(fse);
|
||||||
|
case BrokenFileNodeEvent fse -> this.adjustToBrokenFileNodeEvent(fse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void adjustToBrokenFileNodeEvent(BrokenFileNodeEvent bfe) {
|
||||||
|
eventIcon.setValue(FontAwesome5Icon.TIMES);
|
||||||
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message"));
|
||||||
|
eventDescription.setValue(bfe.ciphertextPath().getFileName().toString());
|
||||||
|
if (revealService != null) {
|
||||||
|
addAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, convertVaultPathToSystemPath(bfe.ciphertextPath())));
|
||||||
|
} else {
|
||||||
|
addAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.ciphertextPath()).toString()));
|
||||||
|
}
|
||||||
|
addAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void adjustToConflictResolvedEvent(ConflictResolvedEvent cre) {
|
||||||
|
eventIcon.setValue(FontAwesome5Icon.CHECK);
|
||||||
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message"));
|
||||||
|
eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString());
|
||||||
|
if (revealService != null) {
|
||||||
|
addAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
|
||||||
|
} else {
|
||||||
|
addAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void adjustToConflictEvent(ConflictResolutionFailedEvent cfe) {
|
||||||
|
eventIcon.setValue(FontAwesome5Icon.COMPRESS_ALT);
|
||||||
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message"));
|
||||||
|
eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString());
|
||||||
|
if (revealService != null) {
|
||||||
|
addAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
|
||||||
|
addAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
|
||||||
|
} else {
|
||||||
|
addAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString()));
|
||||||
|
addAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void adjustToDecryptionFailedEvent(DecryptionFailedEvent dfe) {
|
||||||
|
eventIcon.setValue(FontAwesome5Icon.BAN);
|
||||||
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message"));
|
||||||
|
eventDescription.setValue(dfe.ciphertextPath().getFileName().toString());
|
||||||
|
if (revealService != null) {
|
||||||
|
addAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
|
||||||
|
} else {
|
||||||
|
addAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void adjustToBrokenDirFileEvent(BrokenDirFileEvent bde) {
|
||||||
|
eventIcon.setValue(FontAwesome5Icon.TIMES);
|
||||||
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message"));
|
||||||
|
eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString());
|
||||||
|
if (revealService != null) {
|
||||||
|
addAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
|
||||||
|
} else {
|
||||||
|
addAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAction(String localizationKey, Runnable action) {
|
||||||
|
var entry = new MenuItem(resourceBundle.getString(localizationKey));
|
||||||
|
entry.getStyleClass().addLast("dropdown-button-context-menu-item");
|
||||||
|
entry.setOnAction(_ -> action.run());
|
||||||
|
eventActionsMenu.getItems().addLast(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private FontAwesome5Icon selectIcon() {
|
||||||
|
if (vaultUnlocked.getValue()) {
|
||||||
|
return eventIcon.getValue();
|
||||||
|
} else {
|
||||||
|
return FontAwesome5Icon.LOCK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String selectMessage() {
|
||||||
|
if (vaultUnlocked.getValue()) {
|
||||||
|
return eventMessage.getValue();
|
||||||
|
} else {
|
||||||
|
return resourceBundle.getString("eventView.entry.vaultLocked.message");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String selectDescription() {
|
||||||
|
if (vaultUnlocked.getValue()) {
|
||||||
|
return eventDescription.getValue();
|
||||||
|
} else if (eventEntry.getValue() != null) {
|
||||||
|
var e = eventEntry.getValue().getKey();
|
||||||
|
return resourceBundle.getString("eventView.entry.vaultLocked.description").formatted(e != null ? e.vault().getDisplayName() : "");
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void toggleEventActionsMenu() {
|
||||||
|
var e = eventEntry.get();
|
||||||
|
if (e != null) {
|
||||||
|
if (eventActionsMenu.isShowing()) {
|
||||||
|
eventActionsMenu.hide();
|
||||||
|
} else {
|
||||||
|
eventActionsMenu.show(eventActionsButton, Side.BOTTOM, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path convertVaultPathToSystemPath(Path p) {
|
||||||
|
if (!(p instanceof CryptoPath)) {
|
||||||
|
throw new IllegalArgumentException("Path " + p + " is not a vault path");
|
||||||
|
}
|
||||||
|
var v = eventEntry.getValue().getKey().vault();
|
||||||
|
if (!v.isUnlocked()) {
|
||||||
|
return Path.of(System.getProperty("user.home"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var mountUri = v.getMountPoint().uri();
|
||||||
|
var internalPath = p.toString().substring(1);
|
||||||
|
return Path.of(mountUri.getPath().concat(internalPath).substring(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reveal(RevealPathService s, Path p) {
|
||||||
|
try {
|
||||||
|
s.reveal(p);
|
||||||
|
} catch (RevealFailedException e) {
|
||||||
|
LOG.warn("Failed to show path {}", p, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyToClipboard(String s) {
|
||||||
|
var content = new ClipboardContent();
|
||||||
|
content.putString(s);
|
||||||
|
Clipboard.getSystemClipboard().setContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-- property accessors --
|
||||||
|
public ObservableValue<String> messageProperty() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableValue<String> countProperty() {
|
||||||
|
return eventCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCount() {
|
||||||
|
return eventCount.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableValue<String> descriptionProperty() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableValue<FontAwesome5Icon> iconProperty() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FontAwesome5Icon getIcon() {
|
||||||
|
return icon.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableValue<Boolean> actionsButtonVisibleProperty() {
|
||||||
|
return actionsButtonVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActionsButtonVisible() {
|
||||||
|
return actionsButtonVisible.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableValue<String> eventLocalTimeProperty() {
|
||||||
|
return readableTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEventLocalTime() {
|
||||||
|
return readableTime.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableValue<String> eventLocalDateProperty() {
|
||||||
|
return readableDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEventLocalDate() {
|
||||||
|
return readableDate.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableValue<Boolean> vaultUnlockedProperty() {
|
||||||
|
return vaultUnlocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVaultUnlocked() {
|
||||||
|
return vaultUnlocked.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package org.cryptomator.ui.eventview;
|
||||||
|
|
||||||
|
import org.cryptomator.event.FSEventBucket;
|
||||||
|
import org.cryptomator.event.FSEventBucketContent;
|
||||||
|
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.control.ContentDisplay;
|
||||||
|
import javafx.scene.control.ListCell;
|
||||||
|
import javafx.scene.control.ListView;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@EventViewScoped
|
||||||
|
public class EventListCellFactory implements Callback<ListView<Map.Entry<FSEventBucket, FSEventBucketContent>>, ListCell<Map.Entry<FSEventBucket, FSEventBucketContent>>> {
|
||||||
|
|
||||||
|
private static final String FXML_PATH = "/fxml/eventview_cell.fxml";
|
||||||
|
|
||||||
|
private final FxmlLoaderFactory fxmlLoaders;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
EventListCellFactory(@EventViewWindow FxmlLoaderFactory fxmlLoaders) {
|
||||||
|
this.fxmlLoaders = fxmlLoaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListCell<Map.Entry<FSEventBucket, FSEventBucketContent>> call(ListView<Map.Entry<FSEventBucket, FSEventBucketContent>> eventListView) {
|
||||||
|
try {
|
||||||
|
FXMLLoader fxmlLoader = fxmlLoaders.load(FXML_PATH);
|
||||||
|
return new Cell(fxmlLoader.getRoot(), fxmlLoader.getController());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException("Failed to load %s.".formatted(FXML_PATH), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Cell extends ListCell<Map.Entry<FSEventBucket, FSEventBucketContent>> {
|
||||||
|
|
||||||
|
private final Parent root;
|
||||||
|
private final EventListCellController controller;
|
||||||
|
|
||||||
|
public Cell(Parent root, EventListCellController controller) {
|
||||||
|
this.root = root;
|
||||||
|
this.controller = controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateItem(Map.Entry<FSEventBucket, FSEventBucketContent> item, boolean empty) {
|
||||||
|
super.updateItem(item, empty);
|
||||||
|
|
||||||
|
if (empty || item == null) {
|
||||||
|
setGraphic(null);
|
||||||
|
this.getStyleClass().remove("list-cell");
|
||||||
|
} else {
|
||||||
|
this.getStyleClass().addLast("list-cell");
|
||||||
|
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
|
||||||
|
setGraphic(root);
|
||||||
|
controller.setEventEntry(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package org.cryptomator.ui.eventview;
|
||||||
|
|
||||||
|
import dagger.Lazy;
|
||||||
|
import dagger.Subcomponent;
|
||||||
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
|
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
@EventViewScoped
|
||||||
|
@Subcomponent(modules = {EventViewModule.class})
|
||||||
|
public interface EventViewComponent {
|
||||||
|
|
||||||
|
@EventViewWindow
|
||||||
|
Stage window();
|
||||||
|
|
||||||
|
@FxmlScene(FxmlFile.EVENT_VIEW)
|
||||||
|
Lazy<Scene> scene();
|
||||||
|
|
||||||
|
default Stage showEventViewerWindow() {
|
||||||
|
Stage stage = window();
|
||||||
|
stage.setScene(scene().get());
|
||||||
|
stage.sizeToScene();
|
||||||
|
stage.show();
|
||||||
|
stage.requestFocus();
|
||||||
|
return stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcomponent.Factory
|
||||||
|
interface Factory {
|
||||||
|
|
||||||
|
EventViewComponent create();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package org.cryptomator.ui.eventview;
|
||||||
|
|
||||||
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.event.FSEventBucket;
|
||||||
|
import org.cryptomator.event.FSEventBucketContent;
|
||||||
|
import org.cryptomator.event.FileSystemEventAggregator;
|
||||||
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.fxapp.FxFSEventList;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ListChangeListener;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.collections.transformation.FilteredList;
|
||||||
|
import javafx.collections.transformation.SortedList;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.ChoiceBox;
|
||||||
|
import javafx.scene.control.ListView;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
@EventViewScoped
|
||||||
|
public class EventViewController implements FxController {
|
||||||
|
|
||||||
|
private final FilteredList<Map.Entry<FSEventBucket, FSEventBucketContent>> filteredEventList;
|
||||||
|
private final ObservableList<Vault> vaults;
|
||||||
|
private final FileSystemEventAggregator aggregator;
|
||||||
|
private final SortedList<Map.Entry<FSEventBucket, FSEventBucketContent>> sortedEventList;
|
||||||
|
private final ObservableList<Vault> choiceBoxEntries;
|
||||||
|
private final ResourceBundle resourceBundle;
|
||||||
|
private final EventListCellFactory cellFactory;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
ChoiceBox<Vault> vaultFilterChoiceBox;
|
||||||
|
@FXML
|
||||||
|
ListView<Map.Entry<FSEventBucket, FSEventBucketContent>> eventListView;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public EventViewController(FxFSEventList fxFSEventList, ObservableList<Vault> vaults, ResourceBundle resourceBundle, EventListCellFactory cellFactory, FileSystemEventAggregator aggregator) {
|
||||||
|
this.filteredEventList = fxFSEventList.getObservableList().filtered(_ -> true);
|
||||||
|
this.vaults = vaults;
|
||||||
|
this.aggregator = aggregator;
|
||||||
|
this.sortedEventList = new SortedList<>(filteredEventList, this::compareBuckets);
|
||||||
|
this.choiceBoxEntries = FXCollections.observableArrayList();
|
||||||
|
this.resourceBundle = resourceBundle;
|
||||||
|
this.cellFactory = cellFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comparison method for the lru cache. During comparsion the map is accessed.
|
||||||
|
* First the entries are compared by the event timestamp, then vaultId, then identifying path and lastly by class name.
|
||||||
|
*
|
||||||
|
* @param left an entry of a {@link FSEventBucket} and its content
|
||||||
|
* @param right another entry of a {@link FSEventBucket} plus content, compared to {@code left}
|
||||||
|
* @return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
|
||||||
|
*/
|
||||||
|
private int compareBuckets(Map.Entry<FSEventBucket, FSEventBucketContent> left, Map.Entry<FSEventBucket, FSEventBucketContent> right) {
|
||||||
|
var t1 = left.getValue().mostRecentEvent().getTimestamp();
|
||||||
|
var t2 = right.getValue().mostRecentEvent().getTimestamp();
|
||||||
|
var timeComparison = t1.compareTo(t2);
|
||||||
|
if (timeComparison != 0) {
|
||||||
|
return -timeComparison; //we need the reverse timesorting
|
||||||
|
}
|
||||||
|
var vaultIdComparison = left.getKey().vault().getId().compareTo(right.getKey().vault().getId());
|
||||||
|
if (vaultIdComparison != 0) {
|
||||||
|
return vaultIdComparison;
|
||||||
|
}
|
||||||
|
var pathComparison = left.getKey().idPath().compareTo(right.getKey().idPath());
|
||||||
|
if (pathComparison != 0) {
|
||||||
|
return pathComparison;
|
||||||
|
}
|
||||||
|
return left.getKey().c().getName().compareTo(right.getKey().c().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void initialize() {
|
||||||
|
choiceBoxEntries.add(null);
|
||||||
|
choiceBoxEntries.addAll(vaults);
|
||||||
|
vaults.addListener((ListChangeListener<? super Vault>) c -> {
|
||||||
|
while (c.next()) {
|
||||||
|
choiceBoxEntries.removeAll(c.getRemoved());
|
||||||
|
choiceBoxEntries.addAll(c.getAddedSubList());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
eventListView.setCellFactory(cellFactory);
|
||||||
|
eventListView.setItems(sortedEventList);
|
||||||
|
|
||||||
|
vaultFilterChoiceBox.setItems(choiceBoxEntries);
|
||||||
|
vaultFilterChoiceBox.valueProperty().addListener(this::applyVaultFilter);
|
||||||
|
vaultFilterChoiceBox.setConverter(new VaultConverter(resourceBundle));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyVaultFilter(ObservableValue<? extends Vault> v, Vault oldV, Vault newV) {
|
||||||
|
if (newV == null) {
|
||||||
|
filteredEventList.setPredicate(_ -> true);
|
||||||
|
} else {
|
||||||
|
filteredEventList.setPredicate(e -> e.getKey().vault().equals(newV));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void clearEvents() {
|
||||||
|
aggregator.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class VaultConverter extends StringConverter<Vault> {
|
||||||
|
|
||||||
|
private final ResourceBundle resourceBundle;
|
||||||
|
|
||||||
|
VaultConverter(ResourceBundle resourceBundle) {
|
||||||
|
this.resourceBundle = resourceBundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(Vault v) {
|
||||||
|
if (v == null) {
|
||||||
|
return resourceBundle.getString("eventView.filter.allVaults");
|
||||||
|
} else {
|
||||||
|
return v.getDisplayName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vault fromString(String displayLanguage) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package org.cryptomator.ui.eventview;
|
||||||
|
|
||||||
|
import dagger.Binds;
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
import dagger.multibindings.IntoMap;
|
||||||
|
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||||
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.common.FxControllerKey;
|
||||||
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
|
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||||
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
|
import org.cryptomator.ui.common.StageFactory;
|
||||||
|
import org.cryptomator.ui.fxapp.FxFSEventList;
|
||||||
|
|
||||||
|
import javax.inject.Provider;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.stage.Modality;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
abstract class EventViewModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@EventViewScoped
|
||||||
|
@EventViewWindow
|
||||||
|
static Stage provideStage(StageFactory factory, ResourceBundle resourceBundle, FxFSEventList fxFSEventList) {
|
||||||
|
Stage stage = factory.create();
|
||||||
|
stage.setHeight(498);
|
||||||
|
stage.setTitle(resourceBundle.getString("eventView.title"));
|
||||||
|
stage.setResizable(true);
|
||||||
|
stage.initModality(Modality.NONE);
|
||||||
|
stage.focusedProperty().addListener((_,_,isFocused) -> {
|
||||||
|
if(isFocused) {
|
||||||
|
fxFSEventList.unreadEventsProperty().setValue(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@EventViewScoped
|
||||||
|
@EventViewWindow
|
||||||
|
static FxmlLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
|
||||||
|
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@FxmlScene(FxmlFile.EVENT_VIEW)
|
||||||
|
@EventViewScoped
|
||||||
|
static Scene provideEventViewerScene(@EventViewWindow FxmlLoaderFactory fxmlLoaders) {
|
||||||
|
return fxmlLoaders.createScene(FxmlFile.EVENT_VIEW);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(EventViewController.class)
|
||||||
|
abstract FxController bindEventViewController(EventViewController controller);
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(EventListCellController.class)
|
||||||
|
abstract FxController bindEventListCellController(EventListCellController controller);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.cryptomator.ui.eventview;
|
||||||
|
|
||||||
|
import javax.inject.Scope;
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
@Scope
|
||||||
|
@Documented
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@interface EventViewScoped {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package org.cryptomator.ui.eventview;
|
||||||
|
|
||||||
|
import javax.inject.Qualifier;
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Documented
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@interface EventViewWindow {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -43,8 +43,8 @@ public class AutoUnlocker {
|
|||||||
private CompletionStage<Void> unlockSequentially(Stream<Vault> vaultStream) {
|
private CompletionStage<Void> unlockSequentially(Stream<Vault> vaultStream) {
|
||||||
// this is an attempt to run all the unlock workflows sequentially, i.e. start the next workflow only after completing/failing the previous workflow.
|
// this is an attempt to run all the unlock workflows sequentially, i.e. start the next workflow only after completing/failing the previous workflow.
|
||||||
return vaultStream.filter(Vault::isLocked).reduce(CompletableFuture.completedFuture(null),
|
return vaultStream.filter(Vault::isLocked).reduce(CompletableFuture.completedFuture(null),
|
||||||
(prevUnlock, nextVault) -> prevUnlock.thenCompose(unused -> appWindows.startUnlockWorkflow(nextVault, null)),
|
(prevUnlock, nextVault) -> prevUnlock.thenCompose(_ -> appWindows.startUnlockWorkflow(nextVault, null)),
|
||||||
(prevUnlock, nextUnlock) -> nextUnlock.exceptionally(e -> null) // we don't care here about the exception, logged elsewhere
|
(_, nextUnlock) -> nextUnlock.exceptionally(_ -> null) // we don't care here about the exception, logged elsewhere
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,9 +29,10 @@ public class FxApplication {
|
|||||||
private final FxApplicationStyle applicationStyle;
|
private final FxApplicationStyle applicationStyle;
|
||||||
private final FxApplicationTerminator applicationTerminator;
|
private final FxApplicationTerminator applicationTerminator;
|
||||||
private final AutoUnlocker autoUnlocker;
|
private final AutoUnlocker autoUnlocker;
|
||||||
|
private final FxFSEventList fxFSEventList; //not unused! By injecting it here, the object gets initiated the service starts
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
FxApplication(@Named("startupTime") long startupTime, Environment environment, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) {
|
FxApplication(@Named("startupTime") long startupTime, Environment environment, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker, FxFSEventList fxFSEventList) {
|
||||||
this.startupTime = startupTime;
|
this.startupTime = startupTime;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
@@ -41,6 +42,7 @@ public class FxApplication {
|
|||||||
this.applicationStyle = applicationStyle;
|
this.applicationStyle = applicationStyle;
|
||||||
this.applicationTerminator = applicationTerminator;
|
this.applicationTerminator = applicationTerminator;
|
||||||
this.autoUnlocker = autoUnlocker;
|
this.autoUnlocker = autoUnlocker;
|
||||||
|
this.fxFSEventList = fxFSEventList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package org.cryptomator.ui.fxapp;
|
|||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import org.cryptomator.ui.error.ErrorComponent;
|
import org.cryptomator.ui.error.ErrorComponent;
|
||||||
|
import org.cryptomator.ui.eventview.EventViewComponent;
|
||||||
import org.cryptomator.ui.health.HealthCheckComponent;
|
import org.cryptomator.ui.health.HealthCheckComponent;
|
||||||
import org.cryptomator.ui.lock.LockComponent;
|
import org.cryptomator.ui.lock.LockComponent;
|
||||||
import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
||||||
@@ -19,6 +20,9 @@ import org.cryptomator.ui.unlock.UnlockComponent;
|
|||||||
import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
|
import org.cryptomator.ui.updatereminder.UpdateReminderComponent;
|
||||||
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -33,7 +37,8 @@ import java.io.InputStream;
|
|||||||
ErrorComponent.class, //
|
ErrorComponent.class, //
|
||||||
HealthCheckComponent.class, //
|
HealthCheckComponent.class, //
|
||||||
UpdateReminderComponent.class, //
|
UpdateReminderComponent.class, //
|
||||||
ShareVaultComponent.class})
|
ShareVaultComponent.class, //
|
||||||
|
EventViewComponent.class})
|
||||||
abstract class FxApplicationModule {
|
abstract class FxApplicationModule {
|
||||||
|
|
||||||
private static Image createImageFromResource(String resourceName) throws IOException {
|
private static Image createImageFromResource(String resourceName) throws IOException {
|
||||||
@@ -66,4 +71,10 @@ abstract class FxApplicationModule {
|
|||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@FxApplicationScoped
|
||||||
|
static EventViewComponent provideEventViewComponent(EventViewComponent.Factory factory) {
|
||||||
|
return factory.create();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,9 @@ import org.cryptomator.common.vaults.Vault;
|
|||||||
import org.cryptomator.common.vaults.VaultState;
|
import org.cryptomator.common.vaults.VaultState;
|
||||||
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
||||||
import org.cryptomator.ui.dialogs.Dialogs;
|
import org.cryptomator.ui.dialogs.Dialogs;
|
||||||
|
import org.cryptomator.ui.dialogs.SimpleDialog;
|
||||||
import org.cryptomator.ui.error.ErrorComponent;
|
import org.cryptomator.ui.error.ErrorComponent;
|
||||||
|
import org.cryptomator.ui.eventview.EventViewComponent;
|
||||||
import org.cryptomator.ui.lock.LockComponent;
|
import org.cryptomator.ui.lock.LockComponent;
|
||||||
import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
||||||
import org.cryptomator.ui.preferences.PreferencesComponent;
|
import org.cryptomator.ui.preferences.PreferencesComponent;
|
||||||
@@ -51,6 +53,7 @@ public class FxApplicationWindows {
|
|||||||
private final UpdateReminderComponent.Factory updateReminderWindowFactory;
|
private final UpdateReminderComponent.Factory updateReminderWindowFactory;
|
||||||
private final LockComponent.Factory lockWorkflowFactory;
|
private final LockComponent.Factory lockWorkflowFactory;
|
||||||
private final ErrorComponent.Factory errorWindowFactory;
|
private final ErrorComponent.Factory errorWindowFactory;
|
||||||
|
private final Lazy<EventViewComponent> eventViewWindow;
|
||||||
private final ExecutorService executor;
|
private final ExecutorService executor;
|
||||||
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
||||||
private final ShareVaultComponent.Factory shareVaultWindow;
|
private final ShareVaultComponent.Factory shareVaultWindow;
|
||||||
@@ -69,6 +72,7 @@ public class FxApplicationWindows {
|
|||||||
ErrorComponent.Factory errorWindowFactory, //
|
ErrorComponent.Factory errorWindowFactory, //
|
||||||
VaultOptionsComponent.Factory vaultOptionsWindow, //
|
VaultOptionsComponent.Factory vaultOptionsWindow, //
|
||||||
ShareVaultComponent.Factory shareVaultWindow, //
|
ShareVaultComponent.Factory shareVaultWindow, //
|
||||||
|
Lazy<EventViewComponent> eventViewWindow, //
|
||||||
ExecutorService executor, //
|
ExecutorService executor, //
|
||||||
Dialogs dialogs) {
|
Dialogs dialogs) {
|
||||||
this.primaryStage = primaryStage;
|
this.primaryStage = primaryStage;
|
||||||
@@ -80,6 +84,7 @@ public class FxApplicationWindows {
|
|||||||
this.updateReminderWindowFactory = updateReminderWindowFactory;
|
this.updateReminderWindowFactory = updateReminderWindowFactory;
|
||||||
this.lockWorkflowFactory = lockWorkflowFactory;
|
this.lockWorkflowFactory = lockWorkflowFactory;
|
||||||
this.errorWindowFactory = errorWindowFactory;
|
this.errorWindowFactory = errorWindowFactory;
|
||||||
|
this.eventViewWindow = eventViewWindow;
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
this.vaultOptionsWindow = vaultOptionsWindow;
|
this.vaultOptionsWindow = vaultOptionsWindow;
|
||||||
this.shareVaultWindow = shareVaultWindow;
|
this.shareVaultWindow = shareVaultWindow;
|
||||||
@@ -93,17 +98,17 @@ public class FxApplicationWindows {
|
|||||||
|
|
||||||
// register preferences shortcut
|
// register preferences shortcut
|
||||||
if (desktop.isSupported(Desktop.Action.APP_PREFERENCES)) {
|
if (desktop.isSupported(Desktop.Action.APP_PREFERENCES)) {
|
||||||
desktop.setPreferencesHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ANY));
|
desktop.setPreferencesHandler(_ -> showPreferencesWindow(SelectedPreferencesTab.ANY));
|
||||||
}
|
}
|
||||||
|
|
||||||
// register preferences shortcut
|
// register preferences shortcut
|
||||||
if (desktop.isSupported(Desktop.Action.APP_ABOUT)) {
|
if (desktop.isSupported(Desktop.Action.APP_ABOUT)) {
|
||||||
desktop.setAboutHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ABOUT));
|
desktop.setAboutHandler(_ -> showPreferencesWindow(SelectedPreferencesTab.ABOUT));
|
||||||
}
|
}
|
||||||
|
|
||||||
// register app reopen listener
|
// register app reopen listener
|
||||||
if (desktop.isSupported(Desktop.Action.APP_EVENT_REOPENED)) {
|
if (desktop.isSupported(Desktop.Action.APP_EVENT_REOPENED)) {
|
||||||
desktop.addAppEventListener((AppReopenedListener) e -> showMainWindow());
|
desktop.addAppEventListener((AppReopenedListener) _ -> showMainWindow());
|
||||||
}
|
}
|
||||||
|
|
||||||
// observe visible windows
|
// observe visible windows
|
||||||
@@ -135,11 +140,12 @@ public class FxApplicationWindows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CompletionStage<Stage> showVaultOptionsWindow(Vault vault, SelectedVaultOptionsTab tab) {
|
public CompletionStage<Stage> showVaultOptionsWindow(Vault vault, SelectedVaultOptionsTab tab) {
|
||||||
return showMainWindow().thenApplyAsync((window) -> vaultOptionsWindow.create(vault).showVaultOptionsWindow(tab), Platform::runLater).whenComplete(this::reportErrors);
|
return showMainWindow().thenApplyAsync(_ -> vaultOptionsWindow.create(vault).showVaultOptionsWindow(tab), Platform::runLater) //
|
||||||
|
.whenComplete(this::reportErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showQuitWindow(QuitResponse response, boolean forced) {
|
public void showQuitWindow(QuitResponse response, boolean forced) {
|
||||||
CompletableFuture.runAsync(() -> quitWindowBuilder.build().showQuitWindow(response,forced), Platform::runLater);
|
CompletableFuture.runAsync(() -> quitWindowBuilder.build().showQuitWindow(response, forced), Platform::runLater);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showUpdateReminderWindow() {
|
public void showUpdateReminderWindow() {
|
||||||
@@ -147,13 +153,14 @@ public class FxApplicationWindows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void showDokanySupportEndWindow() {
|
public void showDokanySupportEndWindow() {
|
||||||
CompletableFuture.runAsync(() -> dialogs.prepareDokanySupportEndDialog(
|
CompletableFuture.runAsync(() -> createDokanySupportEndDialog().showAndWait(), Platform::runLater);
|
||||||
mainWindow.get().window(),
|
}
|
||||||
stage -> {
|
|
||||||
showPreferencesWindow(SelectedPreferencesTab.VOLUME);
|
private SimpleDialog createDokanySupportEndDialog() {
|
||||||
stage.close();
|
return dialogs.prepareDokanySupportEndDialog(mainWindow.get().window(), stage -> {
|
||||||
}
|
showPreferencesWindow(SelectedPreferencesTab.VOLUME);
|
||||||
).build().showAndWait(), Platform::runLater);
|
stage.close();
|
||||||
|
}).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletionStage<Void> startUnlockWorkflow(Vault vault, @Nullable Stage owner) {
|
public CompletionStage<Void> startUnlockWorkflow(Vault vault, @Nullable Stage owner) {
|
||||||
@@ -162,8 +169,7 @@ public class FxApplicationWindows {
|
|||||||
LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
|
LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
|
||||||
return unlockWorkflowFactory.create(vault, owner).unlockWorkflow();
|
return unlockWorkflowFactory.create(vault, owner).unlockWorkflow();
|
||||||
}, Platform::runLater) //
|
}, Platform::runLater) //
|
||||||
.thenAcceptAsync(UnlockWorkflow::run, executor)
|
.thenAcceptAsync(UnlockWorkflow::run, executor).exceptionally(e -> {
|
||||||
.exceptionally(e -> {
|
|
||||||
showErrorWindow(e, owner == null ? primaryStage : owner, null);
|
showErrorWindow(e, owner == null ? primaryStage : owner, null);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@@ -182,6 +188,11 @@ public class FxApplicationWindows {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public CompletionStage<Stage> showEventViewer() {
|
||||||
|
return CompletableFuture.supplyAsync(() -> eventViewWindow.get().showEventViewerWindow(), Platform::runLater).whenComplete(this::reportErrors);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays the generic error scene in the given window.
|
* Displays the generic error scene in the given window.
|
||||||
*
|
*
|
||||||
@@ -199,5 +210,4 @@ public class FxApplicationWindows {
|
|||||||
LOG.error("Failed to display stage", error);
|
LOG.error("Failed to display stage", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package org.cryptomator.ui.fxapp;
|
||||||
|
|
||||||
|
import org.cryptomator.event.FSEventBucket;
|
||||||
|
import org.cryptomator.event.FSEventBucketContent;
|
||||||
|
import org.cryptomator.event.FileSystemEventAggregator;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@FxApplicationScoped
|
||||||
|
public class FxFSEventList {
|
||||||
|
|
||||||
|
private final ObservableList<Map.Entry<FSEventBucket, FSEventBucketContent>> events;
|
||||||
|
private final FileSystemEventAggregator eventAggregator;
|
||||||
|
private final ScheduledFuture<?> scheduledTask;
|
||||||
|
private final BooleanProperty unreadEvents;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public FxFSEventList(FileSystemEventAggregator fsEventAggregator, ScheduledExecutorService scheduler) {
|
||||||
|
this.events = FXCollections.observableArrayList();
|
||||||
|
this.eventAggregator = fsEventAggregator;
|
||||||
|
this.unreadEvents = new SimpleBooleanProperty(false);
|
||||||
|
this.scheduledTask = scheduler.scheduleWithFixedDelay(() -> {
|
||||||
|
if (fsEventAggregator.hasUpdates()) {
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
}, 1000, 1000, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the clone task on the FX thread and wait till it is completed
|
||||||
|
*/
|
||||||
|
private void flush() {
|
||||||
|
var latch = new CountDownLatch(1);
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
eventAggregator.cloneTo(events);
|
||||||
|
unreadEvents.setValue(true);
|
||||||
|
latch.countDown();
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
latch.await();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableList<Map.Entry<FSEventBucket, FSEventBucketContent>> getObservableList() {
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty unreadEventsProperty() {
|
||||||
|
return unreadEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ package org.cryptomator.ui.keyloading;
|
|||||||
import org.cryptomator.cryptolib.api.Masterkey;
|
import org.cryptomator.cryptolib.api.Masterkey;
|
||||||
import org.cryptomator.cryptolib.api.MasterkeyLoader;
|
import org.cryptomator.cryptolib.api.MasterkeyLoader;
|
||||||
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
||||||
|
import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy;
|
||||||
|
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -28,6 +30,33 @@ public interface KeyLoadingStrategy extends MasterkeyLoader {
|
|||||||
@Override
|
@Override
|
||||||
Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException;
|
Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the provided key loader scheme corresponds to a Hub Vault.
|
||||||
|
* <p>
|
||||||
|
* This method compares the {@code keyLoader} parameter with the known Hub Vault schemes
|
||||||
|
* {@link HubKeyLoadingStrategy#SCHEME_HUB_HTTP} and {@link HubKeyLoadingStrategy#SCHEME_HUB_HTTPS}.
|
||||||
|
*
|
||||||
|
* @param keyLoader A string representing the key loader scheme to be checked.
|
||||||
|
* @return {@code true} if the given key loader scheme represents a Hub Vault; {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
static boolean isHubVault(String keyLoader) {
|
||||||
|
return HubKeyLoadingStrategy.SCHEME_HUB_HTTP.equals(keyLoader) || HubKeyLoadingStrategy.SCHEME_HUB_HTTPS.equals(keyLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the provided key loader scheme corresponds to a Masterkey File Vault.
|
||||||
|
* <p>
|
||||||
|
* This method checks if the {@code keyLoader} parameter matches the known Masterkey File Vault scheme
|
||||||
|
* {@link MasterkeyFileLoadingStrategy#SCHEME}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param keyLoader A string representing the key loader scheme to be checked.
|
||||||
|
* @return {@code true} if the given key loader scheme represents a Masterkey File Vault; {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
static boolean isMasterkeyFileVault(String keyLoader) {
|
||||||
|
return MasterkeyFileLoadingStrategy.SCHEME.equals(keyLoader);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows the loader to try and recover from an exception thrown during the last attempt.
|
* Allows the loader to try and recover from an exception thrown during the last attempt.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -165,6 +165,7 @@ public class ReceiveKeyController implements FxController {
|
|||||||
var vaultKeyUri = hubConfig.URIs.API.resolve("vaults/" + vaultId + "/access-token");
|
var vaultKeyUri = hubConfig.URIs.API.resolve("vaults/" + vaultId + "/access-token");
|
||||||
var request = HttpRequest.newBuilder(vaultKeyUri) //
|
var request = HttpRequest.newBuilder(vaultKeyUri) //
|
||||||
.header("Authorization", "Bearer " + bearerToken) //
|
.header("Authorization", "Bearer " + bearerToken) //
|
||||||
|
.header("Hub-Device-ID", deviceId) //
|
||||||
.GET() //
|
.GET() //
|
||||||
.timeout(REQ_TIMEOUT) //
|
.timeout(REQ_TIMEOUT) //
|
||||||
.build();
|
.build();
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
package org.cryptomator.ui.mainwindow;
|
package org.cryptomator.ui.mainwindow;
|
||||||
|
|
||||||
import javafx.beans.Observable;
|
|
||||||
import javafx.beans.binding.BooleanBinding;
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.scene.layout.StackPane;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.LicenseHolder;
|
import org.cryptomator.common.LicenseHolder;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
@@ -21,6 +13,17 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javafx.beans.Observable;
|
||||||
|
import javafx.beans.binding.BooleanBinding;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Rectangle2D;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.stage.Screen;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import javafx.stage.WindowEvent;
|
||||||
|
|
||||||
@MainWindowScoped
|
@MainWindowScoped
|
||||||
public class MainWindowController implements FxController {
|
public class MainWindowController implements FxController {
|
||||||
@@ -63,27 +66,76 @@ public class MainWindowController implements FxController {
|
|||||||
}
|
}
|
||||||
window.focusedProperty().addListener(this::mainWindowFocusChanged);
|
window.focusedProperty().addListener(this::mainWindowFocusChanged);
|
||||||
|
|
||||||
if (!neverTouched()) {
|
int x = settings.windowXPosition.get();
|
||||||
window.setHeight(settings.windowHeight.get() > window.getMinHeight() ? settings.windowHeight.get() : window.getMinHeight());
|
int y = settings.windowYPosition.get();
|
||||||
window.setWidth(settings.windowWidth.get() > window.getMinWidth() ? settings.windowWidth.get() : window.getMinWidth());
|
int width = settings.windowWidth.get();
|
||||||
window.setX(settings.windowXPosition.get());
|
int height = settings.windowHeight.get();
|
||||||
window.setY(settings.windowYPosition.get());
|
if (windowPositionSaved(x, y, width, height)) {
|
||||||
|
window.setX(x);
|
||||||
|
window.setY(y);
|
||||||
|
window.setWidth(Math.clamp(width, window.getMinWidth(), window.getMaxWidth()));
|
||||||
|
window.setHeight(Math.clamp(height, window.getMinHeight(), window.getMaxHeight()));
|
||||||
}
|
}
|
||||||
window.widthProperty().addListener((_, _, _) -> savePositionalSettings());
|
|
||||||
window.heightProperty().addListener((_, _, _) -> savePositionalSettings());
|
window.setOnShowing(this::checkDisplayBounds);
|
||||||
window.xProperty().addListener((_, _, _) -> savePositionalSettings());
|
|
||||||
window.yProperty().addListener((_, _, _) -> savePositionalSettings());
|
settings.windowXPosition.bind(window.xProperty());
|
||||||
|
settings.windowYPosition.bind(window.yProperty());
|
||||||
|
settings.windowWidth.bind(window.widthProperty());
|
||||||
|
settings.windowHeight.bind(window.heightProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean neverTouched() {
|
private boolean windowPositionSaved(int x, int y, int width, int height) {
|
||||||
return (settings.windowHeight.get() == 0) && (settings.windowWidth.get() == 0) && (settings.windowXPosition.get() == 0) && (settings.windowYPosition.get() == 0);
|
return x != 0 || y != 0 || width != 0 || height != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void savePositionalSettings() {
|
private void checkDisplayBounds(WindowEvent windowEvent) {
|
||||||
settings.windowWidth.setValue(window.getWidth());
|
int x = settings.windowXPosition.get();
|
||||||
settings.windowHeight.setValue(window.getHeight());
|
int y = settings.windowYPosition.get();
|
||||||
settings.windowXPosition.setValue(window.getX());
|
int width = settings.windowWidth.get();
|
||||||
settings.windowYPosition.setValue(window.getY());
|
int height = settings.windowHeight.get();
|
||||||
|
|
||||||
|
// Minimizing a window in Windows and closing it could result in an out of bounds position at (x, y) = (-32000, -32000)
|
||||||
|
// See https://devblogs.microsoft.com/oldnewthing/20041028-00/?p=37453
|
||||||
|
// If the position is (-32000, -32000), restore to the last saved position
|
||||||
|
if (window.getX() == -32000 && window.getY() == -32000) {
|
||||||
|
window.setX(x);
|
||||||
|
window.setY(y);
|
||||||
|
window.setWidth(width);
|
||||||
|
window.setHeight(height);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds();
|
||||||
|
if (!isWithinDisplayBounds(x, y, width, height)) { //use stored window position
|
||||||
|
LOG.debug("Resetting window position due to insufficient screen overlap");
|
||||||
|
var centeredX = (primaryScreenBounds.getWidth() - window.getMinWidth()) / 2;
|
||||||
|
var centeredY = (primaryScreenBounds.getHeight() - window.getMinHeight()) / 2;
|
||||||
|
//check if we can keep width and height
|
||||||
|
if (isWithinDisplayBounds((int) centeredX, (int) centeredY, width, height)) {
|
||||||
|
//if so, keep window size
|
||||||
|
window.setWidth(Math.clamp(width, window.getMinWidth(), window.getMaxWidth()));
|
||||||
|
window.setHeight(Math.clamp(height, window.getMinHeight(), window.getMaxHeight()));
|
||||||
|
}
|
||||||
|
//reset position of upper left corner
|
||||||
|
window.setX(centeredX);
|
||||||
|
window.setY(centeredY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWithinDisplayBounds(int x, int y, int width, int height) {
|
||||||
|
// define a rect which is inset on all sides from the window's rect:
|
||||||
|
final int shrinkedX = x + 20; // 20px left
|
||||||
|
final int shrinkedY = y + 5; // 5px top
|
||||||
|
final int shrinkedWidth = width - 40; // 20px left + 20px right
|
||||||
|
final int shrinkedHeigth = height - 25; // 5px top + 20px bottom
|
||||||
|
return isRectangleWithinBounds(shrinkedX, shrinkedY, 0, shrinkedHeigth) // Left pixel column
|
||||||
|
&& isRectangleWithinBounds(shrinkedX + shrinkedWidth, shrinkedY, 0, shrinkedHeigth) // Right pixel column
|
||||||
|
&& isRectangleWithinBounds(shrinkedX, shrinkedY, shrinkedWidth, 0) // Top pixel row
|
||||||
|
&& isRectangleWithinBounds(shrinkedX, shrinkedY + shrinkedHeigth, shrinkedWidth, 0); // Bottom pixel row
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isRectangleWithinBounds(int x, int y, int width, int height) {
|
||||||
|
return !Screen.getScreensForRectangle(x, y, width, height).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mainWindowFocusChanged(Observable observable) {
|
private void mainWindowFocusChanged(Observable observable) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.cryptomator.ui.mainwindow;
|
package org.cryptomator.ui.mainwindow;
|
||||||
|
|
||||||
import dagger.Binds;
|
import dagger.Binds;
|
||||||
|
import dagger.Lazy;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import dagger.multibindings.IntoMap;
|
import dagger.multibindings.IntoMap;
|
||||||
@@ -14,9 +15,11 @@ import org.cryptomator.ui.common.FxmlScene;
|
|||||||
import org.cryptomator.ui.common.StageFactory;
|
import org.cryptomator.ui.common.StageFactory;
|
||||||
import org.cryptomator.ui.common.StageInitializer;
|
import org.cryptomator.ui.common.StageInitializer;
|
||||||
import org.cryptomator.ui.error.ErrorComponent;
|
import org.cryptomator.ui.error.ErrorComponent;
|
||||||
|
import org.cryptomator.ui.fxapp.FxApplicationTerminator;
|
||||||
import org.cryptomator.ui.fxapp.PrimaryStage;
|
import org.cryptomator.ui.fxapp.PrimaryStage;
|
||||||
import org.cryptomator.ui.migration.MigrationComponent;
|
import org.cryptomator.ui.migration.MigrationComponent;
|
||||||
import org.cryptomator.ui.stats.VaultStatisticsComponent;
|
import org.cryptomator.ui.stats.VaultStatisticsComponent;
|
||||||
|
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
||||||
import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent;
|
import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
@@ -35,11 +38,19 @@ abstract class MainWindowModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@MainWindow
|
@MainWindow
|
||||||
@MainWindowScoped
|
@MainWindowScoped
|
||||||
static Stage provideMainWindow(@PrimaryStage Stage stage, StageInitializer initializer) {
|
static Stage provideMainWindow(@PrimaryStage Stage stage, StageInitializer initializer, FxApplicationTerminator terminator, Lazy<TrayMenuComponent> trayMenu) {
|
||||||
initializer.accept(stage);
|
initializer.accept(stage);
|
||||||
stage.setTitle("Cryptomator");
|
stage.setTitle("Cryptomator");
|
||||||
stage.setMinWidth(650);
|
stage.setMinWidth(650);
|
||||||
stage.setMinHeight(498);
|
stage.setMinHeight(498);
|
||||||
|
stage.setOnCloseRequest(e -> {
|
||||||
|
if (!trayMenu.get().isInitialized()) {
|
||||||
|
terminator.terminate();
|
||||||
|
e.consume();
|
||||||
|
} else {
|
||||||
|
stage.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,16 @@ import javax.inject.Inject;
|
|||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
|
||||||
// unscoped because each cell needs its own controller
|
// unscoped because each cell needs its own controller
|
||||||
public class VaultListCellController implements FxController {
|
public class VaultListCellController implements FxController {
|
||||||
|
|
||||||
|
private static final Insets COMPACT_INSETS = new Insets(6, 12, 6, 12);
|
||||||
|
private static final Insets DEFAULT_INSETS = new Insets(12);
|
||||||
|
|
||||||
private final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
|
private final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
|
||||||
private final ObservableValue<FontAwesome5Icon> glyph;
|
private final ObservableValue<FontAwesome5Icon> glyph;
|
||||||
private final ObservableValue<Boolean> compactMode;
|
private final ObservableValue<Boolean> compactMode;
|
||||||
@@ -25,6 +31,8 @@ public class VaultListCellController implements FxController {
|
|||||||
|
|
||||||
/* FXML */
|
/* FXML */
|
||||||
public FontAwesome5IconView vaultStateView;
|
public FontAwesome5IconView vaultStateView;
|
||||||
|
@FXML
|
||||||
|
public HBox vaultListCell;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
VaultListCellController(Settings settings) {
|
VaultListCellController(Settings settings) {
|
||||||
@@ -37,6 +45,7 @@ public class VaultListCellController implements FxController {
|
|||||||
.onCondition(vault.flatMap(Vault::stateProperty).map(VaultState.Value.PROCESSING::equals).orElse(false)) //
|
.onCondition(vault.flatMap(Vault::stateProperty).map(VaultState.Value.PROCESSING::equals).orElse(false)) //
|
||||||
.afterStop(() -> vaultStateView.setRotate(0)) //
|
.afterStop(() -> vaultStateView.setRotate(0)) //
|
||||||
.build();
|
.build();
|
||||||
|
this.vaultListCell.paddingProperty().bind(compactMode.map(c -> c ? COMPACT_INSETS : DEFAULT_INSETS));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO deduplicate w/ VaultDetailController
|
// TODO deduplicate w/ VaultDetailController
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
|
|||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.VaultService;
|
import org.cryptomator.ui.common.VaultService;
|
||||||
import org.cryptomator.ui.dialogs.Dialogs;
|
import org.cryptomator.ui.dialogs.Dialogs;
|
||||||
|
import org.cryptomator.ui.fxapp.FxFSEventList;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -26,6 +27,7 @@ import javafx.collections.ListChangeListener;
|
|||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.geometry.Side;
|
import javafx.geometry.Side;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.ContextMenu;
|
import javafx.scene.control.ContextMenu;
|
||||||
import javafx.scene.control.ListView;
|
import javafx.scene.control.ListView;
|
||||||
import javafx.scene.input.ContextMenuEvent;
|
import javafx.scene.input.ContextMenuEvent;
|
||||||
@@ -34,7 +36,6 @@ import javafx.scene.input.KeyCode;
|
|||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
import javafx.scene.input.MouseEvent;
|
import javafx.scene.input.MouseEvent;
|
||||||
import javafx.scene.input.TransferMode;
|
import javafx.scene.input.TransferMode;
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -66,6 +67,7 @@ public class VaultListController implements FxController {
|
|||||||
private final VaultListCellFactory cellFactory;
|
private final VaultListCellFactory cellFactory;
|
||||||
private final AddVaultWizardComponent.Builder addVaultWizard;
|
private final AddVaultWizardComponent.Builder addVaultWizard;
|
||||||
private final BooleanBinding emptyVaultList;
|
private final BooleanBinding emptyVaultList;
|
||||||
|
private final BooleanProperty unreadEvents;
|
||||||
private final VaultListManager vaultListManager;
|
private final VaultListManager vaultListManager;
|
||||||
private final BooleanProperty draggingVaultOver = new SimpleBooleanProperty();
|
private final BooleanProperty draggingVaultOver = new SimpleBooleanProperty();
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
@@ -76,7 +78,7 @@ public class VaultListController implements FxController {
|
|||||||
public ListView<Vault> vaultList;
|
public ListView<Vault> vaultList;
|
||||||
public StackPane root;
|
public StackPane root;
|
||||||
@FXML
|
@FXML
|
||||||
private HBox addVaultButton;
|
private Button addVaultButton;
|
||||||
@FXML
|
@FXML
|
||||||
private ContextMenu addVaultContextMenu;
|
private ContextMenu addVaultContextMenu;
|
||||||
|
|
||||||
@@ -91,7 +93,8 @@ public class VaultListController implements FxController {
|
|||||||
ResourceBundle resourceBundle, //
|
ResourceBundle resourceBundle, //
|
||||||
FxApplicationWindows appWindows, //
|
FxApplicationWindows appWindows, //
|
||||||
Settings settings, //
|
Settings settings, //
|
||||||
Dialogs dialogs) {
|
Dialogs dialogs, //
|
||||||
|
FxFSEventList fxFSEventList) {
|
||||||
this.mainWindow = mainWindow;
|
this.mainWindow = mainWindow;
|
||||||
this.vaults = vaults;
|
this.vaults = vaults;
|
||||||
this.selectedVault = selectedVault;
|
this.selectedVault = selectedVault;
|
||||||
@@ -104,6 +107,7 @@ public class VaultListController implements FxController {
|
|||||||
this.dialogs = dialogs;
|
this.dialogs = dialogs;
|
||||||
|
|
||||||
this.emptyVaultList = Bindings.isEmpty(vaults);
|
this.emptyVaultList = Bindings.isEmpty(vaults);
|
||||||
|
this.unreadEvents = fxFSEventList.unreadEventsProperty();
|
||||||
|
|
||||||
selectedVault.addListener(this::selectedVaultDidChange);
|
selectedVault.addListener(this::selectedVaultDidChange);
|
||||||
cellSize = settings.compactMode.map(compact -> compact ? 30.0 : 60.0);
|
cellSize = settings.compactMode.map(compact -> compact ? 30.0 : 60.0);
|
||||||
@@ -263,6 +267,11 @@ public class VaultListController implements FxController {
|
|||||||
appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY);
|
appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void showEventViewer() {
|
||||||
|
appWindows.showEventViewer();
|
||||||
|
unreadEvents.setValue(false);
|
||||||
|
}
|
||||||
// Getter and Setter
|
// Getter and Setter
|
||||||
|
|
||||||
public BooleanBinding emptyVaultListProperty() {
|
public BooleanBinding emptyVaultListProperty() {
|
||||||
@@ -289,4 +298,11 @@ public class VaultListController implements FxController {
|
|||||||
return cellSize.getValue();
|
return cellSize.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ObservableValue<Boolean> unreadEventsPresentProperty() {
|
||||||
|
return unreadEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getUnreadEventsPresent() {
|
||||||
|
return unreadEvents.getValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import javafx.fxml.FXML;
|
|||||||
public class WelcomeController implements FxController {
|
public class WelcomeController implements FxController {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(WelcomeController.class);
|
private static final Logger LOG = LoggerFactory.getLogger(WelcomeController.class);
|
||||||
private static final String GETTING_STARTED_URI = "https://docs.cryptomator.org/en/1.7/desktop/getting-started/";
|
private static final String GETTING_STARTED_URI = "https://docs.cryptomator.org/desktop/getting-started/";
|
||||||
|
|
||||||
private final Application application;
|
private final Application application;
|
||||||
private final BooleanBinding noVaultPresent;
|
private final BooleanBinding noVaultPresent;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import javafx.stage.Stage;
|
|||||||
|
|
||||||
public class MigrationImpossibleController implements FxController {
|
public class MigrationImpossibleController implements FxController {
|
||||||
|
|
||||||
private static final String HELP_URI = "https://docs.cryptomator.org/en/1.7/help/manual-migration/";
|
private static final String HELP_URI = "https://docs.cryptomator.org/help/manual-migration/";
|
||||||
|
|
||||||
private final Application application;
|
private final Application application;
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import javafx.beans.value.ObservableValue;
|
|||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.control.ContentDisplay;
|
import javafx.scene.control.ContentDisplay;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -32,7 +34,10 @@ import java.util.ResourceBundle;
|
|||||||
@PreferencesScoped
|
@PreferencesScoped
|
||||||
public class UpdatesPreferencesController implements FxController {
|
public class UpdatesPreferencesController implements FxController {
|
||||||
|
|
||||||
private static final String DOWNLOADS_URI = "https://cryptomator.org/downloads";
|
private static final String DOWNLOADS_URI_TEMPLATE = "https://cryptomator.org/downloads/" //
|
||||||
|
+ "?utm_source=cryptomator-desktop" //
|
||||||
|
+ "&utm_medium=update-notification&" //
|
||||||
|
+ "utm_campaign=app-update-%s";
|
||||||
|
|
||||||
private final Application application;
|
private final Application application;
|
||||||
private final Environment environment;
|
private final Environment environment;
|
||||||
@@ -50,6 +55,7 @@ public class UpdatesPreferencesController implements FxController {
|
|||||||
private final BooleanProperty upToDateLabelVisible = new SimpleBooleanProperty(false);
|
private final BooleanProperty upToDateLabelVisible = new SimpleBooleanProperty(false);
|
||||||
private final DateTimeFormatter formatter;
|
private final DateTimeFormatter formatter;
|
||||||
private final BooleanBinding upToDate;
|
private final BooleanBinding upToDate;
|
||||||
|
private final String downloadsUri;
|
||||||
|
|
||||||
/* FXML */
|
/* FXML */
|
||||||
public CheckBox checkForUpdatesCheckbox;
|
public CheckBox checkForUpdatesCheckbox;
|
||||||
@@ -65,12 +71,13 @@ public class UpdatesPreferencesController implements FxController {
|
|||||||
this.latestVersion = updateChecker.latestVersionProperty();
|
this.latestVersion = updateChecker.latestVersionProperty();
|
||||||
this.lastSuccessfulUpdateCheck = updateChecker.lastSuccessfulUpdateCheckProperty();
|
this.lastSuccessfulUpdateCheck = updateChecker.lastSuccessfulUpdateCheckProperty();
|
||||||
this.timeDifferenceMessage = Bindings.createStringBinding(this::getTimeDifferenceMessage, lastSuccessfulUpdateCheck);
|
this.timeDifferenceMessage = Bindings.createStringBinding(this::getTimeDifferenceMessage, lastSuccessfulUpdateCheck);
|
||||||
this.currentVersion = updateChecker.getCurrentVersion();
|
this.currentVersion = environment.getAppVersion();
|
||||||
this.updateAvailable = updateChecker.updateAvailableProperty();
|
this.updateAvailable = updateChecker.updateAvailableProperty();
|
||||||
this.formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
|
this.formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
|
||||||
this.upToDate = updateChecker.updateCheckStateProperty().isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL).and(latestVersion.isEqualTo(currentVersion));
|
this.upToDate = updateChecker.updateCheckStateProperty().isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL).and(latestVersion.isEqualTo(currentVersion));
|
||||||
this.checkFailed = updateChecker.checkFailedProperty();
|
this.checkFailed = updateChecker.checkFailedProperty();
|
||||||
this.lastUpdateCheckMessage = Bindings.createStringBinding(this::getLastUpdateCheckMessage, lastSuccessfulUpdateCheck);
|
this.lastUpdateCheckMessage = Bindings.createStringBinding(this::getLastUpdateCheckMessage, lastSuccessfulUpdateCheck);
|
||||||
|
this.downloadsUri = DOWNLOADS_URI_TEMPLATE.formatted(URLEncoder.encode(currentVersion, StandardCharsets.US_ASCII));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
@@ -93,7 +100,7 @@ public class UpdatesPreferencesController implements FxController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void visitDownloadsPage() {
|
public void visitDownloadsPage() {
|
||||||
application.getHostServices().showDocument(DOWNLOADS_URI);
|
application.getHostServices().showDocument(downloadsUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import java.util.ResourceBundle;
|
|||||||
@PreferencesScoped
|
@PreferencesScoped
|
||||||
public class VolumePreferencesController implements FxController {
|
public class VolumePreferencesController implements FxController {
|
||||||
|
|
||||||
public static final String DOCS_MOUNTING_URL = "https://docs.cryptomator.org/en/1.7/desktop/volume-type/";
|
public static final String DOCS_MOUNTING_URL = "https://docs.cryptomator.org/desktop/volume-type/";
|
||||||
public static final int MIN_PORT = 1024;
|
public static final int MIN_PORT = 1024;
|
||||||
public static final int MAX_PORT = 65535;
|
public static final int MAX_PORT = 65535;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package org.cryptomator.ui.sharevault;
|
|||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy;
|
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
@@ -33,8 +33,7 @@ public class ShareVaultController implements FxController {
|
|||||||
this.window = window;
|
this.window = window;
|
||||||
this.application = application;
|
this.application = application;
|
||||||
this.vault = vault;
|
this.vault = vault;
|
||||||
var vaultScheme = vault.getVaultConfigCache().getUnchecked().getKeyId().getScheme();
|
this.hubVault = KeyLoadingStrategy.isHubVault(vault.getVaultSettings().lastKnownKeyLoader.get());
|
||||||
this.hubVault = (vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTP) || vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTPS));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import org.cryptomator.integrations.mount.MountFailedException;
|
|||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
import org.cryptomator.ui.common.VaultService;
|
import org.cryptomator.ui.common.VaultService;
|
||||||
|
import org.cryptomator.ui.dialogs.Dialogs;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||||
import org.cryptomator.ui.fxapp.PrimaryStage;
|
import org.cryptomator.ui.fxapp.PrimaryStage;
|
||||||
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
||||||
@@ -25,6 +26,8 @@ import javafx.scene.Scene;
|
|||||||
import javafx.stage.Screen;
|
import javafx.stage.Screen;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.ReadOnlyFileSystemException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A multi-step task that consists of background activities as well as user interaction.
|
* A multi-step task that consists of background activities as well as user interaction.
|
||||||
@@ -46,6 +49,7 @@ public class UnlockWorkflow extends Task<Void> {
|
|||||||
private final FxApplicationWindows appWindows;
|
private final FxApplicationWindows appWindows;
|
||||||
private final KeyLoadingStrategy keyLoadingStrategy;
|
private final KeyLoadingStrategy keyLoadingStrategy;
|
||||||
private final ObjectProperty<IllegalMountPointException> illegalMountPointException;
|
private final ObjectProperty<IllegalMountPointException> illegalMountPointException;
|
||||||
|
private final Dialogs dialogs;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
UnlockWorkflow(@PrimaryStage Stage mainWindow, //
|
UnlockWorkflow(@PrimaryStage Stage mainWindow, //
|
||||||
@@ -57,7 +61,8 @@ public class UnlockWorkflow extends Task<Void> {
|
|||||||
@FxmlScene(FxmlFile.UNLOCK_REQUIRES_RESTART) Lazy<Scene> restartRequiredScene, //
|
@FxmlScene(FxmlFile.UNLOCK_REQUIRES_RESTART) Lazy<Scene> restartRequiredScene, //
|
||||||
FxApplicationWindows appWindows, //
|
FxApplicationWindows appWindows, //
|
||||||
@UnlockWindow KeyLoadingStrategy keyLoadingStrategy, //
|
@UnlockWindow KeyLoadingStrategy keyLoadingStrategy, //
|
||||||
@UnlockWindow ObjectProperty<IllegalMountPointException> illegalMountPointException) {
|
@UnlockWindow ObjectProperty<IllegalMountPointException> illegalMountPointException, //
|
||||||
|
Dialogs dialogs) {
|
||||||
this.mainWindow = mainWindow;
|
this.mainWindow = mainWindow;
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.vault = vault;
|
this.vault = vault;
|
||||||
@@ -68,6 +73,7 @@ public class UnlockWorkflow extends Task<Void> {
|
|||||||
this.appWindows = appWindows;
|
this.appWindows = appWindows;
|
||||||
this.keyLoadingStrategy = keyLoadingStrategy;
|
this.keyLoadingStrategy = keyLoadingStrategy;
|
||||||
this.illegalMountPointException = illegalMountPointException;
|
this.illegalMountPointException = illegalMountPointException;
|
||||||
|
this.dialogs = dialogs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -144,11 +150,36 @@ public class UnlockWorkflow extends Task<Void> {
|
|||||||
switch (throwable) {
|
switch (throwable) {
|
||||||
case IllegalMountPointException e -> handleIllegalMountPointError(e);
|
case IllegalMountPointException e -> handleIllegalMountPointError(e);
|
||||||
case ConflictingMountServiceException _ -> handleConflictingMountServiceException();
|
case ConflictingMountServiceException _ -> handleConflictingMountServiceException();
|
||||||
|
case ReadOnlyFileSystemException _ -> handleReadOnlyFileSystem();
|
||||||
default -> handleGenericError(throwable);
|
default -> handleGenericError(throwable);
|
||||||
}
|
}
|
||||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
|
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleReadOnlyFileSystem() {
|
||||||
|
var readOnlyDialog = dialogs.prepareRetryIfReadonlyDialog(mainWindow, stage -> {
|
||||||
|
stage.close();
|
||||||
|
this.retry();
|
||||||
|
}).build();
|
||||||
|
|
||||||
|
Platform.runLater(readOnlyDialog::showAndWait);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void retry() {
|
||||||
|
try {
|
||||||
|
vault.getVaultSettings().usesReadOnlyMode.set(true);
|
||||||
|
var isLocked = vault.stateProperty().awaitState(VaultState.Value.LOCKED, 5, TimeUnit.SECONDS);
|
||||||
|
if (!isLocked) {
|
||||||
|
LOG.error("Vault did not changed to LOCKED state within 5 seconds. Aborting unlock retry.");
|
||||||
|
} else {
|
||||||
|
appWindows.startUnlockWorkflow(vault, mainWindow);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.error("Waiting for LOCKED vault state was interrupted. Aborting unlock retry.", e);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void cancelled() {
|
protected void cancelled() {
|
||||||
LOG.debug("Unlock of '{}' canceled.", vault.getDisplayName());
|
LOG.debug("Unlock of '{}' canceled.", vault.getDisplayName());
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import org.cryptomator.ui.preferences.VolumePreferencesController;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.binding.BooleanBinding;
|
||||||
|
import javafx.beans.value.ObservableBooleanValue;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
@@ -50,7 +52,6 @@ public class MountOptionsController implements FxController {
|
|||||||
private final ObservableValue<String> defaultMountFlags;
|
private final ObservableValue<String> defaultMountFlags;
|
||||||
private final ObservableValue<Boolean> mountpointDirSupported;
|
private final ObservableValue<Boolean> mountpointDirSupported;
|
||||||
private final ObservableValue<Boolean> mountpointDriveLetterSupported;
|
private final ObservableValue<Boolean> mountpointDriveLetterSupported;
|
||||||
private final ObservableValue<Boolean> readOnlySupported;
|
|
||||||
private final ObservableValue<Boolean> mountFlagsSupported;
|
private final ObservableValue<Boolean> mountFlagsSupported;
|
||||||
private final ObservableValue<Boolean> defaultMountServiceSelected;
|
private final ObservableValue<Boolean> defaultMountServiceSelected;
|
||||||
private final ObservableValue<String> directoryPath;
|
private final ObservableValue<String> directoryPath;
|
||||||
@@ -60,6 +61,7 @@ public class MountOptionsController implements FxController {
|
|||||||
private final ObservableValue<MountService> selectedMountService;
|
private final ObservableValue<MountService> selectedMountService;
|
||||||
private final ObservableValue<Boolean> selectedMountServiceRequiresRestart;
|
private final ObservableValue<Boolean> selectedMountServiceRequiresRestart;
|
||||||
private final ObservableValue<Boolean> loopbackPortChangeable;
|
private final ObservableValue<Boolean> loopbackPortChangeable;
|
||||||
|
private final ObservableBooleanValue readOnlyOptionAllowed;
|
||||||
|
|
||||||
|
|
||||||
//-- FXML objects --
|
//-- FXML objects --
|
||||||
@@ -108,10 +110,10 @@ public class MountOptionsController implements FxController {
|
|||||||
});
|
});
|
||||||
this.mountFlagsSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_FLAGS));
|
this.mountFlagsSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_FLAGS));
|
||||||
this.defaultMountServiceSelected = ObservableUtil.mapWithDefault(vaultSettings.mountService, _ -> false, true);
|
this.defaultMountServiceSelected = ObservableUtil.mapWithDefault(vaultSettings.mountService, _ -> false, true);
|
||||||
this.readOnlySupported = selectedMountService.map(s -> s.hasCapability(MountCapability.READ_ONLY));
|
|
||||||
this.mountpointDirSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_TO_EXISTING_DIR) || s.hasCapability(MountCapability.MOUNT_WITHIN_EXISTING_PARENT));
|
this.mountpointDirSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_TO_EXISTING_DIR) || s.hasCapability(MountCapability.MOUNT_WITHIN_EXISTING_PARENT));
|
||||||
this.mountpointDriveLetterSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_AS_DRIVE_LETTER));
|
this.mountpointDriveLetterSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_AS_DRIVE_LETTER));
|
||||||
this.loopbackPortChangeable = selectedMountService.map(s -> s.hasCapability(MountCapability.LOOPBACK_PORT) && vaultSettings.mountService.getValue() != null);
|
this.loopbackPortChangeable = selectedMountService.map(s -> s.hasCapability(MountCapability.LOOPBACK_PORT) && vaultSettings.mountService.getValue() != null);
|
||||||
|
this.readOnlyOptionAllowed = BooleanBinding.booleanExpression(selectedMountService.map(s -> s.hasCapability(MountCapability.READ_ONLY))).or(vaultSettings.usesReadOnlyMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MountService reselectMountService() {
|
private MountService reselectMountService() {
|
||||||
@@ -345,12 +347,12 @@ public class MountOptionsController implements FxController {
|
|||||||
return mountpointDriveLetterSupported.getValue();
|
return mountpointDriveLetterSupported.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableValue<Boolean> readOnlySupportedProperty() {
|
public ObservableValue<Boolean> readOnlyOptionAllowedProperty() {
|
||||||
return readOnlySupported;
|
return readOnlyOptionAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isReadOnlySupported() {
|
public boolean isReadOnlyOptionAllowed() {
|
||||||
return readOnlySupported.getValue();
|
return readOnlyOptionAllowed.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableValue<String> directoryPathProperty() {
|
public ObservableValue<String> directoryPathProperty() {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.cryptomator.ui.vaultoptions;
|
|||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.common.vaults.VaultState;
|
import org.cryptomator.common.vaults.VaultState;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
||||||
import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy;
|
import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy;
|
||||||
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
|
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -42,11 +43,11 @@ public class VaultOptionsController implements FxController {
|
|||||||
window.setOnShowing(this::windowWillAppear);
|
window.setOnShowing(this::windowWillAppear);
|
||||||
selectedTabProperty.addListener(observable -> this.selectChosenTab());
|
selectedTabProperty.addListener(observable -> this.selectChosenTab());
|
||||||
tabPane.getSelectionModel().selectedItemProperty().addListener(observable -> this.selectedTabChanged());
|
tabPane.getSelectionModel().selectedItemProperty().addListener(observable -> this.selectedTabChanged());
|
||||||
var vaultScheme = vault.getVaultConfigCache().getUnchecked().getKeyId().getScheme();
|
var vaultKeyLoader = vault.getVaultSettings().lastKnownKeyLoader.get();
|
||||||
if(!vaultScheme.equals(MasterkeyFileLoadingStrategy.SCHEME)){
|
if(!KeyLoadingStrategy.isMasterkeyFileVault(vaultKeyLoader)){
|
||||||
tabPane.getTabs().remove(keyTab);
|
tabPane.getTabs().remove(keyTab);
|
||||||
}
|
}
|
||||||
if(!(vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTP) || vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTPS))){
|
if(!KeyLoadingStrategy.isHubVault(vaultKeyLoader)){
|
||||||
tabPane.getTabs().remove(hubTab);
|
tabPane.getTabs().remove(hubTab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import java.io.UncheckedIOException;
|
|||||||
@WrongFileAlertScoped
|
@WrongFileAlertScoped
|
||||||
public class WrongFileAlertController implements FxController {
|
public class WrongFileAlertController implements FxController {
|
||||||
|
|
||||||
private static final String DOCUMENTATION_URI = "https://docs.cryptomator.org/en/1.7/desktop/accessing-vaults/";
|
private static final String DOCUMENTATION_URI = "https://docs.cryptomator.org/desktop/accessing-vaults/";
|
||||||
|
|
||||||
private final Application app;
|
private final Application app;
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
|
|||||||
@@ -183,19 +183,37 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.main-window .button-bar {
|
.main-window .button-bar {
|
||||||
|
-fx-min-height:42px;
|
||||||
|
-fx-max-height:42px;
|
||||||
-fx-background-color: MAIN_BG;
|
-fx-background-color: MAIN_BG;
|
||||||
-fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent;
|
-fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent;
|
||||||
-fx-border-width: 1px 0 0 0;
|
-fx-border-width: 1px 0 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .button-left {
|
.main-window .button-bar .button-left {
|
||||||
-fx-border-color: CONTROL_BORDER_NORMAL;
|
-fx-border-color: CONTROL_BORDER_NORMAL;
|
||||||
-fx-border-width: 0 1px 0 0;
|
-fx-border-width: 0 1px 0 0;
|
||||||
|
-fx-background-color: MAIN_BG;
|
||||||
|
-fx-background-radius: 0px;
|
||||||
|
-fx-min-height: 42px;
|
||||||
|
-fx-max-height: 42px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .button-right {
|
.main-window .button-bar .button-right {
|
||||||
-fx-border-color: CONTROL_BORDER_NORMAL;
|
-fx-border-color: CONTROL_BORDER_NORMAL;
|
||||||
-fx-border-width: 0 0 0 1px;
|
-fx-border-width: 0 0 0 1px;
|
||||||
|
-fx-background-color: MAIN_BG;
|
||||||
|
-fx-background-radius: 0px;
|
||||||
|
-fx-min-height: 42px;
|
||||||
|
-fx-max-height: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-window .button-bar .button-left:armed {
|
||||||
|
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-window .button-bar .button-right:armed {
|
||||||
|
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -312,6 +330,42 @@
|
|||||||
-fx-fill: transparent;
|
-fx-fill: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* *
|
||||||
|
* Event List *
|
||||||
|
* *
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
.event-window .button-bar {
|
||||||
|
-fx-min-height:42px;
|
||||||
|
-fx-max-height:42px;
|
||||||
|
-fx-background-color: MAIN_BG;
|
||||||
|
-fx-border-color: transparent transparent CONTROL_BORDER_NORMAL transparent;
|
||||||
|
-fx-border-width: 0 0 1px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-window .button-bar .button-right {
|
||||||
|
-fx-border-color: transparent transparent transparent CONTROL_BORDER_NORMAL;
|
||||||
|
-fx-border-width: 0 0 0 1px;
|
||||||
|
-fx-background-color: MAIN_BG;
|
||||||
|
-fx-background-radius: 0px;
|
||||||
|
-fx-min-height: 42px;
|
||||||
|
-fx-max-height: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-window .button-bar .button-right:armed {
|
||||||
|
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-window .list-view .list-cell:hover {
|
||||||
|
-fx-background-color: CONTROL_BG_SELECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-window .list-view .list-cell:selected {
|
||||||
|
-fx-background-color: PRIMARY, CONTROL_BG_SELECTED;
|
||||||
|
-fx-background-insets: 0, 0 0 0 3px;
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
||||||
* NotificationBar *
|
* NotificationBar *
|
||||||
@@ -341,6 +395,12 @@
|
|||||||
-fx-background-color: PRIMARY;
|
-fx-background-color: PRIMARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notification-debug:hover .notification-label,
|
||||||
|
.notification-update:hover .notification-label,
|
||||||
|
.notification-support:hover .notification-label {
|
||||||
|
-fx-underline:true;
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
||||||
* ScrollBar *
|
* ScrollBar *
|
||||||
@@ -575,6 +635,16 @@
|
|||||||
-fx-graphic-text-gap: 9px;
|
-fx-graphic-text-gap: 9px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* *
|
||||||
|
* Update indicator
|
||||||
|
* *
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
.update-indicator {
|
||||||
|
-fx-fill: RED_5;
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
||||||
* Hyperlinks *
|
* Hyperlinks *
|
||||||
|
|||||||
@@ -182,6 +182,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.main-window .button-bar {
|
.main-window .button-bar {
|
||||||
|
-fx-min-height:42px;
|
||||||
|
-fx-max-height:42px;
|
||||||
-fx-background-color: MAIN_BG;
|
-fx-background-color: MAIN_BG;
|
||||||
-fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent;
|
-fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent;
|
||||||
-fx-border-width: 1px 0 0 0;
|
-fx-border-width: 1px 0 0 0;
|
||||||
@@ -190,11 +192,27 @@
|
|||||||
.main-window .button-bar .button-left {
|
.main-window .button-bar .button-left {
|
||||||
-fx-border-color: CONTROL_BORDER_NORMAL;
|
-fx-border-color: CONTROL_BORDER_NORMAL;
|
||||||
-fx-border-width: 0 1px 0 0;
|
-fx-border-width: 0 1px 0 0;
|
||||||
|
-fx-background-color: MAIN_BG;
|
||||||
|
-fx-background-radius: 0px;
|
||||||
|
-fx-min-height: 42px;
|
||||||
|
-fx-max-height: 42px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .button-bar .button-right {
|
.main-window .button-bar .button-right {
|
||||||
-fx-border-color: CONTROL_BORDER_NORMAL;
|
-fx-border-color: CONTROL_BORDER_NORMAL;
|
||||||
-fx-border-width: 0 0 0 1px;
|
-fx-border-width: 0 0 0 1px;
|
||||||
|
-fx-background-color: MAIN_BG;
|
||||||
|
-fx-background-radius: 0px;
|
||||||
|
-fx-min-height: 42px;
|
||||||
|
-fx-max-height: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-window .button-bar .button-left:armed {
|
||||||
|
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-window .button-bar .button-right:armed {
|
||||||
|
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -283,6 +301,10 @@
|
|||||||
-fx-font-size: 1.0em;
|
-fx-font-size: 1.0em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-cell .header-misc {
|
||||||
|
-fx-font-size: 1.0em;
|
||||||
|
}
|
||||||
|
|
||||||
.list-cell .detail-label {
|
.list-cell .detail-label {
|
||||||
-fx-text-fill: TEXT_FILL_MUTED;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
-fx-font-size: 0.8em;
|
-fx-font-size: 0.8em;
|
||||||
@@ -311,6 +333,42 @@
|
|||||||
-fx-fill: transparent;
|
-fx-fill: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* *
|
||||||
|
* Event List *
|
||||||
|
* *
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
.event-window .button-bar {
|
||||||
|
-fx-min-height:42px;
|
||||||
|
-fx-max-height:42px;
|
||||||
|
-fx-background-color: MAIN_BG;
|
||||||
|
-fx-border-color: transparent transparent CONTROL_BORDER_NORMAL transparent;
|
||||||
|
-fx-border-width: 0 0 1px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-window .button-bar .button-right {
|
||||||
|
-fx-border-color: transparent transparent transparent CONTROL_BORDER_NORMAL;
|
||||||
|
-fx-border-width: 0 0 0 1px;
|
||||||
|
-fx-background-color: MAIN_BG;
|
||||||
|
-fx-background-radius: 0px;
|
||||||
|
-fx-min-height: 42px;
|
||||||
|
-fx-max-height: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-window .button-bar .button-right:armed {
|
||||||
|
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_ARMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-window .list-view .list-cell:hover {
|
||||||
|
-fx-background-color: CONTROL_BG_SELECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-window .list-view .list-cell:selected {
|
||||||
|
-fx-background-color: PRIMARY, CONTROL_BG_SELECTED;
|
||||||
|
-fx-background-insets: 0, 0 0 0 3px;
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
||||||
* NotificationBar *
|
* NotificationBar *
|
||||||
@@ -340,6 +398,12 @@
|
|||||||
-fx-background-color: PRIMARY;
|
-fx-background-color: PRIMARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notification-debug:hover .notification-label,
|
||||||
|
.notification-update:hover .notification-label,
|
||||||
|
.notification-support:hover .notification-label {
|
||||||
|
-fx-underline:true;
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
||||||
* ScrollBar *
|
* ScrollBar *
|
||||||
@@ -574,6 +638,16 @@
|
|||||||
-fx-graphic-text-gap: 9px;
|
-fx-graphic-text-gap: 9px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* *
|
||||||
|
* Update indicator
|
||||||
|
* *
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
.icon-update-indicator {
|
||||||
|
-fx-fill: RED_5;
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
||||||
* Hyperlinks *
|
* Hyperlinks *
|
||||||
@@ -786,11 +860,11 @@
|
|||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
||||||
* Add Vault - MenuItem *
|
* Dropdown button context menu
|
||||||
* *
|
* *
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
.add-vault-menu-item {
|
.dropdown-button-context-menu-item {
|
||||||
-fx-padding: 4px 8px;
|
-fx-padding: 4px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ListView?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.Region?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||||
|
<?import javafx.scene.control.ChoiceBox?>
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Tooltip?>
|
||||||
|
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||||
|
xmlns="http://javafx.com/javafx"
|
||||||
|
fx:controller="org.cryptomator.ui.eventview.EventViewController"
|
||||||
|
minWidth="300"
|
||||||
|
prefWidth="300"
|
||||||
|
styleClass="event-window"
|
||||||
|
>
|
||||||
|
<HBox styleClass="button-bar" alignment="CENTER">
|
||||||
|
<padding>
|
||||||
|
<Insets left="6" />
|
||||||
|
</padding>
|
||||||
|
<ChoiceBox fx:id="vaultFilterChoiceBox"/>
|
||||||
|
<Region HBox.hgrow="ALWAYS"/>
|
||||||
|
<Button styleClass="button-right" onAction="#clearEvents" contentDisplay="GRAPHIC_ONLY">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="TRASH" glyphSize="16"/>
|
||||||
|
</graphic>
|
||||||
|
<tooltip>
|
||||||
|
<Tooltip text="%eventView.clearListButton.tooltip"/>
|
||||||
|
</tooltip>
|
||||||
|
</Button>
|
||||||
|
</HBox>
|
||||||
|
<ListView fx:id="eventListView" fixedCellSize="60" VBox.vgrow="ALWAYS"/>
|
||||||
|
</VBox>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ContextMenu?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||||
|
xmlns="http://javafx.com/javafx"
|
||||||
|
fx:controller="org.cryptomator.ui.eventview.EventListCellController"
|
||||||
|
prefHeight="60"
|
||||||
|
prefWidth="200"
|
||||||
|
spacing="12"
|
||||||
|
alignment="CENTER_LEFT"
|
||||||
|
fx:id="root">
|
||||||
|
<padding>
|
||||||
|
<Insets topRightBottomLeft="12"/>
|
||||||
|
</padding>
|
||||||
|
<!-- Remark Check the containing list view for a fixed cell size before editing height properties -->
|
||||||
|
<VBox alignment="CENTER" minWidth="20">
|
||||||
|
<FontAwesome5IconView glyph="${controller.icon}" HBox.hgrow="NEVER" glyphSize="16"/>
|
||||||
|
</VBox>
|
||||||
|
<VBox spacing="4" HBox.hgrow="ALWAYS">
|
||||||
|
<HBox spacing="4">
|
||||||
|
<Label styleClass="header-label" text="${controller.message}"/>
|
||||||
|
<Label styleClass="header-misc" text="${controller.count}" visible="${controller.vaultUnlocked}"/>
|
||||||
|
</HBox>
|
||||||
|
<Label text="${controller.description}"/>
|
||||||
|
</VBox>
|
||||||
|
<Button fx:id="eventActionsButton" contentDisplay="GRAPHIC_ONLY" onAction="#toggleEventActionsMenu" managed="${controller.actionsButtonVisible}" visible="${controller.actionsButtonVisible}">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="ELLIPSIS_V" glyphSize="16"/>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<VBox alignment="CENTER" maxWidth="64" minWidth="64" visible="${!controller.actionsButtonVisible}" managed="${!controller.actionsButtonVisible}">
|
||||||
|
<Label text="${controller.eventLocalTime}" />
|
||||||
|
<Label text="${controller.eventLocalDate}" />
|
||||||
|
</VBox>
|
||||||
|
|
||||||
|
<fx:define>
|
||||||
|
<ContextMenu fx:id="eventActionsMenu"/>
|
||||||
|
</fx:define>
|
||||||
|
</HBox>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
</ImageView>
|
</ImageView>
|
||||||
<VBox spacing="3" HBox.hgrow="ALWAYS" alignment="CENTER_LEFT">
|
<VBox spacing="3" HBox.hgrow="ALWAYS" alignment="CENTER_LEFT">
|
||||||
<FormattedLabel styleClass="label-extra-large" format="Cryptomator %s" arg1="${controller.fullApplicationVersion}"/>
|
<FormattedLabel styleClass="label-extra-large" format="Cryptomator %s" arg1="${controller.fullApplicationVersion}"/>
|
||||||
<Label text="© 2016 – 2024 Skymatic GmbH"/>
|
<Label text="© 2016 – 2025 Skymatic GmbH"/>
|
||||||
</VBox>
|
</VBox>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import javafx.scene.control.Label?>
|
|
||||||
<?import javafx.scene.layout.HBox?>
|
|
||||||
<?import javafx.scene.control.Button?>
|
|
||||||
<?import javafx.geometry.Insets?>
|
|
||||||
<?import javafx.scene.Group?>
|
|
||||||
<?import javafx.scene.layout.StackPane?>
|
|
||||||
<?import javafx.scene.shape.Circle?>
|
|
||||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.layout.Region?>
|
<?import javafx.scene.control.Button?>
|
||||||
<?import javafx.scene.control.ButtonBar?>
|
<?import javafx.scene.control.ButtonBar?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.Group?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.Region?>
|
||||||
|
<?import javafx.scene.layout.StackPane?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.shape.Circle?>
|
||||||
<HBox xmlns:fx="http://javafx.com/fxml"
|
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="org.cryptomator.ui.dialogs.SimpleDialogController"
|
fx:controller="org.cryptomator.ui.dialogs.SimpleDialogController"
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
||||||
<ButtonBar buttonMinWidth="120" buttonOrder="+CI">
|
<ButtonBar buttonMinWidth="120" buttonOrder="+CI">
|
||||||
<buttons>
|
<buttons>
|
||||||
<Button text="${controller.cancelButtonText}" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#handleCancel"/>
|
<Button text="${controller.cancelButtonText}" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#handleCancel" visible="${controller.cancelButtonVisible}" managed="${controller.cancelButtonVisible}"/>
|
||||||
<Button text="${controller.okButtonText}" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#handleOk"/>
|
<Button text="${controller.okButtonText}" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#handleOk"/>
|
||||||
</buttons>
|
</buttons>
|
||||||
</ButtonBar>
|
</ButtonBar>
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ContextMenu?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.ListView?>
|
<?import javafx.scene.control.ListView?>
|
||||||
|
<?import javafx.scene.control.MenuItem?>
|
||||||
|
<?import javafx.scene.control.Tooltip?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
<?import javafx.scene.layout.Region?>
|
<?import javafx.scene.layout.Region?>
|
||||||
<?import javafx.scene.layout.StackPane?>
|
<?import javafx.scene.layout.StackPane?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<?import javafx.scene.control.ContextMenu?>
|
|
||||||
<?import javafx.scene.control.MenuItem?>
|
|
||||||
<?import javafx.scene.layout.HBox?>
|
|
||||||
<?import javafx.geometry.Insets?>
|
|
||||||
<?import javafx.scene.shape.Arc?>
|
<?import javafx.scene.shape.Arc?>
|
||||||
|
<?import javafx.scene.shape.Circle?>
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.shape.Rectangle?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
<StackPane xmlns:fx="http://javafx.com/fxml"
|
<StackPane xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:id="root"
|
fx:id="root"
|
||||||
@@ -35,33 +40,44 @@
|
|||||||
</VBox>
|
</VBox>
|
||||||
</StackPane>
|
</StackPane>
|
||||||
<HBox styleClass="button-bar">
|
<HBox styleClass="button-bar">
|
||||||
<HBox fx:id="addVaultButton" onMouseClicked="#toggleMenu" styleClass="button-left" alignment="CENTER" minWidth="20">
|
<Button fx:id="addVaultButton" onMouseClicked="#toggleMenu" styleClass="button-left" alignment="CENTER" minWidth="20" contentDisplay="GRAPHIC_ONLY">
|
||||||
<padding>
|
<graphic>
|
||||||
<Insets topRightBottomLeft="12"/>
|
<FontAwesome5IconView glyph="PLUS" glyphSize="16"/>
|
||||||
</padding>
|
</graphic>
|
||||||
<FontAwesome5IconView glyph="PLUS" HBox.hgrow="NEVER" glyphSize="16"/>
|
</Button>
|
||||||
</HBox>
|
|
||||||
<Region HBox.hgrow="ALWAYS"/>
|
<Region HBox.hgrow="ALWAYS"/>
|
||||||
<HBox onMouseClicked="#showPreferences" styleClass="button-right" alignment="CENTER" minWidth="20">
|
<StackPane>
|
||||||
<padding>
|
<Button onMouseClicked="#showEventViewer" styleClass="button-right" minWidth="20" contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false">
|
||||||
<Insets topRightBottomLeft="12"/>
|
<graphic>
|
||||||
</padding>
|
<FontAwesome5IconView glyph="BELL" glyphSize="16"/>
|
||||||
<FontAwesome5IconView glyph="COG" HBox.hgrow="NEVER" glyphSize="16"/>
|
</graphic>
|
||||||
</HBox>
|
<tooltip>
|
||||||
|
<Tooltip text="%main.vaultlist.showEventsButton.tooltip"/>
|
||||||
|
</tooltip>
|
||||||
|
</Button>
|
||||||
|
<AnchorPane mouseTransparent="true" minWidth="12" maxWidth="12" minHeight="12" maxHeight="12" StackPane.alignment="CENTER">
|
||||||
|
<Circle radius="4" styleClass="icon-update-indicator" AnchorPane.topAnchor="-8" AnchorPane.rightAnchor="-6" visible="${controller.unreadEventsPresent}" />
|
||||||
|
</AnchorPane>
|
||||||
|
</StackPane>
|
||||||
|
<Button onMouseClicked="#showPreferences" styleClass="button-right" alignment="CENTER" minWidth="20" contentDisplay="GRAPHIC_ONLY">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="COG" glyphSize="16"/>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
</HBox>
|
</HBox>
|
||||||
</VBox>
|
</VBox>
|
||||||
<Region styleClass="drag-n-drop-border" visible="${controller.draggingVaultOver}"/>
|
<Region styleClass="drag-n-drop-border" visible="${controller.draggingVaultOver}"/>
|
||||||
<fx:define>
|
<fx:define>
|
||||||
<ContextMenu fx:id="addVaultContextMenu">
|
<ContextMenu fx:id="addVaultContextMenu">
|
||||||
<items>
|
<items>
|
||||||
<MenuItem styleClass="add-vault-menu-item" text="%main.vaultlist.addVaultBtn.menuItemNew" onAction="#didClickAddNewVault" >
|
<MenuItem styleClass="dropdown-button-context-menu-item" text="%main.vaultlist.addVaultBtn.menuItemNew" onAction="#didClickAddNewVault">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesome5IconView glyph="PLUS" textAlignment="CENTER" wrappingWidth="14" />
|
<FontAwesome5IconView glyph="PLUS" textAlignment="CENTER" wrappingWidth="14"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem styleClass="add-vault-menu-item" text="%main.vaultlist.addVaultBtn.menuItemExisting" onAction="#didClickAddExistingVault" >
|
<MenuItem styleClass="dropdown-button-context-menu-item" text="%main.vaultlist.addVaultBtn.menuItemExisting" onAction="#didClickAddExistingVault">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesome5IconView glyph="FOLDER_OPEN" textAlignment="CENTER" wrappingWidth="14" />
|
<FontAwesome5IconView glyph="FOLDER_OPEN" textAlignment="CENTER" wrappingWidth="14"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</items>
|
</items>
|
||||||
|
|||||||
@@ -1,33 +1,28 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||||
<?import javafx.geometry.Insets?>
|
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.Tooltip?>
|
<?import javafx.scene.control.Tooltip?>
|
||||||
<?import javafx.scene.layout.HBox?>
|
<?import javafx.scene.layout.HBox?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<HBox xmlns:fx="http://javafx.com/fxml"
|
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
|
fx:id="vaultListCell"
|
||||||
fx:controller="org.cryptomator.ui.mainwindow.VaultListCellController"
|
fx:controller="org.cryptomator.ui.mainwindow.VaultListCellController"
|
||||||
prefHeight="60"
|
prefHeight="60"
|
||||||
prefWidth="200"
|
prefWidth="200"
|
||||||
spacing="12"
|
spacing="12"
|
||||||
alignment="CENTER_LEFT">
|
alignment="CENTER_LEFT">
|
||||||
<!-- Remark Check the containing list view for a fixed cell size before editing height properties -->
|
<!-- Remark Check the containing list view for a fixed cell size before editing height properties -->
|
||||||
<padding>
|
<VBox alignment="CENTER" minWidth="20">
|
||||||
<Insets topRightBottomLeft="12"/>
|
<FontAwesome5IconView fx:id="vaultStateView" glyph="${controller.glyph}" HBox.hgrow="NEVER" glyphSize="16"/>
|
||||||
</padding>
|
</VBox>
|
||||||
<children>
|
<VBox spacing="4" HBox.hgrow="ALWAYS">
|
||||||
<VBox alignment="CENTER" minWidth="20">
|
<Label styleClass="header-label" text="${controller.vault.displayName}"/>
|
||||||
<FontAwesome5IconView fx:id="vaultStateView" glyph="${controller.glyph}" HBox.hgrow="NEVER" glyphSize="16"/>
|
<Label styleClass="detail-label" text="${controller.vault.displayablePath}" textOverrun="CENTER_ELLIPSIS" visible="${!controller.compactMode}" managed="${!controller.compactMode}">
|
||||||
</VBox>
|
<tooltip>
|
||||||
<VBox spacing="4" HBox.hgrow="ALWAYS">
|
<Tooltip text="${controller.vault.displayablePath}"/>
|
||||||
<Label styleClass="header-label" text="${controller.vault.displayName}"/>
|
</tooltip>
|
||||||
<Label styleClass="detail-label" text="${controller.vault.displayablePath}" textOverrun="CENTER_ELLIPSIS" visible="${!controller.compactMode}" managed="${!controller.compactMode}">
|
</Label>
|
||||||
<tooltip>
|
</VBox>
|
||||||
<Tooltip text="${controller.vault.displayablePath}"/>
|
|
||||||
</tooltip>
|
|
||||||
</Label>
|
|
||||||
</VBox>
|
|
||||||
</children>
|
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
<Button text="%generic.button.apply" fx:id="vaultLoopbackPortApplyButton" onAction="#doChangeLoopbackPort"/>
|
<Button text="%generic.button.apply" fx:id="vaultLoopbackPortApplyButton" onAction="#doChangeLoopbackPort"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
<CheckBox fx:id="readOnlyCheckbox" text="%vaultOptions.mount.readonly" visible="${controller.readOnlySupported}" managed="${controller.readOnlySupported}"/>
|
<CheckBox fx:id="readOnlyCheckbox" text="%vaultOptions.mount.readonly" visible="${controller.readOnlyOptionAllowed}" managed="${controller.readOnlyOptionAllowed}"/>
|
||||||
|
|
||||||
<VBox visible="${controller.mountFlagsSupported}" managed="${controller.mountFlagsSupported}">
|
<VBox visible="${controller.mountFlagsSupported}" managed="${controller.mountFlagsSupported}">
|
||||||
<CheckBox fx:id="customMountFlagsCheckbox" text="%vaultOptions.mount.customMountFlags" onAction="#toggleUseCustomMountFlags"/>
|
<CheckBox fx:id="customMountFlagsCheckbox" text="%vaultOptions.mount.customMountFlags" onAction="#toggleUseCustomMountFlags"/>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
additionalStyleSheets=
|
additionalStyleSheets=
|
||||||
|
|
||||||
# Generics
|
# Generics
|
||||||
|
generic.action.dismiss=Dismiss
|
||||||
## Button
|
## Button
|
||||||
generic.button.apply=Apply
|
generic.button.apply=Apply
|
||||||
generic.button.back=Back
|
generic.button.back=Back
|
||||||
@@ -177,7 +178,7 @@ hub.registerFailed.description.generic=An error was thrown in the registration p
|
|||||||
hub.registerFailed.description.deviceAlreadyExists=This device is already registered for a different user. Try to change the user account or use a different device.
|
hub.registerFailed.description.deviceAlreadyExists=This device is already registered for a different user. Try to change the user account or use a different device.
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Access denied
|
hub.unauthorized.message=Access denied
|
||||||
hub.unauthorized.description=Your device has not yet been authorized to access this vault. Ask the vault owner to authorize it.
|
hub.unauthorized.description=You are not authorized to open this vault. Contact the vault's owner to request access.
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Action required
|
hub.requireAccountInit.message=Action required
|
||||||
hub.requireAccountInit.description.0=To proceed, please complete the steps required in your
|
hub.requireAccountInit.description.0=To proceed, please complete the steps required in your
|
||||||
@@ -283,7 +284,7 @@ preferences.title=Preferences
|
|||||||
## General
|
## General
|
||||||
preferences.general=General
|
preferences.general=General
|
||||||
preferences.general.startHidden=Hide window when starting Cryptomator
|
preferences.general.startHidden=Hide window when starting Cryptomator
|
||||||
preferences.general.autoCloseVaults=Lock open vaults automatically when quitting application
|
preferences.general.autoCloseVaults=Lock vaults without asking when quitting application
|
||||||
preferences.general.debugLogging=Enable debug logging
|
preferences.general.debugLogging=Enable debug logging
|
||||||
preferences.general.debugDirectory=Reveal log files
|
preferences.general.debugDirectory=Reveal log files
|
||||||
preferences.general.autoStart=Launch Cryptomator on system start
|
preferences.general.autoStart=Launch Cryptomator on system start
|
||||||
@@ -395,6 +396,7 @@ main.vaultlist.contextMenu.vaultoptions=Show Vault Options
|
|||||||
main.vaultlist.contextMenu.reveal=Reveal Drive
|
main.vaultlist.contextMenu.reveal=Reveal Drive
|
||||||
main.vaultlist.addVaultBtn.menuItemNew=Create New Vault...
|
main.vaultlist.addVaultBtn.menuItemNew=Create New Vault...
|
||||||
main.vaultlist.addVaultBtn.menuItemExisting=Open Existing Vault...
|
main.vaultlist.addVaultBtn.menuItemExisting=Open Existing Vault...
|
||||||
|
main.vaultlist.showEventsButton.tooltip=Open event view
|
||||||
##Notificaition
|
##Notificaition
|
||||||
main.notification.updateAvailable=Update is available.
|
main.notification.updateAvailable=Update is available.
|
||||||
main.notification.support=Support Cryptomator.
|
main.notification.support=Support Cryptomator.
|
||||||
@@ -553,6 +555,12 @@ dokanySupportEnd.message=Support end for Dokany
|
|||||||
dokanySupportEnd.description=The volume type Dokany is no longer supported by Cryptomator. Your settings are adjusted to use the default volume type now. You can view the default type in the preferences.
|
dokanySupportEnd.description=The volume type Dokany is no longer supported by Cryptomator. Your settings are adjusted to use the default volume type now. You can view the default type in the preferences.
|
||||||
dokanySupportEnd.preferencesBtn=Open Preferences
|
dokanySupportEnd.preferencesBtn=Open Preferences
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
retryIfReadonly.title=Restricted Vault Access
|
||||||
|
retryIfReadonly.message=No write access to vault directory
|
||||||
|
retryIfReadonly.description=Cryptomator cannot write to the vault directory. You can change the vault to be read-only and try again. This option can be disabled in the vault options.
|
||||||
|
retryIfReadonly.retry=Change and Retry
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Share Vault
|
shareVault.title=Share Vault
|
||||||
shareVault.message=Would you like to share your vault with others?
|
shareVault.message=Would you like to share your vault with others?
|
||||||
@@ -571,4 +579,30 @@ shareVault.hub.message=How to share a Hub vault
|
|||||||
shareVault.hub.description=In order to share the vault content with another team member, you have to perform two steps:
|
shareVault.hub.description=In order to share the vault content with another team member, you have to perform two steps:
|
||||||
shareVault.hub.instruction.1=1. Share access of the encrypted vault folder via cloud storage.
|
shareVault.hub.instruction.1=1. Share access of the encrypted vault folder via cloud storage.
|
||||||
shareVault.hub.instruction.2=2. Grant access to team member in Cryptomator Hub.
|
shareVault.hub.instruction.2=2. Grant access to team member in Cryptomator Hub.
|
||||||
shareVault.hub.openHub=Open Cryptomator Hub
|
shareVault.hub.openHub=Open Cryptomator Hub
|
||||||
|
|
||||||
|
# Event View
|
||||||
|
eventView.title=Events
|
||||||
|
eventView.filter.allVaults=All
|
||||||
|
eventView.clearListButton.tooltip=Dismiss all
|
||||||
|
## event list entries
|
||||||
|
eventView.entry.vaultLocked.message=***********
|
||||||
|
eventView.entry.vaultLocked.description=Unlock "%s" for details
|
||||||
|
eventView.entry.conflictResolved.message=Resolved conflict
|
||||||
|
eventView.entry.conflictResolved.showDecrypted=Show decrypted file
|
||||||
|
eventView.entry.conflictResolved.copyDecrypted=Copy decrypted path
|
||||||
|
eventView.entry.conflict.message=Conflict resolution failed
|
||||||
|
eventView.entry.conflict.showDecrypted=Show decrypted, original file
|
||||||
|
eventView.entry.conflict.copyDecrypted=Copy decrypted, original path
|
||||||
|
eventView.entry.conflict.showEncrypted=Show conflicting, encrypted file
|
||||||
|
eventView.entry.conflict.copyEncrypted=Copy conflicting, encrypted path
|
||||||
|
eventView.entry.decryptionFailed.message=Decryption failed
|
||||||
|
eventView.entry.decryptionFailed.showEncrypted=Show encrypted file
|
||||||
|
eventView.entry.decryptionFailed.copyEncrypted=Copy encrypted path
|
||||||
|
eventView.entry.brokenDirFile.message=Broken directory link
|
||||||
|
eventView.entry.brokenDirFile.showEncrypted=Show broken, encrypted link
|
||||||
|
eventView.entry.brokenDirFile.copyEncrypted=Copy path of broken link
|
||||||
|
eventView.entry.brokenFileNode.message=Broken filesystem node
|
||||||
|
eventView.entry.brokenFileNode.showEncrypted=Show broken, encrypted node
|
||||||
|
eventView.entry.brokenFileNode.copyEncrypted=Copy path of broken, encrypted node
|
||||||
|
eventView.entry.brokenFileNode.copyDecrypted=Copy decrypted path
|
||||||
|
|||||||
@@ -118,4 +118,6 @@
|
|||||||
|
|
||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ generic.button.copied=تم النسخ!
|
|||||||
generic.button.done=تم
|
generic.button.done=تم
|
||||||
generic.button.next=التالي
|
generic.button.next=التالي
|
||||||
generic.button.print=طباعة
|
generic.button.print=طباعة
|
||||||
|
generic.button.remove=حذف
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
error.message=حدث خطأ ما
|
error.message=حدث خطأ ما
|
||||||
@@ -105,7 +106,6 @@ addvaultwizard.success.unlockNow=افتح الان
|
|||||||
removeVault.title=احذف الحافظة
|
removeVault.title=احذف الحافظة
|
||||||
removeVault.message=حذف المخزن؟
|
removeVault.message=حذف المخزن؟
|
||||||
removeVault.description=سيؤدي هذا إلى نسيان Cryptomator لهذا المخزن فقط. يمكنك إضافته مرة أخرى لاحقاً. لن يتم حذف أي من الملفات المشفرة من القرص الصلب الخاص بك.
|
removeVault.description=سيؤدي هذا إلى نسيان Cryptomator لهذا المخزن فقط. يمكنك إضافته مرة أخرى لاحقاً. لن يتم حذف أي من الملفات المشفرة من القرص الصلب الخاص بك.
|
||||||
removeVault.confirmBtn=احذف الحافظة
|
|
||||||
|
|
||||||
# Change Password
|
# Change Password
|
||||||
changepassword.title=تغيير كلمة المرور
|
changepassword.title=تغيير كلمة المرور
|
||||||
@@ -176,7 +176,6 @@ hub.registerFailed.description.generic=حدث خطأ في عملية تسجيل
|
|||||||
hub.registerFailed.description.deviceAlreadyExists=هذا الجهاز مسجل لمستخدم مختلف بالفعل. حاول تغيير حساب المستخدم أو استخدام جهاز مختلف.
|
hub.registerFailed.description.deviceAlreadyExists=هذا الجهاز مسجل لمستخدم مختلف بالفعل. حاول تغيير حساب المستخدم أو استخدام جهاز مختلف.
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=تم رفض الوصول
|
hub.unauthorized.message=تم رفض الوصول
|
||||||
hub.unauthorized.description=لم يتم بعد منح الإذن لجهازك بالوصول إلى هذا المخزن. اطلب من مالك المخزن أن يأذن بذلك.
|
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=مطلوب اتخاذ إجراء
|
hub.requireAccountInit.message=مطلوب اتخاذ إجراء
|
||||||
hub.requireAccountInit.description.0=للمتابعة، يرجى إكمال الخطوات المطلوبة في
|
hub.requireAccountInit.description.0=للمتابعة، يرجى إكمال الخطوات المطلوبة في
|
||||||
@@ -287,6 +286,7 @@ preferences.general.debugLogging=تمكين سجلات التصحيح
|
|||||||
preferences.general.debugDirectory=عرض ملفات السجل
|
preferences.general.debugDirectory=عرض ملفات السجل
|
||||||
preferences.general.autoStart=تشغيل Cryptomator عند بدء تشغيل النظام
|
preferences.general.autoStart=تشغيل Cryptomator عند بدء تشغيل النظام
|
||||||
preferences.general.keychainBackend=تخزين كلمات المرور مع
|
preferences.general.keychainBackend=تخزين كلمات المرور مع
|
||||||
|
preferences.general.quickAccessService=إضافة الخزانات المفتوحة إلى منطقة الوصول السريع
|
||||||
## Interface
|
## Interface
|
||||||
preferences.interface=الواجهة
|
preferences.interface=الواجهة
|
||||||
preferences.interface.theme=الشكل والمظهر
|
preferences.interface.theme=الشكل والمظهر
|
||||||
@@ -300,6 +300,7 @@ preferences.interface.interfaceOrientation=اتجاه الواجهة
|
|||||||
preferences.interface.interfaceOrientation.ltr=من اليسار إلى اليمين
|
preferences.interface.interfaceOrientation.ltr=من اليسار إلى اليمين
|
||||||
preferences.interface.interfaceOrientation.rtl=من اليمين إلى اليسار
|
preferences.interface.interfaceOrientation.rtl=من اليمين إلى اليسار
|
||||||
preferences.interface.showTrayIcon=إظهار أيقونة اللوحة (يتطلب إعادة تشغيل)
|
preferences.interface.showTrayIcon=إظهار أيقونة اللوحة (يتطلب إعادة تشغيل)
|
||||||
|
preferences.interface.compactMode=تمكين قائمة الخزنة المدمجة
|
||||||
## Volume
|
## Volume
|
||||||
preferences.volume=القرص الإفتراضي
|
preferences.volume=القرص الإفتراضي
|
||||||
preferences.volume.type=نوع القرص الافتراضي
|
preferences.volume.type=نوع القرص الافتراضي
|
||||||
@@ -333,12 +334,14 @@ preferences.contribute.registeredFor=شهادة الداعم مسجلة لـ %s
|
|||||||
preferences.contribute.noCertificate=ادعم Cryptomator واحصل على شهادة الداعم. إنها مثل مفتاح الترخيص لكن للأشخاص الرائعين الذين يستخدمون البرامج المجانية ؛-)
|
preferences.contribute.noCertificate=ادعم Cryptomator واحصل على شهادة الداعم. إنها مثل مفتاح الترخيص لكن للأشخاص الرائعين الذين يستخدمون البرامج المجانية ؛-)
|
||||||
preferences.contribute.getCertificate=ليس لديك واحدة بعد؟ تعلم كيف يمكنك الحصول عليها.
|
preferences.contribute.getCertificate=ليس لديك واحدة بعد؟ تعلم كيف يمكنك الحصول عليها.
|
||||||
preferences.contribute.promptText=قم بلصق رمز شهادة الداعم هنا
|
preferences.contribute.promptText=قم بلصق رمز شهادة الداعم هنا
|
||||||
|
preferences.contribute.thankYou=نشكرك على دعمك لتطوير Cryptomator مفتوح المصدر!
|
||||||
preferences.contribute.donate=تبرع
|
preferences.contribute.donate=تبرع
|
||||||
|
preferences.contribute.sponsor=الراعي
|
||||||
|
|
||||||
### Remove License Key Dialog
|
### Remove License Key Dialog
|
||||||
removeCert.title=إزالة الشهادة
|
removeCert.title=إزالة الشهادة
|
||||||
removeCert.message=إزالة شهادة الداعم؟
|
removeCert.message=إزالة شهادة الداعم؟
|
||||||
removeCert.confirmBtn=حذف
|
removeCert.description=الميزات الأساسية لـ Cryptomator غير متأثرة بهذا. لا يتم تقييد الوصول إلى خزاناتك ولا يتم تخفيض مستوى الأمان.
|
||||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||||
|
|
||||||
## About
|
## About
|
||||||
@@ -388,7 +391,11 @@ main.vaultlist.contextMenu.unlock=فتح…
|
|||||||
main.vaultlist.contextMenu.unlockNow=افتح الان
|
main.vaultlist.contextMenu.unlockNow=افتح الان
|
||||||
main.vaultlist.contextMenu.vaultoptions=إظهار خيارات المخزن
|
main.vaultlist.contextMenu.vaultoptions=إظهار خيارات المخزن
|
||||||
main.vaultlist.contextMenu.reveal=اظهار القرص
|
main.vaultlist.contextMenu.reveal=اظهار القرص
|
||||||
|
main.vaultlist.addVaultBtn.menuItemNew=إنشاء مخزن جديد...
|
||||||
|
main.vaultlist.addVaultBtn.menuItemExisting=افتح مخزن موجود...
|
||||||
##Notificaition
|
##Notificaition
|
||||||
|
main.notification.updateAvailable=هناك تحديث متاح.
|
||||||
|
main.notification.support=دعم Cryptomator.
|
||||||
## Vault Detail
|
## Vault Detail
|
||||||
### Welcome
|
### Welcome
|
||||||
main.vaultDetail.welcomeOnboarding=شكرا لاختيار Cryptomator لحماية ملفاتك. إذا كنت بحاجة إلى أية مساعدة، تحقق من دليل وتعليمات الإستخدام:
|
main.vaultDetail.welcomeOnboarding=شكرا لاختيار Cryptomator لحماية ملفاتك. إذا كنت بحاجة إلى أية مساعدة، تحقق من دليل وتعليمات الإستخدام:
|
||||||
@@ -544,6 +551,8 @@ dokanySupportEnd.message=انتهاء الدعم لـDokany
|
|||||||
dokanySupportEnd.description=نوع وحدة التخزين Dokany لم يعد مدعوماً من قبل Cryptomator. تم تعديل إعداداتك لاستخدام نوع وحدة التخزين الافتراضي الآن. يمكنك عرض النوع الافتراضي في التفضيلات.
|
dokanySupportEnd.description=نوع وحدة التخزين Dokany لم يعد مدعوماً من قبل Cryptomator. تم تعديل إعداداتك لاستخدام نوع وحدة التخزين الافتراضي الآن. يمكنك عرض النوع الافتراضي في التفضيلات.
|
||||||
dokanySupportEnd.preferencesBtn=فتح التفضيلات
|
dokanySupportEnd.preferencesBtn=فتح التفضيلات
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=مشاركة الخزانة
|
shareVault.title=مشاركة الخزانة
|
||||||
shareVault.message=هل ترغب في مشاركة خزانتك مع الآخرين؟
|
shareVault.message=هل ترغب في مشاركة خزانتك مع الآخرين؟
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ generic.button.copied=Күсермә алынды!
|
|||||||
generic.button.done=Тамам
|
generic.button.done=Тамам
|
||||||
generic.button.next=Киләһе
|
generic.button.next=Киләһе
|
||||||
generic.button.print=Баҫтыр
|
generic.button.print=Баҫтыр
|
||||||
|
generic.button.remove=Алып ташлау
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
error.message=Хата килеп сыҡты
|
error.message=Хата килеп сыҡты
|
||||||
@@ -104,7 +105,6 @@ addvaultwizard.success.unlockNow=Хәҙер бикте ас
|
|||||||
removeVault.title="%s" һаҡлағысын алып ташла
|
removeVault.title="%s" һаҡлағысын алып ташла
|
||||||
removeVault.message=Һаҡлағысты алып ташларғамы?
|
removeVault.message=Һаҡлағысты алып ташларғамы?
|
||||||
removeVault.description=Cryptomator был һаҡлағысты ғына онотасаҡ. Һеҙ уны яңынан өҫтәй алаһығыҙ. Ҡаты дисктан шифрланған файлдар юйылмаясаҡ.
|
removeVault.description=Cryptomator был һаҡлағысты ғына онотасаҡ. Һеҙ уны яңынан өҫтәй алаһығыҙ. Ҡаты дисктан шифрланған файлдар юйылмаясаҡ.
|
||||||
removeVault.confirmBtn=Һаҡлағысты алып ташла
|
|
||||||
|
|
||||||
# Change Password
|
# Change Password
|
||||||
changepassword.title=Серһүҙҙе үҙгәртеү
|
changepassword.title=Серһүҙҙе үҙгәртеү
|
||||||
@@ -164,7 +164,6 @@ hub.registerSuccess.unlockBtn=Биген ас
|
|||||||
### Registration Failed
|
### Registration Failed
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Инеү кире ҡағылды
|
hub.unauthorized.message=Инеү кире ҡағылды
|
||||||
hub.unauthorized.description=Һеҙҙең йыһаз әлегә был һаҡлағысҡа инеү хоҡуғына эйә түгел. Һаҡлағыс хужаһынан рөхсәт һорағыҙ.
|
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Эш-хәрәкәт кәрәкле
|
hub.requireAccountInit.message=Эш-хәрәкәт кәрәкле
|
||||||
hub.requireAccountInit.description.0=Дауам итер өсөн, кәрәкле аҙымдар тамамланырға тейеш урын:
|
hub.requireAccountInit.description.0=Дауам итер өсөн, кәрәкле аҙымдар тамамланырға тейеш урын:
|
||||||
@@ -316,7 +315,6 @@ preferences.contribute.getCertificate=Юҡ мы әллә? Уны нисек ал
|
|||||||
preferences.contribute.promptText=Ярҙамсы сертификатын бында йәбештерегеҙ
|
preferences.contribute.promptText=Ярҙамсы сертификатын бында йәбештерегеҙ
|
||||||
|
|
||||||
### Remove License Key Dialog
|
### Remove License Key Dialog
|
||||||
removeCert.confirmBtn=Алып ташлау
|
|
||||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||||
|
|
||||||
## About
|
## About
|
||||||
@@ -517,4 +515,6 @@ updateReminder.yesAutomatically=Эйе, автоматик рәүештә
|
|||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
dokanySupportEnd.preferencesBtn=Көйләүҙәрҙе ас
|
dokanySupportEnd.preferencesBtn=Көйләүҙәрҙе ас
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ generic.button.copied=Скапіявана!
|
|||||||
generic.button.done=Файна
|
generic.button.done=Файна
|
||||||
generic.button.next=Далей
|
generic.button.next=Далей
|
||||||
generic.button.print=Друкаваць
|
generic.button.print=Друкаваць
|
||||||
|
generic.button.remove=Выдаліць
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
error.message=Адбылася памылка
|
error.message=Адбылася памылка
|
||||||
@@ -104,7 +105,6 @@ addvaultwizard.success.unlockNow=Разамкнуць зараз
|
|||||||
removeVault.title=Выдаліць "%s"
|
removeVault.title=Выдаліць "%s"
|
||||||
removeVault.message=Ці выдаліць скарбніцу?
|
removeVault.message=Ці выдаліць скарбніцу?
|
||||||
removeVault.description=Гэта прывядзе толькі да таго, што Cryptomator забудзецца на гэтую скрынку. Ты зможаш дадаць яе пазней ізноў. Аніякія зашыфраваныя файлы з тайго жорсткага дыску не выдаляцьмуцца.
|
removeVault.description=Гэта прывядзе толькі да таго, што Cryptomator забудзецца на гэтую скрынку. Ты зможаш дадаць яе пазней ізноў. Аніякія зашыфраваныя файлы з тайго жорсткага дыску не выдаляцьмуцца.
|
||||||
removeVault.confirmBtn=Выдаліць скарбніцу
|
|
||||||
|
|
||||||
# Change Password
|
# Change Password
|
||||||
changepassword.title=Змяніць пароль
|
changepassword.title=Змяніць пароль
|
||||||
@@ -157,7 +157,6 @@ hub.registerSuccess.unlockBtn=Адамкнуць
|
|||||||
### Registration Failed
|
### Registration Failed
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Адмова ў доступе
|
hub.unauthorized.message=Адмова ў доступе
|
||||||
hub.unauthorized.description=Тваёй прыладзе ў дадзены момант не дазволена мець доступ да гэтай скрабніцы. Запытайся ўладальніка скрабніцы за дазволам.
|
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
### License Exceeded
|
### License Exceeded
|
||||||
hub.invalidLicense.message=Несапраўдная ліцэнзія Hub
|
hub.invalidLicense.message=Несапраўдная ліцэнзія Hub
|
||||||
@@ -303,7 +302,6 @@ preferences.contribute.getCertificate=Яшчэ ня маеш такога? Да
|
|||||||
preferences.contribute.promptText=Устаў код сэртыфікату ахвяравальніка сюды
|
preferences.contribute.promptText=Устаў код сэртыфікату ахвяравальніка сюды
|
||||||
|
|
||||||
### Remove License Key Dialog
|
### Remove License Key Dialog
|
||||||
removeCert.confirmBtn=Выдаліць
|
|
||||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||||
|
|
||||||
## About
|
## About
|
||||||
@@ -497,4 +495,6 @@ updateReminder.yesAutomatically=Так, аўтаматычна
|
|||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
dokanySupportEnd.preferencesBtn=Адчыніць налады
|
dokanySupportEnd.preferencesBtn=Адчыніць налады
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ generic.button.copied=Копирано!
|
|||||||
generic.button.done=Готово
|
generic.button.done=Готово
|
||||||
generic.button.next=Напред
|
generic.button.next=Напред
|
||||||
generic.button.print=Отпечатване
|
generic.button.print=Отпечатване
|
||||||
|
generic.button.remove=Премахване
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
error.message=Възникна грешка
|
error.message=Възникна грешка
|
||||||
@@ -104,7 +105,6 @@ addvaultwizard.success.unlockNow=Отключване сега
|
|||||||
removeVault.title=Премахване на „%s“
|
removeVault.title=Премахване на „%s“
|
||||||
removeVault.message=Премахване на хранилище?
|
removeVault.message=Премахване на хранилище?
|
||||||
removeVault.description=По този начин Криптоматор ще забрави за това хранилище. Можете да го добавите отново. Шифрованите файлове няма да бъдат премахнати от твърдия диск.
|
removeVault.description=По този начин Криптоматор ще забрави за това хранилище. Можете да го добавите отново. Шифрованите файлове няма да бъдат премахнати от твърдия диск.
|
||||||
removeVault.confirmBtn=Премахване
|
|
||||||
|
|
||||||
# Change Password
|
# Change Password
|
||||||
changepassword.title=Промяна на парола
|
changepassword.title=Промяна на парола
|
||||||
@@ -164,7 +164,6 @@ hub.registerSuccess.unlockBtn=Отключване
|
|||||||
### Registration Failed
|
### Registration Failed
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Отказан достъп
|
hub.unauthorized.message=Отказан достъп
|
||||||
hub.unauthorized.description=Устройството не е упълномощено за достъп до това хранилище. Поискайте достъп от собственика.
|
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Необходимо е действие
|
hub.requireAccountInit.message=Необходимо е действие
|
||||||
hub.requireAccountInit.description.0=За да продължите завършете необходимите стъпки в
|
hub.requireAccountInit.description.0=За да продължите завършете необходимите стъпки в
|
||||||
@@ -316,7 +315,6 @@ preferences.contribute.getCertificate=Все още нямате? Научете
|
|||||||
preferences.contribute.promptText=Поставете тук кода на сертификата за дарение
|
preferences.contribute.promptText=Поставете тук кода на сертификата за дарение
|
||||||
|
|
||||||
### Remove License Key Dialog
|
### Remove License Key Dialog
|
||||||
removeCert.confirmBtn=Премахване
|
|
||||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||||
|
|
||||||
## About
|
## About
|
||||||
@@ -517,4 +515,6 @@ updateReminder.yesAutomatically=Да, автоматично
|
|||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
dokanySupportEnd.preferencesBtn=Към настройките
|
dokanySupportEnd.preferencesBtn=Към настройките
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ generic.button.copied=কপি হয়েছে!
|
|||||||
generic.button.done=সম্পন্ন হয়েছে
|
generic.button.done=সম্পন্ন হয়েছে
|
||||||
generic.button.next=পরবর্তী
|
generic.button.next=পরবর্তী
|
||||||
generic.button.print=প্রিন্ট
|
generic.button.print=প্রিন্ট
|
||||||
|
generic.button.remove=বাতিল
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
error.message=ত্রুটি %s
|
error.message=ত্রুটি %s
|
||||||
@@ -127,7 +128,6 @@ lock.forced.retryBtn=পুনরায় চেষ্টা করুন
|
|||||||
## Contribution
|
## Contribution
|
||||||
|
|
||||||
### Remove License Key Dialog
|
### Remove License Key Dialog
|
||||||
removeCert.confirmBtn=বাতিল
|
|
||||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||||
|
|
||||||
## About
|
## About
|
||||||
@@ -182,4 +182,6 @@ vaultOptions.mount.mountPoint.directoryPickerButton=নির্বাচন ক
|
|||||||
|
|
||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ generic.button.copied=Kopirano!
|
|||||||
generic.button.done=Gotovo
|
generic.button.done=Gotovo
|
||||||
generic.button.next=Sljedeće
|
generic.button.next=Sljedeće
|
||||||
generic.button.print=Ispis
|
generic.button.print=Ispis
|
||||||
|
generic.button.remove=Ukloni
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
error.message=Došlo je do greške
|
error.message=Došlo je do greške
|
||||||
@@ -78,7 +79,6 @@ addvaultwizard.success.unlockNow=Otključaj sada
|
|||||||
# Remove Vault
|
# Remove Vault
|
||||||
removeVault.title=Ukloni Sef
|
removeVault.title=Ukloni Sef
|
||||||
removeVault.description=Ovo će samo natjerati Cryptomator da zaboravi na ovaj sef. Možete ga dodati ponovo kasnije. Nijedna enkriptovana datoteka neće se izbrisati s vašeg diska.
|
removeVault.description=Ovo će samo natjerati Cryptomator da zaboravi na ovaj sef. Možete ga dodati ponovo kasnije. Nijedna enkriptovana datoteka neće se izbrisati s vašeg diska.
|
||||||
removeVault.confirmBtn=Ukloni Sef
|
|
||||||
|
|
||||||
# Change Password
|
# Change Password
|
||||||
changepassword.title=Promjeni lozinku
|
changepassword.title=Promjeni lozinku
|
||||||
@@ -175,7 +175,6 @@ preferences.updates.updateAvailable=Dostupno ažuriranje na verziju %s.
|
|||||||
## Contribution
|
## Contribution
|
||||||
|
|
||||||
### Remove License Key Dialog
|
### Remove License Key Dialog
|
||||||
removeCert.confirmBtn=Ukloni
|
|
||||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||||
|
|
||||||
## About
|
## About
|
||||||
@@ -319,4 +318,6 @@ quit.lockAndQuitBtn=Zaključaj i zatvori
|
|||||||
|
|
||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ generic.button.copied=Copiat!
|
|||||||
generic.button.done=Fet
|
generic.button.done=Fet
|
||||||
generic.button.next=Següent
|
generic.button.next=Següent
|
||||||
generic.button.print=Imprimeix
|
generic.button.print=Imprimeix
|
||||||
|
generic.button.remove=Elimina
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
error.message=S'ha produït un error
|
error.message=S'ha produït un error
|
||||||
@@ -105,7 +106,6 @@ addvaultwizard.success.unlockNow=Desbloqueja ara
|
|||||||
removeVault.title=Elimina la caixa forta
|
removeVault.title=Elimina la caixa forta
|
||||||
removeVault.message=Voleu eliminar la caixa forta?
|
removeVault.message=Voleu eliminar la caixa forta?
|
||||||
removeVault.description=Cryptomator simplement deixarà de mostrar la caixa forta. Podeu tornar a afegir-la més endavant. Cap fitxer xifrat serà eliminat del disc dur.
|
removeVault.description=Cryptomator simplement deixarà de mostrar la caixa forta. Podeu tornar a afegir-la més endavant. Cap fitxer xifrat serà eliminat del disc dur.
|
||||||
removeVault.confirmBtn=Elimina la caixa forta
|
|
||||||
|
|
||||||
# Change Password
|
# Change Password
|
||||||
changepassword.title=Canvia la contrasenya
|
changepassword.title=Canvia la contrasenya
|
||||||
@@ -176,7 +176,6 @@ hub.registerFailed.description.generic=S'ha produït un error en el procés de r
|
|||||||
hub.registerFailed.description.deviceAlreadyExists=El dispositiu ja ha estat registrat per un altre usuari. Mireu de canviar el compte d'usuari o feu servir un dispositiu diferent.
|
hub.registerFailed.description.deviceAlreadyExists=El dispositiu ja ha estat registrat per un altre usuari. Mireu de canviar el compte d'usuari o feu servir un dispositiu diferent.
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Accés denegat
|
hub.unauthorized.message=Accés denegat
|
||||||
hub.unauthorized.description=El vostre dispositiu no ha estat encara autoritzat a accedir a aquesta caixa forta. Demaneu autorització al propietari.
|
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Acció necessària
|
hub.requireAccountInit.message=Acció necessària
|
||||||
hub.requireAccountInit.description.0=Per a continuar, si us plau, seguiu els passos necessaris en el vostre
|
hub.requireAccountInit.description.0=Per a continuar, si us plau, seguiu els passos necessaris en el vostre
|
||||||
@@ -287,6 +286,7 @@ preferences.general.debugLogging=Habilita el registre de depuració
|
|||||||
preferences.general.debugDirectory=Mostra els fitxers de registres
|
preferences.general.debugDirectory=Mostra els fitxers de registres
|
||||||
preferences.general.autoStart=Executa Cryptomator en engegar el sistema
|
preferences.general.autoStart=Executa Cryptomator en engegar el sistema
|
||||||
preferences.general.keychainBackend=Desar contrasenyes amb
|
preferences.general.keychainBackend=Desar contrasenyes amb
|
||||||
|
preferences.general.quickAccessService=Afegeix caixes fortes desblocades a l'àrea d'accés directe
|
||||||
## Interface
|
## Interface
|
||||||
preferences.interface=Interfície
|
preferences.interface=Interfície
|
||||||
preferences.interface.theme=Aparença
|
preferences.interface.theme=Aparença
|
||||||
@@ -300,6 +300,7 @@ preferences.interface.interfaceOrientation=Orientació de la interfície
|
|||||||
preferences.interface.interfaceOrientation.ltr=Esquerra a dreta
|
preferences.interface.interfaceOrientation.ltr=Esquerra a dreta
|
||||||
preferences.interface.interfaceOrientation.rtl=Dreta a esquerra
|
preferences.interface.interfaceOrientation.rtl=Dreta a esquerra
|
||||||
preferences.interface.showTrayIcon=Mostra la icona en la barra (cal reiniciar)
|
preferences.interface.showTrayIcon=Mostra la icona en la barra (cal reiniciar)
|
||||||
|
preferences.interface.compactMode=Activa la llista compacta de caixes fortes
|
||||||
## Volume
|
## Volume
|
||||||
preferences.volume=Unitat virtual
|
preferences.volume=Unitat virtual
|
||||||
preferences.volume.type=Tipus de volum per defecte
|
preferences.volume.type=Tipus de volum per defecte
|
||||||
@@ -319,6 +320,13 @@ preferences.updates.currentVersion=Versió actual: %s
|
|||||||
preferences.updates.autoUpdateCheck=Comprova automàticament si hi ha actualitzacions
|
preferences.updates.autoUpdateCheck=Comprova automàticament si hi ha actualitzacions
|
||||||
preferences.updates.checkNowBtn=Comprova-ho ara
|
preferences.updates.checkNowBtn=Comprova-ho ara
|
||||||
preferences.updates.updateAvailable=L'actualització a la versió %s es troba disponible.
|
preferences.updates.updateAvailable=L'actualització a la versió %s es troba disponible.
|
||||||
|
preferences.updates.lastUpdateCheck=Darrera comprovació: %s
|
||||||
|
preferences.updates.lastUpdateCheck.never=mai
|
||||||
|
preferences.updates.lastUpdateCheck.recently=recentment
|
||||||
|
preferences.updates.lastUpdateCheck.daysAgo=Fa %s dies
|
||||||
|
preferences.updates.lastUpdateCheck.hoursAgo=fa %s hores
|
||||||
|
preferences.updates.checkFailed=Ha fallat la cerca d'actualitzacions. Si us plau, comproveu la connexió a Internet o torneu a provar-ho més tard.
|
||||||
|
preferences.updates.upToDate=Cryptomator està actualitzat.
|
||||||
|
|
||||||
## Contribution
|
## Contribution
|
||||||
preferences.contribute=Doneu-nos suport
|
preferences.contribute=Doneu-nos suport
|
||||||
@@ -326,9 +334,13 @@ preferences.contribute.registeredFor=Certificat de col·laborador registrat per
|
|||||||
preferences.contribute.noCertificate=Doneu suport a Cryptomator i rebeu un certificat de col·laborador. És com una clau de llicència però per a gent meravellosa que fa servir programari lliure. ;-)
|
preferences.contribute.noCertificate=Doneu suport a Cryptomator i rebeu un certificat de col·laborador. És com una clau de llicència però per a gent meravellosa que fa servir programari lliure. ;-)
|
||||||
preferences.contribute.getCertificate=Encara no en teniu cap? Sapigueu com aconseguir-ne un.
|
preferences.contribute.getCertificate=Encara no en teniu cap? Sapigueu com aconseguir-ne un.
|
||||||
preferences.contribute.promptText=Enganxeu aquí el certificat de col·laborador
|
preferences.contribute.promptText=Enganxeu aquí el certificat de col·laborador
|
||||||
|
preferences.contribute.thankYou=Gràcies pel vostre suport al desenvolupament open-source de Cryptomator!
|
||||||
|
preferences.contribute.donate=Feu un donatiu
|
||||||
|
preferences.contribute.sponsor=Patrocinador
|
||||||
|
|
||||||
### Remove License Key Dialog
|
### Remove License Key Dialog
|
||||||
removeCert.confirmBtn=Elimina
|
removeCert.title=Suprimeix certificat
|
||||||
|
removeCert.description=Les característiques principals de Cryptomator no es veuen afectades per això. Ni l'accés a les vostres caixes fortes s'ha restringit ni el nivell de seguretat ha estat rebaixat.
|
||||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||||
|
|
||||||
## About
|
## About
|
||||||
@@ -378,7 +390,11 @@ main.vaultlist.contextMenu.unlock=Desbloca…
|
|||||||
main.vaultlist.contextMenu.unlockNow=Desbloqueja ara
|
main.vaultlist.contextMenu.unlockNow=Desbloqueja ara
|
||||||
main.vaultlist.contextMenu.vaultoptions=Opcions de la caixa forta
|
main.vaultlist.contextMenu.vaultoptions=Opcions de la caixa forta
|
||||||
main.vaultlist.contextMenu.reveal=Mostra la unitat
|
main.vaultlist.contextMenu.reveal=Mostra la unitat
|
||||||
|
main.vaultlist.addVaultBtn.menuItemNew=Crea una nova caixa forta…
|
||||||
|
main.vaultlist.addVaultBtn.menuItemExisting=Obri una caixa forta existent...
|
||||||
##Notificaition
|
##Notificaition
|
||||||
|
main.notification.updateAvailable=Hi ha una actualització disponible.
|
||||||
|
main.notification.support=Doneu suport a Cryptomator.
|
||||||
## Vault Detail
|
## Vault Detail
|
||||||
### Welcome
|
### Welcome
|
||||||
main.vaultDetail.welcomeOnboarding=Gràcies per escollir Cryptomator per protegir els vostres fitxers. Si vos cal ajuda, llegiu les nostres guies per donar els Primers passos:
|
main.vaultDetail.welcomeOnboarding=Gràcies per escollir Cryptomator per protegir els vostres fitxers. Si vos cal ajuda, llegiu les nostres guies per donar els Primers passos:
|
||||||
@@ -531,6 +547,8 @@ updateReminder.yesAutomatically=Sí, automàticament
|
|||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
dokanySupportEnd.preferencesBtn=Obrir les Preferències
|
dokanySupportEnd.preferencesBtn=Obrir les Preferències
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Comparteix la caixa forta
|
shareVault.title=Comparteix la caixa forta
|
||||||
shareVault.message=Voleu compartir la vostra caixa forta amb altres persones?
|
shareVault.message=Voleu compartir la vostra caixa forta amb altres persones?
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ generic.button.copied=Zkopírováno!
|
|||||||
generic.button.done=Hotovo
|
generic.button.done=Hotovo
|
||||||
generic.button.next=Další
|
generic.button.next=Další
|
||||||
generic.button.print=Tisk
|
generic.button.print=Tisk
|
||||||
|
generic.button.remove=Odstranit
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
error.message=Chyba %s
|
error.message=Chyba %s
|
||||||
@@ -105,7 +106,6 @@ addvaultwizard.success.unlockNow=Odemknout nyní
|
|||||||
removeVault.title=Odstranit trezor
|
removeVault.title=Odstranit trezor
|
||||||
removeVault.message=Odstranit trezor?
|
removeVault.message=Odstranit trezor?
|
||||||
removeVault.description=Toto jen umožní Cryptomatoru zapomenout tento trezor. Později jej můžete opět přidat. Žádné šifrované soubory nebudou z vašeho disku smazány.
|
removeVault.description=Toto jen umožní Cryptomatoru zapomenout tento trezor. Později jej můžete opět přidat. Žádné šifrované soubory nebudou z vašeho disku smazány.
|
||||||
removeVault.confirmBtn=Odstranit trezor
|
|
||||||
|
|
||||||
# Change Password
|
# Change Password
|
||||||
changepassword.title=Změnit heslo
|
changepassword.title=Změnit heslo
|
||||||
@@ -137,6 +137,7 @@ unlock.success.revealBtn=Zobrazit jednotku
|
|||||||
unlock.error.customPath.message=Nelze připojit trezor k vlastní cestě
|
unlock.error.customPath.message=Nelze připojit trezor k vlastní cestě
|
||||||
unlock.error.customPath.description.notSupported=Pokud chcete pokračovat v používání vlastní cesty, přejděte do nastavení a vyberte typ hlasitosti, který ji podporuje. V opačném případě přejděte do možností trezoru a vyberte podporovaný přípojný bod.
|
unlock.error.customPath.description.notSupported=Pokud chcete pokračovat v používání vlastní cesty, přejděte do nastavení a vyberte typ hlasitosti, který ji podporuje. V opačném případě přejděte do možností trezoru a vyberte podporovaný přípojný bod.
|
||||||
unlock.error.customPath.description.notExists=Cesta k připojení neexistuje. Buď ji vytvořte ve vašem lokálním souborovém systému, nebo ji změňte v možnostech trezoru.
|
unlock.error.customPath.description.notExists=Cesta k připojení neexistuje. Buď ji vytvořte ve vašem lokálním souborovém systému, nebo ji změňte v možnostech trezoru.
|
||||||
|
unlock.error.customPath.description.inUse=Označení úložiště nebo vlastní cesta "%s" se již používá.
|
||||||
unlock.error.restartRequired.message=Nelze odemknout trezor
|
unlock.error.restartRequired.message=Nelze odemknout trezor
|
||||||
unlock.error.restartRequired.description=Změňte typ svazku v možnostech trezoru nebo restartujte Cryptomator.
|
unlock.error.restartRequired.description=Změňte typ svazku v možnostech trezoru nebo restartujte Cryptomator.
|
||||||
unlock.error.title=Odemknutí "%s" selhalo
|
unlock.error.title=Odemknutí "%s" selhalo
|
||||||
@@ -171,7 +172,6 @@ hub.registerFailed.description.generic=Došlo k chybě v registračním procesu.
|
|||||||
hub.registerFailed.description.deviceAlreadyExists=Toto zařízení je již registrováno pro jiného uživatele. Zkuste změnit uživatelský účet nebo použijte jiné zařízení.
|
hub.registerFailed.description.deviceAlreadyExists=Toto zařízení je již registrováno pro jiného uživatele. Zkuste změnit uživatelský účet nebo použijte jiné zařízení.
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Přístup odepřen
|
hub.unauthorized.message=Přístup odepřen
|
||||||
hub.unauthorized.description=Vaše zařízení dosud nebylo oprávněno k přístupu k tomuto trezoru. Požádejte vlastníka trezoru, aby jej autorizoval.
|
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Požadována akce
|
hub.requireAccountInit.message=Požadována akce
|
||||||
hub.requireAccountInit.description.0=Chcete-li pokračovat, vyplňte prosím požadované kroky ve vašem
|
hub.requireAccountInit.description.0=Chcete-li pokračovat, vyplňte prosím požadované kroky ve vašem
|
||||||
@@ -315,7 +315,6 @@ preferences.contribute.getCertificate=Ještě žádný nemáte? Naučte se, jak
|
|||||||
preferences.contribute.promptText=Sem vložte kód certifikátu podporovatele
|
preferences.contribute.promptText=Sem vložte kód certifikátu podporovatele
|
||||||
|
|
||||||
### Remove License Key Dialog
|
### Remove License Key Dialog
|
||||||
removeCert.confirmBtn=Odstranit
|
|
||||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||||
|
|
||||||
## About
|
## About
|
||||||
@@ -516,6 +515,8 @@ dokanySupportEnd.title=Oznámení o zastaralosti
|
|||||||
dokanySupportEnd.message=Konec podpory pro Dokany
|
dokanySupportEnd.message=Konec podpory pro Dokany
|
||||||
dokanySupportEnd.preferencesBtn=Otevřít předvolby
|
dokanySupportEnd.preferencesBtn=Otevřít předvolby
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Sdílet trezor
|
shareVault.title=Sdílet trezor
|
||||||
shareVault.message=Chcete sdílet svůj trezor s ostatními?
|
shareVault.message=Chcete sdílet svůj trezor s ostatními?
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ generic.button.copied=Kopieret!
|
|||||||
generic.button.done=Færdig
|
generic.button.done=Færdig
|
||||||
generic.button.next=Næste
|
generic.button.next=Næste
|
||||||
generic.button.print=Print
|
generic.button.print=Print
|
||||||
|
generic.button.remove=Fjern
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
error.message=Der opstod en fejl
|
error.message=Der opstod en fejl
|
||||||
@@ -105,7 +106,6 @@ addvaultwizard.success.unlockNow=Lås op nu
|
|||||||
removeVault.title=Fjern "%s"
|
removeVault.title=Fjern "%s"
|
||||||
removeVault.message=Fjern boks?
|
removeVault.message=Fjern boks?
|
||||||
removeVault.description=Dette får blot Cryptomator til at glemme boksen. Du kan tilføje den igen senere. Ingen krypterede filer bliver slettet fra harddisken.
|
removeVault.description=Dette får blot Cryptomator til at glemme boksen. Du kan tilføje den igen senere. Ingen krypterede filer bliver slettet fra harddisken.
|
||||||
removeVault.confirmBtn=Fjern boks
|
|
||||||
|
|
||||||
# Change Password
|
# Change Password
|
||||||
changepassword.title=Skift adgangskode
|
changepassword.title=Skift adgangskode
|
||||||
@@ -176,7 +176,7 @@ hub.registerFailed.description.generic=Der opstod en fejl i registreringsprocess
|
|||||||
hub.registerFailed.description.deviceAlreadyExists=Denne enhed er allerede registreret af en anden bruger. Prøv at ændre brugerkontoen eller brug en anden enhed.
|
hub.registerFailed.description.deviceAlreadyExists=Denne enhed er allerede registreret af en anden bruger. Prøv at ændre brugerkontoen eller brug en anden enhed.
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Adgang nægtet
|
hub.unauthorized.message=Adgang nægtet
|
||||||
hub.unauthorized.description=Din enhed er endnu ikke blevet godkendt til at få adgang til denne boks. Spørg boks-ejeren om godkendelse.
|
hub.unauthorized.description=Du har ikke tilladelse til at åbne denne boks. Kontant ejeren af boksen for at anmode om adgang.
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Handling påkrævet
|
hub.requireAccountInit.message=Handling påkrævet
|
||||||
hub.requireAccountInit.description.0=For at fortsætte, skal du fuldføre de nødvendige trin i din
|
hub.requireAccountInit.description.0=For at fortsætte, skal du fuldføre de nødvendige trin i din
|
||||||
@@ -287,6 +287,7 @@ preferences.general.debugLogging=Aktivér fejllogning
|
|||||||
preferences.general.debugDirectory=Vis logfiler
|
preferences.general.debugDirectory=Vis logfiler
|
||||||
preferences.general.autoStart=Start Cryptomator automatisk ved opstart
|
preferences.general.autoStart=Start Cryptomator automatisk ved opstart
|
||||||
preferences.general.keychainBackend=Gem adgangskoder med
|
preferences.general.keychainBackend=Gem adgangskoder med
|
||||||
|
preferences.general.quickAccessService=Tilføj oplåste bokse til området hurtig adgang
|
||||||
## Interface
|
## Interface
|
||||||
preferences.interface=Brugerflade
|
preferences.interface=Brugerflade
|
||||||
preferences.interface.theme=Udseende
|
preferences.interface.theme=Udseende
|
||||||
@@ -319,6 +320,7 @@ preferences.updates.currentVersion=Nuværende version: %s
|
|||||||
preferences.updates.autoUpdateCheck=Søg automatisk efter opdateringer
|
preferences.updates.autoUpdateCheck=Søg automatisk efter opdateringer
|
||||||
preferences.updates.checkNowBtn=Kontrollér nu
|
preferences.updates.checkNowBtn=Kontrollér nu
|
||||||
preferences.updates.updateAvailable=Opdatering til version %s er tilgængelig.
|
preferences.updates.updateAvailable=Opdatering til version %s er tilgængelig.
|
||||||
|
preferences.updates.checkFailed=Søgning efter opdateringer fejlede. Tjek din internetforbindelse eller forsøg igen senere.
|
||||||
|
|
||||||
## Contribution
|
## Contribution
|
||||||
preferences.contribute=Støt os
|
preferences.contribute=Støt os
|
||||||
@@ -328,7 +330,6 @@ preferences.contribute.getCertificate=Har du ikke et allerede? Se her hvordan du
|
|||||||
preferences.contribute.promptText=Indsæt koden for supporter-certifikatet her
|
preferences.contribute.promptText=Indsæt koden for supporter-certifikatet her
|
||||||
|
|
||||||
### Remove License Key Dialog
|
### Remove License Key Dialog
|
||||||
removeCert.confirmBtn=Fjern
|
|
||||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||||
|
|
||||||
## About
|
## About
|
||||||
@@ -379,6 +380,8 @@ main.vaultlist.contextMenu.unlockNow=Lås op nu
|
|||||||
main.vaultlist.contextMenu.vaultoptions=Vis boksindstillinger
|
main.vaultlist.contextMenu.vaultoptions=Vis boksindstillinger
|
||||||
main.vaultlist.contextMenu.reveal=Vis drev
|
main.vaultlist.contextMenu.reveal=Vis drev
|
||||||
##Notificaition
|
##Notificaition
|
||||||
|
main.notification.updateAvailable=Opdatering er tilgængelig.
|
||||||
|
main.notification.support=Støt Cryptomator.
|
||||||
## Vault Detail
|
## Vault Detail
|
||||||
### Welcome
|
### Welcome
|
||||||
main.vaultDetail.welcomeOnboarding=Tak fordi du valgte Cryptomator til at beskytte dine filer. Hvis du har brug for hjælp, så tjek vores guider for at komme i gang:
|
main.vaultDetail.welcomeOnboarding=Tak fordi du valgte Cryptomator til at beskytte dine filer. Hvis du har brug for hjælp, så tjek vores guider for at komme i gang:
|
||||||
@@ -531,6 +534,8 @@ updateReminder.yesAutomatically=Ja, automatisk
|
|||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
dokanySupportEnd.preferencesBtn=Åbn Indstillinger
|
dokanySupportEnd.preferencesBtn=Åbn Indstillinger
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Del Boks
|
shareVault.title=Del Boks
|
||||||
shareVault.message=Vil du dele din boks med andre?
|
shareVault.message=Vil du dele din boks med andre?
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ generic.button.copied=Kopiert!
|
|||||||
generic.button.done=Fertig
|
generic.button.done=Fertig
|
||||||
generic.button.next=Weiter
|
generic.button.next=Weiter
|
||||||
generic.button.print=Drucken
|
generic.button.print=Drucken
|
||||||
|
generic.button.remove=Entfernen
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
error.message=Ein Fehler ist aufgetreten
|
error.message=Ein Fehler ist aufgetreten
|
||||||
@@ -105,7 +106,6 @@ addvaultwizard.success.unlockNow=Jetzt entsperren
|
|||||||
removeVault.title=„%s“ entfernen
|
removeVault.title=„%s“ entfernen
|
||||||
removeVault.message=Tresor entfernen?
|
removeVault.message=Tresor entfernen?
|
||||||
removeVault.description=Dieser Tresor wird nur aus der Tresorliste entfernt; du kannst ihn später jederzeit wieder hinzufügen. Es werden keine verschlüsselten Daten gelöscht.
|
removeVault.description=Dieser Tresor wird nur aus der Tresorliste entfernt; du kannst ihn später jederzeit wieder hinzufügen. Es werden keine verschlüsselten Daten gelöscht.
|
||||||
removeVault.confirmBtn=Tresor entfernen
|
|
||||||
|
|
||||||
# Change Password
|
# Change Password
|
||||||
changepassword.title=Passwort ändern
|
changepassword.title=Passwort ändern
|
||||||
@@ -176,7 +176,6 @@ hub.registerFailed.description.generic=Im Registrierungsprozess ist ein Fehler a
|
|||||||
hub.registerFailed.description.deviceAlreadyExists=Dieses Gerät ist bereits für einen anderen Benutzer registriert. Ändere das Benutzerkonto oder verwende ein anderes Gerät.
|
hub.registerFailed.description.deviceAlreadyExists=Dieses Gerät ist bereits für einen anderen Benutzer registriert. Ändere das Benutzerkonto oder verwende ein anderes Gerät.
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Zugriff verweigert
|
hub.unauthorized.message=Zugriff verweigert
|
||||||
hub.unauthorized.description=Dein Gerät wurde noch nicht für den Zugriff auf diesen Tresor autorisiert. Bitte den Tresorbesitzer, dein Gerät zu autorisieren.
|
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Aktion erforderlich
|
hub.requireAccountInit.message=Aktion erforderlich
|
||||||
hub.requireAccountInit.description.0=Um fortzufahren, führe bitte die erforderlichen Schritte in deinem
|
hub.requireAccountInit.description.0=Um fortzufahren, führe bitte die erforderlichen Schritte in deinem
|
||||||
@@ -245,7 +244,7 @@ health.checkList.selectAllButton=Alle Prüfungen auswählen
|
|||||||
health.checkList.deselectAllButton=Alle Prüfungen abwählen
|
health.checkList.deselectAllButton=Alle Prüfungen abwählen
|
||||||
health.check.runBatchBtn=Ausgewählte Prüfungen ausführen
|
health.check.runBatchBtn=Ausgewählte Prüfungen ausführen
|
||||||
## Detail view
|
## Detail view
|
||||||
health.check.detail.noSelectedCheck=Wähle für die Ergebnisse eine abgeschlossene Intregritätsprüfung in der Liste links aus.
|
health.check.detail.noSelectedCheck=Wähle für die Ergebnisse eine abgeschlossene Integritätsprüfung in der Liste links aus.
|
||||||
health.check.detail.checkScheduled=Die Prüfung ist geplant.
|
health.check.detail.checkScheduled=Die Prüfung ist geplant.
|
||||||
health.check.detail.checkRunning=Die Prüfung läuft derzeit …
|
health.check.detail.checkRunning=Die Prüfung läuft derzeit …
|
||||||
health.check.detail.checkSkipped=Die Prüfung wurde nicht zur Ausführung ausgewählt.
|
health.check.detail.checkSkipped=Die Prüfung wurde nicht zur Ausführung ausgewählt.
|
||||||
@@ -287,7 +286,7 @@ preferences.general.debugLogging=Diagnoseprotokoll aktivieren
|
|||||||
preferences.general.debugDirectory=Protokolldateien anzeigen
|
preferences.general.debugDirectory=Protokolldateien anzeigen
|
||||||
preferences.general.autoStart=Cryptomator beim Systemstart starten
|
preferences.general.autoStart=Cryptomator beim Systemstart starten
|
||||||
preferences.general.keychainBackend=Passwörter speichern mit
|
preferences.general.keychainBackend=Passwörter speichern mit
|
||||||
preferences.general.quickAccessService=Die entsperrten Tresore zum Schnellzugriff hinzufügen
|
preferences.general.quickAccessService=Entsperrte Tresore zum Schnellzugriff hinzufügen
|
||||||
## Interface
|
## Interface
|
||||||
preferences.interface=Benutzeroberfläche
|
preferences.interface=Benutzeroberfläche
|
||||||
preferences.interface.theme=Erscheinungsbild
|
preferences.interface.theme=Erscheinungsbild
|
||||||
@@ -301,7 +300,7 @@ preferences.interface.interfaceOrientation=Oberflächenausrichtung
|
|||||||
preferences.interface.interfaceOrientation.ltr=Von links nach rechts
|
preferences.interface.interfaceOrientation.ltr=Von links nach rechts
|
||||||
preferences.interface.interfaceOrientation.rtl=Von rechts nach links
|
preferences.interface.interfaceOrientation.rtl=Von rechts nach links
|
||||||
preferences.interface.showTrayIcon=Symbol im Infobereich anzeigen (Neustart erforderlich)
|
preferences.interface.showTrayIcon=Symbol im Infobereich anzeigen (Neustart erforderlich)
|
||||||
preferences.interface.compactMode=Kompakte Tresoransicht einschalten
|
preferences.interface.compactMode=Kompakte Tresorliste
|
||||||
## Volume
|
## Volume
|
||||||
preferences.volume=Virtuelles Laufwerk
|
preferences.volume=Virtuelles Laufwerk
|
||||||
preferences.volume.type=Standard-Laufwerkstyp
|
preferences.volume.type=Standard-Laufwerkstyp
|
||||||
@@ -343,7 +342,6 @@ preferences.contribute.sponsor=Sponsern
|
|||||||
removeCert.title=Zertifikat entfernen
|
removeCert.title=Zertifikat entfernen
|
||||||
removeCert.message=Supporter-Zertifikat entfernen?
|
removeCert.message=Supporter-Zertifikat entfernen?
|
||||||
removeCert.description=Die Kernfunktionen von Cryptomator sind davon nicht betroffen. Weder der Zugriff auf deine Tresore ist eingeschränkt, noch wird das Sicherheitsniveau verringert.
|
removeCert.description=Die Kernfunktionen von Cryptomator sind davon nicht betroffen. Weder der Zugriff auf deine Tresore ist eingeschränkt, noch wird das Sicherheitsniveau verringert.
|
||||||
removeCert.confirmBtn=Entfernen
|
|
||||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||||
|
|
||||||
## About
|
## About
|
||||||
@@ -548,11 +546,13 @@ updateReminder.yesOnce=Ja, einmalig
|
|||||||
updateReminder.yesAutomatically=Ja, automatisch
|
updateReminder.yesAutomatically=Ja, automatisch
|
||||||
|
|
||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
dokanySupportEnd.title=Hinweis: nicht mehr verwendet
|
dokanySupportEnd.title=Hinweis zur Ausmusterung
|
||||||
dokanySupportEnd.message=Ende der Unterstützung für Dokany
|
dokanySupportEnd.message=Supportende für Dokany
|
||||||
dokanySupportEnd.description=Der Laufwerkstyp Dokany wird von Cryptomator nicht weiter unterstützt. Deine Einstellungen wurden angepasst, dass der Standard-Laufwerkstyp verwendet wird. Du kannst den Standardtyp in den Einstellungen anzeigen lassen.
|
dokanySupportEnd.description=Der Laufwerkstyp Dokany wird von Cryptomator nicht weiter unterstützt. Deine Einstellungen wurden angepasst, dass der Standard-Laufwerkstyp verwendet wird. Du kannst den Standardtyp in den Einstellungen anzeigen lassen.
|
||||||
dokanySupportEnd.preferencesBtn=Einstellungen öffnen
|
dokanySupportEnd.preferencesBtn=Einstellungen öffnen
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Tresor teilen
|
shareVault.title=Tresor teilen
|
||||||
shareVault.message=Möchtest du deinen Tresor mit anderen teilen?
|
shareVault.message=Möchtest du deinen Tresor mit anderen teilen?
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ generic.button.copied=Αντιγράφηκε!
|
|||||||
generic.button.done=Κλείσιμο
|
generic.button.done=Κλείσιμο
|
||||||
generic.button.next=Επόμενο
|
generic.button.next=Επόμενο
|
||||||
generic.button.print=Εκτύπωση
|
generic.button.print=Εκτύπωση
|
||||||
|
generic.button.remove=Αφαίρεση
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
error.message=Παρουσιάστηκε σφάλμα
|
error.message=Παρουσιάστηκε σφάλμα
|
||||||
@@ -105,7 +106,6 @@ addvaultwizard.success.unlockNow=Ξεκλείδωμα τώρα
|
|||||||
removeVault.title=Διαγραφή "%s"
|
removeVault.title=Διαγραφή "%s"
|
||||||
removeVault.message=Διαγραφή κρύπτης;
|
removeVault.message=Διαγραφή κρύπτης;
|
||||||
removeVault.description=Αυτό θα κάνει το Cryptomator να ξεχάσει αυτή την κρύπτη. Μπορείτε να την προσθέσετε ξανά. Κανένα κρυπτογραφημένο αρχείο δε θα διαγραφεί από τον σκληρό σας δίσκο.
|
removeVault.description=Αυτό θα κάνει το Cryptomator να ξεχάσει αυτή την κρύπτη. Μπορείτε να την προσθέσετε ξανά. Κανένα κρυπτογραφημένο αρχείο δε θα διαγραφεί από τον σκληρό σας δίσκο.
|
||||||
removeVault.confirmBtn=Διαγραφή Κρύπτης
|
|
||||||
|
|
||||||
# Change Password
|
# Change Password
|
||||||
changepassword.title=Αλλαγή κωδικού πρόσβασης
|
changepassword.title=Αλλαγή κωδικού πρόσβασης
|
||||||
@@ -176,7 +176,7 @@ hub.registerFailed.description.generic=Παρουσιάστηκε σφάλμα
|
|||||||
hub.registerFailed.description.deviceAlreadyExists=Αυτή η συσκευή είναι ήδη εγγεγραμμένη για διαφορετικό χρήστη. Προσπαθήστε να αλλάξετε τον λογαριασμό χρήστη ή να χρησιμοποιήσετε διαφορετική συσκευή.
|
hub.registerFailed.description.deviceAlreadyExists=Αυτή η συσκευή είναι ήδη εγγεγραμμένη για διαφορετικό χρήστη. Προσπαθήστε να αλλάξετε τον λογαριασμό χρήστη ή να χρησιμοποιήσετε διαφορετική συσκευή.
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Δεν επιτρέπεται η πρόσβαση
|
hub.unauthorized.message=Δεν επιτρέπεται η πρόσβαση
|
||||||
hub.unauthorized.description=Η συσκευή σας δεν έχει ακόμη εξουσιοδοτηθεί να έχει πρόσβαση σε αυτή την κρύπτη. Ζητήστε από τον κάτοχο της κρύπτης να την εξουσιοδοτήσει.
|
hub.unauthorized.description=Δεν είστε εξουσιοδοτημένοι να ανοίξετε αυτή την κρύπτη. Επικοινωνήστε με τον ιδιοκτήτη τς κρύπτης για να ζητήσετε πρόσβαση.
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Απαιτείται ενέργεια
|
hub.requireAccountInit.message=Απαιτείται ενέργεια
|
||||||
hub.requireAccountInit.description.0=Για να συνεχίσετε, παρακαλούμε ολοκληρώστε τα βήματα που απαιτούνται στο δικό σας
|
hub.requireAccountInit.description.0=Για να συνεχίσετε, παρακαλούμε ολοκληρώστε τα βήματα που απαιτούνται στο δικό σας
|
||||||
@@ -343,7 +343,6 @@ preferences.contribute.sponsor=Χορηγός
|
|||||||
removeCert.title=Αφαίρεση Πιστοποιητικού
|
removeCert.title=Αφαίρεση Πιστοποιητικού
|
||||||
removeCert.message=Αφαίρεση πιστοποιητικού υποστηρικτή;
|
removeCert.message=Αφαίρεση πιστοποιητικού υποστηρικτή;
|
||||||
removeCert.description=Οι βασικές λειτουργίες του Cryptomator δεν επηρεάζονται από αυτό. Ούτε η πρόσβαση στις κρύπτες σας είναι περιορισμένη ούτε το επίπεδο ασφάλειας μειώνεται.
|
removeCert.description=Οι βασικές λειτουργίες του Cryptomator δεν επηρεάζονται από αυτό. Ούτε η πρόσβαση στις κρύπτες σας είναι περιορισμένη ούτε το επίπεδο ασφάλειας μειώνεται.
|
||||||
removeCert.confirmBtn=Αφαίρεση
|
|
||||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||||
|
|
||||||
## About
|
## About
|
||||||
@@ -553,6 +552,12 @@ dokanySupportEnd.message=Τέλος υποστήριξης για Dokany
|
|||||||
dokanySupportEnd.description=Ο τύπος τόμου Dokany δεν υποστηρίζεται πλέον από το Cryptomator. Οι ρυθμίσεις σας έχουν προσαρμοστεί για να χρησιμοποιούν τον προεπιλεγμένο τύπο τόμου. Μπορείτε να δείτε τον προεπιλεγμένο τύπο στις προτιμήσεις.
|
dokanySupportEnd.description=Ο τύπος τόμου Dokany δεν υποστηρίζεται πλέον από το Cryptomator. Οι ρυθμίσεις σας έχουν προσαρμοστεί για να χρησιμοποιούν τον προεπιλεγμένο τύπο τόμου. Μπορείτε να δείτε τον προεπιλεγμένο τύπο στις προτιμήσεις.
|
||||||
dokanySupportEnd.preferencesBtn=Άνοιγμα Προτιμήσεων
|
dokanySupportEnd.preferencesBtn=Άνοιγμα Προτιμήσεων
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
retryIfReadonly.title=Περιορισμένη Πρόσβαση Κρύπτης
|
||||||
|
retryIfReadonly.message=Δεν υπάρχει πρόσβαση εγγραφής στον κατάλογο κρύπτης
|
||||||
|
retryIfReadonly.description=Το Cryptomator δεν μπορεί να γράψει στον κατάλογο κρύπτης. Μπορείτε να αλλάξετε την κρύπτη ώστε να είναι μόνο για ανάγνωση και να προσπαθήσετε ξανά. Αυτή η επιλογή μπορεί να απενεργοποιηθεί στις επιλογές κρύπτης.
|
||||||
|
retryIfReadonly.retry=Αλλαγή και Επανάληψη
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Κοινή χρήση Κρύπτης
|
shareVault.title=Κοινή χρήση Κρύπτης
|
||||||
shareVault.message=Θα θέλατε να μοιραστείτε την κρύπτη σας με άλλους;
|
shareVault.message=Θα θέλατε να μοιραστείτε την κρύπτη σας με άλλους;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user