mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-22 16:04:28 +00:00
Compare commits
135
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 |
@@ -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:
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ env:
|
|||||||
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
|
||||||
|
|||||||
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)
|
||||||
@@ -72,7 +72,7 @@ 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
|
||||||
|
|
||||||
|
|||||||
+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
|
||||||
|
|||||||
@@ -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</version>
|
<version>1.16.0-SNAPSHOT</version>
|
||||||
<name>Cryptomator Desktop App</name>
|
<name>Cryptomator Desktop App</name>
|
||||||
|
|
||||||
<organization>
|
<organization>
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
<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.8.0</cryptomator.cryptofs.version>
|
<cryptomator.cryptofs.version>2.9.0-beta2</cryptomator.cryptofs.version>
|
||||||
<cryptomator.integrations.version>1.5.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>
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
|
|
||||||
<!-- 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>12.0.1</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.5.0</license-generator.version>
|
<license-generator.version>2.5.0</license-generator.version>
|
||||||
<junit-tree-reporter.version>1.4.0</junit-tree-reporter.version>
|
<junit-tree-reporter.version>1.4.0</junit-tree-reporter.version>
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -74,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);
|
||||||
@@ -85,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;
|
||||||
@@ -102,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);
|
||||||
}
|
}
|
||||||
@@ -143,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);
|
||||||
}
|
}
|
||||||
@@ -251,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());
|
||||||
|
}
|
||||||
|
}
|
||||||
+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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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
|
||||||
@@ -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.
|
||||||
@@ -577,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
|
||||||
|
|||||||
@@ -551,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=هل ترغب في مشاركة خزانتك مع الآخرين؟
|
||||||
|
|||||||
@@ -515,4 +515,6 @@ updateReminder.yesAutomatically=Эйе, автоматик рәүештә
|
|||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
dokanySupportEnd.preferencesBtn=Көйләүҙәрҙе ас
|
dokanySupportEnd.preferencesBtn=Көйләүҙәрҙе ас
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -495,4 +495,6 @@ updateReminder.yesAutomatically=Так, аўтаматычна
|
|||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
dokanySupportEnd.preferencesBtn=Адчыніць налады
|
dokanySupportEnd.preferencesBtn=Адчыніць налады
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -515,4 +515,6 @@ updateReminder.yesAutomatically=Да, автоматично
|
|||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
dokanySupportEnd.preferencesBtn=Към настройките
|
dokanySupportEnd.preferencesBtn=Към настройките
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -182,4 +182,6 @@ vaultOptions.mount.mountPoint.directoryPickerButton=নির্বাচন ক
|
|||||||
|
|
||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -318,4 +318,6 @@ quit.lockAndQuitBtn=Zaključaj i zatvori
|
|||||||
|
|
||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -547,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?
|
||||||
|
|||||||
@@ -515,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?
|
||||||
|
|||||||
@@ -176,6 +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=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
|
||||||
@@ -286,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
|
||||||
@@ -318,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
|
||||||
@@ -377,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:
|
||||||
@@ -529,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?
|
||||||
|
|||||||
@@ -551,6 +551,8 @@ 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?
|
||||||
|
|||||||
@@ -552,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=Θα θέλατε να μοιραστείτε την κρύπτη σας με άλλους;
|
||||||
|
|||||||
@@ -1,198 +0,0 @@
|
|||||||
# Locale Specific CSS files such as CJK, RTL,...
|
|
||||||
|
|
||||||
# Generics
|
|
||||||
## Button
|
|
||||||
generic.button.apply=Apply
|
|
||||||
generic.button.back=Back
|
|
||||||
generic.button.cancel=Cancel
|
|
||||||
generic.button.change=Change
|
|
||||||
generic.button.choose=Choose…
|
|
||||||
generic.button.close=Close
|
|
||||||
generic.button.done=Done
|
|
||||||
generic.button.next=Next
|
|
||||||
|
|
||||||
# Error
|
|
||||||
|
|
||||||
# Defaults
|
|
||||||
|
|
||||||
# Tray Menu
|
|
||||||
traymenu.showMainWindow=Show
|
|
||||||
traymenu.showPreferencesWindow=Preferences
|
|
||||||
traymenu.quitApplication=Quit
|
|
||||||
traymenu.vault.unlock=Unlock
|
|
||||||
traymenu.vault.lock=Lock
|
|
||||||
traymenu.vault.reveal=Reveal
|
|
||||||
|
|
||||||
# Add Vault Wizard
|
|
||||||
addvaultwizard.title=Add Vault
|
|
||||||
## New
|
|
||||||
### Name
|
|
||||||
addvaultwizard.new.nameInstruction=Choose a name for the vault
|
|
||||||
addvaultwizard.new.namePrompt=Vault Name
|
|
||||||
### Location
|
|
||||||
addvaultwizard.new.locationInstruction=Where should Cryptomator store the encrypted files of your vault?
|
|
||||||
addvaultwizard.new.locationLabel=Storage location
|
|
||||||
addvaultwizard.new.locationPrompt=…
|
|
||||||
addvaultwizard.new.directoryPickerButton=Choose…
|
|
||||||
addvaultwizard.new.directoryPickerTitle=Select Directory
|
|
||||||
### Expert Settings
|
|
||||||
### Password
|
|
||||||
addvaultwizard.new.createVaultBtn=Create Vault
|
|
||||||
### Information
|
|
||||||
## Existing
|
|
||||||
addvaultwizard.existing.chooseBtn=Choose…
|
|
||||||
## Success
|
|
||||||
addvaultwizard.success.nextStepsInstructions=Added vault "%s".\nYou need to unlock this vault to access or add contents. Alternatively you can unlock it at any later point in time.
|
|
||||||
addvaultwizard.success.unlockNow=Unlock Now
|
|
||||||
|
|
||||||
# Remove Vault
|
|
||||||
removeVault.confirmBtn=Remove Vault
|
|
||||||
|
|
||||||
# Change Password
|
|
||||||
changepassword.title=Change Password
|
|
||||||
changepassword.enterOldPassword=Enter the current password for "%s"
|
|
||||||
|
|
||||||
# Forget Password
|
|
||||||
forgetPassword.title=Forget Password
|
|
||||||
forgetPassword.confirmBtn=Forget Password
|
|
||||||
|
|
||||||
# Unlock
|
|
||||||
unlock.passwordPrompt=Enter password for "%s":
|
|
||||||
unlock.unlockBtn=Unlock
|
|
||||||
## Select
|
|
||||||
unlock.chooseMasterkey.filePickerTitle=Select Masterkey File
|
|
||||||
## Success
|
|
||||||
unlock.success.revealBtn=Reveal Drive
|
|
||||||
## Failure
|
|
||||||
## Hub
|
|
||||||
### Waiting
|
|
||||||
### Receive Key
|
|
||||||
### Register Device
|
|
||||||
### Register Device Legacy
|
|
||||||
### Registration Success
|
|
||||||
hub.registerSuccess.unlockBtn=Unlock
|
|
||||||
### Registration Failed
|
|
||||||
### Unauthorized
|
|
||||||
### Requires Account Initialization
|
|
||||||
### License Exceeded
|
|
||||||
|
|
||||||
# Lock
|
|
||||||
## Force
|
|
||||||
## Failure
|
|
||||||
|
|
||||||
# Migration
|
|
||||||
migration.title=Upgrade Vault
|
|
||||||
## Start
|
|
||||||
migration.start.header=Upgrade Vault
|
|
||||||
## Run
|
|
||||||
migration.run.enterPassword=Enter the password for "%s"
|
|
||||||
migration.run.startMigrationBtn=Migrate Vault
|
|
||||||
## Success
|
|
||||||
migration.success.nextStepsInstructions=Migrated "%s" successfully.\nYou can now unlock your vault.
|
|
||||||
migration.success.unlockNow=Unlock Now
|
|
||||||
## Missing file system capabilities
|
|
||||||
## Impossible
|
|
||||||
|
|
||||||
# Health Check
|
|
||||||
## Start
|
|
||||||
## Start Failure
|
|
||||||
## Check Selection
|
|
||||||
## Detail view
|
|
||||||
## Result view
|
|
||||||
## Fix Application
|
|
||||||
|
|
||||||
# Preferences
|
|
||||||
preferences.title=Preferences
|
|
||||||
## General
|
|
||||||
preferences.general=General
|
|
||||||
preferences.general.startHidden=Hide window when starting Cryptomator
|
|
||||||
preferences.general.debugLogging=Enable debug logging
|
|
||||||
## Interface
|
|
||||||
## Volume
|
|
||||||
preferences.volume=Virtual Drive
|
|
||||||
## Updates
|
|
||||||
preferences.updates=Updates
|
|
||||||
preferences.updates.currentVersion=Current Version: %s
|
|
||||||
preferences.updates.autoUpdateCheck=Check for updates automatically
|
|
||||||
preferences.updates.checkNowBtn=Check Now
|
|
||||||
preferences.updates.updateAvailable=Update to version %s available.
|
|
||||||
|
|
||||||
## Contribution
|
|
||||||
|
|
||||||
### Remove License Key Dialog
|
|
||||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
|
||||||
|
|
||||||
## About
|
|
||||||
|
|
||||||
# Vault Statistics
|
|
||||||
## Read
|
|
||||||
## Write
|
|
||||||
|
|
||||||
## Accesses
|
|
||||||
|
|
||||||
|
|
||||||
# Main Window
|
|
||||||
## Vault List
|
|
||||||
main.vaultlist.emptyList.onboardingInstruction=Click here to add a vault
|
|
||||||
main.vaultlist.contextMenu.lock=Lock
|
|
||||||
main.vaultlist.contextMenu.unlockNow=Unlock Now
|
|
||||||
main.vaultlist.contextMenu.reveal=Reveal Drive
|
|
||||||
##Notificaition
|
|
||||||
## Vault Detail
|
|
||||||
### Welcome
|
|
||||||
### Locked
|
|
||||||
main.vaultDetail.lockedStatus=LOCKED
|
|
||||||
main.vaultDetail.unlockNowBtn=Unlock Now
|
|
||||||
main.vaultDetail.optionsBtn=Vault Options
|
|
||||||
### Unlocked
|
|
||||||
main.vaultDetail.unlockedStatus=UNLOCKED
|
|
||||||
main.vaultDetail.accessLocation=Your vault's contents are accessible here:
|
|
||||||
main.vaultDetail.revealBtn=Reveal Drive
|
|
||||||
main.vaultDetail.lockBtn=Lock
|
|
||||||
main.vaultDetail.throughput.idle=idle
|
|
||||||
main.vaultDetail.throughput.mbps=%.1f MiB/s
|
|
||||||
### Missing
|
|
||||||
### Needs Migration
|
|
||||||
main.vaultDetail.migrateButton=Upgrade Vault
|
|
||||||
### Error
|
|
||||||
|
|
||||||
# Wrong File Alert
|
|
||||||
|
|
||||||
# Vault Options
|
|
||||||
## General
|
|
||||||
vaultOptions.general=General
|
|
||||||
vaultOptions.general.vaultName=Vault Name
|
|
||||||
vaultOptions.general.actionAfterUnlock.reveal=Reveal Drive
|
|
||||||
|
|
||||||
## Mount
|
|
||||||
vaultOptions.mount=Mounting
|
|
||||||
vaultOptions.mount.mountPoint.directoryPickerButton=Choose…
|
|
||||||
## Master Key
|
|
||||||
vaultOptions.masterkey.changePasswordBtn=Change Password
|
|
||||||
## Hub
|
|
||||||
|
|
||||||
# Recovery Key
|
|
||||||
## Display Recovery Key
|
|
||||||
## Reset Password
|
|
||||||
### Enter Recovery Key
|
|
||||||
### Reset Password
|
|
||||||
### Recovery Key Password Reset Success
|
|
||||||
|
|
||||||
# Convert Vault
|
|
||||||
|
|
||||||
# New Password
|
|
||||||
passwordStrength.messageLabel.0=Very weak
|
|
||||||
passwordStrength.messageLabel.1=Weak
|
|
||||||
passwordStrength.messageLabel.2=Fair
|
|
||||||
passwordStrength.messageLabel.3=Strong
|
|
||||||
passwordStrength.messageLabel.4=Very strong
|
|
||||||
|
|
||||||
# Quit
|
|
||||||
|
|
||||||
# Forced Quit
|
|
||||||
|
|
||||||
# Update Reminder
|
|
||||||
|
|
||||||
#Dokany Support End
|
|
||||||
|
|
||||||
# Share Vault
|
|
||||||
@@ -552,6 +552,12 @@ dokanySupportEnd.message=Fin de soporte para Dokany
|
|||||||
dokanySupportEnd.description=El tipo de volumen Dokany ya no está soportado por Cryptomator. Su configuración se ajusta para utilizar el tipo de volumen predeterminado ahora. Puede ver el tipo predeterminado en las preferencias.
|
dokanySupportEnd.description=El tipo de volumen Dokany ya no está soportado por Cryptomator. Su configuración se ajusta para utilizar el tipo de volumen predeterminado ahora. Puede ver el tipo predeterminado en las preferencias.
|
||||||
dokanySupportEnd.preferencesBtn=Abrir preferencias
|
dokanySupportEnd.preferencesBtn=Abrir preferencias
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
retryIfReadonly.title=Acceso restringido a la Bóveda
|
||||||
|
retryIfReadonly.message=No tiene acceso de escritura al directorio de bóveda
|
||||||
|
retryIfReadonly.description=Cryptomator no puede escribir en el directorio de la bóveda. Puede cambiar la bóveda para que sea de solo lectura e inténtelo de nuevo. Esta opción puede desactivarse en las opciones de bóveda.
|
||||||
|
retryIfReadonly.retry=Cambiar y reintentar
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Compartir bóveda
|
shareVault.title=Compartir bóveda
|
||||||
shareVault.message=¿Le gustaría compartir su bóveda con alguien más?
|
shareVault.message=¿Le gustaría compartir su bóveda con alguien más?
|
||||||
|
|||||||
@@ -210,4 +210,6 @@ vaultOptions.mount.mountPoint.directoryPickerButton=انتخاب کنید…
|
|||||||
|
|
||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -545,6 +545,8 @@ updateReminder.yesAutomatically=Kyllä, automaattisesti
|
|||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
dokanySupportEnd.preferencesBtn=Avaa asetukset
|
dokanySupportEnd.preferencesBtn=Avaa asetukset
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Jaa holvi
|
shareVault.title=Jaa holvi
|
||||||
shareVault.message=Haluatko jakaa holvisi muiden kanssa?
|
shareVault.message=Haluatko jakaa holvisi muiden kanssa?
|
||||||
|
|||||||
@@ -540,6 +540,8 @@ dokanySupportEnd.message=Pagtatapos ng suporta para sa Dokany
|
|||||||
dokanySupportEnd.description=Ang uri ng volume na Dokany ay hindi na sinusuportahan ng Cryptomator. Isinasaayos ang iyong mga setting upang magamit ang default na uri ng volume ngayon. Maaari mong tingnan ang default na uri sa mga kagustuhan.
|
dokanySupportEnd.description=Ang uri ng volume na Dokany ay hindi na sinusuportahan ng Cryptomator. Isinasaayos ang iyong mga setting upang magamit ang default na uri ng volume ngayon. Maaari mong tingnan ang default na uri sa mga kagustuhan.
|
||||||
dokanySupportEnd.preferencesBtn=Buksan ang Mga Kagustuhan
|
dokanySupportEnd.preferencesBtn=Buksan ang Mga Kagustuhan
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Ibahagi ang Vault
|
shareVault.title=Ibahagi ang Vault
|
||||||
shareVault.message=Gusto mo bang ibahagi ang iyong vault sa iba?
|
shareVault.message=Gusto mo bang ibahagi ang iyong vault sa iba?
|
||||||
|
|||||||
@@ -552,6 +552,12 @@ dokanySupportEnd.message=Fin de la prise en charge de Dokany
|
|||||||
dokanySupportEnd.description=Le type de volume Dokany n'est plus pris en charge par Cryptomator. Vos paramètres sont à présent ajustés pour utiliser le type de volume par défaut. Vous pouvez voir le type par défaut dans les préférences.
|
dokanySupportEnd.description=Le type de volume Dokany n'est plus pris en charge par Cryptomator. Vos paramètres sont à présent ajustés pour utiliser le type de volume par défaut. Vous pouvez voir le type par défaut dans les préférences.
|
||||||
dokanySupportEnd.preferencesBtn=Ouvrir les préférences
|
dokanySupportEnd.preferencesBtn=Ouvrir les préférences
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
retryIfReadonly.title=Accès restreint au coffre
|
||||||
|
retryIfReadonly.message=Pas d'accès en écriture au dossier du coffre
|
||||||
|
retryIfReadonly.description=Cryptomator ne peut pas écrire dans le dossier du coffre. Vous pouvez passer le coffre en mode lecture seule et réessayer. Cette option peut être désactivée dans les options du coffre.
|
||||||
|
retryIfReadonly.retry=Changer et réessayer
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Partager le coffre
|
shareVault.title=Partager le coffre
|
||||||
shareVault.message=Vous aimeriez partager votre coffre avec d'autres personnes ?
|
shareVault.message=Vous aimeriez partager votre coffre avec d'autres personnes ?
|
||||||
|
|||||||
@@ -132,4 +132,6 @@ lock.forced.retryBtn=Tentar de novo
|
|||||||
|
|
||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -491,4 +491,6 @@ updateReminder.notNow=לא עכשיו
|
|||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
dokanySupportEnd.preferencesBtn=פתח העדפות
|
dokanySupportEnd.preferencesBtn=פתח העדפות
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -314,4 +314,6 @@ quit.forced.message=कुछ वॉल्ट्स लॉक नहीं ह
|
|||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
dokanySupportEnd.preferencesBtn=प्राथमिकताएँ खोलें
|
dokanySupportEnd.preferencesBtn=प्राथमिकताएँ खोलें
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -381,4 +381,6 @@ quit.lockAndQuitBtn=Zaključaj i napusti
|
|||||||
|
|
||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -551,6 +551,8 @@ dokanySupportEnd.message=Vége a Dokany támogatásának
|
|||||||
dokanySupportEnd.description=A Cryptomator nem támogatja tovább a Dokany kötettípust. A beállítások az alapértelmezett kötettípus használatára lettek állítva. Az alapértelmezett típust a beállítások közt tekintheted meg.
|
dokanySupportEnd.description=A Cryptomator nem támogatja tovább a Dokany kötettípust. A beállítások az alapértelmezett kötettípus használatára lettek állítva. Az alapértelmezett típust a beállítások közt tekintheted meg.
|
||||||
dokanySupportEnd.preferencesBtn=Beállítások megnyitása
|
dokanySupportEnd.preferencesBtn=Beállítások megnyitása
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Széf megosztása
|
shareVault.title=Széf megosztása
|
||||||
shareVault.message=Szeretné megosztani a széfét másokkal?
|
shareVault.message=Szeretné megosztani a széfét másokkal?
|
||||||
|
|||||||
@@ -551,6 +551,8 @@ dokanySupportEnd.message=Dukungan untuk Dokany telah berakhir
|
|||||||
dokanySupportEnd.description=Volume berjenis Dokany sudah tidak didukung oleh Cryptomator. Konfigurasi Anda telah disesuai kan dengan tipe volume yang saat ini. Anda dapat melihat tipe baku dipreferensi.
|
dokanySupportEnd.description=Volume berjenis Dokany sudah tidak didukung oleh Cryptomator. Konfigurasi Anda telah disesuai kan dengan tipe volume yang saat ini. Anda dapat melihat tipe baku dipreferensi.
|
||||||
dokanySupportEnd.preferencesBtn=Buka Preferensi
|
dokanySupportEnd.preferencesBtn=Buka Preferensi
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Bagikan Vault
|
shareVault.title=Bagikan Vault
|
||||||
shareVault.message=Apakah Anda ingin berbagi vault dengan orang lain?
|
shareVault.message=Apakah Anda ingin berbagi vault dengan orang lain?
|
||||||
|
|||||||
@@ -552,6 +552,12 @@ dokanySupportEnd.message=Supporto terminato per Dokany
|
|||||||
dokanySupportEnd.description=Il tipo di volume Dokany non è più supportato da Cryptomator. Le impostazioni sono state aggiornate per utilizzare il tipo di volume ora predefinito. È possibile visualizzare il tipo predefinito nelle preferenze.
|
dokanySupportEnd.description=Il tipo di volume Dokany non è più supportato da Cryptomator. Le impostazioni sono state aggiornate per utilizzare il tipo di volume ora predefinito. È possibile visualizzare il tipo predefinito nelle preferenze.
|
||||||
dokanySupportEnd.preferencesBtn=Apri Preferenze
|
dokanySupportEnd.preferencesBtn=Apri Preferenze
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
retryIfReadonly.title=Accesso Limitato Cassaforte
|
||||||
|
retryIfReadonly.message=Accesso negato in scrittura alla cartella della cassaforte
|
||||||
|
retryIfReadonly.description=Cryptomator non può scrivere nella cartella della cassaforte. Puoi cambiare l'impostazione "sola lettura" della cassaforte e riprovare. Questa opzione può essere impostata nelle opzioni della cassaforte.
|
||||||
|
retryIfReadonly.retry=Modifica e riprova
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Condividi cassaforte
|
shareVault.title=Condividi cassaforte
|
||||||
shareVault.message=Vuoi condividere la tua cassaforte con altri?
|
shareVault.message=Vuoi condividere la tua cassaforte con altri?
|
||||||
|
|||||||
@@ -540,6 +540,8 @@ dokanySupportEnd.message=Dokany のサポート終了
|
|||||||
dokanySupportEnd.description=ボリュームタイプ
|
dokanySupportEnd.description=ボリュームタイプ
|
||||||
dokanySupportEnd.preferencesBtn=環境設定を開く
|
dokanySupportEnd.preferencesBtn=環境設定を開く
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=保管庫を共有する
|
shareVault.title=保管庫を共有する
|
||||||
shareVault.message=保管庫を他の人と共有しますか?
|
shareVault.message=保管庫を他の人と共有しますか?
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ generic.button.apply=적용
|
|||||||
generic.button.back=이전
|
generic.button.back=이전
|
||||||
generic.button.cancel=취소
|
generic.button.cancel=취소
|
||||||
generic.button.change=변경
|
generic.button.change=변경
|
||||||
generic.button.choose=선택
|
generic.button.choose=선택…
|
||||||
generic.button.close=닫기
|
generic.button.close=닫기
|
||||||
generic.button.copy=복사
|
generic.button.copy=복사
|
||||||
generic.button.copied=복사됨!
|
generic.button.copied=복사됨!
|
||||||
@@ -16,16 +16,16 @@ generic.button.print=인쇄
|
|||||||
generic.button.remove=제거
|
generic.button.remove=제거
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
error.message=오류가 발생했습니다
|
error.message=오류 발생
|
||||||
error.description=예상치 못한 에러가 발생했습니다. 온라인에 에러를 검색해서 해결하십시오. 만약 신고된 적이 없는 에러일 경우, 새로 신고해도 좋습니다.
|
error.description=예상치 못한 에러가 발생했습니다. 해결법을 검색하십시오. 만약 보고된 적이 없는 에러일 경우, 새로 신고해도 좋습니다.
|
||||||
error.hyperlink.lookup=에러 검색하기
|
error.hyperlink.lookup=에러 검색하기
|
||||||
error.hyperlink.report=에러 보고하기
|
error.hyperlink.report=에러 보고하기
|
||||||
error.technicalDetails=상세 정보:
|
error.technicalDetails=상세 정보:
|
||||||
error.existingSolutionDescription=Cryptomator에 알수 없는 문제가 발생했습니다. 하지만 이 오류에 대한 기존 해결책이 있습니다. 다음 링크를 살펴보시기 바랍니다.
|
error.existingSolutionDescription=Cryptomator에 알 수 없는 문제가 발생했습니다. 하지만 이 오류에 대한 기존 해결법이 있습니다. 다음 링크를 살펴보십시오.
|
||||||
error.hyperlink.solution=솔루션 찾기
|
error.hyperlink.solution=해결법 찾기
|
||||||
error.lookupPermissionMessage=Cryptomator는 온라인에서 이 문제에 대한 해결책을 찾아볼 수 있습니다. 그러면 귀하의 IP 주소가 문제 데이터베이스로 전송됩니다.
|
error.lookupPermissionMessage=Cryptomator는 온라인에서 이 문제에 대한 해결책을 찾아볼 수 있습니다. 그러면 귀하의 IP 주소가 문제 데이터베이스로 전송됩니다.
|
||||||
error.dismiss=무시
|
error.dismiss=무시
|
||||||
error.lookUpSolution=솔루션 찾기
|
error.lookUpSolution=해결법 찾기
|
||||||
|
|
||||||
# Defaults
|
# Defaults
|
||||||
defaults.vault.vaultName=Vault
|
defaults.vault.vaultName=Vault
|
||||||
@@ -42,7 +42,7 @@ traymenu.vault.reveal=표시
|
|||||||
# Add Vault Wizard
|
# Add Vault Wizard
|
||||||
addvaultwizard.title=Vault 추가
|
addvaultwizard.title=Vault 추가
|
||||||
## New
|
## New
|
||||||
addvaultwizard.new.title=새로운 금고 추가
|
addvaultwizard.new.title=새로운 Vault 추가
|
||||||
### Name
|
### Name
|
||||||
addvaultwizard.new.nameInstruction=새 Vault의 이름을 입력하십시오
|
addvaultwizard.new.nameInstruction=새 Vault의 이름을 입력하십시오
|
||||||
addvaultwizard.new.namePrompt=Vault 이름
|
addvaultwizard.new.namePrompt=Vault 이름
|
||||||
@@ -52,7 +52,7 @@ addvaultwizard.new.locationLoading=기본 클라우드 저장소 디렉터리에
|
|||||||
addvaultwizard.new.locationLabel=저장 위치
|
addvaultwizard.new.locationLabel=저장 위치
|
||||||
addvaultwizard.new.locationPrompt=…
|
addvaultwizard.new.locationPrompt=…
|
||||||
addvaultwizard.new.directoryPickerLabel=사용자 지정 위치
|
addvaultwizard.new.directoryPickerLabel=사용자 지정 위치
|
||||||
addvaultwizard.new.directoryPickerButton=선택
|
addvaultwizard.new.directoryPickerButton=선택…
|
||||||
addvaultwizard.new.directoryPickerTitle=디렉터리 선택
|
addvaultwizard.new.directoryPickerTitle=디렉터리 선택
|
||||||
addvaultwizard.new.fileAlreadyExists=Vault 내에 이미 존재하는 파일 또는 디렉터리 이름입니다.
|
addvaultwizard.new.fileAlreadyExists=Vault 내에 이미 존재하는 파일 또는 디렉터리 이름입니다.
|
||||||
addvaultwizard.new.locationDoesNotExist=지정된 디렉터리가 존재하지 않거나 접근 할 수 없습니다.
|
addvaultwizard.new.locationDoesNotExist=지정된 디렉터리가 존재하지 않거나 접근 할 수 없습니다.
|
||||||
@@ -65,7 +65,7 @@ addvaultwizard.new.validCharacters.chars=문자 (예시: a, ж or 수)
|
|||||||
addvaultwizard.new.validCharacters.numbers=숫자
|
addvaultwizard.new.validCharacters.numbers=숫자
|
||||||
addvaultwizard.new.validCharacters.dashes=대시 (%s) 또는 언더바 (%s)
|
addvaultwizard.new.validCharacters.dashes=대시 (%s) 또는 언더바 (%s)
|
||||||
### Expert Settings
|
### Expert Settings
|
||||||
addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox=전문가용 설정 활성화
|
addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox=전문가 설정 활성화
|
||||||
addvaultwizard.new.expertSettings.shorteningThreshold.invalid=36과 220 사이 숫자를 입력해주세요 (기본값: 220)
|
addvaultwizard.new.expertSettings.shorteningThreshold.invalid=36과 220 사이 숫자를 입력해주세요 (기본값: 220)
|
||||||
addvaultwizard.new.expertSettings.shorteningThreshold.tooltip=더 자세한 정보는 관련 문서에서 볼 수 있습니다.
|
addvaultwizard.new.expertSettings.shorteningThreshold.tooltip=더 자세한 정보는 관련 문서에서 볼 수 있습니다.
|
||||||
addvaultwizard.new.expertSettings.shorteningThreshold.title=암호화된 파일명의 최대 길이
|
addvaultwizard.new.expertSettings.shorteningThreshold.title=암호화된 파일명의 최대 길이
|
||||||
@@ -73,29 +73,29 @@ addvaultwizard.new.expertSettings.shorteningThreshold.valid=유효
|
|||||||
### Password
|
### Password
|
||||||
addvaultwizard.new.createVaultBtn=Vault 생성
|
addvaultwizard.new.createVaultBtn=Vault 생성
|
||||||
addvaultwizard.new.generateRecoveryKeyChoice=비밀번호가 없으면 데이터에 접근할 수 없습니다. 비밀번호를 잊었을 때를 대비한 복구 키를 원하십니까?
|
addvaultwizard.new.generateRecoveryKeyChoice=비밀번호가 없으면 데이터에 접근할 수 없습니다. 비밀번호를 잊었을 때를 대비한 복구 키를 원하십니까?
|
||||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=네, 보안보다 비밀번호를 잊어버리는 것이 더 걱정됩니다
|
addvaultwizard.new.generateRecoveryKeyChoice.yes=네, 보안보다 비밀번호를 잊어버리는 것이 더 걱정됩니다.
|
||||||
addvaultwizard.new.generateRecoveryKeyChoice.no=아니요, 나는 비밀번호를 잊지 않을겁니다.
|
addvaultwizard.new.generateRecoveryKeyChoice.no=아니요, 나는 비밀번호를 잊지 않을겁니다.
|
||||||
### Information
|
### Information
|
||||||
addvault.new.readme.storageLocation.fileName=IMPORTANT.rtf
|
addvault.new.readme.storageLocation.fileName=IMPORTANT.rtf
|
||||||
addvault.new.readme.storageLocation.1=⚠️ VAULT 파일 ⚠️
|
addvault.new.readme.storageLocation.1=⚠️ VAULT 파일 ⚠️
|
||||||
addvault.new.readme.storageLocation.2=해당 디렉토리는 당신의 Vault 저장 위치 입니다.
|
addvault.new.readme.storageLocation.2=해당 디렉토리는 당신의 Vault 저장 위치입니다.
|
||||||
addvault.new.readme.storageLocation.3=금지사항
|
addvault.new.readme.storageLocation.3=금지 사항
|
||||||
addvault.new.readme.storageLocation.4=• 이 디렉터리를 포함한 어떤 파일도 다른 파일로 교체하거나
|
addvault.new.readme.storageLocation.4=• 이 디렉터리를 포함한 어떤 파일도 다른 파일로 교체하거나
|
||||||
addvault.new.readme.storageLocation.5=• 암호화를 위한 파일을 이 디렉터리에 붙여넣지 마십시요.
|
addvault.new.readme.storageLocation.5=• 암호화하고자 하는 파일을 이 디렉터리에 붙여넣지 마십시오.
|
||||||
addvault.new.readme.storageLocation.6=파일을 암호화하고 Vault 의 내용을 보려면 다음을 수행하십시오.
|
addvault.new.readme.storageLocation.6=파일을 암호화하고 Vault 의 내용을 보려면 다음을 수행하십시오.
|
||||||
addvault.new.readme.storageLocation.7=1. 이 Vault를 Cryptomator에 추가하십시요.
|
addvault.new.readme.storageLocation.7=1. 이 Vault를 Cryptomator에 추가하십시오.
|
||||||
addvault.new.readme.storageLocation.8=2. Cryptomator에서 Vault 잠금을 해제하십시요.
|
addvault.new.readme.storageLocation.8=2. Cryptomator에서 Vault 잠금을 해제하십시오.
|
||||||
addvault.new.readme.storageLocation.9=3. "표시" 버튼을 클릭하여 Vault에 접근하십시요.
|
addvault.new.readme.storageLocation.9=3. "표시" 버튼을 클릭하여 Vault에 접근하십시오.
|
||||||
addvault.new.readme.storageLocation.10=만일 도움이 필요하신 경우, 다음의 문서를 참조하십시요: %s
|
addvault.new.readme.storageLocation.10=만일 도움이 필요하신 경우, 다음의 문서를 참조하십시오: %s
|
||||||
addvault.new.readme.accessLocation.fileName=WELCOME.rtf
|
addvault.new.readme.accessLocation.fileName=WELCOME.rtf
|
||||||
addvault.new.readme.accessLocation.1=🔐️ 암호화 된 볼륨 🔐️
|
addvault.new.readme.accessLocation.1=🔐️ 암호화 된 볼륨 🔐️
|
||||||
addvault.new.readme.accessLocation.2=이것은 당신의 Vault 접근 위치입니다.
|
addvault.new.readme.accessLocation.2=이것은 당신의 Vault 접근 위치입니다.
|
||||||
addvault.new.readme.accessLocation.3=이 볼륨에 추가된 모든 파일은 Cryptomator로 암호화됩니다. 다른 드라이브/폴더처럼 작업할 수 있습니다. 볼륨의 내용은 복호화 된 것 처럼 보여지지만, 모든 파일은 항상 암호화되어 하드디스크에 저장됩니다.
|
addvault.new.readme.accessLocation.3=이 볼륨에 추가된 모든 파일은 Cryptomator로 암호화됩니다. 다른 드라이브/폴더처럼 작업할 수 있습니다. 볼륨의 내용은 복호화 된 것처럼 보이지만, 모든 파일은 항상 암호화되어 하드디스크에 저장됩니다.
|
||||||
addvault.new.readme.accessLocation.4=이 파일은 지우셔도 무방합니다.
|
addvault.new.readme.accessLocation.4=이 파일은 지우셔도 무방합니다.
|
||||||
## Existing
|
## Existing
|
||||||
addvaultwizard.existing.title=기존 금고 추가
|
addvaultwizard.existing.title=기존 금고 추가
|
||||||
addvaultwizard.existing.instruction=이미 존재하는 vault 폴더 내에서 "vault.cryptomator" 파일을 선택하세요. 만약 "masterkey.cryptomator"만 있다면 그걸 대신 선택하세요.
|
addvaultwizard.existing.instruction=이미 존재하는 vault 폴더 내에서 "vault.cryptomator" 파일을 선택하세요. 만약 "masterkey.cryptomator"만 있다면 그걸 대신 선택하세요.
|
||||||
addvaultwizard.existing.chooseBtn=선택
|
addvaultwizard.existing.chooseBtn=선택…
|
||||||
addvaultwizard.existing.filePickerTitle=Vault 파일 선택
|
addvaultwizard.existing.filePickerTitle=Vault 파일 선택
|
||||||
addvaultwizard.existing.filePickerMimeDesc=Cryptomator Vault
|
addvaultwizard.existing.filePickerMimeDesc=Cryptomator Vault
|
||||||
## Success
|
## Success
|
||||||
@@ -105,37 +105,37 @@ addvaultwizard.success.unlockNow=지금 잠금해제
|
|||||||
# Remove Vault
|
# Remove Vault
|
||||||
removeVault.title=Vault 제거
|
removeVault.title=Vault 제거
|
||||||
removeVault.message=Vault를 삭제하시겠습니까?
|
removeVault.message=Vault를 삭제하시겠습니까?
|
||||||
removeVault.description=이 행위는 단지 Cryptomator에서 이 Vault를 잊게합니다. 나중에 다시 추가 할 수 있습니다. 암호화된 파일은 하드디스크에서 삭제되지 않을 것입니다.
|
removeVault.description=이 행위는 Cryptomator에서만 이 Vault를 지웁니다. 나중에 다시 추가할 수 있습니다. 암호화된 파일은 하드디스크에서 삭제되지 않습니다.
|
||||||
|
|
||||||
# Change Password
|
# Change Password
|
||||||
changepassword.title=비밀번호 변경
|
changepassword.title=비밀번호 변경
|
||||||
changepassword.enterOldPassword="%s"의 비밀번호를 입력하여 주십시요.
|
changepassword.enterOldPassword="%s"의 비밀번호를 입력하여 주십시요.
|
||||||
changepassword.finalConfirmation=비밀번호를 잊어버리면, 데이터에 접근할 수 없음을 이해했습니다.
|
changepassword.finalConfirmation=비밀번호를 잊어버리면, 데이터에 접근할 수 없다는 것을 이해했습니다.
|
||||||
|
|
||||||
# Forget Password
|
# Forget Password
|
||||||
forgetPassword.title=비밀번호 분실
|
forgetPassword.title=비밀번호 삭제
|
||||||
forgetPassword.message=저장된 비밀번호를 삭제할까요?
|
forgetPassword.message=저장된 비밀번호를 삭제할까요?
|
||||||
forgetPassword.description=시스템 키체인에서 이 Vault의 저장된 비밀번호가 삭제될 것입니다.
|
forgetPassword.description=시스템 키체인에서 이 Vault의 저장된 비밀번호가 삭제될 것입니다.
|
||||||
forgetPassword.confirmBtn=비밀번호 분실
|
forgetPassword.confirmBtn=비밀번호 삭제
|
||||||
|
|
||||||
# Unlock
|
# Unlock
|
||||||
unlock.title="%s" 잠금 해제
|
unlock.title="%s" 잠금 해제
|
||||||
unlock.passwordPrompt="%s"의 비밀번호를 입력하십시요.
|
unlock.passwordPrompt="%s"의 비밀번호를 입력하십시오.
|
||||||
unlock.savePassword=비밀번호 기억
|
unlock.savePassword=비밀번호 기억
|
||||||
unlock.unlockBtn=잠금해제
|
unlock.unlockBtn=잠금해제
|
||||||
## Select
|
## Select
|
||||||
unlock.chooseMasterkey.message=마스터키 파일을 찾을 수 없습니다
|
unlock.chooseMasterkey.message=마스터키 파일을 찾을 수 없습니다
|
||||||
unlock.chooseMasterkey.description=추정되는 위치에서 이 Vault의 마스터 키를 찾지 못했습니다. 마스터 키 위치를 수동으로 선택하여 주십시요.
|
unlock.chooseMasterkey.description=이 Vault의 Masterkey를 찾지 못했습니다. 마스터 키 위치를 수동으로 선택하여 주십시오.
|
||||||
unlock.chooseMasterkey.filePickerTitle=마스터키 파일 선택
|
unlock.chooseMasterkey.filePickerTitle=Masterkey 파일 선택
|
||||||
unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator 마스터키
|
unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator Masterkey
|
||||||
## Success
|
## Success
|
||||||
unlock.success.message=잠금 해제 성공
|
unlock.success.message=잠금 해제 성공
|
||||||
unlock.success.description="%s"이(가) 성공적으로 잠금 해제되었습니다. 이제 이 Vault를 마운트 지점으로 접근할 수 있습니다.
|
unlock.success.description="%s"이(가) 성공적으로 잠금 해제되었습니다. 이제 이 Vault를 마운트 지점으로 접근할 수 있습니다.
|
||||||
unlock.success.rememberChoice=선택 기억함, 다시 묻지 않음
|
unlock.success.rememberChoice=선택 기억하기, 다시 묻지 않음
|
||||||
unlock.success.revealBtn=드라이브 표시
|
unlock.success.revealBtn=드라이브 표시
|
||||||
## Failure
|
## Failure
|
||||||
unlock.error.customPath.message=Vault을 사용자 정의 경로에 마운트할 수 없습니다.
|
unlock.error.customPath.message=Vault를 사용자 정의 경로에 마운트할 수 없습니다.
|
||||||
unlock.error.customPath.description.notSupported=사용자 지정 경로를 계속 사용하려면 기본 설정으로 이동하여 이를 지원하는 볼륨 유형을 선택하세요. 그렇지 않으면 볼트 옵션으로 이동하여 지원되는 마운트 지점을 선택하십시오.
|
unlock.error.customPath.description.notSupported=사용자 지정 경로를 계속 사용하려면 설정으로 이동하여 이를 지원하는 볼륨 유형을 선택하십시오. 그렇지 않으면 볼트 옵션으로 이동하여 지원되는 마운트 지점을 선택하십시오.
|
||||||
unlock.error.customPath.description.notExists=사용자 정의 마운트 경로가 존재하지 않습니다. 로컬 파일 시스템에서 생성하거나 볼트 옵션에서 변경하세요.
|
unlock.error.customPath.description.notExists=사용자 정의 마운트 경로가 존재하지 않습니다. 로컬 파일 시스템에서 생성하거나 볼트 옵션에서 변경하세요.
|
||||||
unlock.error.customPath.description.inUse=드라이브 문자 또는 사용자 정의 마운트 경로 "%s"가 이미 사용 중입니다.
|
unlock.error.customPath.description.inUse=드라이브 문자 또는 사용자 정의 마운트 경로 "%s"가 이미 사용 중입니다.
|
||||||
unlock.error.customPath.description.hideawayNotDir=잠금 해제에 사용된 임시 숨김 파일 "%3$s"을 제거할 수 없습니다. 파일을 확인한 후 수동으로 삭제해 주세요.
|
unlock.error.customPath.description.hideawayNotDir=잠금 해제에 사용된 임시 숨김 파일 "%3$s"을 제거할 수 없습니다. 파일을 확인한 후 수동으로 삭제해 주세요.
|
||||||
@@ -143,11 +143,11 @@ unlock.error.customPath.description.couldNotBeCleaned=Vault를 "%s" 경로에
|
|||||||
unlock.error.customPath.description.notEmptyDir=사용자 정의 마운트 경로 "%s"은 빈 폴더가 아닙니다. 빈 폴더를 선택하고 다시 시도하세요.
|
unlock.error.customPath.description.notEmptyDir=사용자 정의 마운트 경로 "%s"은 빈 폴더가 아닙니다. 빈 폴더를 선택하고 다시 시도하세요.
|
||||||
unlock.error.customPath.description.generic=이 볼트에 대한 사용자 정의 마운트 경로를 선택했지만 다음 메시지와 함께 해당 경로를 사용하지 못했습니다: %2$s
|
unlock.error.customPath.description.generic=이 볼트에 대한 사용자 정의 마운트 경로를 선택했지만 다음 메시지와 함께 해당 경로를 사용하지 못했습니다: %2$s
|
||||||
unlock.error.restartRequired.message=Vault을 잠금 해제할 수 없습니다.
|
unlock.error.restartRequired.message=Vault을 잠금 해제할 수 없습니다.
|
||||||
unlock.error.restartRequired.description=볼트 옵션에서 볼륨 유형을 변경하거나 Cryptomator를 다시 시작하세요.
|
unlock.error.restartRequired.description=볼트 옵션에서 볼륨 유형을 변경하거나 Cryptomator를 다시 시작하십시오.
|
||||||
unlock.error.title="%s" 잠금 해제 실패
|
unlock.error.title="%s" 잠금 해제 실패
|
||||||
## Hub
|
## Hub
|
||||||
hub.noKeychain.message=장치 키에 액세스할 수 없습니다
|
hub.noKeychain.message=장치 키에 액세스할 수 없습니다
|
||||||
hub.noKeychain.description=허브 저장소를 잠금 해제하려면 키체인을 사용하여 보호되는 장치 키가 필요합니다. 계속하려면 "%s"을 활성화하고 기본 설정에서 키체인을 선택하세요.
|
hub.noKeychain.description=허브 저장소를 잠금 해제하려면 키체인을 사용하여 보호되는 장치 키가 필요합니다. 계속하려면 "%s"을 활성화하고 기본 설정에서 키체인을 선택하십시오.
|
||||||
hub.noKeychain.openBtn=설정 열기
|
hub.noKeychain.openBtn=설정 열기
|
||||||
### Waiting
|
### Waiting
|
||||||
hub.auth.message=인증 대기중…
|
hub.auth.message=인증 대기중…
|
||||||
@@ -273,7 +273,7 @@ health.result.fixStateFilter.fixing=문제 해결중…
|
|||||||
health.result.fixStateFilter.fixed=문제 해결됨
|
health.result.fixStateFilter.fixed=문제 해결됨
|
||||||
health.result.fixStateFilter.fixFailed=문제 해결 실패
|
health.result.fixStateFilter.fixFailed=문제 해결 실패
|
||||||
## Fix Application
|
## Fix Application
|
||||||
health.fix.fixBtn=문제해결
|
health.fix.fixBtn=문제 해결
|
||||||
health.fix.successTip=문제 해결이 성공적으로 완료되었습니다
|
health.fix.successTip=문제 해결이 성공적으로 완료되었습니다
|
||||||
health.fix.failTip=문제 해결 실패, 상세 정보는 로그를 참조하십시요.
|
health.fix.failTip=문제 해결 실패, 상세 정보는 로그를 참조하십시요.
|
||||||
|
|
||||||
@@ -290,7 +290,7 @@ preferences.general.keychainBackend=다음 경로에 비밀번호 저장
|
|||||||
preferences.general.quickAccessService=열린 Vault를 빠른 접근 위치에 추가하기
|
preferences.general.quickAccessService=열린 Vault를 빠른 접근 위치에 추가하기
|
||||||
## Interface
|
## Interface
|
||||||
preferences.interface=인터페이스
|
preferences.interface=인터페이스
|
||||||
preferences.interface.theme=테마설정
|
preferences.interface.theme=테마
|
||||||
preferences.interface.theme.automatic=자동
|
preferences.interface.theme.automatic=자동
|
||||||
preferences.interface.theme.dark=어둡게
|
preferences.interface.theme.dark=어둡게
|
||||||
preferences.interface.theme.light=밝게
|
preferences.interface.theme.light=밝게
|
||||||
@@ -331,7 +331,7 @@ preferences.updates.upToDate=현재 최신 버전의 Cryptomator를 사용하고
|
|||||||
|
|
||||||
## Contribution
|
## Contribution
|
||||||
preferences.contribute=후원하기
|
preferences.contribute=후원하기
|
||||||
preferences.contribute.registeredFor=%s (으)로 후원자 인증 등록됨
|
preferences.contribute.registeredFor=%s(으)로 후원자 인증 등록됨
|
||||||
preferences.contribute.noCertificate=Cryptomator를 후원하시고 후원자 인증을 받으십시요. 라이선스 키와 비슷하지만 무료 소프트웨어를 사용하는 멋진 사람들을 위한 것입니다. ;-)
|
preferences.contribute.noCertificate=Cryptomator를 후원하시고 후원자 인증을 받으십시요. 라이선스 키와 비슷하지만 무료 소프트웨어를 사용하는 멋진 사람들을 위한 것입니다. ;-)
|
||||||
preferences.contribute.getCertificate=아직 후원자 인증이 없으신가요? 어떻게 얻는지 배울 수 있습니다.
|
preferences.contribute.getCertificate=아직 후원자 인증이 없으신가요? 어떻게 얻는지 배울 수 있습니다.
|
||||||
preferences.contribute.promptText=후원자 인증코드를 여기에 붙여넣기
|
preferences.contribute.promptText=후원자 인증코드를 여기에 붙여넣기
|
||||||
@@ -406,7 +406,7 @@ main.vaultDetail.unlockBtn=잠금 해제...
|
|||||||
main.vaultDetail.unlockNowBtn=지금 잠금 해제
|
main.vaultDetail.unlockNowBtn=지금 잠금 해제
|
||||||
main.vaultDetail.optionsBtn=Vault 옵션
|
main.vaultDetail.optionsBtn=Vault 옵션
|
||||||
main.vaultDetail.passwordSavedInKeychain=비밀번호 저장됨
|
main.vaultDetail.passwordSavedInKeychain=비밀번호 저장됨
|
||||||
main.vaultDetail.share=공유하기
|
main.vaultDetail.share=공유하기…
|
||||||
### Unlocked
|
### Unlocked
|
||||||
main.vaultDetail.unlockedStatus=잠금 해제됨
|
main.vaultDetail.unlockedStatus=잠금 해제됨
|
||||||
main.vaultDetail.accessLocation=이 Vault의 내용은 다음의 경로에서 접근할 수 있습니다:
|
main.vaultDetail.accessLocation=이 Vault의 내용은 다음의 경로에서 접근할 수 있습니다:
|
||||||
@@ -450,7 +450,7 @@ wrongFileAlert.link=더 많은 지원을 위해, 다음을 방문하십시오
|
|||||||
## General
|
## General
|
||||||
vaultOptions.general=일반
|
vaultOptions.general=일반
|
||||||
vaultOptions.general.vaultName=Vault 이름
|
vaultOptions.general.vaultName=Vault 이름
|
||||||
vaultOptions.general.autoLock.lockAfterTimePart1=다음 시간동안 유휴상태 시 잠금 :
|
vaultOptions.general.autoLock.lockAfterTimePart1=다음 시간 동안 유휴상태 시 잠그기
|
||||||
vaultOptions.general.autoLock.lockAfterTimePart2=분
|
vaultOptions.general.autoLock.lockAfterTimePart2=분
|
||||||
vaultOptions.general.unlockAfterStartup=Cryptomator를 시작할 때 Vault 잠금 해제
|
vaultOptions.general.unlockAfterStartup=Cryptomator를 시작할 때 Vault 잠금 해제
|
||||||
vaultOptions.general.actionAfterUnlock=성공적으로 잠금해제 후
|
vaultOptions.general.actionAfterUnlock=성공적으로 잠금해제 후
|
||||||
@@ -552,10 +552,16 @@ dokanySupportEnd.message=Dokany에 대한 지원 중단
|
|||||||
dokanySupportEnd.description=Cryptomator에서 Dokany 볼륨 형식은 더이상 지원되지 않습니다. 기본 볼륨 형식을 사용하도록 설정이 조정되었으며, 설정에서 기본 형식을 확인할 수 있습니다.
|
dokanySupportEnd.description=Cryptomator에서 Dokany 볼륨 형식은 더이상 지원되지 않습니다. 기본 볼륨 형식을 사용하도록 설정이 조정되었으며, 설정에서 기본 형식을 확인할 수 있습니다.
|
||||||
dokanySupportEnd.preferencesBtn=설정 열기
|
dokanySupportEnd.preferencesBtn=설정 열기
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
retryIfReadonly.title=Vault 접근 제한됨
|
||||||
|
retryIfReadonly.message=Vault 디렉터리에 쓰기 권한 없음
|
||||||
|
retryIfReadonly.description=Cryptomator가 Vault 디렉터리에 쓸 수 없습니다. Vault를 읽기 전용으로 설정하고 다시 시도할 수 있습니다. 이 옵션은 Vault 옵션에서 바꿀 수 있습니다.
|
||||||
|
retryIfReadonly.retry=바꾸고 다시 시도하기
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Vault 공유
|
shareVault.title=Vault 공유
|
||||||
shareVault.message=Vault를 다른 사람과 공유하려 하십니까?
|
shareVault.message=Vault를 다른 사람과 공유하려 하십니까?
|
||||||
shareVault.description=Vault를 타인과 공유할 때에는 항상 주의하십시오. 간단히 이 단계들을 따르세요.
|
shareVault.description=Vault를 타인과 공유할 때에는 항상 주의하십시오. 간단히 이 단계들을 따르십시오.
|
||||||
shareVault.instruction.1=1. 암호화된 Vault 폴더를 클라우드 스토리지를 통해 공유하십시오.
|
shareVault.instruction.1=1. 암호화된 Vault 폴더를 클라우드 스토리지를 통해 공유하십시오.
|
||||||
shareVault.instruction.2=2. Vault의 비밀번호를 안전한 방식으로 전달하십시오.
|
shareVault.instruction.2=2. Vault의 비밀번호를 안전한 방식으로 전달하십시오.
|
||||||
shareVault.remarkBestPractices=문서에 있는 권장사항을 통해 더 많은 정보를 확인하십시오.
|
shareVault.remarkBestPractices=문서에 있는 권장사항을 통해 더 많은 정보를 확인하십시오.
|
||||||
|
|||||||
@@ -280,4 +280,6 @@ quit.lockAndQuitBtn=Aizslēgt un aizvērt
|
|||||||
|
|
||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -164,4 +164,6 @@ vaultOptions.mount.mountPoint.directoryPickerButton=Избор…
|
|||||||
|
|
||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -118,4 +118,6 @@
|
|||||||
|
|
||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -543,6 +543,8 @@ dokanySupportEnd.message=Støtte for Dokany er avsluttet
|
|||||||
dokanySupportEnd.description=Volumtypen Dokany støttes ikke lenger av Cryptomator. Innstillingene dine er justert for å bruke standard volumtype nå. Du kan se standardtypen i innstillingene.
|
dokanySupportEnd.description=Volumtypen Dokany støttes ikke lenger av Cryptomator. Innstillingene dine er justert for å bruke standard volumtype nå. Du kan se standardtypen i innstillingene.
|
||||||
dokanySupportEnd.preferencesBtn=Åpne innstillinger
|
dokanySupportEnd.preferencesBtn=Åpne innstillinger
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Del hvelv
|
shareVault.title=Del hvelv
|
||||||
shareVault.message=Vil du dele hvelvet ditt med andre?
|
shareVault.message=Vil du dele hvelvet ditt med andre?
|
||||||
|
|||||||
@@ -552,6 +552,12 @@ dokanySupportEnd.message=Ondersteuning beëindigd voor Dokany
|
|||||||
dokanySupportEnd.description=Het volumetype Dokany wordt niet langer ondersteund door Cryptomator. Uw instellingen zijn aangepast om nu het standaard volumetype te gebruiken. U kunt het standaardtype bekijken in de voorkeuren.
|
dokanySupportEnd.description=Het volumetype Dokany wordt niet langer ondersteund door Cryptomator. Uw instellingen zijn aangepast om nu het standaard volumetype te gebruiken. U kunt het standaardtype bekijken in de voorkeuren.
|
||||||
dokanySupportEnd.preferencesBtn=Open Voorkeuren
|
dokanySupportEnd.preferencesBtn=Open Voorkeuren
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
retryIfReadonly.title=Beperkte toegang tot kluis
|
||||||
|
retryIfReadonly.message=Geen schrijftoegang tot de kluis map
|
||||||
|
retryIfReadonly.description=Cryptomator kan niet naar de kluis map schrijven. U kunt de kluis veranderen om alleen-lezen te zijn en het opnieuw proberen. Deze optie kan worden uitgeschakeld in de kluis opties.
|
||||||
|
retryIfReadonly.retry=Wijzig en probeer opnieuw
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Kluis delen
|
shareVault.title=Kluis delen
|
||||||
shareVault.message=Wilt u uw kluis met anderen delen?
|
shareVault.message=Wilt u uw kluis met anderen delen?
|
||||||
|
|||||||
@@ -278,4 +278,6 @@ quit.lockAndQuitBtn=Lås og avslutt
|
|||||||
|
|
||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -118,4 +118,6 @@
|
|||||||
|
|
||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -475,6 +475,8 @@ dokanySupportEnd.title=ਬਰਤਰਫ਼ੀ ਨੋਟਿਸ
|
|||||||
dokanySupportEnd.message=Dokany ਲਈ ਸਹਿਯੋਗ ਖ਼ਤਮ
|
dokanySupportEnd.message=Dokany ਲਈ ਸਹਿਯੋਗ ਖ਼ਤਮ
|
||||||
dokanySupportEnd.preferencesBtn=ਪਸੰਦੀਦਾ ਖੋਲ੍ਹੋ
|
dokanySupportEnd.preferencesBtn=ਪਸੰਦੀਦਾ ਖੋਲ੍ਹੋ
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=ਵਾਲਟ ਨੂੰ ਸਾਂਝਾ ਕਰੋ
|
shareVault.title=ਵਾਲਟ ਨੂੰ ਸਾਂਝਾ ਕਰੋ
|
||||||
shareVault.message=ਕੀ ਤੁਸੀਂ ਆਪਣੇ ਵਾਲਟ ਨੂੰ ਹੋਰਾਂ ਨਾਲ ਸਾਂਝਾ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?
|
shareVault.message=ਕੀ ਤੁਸੀਂ ਆਪਣੇ ਵਾਲਟ ਨੂੰ ਹੋਰਾਂ ਨਾਲ ਸਾਂਝਾ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?
|
||||||
|
|||||||
@@ -551,6 +551,8 @@ dokanySupportEnd.message=Koniec wsparcia dla Dokany
|
|||||||
dokanySupportEnd.description=Typ udziału Dokany nie jest już wspierany przez Cryptomator. Twoje ustawienia zostały dostosowane do domyślnego typu udziału. Możesz sprawdzić jaki jest domyślny typ w ustawieniach.
|
dokanySupportEnd.description=Typ udziału Dokany nie jest już wspierany przez Cryptomator. Twoje ustawienia zostały dostosowane do domyślnego typu udziału. Możesz sprawdzić jaki jest domyślny typ w ustawieniach.
|
||||||
dokanySupportEnd.preferencesBtn=Otwórz ustawienia
|
dokanySupportEnd.preferencesBtn=Otwórz ustawienia
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Udostępnij sejf
|
shareVault.title=Udostępnij sejf
|
||||||
shareVault.message=Czy chcesz udostępnić swój sejf innym?
|
shareVault.message=Czy chcesz udostępnić swój sejf innym?
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ hub.registerFailed.description.generic=Ocorreu um erro no processo de registo. P
|
|||||||
hub.registerFailed.description.deviceAlreadyExists=Este dispositivo já está registado para um utilizador diferente. Tente alterar a conta de utilizador ou use um dispositivo diferente.
|
hub.registerFailed.description.deviceAlreadyExists=Este dispositivo já está registado para um utilizador diferente. Tente alterar a conta de utilizador ou use um dispositivo diferente.
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Acesso negado
|
hub.unauthorized.message=Acesso negado
|
||||||
hub.unauthorized.description=Não está autorizado a abrir este cofre. Contacte o proprietário do cofre para solicitar acesso.
|
hub.unauthorized.description=Não está autorizado a abrir este cofre. Contacte o proprietário do cofre para solicitar o acesso.
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Ação requerida
|
hub.requireAccountInit.message=Ação requerida
|
||||||
hub.requireAccountInit.description.0=Para continuar, conclua as etapas necessárias no seu
|
hub.requireAccountInit.description.0=Para continuar, conclua as etapas necessárias no seu
|
||||||
@@ -552,6 +552,12 @@ dokanySupportEnd.message=Suporte termina para o Dokany
|
|||||||
dokanySupportEnd.description=O tipo de volume Dokany não é mais suportado pelo Cryptomator. As configurações serão ajustadas para usar o tipo de volume padrão por agora. Pode ver o tipo padrão nas preferências.
|
dokanySupportEnd.description=O tipo de volume Dokany não é mais suportado pelo Cryptomator. As configurações serão ajustadas para usar o tipo de volume padrão por agora. Pode ver o tipo padrão nas preferências.
|
||||||
dokanySupportEnd.preferencesBtn=Abrir preferências
|
dokanySupportEnd.preferencesBtn=Abrir preferências
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
retryIfReadonly.title=Acesso ao Cofre Restrito
|
||||||
|
retryIfReadonly.message=Sem acesso de gravação ao diretório do cofre
|
||||||
|
retryIfReadonly.description=O Cryptomator não pode escrever no diretório do cofre. Pode alterar o cofre para ser apenas de leitura e tentar novamente. Esta opção pode ser desactivada nas opções do cofre.
|
||||||
|
retryIfReadonly.retry=Alterar e tentar de novo
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Partilhar cofre
|
shareVault.title=Partilhar cofre
|
||||||
shareVault.message=Quer partilhar o seu cofre com outros?
|
shareVault.message=Quer partilhar o seu cofre com outros?
|
||||||
|
|||||||
@@ -176,6 +176,7 @@ hub.registerFailed.description.generic=Um erro ocorreu no processo de registro.
|
|||||||
hub.registerFailed.description.deviceAlreadyExists=Este dispositivo já está registrado para um usuário diferente. Tente alterar a conta de usuário ou use um dispositivo diferente.
|
hub.registerFailed.description.deviceAlreadyExists=Este dispositivo já está registrado para um usuário diferente. Tente alterar a conta de usuário ou use um dispositivo diferente.
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Acesso negado
|
hub.unauthorized.message=Acesso negado
|
||||||
|
hub.unauthorized.description=Você não está autorizado a abrir este cofre. Entre em contato com o proprietário do cofre para solicitar acesso.
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Ação necessária
|
hub.requireAccountInit.message=Ação necessária
|
||||||
hub.requireAccountInit.description.0=Para prosseguir, por favor, complete os passos necessários
|
hub.requireAccountInit.description.0=Para prosseguir, por favor, complete os passos necessários
|
||||||
@@ -551,6 +552,12 @@ dokanySupportEnd.message=Fim do suporte para o Dokany
|
|||||||
dokanySupportEnd.description=O volume do tipo Dokany não é mais suportado pelo Cryptomator. Suas configurações serão ajustadas para usar o tipo de volume padrão agora. Você pode ver o tipo padrão nas preferências.
|
dokanySupportEnd.description=O volume do tipo Dokany não é mais suportado pelo Cryptomator. Suas configurações serão ajustadas para usar o tipo de volume padrão agora. Você pode ver o tipo padrão nas preferências.
|
||||||
dokanySupportEnd.preferencesBtn=Abrir Preferências
|
dokanySupportEnd.preferencesBtn=Abrir Preferências
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
retryIfReadonly.title=Acesso Restrito ao Cofre
|
||||||
|
retryIfReadonly.message=Sem acesso de gravação ao diretório do cofre
|
||||||
|
retryIfReadonly.description=O Cryptomator não pode escrever no diretório do cofre. Você pode alterar o cofre para somente leitura e tentar novamente. Esta opção pode ser desativada nas opções de cofre.
|
||||||
|
retryIfReadonly.retry=Alterar e Repetir
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Compartilhar Cofre
|
shareVault.title=Compartilhar Cofre
|
||||||
shareVault.message=Gostaria de compartilhar o seu cofre com outras pessoas?
|
shareVault.message=Gostaria de compartilhar o seu cofre com outras pessoas?
|
||||||
|
|||||||
@@ -548,6 +548,8 @@ dokanySupportEnd.message=Se termină suportul pentru Dokany
|
|||||||
dokanySupportEnd.description=Tipul de volum Dokany nu mai este suportat de Cryptomator. Setările dumneavoastra sunt ajustate pentru a utiliza acum tipul de volum implicit. Puteți vizualiza tipul implicit din preferințe.
|
dokanySupportEnd.description=Tipul de volum Dokany nu mai este suportat de Cryptomator. Setările dumneavoastra sunt ajustate pentru a utiliza acum tipul de volum implicit. Puteți vizualiza tipul implicit din preferințe.
|
||||||
dokanySupportEnd.preferencesBtn=Deschideţi preferinţele
|
dokanySupportEnd.preferencesBtn=Deschideţi preferinţele
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Partajare seif
|
shareVault.title=Partajare seif
|
||||||
shareVault.message=Doriți să vă împărtășiți seiful cu ceilalți?
|
shareVault.message=Doriți să vă împărtășiți seiful cu ceilalți?
|
||||||
|
|||||||
@@ -552,6 +552,12 @@ dokanySupportEnd.message=Заканчивается поддержка для Do
|
|||||||
dokanySupportEnd.description=Cryptomator больше не поддерживает тома Dokany. Параметры будут изменены на использование типа томов по умолчанию. Тип по умолчанию см. в настройках.
|
dokanySupportEnd.description=Cryptomator больше не поддерживает тома Dokany. Параметры будут изменены на использование типа томов по умолчанию. Тип по умолчанию см. в настройках.
|
||||||
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=Хотите поделиться хранилищем с другими?
|
||||||
|
|||||||
@@ -135,4 +135,6 @@ hub.registerSuccess.unlockBtn=අගුළුහරින්න
|
|||||||
|
|
||||||
#Dokany Support End
|
#Dokany Support End
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
|
|||||||
@@ -542,6 +542,8 @@ dokanySupportEnd.message=Ukončenie podpory pre Dokany
|
|||||||
dokanySupportEnd.description=Typ média Dokany už Cryptomator viac nepodporuje. Vaše nastavenia sú nastavené používať základný typ média. Základný typ môžete vidieť v nastaveniach.
|
dokanySupportEnd.description=Typ média Dokany už Cryptomator viac nepodporuje. Vaše nastavenia sú nastavené používať základný typ média. Základný typ môžete vidieť v nastaveniach.
|
||||||
dokanySupportEnd.preferencesBtn=Otvoriť predvoľby
|
dokanySupportEnd.preferencesBtn=Otvoriť predvoľby
|
||||||
|
|
||||||
|
#Retry If Readonly
|
||||||
|
|
||||||
# Share Vault
|
# Share Vault
|
||||||
shareVault.title=Zdielať trezor
|
shareVault.title=Zdielať trezor
|
||||||
shareVault.hubAd.authentication=* Silná autentifikácia
|
shareVault.hubAd.authentication=* Silná autentifikácia
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user