mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-21 23:44:42 +00:00
Compare commits
187
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1dab85d265 | ||
|
|
423752e720 | ||
|
|
4efcec7e93 | ||
|
|
619d981ded | ||
|
|
72d93e943b | ||
|
|
f07523267d | ||
|
|
5409470750 | ||
|
|
70b7f39bcc | ||
|
|
5ab12c1b1a | ||
|
|
0ac63e7aba | ||
|
|
ac6f34fe17 | ||
|
|
dba3de230b | ||
|
|
e11d42bb07 | ||
|
|
171d5d5a4c | ||
|
|
b86d8ca790 | ||
|
|
74fc77ab8d | ||
|
|
044dbcb11f | ||
|
|
1b243bb725 | ||
|
|
f21cb90e7d | ||
|
|
1e8f272f6a | ||
|
|
5c1f8e7576 | ||
|
|
bc0bb38e4c | ||
|
|
b9b02acbe5 | ||
|
|
e53598cfce | ||
|
|
a79fd63634 | ||
|
|
f753ddc9be | ||
|
|
158b454b0d | ||
|
|
9e6bd913cb | ||
|
|
83ef9d06d9 | ||
|
|
cf0052b4f5 | ||
|
|
0b4d86768a | ||
|
|
a77e90738a | ||
|
|
0bec741c2b | ||
|
|
af43aeca15 | ||
|
|
1629eae5d3 | ||
|
|
99898b74fb | ||
|
|
327d6065e9 | ||
|
|
2a5ef5d999 | ||
|
|
34e5d19a04 | ||
|
|
33851a8559 | ||
|
|
fb54d96997 | ||
|
|
cff47b1c73 | ||
|
|
5db05d8bc7 | ||
|
|
b85780eae9 | ||
|
|
bdfd22c483 | ||
|
|
e3433cb312 | ||
|
|
46d1d605ad | ||
|
|
a4eadd4817 | ||
|
|
c6717bd4e1 | ||
|
|
53f368108a | ||
|
|
5e52811c74 | ||
|
|
cad7b45808 | ||
|
|
a057bf6f70 | ||
|
|
4cf11cf293 | ||
|
|
2523609996 | ||
|
|
80276ee992 | ||
|
|
561432bc98 | ||
|
|
cd0a640a36 | ||
|
|
a7b8541912 | ||
|
|
d4382f3130 | ||
|
|
2983745be5 | ||
|
|
77983fe00a | ||
|
|
a4836f6528 | ||
|
|
e9b3b505a8 | ||
|
|
19c9eada9d | ||
|
|
2a5bce2c5c | ||
|
|
9ee81a0e35 | ||
|
|
8b05ae0a54 | ||
|
|
aa898c634f | ||
|
|
efbd107fb5 | ||
|
|
300cac5441 | ||
|
|
b651b9ac26 | ||
|
|
29e76e7f93 | ||
|
|
5cac6b8114 | ||
|
|
35c2141fd6 | ||
|
|
b00c81c20a | ||
|
|
7ee0606306 | ||
|
|
046372f95b | ||
|
|
c198adaf3f | ||
|
|
d53f0880ca | ||
|
|
088b177c0e | ||
|
|
287ab4e792 | ||
|
|
0e9cad5b09 | ||
|
|
a0c9caeb21 | ||
|
|
f620c6685a | ||
|
|
61be8c449c | ||
|
|
43343b9954 | ||
|
|
460b7e6225 | ||
|
|
e2430bfb22 | ||
|
|
ac7c9c2165 | ||
|
|
efd73e0d3e | ||
|
|
b441803462 | ||
|
|
42b06aa556 | ||
|
|
b1c21501a6 | ||
|
|
45633837e0 | ||
|
|
b23bd0b27a | ||
|
|
b3f3faf4ee | ||
|
|
33f26bf804 | ||
|
|
a06accb80f | ||
|
|
a50e372f05 | ||
|
|
06726303fb | ||
|
|
11c86f7287 | ||
|
|
8e68a62ab0 | ||
|
|
787d0b8e12 | ||
|
|
ba765f7a69 | ||
|
|
e9d7e631c7 | ||
|
|
88acbc8ed7 | ||
|
|
e84a73b9c3 | ||
|
|
2324ddad8b | ||
|
|
04da2504de | ||
|
|
ad240eb18b | ||
|
|
8105dbc236 | ||
|
|
ceed70c314 | ||
|
|
eddf586b81 | ||
|
|
0d7ddd3189 | ||
|
|
491cd5e550 | ||
|
|
93d95ecd93 | ||
|
|
05a41a625c | ||
|
|
2aff02a186 | ||
|
|
4b7de11750 | ||
|
|
aaf921afcf | ||
|
|
7718445f7c | ||
|
|
0fa27f66c9 | ||
|
|
111c5ae2de | ||
|
|
8841bb1c6a | ||
|
|
8ca7e34d8f | ||
|
|
0fa6f0d915 | ||
|
|
4ffd8f4467 | ||
|
|
bb0c1b54c1 | ||
|
|
8eb91f6471 | ||
|
|
eb20fd7278 | ||
|
|
d836798361 | ||
|
|
760e260eba | ||
|
|
0cd6eb8b5e | ||
|
|
0b2107309b | ||
|
|
309881b339 | ||
|
|
532cdf30ac | ||
|
|
d746e9e215 | ||
|
|
408c80744a | ||
|
|
bf890d2165 | ||
|
|
eb90a7fb33 | ||
|
|
a47814f38c | ||
|
|
8b157fd935 | ||
|
|
5e582f666c | ||
|
|
bebe3064ee | ||
|
|
bbf8e1d1e5 | ||
|
|
98d2442deb | ||
|
|
cac5c86dab | ||
|
|
5a0145800d | ||
|
|
f4b56e5e51 | ||
|
|
7cfb63ed48 | ||
|
|
2521cd2bfb | ||
|
|
1e861ac187 | ||
|
|
a4c85be61c | ||
|
|
5b9e70da33 | ||
|
|
b83a3c27c6 | ||
|
|
5c2954de03 | ||
|
|
8a834fe71c | ||
|
|
4ff2905723 | ||
|
|
538e01b1e5 | ||
|
|
3135be9178 | ||
|
|
2c456f5323 | ||
|
|
9d464e3a4f | ||
|
|
83199eb1de | ||
|
|
5772ffec17 | ||
|
|
f3415277d7 | ||
|
|
8e9a001fde | ||
|
|
5e229395f1 | ||
|
|
18dd9770f2 | ||
|
|
185c5a4f5d | ||
|
|
285ddac219 | ||
|
|
9e631a78f2 | ||
|
|
7fe9049266 | ||
|
|
39ab9ad63f | ||
|
|
b4528f825e | ||
|
|
3d679c73e7 | ||
|
|
b6d0823c24 | ||
|
|
7f4776a995 | ||
|
|
9d2b31cd56 | ||
|
|
cee22e34db | ||
|
|
9a74194391 | ||
|
|
18300ee67f | ||
|
|
5797108dfe | ||
|
|
e1ff552f08 | ||
|
|
f3d4494d91 | ||
|
|
bdc1cc3f05 | ||
|
|
f5bd23de51 |
@@ -48,7 +48,7 @@ runs:
|
|||||||
echo "client-secret=${{ inputs.client-secret }}" >> "$GITHUB_OUTPUT"
|
echo "client-secret=${{ inputs.client-secret }}" >> "$GITHUB_OUTPUT"
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Sign DLLs with Azure Trusted Signing
|
- name: Sign DLLs with Azure Trusted Signing
|
||||||
uses: azure/trusted-signing-action@fc390cf8ed0f14e248a542af1d838388a47c7a7c # v0.5.10
|
uses: azure/artifact-signing-action@87c2e83e6868da99d3380aa309851b32ed9a8346 # v1.1.0
|
||||||
with:
|
with:
|
||||||
files-folder: ${{ inputs.base-dir }}
|
files-folder: ${{ inputs.base-dir }}
|
||||||
files-folder-filter: ${{ inputs.file-extensions }}
|
files-folder-filter: ${{ inputs.file-extensions }}
|
||||||
@@ -59,7 +59,7 @@ runs:
|
|||||||
azure-tenant-id: ${{ steps.set-secrets.outputs.tenant-id }}
|
azure-tenant-id: ${{ steps.set-secrets.outputs.tenant-id }}
|
||||||
azure-client-id: ${{ steps.set-secrets.outputs.client-id }}
|
azure-client-id: ${{ steps.set-secrets.outputs.client-id }}
|
||||||
azure-client-secret: ${{ steps.set-secrets.outputs.client-secret }}
|
azure-client-secret: ${{ steps.set-secrets.outputs.client-secret }}
|
||||||
trusted-signing-account-name: cryptomatorSigning
|
signing-account-name: cryptomatorSigning
|
||||||
certificate-profile-name: production
|
certificate-profile-name: production
|
||||||
endpoint: https://weu.codesigning.azure.net/
|
endpoint: https://weu.codesigning.azure.net/
|
||||||
timestamp-rfc3161: http://timestamp.acs.microsoft.com
|
timestamp-rfc3161: http://timestamp.acs.microsoft.com
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: '25.0.1+8.0.LTS'
|
JAVA_VERSION: '25.0.2+10.0.LTS'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
get-version:
|
||||||
@@ -37,16 +37,16 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
appimage-suffix: x86_64
|
appimage-suffix: x86_64
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-x64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-x64_bin-jmods.zip'
|
||||||
openjfx-sha: '96e520f48610d8ffb94ca30face1f11ffe8a977ddc1c4ff80b1a9e9f048bd94e'
|
openjfx-sha: 'e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b'
|
||||||
- os: ubuntu-24.04-arm
|
- os: ubuntu-24.04-arm
|
||||||
appimage-suffix: aarch64
|
appimage-suffix: aarch64
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-aarch64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip'
|
||||||
openjfx-sha: '9ad4ca7b769ca4ee6419f1e99143dd6ff812f8be4fddb46a7d7cacbeea148af4'
|
openjfx-sha: 'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
@@ -122,8 +122,8 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||||
--java-options "-Dfile.encoding=\"utf-8\""
|
--java-options "-Dfile.encoding=\"utf-8\""
|
||||||
--java-options "-Djava.net.useSystemProxies=true"
|
--java-options "-Djava.net.useSystemProxies=true"
|
||||||
|
--java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\""
|
||||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\""
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\""
|
||||||
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\""
|
|
||||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\""
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\""
|
||||||
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\""
|
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\""
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\""
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\""
|
||||||
@@ -175,7 +175,7 @@ jobs:
|
|||||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage
|
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage
|
||||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage.zsync
|
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage.zsync
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||||
with:
|
with:
|
||||||
name: appimage-${{ matrix.appimage-suffix }}
|
name: appimage-${{ matrix.appimage-suffix }}
|
||||||
path: |
|
path: |
|
||||||
@@ -185,7 +185,7 @@ jobs:
|
|||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Publish AppImage on GitHub Releases
|
- name: Publish AppImage on GitHub Releases
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
||||||
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
|
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||||
with:
|
with:
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
@@ -201,7 +201,7 @@ jobs:
|
|||||||
if: github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable'
|
if: github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable'
|
||||||
steps:
|
steps:
|
||||||
- name: Download AppImages
|
- name: Download AppImages
|
||||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||||
with:
|
with:
|
||||||
path: downloads/
|
path: downloads/
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -212,7 +212,7 @@ jobs:
|
|||||||
echo "x64-sha256sum=${X64_SHA256}" >> "$GITHUB_OUTPUT"
|
echo "x64-sha256sum=${X64_SHA256}" >> "$GITHUB_OUTPUT"
|
||||||
AARCH64_SHA256=$(sha256sum downloads/cryptomator-*-aarch64.AppImage | cut -d ' ' -f1)
|
AARCH64_SHA256=$(sha256sum downloads/cryptomator-*-aarch64.AppImage | cut -d ' ' -f1)
|
||||||
echo "aarch64-sha256sum=${AARCH64_SHA256}" >> "$GITHUB_OUTPUT"
|
echo "aarch64-sha256sum=${AARCH64_SHA256}" >> "$GITHUB_OUTPUT"
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
repository: 'cryptomator/aur-bin'
|
repository: 'cryptomator/aur-bin'
|
||||||
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
AUR_PR_URL: tbd
|
AUR_PR_URL: tbd
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
repository: 'cryptomator/aur'
|
repository: 'cryptomator/aur'
|
||||||
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||||
|
|||||||
@@ -7,6 +7,16 @@ on:
|
|||||||
description: "Url to the file to upload"
|
description: "Url to the file to upload"
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
avast:
|
||||||
|
description: "Upload to Avast"
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
kaspersky:
|
||||||
|
description: "Upload to Kaspersky"
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
url:
|
url:
|
||||||
@@ -39,9 +49,9 @@ jobs:
|
|||||||
url="${INPUT_URL}"
|
url="${INPUT_URL}"
|
||||||
echo "fileName=${url##*/}" >> $GITHUB_OUTPUT
|
echo "fileName=${url##*/}" >> $GITHUB_OUTPUT
|
||||||
- name: Download file
|
- name: Download file
|
||||||
run: curl --remote-name ${INPUT_URL} -L -o ${{steps.extractName.outputs.fileName}}
|
run: curl "${INPUT_URL}" -L -o "${{steps.extractName.outputs.fileName}}" --fail-with-body
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.extractName.outputs.fileName }}
|
name: ${{ steps.extractName.outputs.fileName }}
|
||||||
path: ${{ steps.extractName.outputs.fileName }}
|
path: ${{ steps.extractName.outputs.fileName }}
|
||||||
@@ -50,10 +60,10 @@ jobs:
|
|||||||
name: Anti Virus Allowlisting Kaspersky
|
name: Anti Virus Allowlisting Kaspersky
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: download-file
|
needs: download-file
|
||||||
if: github.event_name == 'workflow_call' || inputs.kaspersky
|
if: inputs.kaspersky
|
||||||
steps:
|
steps:
|
||||||
- name: Download artifact
|
- name: Download artifact
|
||||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||||
with:
|
with:
|
||||||
name: ${{ needs.download-file.outputs.fileName }}
|
name: ${{ needs.download-file.outputs.fileName }}
|
||||||
path: upload
|
path: upload
|
||||||
@@ -70,10 +80,10 @@ jobs:
|
|||||||
name: Anti Virus Allowlisting Avast
|
name: Anti Virus Allowlisting Avast
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: download-file
|
needs: download-file
|
||||||
if: github.event_name == 'workflow_call' || inputs.avast
|
if: inputs.avast
|
||||||
steps:
|
steps:
|
||||||
- name: Download artifact
|
- name: Download artifact
|
||||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||||
with:
|
with:
|
||||||
name: ${{ needs.download-file.outputs.fileName }}
|
name: ${{ needs.download-file.outputs.fileName }}
|
||||||
path: upload
|
path: upload
|
||||||
|
|||||||
@@ -22,14 +22,14 @@ jobs:
|
|||||||
name: Compile and Test
|
name: Compile and Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
|
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
cache: 'maven'
|
cache: 'maven'
|
||||||
- name: Cache SonarCloud packages
|
- name: Cache SonarCloud packages
|
||||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||||
with:
|
with:
|
||||||
path: ~/.sonar/cache
|
path: ~/.sonar/cache
|
||||||
key: ${{ runner.os }}-sonar
|
key: ${{ runner.os }}-sonar
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
- name: Draft a release
|
- name: Draft a release
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
|
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
discussion_category_name: releases
|
discussion_category_name: releases
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
run: echo 'JDK_MAJOR_VERSION=${{ env.JDK_VERSION }}'.substring(0,2) >> "$env:GITHUB_ENV"
|
run: echo 'JDK_MAJOR_VERSION=${{ env.JDK_VERSION }}'.substring(0,2) >> "$env:GITHUB_ENV"
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
- name: Checkout latest JDK ${{ env.JDK_MAJOR_VERSION }}
|
- name: Checkout latest JDK ${{ env.JDK_MAJOR_VERSION }}
|
||||||
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
with:
|
with:
|
||||||
java-version: ${{ env.JDK_MAJOR_VERSION}}
|
java-version: ${{ env.JDK_MAJOR_VERSION}}
|
||||||
distribution: ${{ env.JDK_VENDOR }}
|
distribution: ${{ env.JDK_VENDOR }}
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: '25.0.1+8.0.LTS'
|
JAVA_VERSION: '25.0.2+10.0.LTS'
|
||||||
DEB_BUILD_DEPENDS: 'debhelper (>=10), openjdk-25-jdk (>= 25+36), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1'
|
DEB_BUILD_DEPENDS: 'debhelper (>=10), openjdk-25-jdk (>= 25+36), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1'
|
||||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-x64_bin-jmods.zip'
|
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-x64_bin-jmods.zip'
|
||||||
OPENJFX_JMODS_AMD64_HASH: '96e520f48610d8ffb94ca30face1f11ffe8a977ddc1c4ff80b1a9e9f048bd94e'
|
OPENJFX_JMODS_AMD64_HASH: 'e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b'
|
||||||
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_linux-aarch64_bin-jmods.zip'
|
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip'
|
||||||
OPENJFX_JMODS_AARCH64_HASH: '9ad4ca7b769ca4ee6419f1e99143dd6ff812f8be4fddb46a7d7cacbeea148af4'
|
OPENJFX_JMODS_AARCH64_HASH: 'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
get-version:
|
||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
INPUT_PPAVER: ${{ inputs.ppaver }}
|
INPUT_PPAVER: ${{ inputs.ppaver }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- id: deb-version
|
- id: deb-version
|
||||||
name: Determine deb-version
|
name: Determine deb-version
|
||||||
run: |
|
run: |
|
||||||
@@ -60,7 +60,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
DEB_BUILD_DEPENDS: ${{ env.DEB_BUILD_DEPENDS }}
|
DEB_BUILD_DEPENDS: ${{ env.DEB_BUILD_DEPENDS }}
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
@@ -143,7 +143,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator_*_amd64.deb
|
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator_*_amd64.deb
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||||
with:
|
with:
|
||||||
name: linux-deb-package
|
name: linux-deb-package
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-dependencies:
|
check-dependencies:
|
||||||
uses: skymatic/workflows/.github/workflows/run-dependency-check.yml@1074588008ae3326a2221ea451783280518f0366 # v3.0.1
|
uses: skymatic/workflows/.github/workflows/run-dependency-check.yml@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # v3.0.3
|
||||||
with:
|
with:
|
||||||
runner-os: 'ubuntu-latest'
|
runner-os: 'ubuntu-latest'
|
||||||
java-distribution: 'temurin'
|
java-distribution: 'temurin'
|
||||||
@@ -16,4 +16,4 @@ jobs:
|
|||||||
nvd-api-key: ${{ secrets.NVD_API_KEY }}
|
nvd-api-key: ${{ secrets.NVD_API_KEY }}
|
||||||
ossindex-username: ${{ secrets.OSSINDEX_USERNAME }}
|
ossindex-username: ${{ secrets.OSSINDEX_USERNAME }}
|
||||||
ossindex-token: ${{ secrets.OSSINDEX_API_TOKEN }}
|
ossindex-token: ${{ secrets.OSSINDEX_API_TOKEN }}
|
||||||
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ jobs:
|
|||||||
INTERVAL: 900
|
INTERVAL: 900
|
||||||
JSON_DATA: ${{ steps.get-stats.outputs.result }}
|
JSON_DATA: ${{ steps.get-stats.outputs.result }}
|
||||||
- name: Upload Results
|
- name: Upload Results
|
||||||
uses: fjogeleit/http-request-action@1297c6fc63a79b147d1676540a3fd9d2e37817c5 # v1.16.5
|
uses: fjogeleit/http-request-action@551353b829c3646756b2ec2b3694f819d7957495 # v2.0.0
|
||||||
with:
|
with:
|
||||||
url: 'https://graphite-us-central1.grafana.net/metrics'
|
url: 'https://graphite-us-central1.grafana.net/metrics'
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
FLATHUB_PR_URL: tbd
|
FLATHUB_PR_URL: tbd
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
repository: 'flathub/org.cryptomator.Cryptomator'
|
repository: 'flathub/org.cryptomator.Cryptomator'
|
||||||
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ jobs:
|
|||||||
revNum: ${{ steps.versions.outputs.revNum }}
|
revNum: ${{ steps.versions.outputs.revNum }}
|
||||||
type: ${{ steps.versions.outputs.type}}
|
type: ${{ steps.versions.outputs.type}}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: '25.0.1+8.0.LTS'
|
JAVA_VERSION: '25.0.2+10.0.LTS'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
get-version:
|
||||||
@@ -44,12 +44,12 @@ jobs:
|
|||||||
architecture: x64
|
architecture: x64
|
||||||
output-suffix: x64
|
output-suffix: x64
|
||||||
fuse-lib: macFUSE
|
fuse-lib: macFUSE
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_osx-x64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_osx-x64_bin-jmods.zip'
|
||||||
openjfx-sha: '0eba73fb28a24c845175d16fa2f8c081c936ce6de1be9b79eb6119fa32e53d52'
|
openjfx-sha: '0b4d8463f03901b7425d94628e4116b7078abb8dd540fbec415266fac20bda5c'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
@@ -128,8 +128,8 @@ jobs:
|
|||||||
--java-options "-Dapple.awt.enableTemplateImages=true"
|
--java-options "-Dapple.awt.enableTemplateImages=true"
|
||||||
--java-options "-Dsun.java2d.metal=true"
|
--java-options "-Dsun.java2d.metal=true"
|
||||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||||
|
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\""
|
||||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
||||||
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/Library/Application Support/Cryptomator/Plugins\""
|
|
||||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
||||||
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/Cryptomator/key.p12\""
|
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/Cryptomator/key.p12\""
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/Cryptomator/ipc.socket\""
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/Cryptomator/ipc.socket\""
|
||||||
@@ -283,7 +283,7 @@ jobs:
|
|||||||
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||||
with:
|
with:
|
||||||
name: dmg-${{ matrix.output-suffix }}
|
name: dmg-${{ matrix.output-suffix }}
|
||||||
path: |
|
path: |
|
||||||
@@ -292,7 +292,7 @@ jobs:
|
|||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Publish dmg on GitHub Releases
|
- name: Publish dmg on GitHub Releases
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
||||||
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
|
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||||
with:
|
with:
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: '25.0.1+8.0.LTS'
|
JAVA_VERSION: '25.0.2+10.0.LTS'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
get-version:
|
||||||
@@ -42,12 +42,12 @@ jobs:
|
|||||||
architecture: aarch64
|
architecture: aarch64
|
||||||
output-suffix: arm64
|
output-suffix: arm64
|
||||||
fuse-lib: FUSE-T
|
fuse-lib: FUSE-T
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_osx-aarch64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_osx-aarch64_bin-jmods.zip'
|
||||||
openjfx-sha: '13f8c0513c40c95881479fbcf0465a29a60217393fb0656f5e4eab78a9442fba'
|
openjfx-sha: '4cd258001c75af7047005c5c891e2400ed11d24fbb09412324c0cbaf8b503c5a'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
@@ -126,8 +126,8 @@ jobs:
|
|||||||
--java-options "-Dapple.awt.enableTemplateImages=true"
|
--java-options "-Dapple.awt.enableTemplateImages=true"
|
||||||
--java-options "-Dsun.java2d.metal=true"
|
--java-options "-Dsun.java2d.metal=true"
|
||||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||||
|
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\""
|
||||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
||||||
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/Library/Application Support/Cryptomator/Plugins\""
|
|
||||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
||||||
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/Cryptomator/key.p12\""
|
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/Cryptomator/key.p12\""
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/Cryptomator/ipc.socket\""
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/Cryptomator/ipc.socket\""
|
||||||
@@ -282,7 +282,7 @@ jobs:
|
|||||||
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||||
with:
|
with:
|
||||||
name: dmg-${{ matrix.output-suffix }}
|
name: dmg-${{ matrix.output-suffix }}
|
||||||
path: |
|
path: |
|
||||||
@@ -291,7 +291,7 @@ jobs:
|
|||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Publish dmg on GitHub Releases
|
- name: Publish dmg on GitHub Releases
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
||||||
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
|
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||||
with:
|
with:
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
|
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
|
||||||
with:
|
with:
|
||||||
days-before-stale: 14
|
days-before-stale: 14
|
||||||
days-before-close: 0
|
days-before-close: 0
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||||
- name: Publish asc on GitHub Releases
|
- name: Publish asc on GitHub Releases
|
||||||
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
|
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||||
with:
|
with:
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ jobs:
|
|||||||
name: Compile and Test
|
name: Compile and Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
|
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ jobs:
|
|||||||
name: Validate commits pushed to release/hotfix branch to fulfill release requirements
|
name: Validate commits pushed to release/hotfix branch to fulfill release requirements
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
- name: Cache NVD DB
|
- name: Cache NVD DB
|
||||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||||
with:
|
with:
|
||||||
path: ~/.m2/repository/org/owasp/dependency-check-data/
|
path: ~/.m2/repository/org/owasp/dependency-check-data/
|
||||||
key: dependency-check-${{ github.run_id }}
|
key: dependency-check-${{ github.run_id }}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
|
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
|
||||||
with:
|
with:
|
||||||
days-before-stale: 365
|
days-before-stale: 365
|
||||||
days-before-close: 90
|
days-before-close: 90
|
||||||
|
|||||||
@@ -22,11 +22,12 @@ on:
|
|||||||
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25/openjfx-25_windows-x64_bin-jmods.zip'
|
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_windows-x64_bin-jmods.zip'
|
||||||
OPENJFX_JMODS_AMD64_HASH: 'c8eb9fd039b00e0020cf6c3db8ed7876bf3ee4d27860aa697a247b83b8296ae7'
|
OPENJFX_JMODS_AMD64_HASH: '33d878dfac85590c4d77c518ed413e512d34a8479d90132b230a7ddd173576b3'
|
||||||
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
|
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
|
||||||
WINFSP_MSI_HASH: '073a70e00f77423e34bed98b86e600def93393ba5822204fac57a29324db9f7a'
|
WINFSP_MSI_HASH: '073a70e00f77423e34bed98b86e600def93393ba5822204fac57a29324db9f7a'
|
||||||
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
|
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
|
||||||
|
WIX_VERSION: '6.0.2'
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -51,9 +52,9 @@ jobs:
|
|||||||
java-version: '25.0.1+8'
|
java-version: '25.0.1+8'
|
||||||
java-package: 'jdk'
|
java-package: 'jdk'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ matrix.java-dist }}
|
distribution: ${{ matrix.java-dist }}
|
||||||
java-version: ${{ matrix.java-version }}
|
java-version: ${{ matrix.java-version }}
|
||||||
@@ -62,9 +63,11 @@ jobs:
|
|||||||
cache: 'maven'
|
cache: 'maven'
|
||||||
- name: Install wix and extensions
|
- name: Install wix and extensions
|
||||||
run: |
|
run: |
|
||||||
dotnet tool install --global wix --version 6.0.0
|
dotnet tool install --global wix --version ${WIX_VERSION}
|
||||||
wix.exe extension add WixToolset.UI.wixext/6.0.0 --global
|
wix.exe extension add --global WixToolset.UI.wixext/${WIX_VERSION}
|
||||||
wix.exe extension add WixToolset.Util.wixext/6.0.0 --global
|
wix.exe extension add --global WixToolset.Util.wixext/${WIX_VERSION}
|
||||||
|
env:
|
||||||
|
WIX_VERSION: ${{ env.WIX_VERSION }}
|
||||||
- name: Download and extract JavaFX jmods from Gluon
|
- name: Download and extract JavaFX jmods from Gluon
|
||||||
if: matrix.arch == 'x64'
|
if: matrix.arch == 'x64'
|
||||||
#In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip
|
#In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip
|
||||||
@@ -140,8 +143,8 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||||
--java-options "-Dfile.encoding=\"utf-8\""
|
--java-options "-Dfile.encoding=\"utf-8\""
|
||||||
--java-options "-Djava.net.useSystemProxies=true"
|
--java-options "-Djava.net.useSystemProxies=true"
|
||||||
|
--java-options "-Dcryptomator.adminConfigPath=\"C:/ProgramData/Cryptomator/config.properties\""
|
||||||
--java-options "-Dcryptomator.logDir=\"@{localappdata}/Cryptomator\""
|
--java-options "-Dcryptomator.logDir=\"@{localappdata}/Cryptomator\""
|
||||||
--java-options "-Dcryptomator.pluginDir=\"@{appdata}/Cryptomator/Plugins\""
|
|
||||||
--java-options "-Dcryptomator.settingsPath=\"@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json\""
|
--java-options "-Dcryptomator.settingsPath=\"@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json\""
|
||||||
--java-options "-Dcryptomator.p12Path=\"@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12\""
|
--java-options "-Dcryptomator.p12Path=\"@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12\""
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{localappdata}/Cryptomator/ipc.socket\""
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{localappdata}/Cryptomator/ipc.socket\""
|
||||||
@@ -200,7 +203,7 @@ jobs:
|
|||||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||||
- name: Sign DLLs with Actalis CodeSigner
|
- name: Sign DLLs with Actalis CodeSigner
|
||||||
if: inputs.sign || github.event_name == 'release'
|
if: inputs.sign || github.event_name == 'release'
|
||||||
uses: skymatic/workflows/.github/actions/win-sign-action@450e322ff2214d0be0b079b63343c894f3ef735f # no specific version
|
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
|
||||||
with:
|
with:
|
||||||
base-dir: 'appdir'
|
base-dir: 'appdir'
|
||||||
file-extensions: 'dll,exe,ps1'
|
file-extensions: 'dll,exe,ps1'
|
||||||
@@ -277,7 +280,7 @@ jobs:
|
|||||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||||
with:
|
with:
|
||||||
name: msi-${{ matrix.arch }}
|
name: msi-${{ matrix.arch }}
|
||||||
path: |
|
path: |
|
||||||
@@ -299,21 +302,23 @@ jobs:
|
|||||||
java-version: '24.0.1+9'
|
java-version: '24.0.1+9'
|
||||||
java-package: 'jdk'
|
java-package: 'jdk'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Install wix and extensions
|
- name: Install wix and extensions
|
||||||
run: |
|
run: |
|
||||||
dotnet tool install --global wix --version 6.0.0
|
dotnet tool install --global wix --version ${WIX_VERSION}
|
||||||
wix.exe extension add WixToolset.BootstrapperApplications.wixext/6.0.0 --global
|
wix.exe extension add --global WixToolset.BootstrapperApplications.wixext/${WIX_VERSION}
|
||||||
wix.exe extension add WixToolset.Util.wixext/6.0.0 --global
|
wix.exe extension add --global WixToolset.Util.wixext/${WIX_VERSION}
|
||||||
|
env:
|
||||||
|
WIX_VERSION: ${{ env.WIX_VERSION }}
|
||||||
- name: Download .msi
|
- name: Download .msi
|
||||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||||
with:
|
with:
|
||||||
name: msi-${{ matrix.arch }}
|
name: msi-${{ matrix.arch }}
|
||||||
path: dist/win/bundle/resources
|
path: dist/win/bundle/resources
|
||||||
- name: Strip version info from msi file name
|
- name: Strip version info from msi file name
|
||||||
run: mv dist/win/bundle/resources/Cryptomator*.msi dist/win/bundle/resources/Cryptomator.msi
|
run: mv dist/win/bundle/resources/Cryptomator*.msi dist/win/bundle/resources/Cryptomator.msi
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
|
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ matrix.java-dist }}
|
distribution: ${{ matrix.java-dist }}
|
||||||
java-version: ${{ matrix.java-version }}
|
java-version: ${{ matrix.java-version }}
|
||||||
@@ -376,7 +381,7 @@ jobs:
|
|||||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||||
- name: Sign burn engine with Actalis CodeSigner
|
- name: Sign burn engine with Actalis CodeSigner
|
||||||
if: inputs.sign || github.event_name == 'release'
|
if: inputs.sign || github.event_name == 'release'
|
||||||
uses: skymatic/workflows/.github/actions/win-sign-action@450e322ff2214d0be0b079b63343c894f3ef735f # no specific version
|
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
|
||||||
with:
|
with:
|
||||||
base-dir: 'tmp'
|
base-dir: 'tmp'
|
||||||
file-extensions: 'exe'
|
file-extensions: 'exe'
|
||||||
@@ -400,7 +405,7 @@ jobs:
|
|||||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||||
- name: Sign installer with Actalis CodeSigner
|
- name: Sign installer with Actalis CodeSigner
|
||||||
if: inputs.sign || github.event_name == 'release'
|
if: inputs.sign || github.event_name == 'release'
|
||||||
uses: skymatic/workflows/.github/actions/win-sign-action@450e322ff2214d0be0b079b63343c894f3ef735f # no specific version
|
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
|
||||||
with:
|
with:
|
||||||
base-dir: 'installer'
|
base-dir: 'installer'
|
||||||
file-extensions: 'exe'
|
file-extensions: 'exe'
|
||||||
@@ -418,7 +423,7 @@ jobs:
|
|||||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||||
with:
|
with:
|
||||||
name: exe-${{ matrix.executable-suffix }}
|
name: exe-${{ matrix.executable-suffix }}
|
||||||
path: |
|
path: |
|
||||||
@@ -436,12 +441,12 @@ jobs:
|
|||||||
download-url-exe-x64: ${{ fromJSON(steps.publish.outputs.assets)[2].browser_download_url }}
|
download-url-exe-x64: ${{ fromJSON(steps.publish.outputs.assets)[2].browser_download_url }}
|
||||||
steps:
|
steps:
|
||||||
- name: Download installers
|
- name: Download installers
|
||||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||||
with:
|
with:
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
- name: Publish installers on GitHub Releases
|
- name: Publish installers on GitHub Releases
|
||||||
id: publish
|
id: publish
|
||||||
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1
|
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||||
with:
|
with:
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
|
|||||||
Generated
+33
-10
@@ -12,17 +12,15 @@
|
|||||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||||
<outputRelativeToContentRoot value="true" />
|
<outputRelativeToContentRoot value="true" />
|
||||||
<option name="dagger.fastInit" value="enabled" />
|
<option name="dagger.fastInit" value="enabled" />
|
||||||
<option name="dagger.formatGeneratedSource" value="enabled" />
|
|
||||||
<processorPath useClasspath="false">
|
<processorPath useClasspath="false">
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.57.2/dagger-compiler-2.57.2.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.59.1/dagger-compiler-2.59.1.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.57.2/dagger-2.57.2.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.59.1/dagger-2.59.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$/jakarta/inject/jakarta.inject-api/2.0.1/jakarta.inject-api-2.0.1.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.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.57.2/dagger-spi-2.57.2.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.59.1/dagger-spi-2.59.1.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/2.1.21-2.0.2/symbol-processing-api-2.1.21-2.0.2.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/2.2.20-2.0.3/symbol-processing-api-2.2.20-2.0.3.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/googlejavaformat/google-java-format/1.33.0/google-java-format-1.33.0.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/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" />
|
||||||
@@ -37,8 +35,33 @@
|
|||||||
<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$/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.3/checker-compat-qual-2.5.3.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.3/checker-compat-qual-2.5.3.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-metadata-jvm/2.1.21/kotlin-metadata-jvm-2.1.21.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-metadata-jvm/2.2.20/kotlin-metadata-jvm-2.2.20.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.21/kotlin-stdlib-2.1.21.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.59.1/dagger-compiler-2.59.1.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.59.1/dagger-2.59.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$/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.59.1/dagger-spi-2.59.1.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/2.2.20-2.0.3/symbol-processing-api-2.2.20-2.0.3.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.33.0/google-java-format-1.33.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/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$/org/checkerframework/checker-qual/3.41.0/checker-qual-3.41.0.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.23.0/error_prone_annotations-2.23.0.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/2.8/j2objc-annotations-2.8.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.13.0/javapoet-1.13.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$/javax/inject/javax.inject/1/javax.inject-1.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.3/checker-compat-qual-2.5.3.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-metadata-jvm/2.2.20/kotlin-metadata-jvm-2.2.20.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20.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" />
|
||||||
</processorPath>
|
</processorPath>
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
@@ -47,7 +70,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" />
|
<module name="cryptomator" options="-Adagger.fastInit=enabled" />
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
+34
-3
@@ -10,10 +10,41 @@ Changes to prior versions can be found on the [Github release page](https://gith
|
|||||||
## [Unreleased](https://github.com/cryptomator/cryptomator/compare/1.18.0...HEAD)
|
## [Unreleased](https://github.com/cryptomator/cryptomator/compare/1.18.0...HEAD)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
* New Self-Update Mechanism (#3948)
|
* Self-Update Mechanism ([#3948](https://github.com/cryptomator/cryptomator/pull/3948))
|
||||||
* Implemented `.dmg` update mechanism
|
* Implemented `.dmg` update mechanism
|
||||||
* Implemented Flatpak update mechanism
|
* Implemented Flatpak update mechanism
|
||||||
|
* App notifications ([#4069](https://github.com/cryptomator/cryptomator/pull/4069))
|
||||||
|
* Mark files in-use for Hub vaults ([#4078](https://github.com/cryptomator/cryptomator/pull/4078))
|
||||||
|
* Accessibility: Adjust app to be used with a screen reader ([#547](https://github.com/cryptomator/cryptomator/issues/547))
|
||||||
|
* Show Archived Vault Dialog on unlock when Hub returns 410 ([#4081](https://github.com/cryptomator/cryptomator/pull/4081))
|
||||||
|
* Support automatic app theme selection according to OS theme on Linux ([#4027](https://github.com/cryptomator/cryptomator/issues/4027))
|
||||||
|
* Admin configuration: Allow overwriting certain app properties by external config file ([#4105](https://github.com/cryptomator/cryptomator/pull/4105))
|
||||||
|
* New keychain backend using [secret service API](https://specifications.freedesktop.org/secret-service/0.2) for Linux ([#4025](https://github.com/cryptomator/cryptomator/pull/4025))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* Fixed password reset/show recovery possible for vaults without masterkey file ([#4120](https://github.com/cryptomator/cryptomator/pull/4120))
|
||||||
|
* Fixed restore vault config failed due to selecting a directory instead of file ([#4141](https://github.com/cryptomator/cryptomator/issues/4141))
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* Built using JDK 25 (#4031)
|
* Disable user defined app start config on Windows ([#4132](https://github.com/cryptomator/cryptomator/issues/4132))
|
||||||
* Modernized Templage for GitHub Releases
|
* Disable plugin loading by default ([#4136](https://github.com/cryptomator/cryptomator/4136))
|
||||||
|
* Use JDK 25 ([#4031](https://github.com/cryptomator/cryptomator/pull/4031))
|
||||||
|
* Update JavaFX to 25.0.2 ([#4145](https://github.com/cryptomator/cryptomator/pull/4145)))
|
||||||
|
* Updated translations
|
||||||
|
* Updated dependencies
|
||||||
|
* `ch.qos.logback:*` from 1.5.19 to 1.5.31
|
||||||
|
* `com.fasterxml.jackson.core:jackson-databind` from 2.20.0 to 2.21.0
|
||||||
|
* `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.20.0 to 2.21.0
|
||||||
|
* `com.github.ben-manes.caffeine:caffeine` from 3.2.2 to 3.2.3
|
||||||
|
* `com.google.dagger:*` from 2.57.2 to 2.59.1
|
||||||
|
* `org.apache.commons:commons-lang3` from 3.19.0 to 3.20.0
|
||||||
|
* `org.cryptomator:cryptofs` from 2.9.0 to 2.10.0-beta3
|
||||||
|
* `org.cryptomator:cryptolib` from 2.2.1 to 2.2.2
|
||||||
|
* `org.cryptomator:fuse-nio-adapter` from 5.1.0 to 6.0.0
|
||||||
|
* `org.cryptomator:integrations-api` from 1.7.0 to 1.8.0-beta1
|
||||||
|
* `org.cryptomator:integrations-linux` from 1.6.1 to 1.7.0-beta4
|
||||||
|
* `org.cryptomator:integrations-mac` from 1.4.1 to 1.5.0-beta3
|
||||||
|
* `org.cryptomator:integrations-win` from 1.5.1 to 1.6.0
|
||||||
|
* `org.cryptomator:webdav-nio-adapter` from 3.0.0 to 3.0.1
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ For more information on the security details visit [cryptomator.org](https://doc
|
|||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* JDK 24 (e.g. temurin, zulu)
|
* JDK 25 (e.g. temurin, zulu)
|
||||||
* Maven 3
|
* Maven 3
|
||||||
|
|
||||||
### Run Maven
|
### Run Maven
|
||||||
|
|||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
# This is the Cryptomator administrative configuration file.
|
||||||
|
# It is a simple key-value pair file.
|
||||||
|
# Lines starting with '#' are comments and will be ignored.
|
||||||
|
# For more info, read the docs at https://docs.cryptomator.org/desktop/advanced-settings/
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# Sets the plugin directory and enables plugin loading
|
||||||
|
# cryptomator.pluginDir=@{userhome}/Cryptomator/Plugins
|
||||||
Vendored
+5
-4
@@ -23,12 +23,12 @@ mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests
|
|||||||
cp ../../../LICENSE.txt ../../../target
|
cp ../../../LICENSE.txt ../../../target
|
||||||
cp ../../../target/cryptomator-*.jar ../../../target/mods
|
cp ../../../target/cryptomator-*.jar ../../../target/mods
|
||||||
|
|
||||||
JAVAFX_VERSION=25
|
JAVAFX_VERSION=25.0.2
|
||||||
JAVAFX_ARCH="x64"
|
JAVAFX_ARCH="x64"
|
||||||
JAVAFX_JMODS_SHA256='96e520f48610d8ffb94ca30face1f11ffe8a977ddc1c4ff80b1a9e9f048bd94e'
|
JAVAFX_JMODS_SHA256='e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b'
|
||||||
if [ "${CPU_ARCH}" = "aarch64" ]; then
|
if [ "${CPU_ARCH}" = "aarch64" ]; then
|
||||||
JAVAFX_ARCH="aarch64"
|
JAVAFX_ARCH="aarch64"
|
||||||
JAVAFX_JMODS_SHA256='951c52481af0ec5885b06f1ebaa8a10da7e8ea23c5e1ef3e2f6f11fa1b3a7ce1'
|
JAVAFX_JMODS_SHA256='c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# download javaFX jmods
|
# download javaFX jmods
|
||||||
@@ -88,8 +88,8 @@ ${JAVA_HOME}/bin/jpackage \
|
|||||||
--app-version "${VERSION}.${REVISION_NO}" \
|
--app-version "${VERSION}.${REVISION_NO}" \
|
||||||
--java-options "-Dfile.encoding=\"utf-8\"" \
|
--java-options "-Dfile.encoding=\"utf-8\"" \
|
||||||
--java-options "-Djava.net.useSystemProxies=true" \
|
--java-options "-Djava.net.useSystemProxies=true" \
|
||||||
|
--java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\"" \
|
||||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \
|
||||||
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" \
|
|
||||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" \
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" \
|
||||||
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
|
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \
|
||||||
@@ -115,6 +115,7 @@ cp ../common/org.cryptomator.Cryptomator.tray-unlocked.svg Cryptomator.AppDir/us
|
|||||||
cp ../common/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop
|
cp ../common/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/usr/share/applications/org.cryptomator.Cryptomator.desktop
|
||||||
cp ../common/org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.metainfo.xml
|
cp ../common/org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/metainfo/org.cryptomator.Cryptomator.metainfo.xml
|
||||||
cp ../common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
cp ../common/application-vnd.cryptomator.vault.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
||||||
|
cp ../common/application-vnd.cryptomator.encrypted.xml Cryptomator.AppDir/usr/share/mime/packages/application-vnd.cryptomator.encrypted.xml
|
||||||
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/org.cryptomator.Cryptomator.svg
|
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/org.cryptomator.Cryptomator.svg
|
||||||
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon
|
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon
|
||||||
ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/org.cryptomator.Cryptomator.desktop
|
ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/org.cryptomator.Cryptomator.desktop
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||||
|
<mime-type type="application/vnd.cryptomator.encrypted">
|
||||||
|
<comment>Cryptomator Encrypted Data</comment>
|
||||||
|
<glob pattern="*.c9r"/>
|
||||||
|
<glob pattern="*.c9s"/>
|
||||||
|
<glob pattern="*.c9u"/>
|
||||||
|
</mime-type>
|
||||||
|
</mime-info>
|
||||||
+2
-1
@@ -6,4 +6,5 @@ common/org.cryptomator.Cryptomator.tray-unlocked.svg usr/share/icons/hicolor/sca
|
|||||||
common/org.cryptomator.Cryptomator256.png usr/share/icons/hicolor/256x256/apps
|
common/org.cryptomator.Cryptomator256.png usr/share/icons/hicolor/256x256/apps
|
||||||
common/org.cryptomator.Cryptomator512.png usr/share/icons/hicolor/512x512/apps
|
common/org.cryptomator.Cryptomator512.png usr/share/icons/hicolor/512x512/apps
|
||||||
common/org.cryptomator.Cryptomator.metainfo.xml usr/share/metainfo
|
common/org.cryptomator.Cryptomator.metainfo.xml usr/share/metainfo
|
||||||
common/application-vnd.cryptomator.vault.xml usr/share/mime/packages
|
common/application-vnd.cryptomator.vault.xml usr/share/mime/packages
|
||||||
|
common/application-vnd.cryptomator.encrypted.xml usr/share/mime/packages
|
||||||
Vendored
+1
@@ -25,6 +25,7 @@ case "$1" in
|
|||||||
fi
|
fi
|
||||||
xdg-desktop-menu install --novendor /usr/share/applications/org.cryptomator.Cryptomator.desktop
|
xdg-desktop-menu install --novendor /usr/share/applications/org.cryptomator.Cryptomator.desktop
|
||||||
xdg-mime install /usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
xdg-mime install /usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
||||||
|
xdg-mime install /usr/share/mime/packages/application-vnd.cryptomator.encrypted.xml
|
||||||
;;
|
;;
|
||||||
|
|
||||||
abort-upgrade|abort-remove|abort-deconfigure)
|
abort-upgrade|abort-remove|abort-deconfigure)
|
||||||
|
|||||||
Vendored
+1
@@ -23,6 +23,7 @@ case "$1" in
|
|||||||
|
|
||||||
xdg-desktop-menu uninstall --novendor /usr/share/applications/org.cryptomator.Cryptomator.desktop
|
xdg-desktop-menu uninstall --novendor /usr/share/applications/org.cryptomator.Cryptomator.desktop
|
||||||
xdg-mime uninstall /usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
xdg-mime uninstall /usr/share/mime/packages/application-vnd.cryptomator.vault.xml
|
||||||
|
xdg-mime uninstall /usr/share/mime/packages/application-vnd.cryptomator.encrypted.xml
|
||||||
;;
|
;;
|
||||||
|
|
||||||
failed-upgrade)
|
failed-upgrade)
|
||||||
|
|||||||
Vendored
+1
-1
@@ -51,8 +51,8 @@ override_dh_auto_build:
|
|||||||
--java-options "-Xmx256m" \
|
--java-options "-Xmx256m" \
|
||||||
--java-options "-Dfile.encoding=\"utf-8\"" \
|
--java-options "-Dfile.encoding=\"utf-8\"" \
|
||||||
--java-options "-Djava.net.useSystemProxies=true" \
|
--java-options "-Djava.net.useSystemProxies=true" \
|
||||||
|
--java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\"" \
|
||||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \
|
||||||
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" \
|
|
||||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" \
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:@{userhome}/.Cryptomator/settings.json\"" \
|
||||||
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
|
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \
|
||||||
|
|||||||
Vendored
+4
-4
@@ -32,15 +32,15 @@ REVISION_NO=`git rev-list --count HEAD`
|
|||||||
VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
|
VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
|
||||||
FUSE_LIB="FUSE-T"
|
FUSE_LIB="FUSE-T"
|
||||||
|
|
||||||
JAVAFX_VERSION=25
|
JAVAFX_VERSION=25.0.2
|
||||||
JAVAFX_ARCH="undefined"
|
JAVAFX_ARCH="undefined"
|
||||||
JAVAFX_JMODS_SHA256="undefined"
|
JAVAFX_JMODS_SHA256="undefined"
|
||||||
if [ "$(machine)" = "arm64e" ]; then
|
if [ "$(machine)" = "arm64e" ]; then
|
||||||
JAVAFX_ARCH="aarch64"
|
JAVAFX_ARCH="aarch64"
|
||||||
JAVAFX_JMODS_SHA256="13f8c0513c40c95881479fbcf0465a29a60217393fb0656f5e4eab78a9442fba"
|
JAVAFX_JMODS_SHA256="4cd258001c75af7047005c5c891e2400ed11d24fbb09412324c0cbaf8b503c5a"
|
||||||
else
|
else
|
||||||
JAVAFX_ARCH="x64"
|
JAVAFX_ARCH="x64"
|
||||||
JAVAFX_JMODS_SHA256="0eba73fb28a24c845175d16fa2f8c081c936ce6de1be9b79eb6119fa32e53d52"
|
JAVAFX_JMODS_SHA256="0b4d8463f03901b7425d94628e4116b7078abb8dd540fbec415266fac20bda5c"
|
||||||
fi
|
fi
|
||||||
JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-${JAVAFX_ARCH}_bin-jmods.zip"
|
JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-${JAVAFX_ARCH}_bin-jmods.zip"
|
||||||
|
|
||||||
@@ -114,9 +114,9 @@ ${JAVA_HOME}/bin/jpackage \
|
|||||||
--java-options "-Dapple.awt.enableTemplateImages=true" \
|
--java-options "-Dapple.awt.enableTemplateImages=true" \
|
||||||
--java-options "-Dsun.java2d.metal=true" \
|
--java-options "-Dsun.java2d.metal=true" \
|
||||||
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
|
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
|
||||||
|
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\"" \
|
||||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/${APP_NAME}\"" \
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/${APP_NAME}\"" \
|
||||||
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log" \
|
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log" \
|
||||||
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/Library/Application Support/${APP_NAME}/Plugins\"" \
|
|
||||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/${APP_NAME}/settings.json\"" \
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/${APP_NAME}/settings.json\"" \
|
||||||
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/${APP_NAME}/ipc.socket\"" \
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/${APP_NAME}/ipc.socket\"" \
|
||||||
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/${APP_NAME}/key.p12\"" \
|
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/${APP_NAME}/key.p12\"" \
|
||||||
|
|||||||
Vendored
+1
@@ -105,6 +105,7 @@
|
|||||||
<array>
|
<array>
|
||||||
<string>c9r</string>
|
<string>c9r</string>
|
||||||
<string>c9s</string>
|
<string>c9s</string>
|
||||||
|
<string>c9u</string>
|
||||||
</array>
|
</array>
|
||||||
<key>public.mime-type</key>
|
<key>public.mime-type</key>
|
||||||
<array>
|
<array>
|
||||||
|
|||||||
Vendored
+7
-7
@@ -34,20 +34,20 @@ if ((Get-Command "mvn" -ErrorAction SilentlyContinue) -eq $null)
|
|||||||
}
|
}
|
||||||
if ((Get-Command 'wix' -ErrorAction SilentlyContinue) -eq $null)
|
if ((Get-Command 'wix' -ErrorAction SilentlyContinue) -eq $null)
|
||||||
{
|
{
|
||||||
Write-Error 'Unable to find wix in your PATH (try: dotnet tool install --global wix --version 6.0.0)'
|
Write-Error 'Unable to find wix in your PATH (try: dotnet tool install --global wix --version 6.0.2)'
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
$wixExtensions = & wix.exe extension list --global | Out-String
|
$wixExtensions = & wix.exe extension list --global | Out-String
|
||||||
if ($wixExtensions -notmatch 'WixToolset.UI.wixext') {
|
if ($wixExtensions -notmatch 'WixToolset.UI.wixext') {
|
||||||
Write-Error 'Wix UI extension missing. Please install it with: wix.exe extension add WixToolset.UI.wixext/6.0.0 --global)'
|
Write-Error 'Wix UI extension missing. Please install it with: wix.exe extension add WixToolset.UI.wixext/6.0.2 --global)'
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
if ($wixExtensions -notmatch 'WixToolset.Util.wixext') {
|
if ($wixExtensions -notmatch 'WixToolset.Util.wixext') {
|
||||||
Write-Error 'Wix Util extension missing. Please install it with: wix.exe extension add WixToolset.Util.wixext/6.0.0 --global)'
|
Write-Error 'Wix Util extension missing. Please install it with: wix.exe extension add WixToolset.Util.wixext/6.0.2 --global)'
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
if ($wixExtensions -notmatch 'WixToolset.BootstrapperApplications.wixext') {
|
if ($wixExtensions -notmatch 'WixToolset.BootstrapperApplications.wixext') {
|
||||||
Write-Error 'Wix Bootstrapper extension missing. Please install it with: wix.exe extension add WixToolset.BootstrapperApplications.wixext/6.0.0 --global)'
|
Write-Error 'Wix Bootstrapper extension missing. Please install it with: wix.exe extension add WixToolset.BootstrapperApplications.wixext/6.0.2 --global)'
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,9 +93,9 @@ switch ($archName) {
|
|||||||
$jmodPaths = "$Env:JAVA_HOME/jmods"
|
$jmodPaths = "$Env:JAVA_HOME/jmods"
|
||||||
}
|
}
|
||||||
'x64' {
|
'x64' {
|
||||||
$javaFxVersion='25'
|
$javaFxVersion='25.0.2'
|
||||||
$javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip"
|
$javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip"
|
||||||
$javaFxJmodsSHA256 = 'c8eb9fd039b00e0020cf6c3db8ed7876bf3ee4d27860aa697a247b83b8296ae7'
|
$javaFxJmodsSHA256 = '33d878dfac85590c4d77c518ed413e512d34a8479d90132b230a7ddd173576b3'
|
||||||
$javaFxJmods = '.\resources\jfxJmods.zip'
|
$javaFxJmods = '.\resources\jfxJmods.zip'
|
||||||
|
|
||||||
if( !(Test-Path -Path $javaFxJmods) ) {
|
if( !(Test-Path -Path $javaFxJmods) ) {
|
||||||
@@ -155,7 +155,7 @@ $javaOptions = @(
|
|||||||
"--java-options", "-Djava.net.useSystemProxies=true"
|
"--java-options", "-Djava.net.useSystemProxies=true"
|
||||||
"--java-options", "-Dcryptomator.logDir=`"@{localappdata}/$AppName`""
|
"--java-options", "-Dcryptomator.logDir=`"@{localappdata}/$AppName`""
|
||||||
"--java-options", "-XX:ErrorFile=`"C:/cryptomator/cryptomator_crash.log`""
|
"--java-options", "-XX:ErrorFile=`"C:/cryptomator/cryptomator_crash.log`""
|
||||||
"--java-options", "-Dcryptomator.pluginDir=`"@{appdata}/$AppName/Plugins`""
|
"--java-options", "-Dcryptomator.adminConfigPath=`"C:/ProgramData/$AppName/config.properties`""
|
||||||
"--java-options", "-Dcryptomator.settingsPath=`"@{appdata}/$AppName/settings.json;@{userhome}/AppData/Roaming/$AppName/settings.json`""
|
"--java-options", "-Dcryptomator.settingsPath=`"@{appdata}/$AppName/settings.json;@{userhome}/AppData/Roaming/$AppName/settings.json`""
|
||||||
"--java-options", "-Dcryptomator.ipcSocketPath=`"@{localappdata}/$AppName/ipc.socket`""
|
"--java-options", "-Dcryptomator.ipcSocketPath=`"@{localappdata}/$AppName/ipc.socket`""
|
||||||
"--java-options", "-Dcryptomator.p12Path=`"@{appdata}/$AppName/key.p12;@{userhome}/AppData/Roaming/$AppName/key.p12`""
|
"--java-options", "-Dcryptomator.p12Path=`"@{appdata}/$AppName/key.p12;@{userhome}/AppData/Roaming/$AppName/key.p12`""
|
||||||
|
|||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
@echo off
|
||||||
|
:: Batch wrapper for PowerShell script to disable user configuration in Cryptomator
|
||||||
|
:: This is executed as a Custom Action during MSI installation
|
||||||
|
:: This file must be located in the INSTALLDIR
|
||||||
|
|
||||||
|
:: Change to INSTALLDIR
|
||||||
|
cd %~dp0
|
||||||
|
:: Execute the PowerShell script
|
||||||
|
powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -File ".\disableUserConfig.ps1"
|
||||||
|
|
||||||
|
:: Return the exit code from PowerShell
|
||||||
|
exit /b %ERRORLEVEL%
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
# PowerShell script to disable user configuration
|
||||||
|
# This script is executed as a Custom Action during MSI installation
|
||||||
|
# It deletes the file .package, effectively disabling user specific jpackage configuration.
|
||||||
|
# NOTE: This file must be located in the same directory as set in the MSI property INSTALLDIR
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
# Determine file path
|
||||||
|
$packageFile = Join-Path $PSScriptRoot 'app\.package'
|
||||||
|
|
||||||
|
#check if file exists
|
||||||
|
if (Test-Path -Path $packageFile) {
|
||||||
|
Write-Host "Deleting file: $packageFile"
|
||||||
|
Remove-Item -Path $packageFile -Force -ErrorAction Stop
|
||||||
|
} else {
|
||||||
|
Write-Host "File not found: $packageFile. Skipping deletion."
|
||||||
|
}
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "Error deleting package file: $_"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
Vendored
+30
-3
@@ -87,17 +87,37 @@
|
|||||||
|
|
||||||
<!-- Non-Opening ProgID -->
|
<!-- Non-Opening ProgID -->
|
||||||
<ns0:DirectoryRef Id="INSTALLDIR">
|
<ns0:DirectoryRef Id="INSTALLDIR">
|
||||||
<ns0:Component Bitness="always64" Id="nonStartingProgID" >
|
<ns0:Component Bitness="always64" Id="nonStartingProgID">
|
||||||
<ns0:File Id="IconFileForEncryptedData" KeyPath="yes" Source="$(env.JP_WIXWIZARD_RESOURCES)\$(var.IconFileEncryptedData)" Name="$(var.IconFileEncryptedData)"/>
|
<ns0:File Id="IconFileForEncryptedData" KeyPath="yes" Source="$(env.JP_WIXWIZARD_RESOURCES)\$(var.IconFileEncryptedData)" Name="$(var.IconFileEncryptedData)"/>
|
||||||
<ns0:ProgId Id="$(var.JpAppName).Encrypted.1" Description="$(var.JpAppName) Encrypted Data" Icon="IconFileForEncryptedData" IconIndex="0">
|
<ns0:ProgId Id="$(var.JpAppName).Encrypted.1" Description="$(var.JpAppName) Encrypted Data" Icon="IconFileForEncryptedData" IconIndex="0">
|
||||||
<ns0:Extension Id="c9r" Advertise="no" ContentType="$(var.ProgIdContentType)">
|
<ns0:Extension Id="c9r" Advertise="no" ContentType="$(var.ProgIdContentType)">
|
||||||
<ns0:MIME ContentType="$(var.ProgIdContentType)" Default="yes"/>
|
<ns0:MIME ContentType="$(var.ProgIdContentType)" Default="yes"/>
|
||||||
</ns0:Extension>
|
</ns0:Extension>
|
||||||
<ns0:Extension Id="c9s" Advertise="no" ContentType="$(var.ProgIdContentType)"/>
|
<ns0:Extension Id="c9s" Advertise="no" ContentType="$(var.ProgIdContentType)"/>
|
||||||
|
<ns0:Extension Id="c9u" Advertise="no" ContentType="$(var.ProgIdContentType)"/>
|
||||||
</ns0:ProgId>
|
</ns0:ProgId>
|
||||||
</ns0:Component>
|
</ns0:Component>
|
||||||
</ns0:DirectoryRef>
|
</ns0:DirectoryRef>
|
||||||
|
|
||||||
|
<ns0:StandardDirectory Id="CommonAppDataFolder">
|
||||||
|
<ns0:Directory Id="CryptomatorDesktopProgramData" Name="Cryptomator">
|
||||||
|
<ns0:Component Id="AdminConfigDir" Guid="c078b7da-ba6e-4069-a5ab-5c0f0f9856a0">
|
||||||
|
<ns0:CreateFolder>
|
||||||
|
<util:PermissionEx User="SYSTEM" GenericAll="yes"/>
|
||||||
|
<util:PermissionEx User="Administrators" GenericAll="yes"/>
|
||||||
|
<util:PermissionEx User="Users" GenericRead="yes" GenericExecute="yes"/>
|
||||||
|
</ns0:CreateFolder>
|
||||||
|
</ns0:Component>
|
||||||
|
<ns0:Component Id="AdminConfigFile" NeverOverwrite="yes" Permanent="yes">
|
||||||
|
<ns0:File Id="EmptyAdminConfig" Source="$(env.JP_WIXWIZARD_RESOURCES)\..\..\common\cryptomator.config" Name="cryptomator.config" KeyPath="yes">
|
||||||
|
<util:PermissionEx User="SYSTEM" GenericAll="yes"/>
|
||||||
|
<util:PermissionEx User="Administrators" GenericAll="yes"/>
|
||||||
|
<util:PermissionEx User="Users" GenericRead="yes" GenericExecute="yes"/>
|
||||||
|
</ns0:File>
|
||||||
|
</ns0:Component>
|
||||||
|
</ns0:Directory>
|
||||||
|
</ns0:StandardDirectory>
|
||||||
|
|
||||||
<!-- Standard required root -->
|
<!-- Standard required root -->
|
||||||
|
|
||||||
<ns0:Feature Id="DefaultFeature" Title="!(loc.MainFeatureTitle)" Level="1">
|
<ns0:Feature Id="DefaultFeature" Title="!(loc.MainFeatureTitle)" Level="1">
|
||||||
@@ -105,7 +125,9 @@
|
|||||||
<ns0:ComponentGroupRef Id="Files"/>
|
<ns0:ComponentGroupRef Id="Files"/>
|
||||||
<ns0:ComponentGroupRef Id="FileAssociations"/>
|
<ns0:ComponentGroupRef Id="FileAssociations"/>
|
||||||
<!-- Ref to additional ProgIDs -->
|
<!-- Ref to additional ProgIDs -->
|
||||||
<ns0:ComponentRef Id="nonStartingProgID" />
|
<ns0:ComponentRef Id="nonStartingProgID"/>
|
||||||
|
<ns0:ComponentRef Id="AdminConfigDir"/>
|
||||||
|
<ns0:ComponentRef Id="AdminConfigFile"/>
|
||||||
</ns0:Feature>
|
</ns0:Feature>
|
||||||
|
|
||||||
<ns0:CustomAction Id="JpSetARPINSTALLLOCATION" Property="ARPINSTALLLOCATION" Value="[INSTALLDIR]" />
|
<ns0:CustomAction Id="JpSetARPINSTALLLOCATION" Property="ARPINSTALLLOCATION" Value="[INSTALLDIR]" />
|
||||||
@@ -131,6 +153,10 @@
|
|||||||
<!-- Property for controlling update check behavior (can be set via command line) -->
|
<!-- Property for controlling update check behavior (can be set via command line) -->
|
||||||
<ns0:Property Id="DISABLEUPDATECHECK" Secure="yes" />
|
<ns0:Property Id="DISABLEUPDATECHECK" Secure="yes" />
|
||||||
|
|
||||||
|
<!-- Disable user config -->
|
||||||
|
<ns0:SetProperty Id="DisableUserConfig" Value=""[INSTALLDIR]disableUserConfig.bat"" Sequence="execute" Before="DisableUserConfig" />
|
||||||
|
<ns0:CustomAction Id="DisableUserConfig" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/>
|
||||||
|
|
||||||
<!-- WebDAV patches -->
|
<!-- WebDAV patches -->
|
||||||
<ns0:SetProperty Id="PatchWebDAV" Value=""[INSTALLDIR]patchWebDAV.bat" "$(var.LoopbackAlias)"" Sequence="execute" Before="PatchWebDAV" />
|
<ns0:SetProperty Id="PatchWebDAV" Value=""[INSTALLDIR]patchWebDAV.bat" "$(var.LoopbackAlias)"" Sequence="execute" Before="PatchWebDAV" />
|
||||||
<ns0:CustomAction Id="PatchWebDAV" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/>
|
<ns0:CustomAction Id="PatchWebDAV" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/>
|
||||||
@@ -187,9 +213,10 @@
|
|||||||
<ns0:Custom Action="FailOnRunningApp" After="Wix4CloseApplications_$(sys.BUILDARCHSHORT)" Condition="FOUNDRUNNINGAPP" />
|
<ns0:Custom Action="FailOnRunningApp" After="Wix4CloseApplications_$(sys.BUILDARCHSHORT)" Condition="FOUNDRUNNINGAPP" />
|
||||||
|
|
||||||
<ns0:RemoveExistingProducts After="InstallValidate"/> <!-- Moved from CostInitialize, due to Wix4CloseApplications_* -->
|
<ns0:RemoveExistingProducts After="InstallValidate"/> <!-- Moved from CostInitialize, due to Wix4CloseApplications_* -->
|
||||||
|
<ns0:Custom Action="DisableUserConfig" After="InstallFiles" Condition="NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
|
||||||
<!-- Skip action on uninstall -->
|
<!-- Skip action on uninstall -->
|
||||||
<!-- TODO: don't skip action, but remove cryptomator alias from hosts file -->
|
<!-- TODO: don't skip action, but remove cryptomator alias from hosts file -->
|
||||||
<ns0:Custom Action="PatchWebDAV" After="InstallFiles" Condition="NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
|
<ns0:Custom Action="PatchWebDAV" After="DisableUserConfig" Condition="NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
|
||||||
<!-- Configure update check setting if property is provided -->
|
<!-- Configure update check setting if property is provided -->
|
||||||
<ns0:Custom Action="PatchUpdateCheck" After="PatchWebDAV" Condition="DISABLEUPDATECHECK AND NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
|
<ns0:Custom Action="PatchUpdateCheck" After="PatchWebDAV" Condition="DISABLEUPDATECHECK AND NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
|
||||||
</ns0:InstallExecuteSequence>
|
</ns0:InstallExecuteSequence>
|
||||||
|
|||||||
@@ -33,23 +33,25 @@
|
|||||||
<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.9.0</cryptomator.cryptofs.version>
|
<cryptomator.cryptofs.version>2.10.0-beta3</cryptomator.cryptofs.version>
|
||||||
|
<cryptomator.cryptolib.version>2.2.2</cryptomator.cryptolib.version>
|
||||||
<cryptomator.integrations.version>1.8.0-beta1</cryptomator.integrations.version>
|
<cryptomator.integrations.version>1.8.0-beta1</cryptomator.integrations.version>
|
||||||
<cryptomator.integrations.win.version>1.5.1</cryptomator.integrations.win.version>
|
<cryptomator.integrations.win.version>1.6.0</cryptomator.integrations.win.version>
|
||||||
<cryptomator.integrations.mac.version>1.5.0-beta2</cryptomator.integrations.mac.version>
|
<cryptomator.integrations.mac.version>1.5.0-beta3</cryptomator.integrations.mac.version>
|
||||||
<cryptomator.integrations.linux.version>1.7.0-beta2</cryptomator.integrations.linux.version>
|
<cryptomator.integrations.linux.version>1.7.0-beta4</cryptomator.integrations.linux.version>
|
||||||
<cryptomator.fuse.version>5.1.0</cryptomator.fuse.version>
|
<cryptomator.fuse.version>6.0.0</cryptomator.fuse.version>
|
||||||
<cryptomator.webdav.version>3.0.0</cryptomator.webdav.version>
|
<cryptomator.webdav.version>3.0.1</cryptomator.webdav.version>
|
||||||
|
|
||||||
<!-- 3rd party dependencies -->
|
<!-- 3rd party dependencies -->
|
||||||
<commons-lang3.version>3.19.0</commons-lang3.version>
|
<caffeine.version>3.2.3</caffeine.version>
|
||||||
<dagger.version>2.57.2</dagger.version>
|
<commons-lang3.version>3.20.0</commons-lang3.version>
|
||||||
|
<dagger.version>2.59.1</dagger.version>
|
||||||
<easybind.version>2.2</easybind.version>
|
<easybind.version>2.2</easybind.version>
|
||||||
<jackson.version>2.20.0</jackson.version>
|
<jackson.version>2.21.0</jackson.version>
|
||||||
<javafx.version>25</javafx.version>
|
<javafx.version>25.0.2</javafx.version>
|
||||||
<jwt.version>4.5.0</jwt.version>
|
<jwt.version>4.5.0</jwt.version>
|
||||||
<nimbus-jose.version>10.5</nimbus-jose.version>
|
<nimbus-jose.version>10.5</nimbus-jose.version>
|
||||||
<logback.version>1.5.19</logback.version>
|
<logback.version>1.5.31</logback.version>
|
||||||
<slf4j.version>2.0.17</slf4j.version>
|
<slf4j.version>2.0.17</slf4j.version>
|
||||||
<tinyoauth2.version>0.8.1</tinyoauth2.version>
|
<tinyoauth2.version>0.8.1</tinyoauth2.version>
|
||||||
<zxcvbn.version>1.9.0</zxcvbn.version>
|
<zxcvbn.version>1.9.0</zxcvbn.version>
|
||||||
@@ -94,7 +96,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>cryptolib</artifactId>
|
<artifactId>cryptolib</artifactId>
|
||||||
<version>2.2.1</version>
|
<version>${cryptomator.cryptolib.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
@@ -228,7 +230,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
<artifactId>caffeine</artifactId>
|
<artifactId>caffeine</artifactId>
|
||||||
<version>3.2.2</version>
|
<version>${caffeine.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- JUnit / Mockito / Hamcrest -->
|
<!-- JUnit / Mockito / Hamcrest -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -330,7 +332,6 @@
|
|||||||
</annotationProcessorPaths>
|
</annotationProcessorPaths>
|
||||||
<compilerArgs>
|
<compilerArgs>
|
||||||
<arg>-Adagger.fastInit=enabled</arg>
|
<arg>-Adagger.fastInit=enabled</arg>
|
||||||
<arg>-Adagger.formatGeneratedSource=enabled</arg>
|
|
||||||
</compilerArgs>
|
</compilerArgs>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import ch.qos.logback.classic.spi.Configurator;
|
import ch.qos.logback.classic.spi.Configurator;
|
||||||
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
|
|
||||||
import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider;
|
||||||
import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider;
|
||||||
import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider;
|
||||||
@@ -14,11 +13,14 @@ import org.cryptomator.common.locationpresets.OneDriveLinuxLocationPresetsProvid
|
|||||||
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
|
||||||
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
|
||||||
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
|
||||||
import org.cryptomator.networking.SSLContextWithMacKeychain;
|
|
||||||
import org.cryptomator.networking.SSLContextProvider;
|
|
||||||
import org.cryptomator.networking.SSLContextWithWindowsCertStore;
|
|
||||||
import org.cryptomator.integrations.tray.TrayMenuController;
|
import org.cryptomator.integrations.tray.TrayMenuController;
|
||||||
|
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||||
import org.cryptomator.logging.LogbackConfiguratorFactory;
|
import org.cryptomator.logging.LogbackConfiguratorFactory;
|
||||||
|
import org.cryptomator.networking.SSLContextProvider;
|
||||||
|
import org.cryptomator.networking.SSLContextWithMacKeychain;
|
||||||
|
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
|
||||||
|
import org.cryptomator.networking.SSLContextWithWindowsCertStore;
|
||||||
|
import org.cryptomator.ui.fxapp.JfxUiAppearanceProvider;
|
||||||
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
||||||
|
|
||||||
open module org.cryptomator.desktop {
|
open module org.cryptomator.desktop {
|
||||||
@@ -61,6 +63,7 @@ open module org.cryptomator.desktop {
|
|||||||
uses SSLContextProvider;
|
uses SSLContextProvider;
|
||||||
uses org.cryptomator.event.NotificationHandler;
|
uses org.cryptomator.event.NotificationHandler;
|
||||||
|
|
||||||
|
provides UiAppearanceProvider with JfxUiAppearanceProvider;
|
||||||
provides TrayMenuController with AwtTrayMenuController;
|
provides TrayMenuController with AwtTrayMenuController;
|
||||||
provides Configurator with LogbackConfiguratorFactory;
|
provides Configurator with LogbackConfiguratorFactory;
|
||||||
provides SSLContextProvider with SSLContextWithWindowsCertStore, SSLContextWithMacKeychain, SSLContextWithPKCS12TrustStore;
|
provides SSLContextProvider with SSLContextWithWindowsCertStore, SSLContextWithMacKeychain, SSLContextWithPKCS12TrustStore;
|
||||||
|
|||||||
@@ -13,5 +13,7 @@ public interface Constants {
|
|||||||
String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator";
|
String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator";
|
||||||
URI DEFAULT_KEY_ID = URI.create(MasterkeyFileLoadingStrategy.SCHEME + ":" + MASTERKEY_FILENAME);
|
URI DEFAULT_KEY_ID = URI.create(MasterkeyFileLoadingStrategy.SCHEME + ":" + MASTERKEY_FILENAME);
|
||||||
byte[] PEPPER = new byte[0];
|
byte[] PEPPER = new byte[0];
|
||||||
|
// Separator used to concatenate Hub username and device name in the filesystem owner identifier.
|
||||||
|
String HUB_USER_DEVICE_SEPARATOR = "&";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,160 +0,0 @@
|
|||||||
package org.cryptomator.common;
|
|
||||||
|
|
||||||
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 org.cryptomator.event.VaultEvent;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import javafx.beans.InvalidationListener;
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.MapChangeListener;
|
|
||||||
import javafx.collections.ObservableMap;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map containing {@link VaultEvent}s.
|
|
||||||
* The map is keyed by the ciphertext path of the affected resource _and_ the {@link FilesystemEvent}s class in order to group same events
|
|
||||||
* <p>
|
|
||||||
* Use {@link EventMap#put(VaultEvent)} to add an element and {@link EventMap#remove(VaultEvent)} to remove it.
|
|
||||||
* <p>
|
|
||||||
* The map is size restricted to {@value MAX_SIZE} elements. If a _new_ element (i.e. not already present) is added, the least recently added is removed.
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class EventMap implements ObservableMap<EventMap.EventKey, VaultEvent> {
|
|
||||||
|
|
||||||
private static final int MAX_SIZE = 300;
|
|
||||||
|
|
||||||
public record EventKey(Path ciphertextPath, Class<? extends FilesystemEvent> c) {}
|
|
||||||
|
|
||||||
private final ObservableMap<EventMap.EventKey, VaultEvent> delegate;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public EventMap() {
|
|
||||||
delegate = FXCollections.observableHashMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addListener(MapChangeListener<? super EventKey, ? super VaultEvent> mapChangeListener) {
|
|
||||||
delegate.addListener(mapChangeListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeListener(MapChangeListener<? super EventKey, ? super VaultEvent> mapChangeListener) {
|
|
||||||
delegate.removeListener(mapChangeListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return delegate.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return delegate.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsKey(Object key) {
|
|
||||||
return delegate.containsKey(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsValue(Object value) {
|
|
||||||
return delegate.containsValue(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public VaultEvent get(Object key) {
|
|
||||||
return delegate.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable VaultEvent put(EventKey key, VaultEvent value) {
|
|
||||||
return delegate.put(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public VaultEvent remove(Object key) {
|
|
||||||
return delegate.remove(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void putAll(@NotNull Map<? extends EventKey, ? extends VaultEvent> m) {
|
|
||||||
delegate.putAll(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
delegate.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Set<EventKey> keySet() {
|
|
||||||
return delegate.keySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Collection<VaultEvent> values() {
|
|
||||||
return delegate.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Set<Entry<EventKey, VaultEvent>> entrySet() {
|
|
||||||
return delegate.entrySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addListener(InvalidationListener invalidationListener) {
|
|
||||||
delegate.addListener(invalidationListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeListener(InvalidationListener invalidationListener) {
|
|
||||||
delegate.removeListener(invalidationListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void put(VaultEvent e) {
|
|
||||||
//compute key
|
|
||||||
var key = computeKey(e.actualEvent());
|
|
||||||
//if-else
|
|
||||||
var nullOrEntry = delegate.get(key);
|
|
||||||
if (nullOrEntry == null) {
|
|
||||||
if (size() == MAX_SIZE) {
|
|
||||||
delegate.entrySet().stream() //
|
|
||||||
.min(Comparator.comparing(entry -> entry.getValue().actualEvent().getTimestamp())) //
|
|
||||||
.ifPresent(oldestEntry -> delegate.remove(oldestEntry.getKey()));
|
|
||||||
}
|
|
||||||
delegate.put(key, e);
|
|
||||||
} else {
|
|
||||||
delegate.put(key, nullOrEntry.incrementCount(e.actualEvent()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized VaultEvent remove(VaultEvent similar) {
|
|
||||||
//compute key
|
|
||||||
var key = computeKey(similar.actualEvent());
|
|
||||||
return this.remove(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
private EventKey computeKey(FilesystemEvent e) {
|
|
||||||
var p = switch (e) {
|
|
||||||
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 EventKey(p, e.getClass());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.cryptomator.common;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface marking a class to be used in {@link org.cryptomator.cryptofs.CryptoFileSystemProperties.Builder#withOwnerGetter(Supplier)}.
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface FilesystemOwnerSupplier {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the filesystem owner.
|
||||||
|
*
|
||||||
|
* @return the filesystem owner
|
||||||
|
*/
|
||||||
|
String getOwner();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -167,6 +167,7 @@ public class Mounter {
|
|||||||
usedMountServices.add(mountService);
|
usedMountServices.add(mountService);
|
||||||
|
|
||||||
var builder = mountService.forFileSystem(cryptoFsRoot);
|
var builder = mountService.forFileSystem(cryptoFsRoot);
|
||||||
|
LOG.debug("Using mount service {} for mounting vault {}", mountService.getClass().getName(), vaultSettings.displayName);
|
||||||
var internal = new SettledMounter(mountService, builder, vaultSettings); // FIXME: no need for an inner class
|
var internal = new SettledMounter(mountService, builder, vaultSettings); // FIXME: no need for an inner class
|
||||||
var cleanup = internal.prepare();
|
var cleanup = internal.prepare();
|
||||||
return new MountHandle(builder.mount(), mountService.hasCapability(UNMOUNT_FORCED), cleanup);
|
return new MountHandle(builder.mount(), mountService.hasCapability(UNMOUNT_FORCED), cleanup);
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ public final class MasterkeyService {
|
|||||||
Optional<Path> c9rFile = paths //
|
Optional<Path> c9rFile = paths //
|
||||||
.filter(p -> p.toString().endsWith(".c9r")) //
|
.filter(p -> p.toString().endsWith(".c9r")) //
|
||||||
.filter(p -> !p.endsWith("dir.c9r")) //
|
.filter(p -> !p.endsWith("dir.c9r")) //
|
||||||
|
.filter(Files::isRegularFile) //
|
||||||
.findFirst();
|
.findFirst();
|
||||||
if (c9rFile.isEmpty()) {
|
if (c9rFile.isEmpty()) {
|
||||||
LOG.info("Unable to detect Crypto scheme: No *.c9r file found in {}", vaultPath);
|
LOG.info("Unable to detect Crypto scheme: No *.c9r file found in {}", vaultPath);
|
||||||
|
|||||||
@@ -153,11 +153,6 @@ public class Settings {
|
|||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private void migrateLegacySettings(SettingsJson json) {
|
private void migrateLegacySettings(SettingsJson json) {
|
||||||
// migrate renamed keychainAccess
|
|
||||||
if(this.keychainProvider.getValueSafe().equals("org.cryptomator.linux.keychain.SecretServiceKeychainAccess")) {
|
|
||||||
this.keychainProvider.setValue("org.cryptomator.linux.keychain.GnomeKeyringKeychainAccess");
|
|
||||||
}
|
|
||||||
|
|
||||||
// implicit migration of 1.6.x legacy setting "preferredVolumeImpl":
|
// implicit migration of 1.6.x legacy setting "preferredVolumeImpl":
|
||||||
if (this.mountService.get() == null && json.preferredVolumeImpl != null) {
|
if (this.mountService.get() == null && json.preferredVolumeImpl != null) {
|
||||||
this.mountService.set(switch (json.preferredVolumeImpl) {
|
this.mountService.set(switch (json.preferredVolumeImpl) {
|
||||||
|
|||||||
@@ -10,14 +10,6 @@ public enum UiTheme {
|
|||||||
DARK("preferences.interface.theme.dark"), //
|
DARK("preferences.interface.theme.dark"), //
|
||||||
AUTOMATIC("preferences.interface.theme.automatic");
|
AUTOMATIC("preferences.interface.theme.automatic");
|
||||||
|
|
||||||
public static UiTheme[] applicableValues() {
|
|
||||||
if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_WINDOWS) {
|
|
||||||
return values();
|
|
||||||
} else {
|
|
||||||
return new UiTheme[]{LIGHT, DARK};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String displayName;
|
private final String displayName;
|
||||||
|
|
||||||
UiTheme(String displayName) {
|
UiTheme(String displayName) {
|
||||||
|
|||||||
@@ -10,7 +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.FilesystemOwnerSupplier;
|
||||||
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;
|
||||||
@@ -23,6 +23,8 @@ 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;
|
||||||
|
import org.cryptomator.event.FileSystemEventAggregator;
|
||||||
|
import org.cryptomator.event.NotificationManager;
|
||||||
import org.cryptomator.integrations.mount.MountFailedException;
|
import org.cryptomator.integrations.mount.MountFailedException;
|
||||||
import org.cryptomator.integrations.mount.Mountpoint;
|
import org.cryptomator.integrations.mount.Mountpoint;
|
||||||
import org.cryptomator.integrations.mount.UnmountFailedException;
|
import org.cryptomator.integrations.mount.UnmountFailedException;
|
||||||
@@ -78,6 +80,7 @@ public class Vault {
|
|||||||
private final Mounter mounter;
|
private final Mounter mounter;
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
private final FileSystemEventAggregator fileSystemEventAggregator;
|
private final FileSystemEventAggregator fileSystemEventAggregator;
|
||||||
|
private final NotificationManager notificationManager;
|
||||||
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);
|
||||||
@@ -90,7 +93,8 @@ public class Vault {
|
|||||||
@Named("lastKnownException") ObjectProperty<Exception> lastKnownException, //
|
@Named("lastKnownException") ObjectProperty<Exception> lastKnownException, //
|
||||||
VaultStats stats, //
|
VaultStats stats, //
|
||||||
Mounter mounter, Settings settings, //
|
Mounter mounter, Settings settings, //
|
||||||
FileSystemEventAggregator fileSystemEventAggregator) {
|
FileSystemEventAggregator fileSystemEventAggregator, //
|
||||||
|
NotificationManager notificationManager) {
|
||||||
this.vaultSettings = vaultSettings;
|
this.vaultSettings = vaultSettings;
|
||||||
this.configCache = configCache;
|
this.configCache = configCache;
|
||||||
this.cryptoFileSystem = cryptoFileSystem;
|
this.cryptoFileSystem = cryptoFileSystem;
|
||||||
@@ -109,6 +113,7 @@ public class Vault {
|
|||||||
this.mounter = mounter;
|
this.mounter = mounter;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.fileSystemEventAggregator = fileSystemEventAggregator;
|
this.fileSystemEventAggregator = fileSystemEventAggregator;
|
||||||
|
this.notificationManager = notificationManager;
|
||||||
this.showingStats = new SimpleBooleanProperty(false);
|
this.showingStats = new SimpleBooleanProperty(false);
|
||||||
this.quickAccessEntry = new AtomicReference<>(null);
|
this.quickAccessEntry = new AtomicReference<>(null);
|
||||||
}
|
}
|
||||||
@@ -145,14 +150,17 @@ public class Vault {
|
|||||||
LOG.warn("Limiting cleartext filename length on this device to {}.", vaultSettings.maxCleartextFilenameLength.get());
|
LOG.warn("Limiting cleartext filename length on this device to {}.", vaultSettings.maxCleartextFilenameLength.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
|
var fsPropsBuilder = CryptoFileSystemProperties.cryptoFileSystemProperties() //
|
||||||
.withKeyLoader(keyLoader) //
|
.withKeyLoader(keyLoader) //
|
||||||
.withFlags(flags) //
|
.withFlags(flags) //
|
||||||
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength.get()) //
|
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength.get()) //
|
||||||
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
|
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
|
||||||
.withFilesystemEventConsumer(this::consumeVaultEvent) //
|
.withFilesystemEventConsumer(this::consumeVaultEvent);
|
||||||
.build();
|
if (keyLoader instanceof FilesystemOwnerSupplier oo) {
|
||||||
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
|
fsPropsBuilder.withOwnerGetter(oo::getOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CryptoFileSystemProvider.newFileSystem(getPath(), fsPropsBuilder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void destroyCryptoFileSystem() {
|
private void destroyCryptoFileSystem() {
|
||||||
@@ -262,6 +270,7 @@ public class Vault {
|
|||||||
|
|
||||||
private void consumeVaultEvent(FilesystemEvent e) {
|
private void consumeVaultEvent(FilesystemEvent e) {
|
||||||
fileSystemEventAggregator.put(this, e);
|
fileSystemEventAggregator.put(this, e);
|
||||||
|
notificationManager.offer(this, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ******************************************************************************
|
// ******************************************************************************
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.cryptomator.cryptofs.event.BrokenFileNodeEvent;
|
|||||||
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
|
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
|
||||||
import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
|
import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
|
||||||
import org.cryptomator.cryptofs.event.DecryptionFailedEvent;
|
import org.cryptomator.cryptofs.event.DecryptionFailedEvent;
|
||||||
|
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
|
||||||
import org.cryptomator.cryptofs.event.FilesystemEvent;
|
import org.cryptomator.cryptofs.event.FilesystemEvent;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -101,6 +102,7 @@ public class FileSystemEventAggregator {
|
|||||||
case ConflictResolutionFailedEvent(_, _, Path conflictingCiphertext, _) -> conflictingCiphertext;
|
case ConflictResolutionFailedEvent(_, _, Path conflictingCiphertext, _) -> conflictingCiphertext;
|
||||||
case BrokenDirFileEvent(_, Path ciphertext) -> ciphertext;
|
case BrokenDirFileEvent(_, Path ciphertext) -> ciphertext;
|
||||||
case BrokenFileNodeEvent(_, _, Path ciphertext) -> ciphertext;
|
case BrokenFileNodeEvent(_, _, Path ciphertext) -> ciphertext;
|
||||||
|
case FileIsInUseEvent(_, _, Path ciphertext, _, _, _) -> ciphertext;
|
||||||
};
|
};
|
||||||
return new FSEventBucket(v, p, event.getClass());
|
return new FSEventBucket(v, p, event.getClass());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package org.cryptomator.event;
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
|
||||||
|
import org.cryptomator.cryptofs.event.FilesystemEvent;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager for notifications.
|
||||||
|
* <p>
|
||||||
|
* To add (filesystem) events, use method {@link #offer(Vault, FilesystemEvent)}. If the input event is eligible, it is added to an internal queue.
|
||||||
|
* An event is eligible, if
|
||||||
|
* <ul>
|
||||||
|
* <li>the event should trigger a notification and</li>
|
||||||
|
* <li>it is not added within the last {@value DEBOUNCE_THRESHOLD_SECONDS} seconds</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @see org.cryptomator.ui.fxapp.FxNotificationManager
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class NotificationManager {
|
||||||
|
|
||||||
|
private static final int DEBOUNCE_THRESHOLD_SECONDS = 5;
|
||||||
|
|
||||||
|
private final Cache<FSEventBucket, FilesystemEvent> debounceCache;
|
||||||
|
private final ConcurrentLinkedQueue<VaultEvent> pendingEvents;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public NotificationManager() {
|
||||||
|
debounceCache = Caffeine.newBuilder().expireAfterWrite(Duration.ofSeconds(DEBOUNCE_THRESHOLD_SECONDS)).build();
|
||||||
|
pendingEvents = new ConcurrentLinkedQueue<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offers the given filesystem event to the notification manager.
|
||||||
|
*
|
||||||
|
* @param v The vault where the filesystem event happened
|
||||||
|
* @param e the actual filesystem event
|
||||||
|
* @return {@code true} if the filesystem event is accepted, otherwise {@code false}.
|
||||||
|
*/
|
||||||
|
public boolean offer(Vault v, FilesystemEvent e) {
|
||||||
|
return switch (e) {
|
||||||
|
case FileIsInUseEvent fiiue -> addEvent(v, fiiue.ciphertextPath(), fiiue);
|
||||||
|
default -> false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean addEvent(Vault v, Path keyPath, FilesystemEvent e) {
|
||||||
|
var key = new FSEventBucket(v, keyPath, e.getClass());
|
||||||
|
var isAdded = new AtomicBoolean(false);
|
||||||
|
debounceCache.asMap().computeIfAbsent(key, _ -> {
|
||||||
|
synchronized (this) {
|
||||||
|
pendingEvents.add(new VaultEvent(v, e));
|
||||||
|
isAdded.set(true);
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
});
|
||||||
|
return isAdded.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds all events to the target list and clears afterward the pending-event-queue
|
||||||
|
*
|
||||||
|
* @param target list where the filesystem events are copied to
|
||||||
|
* @return {@code true}, if elements were copied
|
||||||
|
*/
|
||||||
|
public boolean appendToAndClear(List<VaultEvent> target) {
|
||||||
|
//it is not clear, if addAll iterates thread-safe over the pendingEvents
|
||||||
|
//hence we synchronize moving (copy then clear) and adding-single-element operations
|
||||||
|
synchronized (this) {
|
||||||
|
var result = target.addAll(pendingEvents);
|
||||||
|
pendingEvents.clear();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,25 +3,6 @@ package org.cryptomator.event;
|
|||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.cryptofs.event.FilesystemEvent;
|
import org.cryptomator.cryptofs.event.FilesystemEvent;
|
||||||
|
|
||||||
import java.time.Instant;
|
public record VaultEvent(Vault v, FilesystemEvent actualEvent) {
|
||||||
|
|
||||||
public record VaultEvent(Vault v, FilesystemEvent actualEvent, int count) implements Comparable<VaultEvent> {
|
|
||||||
|
|
||||||
public VaultEvent(Vault v, FilesystemEvent actualEvent) {
|
|
||||||
this(v, actualEvent, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(VaultEvent other) {
|
|
||||||
var timeResult = actualEvent.getTimestamp().compareTo(other.actualEvent().getTimestamp());
|
|
||||||
if(timeResult != 0) {
|
|
||||||
return timeResult;
|
|
||||||
} else {
|
|
||||||
return this.equals(other) ? 0 : this.actualEvent.getClass().getName().compareTo(other.actualEvent.getClass().getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public VaultEvent incrementCount(FilesystemEvent update) {
|
|
||||||
return new VaultEvent(v, update, count+1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.NoSuchFileException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory to generate admin properties.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Admin properties are {@link Properties} using system properties as defaults, but allow overwriting a specific set of properties with an external config file.
|
||||||
|
* Those properties are created by calling {@link #create()}. The method first reads system property {@value #ADMIN_PROP_FILE_KEY}. If it contains a path to a valid properties file, all overridable properties from the file are loaded into the returned admin properties.
|
||||||
|
* <p>
|
||||||
|
* The overridable properties are:
|
||||||
|
* <ul>
|
||||||
|
* <li>cryptomator.logDir</li>
|
||||||
|
* <li>cryptomator.pluginDir</li>
|
||||||
|
* <li>cryptomator.p12Path</li>
|
||||||
|
* <li>cryptomator.mountPointsDir</li>
|
||||||
|
* <li>cryptomator.disableUpdateCheck</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @see Properties
|
||||||
|
* @see System#getProperties()
|
||||||
|
*/
|
||||||
|
class AdminPropertiesFactory {
|
||||||
|
|
||||||
|
private static final Logger LOG = EventualLogger.INSTANCE;
|
||||||
|
private static final long MAX_CONFIG_SIZE_BYTES = 8192;
|
||||||
|
private static final String ADMIN_PROP_FILE_KEY = "cryptomator.adminConfigPath";
|
||||||
|
private static final Set<String> ALLOWED_OVERRIDES = Set.of( //
|
||||||
|
"cryptomator.logDir", //
|
||||||
|
"cryptomator.pluginDir", //
|
||||||
|
"cryptomator.p12Path", //
|
||||||
|
"cryptomator.mountPointsDir", //
|
||||||
|
"cryptomator.disableUpdateCheck");
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new {@link Properties} containing overridable properties from the admin config.
|
||||||
|
* <p>
|
||||||
|
* The returned properties object uses as default the {@link System} properties.
|
||||||
|
* For a list of overridable properties, see {@link AdminPropertiesFactory}
|
||||||
|
*
|
||||||
|
* @return {@link Properties} containing overridable properties from the admin config and defaulting to system properties.
|
||||||
|
*/
|
||||||
|
static Properties create() {
|
||||||
|
var systemProps = System.getProperties();
|
||||||
|
var adminProps = new Properties(systemProps);
|
||||||
|
|
||||||
|
final String adminCfgPath = System.getProperty(ADMIN_PROP_FILE_KEY);
|
||||||
|
if (adminCfgPath == null) {
|
||||||
|
LOG.debug("Admin config property is not defined. Skipping.");
|
||||||
|
return adminProps;
|
||||||
|
}
|
||||||
|
var propsFromFile = loadPropertiesFromFile(Path.of(adminCfgPath));
|
||||||
|
|
||||||
|
for (var key : propsFromFile.stringPropertyNames()) {
|
||||||
|
if (ALLOWED_OVERRIDES.contains(key)) {
|
||||||
|
var value = propsFromFile.getProperty(key);
|
||||||
|
LOG.info("Overwriting {} with value {} from admin config.", key, value);
|
||||||
|
adminProps.setProperty(key, value);
|
||||||
|
} else {
|
||||||
|
LOG.debug("Property {} in admin config is not supported for override.", key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return adminProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
//visible for testing
|
||||||
|
static Properties loadPropertiesFromFile(Path adminPropertiesPath) {
|
||||||
|
var adminProps = new Properties();
|
||||||
|
try (FileChannel ch = FileChannel.open(adminPropertiesPath, StandardOpenOption.READ); //
|
||||||
|
Reader reader = Channels.newReader(ch, StandardCharsets.UTF_8)) {
|
||||||
|
if (ch.size() > MAX_CONFIG_SIZE_BYTES) {
|
||||||
|
throw new IOException("Config file %s exceeds maximum size of %d".formatted(adminPropertiesPath, MAX_CONFIG_SIZE_BYTES));
|
||||||
|
}
|
||||||
|
adminProps.load(reader);
|
||||||
|
} catch (NoSuchFileException _) {
|
||||||
|
//NO-OP
|
||||||
|
LOG.debug("No admin properties found at {}.", adminPropertiesPath);
|
||||||
|
} catch (IOException | IllegalArgumentException e) {
|
||||||
|
LOG.warn("Failed to read administrative properties from {}. Returning empty properties.", adminPropertiesPath, e);
|
||||||
|
}
|
||||||
|
return adminProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -11,9 +11,9 @@ import org.apache.commons.lang3.SystemUtils;
|
|||||||
import org.cryptomator.common.Environment;
|
import org.cryptomator.common.Environment;
|
||||||
import org.cryptomator.common.ShutdownHook;
|
import org.cryptomator.common.ShutdownHook;
|
||||||
import org.cryptomator.common.SubstitutingProperties;
|
import org.cryptomator.common.SubstitutingProperties;
|
||||||
import org.cryptomator.networking.SSLContextProvider;
|
|
||||||
import org.cryptomator.ipc.IpcCommunicator;
|
import org.cryptomator.ipc.IpcCommunicator;
|
||||||
import org.cryptomator.logging.DebugMode;
|
import org.cryptomator.logging.DebugMode;
|
||||||
|
import org.cryptomator.networking.SSLContextProvider;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -35,7 +35,8 @@ public class Cryptomator {
|
|||||||
private static final long STARTUP_TIME = System.currentTimeMillis();
|
private static final long STARTUP_TIME = System.currentTimeMillis();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
var lazyProcessedProps = new SubstitutingProperties(System.getProperties(), System.getenv());
|
var adminProps = AdminPropertiesFactory.create();
|
||||||
|
var lazyProcessedProps = new SubstitutingProperties(adminProps, System.getenv());
|
||||||
System.setProperties(lazyProcessedProps);
|
System.setProperties(lazyProcessedProps);
|
||||||
CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
|
CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
|
||||||
LOG = LoggerFactory.getLogger(Cryptomator.class);
|
LOG = LoggerFactory.getLogger(Cryptomator.class);
|
||||||
@@ -64,6 +65,7 @@ public class Cryptomator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
EventualLogger.INSTANCE.drainTo(LOG);
|
||||||
var printVersion = Optional.ofNullable(args) //
|
var printVersion = Optional.ofNullable(args) //
|
||||||
.stream() //Streams either one element (the args-array) or zero elements
|
.stream() //Streams either one element (the args-array) or zero elements
|
||||||
.flatMap(Arrays::stream) //
|
.flatMap(Arrays::stream) //
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import dagger.Module;
|
|||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import org.cryptomator.integrations.autostart.AutoStartProvider;
|
import org.cryptomator.integrations.autostart.AutoStartProvider;
|
||||||
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
||||||
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
@@ -30,11 +29,6 @@ class CryptomatorModule {
|
|||||||
return new ArrayBlockingQueue<>(10);
|
return new ArrayBlockingQueue<>(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
static Optional<UiAppearanceProvider> provideAppearanceProvider() {
|
|
||||||
return UiAppearanceProvider.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.Marker;
|
||||||
|
import org.slf4j.event.DefaultLoggingEvent;
|
||||||
|
import org.slf4j.event.Level;
|
||||||
|
import org.slf4j.event.LoggingEvent;
|
||||||
|
import org.slf4j.helpers.AbstractLogger;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
|
class EventualLogger extends AbstractLogger {
|
||||||
|
|
||||||
|
static final EventualLogger INSTANCE = new EventualLogger();
|
||||||
|
|
||||||
|
private final Queue<LoggingEvent> bufferedEvents = new ArrayDeque<>();
|
||||||
|
|
||||||
|
private EventualLogger() {
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void drainTo(Logger gutter) {
|
||||||
|
for (var event : bufferedEvents) {
|
||||||
|
var builder = gutter.atLevel(event.getLevel()) //
|
||||||
|
.setCause(event.getThrowable()) //
|
||||||
|
.setMessage(event.getMessage());
|
||||||
|
Objects.requireNonNullElse(event.getArguments(), List.of()).forEach(builder::addArgument);
|
||||||
|
Objects.requireNonNullElse(event.getMarkers(), List.<Marker>of()).forEach(builder::addMarker);
|
||||||
|
builder.log();
|
||||||
|
}
|
||||||
|
bufferedEvents.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected synchronized void handleNormalizedLoggingCall(Level level, Marker marker, String messagePattern, Object[] arguments, Throwable throwable) {
|
||||||
|
var event = new DefaultLoggingEvent(level, this);
|
||||||
|
if (marker != null) {
|
||||||
|
event.addMarker(marker);
|
||||||
|
}
|
||||||
|
event.setMessage(messagePattern);
|
||||||
|
for (var arg : Objects.requireNonNullElse(arguments, new Object[]{})) {
|
||||||
|
event.addArgument(arg);
|
||||||
|
}
|
||||||
|
event.setThrowable(throwable);
|
||||||
|
bufferedEvents.add(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Unclear, unused and undocumented method of slf4j, see also https://github.com/qos-ch/slf4j/discussions/348
|
||||||
|
@Override
|
||||||
|
protected String getFullyQualifiedCallerName() {
|
||||||
|
return getClass().getCanonicalName();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTraceEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTraceEnabled(Marker marker) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDebugEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDebugEnabled(Marker marker) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInfoEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInfoEnabled(Marker marker) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWarnEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWarnEnabled(Marker marker) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isErrorEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isErrorEnabled(Marker marker) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,7 +59,7 @@ public class ChooseExistingVaultController implements FxController {
|
|||||||
this.vault = vault;
|
this.vault = vault;
|
||||||
this.vaultListManager = vaultListManager;
|
this.vaultListManager = vaultListManager;
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
this.screenshot = applicationStyle.appliedThemeProperty().map(this::selectScreenshot);
|
this.screenshot = applicationStyle.appliedAppThemeProperty().map(this::selectScreenshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Image selectScreenshot(Theme theme) {
|
private Image selectScreenshot(Theme theme) {
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ public enum FxmlFile {
|
|||||||
MIGRATION_RUN("/fxml/migration_run.fxml"), //
|
MIGRATION_RUN("/fxml/migration_run.fxml"), //
|
||||||
MIGRATION_START("/fxml/migration_start.fxml"), //
|
MIGRATION_START("/fxml/migration_start.fxml"), //
|
||||||
MIGRATION_SUCCESS("/fxml/migration_success.fxml"), //
|
MIGRATION_SUCCESS("/fxml/migration_success.fxml"), //
|
||||||
|
NOTIFICATION("/fxml/notification.fxml"), //
|
||||||
PREFERENCES("/fxml/preferences.fxml"), //
|
PREFERENCES("/fxml/preferences.fxml"), //
|
||||||
QUIT("/fxml/quit.fxml"), //
|
QUIT("/fxml/quit.fxml"), //
|
||||||
QUIT_FORCED("/fxml/quit_forced.fxml"), //
|
QUIT_FORCED("/fxml/quit_forced.fxml"), //
|
||||||
@@ -59,6 +60,7 @@ public enum FxmlFile {
|
|||||||
VAULT_STATISTICS("/fxml/stats.fxml"), //
|
VAULT_STATISTICS("/fxml/stats.fxml"), //
|
||||||
WRONGFILEALERT("/fxml/wrongfilealert.fxml");
|
WRONGFILEALERT("/fxml/wrongfilealert.fxml");
|
||||||
|
|
||||||
|
|
||||||
private final String ressourcePathString;
|
private final String ressourcePathString;
|
||||||
|
|
||||||
FxmlFile(String ressourcePathString) {
|
FxmlFile(String ressourcePathString) {
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package org.cryptomator.ui.common;
|
||||||
|
|
||||||
|
import javafx.stage.Screen;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class providing methods regarding the OS bar.
|
||||||
|
*/
|
||||||
|
public class SystemBarUtil {
|
||||||
|
|
||||||
|
public enum Placement {
|
||||||
|
/**
|
||||||
|
* OS Bar placed at the left screen edge
|
||||||
|
*/
|
||||||
|
LEFT,
|
||||||
|
/**
|
||||||
|
* OS Bar placed at the top screen edge
|
||||||
|
*/
|
||||||
|
TOP,
|
||||||
|
/**
|
||||||
|
* OS Bar placed at the right screen edge
|
||||||
|
*/
|
||||||
|
RIGHT,
|
||||||
|
/**
|
||||||
|
* OS Bar placed at the bottom screen edge
|
||||||
|
*/
|
||||||
|
BOTTOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the placement of the OS bar on the given screen.
|
||||||
|
* <p>
|
||||||
|
* <b>Assuming the OS bar fills one screen edge completely</b>,
|
||||||
|
* this method determines that screen edge by comparing the actual screen bounds with the visual ones.
|
||||||
|
* <p>
|
||||||
|
* If the screen does not have a system bar, the bottom placement is returned.
|
||||||
|
* If the screen does have multiple system bars, the first in following priority is returned:
|
||||||
|
* LEFT, TOP, RIGHT, BOTTOM.
|
||||||
|
*
|
||||||
|
* @param screen a {@link Screen} where an OS bar exists
|
||||||
|
* @return {@link Placement} indicating the screen edge.
|
||||||
|
*/
|
||||||
|
public static Placement getPlacementOfSystembar(Screen screen) {
|
||||||
|
var bounds = screen.getBounds();
|
||||||
|
var vBounds = screen.getVisualBounds();
|
||||||
|
//assumption: the system bar fills a whole screen side
|
||||||
|
if (bounds.getMinX() != vBounds.getMinX()) {
|
||||||
|
return Placement.LEFT;
|
||||||
|
} else if (bounds.getMinY() != vBounds.getMinY()) {
|
||||||
|
return Placement.TOP;
|
||||||
|
} else if (bounds.getMaxX() != vBounds.getMaxX()) {
|
||||||
|
return Placement.RIGHT;
|
||||||
|
} else {
|
||||||
|
return Placement.BOTTOM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,8 @@ public enum FontAwesome5Icon {
|
|||||||
CARET_DOWN("\uF0D7"), //
|
CARET_DOWN("\uF0D7"), //
|
||||||
CARET_RIGHT("\uF0Da"), //
|
CARET_RIGHT("\uF0Da"), //
|
||||||
CHECK("\uF00C"), //
|
CHECK("\uF00C"), //
|
||||||
|
CHEVRON_LEFT("\uF053"), //
|
||||||
|
CHEVRON_RIGHT("\uF054"), //
|
||||||
CLOCK("\uF017"), //
|
CLOCK("\uF017"), //
|
||||||
CLIPBOARD("\uF328"), //
|
CLIPBOARD("\uF328"), //
|
||||||
COG("\uF013"), //
|
COG("\uF013"), //
|
||||||
@@ -60,6 +62,7 @@ public enum FontAwesome5Icon {
|
|||||||
TRASH("\uF1F8"), //
|
TRASH("\uF1F8"), //
|
||||||
UNLINK("\uf127"), //
|
UNLINK("\uf127"), //
|
||||||
USER_COG("\uf4fe"), //
|
USER_COG("\uf4fe"), //
|
||||||
|
USER_LOCK("\uf502"), //
|
||||||
WRENCH("\uF0AD"), //
|
WRENCH("\uF0AD"), //
|
||||||
WINDOW_MINIMIZE("\uF2D1"), //
|
WINDOW_MINIMIZE("\uF2D1"), //
|
||||||
;
|
;
|
||||||
|
|||||||
+22
-12
@@ -4,24 +4,27 @@ import javafx.beans.property.BooleanProperty;
|
|||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.AccessibleRole;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.ContentDisplay;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
public class NotificationBar extends HBox {
|
public class InfoBar extends HBox {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Label notificationLabel;
|
private Label infoMessage;
|
||||||
|
|
||||||
private final BooleanProperty dismissable = new SimpleBooleanProperty();
|
private final BooleanProperty dismissable = new SimpleBooleanProperty();
|
||||||
private final BooleanProperty notify = new SimpleBooleanProperty();
|
private final BooleanProperty notify = new SimpleBooleanProperty();
|
||||||
|
|
||||||
|
|
||||||
public NotificationBar() {
|
public InfoBar() {
|
||||||
setAlignment(Pos.CENTER);
|
setAlignment(Pos.CENTER);
|
||||||
setStyle("-fx-alignment: center;");
|
getStyleClass().addAll("info-bar");
|
||||||
|
|
||||||
Region spacer = new Region();
|
Region spacer = new Region();
|
||||||
spacer.setMinWidth(40);
|
spacer.setMinWidth(40);
|
||||||
@@ -36,14 +39,21 @@ public class NotificationBar extends HBox {
|
|||||||
vbox.setAlignment(Pos.CENTER);
|
vbox.setAlignment(Pos.CENTER);
|
||||||
HBox.setHgrow(vbox, javafx.scene.layout.Priority.ALWAYS);
|
HBox.setHgrow(vbox, javafx.scene.layout.Priority.ALWAYS);
|
||||||
|
|
||||||
notificationLabel = new Label();
|
infoMessage = new Label();
|
||||||
notificationLabel.getStyleClass().add("notification-label");
|
infoMessage.setFocusTraversable(true);
|
||||||
notificationLabel.setStyle("-fx-alignment: center;");
|
infoMessage.setAccessibleRole(AccessibleRole.BUTTON);
|
||||||
vbox.getChildren().add(notificationLabel);
|
vbox.getChildren().add(infoMessage);
|
||||||
|
|
||||||
Button closeButton = new Button("X");
|
var closeGraphic = new FontAwesome5IconView();
|
||||||
|
closeGraphic.setGlyph(FontAwesome5Icon.TIMES);
|
||||||
|
closeGraphic.setGlyphSize(12);
|
||||||
|
closeGraphic.getStyleClass().add("glyph");
|
||||||
|
|
||||||
|
Button closeButton = new Button();
|
||||||
|
closeButton.setGraphic(closeGraphic);
|
||||||
|
closeButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
|
||||||
|
closeButton.setAccessibleText(ResourceBundle.getBundle("i18n.strings").getString("main.notification.closeButton.tooltip"));
|
||||||
closeButton.setMinWidth(40);
|
closeButton.setMinWidth(40);
|
||||||
closeButton.setStyle("-fx-background-color: transparent; -fx-text-fill: white; -fx-font-weight: bold;");
|
|
||||||
closeButton.visibleProperty().bind(dismissable);
|
closeButton.visibleProperty().bind(dismissable);
|
||||||
|
|
||||||
closeButton.setOnAction(_ -> {
|
closeButton.setOnAction(_ -> {
|
||||||
@@ -61,11 +71,11 @@ public class NotificationBar extends HBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getText() {
|
public String getText() {
|
||||||
return notificationLabel.getText();
|
return infoMessage.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setText(String text) {
|
public void setText(String text) {
|
||||||
notificationLabel.setText(text);
|
infoMessage.setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStyleClass(String styleClass) {
|
public void setStyleClass(String styleClass) {
|
||||||
@@ -61,6 +61,15 @@ public class Dialogs {
|
|||||||
.setOkButtonKey(BUTTON_KEY_CLOSE);
|
.setOkButtonKey(BUTTON_KEY_CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SimpleDialog.Builder prepareHubVaultArchived(Stage window, Vault vault) {
|
||||||
|
return createDialogBuilder().setOwner(window) //
|
||||||
|
.setTitleKey("unlock.title", vault.getDisplayName()) //
|
||||||
|
.setMessageKey("hub.archived.message") //
|
||||||
|
.setDescriptionKey("hub.archived.description") //
|
||||||
|
.setIcon(FontAwesome5Icon.BAN)//
|
||||||
|
.setOkButtonKey(BUTTON_KEY_CLOSE);
|
||||||
|
}
|
||||||
|
|
||||||
public SimpleDialog.Builder prepareRecoveryVaultAdded(Stage window, String displayName) {
|
public SimpleDialog.Builder prepareRecoveryVaultAdded(Stage window, String displayName) {
|
||||||
return createDialogBuilder().setOwner(window) //
|
return createDialogBuilder().setOwner(window) //
|
||||||
.setTitleKey("recover.existing.title") //
|
.setTitleKey("recover.existing.title") //
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package org.cryptomator.ui.eventview;
|
package org.cryptomator.ui.eventview;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.cryptomator.common.Constants;
|
||||||
|
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
|
||||||
import org.cryptomator.event.FSEventBucket;
|
import org.cryptomator.event.FSEventBucket;
|
||||||
import org.cryptomator.event.FSEventBucketContent;
|
import org.cryptomator.event.FSEventBucketContent;
|
||||||
import org.cryptomator.event.FileSystemEventAggregator;
|
import org.cryptomator.event.FileSystemEventAggregator;
|
||||||
@@ -115,7 +118,7 @@ public class EventListCellController implements FxController {
|
|||||||
eventActionsMenu.hide();
|
eventActionsMenu.hide();
|
||||||
eventActionsMenu.getItems().clear();
|
eventActionsMenu.getItems().clear();
|
||||||
eventTooltip.setText(item.getKey().vault().getDisplayName());
|
eventTooltip.setText(item.getKey().vault().getDisplayName());
|
||||||
addAction("generic.action.dismiss", () -> {
|
addLocalizedAction("generic.action.dismiss", () -> {
|
||||||
fileSystemEventAggregator.remove(item.getKey());
|
fileSystemEventAggregator.remove(item.getKey());
|
||||||
});
|
});
|
||||||
switch (item.getValue().mostRecentEvent()) {
|
switch (item.getValue().mostRecentEvent()) {
|
||||||
@@ -124,20 +127,41 @@ public class EventListCellController implements FxController {
|
|||||||
case DecryptionFailedEvent fse -> this.adjustToDecryptionFailedEvent(fse);
|
case DecryptionFailedEvent fse -> this.adjustToDecryptionFailedEvent(fse);
|
||||||
case BrokenDirFileEvent fse -> this.adjustToBrokenDirFileEvent(fse);
|
case BrokenDirFileEvent fse -> this.adjustToBrokenDirFileEvent(fse);
|
||||||
case BrokenFileNodeEvent fse -> this.adjustToBrokenFileNodeEvent(fse);
|
case BrokenFileNodeEvent fse -> this.adjustToBrokenFileNodeEvent(fse);
|
||||||
|
case FileIsInUseEvent fse -> this.adjustToFileInUseEvent(fse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void adjustToFileInUseEvent(FileIsInUseEvent fiiue) {
|
||||||
|
eventIcon.setValue(FontAwesome5Icon.USER_LOCK);
|
||||||
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.inUse.message"));
|
||||||
|
var indexFileName = fiiue.cleartextPath().lastIndexOf("/");
|
||||||
|
eventDescription.setValue(fiiue.cleartextPath().substring(indexFileName + 1));
|
||||||
|
if (revealService != null) {
|
||||||
|
addLocalizedAction("eventView.entry.inUse.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(fiiue.cleartextPath())));
|
||||||
|
addLocalizedAction("eventView.entry.inUse.showEncrypted", () -> reveal(revealService, fiiue.ciphertextPath()));
|
||||||
|
} else {
|
||||||
|
addLocalizedAction("eventView.entry.inUse.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(fiiue.cleartextPath()).toString()));
|
||||||
|
addLocalizedAction("eventView.entry.inUse.copyEncrypted", () -> copyToClipboard(fiiue.ciphertextPath().toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
var userAndDevice = fiiue.owner().split(Constants.HUB_USER_DEVICE_SEPARATOR);
|
||||||
|
var user = userAndDevice[0];
|
||||||
|
var device = userAndDevice.length == 1 ? userAndDevice[0] : userAndDevice[1];
|
||||||
|
addLocalizedAction("eventView.entry.inUse.copyUserAndDevice", () -> copyToClipboard(user + ", " + device));
|
||||||
|
addLocalizedAction("eventView.entry.inUse.ignoreLock", fiiue.ignoreMethod());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void adjustToBrokenFileNodeEvent(BrokenFileNodeEvent bfe) {
|
private void adjustToBrokenFileNodeEvent(BrokenFileNodeEvent bfe) {
|
||||||
eventIcon.setValue(FontAwesome5Icon.TIMES);
|
eventIcon.setValue(FontAwesome5Icon.TIMES);
|
||||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message"));
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message"));
|
||||||
eventDescription.setValue(bfe.ciphertextPath().getFileName().toString());
|
eventDescription.setValue(bfe.ciphertextPath().getFileName().toString());
|
||||||
if (revealService != null) {
|
if (revealService != null) {
|
||||||
addAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
|
addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
|
||||||
} else {
|
} else {
|
||||||
addAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString()));
|
addLocalizedAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString()));
|
||||||
}
|
}
|
||||||
addAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
|
addLocalizedAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void adjustToConflictResolvedEvent(ConflictResolvedEvent cre) {
|
private void adjustToConflictResolvedEvent(ConflictResolvedEvent cre) {
|
||||||
@@ -145,9 +169,9 @@ public class EventListCellController implements FxController {
|
|||||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message"));
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message"));
|
||||||
eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString());
|
eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString());
|
||||||
if (revealService != null) {
|
if (revealService != null) {
|
||||||
addAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
|
addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
|
||||||
} else {
|
} else {
|
||||||
addAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString()));
|
addLocalizedAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,11 +180,11 @@ public class EventListCellController implements FxController {
|
|||||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message"));
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message"));
|
||||||
eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString());
|
eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString());
|
||||||
if (revealService != null) {
|
if (revealService != null) {
|
||||||
addAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
|
addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
|
||||||
addAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
|
addLocalizedAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
|
||||||
} else {
|
} else {
|
||||||
addAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString()));
|
addLocalizedAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString()));
|
||||||
addAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString()));
|
addLocalizedAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,9 +193,9 @@ public class EventListCellController implements FxController {
|
|||||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message"));
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message"));
|
||||||
eventDescription.setValue(dfe.ciphertextPath().getFileName().toString());
|
eventDescription.setValue(dfe.ciphertextPath().getFileName().toString());
|
||||||
if (revealService != null) {
|
if (revealService != null) {
|
||||||
addAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
|
addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
|
||||||
} else {
|
} else {
|
||||||
addAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString()));
|
addLocalizedAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,14 +204,19 @@ public class EventListCellController implements FxController {
|
|||||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message"));
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message"));
|
||||||
eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString());
|
eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString());
|
||||||
if (revealService != null) {
|
if (revealService != null) {
|
||||||
addAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
|
addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
|
||||||
} else {
|
} else {
|
||||||
addAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString()));
|
addLocalizedAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAction(String localizationKey, Runnable action) {
|
private void addLocalizedAction(String localizationKey, Runnable action) {
|
||||||
var entry = new MenuItem(resourceBundle.getString(localizationKey));
|
var entryText = resourceBundle.getString(localizationKey);
|
||||||
|
addAction(entryText, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAction(String entryText, Runnable action) {
|
||||||
|
var entry = new MenuItem(entryText);
|
||||||
entry.getStyleClass().addLast("dropdown-button-context-menu-item");
|
entry.getStyleClass().addLast("dropdown-button-context-menu-item");
|
||||||
entry.setOnAction(_ -> action.run());
|
entry.setOnAction(_ -> action.run());
|
||||||
eventActionsMenu.getItems().addLast(entry);
|
eventActionsMenu.getItems().addLast(entry);
|
||||||
@@ -234,18 +263,17 @@ public class EventListCellController implements FxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path convertVaultPathToSystemPath(Path p) {
|
private Path convertVaultPathToSystemPath(String vaultInternalPath) {
|
||||||
if (!(p instanceof CryptoPath)) {
|
|
||||||
throw new IllegalArgumentException("Path " + p + " is not a vault path");
|
|
||||||
}
|
|
||||||
var v = eventEntry.getValue().getKey().vault();
|
var v = eventEntry.getValue().getKey().vault();
|
||||||
if (!v.isUnlocked()) {
|
if (!v.isUnlocked()) {
|
||||||
return Path.of(System.getProperty("user.home"));
|
return Path.of(System.getProperty("user.home"));
|
||||||
}
|
}
|
||||||
|
|
||||||
var mountUri = v.getMountPoint().uri();
|
var mountPoint = v.getMountPoint().uri().getPath();
|
||||||
var internalPath = p.toString().substring(1);
|
if(SystemUtils.IS_OS_WINDOWS) {
|
||||||
return Path.of(mountUri.getPath().concat(internalPath).substring(1));
|
mountPoint = mountPoint.substring(1); //strip away any leading "/", otherwise there are errors
|
||||||
|
}
|
||||||
|
return Path.of(mountPoint, vaultInternalPath.substring(1)); //vaultPaths are always absolute
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reveal(RevealPathService s, Path p) {
|
private void reveal(RevealPathService s, Path p) {
|
||||||
|
|||||||
@@ -30,9 +30,20 @@ public class FxApplication {
|
|||||||
private final FxApplicationTerminator applicationTerminator;
|
private final FxApplicationTerminator applicationTerminator;
|
||||||
private final AutoUnlocker autoUnlocker;
|
private final AutoUnlocker autoUnlocker;
|
||||||
private final FxFSEventList fxFSEventList;
|
private final FxFSEventList fxFSEventList;
|
||||||
|
private final FxNotificationManager notificationManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
FxApplication(@Named("startupTime") long startupTime, Environment environment, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker, FxFSEventList fxFSEventList) {
|
FxApplication(@Named("startupTime") long startupTime, //
|
||||||
|
Environment environment, //
|
||||||
|
Settings settings, //
|
||||||
|
AppLaunchEventHandler launchEventHandler, //
|
||||||
|
Lazy<TrayMenuComponent> trayMenu, //
|
||||||
|
FxApplicationWindows appWindows, //
|
||||||
|
FxApplicationStyle applicationStyle, //
|
||||||
|
FxApplicationTerminator applicationTerminator, //
|
||||||
|
AutoUnlocker autoUnlocker, //
|
||||||
|
FxFSEventList fxFSEventList, //
|
||||||
|
FxNotificationManager notificationManager) {
|
||||||
this.startupTime = startupTime;
|
this.startupTime = startupTime;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
@@ -43,6 +54,7 @@ public class FxApplication {
|
|||||||
this.applicationTerminator = applicationTerminator;
|
this.applicationTerminator = applicationTerminator;
|
||||||
this.autoUnlocker = autoUnlocker;
|
this.autoUnlocker = autoUnlocker;
|
||||||
this.fxFSEventList = fxFSEventList;
|
this.fxFSEventList = fxFSEventList;
|
||||||
|
this.notificationManager = notificationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
@@ -88,6 +100,7 @@ public class FxApplication {
|
|||||||
|
|
||||||
launchEventHandler.startHandlingLaunchEvents();
|
launchEventHandler.startHandlingLaunchEvents();
|
||||||
fxFSEventList.schedulePollForUpdates();
|
fxFSEventList.schedulePollForUpdates();
|
||||||
|
notificationManager.schedulePollForUpdates();
|
||||||
autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES);
|
autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,14 @@ package org.cryptomator.ui.fxapp;
|
|||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||||
import org.cryptomator.ui.decryptname.DecryptNameComponent;
|
import org.cryptomator.ui.decryptname.DecryptNameComponent;
|
||||||
import org.cryptomator.ui.error.ErrorComponent;
|
import org.cryptomator.ui.error.ErrorComponent;
|
||||||
import org.cryptomator.ui.eventview.EventViewComponent;
|
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;
|
||||||
|
import org.cryptomator.ui.notification.NotificationComponent;
|
||||||
import org.cryptomator.ui.preferences.PreferencesComponent;
|
import org.cryptomator.ui.preferences.PreferencesComponent;
|
||||||
import org.cryptomator.ui.quit.QuitComponent;
|
import org.cryptomator.ui.quit.QuitComponent;
|
||||||
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
|
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
|
||||||
@@ -25,6 +27,7 @@ import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
|||||||
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;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Module(subcomponents = {TrayMenuComponent.class, //
|
@Module(subcomponents = {TrayMenuComponent.class, //
|
||||||
DecryptNameComponent.class, //
|
DecryptNameComponent.class, //
|
||||||
@@ -39,7 +42,8 @@ import java.io.InputStream;
|
|||||||
UpdateReminderComponent.class, //
|
UpdateReminderComponent.class, //
|
||||||
ShareVaultComponent.class, //
|
ShareVaultComponent.class, //
|
||||||
EventViewComponent.class, //
|
EventViewComponent.class, //
|
||||||
RecoveryKeyComponent.class})
|
RecoveryKeyComponent.class, //
|
||||||
|
NotificationComponent.class})
|
||||||
abstract class FxApplicationModule {
|
abstract class FxApplicationModule {
|
||||||
|
|
||||||
private static Image createImageFromResource(String resourceName) throws IOException {
|
private static Image createImageFromResource(String resourceName) throws IOException {
|
||||||
@@ -48,6 +52,12 @@ abstract class FxApplicationModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@FxApplicationScoped
|
||||||
|
static Optional<UiAppearanceProvider> provideAppearanceProvider() {
|
||||||
|
return UiAppearanceProvider.get();
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@FxApplicationScoped
|
@FxApplicationScoped
|
||||||
static TrayMenuComponent provideTrayMenuComponent(TrayMenuComponent.Builder builder) {
|
static TrayMenuComponent provideTrayMenuComponent(TrayMenuComponent.Builder builder) {
|
||||||
@@ -78,4 +88,10 @@ abstract class FxApplicationModule {
|
|||||||
return factory.create();
|
return factory.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@FxApplicationScoped
|
||||||
|
static NotificationComponent provideNotificationComponent(NotificationComponent.Factory factory) {
|
||||||
|
return factory.create();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -36,82 +36,91 @@ public class FxApplicationStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
|
var uiTheme = settings.theme.get();
|
||||||
|
if (uiTheme == UiTheme.AUTOMATIC) {
|
||||||
|
registerOsThemeListener();
|
||||||
|
}
|
||||||
|
applyTheme(uiTheme);
|
||||||
settings.theme.addListener(this::appThemeChanged);
|
settings.theme.addListener(this::appThemeChanged);
|
||||||
loadSelectedStyleSheet(settings.theme.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appThemeChanged(@SuppressWarnings("unused") ObservableValue<? extends UiTheme> observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) {
|
private void appThemeChanged(@SuppressWarnings("unused") ObservableValue<? extends UiTheme> observable, UiTheme oldValue, UiTheme newValue) {
|
||||||
if (appearanceProvider.isPresent() && oldValue == UiTheme.AUTOMATIC && newValue != UiTheme.AUTOMATIC) {
|
if (oldValue == newValue) {
|
||||||
|
// no-op
|
||||||
|
} else if (newValue == UiTheme.AUTOMATIC) {
|
||||||
|
registerOsThemeListener();
|
||||||
|
} else if (oldValue == UiTheme.AUTOMATIC) {
|
||||||
|
removeOsThemeListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
applyTheme(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeOsThemeListener() {
|
||||||
|
if (appearanceProvider.isPresent()) {
|
||||||
try {
|
try {
|
||||||
appearanceProvider.get().removeListener(systemInterfaceThemeListener);
|
appearanceProvider.get().removeListener(systemInterfaceThemeListener);
|
||||||
} catch (UiAppearanceException e) {
|
} catch (UiAppearanceException e) {
|
||||||
LOG.error("Failed to disable automatic theme switching.");
|
LOG.warn("Failed to disable automatic theme switching.", e);
|
||||||
}
|
|
||||||
}
|
|
||||||
loadSelectedStyleSheet(newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadSelectedStyleSheet(UiTheme desiredTheme) {
|
|
||||||
UiTheme theme = licenseHolder.isValidLicense() ? desiredTheme : UiTheme.LIGHT;
|
|
||||||
switch (theme) {
|
|
||||||
case LIGHT -> applyLightTheme();
|
|
||||||
case DARK -> applyDarkTheme();
|
|
||||||
case AUTOMATIC -> {
|
|
||||||
appearanceProvider.ifPresent(provider -> {
|
|
||||||
try {
|
|
||||||
provider.addListener(systemInterfaceThemeListener);
|
|
||||||
} catch (UiAppearanceException e) {
|
|
||||||
LOG.error("Failed to enable automatic theme switching.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
applySystemTheme();
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
LOG.debug("Unable to remove listener os theme changes: No supported UiAppearanceProvider present");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void systemInterfaceThemeChanged(Theme theme) {
|
private void registerOsThemeListener() {
|
||||||
switch (theme) {
|
|
||||||
case LIGHT -> applyLightTheme();
|
|
||||||
case DARK -> applyDarkTheme();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applySystemTheme() {
|
|
||||||
if (appearanceProvider.isPresent()) {
|
if (appearanceProvider.isPresent()) {
|
||||||
systemInterfaceThemeChanged(appearanceProvider.get().getSystemTheme());
|
try {
|
||||||
|
appearanceProvider.get().addListener(systemInterfaceThemeListener);
|
||||||
|
} catch (UiAppearanceException e) {
|
||||||
|
LOG.warn("Failed to enable automatic theme switching.", e);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG.warn("No UiAppearanceProvider present, assuming LIGHT theme...");
|
LOG.warn("Unable to register for os theme changes: No supported UiAppearanceProvider present");
|
||||||
applyLightTheme();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyLightTheme() {
|
private void applyTheme(UiTheme uiTheme) {
|
||||||
var stylesheet = Optional //
|
if (!licenseHolder.isValidLicense()) {
|
||||||
.ofNullable(getClass().getResource("/css/light_theme.bss")) //
|
loadAndApplyLightTheme();
|
||||||
.orElse(getClass().getResource("/css/light_theme.css"));
|
} else {
|
||||||
|
switch (uiTheme) {
|
||||||
|
case AUTOMATIC -> {
|
||||||
|
var osTheme = appearanceProvider.map(UiAppearanceProvider::getSystemTheme).orElse(Theme.LIGHT);
|
||||||
|
systemInterfaceThemeChanged(osTheme);
|
||||||
|
}
|
||||||
|
case LIGHT -> loadAndApplyLightTheme();
|
||||||
|
case DARK -> loadAndApplyDarkTheme();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void systemInterfaceThemeChanged(Theme osTheme) {
|
||||||
|
switch (osTheme) {
|
||||||
|
case LIGHT -> loadAndApplyLightTheme();
|
||||||
|
case DARK -> loadAndApplyDarkTheme();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadAndApplyLightTheme() {
|
||||||
|
loadAndApplyTheme(Theme.LIGHT, "/css/light_theme.css");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadAndApplyDarkTheme() {
|
||||||
|
loadAndApplyTheme(Theme.DARK, "/css/dark_theme.css");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadAndApplyTheme(Theme appTheme, String cssFile) {
|
||||||
|
var stylesheet = getClass().getResource(cssFile);
|
||||||
if (stylesheet == null) {
|
if (stylesheet == null) {
|
||||||
LOG.warn("Failed to load light_theme stylesheet");
|
throw new IllegalStateException("Cannot find resource %s".formatted(cssFile));
|
||||||
} else {
|
|
||||||
Application.setUserAgentStylesheet(stylesheet.toString());
|
|
||||||
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.LIGHT));
|
|
||||||
appliedTheme.set(Theme.LIGHT);
|
|
||||||
}
|
}
|
||||||
|
Application.setUserAgentStylesheet(stylesheet.toString());
|
||||||
|
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(appTheme));
|
||||||
|
appliedTheme.set(appTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyDarkTheme() {
|
public ObjectProperty<Theme> appliedAppThemeProperty() {
|
||||||
var stylesheet = Optional //
|
|
||||||
.ofNullable(getClass().getResource("/css/dark_theme.bss")) //
|
|
||||||
.orElse(getClass().getResource("/css/dark_theme.css"));
|
|
||||||
if (stylesheet == null) {
|
|
||||||
LOG.warn("Failed to load dark_theme stylesheet");
|
|
||||||
} else {
|
|
||||||
Application.setUserAgentStylesheet(stylesheet.toString());
|
|
||||||
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.DARK));
|
|
||||||
appliedTheme.set(Theme.DARK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectProperty<Theme> appliedThemeProperty() {
|
|
||||||
return appliedTheme;
|
return appliedTheme;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import org.cryptomator.ui.error.ErrorComponent;
|
|||||||
import org.cryptomator.ui.eventview.EventViewComponent;
|
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.notification.NotificationComponent;
|
||||||
import org.cryptomator.ui.preferences.PreferencesComponent;
|
import org.cryptomator.ui.preferences.PreferencesComponent;
|
||||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||||
import org.cryptomator.ui.quit.QuitComponent;
|
import org.cryptomator.ui.quit.QuitComponent;
|
||||||
@@ -54,6 +55,7 @@ public class FxApplicationWindows {
|
|||||||
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 Lazy<EventViewComponent> eventViewWindow;
|
||||||
|
private final Lazy<NotificationComponent> notificationWindow;
|
||||||
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;
|
||||||
@@ -73,6 +75,7 @@ public class FxApplicationWindows {
|
|||||||
VaultOptionsComponent.Factory vaultOptionsWindow, //
|
VaultOptionsComponent.Factory vaultOptionsWindow, //
|
||||||
ShareVaultComponent.Factory shareVaultWindow, //
|
ShareVaultComponent.Factory shareVaultWindow, //
|
||||||
Lazy<EventViewComponent> eventViewWindow, //
|
Lazy<EventViewComponent> eventViewWindow, //
|
||||||
|
Lazy<NotificationComponent> notificationWindow,
|
||||||
ExecutorService executor, //
|
ExecutorService executor, //
|
||||||
Dialogs dialogs) {
|
Dialogs dialogs) {
|
||||||
this.primaryStage = primaryStage;
|
this.primaryStage = primaryStage;
|
||||||
@@ -85,6 +88,7 @@ public class FxApplicationWindows {
|
|||||||
this.lockWorkflowFactory = lockWorkflowFactory;
|
this.lockWorkflowFactory = lockWorkflowFactory;
|
||||||
this.errorWindowFactory = errorWindowFactory;
|
this.errorWindowFactory = errorWindowFactory;
|
||||||
this.eventViewWindow = eventViewWindow;
|
this.eventViewWindow = eventViewWindow;
|
||||||
|
this.notificationWindow = notificationWindow;
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
this.vaultOptionsWindow = vaultOptionsWindow;
|
this.vaultOptionsWindow = vaultOptionsWindow;
|
||||||
this.shareVaultWindow = shareVaultWindow;
|
this.shareVaultWindow = shareVaultWindow;
|
||||||
@@ -193,6 +197,10 @@ public class FxApplicationWindows {
|
|||||||
return CompletableFuture.supplyAsync(() -> eventViewWindow.get().showEventViewerWindow(), Platform::runLater).whenComplete(this::reportErrors);
|
return CompletableFuture.supplyAsync(() -> eventViewWindow.get().showEventViewerWindow(), Platform::runLater).whenComplete(this::reportErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CompletionStage<Stage> showNotification() {
|
||||||
|
return CompletableFuture.supplyAsync(() -> notificationWindow.get().showNotification(), Platform::runLater).whenComplete(this::reportErrors);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays the generic error scene in the given window.
|
* Displays the generic error scene in the given window.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package org.cryptomator.ui.fxapp;
|
|||||||
import org.cryptomator.event.FSEventBucket;
|
import org.cryptomator.event.FSEventBucket;
|
||||||
import org.cryptomator.event.FSEventBucketContent;
|
import org.cryptomator.event.FSEventBucketContent;
|
||||||
import org.cryptomator.event.FileSystemEventAggregator;
|
import org.cryptomator.event.FileSystemEventAggregator;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
@@ -11,6 +13,7 @@ import javafx.beans.property.SimpleBooleanProperty;
|
|||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@@ -23,6 +26,8 @@ import java.util.concurrent.TimeUnit;
|
|||||||
@FxApplicationScoped
|
@FxApplicationScoped
|
||||||
public class FxFSEventList {
|
public class FxFSEventList {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(FxFSEventList.class);
|
||||||
|
|
||||||
private final ObservableList<Map.Entry<FSEventBucket, FSEventBucketContent>> events;
|
private final ObservableList<Map.Entry<FSEventBucket, FSEventBucketContent>> events;
|
||||||
private final FileSystemEventAggregator eventAggregator;
|
private final FileSystemEventAggregator eventAggregator;
|
||||||
private final ScheduledExecutorService scheduler;
|
private final ScheduledExecutorService scheduler;
|
||||||
@@ -37,7 +42,13 @@ public class FxFSEventList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void schedulePollForUpdates() {
|
public void schedulePollForUpdates() {
|
||||||
scheduler.schedule(this::checkForEventUpdates, 1000, TimeUnit.MILLISECONDS);
|
try {
|
||||||
|
scheduler.schedule(this::checkForEventUpdates, 1000, TimeUnit.MILLISECONDS);
|
||||||
|
} catch ( RejectedExecutionException e) {
|
||||||
|
if(!scheduler.isShutdown()) {
|
||||||
|
LOG.warn("Failed to poll for filesystem events", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package org.cryptomator.ui.fxapp;
|
||||||
|
|
||||||
|
import org.cryptomator.event.NotificationManager;
|
||||||
|
import org.cryptomator.event.VaultEvent;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification manager inside the UI domain.
|
||||||
|
* <p>
|
||||||
|
* Polls the {@link NotificationManager} for pending events every {@value POLL_INTERVAL_SECONDS } seconds and
|
||||||
|
* triggers the notification window display when events are available.
|
||||||
|
* Returns an observable list of events requiring a user notification with {@link #getEventsRequiringNotification()}.
|
||||||
|
*
|
||||||
|
* @see NotificationManager
|
||||||
|
*/
|
||||||
|
@FxApplicationScoped
|
||||||
|
public class FxNotificationManager {
|
||||||
|
|
||||||
|
private static final int POLL_INTERVAL_SECONDS = 1;
|
||||||
|
|
||||||
|
private final NotificationManager notificationManager;
|
||||||
|
private final ScheduledExecutorService scheduler;
|
||||||
|
private final FxApplicationWindows applicationWindows;
|
||||||
|
private final ObservableList<VaultEvent> eventsRequiringNotification;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public FxNotificationManager(NotificationManager notificationManager, ScheduledExecutorService scheduler, FxApplicationWindows applicationWindows) {
|
||||||
|
this.notificationManager = notificationManager;
|
||||||
|
this.scheduler = scheduler;
|
||||||
|
this.applicationWindows = applicationWindows;
|
||||||
|
this.eventsRequiringNotification = FXCollections.observableArrayList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void schedulePollForUpdates() {
|
||||||
|
scheduler.scheduleAtFixedRate(this::checkForPendingNotifications, 0, POLL_INTERVAL_SECONDS, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkForPendingNotifications() {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
if (notificationManager.appendToAndClear(eventsRequiringNotification)) {
|
||||||
|
applicationWindows.showNotification();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableList<VaultEvent> getEventsRequiringNotification() {
|
||||||
|
return eventsRequiringNotification;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package org.cryptomator.ui.fxapp;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.DisplayName;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
import org.cryptomator.integrations.common.Priority;
|
||||||
|
import org.cryptomator.integrations.uiappearance.Theme;
|
||||||
|
import org.cryptomator.integrations.uiappearance.UiAppearanceException;
|
||||||
|
import org.cryptomator.integrations.uiappearance.UiAppearanceListener;
|
||||||
|
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javafx.application.ColorScheme;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@DisplayName("JavaFX Color Scheme switcher")
|
||||||
|
@OperatingSystem(OperatingSystem.Value.LINUX)
|
||||||
|
@OperatingSystem(OperatingSystem.Value.WINDOWS)
|
||||||
|
@Priority(1050)
|
||||||
|
public class JfxUiAppearanceProvider implements UiAppearanceProvider {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(JfxUiAppearanceProvider.class);
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<UiAppearanceListener, ChangeListener<ColorScheme>> uiAppearanceListeners = new ConcurrentHashMap<>();
|
||||||
|
private final Platform.Preferences preferences = Platform.getPreferences(); //Note: this service impl MUST be loaded in the fx application thread
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Theme getSystemTheme() {
|
||||||
|
return switch (preferences.getColorScheme()) {
|
||||||
|
case DARK -> Theme.DARK;
|
||||||
|
case LIGHT -> Theme.LIGHT;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void adjustToTheme(Theme theme) {
|
||||||
|
//no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addListener(UiAppearanceListener uiAppearanceListener) throws UiAppearanceException {
|
||||||
|
var fxChangeListener = (ChangeListener<ColorScheme>) (_, _, newScheme) -> {
|
||||||
|
var newTheme = switch (newScheme) {
|
||||||
|
case DARK -> Theme.DARK;
|
||||||
|
case LIGHT -> Theme.LIGHT;
|
||||||
|
};
|
||||||
|
uiAppearanceListener.systemAppearanceChanged(newTheme);
|
||||||
|
};
|
||||||
|
LOG.debug("Register listener for OS theme changes");
|
||||||
|
uiAppearanceListeners.computeIfAbsent(uiAppearanceListener, k -> {
|
||||||
|
Platform.runLater(() -> preferences.colorSchemeProperty().addListener(fxChangeListener));
|
||||||
|
return fxChangeListener;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeListener(UiAppearanceListener uiAppearanceListener) throws UiAppearanceException {
|
||||||
|
var fxChangeListener = uiAppearanceListeners.remove(uiAppearanceListener);
|
||||||
|
if (fxChangeListener != null) {
|
||||||
|
LOG.debug("Removing listener for OS theme changes");
|
||||||
|
Platform.runLater(() -> preferences.colorSchemeProperty().removeListener(fxChangeListener));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -63,8 +63,8 @@ abstract class HealthCheckModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@HealthCheckWindow
|
@HealthCheckWindow
|
||||||
@HealthCheckScoped
|
@HealthCheckScoped
|
||||||
static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Builder compBuilder, @HealthCheckWindow Vault vault, @Named("unlockWindow") Stage window ) {
|
static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Factory compFactory, @HealthCheckWindow Vault vault, @Named("unlockWindow") Stage window ) {
|
||||||
return compBuilder.vault(vault).window(window).build().keyloadingStrategy();
|
return compFactory.create(vault, window).keyloadingStrategy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -3,11 +3,8 @@ package org.cryptomator.ui.keyloading;
|
|||||||
import dagger.BindsInstance;
|
import dagger.BindsInstance;
|
||||||
import dagger.Subcomponent;
|
import dagger.Subcomponent;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.cryptolib.api.MasterkeyLoader;
|
|
||||||
|
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
@KeyLoadingScoped
|
@KeyLoadingScoped
|
||||||
@Subcomponent(modules = {KeyLoadingModule.class})
|
@Subcomponent(modules = {KeyLoadingModule.class})
|
||||||
@@ -16,16 +13,10 @@ public interface KeyLoadingComponent {
|
|||||||
@KeyLoading
|
@KeyLoading
|
||||||
KeyLoadingStrategy keyloadingStrategy();
|
KeyLoadingStrategy keyloadingStrategy();
|
||||||
|
|
||||||
@Subcomponent.Builder
|
@Subcomponent.Factory
|
||||||
interface Builder {
|
interface Factory {
|
||||||
|
|
||||||
@BindsInstance
|
KeyLoadingComponent create(@BindsInstance @KeyLoading Vault vault, @KeyLoading @BindsInstance Stage window);
|
||||||
Builder vault(@KeyLoading Vault vault);
|
|
||||||
|
|
||||||
@BindsInstance
|
|
||||||
Builder window(@KeyLoading Stage window);
|
|
||||||
|
|
||||||
KeyLoadingComponent build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,13 @@ public abstract class HubKeyLoadingModule {
|
|||||||
return new AtomicReference<>();
|
return new AtomicReference<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Named("filesystemOwnerId")
|
||||||
|
@KeyLoadingScoped
|
||||||
|
static AtomicReference<String> provideFilesystemOwnerIdRef() {
|
||||||
|
return new AtomicReference<>();
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@KeyLoadingScoped
|
@KeyLoadingScoped
|
||||||
static CompletableFuture<ReceivedKey> provideResult() {
|
static CompletableFuture<ReceivedKey> provideResult() {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.cryptomator.ui.keyloading.hub;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
|
import org.cryptomator.common.FilesystemOwnerSupplier;
|
||||||
import org.cryptomator.common.keychain.KeychainManager;
|
import org.cryptomator.common.keychain.KeychainManager;
|
||||||
import org.cryptomator.common.keychain.NoKeychainAccessProviderException;
|
import org.cryptomator.common.keychain.NoKeychainAccessProviderException;
|
||||||
import org.cryptomator.common.settings.DeviceKey;
|
import org.cryptomator.common.settings.DeviceKey;
|
||||||
@@ -23,25 +24,28 @@ import java.net.URI;
|
|||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
@KeyLoading
|
@KeyLoading
|
||||||
public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
|
public class HubKeyLoadingStrategy implements KeyLoadingStrategy, FilesystemOwnerSupplier {
|
||||||
|
|
||||||
private static final String SCHEME_PREFIX = "hub+";
|
public static final String SCHEME_PREFIX = "hub+";
|
||||||
public static final String SCHEME_HUB_HTTP = SCHEME_PREFIX + "http";
|
public static final String SCHEME_HUB_HTTP = SCHEME_PREFIX + "http";
|
||||||
public static final String SCHEME_HUB_HTTPS = SCHEME_PREFIX + "https";
|
public static final String SCHEME_HUB_HTTPS = SCHEME_PREFIX + "https";
|
||||||
|
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final KeychainManager keychainManager;
|
private final KeychainManager keychainManager;
|
||||||
|
private final AtomicReference<String> fsOwnerId;
|
||||||
private final Lazy<Scene> authFlowScene;
|
private final Lazy<Scene> authFlowScene;
|
||||||
private final Lazy<Scene> noKeychainScene;
|
private final Lazy<Scene> noKeychainScene;
|
||||||
private final CompletableFuture<ReceivedKey> result;
|
private final CompletableFuture<ReceivedKey> result;
|
||||||
private final DeviceKey deviceKey;
|
private final DeviceKey deviceKey;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<ReceivedKey> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle) {
|
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<ReceivedKey> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle, @Named("filesystemOwnerId") AtomicReference<String> fsOwnerId) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.keychainManager = keychainManager;
|
this.keychainManager = keychainManager;
|
||||||
|
this.fsOwnerId = fsOwnerId;
|
||||||
window.setTitle(windowTitle);
|
window.setTitle(windowTitle);
|
||||||
window.setOnCloseRequest(_ -> result.cancel(true));
|
window.setOnCloseRequest(_ -> result.cancel(true));
|
||||||
this.authFlowScene = authFlowScene;
|
this.authFlowScene = authFlowScene;
|
||||||
@@ -90,4 +94,13 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOwner() {
|
||||||
|
var name = fsOwnerId.get();
|
||||||
|
if (name == null) {
|
||||||
|
throw new IllegalStateException("Owner is not yet determined");
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.nimbusds.jose.JWEObject;
|
import com.nimbusds.jose.JWEObject;
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
|
import org.cryptomator.common.Constants;
|
||||||
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.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
|
import org.cryptomator.ui.dialogs.Dialogs;
|
||||||
import org.cryptomator.ui.keyloading.KeyLoading;
|
import org.cryptomator.ui.keyloading.KeyLoading;
|
||||||
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
|
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -41,7 +43,6 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
public class ReceiveKeyController implements FxController {
|
public class ReceiveKeyController implements FxController {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ReceiveKeyController.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ReceiveKeyController.class);
|
||||||
private static final String SCHEME_PREFIX = "hub+";
|
|
||||||
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
|
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
|
||||||
private static final Duration REQ_TIMEOUT = Duration.ofSeconds(10);
|
private static final Duration REQ_TIMEOUT = Duration.ofSeconds(10);
|
||||||
|
|
||||||
@@ -50,6 +51,7 @@ public class ReceiveKeyController implements FxController {
|
|||||||
private final String vaultId;
|
private final String vaultId;
|
||||||
private final String deviceId;
|
private final String deviceId;
|
||||||
private final String bearerToken;
|
private final String bearerToken;
|
||||||
|
private final AtomicReference<String> fsOwnerId;
|
||||||
private final CompletableFuture<ReceivedKey> result;
|
private final CompletableFuture<ReceivedKey> result;
|
||||||
private final Lazy<Scene> registerDeviceScene;
|
private final Lazy<Scene> registerDeviceScene;
|
||||||
private final Lazy<Scene> legacyRegisterDeviceScene;
|
private final Lazy<Scene> legacyRegisterDeviceScene;
|
||||||
@@ -57,14 +59,30 @@ public class ReceiveKeyController implements FxController {
|
|||||||
private final Lazy<Scene> accountInitializationScene;
|
private final Lazy<Scene> accountInitializationScene;
|
||||||
private final Lazy<Scene> invalidLicenseScene;
|
private final Lazy<Scene> invalidLicenseScene;
|
||||||
private final HttpClient httpClient;
|
private final HttpClient httpClient;
|
||||||
|
private final Dialogs dialogs;
|
||||||
|
private final Vault vault;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, HubConfig hubConfig, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference<String> tokenRef, CompletableFuture<ReceivedKey> result, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy<Scene> registerDeviceScene, @FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE) Lazy<Scene> legacyRegisterDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy<Scene> unauthorizedScene, @FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT) Lazy<Scene> accountInitializationScene, @FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy<Scene> invalidLicenseScene) {
|
public ReceiveKeyController(@KeyLoading Vault vault, //
|
||||||
|
ExecutorService executor, //
|
||||||
|
@KeyLoading Stage window, //
|
||||||
|
HubConfig hubConfig, //
|
||||||
|
@Named("deviceId") String deviceId, //
|
||||||
|
@Named("bearerToken") AtomicReference<String> tokenRef, //
|
||||||
|
@Named("filesystemOwnerId") AtomicReference<String> fsOwnerId, //
|
||||||
|
CompletableFuture<ReceivedKey> result, //
|
||||||
|
@FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy<Scene> registerDeviceScene, //
|
||||||
|
@FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE) Lazy<Scene> legacyRegisterDeviceScene, //
|
||||||
|
@FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy<Scene> unauthorizedScene, //
|
||||||
|
@FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT) Lazy<Scene> accountInitializationScene, //
|
||||||
|
@FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy<Scene> invalidLicenseScene, //
|
||||||
|
Dialogs dialogs) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.hubConfig = hubConfig;
|
this.hubConfig = hubConfig;
|
||||||
this.vaultId = extractVaultId(vault.getVaultConfigCache().getUnchecked().getKeyId()); // TODO: access vault config's JTI directly (requires changes in cryptofs)
|
this.vaultId = extractVaultId(vault.getVaultConfigCache().getUnchecked().getKeyId()); // TODO: access vault config's JTI directly (requires changes in cryptofs)
|
||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
this.bearerToken = Objects.requireNonNull(tokenRef.get());
|
this.bearerToken = Objects.requireNonNull(tokenRef.get());
|
||||||
|
this.fsOwnerId = fsOwnerId;
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.registerDeviceScene = registerDeviceScene;
|
this.registerDeviceScene = registerDeviceScene;
|
||||||
this.legacyRegisterDeviceScene = legacyRegisterDeviceScene;
|
this.legacyRegisterDeviceScene = legacyRegisterDeviceScene;
|
||||||
@@ -73,6 +91,8 @@ public class ReceiveKeyController implements FxController {
|
|||||||
this.invalidLicenseScene = invalidLicenseScene;
|
this.invalidLicenseScene = invalidLicenseScene;
|
||||||
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
||||||
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
|
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
|
||||||
|
this.dialogs = dialogs;
|
||||||
|
this.vault = vault;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -81,7 +101,34 @@ public class ReceiveKeyController implements FxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void receiveKey() {
|
public void receiveKey() {
|
||||||
requestApiConfig();
|
requestUserData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requestUserData() {
|
||||||
|
var userUri = hubConfig.URIs.API.resolve("users/me?withDevices=false");
|
||||||
|
var request = HttpRequest.newBuilder(userUri) //
|
||||||
|
.header("Authorization", "Bearer " + bearerToken) //
|
||||||
|
.GET() //
|
||||||
|
.timeout(REQ_TIMEOUT) //
|
||||||
|
.build();
|
||||||
|
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)) //
|
||||||
|
.thenAcceptAsync(this::receivedUserData) //
|
||||||
|
.exceptionally(this::retrievalFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void receivedUserData(HttpResponse<String> response) {
|
||||||
|
LOG.debug("GET {} -> Status Code {}", response.request().uri(), response.statusCode());
|
||||||
|
try {
|
||||||
|
if (response.statusCode() == 200) {
|
||||||
|
var user = JSON.reader().readValue(response.body(), UserDto.class);
|
||||||
|
fsOwnerId.set(user.name);
|
||||||
|
requestApiConfig();
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Unexpected response " + response.statusCode());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -144,6 +191,7 @@ public class ReceiveKeyController implements FxController {
|
|||||||
switch (response.statusCode()) {
|
switch (response.statusCode()) {
|
||||||
case 200 -> {
|
case 200 -> {
|
||||||
var device = JSON.reader().readValue(response.body(), DeviceDto.class);
|
var device = JSON.reader().readValue(response.body(), DeviceDto.class);
|
||||||
|
fsOwnerId.accumulateAndGet(device.name, (s1, s2) -> s1 + Constants.HUB_USER_DEVICE_SEPARATOR + s2);
|
||||||
requestVaultMasterkey(device.userPrivateKey);
|
requestVaultMasterkey(device.userPrivateKey);
|
||||||
}
|
}
|
||||||
case 404 -> Platform.runLater(this::needsDeviceRegistration);
|
case 404 -> Platform.runLater(this::needsDeviceRegistration);
|
||||||
@@ -184,7 +232,8 @@ public class ReceiveKeyController implements FxController {
|
|||||||
switch (response.statusCode()) {
|
switch (response.statusCode()) {
|
||||||
case 200 -> receivedBothEncryptedKeys(response.body(), encryptedUserKey);
|
case 200 -> receivedBothEncryptedKeys(response.body(), encryptedUserKey);
|
||||||
case 402 -> licenseExceeded();
|
case 402 -> licenseExceeded();
|
||||||
case 403, 410 -> accessNotGranted(); // or vault has been archived, effectively disallowing access - TODO: add specific dialog?
|
case 403 -> accessNotGranted();
|
||||||
|
case 410 -> accessGoneVaultArchived();
|
||||||
case 449 -> accountInitializationRequired();
|
case 449 -> accountInitializationRequired();
|
||||||
default -> throw new IllegalStateException("Unexpected response " + response.statusCode());
|
default -> throw new IllegalStateException("Unexpected response " + response.statusCode());
|
||||||
}
|
}
|
||||||
@@ -228,7 +277,8 @@ public class ReceiveKeyController implements FxController {
|
|||||||
switch (response.statusCode()) {
|
switch (response.statusCode()) {
|
||||||
case 200 -> receivedLegacyAccessTokenSuccess(response.body());
|
case 200 -> receivedLegacyAccessTokenSuccess(response.body());
|
||||||
case 402 -> licenseExceeded();
|
case 402 -> licenseExceeded();
|
||||||
case 403, 410 -> accessNotGranted(); // or vault has been archived, effectively disallowing access
|
case 403 -> accessNotGranted();
|
||||||
|
case 410 -> accessGoneVaultArchived();
|
||||||
case 404 -> needsLegacyDeviceRegistration();
|
case 404 -> needsLegacyDeviceRegistration();
|
||||||
default -> throw new IOException("Unexpected response " + response.statusCode());
|
default -> throw new IOException("Unexpected response " + response.statusCode());
|
||||||
}
|
}
|
||||||
@@ -261,6 +311,11 @@ public class ReceiveKeyController implements FxController {
|
|||||||
window.setScene(unauthorizedScene.get());
|
window.setScene(unauthorizedScene.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void accessGoneVaultArchived() {
|
||||||
|
window.close();
|
||||||
|
dialogs.prepareHubVaultArchived((Stage)window.getOwner(), vault).build().showAndWait();
|
||||||
|
}
|
||||||
|
|
||||||
private void accountInitializationRequired() {
|
private void accountInitializationRequired() {
|
||||||
window.setScene(accountInitializationScene.get());
|
window.setScene(accountInitializationScene.get());
|
||||||
}
|
}
|
||||||
@@ -289,13 +344,16 @@ public class ReceiveKeyController implements FxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String extractVaultId(URI vaultKeyUri) {
|
private static String extractVaultId(URI vaultKeyUri) {
|
||||||
assert vaultKeyUri.getScheme().startsWith(SCHEME_PREFIX);
|
assert vaultKeyUri.getScheme().startsWith(HubKeyLoadingStrategy.SCHEME_PREFIX);
|
||||||
var path = vaultKeyUri.getPath();
|
var path = vaultKeyUri.getPath();
|
||||||
return path.substring(path.lastIndexOf('/') + 1);
|
return path.substring(path.lastIndexOf('/') + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
private record DeviceDto(@JsonProperty(value = "userPrivateKey", required = true) String userPrivateKey) {}
|
private record UserDto(@JsonProperty(value = "name", required = true) String name) {}
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
private record DeviceDto(@JsonProperty(value = "name", required = true) String name, @JsonProperty(value = "userPrivateKey", required = true) String userPrivateKey) {}
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
private record ConfigDto(@JsonProperty(value = "apiLevel") int apiLevel) {}
|
private record ConfigDto(@JsonProperty(value = "apiLevel") int apiLevel) {}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import javafx.beans.value.ObservableValue;
|
|||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
// 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 {
|
||||||
@@ -23,9 +24,12 @@ public class VaultListCellController implements FxController {
|
|||||||
private static final Insets COMPACT_INSETS = new Insets(6, 12, 6, 12);
|
private static final Insets COMPACT_INSETS = new Insets(6, 12, 6, 12);
|
||||||
private static final Insets DEFAULT_INSETS = new Insets(12);
|
private static final Insets DEFAULT_INSETS = new Insets(12);
|
||||||
|
|
||||||
|
private final ResourceBundle resourceBundle;
|
||||||
private final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
|
private final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
|
||||||
|
private final ObservableValue<VaultState.Value> vaultState;
|
||||||
private final ObservableValue<FontAwesome5Icon> glyph;
|
private final ObservableValue<FontAwesome5Icon> glyph;
|
||||||
private final ObservableValue<Boolean> compactMode;
|
private final ObservableValue<Boolean> compactMode;
|
||||||
|
private final ObservableValue<String> accessibleText;
|
||||||
|
|
||||||
private AutoAnimator spinAnimation;
|
private AutoAnimator spinAnimation;
|
||||||
|
|
||||||
@@ -35,17 +39,21 @@ public class VaultListCellController implements FxController {
|
|||||||
public HBox vaultListCell;
|
public HBox vaultListCell;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
VaultListCellController(Settings settings) {
|
VaultListCellController(Settings settings, ResourceBundle resourceBundle) {
|
||||||
this.glyph = vault.flatMap(Vault::stateProperty).map(this::getGlyphForVaultState);
|
this.resourceBundle = resourceBundle;
|
||||||
|
this.vaultState = vault.flatMap(Vault::stateProperty);
|
||||||
|
this.glyph = vaultState.map(this::getGlyphForVaultState);
|
||||||
|
this.accessibleText = vaultState.map(this::getAccessibleTextForVaultState);
|
||||||
this.compactMode = settings.compactMode;
|
this.compactMode = settings.compactMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
this.spinAnimation = AutoAnimator.animate(Animations.createDiscrete360Rotation(vaultStateView)) //
|
this.spinAnimation = AutoAnimator.animate(Animations.createDiscrete360Rotation(vaultStateView)) //
|
||||||
.onCondition(vault.flatMap(Vault::stateProperty).map(VaultState.Value.PROCESSING::equals).orElse(false)) //
|
.onCondition(vaultState.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));
|
this.vaultListCell.paddingProperty().bind(compactMode.map(c -> c ? COMPACT_INSETS : DEFAULT_INSETS));
|
||||||
|
this.vaultListCell.accessibleTextProperty().bind(accessibleText);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO deduplicate w/ VaultDetailController
|
// TODO deduplicate w/ VaultDetailController
|
||||||
@@ -62,6 +70,25 @@ public class VaultListCellController implements FxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getAccessibleTextForVaultState(VaultState.Value state) {
|
||||||
|
var v = vault.get();
|
||||||
|
if (state != null && v != null) {
|
||||||
|
var translationKey = switch (state) {
|
||||||
|
case LOCKED -> "vault.state.locked";
|
||||||
|
case PROCESSING -> "vault.state.processing";
|
||||||
|
case UNLOCKED -> "vault.state.unlocked";
|
||||||
|
case NEEDS_MIGRATION -> "vault.state.migrationNeeded";
|
||||||
|
case MISSING -> "vault.state.missing";
|
||||||
|
case VAULT_CONFIG_MISSING, ALL_MISSING, ERROR -> "vault.state.error";
|
||||||
|
};
|
||||||
|
|
||||||
|
var localizedState = resourceBundle.getString(translationKey);
|
||||||
|
return resourceBundle.getString("main.vaultlist.listEntry").formatted(v.getDisplayName(), localizedState);
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Getter/Setter */
|
/* Getter/Setter */
|
||||||
|
|
||||||
public ObservableValue<FontAwesome5Icon> glyphProperty() {
|
public ObservableValue<FontAwesome5Icon> glyphProperty() {
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ 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.fxapp.FxFSEventList;
|
||||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||||
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
|
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -139,8 +139,8 @@ public class VaultListController implements FxController {
|
|||||||
vaultList.setItems(vaults);
|
vaultList.setItems(vaults);
|
||||||
vaultList.setCellFactory(cellFactory);
|
vaultList.setCellFactory(cellFactory);
|
||||||
|
|
||||||
vaultList.prefHeightProperty().bind(
|
vaultList.prefHeightProperty().bind( //
|
||||||
vaultList.fixedCellSizeProperty().multiply(Bindings.size(vaultList.getItems()))
|
vaultList.fixedCellSizeProperty().multiply(Bindings.size(vaultList.getItems())) //
|
||||||
);
|
);
|
||||||
|
|
||||||
selectedVault.bind(vaultList.getSelectionModel().selectedItemProperty());
|
selectedVault.bind(vaultList.getSelectionModel().selectedItemProperty());
|
||||||
@@ -157,11 +157,11 @@ public class VaultListController implements FxController {
|
|||||||
//unlock vault on double click
|
//unlock vault on double click
|
||||||
vaultList.addEventFilter(MouseEvent.MOUSE_CLICKED, click -> {
|
vaultList.addEventFilter(MouseEvent.MOUSE_CLICKED, click -> {
|
||||||
if (click.getClickCount() >= 2) {
|
if (click.getClickCount() >= 2) {
|
||||||
Optional.ofNullable(selectedVault.get())
|
Optional.ofNullable(selectedVault.get()) //
|
||||||
.filter(Vault::isLocked)
|
.filter(Vault::isLocked) //
|
||||||
.ifPresent(vault -> appWindows.startUnlockWorkflow(vault, mainWindow));
|
.ifPresent(vault -> appWindows.startUnlockWorkflow(vault, mainWindow));
|
||||||
Optional.ofNullable(selectedVault.get())
|
Optional.ofNullable(selectedVault.get()) //
|
||||||
.filter(Vault::isUnlocked)
|
.filter(Vault::isUnlocked) //
|
||||||
.ifPresent(vaultService::reveal);
|
.ifPresent(vaultService::reveal);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package org.cryptomator.ui.notification;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
@NotificationScoped
|
||||||
|
@Subcomponent(modules = {NotificationModule.class})
|
||||||
|
public interface NotificationComponent {
|
||||||
|
|
||||||
|
@NotificationWindow
|
||||||
|
Stage window();
|
||||||
|
|
||||||
|
@FxmlScene(FxmlFile.NOTIFICATION)
|
||||||
|
Lazy<Scene> scene();
|
||||||
|
|
||||||
|
default Stage showNotification() {
|
||||||
|
var window = window();
|
||||||
|
window.setScene(scene().get());
|
||||||
|
window.sizeToScene();
|
||||||
|
window.show();
|
||||||
|
window.requestFocus();
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subcomponent.Factory
|
||||||
|
interface Factory {
|
||||||
|
NotificationComponent create();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
package org.cryptomator.ui.notification;
|
||||||
|
|
||||||
|
import org.cryptomator.common.Constants;
|
||||||
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
|
||||||
|
import org.cryptomator.event.VaultEvent;
|
||||||
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.fxapp.FxNotificationManager;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.property.IntegerProperty;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
|
import javafx.beans.property.SimpleListProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.beans.value.ObservableStringValue;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.format.FormatStyle;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
|
@NotificationScoped
|
||||||
|
public class NotificationController implements FxController {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(NotificationController.class);
|
||||||
|
private static final DateTimeFormatter LOCAL_TIME_FORMATTER_TEMPLATE = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withZone(ZoneId.systemDefault());
|
||||||
|
private static final String BUG_MSG = "If you see this message, please report it on the Cryptomator issue tracker.";
|
||||||
|
|
||||||
|
private final Stage window;
|
||||||
|
private final SimpleListProperty<VaultEvent> events;
|
||||||
|
private final ResourceBundle resourceBundle;
|
||||||
|
private final IntegerProperty selectionIndex;
|
||||||
|
private final ObservableStringValue paging;
|
||||||
|
private final ObjectProperty<VaultEvent> selectedEvent;
|
||||||
|
private final ObservableValue<Boolean> singleEvent;
|
||||||
|
private final StringProperty fileName;
|
||||||
|
private final StringProperty vaultName;
|
||||||
|
private final StringProperty eventTimestamp;
|
||||||
|
private final StringProperty message;
|
||||||
|
private final StringProperty description;
|
||||||
|
private final StringProperty actionText;
|
||||||
|
private final ExecutorService executorService;
|
||||||
|
private final DateTimeFormatter localizedTimeFormatter;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public NotificationController(@NotificationWindow Stage window, FxNotificationManager notificationManager, ExecutorService executorService, ResourceBundle resourceBundle, Settings settings) {
|
||||||
|
this.window = window;
|
||||||
|
var preferredLanguage = settings.language.get();
|
||||||
|
this.localizedTimeFormatter = LOCAL_TIME_FORMATTER_TEMPLATE.withLocale(preferredLanguage == null ? Locale.getDefault() : Locale.forLanguageTag(preferredLanguage));
|
||||||
|
this.events = new SimpleListProperty<>(notificationManager.getEventsRequiringNotification());
|
||||||
|
this.resourceBundle = resourceBundle;
|
||||||
|
this.selectionIndex = new SimpleIntegerProperty(-1);
|
||||||
|
this.selectedEvent = new SimpleObjectProperty<>();
|
||||||
|
this.singleEvent = events.sizeProperty().map(size -> size.intValue() == 1);
|
||||||
|
this.paging = Bindings.createStringBinding(() -> selectionIndex.get() + 1 + "/" + events.size(), selectionIndex, events);
|
||||||
|
this.vaultName = new SimpleStringProperty();
|
||||||
|
this.eventTimestamp = new SimpleStringProperty();
|
||||||
|
this.message = new SimpleStringProperty();
|
||||||
|
this.fileName = new SimpleStringProperty("");
|
||||||
|
this.description = new SimpleStringProperty();
|
||||||
|
this.actionText = new SimpleStringProperty();
|
||||||
|
this.executorService = executorService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void initialize() {
|
||||||
|
window.setOnShowing(_ -> selectionIndex.set(0));
|
||||||
|
selectionIndex.addListener((_, _, n) -> {
|
||||||
|
if (!events.isEmpty()) {
|
||||||
|
selectedEvent.setValue(events.get(n.intValue()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
selectedEvent.addListener(this::selectTexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void selectTexts(ObservableValue<? extends VaultEvent> observable, VaultEvent oldEvent, VaultEvent newEvent) {
|
||||||
|
if (newEvent == null) {
|
||||||
|
vaultName.set("");
|
||||||
|
message.set("NO CONTENT");
|
||||||
|
description.set(BUG_MSG);
|
||||||
|
actionText.set(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vaultName.set(newEvent.v().getDisplayName());
|
||||||
|
switch (newEvent.actualEvent()) {
|
||||||
|
case FileIsInUseEvent fiiue -> {
|
||||||
|
var userAndDevice = fiiue.owner().split(Constants.HUB_USER_DEVICE_SEPARATOR);
|
||||||
|
var user = userAndDevice[0];
|
||||||
|
var device = userAndDevice.length == 1 ? userAndDevice[0] : userAndDevice[1];
|
||||||
|
var cleartextFileName = fiiue.cleartextPath().substring(fiiue.cleartextPath().lastIndexOf('/') + 1);
|
||||||
|
eventTimestamp.set(localizedTimeFormatter.format(fiiue.lastUpdated()));
|
||||||
|
message.set(resourceBundle.getString("notification.inUse.message"));
|
||||||
|
fileName.set(cleartextFileName);
|
||||||
|
description.set(resourceBundle.getString("notification.inUse.description").formatted(user, device));
|
||||||
|
actionText.set(resourceBundle.getString("notification.inUse.action"));
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
message.set("NO CONTENT");
|
||||||
|
description.set(BUG_MSG);
|
||||||
|
actionText.set(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void processSelectedEvent() {
|
||||||
|
try {
|
||||||
|
var ev = selectedEvent.get();
|
||||||
|
switch (ev.actualEvent()) {
|
||||||
|
case FileIsInUseEvent fiiue -> {
|
||||||
|
executorService.submit(fiiue.ignoreMethod());
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
} //normally nothing
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
removeSelectedEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeSelectedEvent() {
|
||||||
|
int i = selectionIndex.get();
|
||||||
|
var size = events.size();
|
||||||
|
if (i < 0 || i >= size) {
|
||||||
|
LOG.error("Selection index {} is out of bounds of list size {} during event removal. Closing Window.", i, size);
|
||||||
|
window.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
events.remove(i);
|
||||||
|
if (events.isEmpty()) {
|
||||||
|
window.close(); //no more events
|
||||||
|
} else if (events.size() == i) {
|
||||||
|
selectionIndex.set(i - 1); //triggers event update
|
||||||
|
} else {
|
||||||
|
selectedEvent.set(events.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void previousNotification() {
|
||||||
|
int i = selectionIndex.get();
|
||||||
|
if (i != 0) {
|
||||||
|
selectionIndex.set(i - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void nextNotification() {
|
||||||
|
int i = selectionIndex.get();
|
||||||
|
if (i != events.size() - 1) {
|
||||||
|
selectionIndex.set(i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void close() {
|
||||||
|
events.clear();
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//FXML bindings
|
||||||
|
public ObservableValue<String> eventTimeProperty() {
|
||||||
|
return eventTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEventTime() {
|
||||||
|
return eventTimestamp.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableValue<String> vaultNameProperty() {
|
||||||
|
return vaultName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVaultName() {
|
||||||
|
return vaultName.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableValue<String> fileNameProperty() {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileName() {
|
||||||
|
return fileName.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableValue<String> messageProperty() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableValue<String> descriptionProperty() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty actionTextProperty() {
|
||||||
|
return actionText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getActionText() {
|
||||||
|
return Objects.requireNonNullElse(actionText.get(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableStringValue pagingProperty() {
|
||||||
|
return paging;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPaging() {
|
||||||
|
return paging.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableValue<Boolean> singleEventProperty() {
|
||||||
|
return singleEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSingleEvent() {
|
||||||
|
return singleEvent.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package org.cryptomator.ui.notification;
|
||||||
|
|
||||||
|
import dagger.Binds;
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
import dagger.multibindings.IntoMap;
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
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.StageInitializer;
|
||||||
|
import org.cryptomator.ui.common.SystemBarUtil;
|
||||||
|
|
||||||
|
import javax.inject.Provider;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.stage.Modality;
|
||||||
|
import javafx.stage.Screen;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import javafx.stage.StageStyle;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
abstract class NotificationModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@NotificationWindow
|
||||||
|
@NotificationScoped
|
||||||
|
static Stage provideStage(StageInitializer initializer) {
|
||||||
|
Stage stage = new Stage(StageStyle.TRANSPARENT);
|
||||||
|
stage.setTitle("Filesystem notification"); //TODO: translate
|
||||||
|
stage.setResizable(false);
|
||||||
|
stage.initModality(Modality.NONE);
|
||||||
|
stage.setAlwaysOnTop(true);
|
||||||
|
initializer.accept(stage);
|
||||||
|
stage.setOnShown(_ -> placeWindow(stage));
|
||||||
|
return stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places the notification window on the screen according to some heuristic based on operating system and system bar placement.
|
||||||
|
* <p>
|
||||||
|
* On macOS, the window is placed in the top-right corner of the primary screen, following platform conventions.
|
||||||
|
* On other operating systems, the window placement depends on the location of the system bar:
|
||||||
|
* <ul>
|
||||||
|
* <li>If the system bar is at the top, the window is centered horizontally at the top of the screen.</li>
|
||||||
|
* <li>Otherwise (e.g., system bar at the bottom or elsewhere), the window is placed in the bottom-right corner.</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
* The method uses the visual bounds of the primary screen to avoid overlapping with system UI elements.
|
||||||
|
* Assumes the window size has already been set before calling this method.
|
||||||
|
*
|
||||||
|
* @param window the Stage representing the notification window to be placed
|
||||||
|
*/
|
||||||
|
static void placeWindow(Stage window) {
|
||||||
|
var screen = Screen.getPrimary();
|
||||||
|
var vBounds = screen.getVisualBounds();
|
||||||
|
if (SystemUtils.IS_OS_MAC) { //place to right top
|
||||||
|
window.setX(vBounds.getMaxX() - window.getWidth());
|
||||||
|
window.setY(vBounds.getMinY());
|
||||||
|
} else {
|
||||||
|
switch (SystemBarUtil.getPlacementOfSystembar(screen)) {
|
||||||
|
case TOP -> { //place to middle top
|
||||||
|
window.setX(vBounds.getMinX() + (vBounds.getWidth() - window.getWidth()) / 2.0);
|
||||||
|
window.setY(vBounds.getMinY());
|
||||||
|
}
|
||||||
|
default -> { //place to right bottom
|
||||||
|
window.setX(vBounds.getMaxX() - window.getWidth());
|
||||||
|
window.setY(vBounds.getMaxY() - window.getHeight());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// javafx setup
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@FxmlScene(FxmlFile.NOTIFICATION)
|
||||||
|
@NotificationScoped
|
||||||
|
static Scene provideNotificationScene(@NotificationWindow FxmlLoaderFactory fxmlLoaders) {
|
||||||
|
return fxmlLoaders.createScene(FxmlFile.NOTIFICATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@NotificationScoped
|
||||||
|
@NotificationWindow
|
||||||
|
static FxmlLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
|
||||||
|
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(NotificationController.class)
|
||||||
|
abstract FxController bindNotificationController(NotificationController controller);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package org.cryptomator.ui.notification;
|
||||||
|
|
||||||
|
import javax.inject.Scope;
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
@Scope
|
||||||
|
@Documented
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface NotificationScoped {}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package org.cryptomator.ui.notification;
|
||||||
|
|
||||||
|
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)
|
||||||
|
public @interface NotificationWindow {}
|
||||||
@@ -101,8 +101,9 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void migrateKeychainEntries(Observable observable, KeychainAccessProvider oldProvider, KeychainAccessProvider newProvider) {
|
private void migrateKeychainEntries(Observable observable, KeychainAccessProvider oldProvider, KeychainAccessProvider newProvider) {
|
||||||
//currently, we only migrate on macOS (touchID vs regular keychain)
|
//currently, we migrate on macOS (touchID vs regular keychain)
|
||||||
if (SystemUtils.IS_OS_MAC) {
|
//and on Linux (GNOME Keyring / KDE Wallet / Secret Service)
|
||||||
|
if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_LINUX) {
|
||||||
var idsAndNames = settings.directories.stream().collect(Collectors.toMap(vs -> vs.id, vs -> vs.displayName.getValue()));
|
var idsAndNames = settings.directories.stream().collect(Collectors.toMap(vs -> vs.id, vs -> vs.displayName.getValue()));
|
||||||
if (!idsAndNames.isEmpty()) {
|
if (!idsAndNames.isEmpty()) {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public class InterfacePreferencesController implements FxController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
themeChoiceBox.getItems().addAll(UiTheme.applicableValues());
|
themeChoiceBox.getItems().addAll(UiTheme.values());
|
||||||
if (!themeChoiceBox.getItems().contains(settings.theme.get())) {
|
if (!themeChoiceBox.getItems().contains(settings.theme.get())) {
|
||||||
settings.theme.set(UiTheme.LIGHT);
|
settings.theme.set(UiTheme.LIGHT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ public class RecoveryKeyCreationController implements FxController {
|
|||||||
descriptionLabel.formatProperty().set(resourceBundle.getString("recoveryKey.recover.description"));
|
descriptionLabel.formatProperty().set(resourceBundle.getString("recoveryKey.recover.description"));
|
||||||
cancelButton.setOnAction((_) -> back());
|
cancelButton.setOnAction((_) -> back());
|
||||||
cancelButton.setText(resourceBundle.getString("generic.button.back"));
|
cancelButton.setText(resourceBundle.getString("generic.button.back"));
|
||||||
nextButton.setOnAction((_) -> restoreWithPassword());
|
nextButton.setOnAction((_) -> restoreWithPasswordAsync());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,11 +137,47 @@ public class RecoveryKeyCreationController implements FxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void restoreWithPassword() {
|
public void restoreWithPasswordAsync() {
|
||||||
|
Task<Void> task = RecoveryKeyTasks.createTask(this::restoreWithPassword);
|
||||||
|
|
||||||
|
task.setOnScheduled(_ -> {
|
||||||
|
LOG.debug("Restoring vault configuration with password for {}.", vault.getDisplayablePath());
|
||||||
|
});
|
||||||
|
|
||||||
|
task.setOnSucceeded(_ -> {
|
||||||
|
LOG.debug("Restored vault configuration for {}.", vault.getDisplayablePath());
|
||||||
|
try {
|
||||||
|
if (!vaultListManager.isAlreadyAdded(vault.getPath())) {
|
||||||
|
vaultListManager.add(vault.getPath());
|
||||||
|
}
|
||||||
|
window.close();
|
||||||
|
dialogs.prepareRecoverPasswordSuccess((Stage) window.getOwner()) //
|
||||||
|
.setTitleKey("recover.recoverVaultConfig.title") //
|
||||||
|
.setMessageKey("recoveryKey.recover.resetVaultConfigSuccess.message") //
|
||||||
|
.setDescriptionKey("recoveryKey.recover.resetMasterkeyFileSuccess.description")
|
||||||
|
.build().showAndWait();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Failed to add vault to list.", e);
|
||||||
|
appWindows.showErrorWindow(e, window, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
task.setOnFailed(_ -> {
|
||||||
|
if (task.getException() instanceof InvalidPassphraseException e) {
|
||||||
|
LOG.info("Password invalid", e);
|
||||||
|
Animations.createShakeWindowAnimation(window).play();
|
||||||
|
} else {
|
||||||
|
LOG.error("Recovery process failed.", task.getException());
|
||||||
|
appWindows.showErrorWindow(task.getException(), window, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
executor.submit(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
void restoreWithPassword() throws IOException, CryptoException {
|
||||||
try (RecoveryDirectory recoveryDirectory = RecoveryDirectory.create(vault.getPath())) {
|
try (RecoveryDirectory recoveryDirectory = RecoveryDirectory.create(vault.getPath())) {
|
||||||
Path recoveryPath = recoveryDirectory.getRecoveryPath();
|
Path recoveryPath = recoveryDirectory.getRecoveryPath();
|
||||||
|
|
||||||
Path masterkeyFilePath = vault.getPath().resolve(MASTERKEY_FILENAME);
|
Path masterkeyFilePath = vault.getPath().resolve(MASTERKEY_FILENAME);
|
||||||
|
|
||||||
try (Masterkey masterkey = MasterkeyService.load(masterkeyFileAccess, masterkeyFilePath, passwordField.getCharacters())) {
|
try (Masterkey masterkey = MasterkeyService.load(masterkeyFileAccess, masterkeyFilePath, passwordField.getCharacters())) {
|
||||||
@@ -152,23 +188,6 @@ public class RecoveryKeyCreationController implements FxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
recoveryDirectory.moveRecoveredFile(VAULTCONFIG_FILENAME);
|
recoveryDirectory.moveRecoveredFile(VAULTCONFIG_FILENAME);
|
||||||
|
|
||||||
if (!vaultListManager.isAlreadyAdded(vault.getPath())) {
|
|
||||||
vaultListManager.add(vault.getPath());
|
|
||||||
}
|
|
||||||
window.close();
|
|
||||||
dialogs.prepareRecoverPasswordSuccess((Stage)window.getOwner()) //
|
|
||||||
.setTitleKey("recover.recoverVaultConfig.title") //
|
|
||||||
.setMessageKey("recoveryKey.recover.resetVaultConfigSuccess.message") //
|
|
||||||
.setDescriptionKey("recoveryKey.recover.resetMasterkeyFileSuccess.description")
|
|
||||||
.build().showAndWait();
|
|
||||||
|
|
||||||
} catch (InvalidPassphraseException e) {
|
|
||||||
LOG.info("Password invalid", e);
|
|
||||||
Animations.createShakeWindowAnimation(window).play();
|
|
||||||
} catch (IOException | CryptoException | IllegalStateException e) {
|
|
||||||
LOG.error("Recovery process failed", e);
|
|
||||||
appWindows.showErrorWindow(e, window, null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+34
-15
@@ -117,14 +117,46 @@ public class RecoveryKeyResetPasswordController implements FxController {
|
|||||||
@FXML
|
@FXML
|
||||||
public void next() {
|
public void next() {
|
||||||
switch (recoverType.get()) {
|
switch (recoverType.get()) {
|
||||||
case RESTORE_ALL -> restorePassword();
|
case RESTORE_ALL -> restorePasswordAsync();
|
||||||
case RESTORE_MASTERKEY, RESET_PASSWORD -> resetPassword();
|
case RESTORE_MASTERKEY, RESET_PASSWORD -> resetPassword();
|
||||||
default -> resetPassword(); // Fallback
|
default -> resetPassword(); // Fallback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void restorePassword() {
|
public void restorePasswordAsync() {
|
||||||
|
Task<Void> task = RecoveryKeyTasks.createTask(this::restorePassword);
|
||||||
|
|
||||||
|
task.setOnScheduled(_ -> {
|
||||||
|
LOG.debug("Restoring vault configuration for {}.", vault.getDisplayablePath());
|
||||||
|
});
|
||||||
|
|
||||||
|
task.setOnSucceeded(_ -> {
|
||||||
|
LOG.debug("Restored vault configuration for {}.", vault.getDisplayablePath());
|
||||||
|
try {
|
||||||
|
if (!vaultListManager.isAlreadyAdded(vault.getPath())) {
|
||||||
|
vaultListManager.add(vault.getPath());
|
||||||
|
}
|
||||||
|
window.close();
|
||||||
|
dialogs.prepareRecoverPasswordSuccess((Stage) window.getOwner()) //
|
||||||
|
.setTitleKey("recover.recoverVaultConfig.title") //
|
||||||
|
.setMessageKey("recoveryKey.recover.resetVaultConfigSuccess.message") //
|
||||||
|
.build().showAndWait();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Failed to add vault to list.", e);
|
||||||
|
appWindows.showErrorWindow(e, window, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
task.setOnFailed(_ -> {
|
||||||
|
LOG.error("Recovery process failed.", task.getException());
|
||||||
|
appWindows.showErrorWindow(task.getException(), window, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
executor.submit(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
void restorePassword() throws IOException, CryptoException {
|
||||||
try (RecoveryDirectory recoveryDirectory = RecoveryDirectory.create(vault.getPath())) {
|
try (RecoveryDirectory recoveryDirectory = RecoveryDirectory.create(vault.getPath())) {
|
||||||
Path recoveryPath = recoveryDirectory.getRecoveryPath();
|
Path recoveryPath = recoveryDirectory.getRecoveryPath();
|
||||||
MasterkeyService.recoverFromRecoveryKey(recoveryKey.get(), recoveryKeyFactory, recoveryPath, newPasswordController.passwordField.getCharacters());
|
MasterkeyService.recoverFromRecoveryKey(recoveryKey.get(), recoveryKeyFactory, recoveryPath, newPasswordController.passwordField.getCharacters());
|
||||||
@@ -135,19 +167,6 @@ public class RecoveryKeyResetPasswordController implements FxController {
|
|||||||
|
|
||||||
recoveryDirectory.moveRecoveredFile(MASTERKEY_FILENAME);
|
recoveryDirectory.moveRecoveredFile(MASTERKEY_FILENAME);
|
||||||
recoveryDirectory.moveRecoveredFile(VAULTCONFIG_FILENAME);
|
recoveryDirectory.moveRecoveredFile(VAULTCONFIG_FILENAME);
|
||||||
|
|
||||||
if (!vaultListManager.isAlreadyAdded(vault.getPath())) {
|
|
||||||
vaultListManager.add(vault.getPath());
|
|
||||||
}
|
|
||||||
window.close();
|
|
||||||
dialogs.prepareRecoverPasswordSuccess((Stage)window.getOwner()) //
|
|
||||||
.setTitleKey("recover.recoverVaultConfig.title") //
|
|
||||||
.setMessageKey("recoveryKey.recover.resetVaultConfigSuccess.message") //
|
|
||||||
.build().showAndWait();
|
|
||||||
|
|
||||||
} catch (IOException | CryptoException e) {
|
|
||||||
LOG.error("Recovery process failed", e);
|
|
||||||
appWindows.showErrorWindow(e, window, null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.cryptomator.ui.recoverykey;
|
||||||
|
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
|
||||||
|
final class RecoveryKeyTasks {
|
||||||
|
|
||||||
|
private RecoveryKeyTasks() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
interface TaskAction {
|
||||||
|
void run() throws Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Task<Void> createTask(TaskAction action) {
|
||||||
|
return new Task<Void>() {
|
||||||
|
@Override
|
||||||
|
protected Void call() throws Exception {
|
||||||
|
action.run();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -57,8 +57,8 @@ abstract class UnlockModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@UnlockWindow
|
@UnlockWindow
|
||||||
@UnlockScoped
|
@UnlockScoped
|
||||||
static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Builder compBuilder, @UnlockWindow Vault vault, @UnlockWindow Stage window) {
|
static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Factory compFactory, @UnlockWindow Vault vault, @UnlockWindow Stage window) {
|
||||||
return compBuilder.vault(vault).window(window).build().keyloadingStrategy();
|
return compFactory.create(vault, window).keyloadingStrategy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -9,11 +9,15 @@ import org.cryptomator.ui.forgetpassword.ForgetPasswordComponent;
|
|||||||
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
|
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
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.fxml.FXML;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
|
||||||
|
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
||||||
|
|
||||||
@VaultOptionsScoped
|
@VaultOptionsScoped
|
||||||
public class MasterkeyOptionsController implements FxController {
|
public class MasterkeyOptionsController implements FxController {
|
||||||
@@ -25,6 +29,7 @@ public class MasterkeyOptionsController implements FxController {
|
|||||||
private final ForgetPasswordComponent.Builder forgetPasswordWindow;
|
private final ForgetPasswordComponent.Builder forgetPasswordWindow;
|
||||||
private final KeychainManager keychain;
|
private final KeychainManager keychain;
|
||||||
private final ObservableValue<Boolean> passwordSaved;
|
private final ObservableValue<Boolean> passwordSaved;
|
||||||
|
private final BooleanProperty masterkeyFileAvailable;
|
||||||
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@@ -40,6 +45,7 @@ public class MasterkeyOptionsController implements FxController {
|
|||||||
} else {
|
} else {
|
||||||
this.passwordSaved = new SimpleBooleanProperty(false);
|
this.passwordSaved = new SimpleBooleanProperty(false);
|
||||||
}
|
}
|
||||||
|
this.masterkeyFileAvailable = new SimpleBooleanProperty(Files.exists(vault.getPath().resolve(MASTERKEY_FILENAME)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -70,4 +76,12 @@ public class MasterkeyOptionsController implements FxController {
|
|||||||
public boolean isPasswordSaved() {
|
public boolean isPasswordSaved() {
|
||||||
return passwordSaved.getValue();
|
return passwordSaved.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BooleanProperty masterkeyFileAvailableProperty() {
|
||||||
|
return masterkeyFileAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMasterkeyFileAvailable() {
|
||||||
|
return masterkeyFileAvailable.get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
GREEN_5: PRIMARY;
|
GREEN_5: PRIMARY;
|
||||||
RED_5: #E74C3C;
|
RED_5: #E74C3C;
|
||||||
ORANGE_5: #E67E22;
|
ORANGE_5: #E67E22;
|
||||||
|
YELLOW_4: #C19F0E;
|
||||||
YELLOW_5: #F1C40F;
|
YELLOW_5: #F1C40F;
|
||||||
|
|
||||||
MAIN_BG: GRAY_1;
|
MAIN_BG: GRAY_1;
|
||||||
@@ -113,6 +114,11 @@
|
|||||||
-fx-font-size: 1.2em;
|
-fx-font-size: 1.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label-window-title {
|
||||||
|
-fx-font-family: 'Open Sans SemiBold';
|
||||||
|
-fx-font-size: 1.0em;
|
||||||
|
}
|
||||||
|
|
||||||
.label-small {
|
.label-small {
|
||||||
-fx-font-size: 0.8em;
|
-fx-font-size: 0.8em;
|
||||||
}
|
}
|
||||||
@@ -382,37 +388,62 @@
|
|||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
||||||
* NotificationBar *
|
* InfoBar *
|
||||||
* *
|
* *
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
.info-bar {
|
||||||
|
-fx-alignment: center;
|
||||||
|
-fx-min-height:24px;
|
||||||
|
-fx-max-height:24px;
|
||||||
|
-fx-background-color: -bar-bg;
|
||||||
|
}
|
||||||
|
|
||||||
.notification-label {
|
.info-bar--green {
|
||||||
-fx-text-fill: white;
|
-bar-bg: GREEN_5;
|
||||||
|
-button-bg: GREEN_5;
|
||||||
|
-button-bg-pressed: GREEN_3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-bar--yellow {
|
||||||
|
-bar-bg: YELLOW_5;
|
||||||
|
-button-bg: YELLOW_5;
|
||||||
|
-button-bg-pressed: YELLOW_4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-bar--red {
|
||||||
|
-bar-bg: RED_5;
|
||||||
|
-button-bg: RED_5;
|
||||||
|
-button-bg-pressed: RED_5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-bar .label {
|
||||||
|
-fx-text-fill: GRAY_9;
|
||||||
-fx-font-weight: bold;
|
-fx-font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-debug {
|
.info-bar--yellow .label {
|
||||||
-fx-min-height:24px;
|
-fx-text-fill: GRAY_9;
|
||||||
-fx-max-height:24px;
|
-fx-font-weight: bold;
|
||||||
-fx-background-color: RED_5;
|
-fx-effect: dropshadow( gaussian , rgba(0,0,0,0.45) , 2 , 0 , 0 , 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-update {
|
.info-bar .glyph {
|
||||||
-fx-min-height:24px;
|
-fx-fill: GRAY_9;
|
||||||
-fx-max-height:24px;
|
|
||||||
-fx-background-color: YELLOW_5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-support {
|
.info-bar:hover .label {
|
||||||
-fx-min-height:24px;
|
-fx-underline:true;
|
||||||
-fx-max-height:24px;
|
|
||||||
-fx-background-color: PRIMARY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-debug:hover .notification-label,
|
.info-bar .button {
|
||||||
.notification-update:hover .notification-label,
|
-fx-background-color: transparent;
|
||||||
.notification-support:hover .notification-label {
|
-fx-border-color: transparent;
|
||||||
-fx-underline:true;
|
-fx-padding: 2 2 2 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-bar .button:armed {
|
||||||
|
-fx-background-color: -button-bg-pressed;
|
||||||
|
-fx-background-radius: 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -1186,3 +1217,32 @@
|
|||||||
-fx-background-color: MAIN_BG;
|
-fx-background-color: MAIN_BG;
|
||||||
-fx-font-size: 1.166667em; /* 14pt - 2 more than the default font */
|
-fx-font-size: 1.166667em; /* 14pt - 2 more than the default font */
|
||||||
}
|
}
|
||||||
|
/*******************************************************************************
|
||||||
|
* *
|
||||||
|
* Notification Window
|
||||||
|
* *
|
||||||
|
******************************************************************************/
|
||||||
|
.notification-window {
|
||||||
|
-fx-background-color: MAIN_BG;
|
||||||
|
-fx-background-radius: 8px;
|
||||||
|
-fx-border-radius: 8px;
|
||||||
|
-fx-background-insets: 0;
|
||||||
|
-fx-border-color: MUTED_BG;
|
||||||
|
-fx-border-width: 1px;
|
||||||
|
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.2), 28, 0.35, 0, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-window .dialog-header {
|
||||||
|
-fx-alignment: center-left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-window .window-bar-button {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-border-color: transparent;
|
||||||
|
-fx-padding: 6 8 6 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-window .window-bar-button:armed {
|
||||||
|
-fx-background-color: CONTROL_BG_ARMED;
|
||||||
|
-fx-background-radius: 8;
|
||||||
|
}
|
||||||
@@ -50,6 +50,7 @@
|
|||||||
GREEN_5: PRIMARY;
|
GREEN_5: PRIMARY;
|
||||||
RED_5: #E74C3C;
|
RED_5: #E74C3C;
|
||||||
ORANGE_5: #E67E22;
|
ORANGE_5: #E67E22;
|
||||||
|
YELLOW_4: #C19F0E;
|
||||||
YELLOW_5: #F1C40F;
|
YELLOW_5: #F1C40F;
|
||||||
|
|
||||||
MAIN_BG: GRAY_9;
|
MAIN_BG: GRAY_9;
|
||||||
@@ -113,6 +114,11 @@
|
|||||||
-fx-font-size: 1.2em;
|
-fx-font-size: 1.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label-window-title {
|
||||||
|
-fx-font-family: 'Open Sans SemiBold';
|
||||||
|
-fx-font-size: 1.0em;
|
||||||
|
}
|
||||||
|
|
||||||
.label-small {
|
.label-small {
|
||||||
-fx-font-size: 0.8em;
|
-fx-font-size: 0.8em;
|
||||||
}
|
}
|
||||||
@@ -382,39 +388,64 @@
|
|||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
||||||
* NotificationBar *
|
* InfoBar *
|
||||||
* *
|
* *
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
.info-bar {
|
||||||
|
-fx-alignment: center;
|
||||||
|
-fx-min-height:24px;
|
||||||
|
-fx-max-height:24px;
|
||||||
|
-fx-background-color: -bar-bg;
|
||||||
|
}
|
||||||
|
|
||||||
.notification-label {
|
.info-bar--green {
|
||||||
-fx-text-fill: white;
|
-bar-bg: GREEN_5;
|
||||||
|
-button-bg: GREEN_5;
|
||||||
|
-button-bg-pressed: GREEN_3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-bar--yellow {
|
||||||
|
-bar-bg: YELLOW_5;
|
||||||
|
-button-bg: YELLOW_5;
|
||||||
|
-button-bg-pressed: YELLOW_4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-bar--red {
|
||||||
|
-bar-bg: RED_5;
|
||||||
|
-button-bg: RED_5;
|
||||||
|
-button-bg-pressed: RED_5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-bar .label {
|
||||||
|
-fx-text-fill: GRAY_9;
|
||||||
-fx-font-weight: bold;
|
-fx-font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-debug {
|
.info-bar--yellow .label {
|
||||||
-fx-min-height:24px;
|
-fx-text-fill: GRAY_9;
|
||||||
-fx-max-height:24px;
|
-fx-font-weight: bold;
|
||||||
-fx-background-color: RED_5;
|
-fx-effect: dropshadow( gaussian , rgba(0,0,0,0.45) , 2 , 0 , 0 , 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-update {
|
.info-bar .glyph {
|
||||||
-fx-min-height:24px;
|
-fx-fill: GRAY_9;
|
||||||
-fx-max-height:24px;
|
|
||||||
-fx-background-color: YELLOW_5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-support {
|
.info-bar:hover .label {
|
||||||
-fx-min-height:24px;
|
|
||||||
-fx-max-height:24px;
|
|
||||||
-fx-background-color: PRIMARY;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-debug:hover .notification-label,
|
|
||||||
.notification-update:hover .notification-label,
|
|
||||||
.notification-support:hover .notification-label {
|
|
||||||
-fx-underline:true;
|
-fx-underline:true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info-bar .button {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-border-color: transparent;
|
||||||
|
-fx-padding: 2 2 2 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-bar .button:armed {
|
||||||
|
-fx-background-color: -button-bg-pressed;
|
||||||
|
-fx-background-radius: 8;
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
||||||
* ScrollBar *
|
* ScrollBar *
|
||||||
@@ -1186,3 +1217,33 @@
|
|||||||
-fx-background-color: MAIN_BG;
|
-fx-background-color: MAIN_BG;
|
||||||
-fx-font-size: 1.166667em; /* 14pt - 2 more than the default font */
|
-fx-font-size: 1.166667em; /* 14pt - 2 more than the default font */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* *
|
||||||
|
* Notification Window
|
||||||
|
* *
|
||||||
|
******************************************************************************/
|
||||||
|
.notification-window {
|
||||||
|
-fx-background-color: MAIN_BG;
|
||||||
|
-fx-background-radius: 8px;
|
||||||
|
-fx-border-radius: 8px;
|
||||||
|
-fx-background-insets: 0;
|
||||||
|
-fx-border-color: MUTED_BG;
|
||||||
|
-fx-border-width: 1px;
|
||||||
|
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.2), 28, 0.35, 0, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-window .dialog-header {
|
||||||
|
-fx-alignment: center-left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-window .window-bar-button {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-border-color: transparent;
|
||||||
|
-fx-padding: 6 8 6 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-window .window-bar-button:armed {
|
||||||
|
-fx-background-color: CONTROL_BG_ARMED;
|
||||||
|
-fx-background-radius: 8;
|
||||||
|
}
|
||||||
@@ -13,7 +13,8 @@
|
|||||||
prefWidth="450"
|
prefWidth="450"
|
||||||
prefHeight="450"
|
prefHeight="450"
|
||||||
spacing="24"
|
spacing="24"
|
||||||
alignment="CENTER">
|
alignment="CENTER"
|
||||||
|
accessibleRole="DIALOG">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets topRightBottomLeft="24"/>
|
<Insets topRightBottomLeft="24"/>
|
||||||
</padding>
|
</padding>
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
prefWidth="450"
|
prefWidth="450"
|
||||||
prefHeight="450"
|
prefHeight="450"
|
||||||
spacing="12"
|
spacing="12"
|
||||||
alignment="CENTER_LEFT">
|
alignment="CENTER_LEFT"
|
||||||
|
accessibleRole="DIALOG">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets topRightBottomLeft="24"/>
|
<Insets topRightBottomLeft="24"/>
|
||||||
</padding>
|
</padding>
|
||||||
@@ -39,9 +40,9 @@
|
|||||||
<CheckBox fx:id="expertSettingsCheckBox" text="%addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox" onAction="#toggleUseExpertSettings"/>
|
<CheckBox fx:id="expertSettingsCheckBox" text="%addvaultwizard.new.expertSettings.enableExpertSettingsCheckbox" onAction="#toggleUseExpertSettings"/>
|
||||||
<VBox spacing="6" visible="${expertSettingsCheckBox.selected}">
|
<VBox spacing="6" visible="${expertSettingsCheckBox.selected}">
|
||||||
<HBox spacing="2" HBox.hgrow="NEVER">
|
<HBox spacing="2" HBox.hgrow="NEVER">
|
||||||
<Label text="%addvaultwizard.new.expertSettings.shorteningThreshold.title"/>
|
<Label text="%addvaultwizard.new.expertSettings.shorteningThreshold.title" labelFor="$shorteningThresholdTextField"/>
|
||||||
<Region prefWidth="2"/>
|
<Region prefWidth="2"/>
|
||||||
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs">
|
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs" accessibleText="%addvaultwizard.new.expertSettings.shorteningThreshold.tooltip">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
|
<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
prefWidth="450"
|
prefWidth="450"
|
||||||
prefHeight="450"
|
prefHeight="450"
|
||||||
spacing="12"
|
spacing="12"
|
||||||
alignment="CENTER_LEFT">
|
alignment="CENTER_LEFT"
|
||||||
|
accessibleRole="DIALOG">
|
||||||
<fx:define>
|
<fx:define>
|
||||||
<ToggleGroup fx:id="locationPresetsToggler"/>
|
<ToggleGroup fx:id="locationPresetsToggler"/>
|
||||||
<FontAwesome5IconView fx:id="badLocation" styleClass="glyph-icon-red" glyph="TIMES"/>
|
<FontAwesome5IconView fx:id="badLocation" styleClass="glyph-icon-red" glyph="TIMES"/>
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
prefWidth="450"
|
prefWidth="450"
|
||||||
prefHeight="450"
|
prefHeight="450"
|
||||||
spacing="12"
|
spacing="12"
|
||||||
alignment="CENTER_LEFT">
|
alignment="CENTER_LEFT"
|
||||||
|
accessibleRole="DIALOG">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets topRightBottomLeft="24"/>
|
<Insets topRightBottomLeft="24"/>
|
||||||
</padding>
|
</padding>
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
prefWidth="450"
|
prefWidth="450"
|
||||||
prefHeight="450"
|
prefHeight="450"
|
||||||
spacing="12"
|
spacing="12"
|
||||||
alignment="CENTER_LEFT">
|
alignment="CENTER_LEFT"
|
||||||
|
accessibleRole="DIALOG">
|
||||||
<fx:define>
|
<fx:define>
|
||||||
<ToggleGroup fx:id="recoveryKeyChoice"/>
|
<ToggleGroup fx:id="recoveryKeyChoice"/>
|
||||||
</fx:define>
|
</fx:define>
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
prefWidth="450"
|
prefWidth="450"
|
||||||
prefHeight="450"
|
prefHeight="450"
|
||||||
spacing="12"
|
spacing="12"
|
||||||
alignment="CENTER_LEFT">
|
alignment="CENTER_LEFT"
|
||||||
|
accessibleRole="DIALOG">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets topRightBottomLeft="24"/>
|
<Insets topRightBottomLeft="24"/>
|
||||||
</padding>
|
</padding>
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
prefWidth="450"
|
prefWidth="450"
|
||||||
prefHeight="450"
|
prefHeight="450"
|
||||||
spacing="12"
|
spacing="12"
|
||||||
alignment="TOP_CENTER">
|
alignment="TOP_CENTER"
|
||||||
|
accessibleRole="DIALOG">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets topRightBottomLeft="24"/>
|
<Insets topRightBottomLeft="24"/>
|
||||||
</padding>
|
</padding>
|
||||||
|
|||||||
@@ -13,13 +13,14 @@
|
|||||||
fx:controller="org.cryptomator.ui.changepassword.ChangePasswordController"
|
fx:controller="org.cryptomator.ui.changepassword.ChangePasswordController"
|
||||||
minWidth="400"
|
minWidth="400"
|
||||||
maxWidth="400"
|
maxWidth="400"
|
||||||
spacing="12">
|
spacing="12"
|
||||||
|
accessibleRole="DIALOG">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets topRightBottomLeft="12"/>
|
<Insets topRightBottomLeft="12"/>
|
||||||
</padding>
|
</padding>
|
||||||
<children>
|
<children>
|
||||||
<VBox spacing="6">
|
<VBox spacing="6">
|
||||||
<FormattedLabel format="%changepassword.enterOldPassword" arg1="${controller.vault.displayName}" wrapText="true"/>
|
<FormattedLabel format="%changepassword.enterOldPassword" arg1="${controller.vault.displayName}" wrapText="true" labelFor="$oldPasswordField"/>
|
||||||
<NiceSecurePasswordField fx:id="oldPasswordField"/>
|
<NiceSecurePasswordField fx:id="oldPasswordField"/>
|
||||||
</VBox>
|
</VBox>
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
maxWidth="400"
|
maxWidth="400"
|
||||||
minHeight="145"
|
minHeight="145"
|
||||||
spacing="12"
|
spacing="12"
|
||||||
alignment="TOP_CENTER">
|
alignment="TOP_CENTER"
|
||||||
|
accessibleRole="DIALOG">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets topRightBottomLeft="12"/>
|
<Insets topRightBottomLeft="12"/>
|
||||||
</padding>
|
</padding>
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
maxWidth="400"
|
maxWidth="400"
|
||||||
minHeight="145"
|
minHeight="145"
|
||||||
spacing="12"
|
spacing="12"
|
||||||
alignment="TOP_CENTER">
|
alignment="TOP_CENTER"
|
||||||
|
accessibleRole="DIALOG">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets topRightBottomLeft="12"/>
|
<Insets topRightBottomLeft="12"/>
|
||||||
</padding>
|
</padding>
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
maxWidth="400"
|
maxWidth="400"
|
||||||
minHeight="145"
|
minHeight="145"
|
||||||
spacing="12"
|
spacing="12"
|
||||||
alignment="TOP_LEFT">
|
alignment="TOP_LEFT"
|
||||||
|
accessibleRole="DIALOG">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets topRightBottomLeft="12"/>
|
<Insets topRightBottomLeft="12"/>
|
||||||
</padding>
|
</padding>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
<Insets left="6"/>
|
<Insets left="6"/>
|
||||||
</padding>
|
</padding>
|
||||||
<Region HBox.hgrow="ALWAYS"/>
|
<Region HBox.hgrow="ALWAYS"/>
|
||||||
<Button styleClass="button-right" contentDisplay="GRAPHIC_ONLY" onAction="#copyTableToClipboard">
|
<Button styleClass="button-right" contentDisplay="GRAPHIC_ONLY" onAction="#copyTableToClipboard" accessibleText="%decryptNames.copyTable.tooltip">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesome5IconView glyph="CLIPBOARD" glyphSize="16"/>
|
<FontAwesome5IconView glyph="CLIPBOARD" glyphSize="16"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
<Tooltip text="%decryptNames.copyTable.tooltip"/>
|
<Tooltip text="%decryptNames.copyTable.tooltip"/>
|
||||||
</tooltip>
|
</tooltip>
|
||||||
</Button>
|
</Button>
|
||||||
<Button styleClass="button-right" contentDisplay="GRAPHIC_ONLY" onAction="#clearTable">
|
<Button styleClass="button-right" contentDisplay="GRAPHIC_ONLY" onAction="#clearTable" accessibleText="%decryptNames.clearTable.tooltip">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesome5IconView glyph="TRASH" glyphSize="16"/>
|
<FontAwesome5IconView glyph="TRASH" glyphSize="16"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
@@ -48,12 +48,12 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</placeholder>
|
</placeholder>
|
||||||
<columns>
|
<columns>
|
||||||
<TableColumn fx:id="ciphertextColumn" prefWidth="${cipherToCleartextTable.width * 0.5}">
|
<TableColumn fx:id="ciphertextColumn" prefWidth="${cipherToCleartextTable.width * 0.5}" text="%decryptNames.column.encrypted">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesome5IconView glyph="LOCK"/>
|
<FontAwesome5IconView glyph="LOCK"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
</TableColumn>
|
</TableColumn>
|
||||||
<TableColumn fx:id="cleartextColumn" prefWidth="${cipherToCleartextTable.width * 0.5}">
|
<TableColumn fx:id="cleartextColumn" prefWidth="${cipherToCleartextTable.width * 0.5}" text="%decryptNames.column.decrypted">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesome5IconView glyph="LOCK_OPEN"/>
|
<FontAwesome5IconView glyph="LOCK_OPEN"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
|
|||||||
@@ -22,7 +22,8 @@
|
|||||||
minHeight="450"
|
minHeight="450"
|
||||||
prefWidth="450"
|
prefWidth="450"
|
||||||
prefHeight="450"
|
prefHeight="450"
|
||||||
spacing="12">
|
spacing="12"
|
||||||
|
accessibleRole="DIALOG">
|
||||||
<padding>
|
<padding>
|
||||||
<Insets topRightBottomLeft="24"/>
|
<Insets topRightBottomLeft="24"/>
|
||||||
</padding>
|
</padding>
|
||||||
@@ -79,7 +80,7 @@
|
|||||||
<padding>
|
<padding>
|
||||||
<Insets top="6" bottom="6"/>
|
<Insets top="6" bottom="6"/>
|
||||||
</padding>
|
</padding>
|
||||||
<Label text="%error.technicalDetails"/>
|
<Label text="%error.technicalDetails" labelFor="$detailsTextArea"/>
|
||||||
<Region HBox.hgrow="ALWAYS"/>
|
<Region HBox.hgrow="ALWAYS"/>
|
||||||
<Hyperlink styleClass="hyperlink-underline" text="%generic.button.copy" onAction="#copyDetails" contentDisplay="LEFT" visible="${!controller.copiedDetails}" managed="${!controller.copiedDetails}">
|
<Hyperlink styleClass="hyperlink-underline" text="%generic.button.copy" onAction="#copyDetails" contentDisplay="LEFT" visible="${!controller.copiedDetails}" managed="${!controller.copiedDetails}">
|
||||||
<graphic>
|
<graphic>
|
||||||
@@ -92,7 +93,7 @@
|
|||||||
</graphic>
|
</graphic>
|
||||||
</Hyperlink>
|
</Hyperlink>
|
||||||
</HBox>
|
</HBox>
|
||||||
<TextArea VBox.vgrow="ALWAYS" text="${controller.detailText}" prefRowCount="5" editable="false"/>
|
<TextArea fx:id="detailsTextArea" VBox.vgrow="ALWAYS" text="${controller.detailText}" prefRowCount="5" editable="false"/>
|
||||||
|
|
||||||
<Region minHeight="18"/>
|
<Region minHeight="18"/>
|
||||||
<ButtonBar buttonMinWidth="120" buttonOrder="B+C">
|
<ButtonBar buttonMinWidth="120" buttonOrder="B+C">
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user